diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index 28d5af82ac..7d6a835e67 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -140,6 +140,8 @@ export function isNonBlockingAsk(ask: ClineAsk): ask is NonBlockingAsk { * - `condense_context_error`: Error occurred during context condensation * - `codebase_search_result`: Results from searching the codebase * - `too_many_tools_warning`: Warning that too many MCP tools are enabled, which may confuse the LLM + * - `inline_subtask_started`: A subtask was auto-flattened and is now executing inline in this conversation + * - `inline_subtask_rejected`: A nested new_task call was rejected (an inline phase is already active) */ export const clineSays = [ "error", @@ -161,6 +163,8 @@ export const clineSays = [ "mcp_server_request_started", "mcp_server_response", "subtask_result", + "inline_subtask_started", + "inline_subtask_rejected", "checkpoint_saved", "rooignore_error", "diff_error", diff --git a/src/core/tools/NewTaskTool.ts b/src/core/tools/NewTaskTool.ts index b5716f70d8..68240bb21d 100644 --- a/src/core/tools/NewTaskTool.ts +++ b/src/core/tools/NewTaskTool.ts @@ -114,6 +114,9 @@ export class NewTaskTool extends BaseTool<"new_task"> { }) if (decision.action === "reject-nested") { + // Surface the rejection in the UI as well — tool results are not rendered. + // Structured payload so the webview can localize the detail text. + await task.say("inline_subtask_rejected", JSON.stringify({ reason: "nested" })) pushToolResult(formatResponse.toolError(decision.message)) return } @@ -121,11 +124,21 @@ export class NewTaskTool extends BaseTool<"new_task"> { if (decision.action === "flatten") { // Set the phase marker and let the tool_result double as the inline prompt. task.inlineSubtask = { message: unescapedMessage, todos: todoItems } + // Surface the auto-flatten in the UI — the model sees it via the tool result, + // but without this the user has no indication that a subtask now runs inline. + // Structured payload so the webview can localize the detail text. + await task.say("inline_subtask_started", JSON.stringify({ maxDepth: maxNestingDepth })) pushToolResult(decision.directive) return } if (decision.action === "reject-limit") { + // Surface the rejection in the UI as well — tool results are not rendered. + // Structured payload so the webview can localize the detail text. + await task.say( + "inline_subtask_rejected", + JSON.stringify({ reason: "limit", maxDepth: maxNestingDepth }), + ) pushToolResult(formatResponse.toolError(decision.message)) return } diff --git a/src/core/tools/__tests__/newTaskInlineFlatten.spec.ts b/src/core/tools/__tests__/newTaskInlineFlatten.spec.ts index 3c960966a9..890fe1bbaf 100644 --- a/src/core/tools/__tests__/newTaskInlineFlatten.spec.ts +++ b/src/core/tools/__tests__/newTaskInlineFlatten.spec.ts @@ -37,6 +37,7 @@ function makeTask(opts: { depth?: number; inlineSubtask?: InlineSubtask; provide didToolFailInCurrentTurn: false, recordToolError: vi.fn(), sayAndCreateMissingParamError: vi.fn().mockResolvedValue("missing param"), + say: vi.fn().mockResolvedValue(undefined), providerRef: { deref: () => opts.provider }, } return task as unknown as Task @@ -91,6 +92,9 @@ describe("NewTaskTool auto-flatten inline", () => { const pushed = pushToolResult.mock.calls[0][0] as string expect(pushed).toContain("auto-flattened") expect(pushed).toContain("do X") + // The auto-flatten is surfaced to the UI via a structured say payload (tool results are not rendered). + const say = (task as unknown as { say: ReturnType }).say + expect(say).toHaveBeenCalledWith("inline_subtask_started", JSON.stringify({ maxDepth: 2 })) }) it("rejects when over the limit and autoFlattenOnLimit is false (error result, no marker)", async () => { @@ -109,6 +113,9 @@ describe("NewTaskTool auto-flatten inline", () => { expect(task.inlineSubtask).toBeUndefined() const pushed = pushToolResult.mock.calls[0][0] as string expect(pushed.toLowerCase()).toContain("error") + // The rejection is surfaced to the UI via a structured say payload. + const say = (task as unknown as { say: ReturnType }).say + expect(say).toHaveBeenCalledWith("inline_subtask_rejected", JSON.stringify({ reason: "limit", maxDepth: 2 })) }) it("rejects a nested new_task while an inline phase is already active", async () => { @@ -128,6 +135,9 @@ describe("NewTaskTool auto-flatten inline", () => { expect(task.inlineSubtask?.message).toBe("outer") const pushed = pushToolResult.mock.calls[0][0] as string expect(pushed.toLowerCase()).toContain("error") + // The rejection is surfaced to the UI via a structured say payload. + const say = (task as unknown as { say: ReturnType }).say + expect(say).toHaveBeenCalledWith("inline_subtask_rejected", JSON.stringify({ reason: "nested" })) }) }) diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 3c48b2fdd1..a360ffe18f 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -73,6 +73,7 @@ import { ArrowRight, Check, OctagonX, + Settings, } from "lucide-react" import { cn } from "@/lib/utils" import { PathTooltip } from "../ui/PathTooltip" @@ -1067,6 +1068,79 @@ export const ChatRowContent = ({ )} ) + case "inline_subtask_started": { + // A subtask was auto-flattened and now runs inline in this conversation. + const started = safeJsonParse<{ maxDepth?: number }>(message.text) + return ( +
+
+ + + {t("chat:subtasks.inlineStarted")} + +
+
+

+ {started?.maxDepth != null + ? t("chat:subtasks.inlineStartedDetail", { maxDepth: started.maxDepth }) + : message.text} +

+ { + e.preventDefault() + vscode.postMessage({ + type: "switchTab", + tab: "settings", + values: { section: "contextManagement" }, + }) + }}> + + {t("chat:subtasks.inlineConfigure")} + +
+
+ ) + } + case "inline_subtask_rejected": { + // A nested new_task was rejected (an inline phase is already active, or the + // nesting limit was hit with auto-flatten disabled). + const rejected = safeJsonParse<{ reason?: string; maxDepth?: number }>(message.text) + return ( +
+
+ + + {t("chat:subtasks.inlineRejected")} + +
+
+

+ {rejected?.reason === "limit" + ? t("chat:subtasks.inlineRejectedLimitDetail", { maxDepth: rejected.maxDepth }) + : rejected?.reason === "nested" + ? t("chat:subtasks.inlineRejectedNestedDetail") + : message.text} +

+ { + e.preventDefault() + vscode.postMessage({ + type: "switchTab", + tab: "settings", + values: { section: "contextManagement" }, + }) + }}> + + {t("chat:subtasks.inlineConfigure")} + +
+
+ ) + } case "reasoning": return ( ({ + vscode: { + postMessage: (msg: unknown) => mockPostMessage(msg), + }, +})) + +// Mock i18n — the two inline-subtask banner titles plus a fallback to the key itself. +vi.mock("react-i18next", () => ({ + useTranslation: () => ({ + t: (key: string, options?: Record) => { + const map: Record = { + "chat:subtasks.inlineStarted": "Subtask flattened to inline", + "chat:subtasks.inlineRejected": "Nested subtask rejected", + "chat:subtasks.inlineConfigure": "Adjust task tree settings", + "chat:subtasks.inlineStartedDetail": + "Nesting limit {{maxDepth}} reached — subtask flattened and executing inline in this conversation.", + "chat:subtasks.inlineRejectedLimitDetail": + "Nesting limit {{maxDepth}} reached and auto-flatten is disabled. Continue working directly in the current conversation instead of delegating.", + "chat:subtasks.inlineRejectedNestedDetail": + "Cannot start a nested subtask while an inline subtask is already in progress. Complete the current inline subtask with attempt_completion first.", + } + const raw = map[key] ?? key + if (!options) return raw + // Substitute {{var}} placeholders from the options object. + return raw.replace(/\{\{(\w+)\}\}/g, (_, name: string) => String(options[name] ?? `{{${name}}}`)) + }, + i18n: { exists: () => true }, + }), + Trans: ({ children }: { children?: React.ReactNode }) => <>{children}, + initReactI18next: { type: "3rdParty", init: () => {} }, +})) + +// Mock extension state context +vi.mock("@src/context/ExtensionStateContext", () => ({ + useExtensionState: () => ({ + mcpServers: [], + alwaysAllowMcp: false, + currentCheckpoint: null, + mode: "code", + apiConfiguration: {}, + clineMessages: [] as ClineMessage[], + currentTaskItem: undefined, + }), +})) + +// Mock useSelectedModel hook +vi.mock("@src/components/ui/hooks/useSelectedModel", () => ({ + useSelectedModel: () => ({ info: { supportsImages: true } }), +})) + +function renderChatRow(message: ClineMessage) { + return render( + {}} + onSuggestionClick={() => {}} + onBatchFileResponse={() => {}} + onFollowUpUnmount={() => {}} + isFollowUpAnswered={false} + />, + ) +} + +describe("ChatRow - inline subtask banners", () => { + it("renders a distinct banner when a subtask is auto-flattened to inline", () => { + const message: ClineMessage = { + ts: Date.now(), + type: "say" as const, + say: "inline_subtask_started" as const, + text: JSON.stringify({ maxDepth: 2 }), + } + + renderChatRow(message) + + // Banner title (i18n) is present… + expect(screen.getByText("Subtask flattened to inline")).toBeInTheDocument() + // …and the localized detail text renders below it. + expect( + screen.getByText("Nesting limit 2 reached — subtask flattened and executing inline in this conversation."), + ).toBeInTheDocument() + }) + + it.each([ + [ + "nested", + JSON.stringify({ reason: "nested" }), + "Cannot start a nested subtask while an inline subtask is already in progress. Complete the current inline subtask with attempt_completion first.", + ], + [ + "limit", + JSON.stringify({ reason: "limit", maxDepth: 2 }), + "Nesting limit 2 reached and auto-flatten is disabled. Continue working directly in the current conversation instead of delegating.", + ], + ] as const)("renders a distinct banner when a new_task is rejected (%s)", (_reason, text, detail) => { + const message: ClineMessage = { + ts: Date.now(), + type: "say" as const, + say: "inline_subtask_rejected" as const, + text, + } + + renderChatRow(message) + + expect(screen.getByText("Nested subtask rejected")).toBeInTheDocument() + expect(screen.getByText(detail)).toBeInTheDocument() + }) + + describe("settings hint link", () => { + beforeEach(() => { + mockPostMessage.mockClear() + }) + + it.each(["inline_subtask_started", "inline_subtask_rejected"] as const)( + "deep-links the %s banner into the task-tree settings section", + (say) => { + const message: ClineMessage = { + ts: Date.now(), + type: "say" as const, + say, + text: "detail", + } + + renderChatRow(message) + + // The banner renders its settings-hint link… + const link = screen.getByText("Adjust task tree settings") + expect(link).toBeInTheDocument() + // …and clicking it switches to the settings tab, deep-linked to contextManagement. + fireEvent.click(link) + expect(mockPostMessage).toHaveBeenCalledWith({ + type: "switchTab", + tab: "settings", + values: { section: "contextManagement" }, + }) + }, + ) + }) + + it("does not render the banners for unrelated say types", () => { + const message: ClineMessage = { + ts: Date.now(), + type: "say" as const, + say: "text" as const, + text: "ordinary model text", + } + + renderChatRow(message) + + expect(screen.queryByText("Subtask flattened to inline")).not.toBeInTheDocument() + expect(screen.queryByText("Nested subtask rejected")).not.toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/components/settings/ContextManagementSettings.tsx b/webview-ui/src/components/settings/ContextManagementSettings.tsx index cf0f09e313..54aa1a05d6 100644 --- a/webview-ui/src/components/settings/ContextManagementSettings.tsx +++ b/webview-ui/src/components/settings/ContextManagementSettings.tsx @@ -442,8 +442,10 @@ export const ContextManagementSettings = ({ - {t("settings:taskTree.maxNestingDepth.label")} + label={t("settings:contextManagement.taskTree.maxNestingDepth.label")}> + + {t("settings:contextManagement.taskTree.maxNestingDepth.label")} +
{maxNestingDepth ?? DEFAULT_MAX_NESTING_DEPTH}
- {t("settings:taskTree.maxNestingDepth.description")} + {t("settings:contextManagement.taskTree.maxNestingDepth.description")}
+ label={t("settings:contextManagement.taskTree.autoFlattenOnLimit.label")}> setCachedStateField("autoFlattenOnLimit", e.target.checked)} data-testid="auto-flatten-on-limit-checkbox">
- {t("settings:taskTree.autoFlattenOnLimit.description")} + {t("settings:contextManagement.taskTree.autoFlattenOnLimit.description")}
diff --git a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx index b7fae7802b..e5a3519e96 100644 --- a/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/ContextManagementSettings.spec.tsx @@ -531,6 +531,16 @@ describe("ContextManagementSettings", () => { }) describe("taskTree settings", () => { + // Regression: the task-tree controls must resolve their i18n keys under + // contextManagement.taskTree.* (where the translations live), not a top-level + // taskTree.* — otherwise the raw key is rendered in the UI. + it("resolves task-tree labels via the nested contextManagement.taskTree path", () => { + render() + + expect(screen.getByText("settings:contextManagement.taskTree.maxNestingDepth.label")).toBeInTheDocument() + expect(screen.queryByText("settings:taskTree.maxNestingDepth.label")).not.toBeInTheDocument() + }) + it("renders max nesting depth slider with default value when unset", () => { render() diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index d5b63ea886..fcb0131432 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -270,7 +270,13 @@ "resultContent": "Resultats de la subtasca", "defaultResult": "Si us plau, continua amb la següent tasca.", "completionInstructions": "Subtasca completada! Pots revisar els resultats i suggerir correccions o següents passos. Si tot sembla correcte, confirma per tornar el resultat a la tasca principal.", - "goToSubtask": "Veure tasca" + "goToSubtask": "Veure tasca", + "inlineStarted": "Subtarea aplanada en línia", + "inlineRejected": "Subtarea niuada rebutjada", + "inlineConfigure": "Ajusta la configuració de l'arbre de tasques", + "inlineStartedDetail": "S'ha assolit el límit d'anidament {{maxDepth}} — la subtasca s'ha aplanat i s'executa en línia en aquesta conversa.", + "inlineRejectedLimitDetail": "S'ha assolit el límit d'anidament {{maxDepth}} i l'aplanament automàtic està desactivat. Continua treballant directament en la conversa actual en lloc de delegar.", + "inlineRejectedNestedDetail": "No es pot iniciar una subtasca anidada mentre n'hi ha una d'en línia en curs. Completa primer la subtasca en línia amb attempt_completion." }, "questions": { "hasQuestion": "Zoo té una pregunta" diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index ad94856234..f5efad9f3c 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -270,7 +270,13 @@ "resultContent": "Teilaufgabenergebnisse", "defaultResult": "Bitte fahre mit der nächsten Aufgabe fort.", "completionInstructions": "Teilaufgabe abgeschlossen! Du kannst die Ergebnisse überprüfen und Korrekturen oder nächste Schritte vorschlagen. Wenn alles gut aussieht, bestätige, um das Ergebnis an die übergeordnete Aufgabe zurückzugeben.", - "goToSubtask": "Aufgabe anzeigen" + "goToSubtask": "Aufgabe anzeigen", + "inlineStarted": "Unteraufgabe inline ausgeführt", + "inlineRejected": "Verschachtelte Unteraufgabe abgelehnt", + "inlineConfigure": "Aufgabenbaum-Einstellungen anpassen", + "inlineStartedDetail": "Nestigungsgrenze {{maxDepth}} erreicht — Unteraufgabe wurde eingeebnet und wird inline in diesem Gespräch ausgeführt.", + "inlineRejectedLimitDetail": "Nestigungsgrenze {{maxDepth}} erreicht und Auto-Einbiegen ist deaktiviert. Führe die Arbeit direkt in der aktuellen Konversation fort, statt zu delegieren.", + "inlineRejectedNestedDetail": "Während eine Inline-Unteraufgabe läuft, kann keine verschachtelte Unteraufgabe gestartet werden. Schließe zuerst die aktuelle Inline-Unteraufgabe mit attempt_completion ab." }, "questions": { "hasQuestion": "Zoo hat eine Frage" diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 89f6c2f488..325c18988f 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -312,7 +312,13 @@ "resultContent": "Subtask completed", "defaultResult": "Please continue to the next task.", "completionInstructions": "You can review the results and suggest any corrections or next steps. If everything looks good, confirm to return the result to the parent task.", - "goToSubtask": "View task" + "goToSubtask": "View task", + "inlineStarted": "Subtask flattened to inline", + "inlineRejected": "Nested subtask rejected", + "inlineConfigure": "Adjust task tree settings", + "inlineStartedDetail": "Nesting limit {{maxDepth}} reached — subtask flattened and executing inline in this conversation.", + "inlineRejectedLimitDetail": "Nesting limit {{maxDepth}} reached and auto-flatten is disabled. Continue working directly in the current conversation instead of delegating.", + "inlineRejectedNestedDetail": "Cannot start a nested subtask while an inline subtask is already in progress. Complete the current inline subtask with attempt_completion first." }, "questions": { "hasQuestion": "Zoo has a question" diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 7876845932..ef0b0b66ec 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -270,7 +270,13 @@ "resultContent": "Resultados de la subtarea", "defaultResult": "Por favor, continúa con la siguiente tarea.", "completionInstructions": "¡Subtarea completada! Puedes revisar los resultados y sugerir correcciones o próximos pasos. Si todo se ve bien, confirma para devolver el resultado a la tarea principal.", - "goToSubtask": "Ver tarea" + "goToSubtask": "Ver tarea", + "inlineStarted": "Subtarea aplanada en línea", + "inlineRejected": "Subtarea anidada rechazada", + "inlineConfigure": "Ajustar configuración del árbol de tareas", + "inlineStartedDetail": "Se alcanzó el límite de anidamiento {{maxDepth}} — la subtarea se aplanó y se ejecuta en línea en esta conversación.", + "inlineRejectedLimitDetail": "Se alcanzó el límite de anidamiento {{maxDepth}} y el aplanado automático está desactivado. Continúa trabajando directamente en la conversación actual en lugar de delegar.", + "inlineRejectedNestedDetail": "No se puede iniciar una subtarea anidada mientras hay una subtarea en línea en curso. Completa primero la subtarea en línea con attempt_completion." }, "questions": { "hasQuestion": "Zoo tiene una pregunta" diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 032d43ce27..d9106944e4 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -270,7 +270,13 @@ "resultContent": "Résultats de la sous-tâche", "defaultResult": "Veuillez continuer avec la tâche suivante.", "completionInstructions": "Sous-tâche terminée ! Vous pouvez examiner les résultats et suggérer des corrections ou les prochaines étapes. Si tout semble bon, confirmez pour retourner le résultat à la tâche parente.", - "goToSubtask": "Afficher la tâche" + "goToSubtask": "Afficher la tâche", + "inlineStarted": "Sous-tâche aplatie en ligne", + "inlineRejected": "Sous-tâche imbriquée rejetée", + "inlineConfigure": "Ajuster les paramètres de l'arborescence des tâches", + "inlineStartedDetail": "Limite d'imbrication {{maxDepth}} atteinte — la sous-tâche a été aplatie et s'exécute en ligne dans cette conversation.", + "inlineRejectedLimitDetail": "Limite d'imbrication {{maxDepth}} atteinte et l'aplatissement automatique est désactivé. Continuez à travailler directement dans la conversation actuelle au lieu de déléguer.", + "inlineRejectedNestedDetail": "Impossible de démarrer une sous-tâche imbriquée pendant qu'une sous-tâche en ligne est en cours. Terminez d'abord la sous-tâche en ligne avec attempt_completion." }, "questions": { "hasQuestion": "Zoo a une question" diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 94a805f328..a13b1de0fd 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -270,7 +270,13 @@ "resultContent": "उपकार्य परिणाम", "defaultResult": "कृपया अगले कार्य पर जारी रखें।", "completionInstructions": "उपकार्य पूर्ण! आप परिणामों की समीक्षा कर सकते हैं और सुधार या अगले चरण सुझा सकते हैं। यदि सब कुछ ठीक लगता है, तो मुख्य कार्य को परिणाम वापस करने के लिए पुष्टि करें।", - "goToSubtask": "कार्य देखें" + "goToSubtask": "कार्य देखें", + "inlineStarted": "उप-कार्य इनलाइन में सपाट किया गया", + "inlineRejected": "नेस्टेड उप-कार्य अस्वीकृत", + "inlineConfigure": "टास्क ट्री सेटिंग्स समायोजित करें", + "inlineStartedDetail": "नेस्टिंग सीमा {{maxDepth}} पहुँची — उप-कार्य सपाट कर दिया गया और इस संवाद में इनलाइन चलाया जा रहा है।", + "inlineRejectedLimitDetail": "नेस्टिंग सीमा {{maxDepth}} पहुँची और ऑटो-फ्लैटन बंद है। डेलीगेट करने के बजाय वर्तमान संवाद में सीधे काम जारी रखें।", + "inlineRejectedNestedDetail": "जब एक इनलाइन उप-कार्य प्रगति पर हो, तब एक नेस्टेड उप-कार्य शुरू नहीं किया जा सकता। पहले attempt_completion से वर्तमान इनलाइन उप-कार्य पूर्ण करें।" }, "questions": { "hasQuestion": "Zoo का एक प्रश्न है" diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index d58d80db00..70128776cd 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -321,7 +321,13 @@ "resultContent": "Hasil Subtugas", "defaultResult": "Silakan lanjutkan ke tugas berikutnya.", "completionInstructions": "Subtugas selesai! Kamu bisa meninjau hasilnya dan menyarankan koreksi atau langkah selanjutnya. Jika semuanya terlihat baik, konfirmasi untuk mengembalikan hasil ke tugas induk.", - "goToSubtask": "Lihat tugas" + "goToSubtask": "Lihat tugas", + "inlineStarted": "Subtugas diratakan inline", + "inlineRejected": "Subtugas bersarang ditolak", + "inlineConfigure": "Atur pengaturan pohon tugas", + "inlineStartedDetail": "Batas penempaan {{maxDepth}} tercapai — sub-tugas diratakan dan dijalankan inline dalam percakapan ini.", + "inlineRejectedLimitDetail": "Batas penempaan {{maxDepth}} tercapai dan auto-flatten nonaktif. Terus bekerja langsung di percakapan saat ini alih-alih mendelegasikan.", + "inlineRejectedNestedDetail": "Tidak dapat memulai sub-tugas bersarang saat sub-tugas inline sedang berjalan. Selesaikan dulu sub-tugas inline dengan attempt_completion." }, "questions": { "hasQuestion": "Zoo punya pertanyaan" diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 7c4c657b35..222435f640 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -270,7 +270,13 @@ "resultContent": "Risultati sottoattività", "defaultResult": "Per favore continua con la prossima attività.", "completionInstructions": "Sottoattività completata! Puoi rivedere i risultati e suggerire correzioni o prossimi passi. Se tutto sembra a posto, conferma per restituire il risultato all'attività principale.", - "goToSubtask": "Visualizza attività" + "goToSubtask": "Visualizza attività", + "inlineStarted": "Compito secondario appiattito in linea", + "inlineRejected": "Compito secondario annidato rifiutato", + "inlineConfigure": "Regola le impostazioni dell'albero dei compiti", + "inlineStartedDetail": "Limite di annidamento {{maxDepth}} raggiunto — il sotto-compito è stato appiattito e viene eseguito in linea in questa conversazione.", + "inlineRejectedLimitDetail": "Limite di annidamento {{maxDepth}} raggiunto e l'appiattimento automatico è disattivato. Continua a lavorare direttamente nella conversazione attuale invece di delegare.", + "inlineRejectedNestedDetail": "Non è possibile avviare un sotto-compito annidato mentre ne è in corso uno in linea. Completa prima il sotto-compito in linea con attempt_completion." }, "questions": { "hasQuestion": "Zoo ha una domanda" diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index f2f17b3fa5..ae6cb095c1 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -270,7 +270,13 @@ "resultContent": "サブタスク結果", "defaultResult": "次のタスクに進んでください。", "completionInstructions": "サブタスク完了!結果を確認し、修正や次のステップを提案できます。問題なければ、親タスクに結果を返すために確認してください。", - "goToSubtask": "タスクを表示" + "goToSubtask": "タスクを表示", + "inlineStarted": "サブタスクをインラインにフラット化しました", + "inlineRejected": "ネストしたサブタスクを拒否しました", + "inlineConfigure": "タスクツリー設定を調整する", + "inlineStartedDetail": "ネスト上限 {{maxDepth}} に到達 — サブタスクはフラット化され、この会話でインライン実行されています。", + "inlineRejectedLimitDetail": "ネスト上限 {{maxDepth}} に到達し、自動フラット化が無効です。委任する代わりに現在の会話で直接作業を続けてください。", + "inlineRejectedNestedDetail": "インラインサブタスクが進行中の間は、ネストしたサブタスクを開始できません。まず attempt_completion で現在のインラインサブタスクを完了してください。" }, "questions": { "hasQuestion": "Zooは質問があります" diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index 090bb1a706..a981a61632 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -270,7 +270,13 @@ "resultContent": "하위 작업 결과", "defaultResult": "다음 작업을 계속 진행해주세요.", "completionInstructions": "하위 작업 완료! 결과를 검토하고 수정 사항이나 다음 단계를 제안할 수 있습니다. 모든 것이 괜찮아 보이면, 부모 작업에 결과를 반환하기 위해 확인해주세요.", - "goToSubtask": "작업 보기" + "goToSubtask": "작업 보기", + "inlineStarted": "서브태스크를 인라인으로 평탄화했습니다", + "inlineRejected": "중첩 서브태스크가 거부되었습니다", + "inlineConfigure": "작업 트리 설정 조정", + "inlineStartedDetail": "네스팅 한도 {{maxDepth}} 도달 — 하위 작업이 평탄화되어 이 대화에서 인라인으로 실행 중입니다.", + "inlineRejectedLimitDetail": "네스팅 한도 {{maxDepth}}에 도달했으며 자동 평탄화가 비활성화되었습니다. 위임 대신 현재 대화에서 직접 작업을 계속하세요.", + "inlineRejectedNestedDetail": "인라인 하위 작업이 진행 중인 동안 중첩된 하위 작업을 시작할 수 없습니다. 먼저 attempt_completion으로 현재 인라인 하위 작업을 완료하세요." }, "questions": { "hasQuestion": "Zoo에게 질문이 있습니다" diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 0f1ce14084..218576fca2 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -265,7 +265,13 @@ "resultContent": "Subtaakresultaten", "defaultResult": "Ga verder met de volgende taak.", "completionInstructions": "Subtaak voltooid! Je kunt de resultaten bekijken en eventuele correcties of volgende stappen voorstellen. Als alles goed is, bevestig dan om het resultaat terug te sturen naar de hoofdtaak.", - "goToSubtask": "Taak weergeven" + "goToSubtask": "Taak weergeven", + "inlineStarted": "Onderopdracht inline afgevlakt", + "inlineRejected": "Geneste onderopdracht afgewezen", + "inlineConfigure": "Instellingen van de taakboom aanpassen", + "inlineStartedDetail": "Nestingslimiet {{maxDepth}} bereikt — de subtaak is afgevlakt en wordt inline in dit gesprek uitgevoerd.", + "inlineRejectedLimitDetail": "Nestingslimiet {{maxDepth}} bereikt en automatisch afvlakken is uitgeschakeld. Ga door met direct werken in het huidige gesprek in plaats van delegeren.", + "inlineRejectedNestedDetail": "Een geneste subtaak kan niet gestart worden terwijl een inline-subtaak bezig is. Voltooi eerst de huidige inline-subtaak met attempt_completion." }, "questions": { "hasQuestion": "Zoo heeft een vraag" diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index 53ae2013e1..6349118413 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -270,7 +270,13 @@ "resultContent": "Wyniki podzadania", "defaultResult": "Proszę kontynuować następne zadanie.", "completionInstructions": "Podzadanie zakończone! Możesz przejrzeć wyniki i zasugerować poprawki lub następne kroki. Jeśli wszystko wygląda dobrze, potwierdź, aby zwrócić wynik do zadania nadrzędnego.", - "goToSubtask": "Wyświetl zadanie" + "goToSubtask": "Wyświetl zadanie", + "inlineStarted": "Zadanie podrzędne spłaszczone do inline", + "inlineRejected": "Zagnieżdżone zadanie odrzucone", + "inlineConfigure": "Dostosuj ustawienia drzewa zadań", + "inlineStartedDetail": "Osiągnięto limit zagnieżdżania {{maxDepth}} — podzadanie zostało spłaszczone i jest wykonywane inline w tej rozmowie.", + "inlineRejectedLimitDetail": "Osiągnięto limit zagnieżdżania {{maxDepth}}, a automatyczne spłaszczanie jest wyłączone. Kontynuuj pracę bezpośrednio w bieżącej rozmowie zamiast delegować.", + "inlineRejectedNestedDetail": "Nie można uruchomić zagnieżdżonego podzadania, gdy trwa podzadanie inline. Najpierw zakończ bieżące podzadanie inline za pomocą attempt_completion." }, "questions": { "hasQuestion": "Zoo ma pytanie" diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 4769341a7b..b645bef1cc 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -270,7 +270,13 @@ "resultContent": "Resultados da subtarefa", "defaultResult": "Por favor, continue com a próxima tarefa.", "completionInstructions": "Subtarefa concluída! Você pode revisar os resultados e sugerir correções ou próximos passos. Se tudo parecer bom, confirme para retornar o resultado à tarefa principal.", - "goToSubtask": "Ver tarefa" + "goToSubtask": "Ver tarefa", + "inlineStarted": "Subtarefa achatada em linha", + "inlineRejected": "Subtarefa aninhada rejeitada", + "inlineConfigure": "Ajustar configurações da árvore de tarefas", + "inlineStartedDetail": "Limite de aninhamento {{maxDepth}} atingido — a subtarefa foi achatada e está sendo executada em linha nesta conversa.", + "inlineRejectedLimitDetail": "Limite de aninhamento {{maxDepth}} atingido e o achatamento automático está desativado. Continue trabalhando diretamente na conversa atual em vez de delegar.", + "inlineRejectedNestedDetail": "Não é possível iniciar uma subtarefa aninhada enquanto há uma subtarefa em linha em andamento. Conclua primeiro a subtarefa em linha com attempt_completion." }, "questions": { "hasQuestion": "Zoo tem uma pergunta" diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index de34a0e0b8..3d366bf9b7 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -266,7 +266,13 @@ "resultContent": "Результаты подзадачи", "defaultResult": "Пожалуйста, переходите к следующей задаче.", "completionInstructions": "Подзадача завершена! Вы можете просмотреть результаты и предложить исправления или следующие шаги. Если всё в порядке, подтвердите для возврата результата в родительскую задачу.", - "goToSubtask": "Просмотреть задачу" + "goToSubtask": "Просмотреть задачу", + "inlineStarted": "Подзадача выровнена в inline", + "inlineRejected": "Вложенная подзадача отклонена", + "inlineConfigure": "Настроить параметры дерева задач", + "inlineStartedDetail": "Достигнут предел вложенности {{maxDepth}} — подзадача выровнена и выполняется inline в этом диалоге.", + "inlineRejectedLimitDetail": "Достигнут предел вложенности {{maxDepth}}, а автовыравнивание отключено. Продолжайте работать непосредственно в текущем диалоге вместо делегирования.", + "inlineRejectedNestedDetail": "Нельзя запустить вложенную подзадачу, пока выполняется inline-подзадача. Сначала завершите текущую inline-подзадачу с помощью attempt_completion." }, "questions": { "hasQuestion": "У Zoo есть вопрос" diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 752e5bff9f..dc9d864587 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -271,7 +271,13 @@ "resultContent": "Alt Görev Sonuçları", "defaultResult": "Lütfen sonraki göreve devam edin.", "completionInstructions": "Alt görev tamamlandı! Sonuçları inceleyebilir ve düzeltmeler veya sonraki adımlar önerebilirsiniz. Her şey iyi görünüyorsa, sonucu üst göreve döndürmek için onaylayın.", - "goToSubtask": "Görevi görüntüle" + "goToSubtask": "Görevi görüntüle", + "inlineStarted": "Alt görev içine düzleştirildi", + "inlineRejected": "İç içe alt görev reddedildi", + "inlineConfigure": "Görev ağacı ayarlarını düzenle", + "inlineStartedDetail": "Yiyeşleme sınırı {{maxDepth}}'e ulaşıldı — alt görev düzleştirildi ve bu sohbette satır içi olarak çalıştırılıyor.", + "inlineRejectedLimitDetail": "Yiyeşleme sınırı {{maxDepth}}'e ulaşıldı ve otomatik düzleştirme kapalı. Devretmek yerine mevcut sohbette doğrudan çalışmaya devam edin.", + "inlineRejectedNestedDetail": "Satır içi bir alt görev sürerken iç içe geçmiş bir alt görev başlatılamaz. Önce attempt_completion ile mevcut satır içi alt görevi tamamlayın." }, "questions": { "hasQuestion": "Zoo'nun bir sorusu var" diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index 3caa0e8d3d..230ba46987 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -271,7 +271,13 @@ "resultContent": "Kết quả nhiệm vụ phụ", "defaultResult": "Vui lòng tiếp tục với nhiệm vụ tiếp theo.", "completionInstructions": "Nhiệm vụ phụ đã hoàn thành! Bạn có thể xem lại kết quả và đề xuất các sửa đổi hoặc bước tiếp theo. Nếu mọi thứ có vẻ tốt, hãy xác nhận để trả kết quả về nhiệm vụ chính.", - "goToSubtask": "Xem nhiệm vụ" + "goToSubtask": "Xem nhiệm vụ", + "inlineStarted": "Nhiệm vụ con đã được làm phẳng inline", + "inlineRejected": "Từ chối nhiệm vụ con lồng nhau", + "inlineConfigure": "Điều chỉnh cài đặt cây nhiệm vụ", + "inlineStartedDetail": "Đã đạt giới hạn lồng nhau {{maxDepth}} — tác vụ con đã được làm phẳng và chạy inline trong cuộc trò chuyện này.", + "inlineRejectedLimitDetail": "Đã đạt giới hạn lồng nhau {{maxDepth}} và tự động làm phẳng đang tắt. Tiếp tục làm việc trực tiếp trong cuộc trò chuyện hiện tại thay vì ủy quyền.", + "inlineRejectedNestedDetail": "Không thể bắt đầu tác vụ con lồng nhau khi một tác vụ con inline đang chạy. Hãy hoàn thành trước tác vụ inline hiện tại bằng attempt_completion." }, "questions": { "hasQuestion": "Zoo có một câu hỏi" diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 76989eb473..d6181bce17 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -271,7 +271,13 @@ "resultContent": "子任务结果", "defaultResult": "请继续下一个任务。", "completionInstructions": "子任务已完成!您可以查看结果并提出修改或下一步建议。如果一切正常,请确认以将结果返回给主任务。", - "goToSubtask": "查看任务" + "goToSubtask": "查看任务", + "inlineStarted": "子任务已扁平化为内联执行", + "inlineRejected": "嵌套子任务被拒绝", + "inlineConfigure": "调整任务树设置", + "inlineStartedDetail": "已达到嵌套上限 {{maxDepth}} — 子任务已扁平化,正在本对话中内联执行。", + "inlineRejectedLimitDetail": "已达到嵌套上限 {{maxDepth}},且自动扁平化已关闭。请直接在当前对话中继续工作,而非委派。", + "inlineRejectedNestedDetail": "当一个内联子任务正在进行时,无法启动嵌套的子任务。请先用 attempt_completion 完成当前的内联子任务。" }, "questions": { "hasQuestion": "Zoo有一个问题" diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index e096c27af4..4ce131819b 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -315,7 +315,13 @@ "resultContent": "子任務結果", "defaultResult": "請繼續下一個工作。", "completionInstructions": "子任務已完成!您可以檢閱結果並提出修正或後續步驟。如果一切順利,請確認以將結果回傳給主任務。", - "goToSubtask": "查看工作" + "goToSubtask": "查看工作", + "inlineStarted": "子任務已扁平化為內聯執行", + "inlineRejected": "巢狀子任務被拒絕", + "inlineConfigure": "調整任務樹設定", + "inlineStartedDetail": "已達到巢狀上限 {{maxDepth}} — 子任務已扁平化,正在本對話中內聯執行。", + "inlineRejectedLimitDetail": "已達到巢狀上限 {{maxDepth}},且自動扁平化已關閉。請直接在目前對話中繼續工作,而非委派。", + "inlineRejectedNestedDetail": "當一個內聯子任務進行中時,無法啟動巢狀的子任務。請先用 attempt_completion 完成目前的內聯子任務。" }, "questions": { "hasQuestion": "Zoo 有一個問題" diff --git a/webview-ui/src/utils/chatBatchingPredicates.ts b/webview-ui/src/utils/chatBatchingPredicates.ts index 9fe65a98fa..00d406ae5d 100644 --- a/webview-ui/src/utils/chatBatchingPredicates.ts +++ b/webview-ui/src/utils/chatBatchingPredicates.ts @@ -25,6 +25,8 @@ export const isBoundary = (msg: BatchableMessage): boolean => { (msg.say === "text" && !!msg.text?.trim()) || msg.say === "completion_result" || msg.say === "checkpoint_saved" || + msg.say === "inline_subtask_started" || + msg.say === "inline_subtask_rejected" || msg.say === "error" || msg.say === "condense_context" || msg.say === "codebase_search_result"