diff --git a/packages/types/src/message.ts b/packages/types/src/message.ts index e518972a1c..4b2aa0c280 100644 --- a/packages/types/src/message.ts +++ b/packages/types/src/message.ts @@ -170,6 +170,8 @@ export const clineSays = [ "user_edit_todos", "too_many_tools_warning", "tool", + "use_advisor_tool", + "advisor_tool_result", ] as const export const clineSaySchema = z.enum(clineSays) diff --git a/packages/types/src/vscode-extension-host.ts b/packages/types/src/vscode-extension-host.ts index b20539afe4..e5ecedb72e 100644 --- a/packages/types/src/vscode-extension-host.ts +++ b/packages/types/src/vscode-extension-host.ts @@ -842,6 +842,12 @@ export interface ClineSayTool { skill?: string } +export interface ClineAskUseAdvisorTool { + toolUseId: string + name: string + input: string // JSON-serialized input +} + export interface ClineAskUseMcpServer { serverName: string type: "use_mcp_tool" | "access_mcp_resource" diff --git a/src/api/providers/__tests__/anthropic.spec.ts b/src/api/providers/__tests__/anthropic.spec.ts index 7dbad50dcd..269b66a561 100644 --- a/src/api/providers/__tests__/anthropic.spec.ts +++ b/src/api/providers/__tests__/anthropic.spec.ts @@ -922,5 +922,151 @@ describe("AnthropicHandler", () => { arguments: '"London"}', }) }) + + it("should emit advisor_tool_use chunk with id from server_tool_use block", async () => { + mockCreate.mockImplementationOnce(async () => ({ + async *[Symbol.asyncIterator]() { + yield { + type: "message_start", + message: { usage: { input_tokens: 100, output_tokens: 50 } }, + } + yield { + type: "content_block_start", + index: 0, + content_block: { + type: "server_tool_use", + id: "srvtoolu_abc123", + name: "advisor", + input: {}, + }, + } + }, + })) + + const stream = handler.createMessage("system", [{ role: "user", content: "Hello" }], { + taskId: "test-task", + }) + + const chunks: any[] = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + const advisorUseChunk = chunks.find((c) => c.type === "advisor_tool_use") + expect(advisorUseChunk).toBeDefined() + expect(advisorUseChunk.id).toBe("srvtoolu_abc123") + expect(advisorUseChunk.name).toBe("advisor") + }) + + it("should emit advisor_tool_result chunk with tool_use_id and text content", async () => { + mockCreate.mockImplementationOnce(async () => ({ + async *[Symbol.asyncIterator]() { + yield { + type: "message_start", + message: { usage: { input_tokens: 100, output_tokens: 50 } }, + } + yield { + type: "content_block_start", + index: 1, + content_block: { + type: "advisor_tool_result", + tool_use_id: "srvtoolu_abc123", + content: { type: "advisor_result", text: "Use channel-based coordination." }, + }, + } + }, + })) + + const stream = handler.createMessage("system", [{ role: "user", content: "Hello" }], { + taskId: "test-task", + }) + + const chunks: any[] = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + const resultChunk = chunks.find((c) => c.type === "advisor_tool_result") + expect(resultChunk).toBeDefined() + expect(resultChunk.tool_use_id).toBe("srvtoolu_abc123") + expect(resultChunk.content).toBe("Use channel-based coordination.") + // rawContent must carry the verbatim object for round-tripping to the API + expect(resultChunk.rawContent).toEqual({ type: "advisor_result", text: "Use channel-based coordination." }) + }) + + it("should extract text from advisor_result object content shape", async () => { + mockCreate.mockImplementationOnce(async () => ({ + async *[Symbol.asyncIterator]() { + yield { + type: "message_start", + message: { usage: { input_tokens: 100, output_tokens: 50 } }, + } + yield { + type: "content_block_start", + index: 0, + content_block: { + type: "advisor_tool_result", + tool_use_id: "srvtoolu_xyz", + content: { type: "advisor_result", text: "Plan: do X then Y." }, + }, + } + }, + })) + + const stream = handler.createMessage("system", [{ role: "user", content: "Plan?" }], { + taskId: "test-task", + }) + + const chunks: any[] = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + const resultChunk = chunks.find((c) => c.type === "advisor_tool_result") + expect(resultChunk).toBeDefined() + expect(resultChunk.content).toBe("Plan: do X then Y.") + // rawContent must be the verbatim object, not just the extracted text + expect(resultChunk.rawContent).toEqual({ type: "advisor_result", text: "Plan: do X then Y." }) + }) + + it("should emit empty string for encrypted advisor_redacted_result content", async () => { + mockCreate.mockImplementationOnce(async () => ({ + async *[Symbol.asyncIterator]() { + yield { + type: "message_start", + message: { usage: { input_tokens: 100, output_tokens: 50 } }, + } + yield { + type: "content_block_start", + index: 0, + content_block: { + type: "advisor_tool_result", + tool_use_id: "srvtoolu_redacted", + content: { type: "advisor_redacted_result", encrypted_content: "opaque-blob" }, + }, + } + }, + })) + + const stream = handler.createMessage("system", [{ role: "user", content: "Plan?" }], { + taskId: "test-task", + }) + + const chunks: any[] = [] + for await (const chunk of stream) { + chunks.push(chunk) + } + + const resultChunk = chunks.find((c) => c.type === "advisor_tool_result") + expect(resultChunk).toBeDefined() + expect(resultChunk.tool_use_id).toBe("srvtoolu_redacted") + // Encrypted content has no text field — content should be empty string + expect(resultChunk.content).toBe("") + // rawContent must carry the verbatim encrypted object for round-tripping + expect(resultChunk.rawContent).toEqual({ + type: "advisor_redacted_result", + encrypted_content: "opaque-blob", + }) + }) }) }) diff --git a/src/api/providers/anthropic.ts b/src/api/providers/anthropic.ts index 010d0df38d..009fbc1145 100644 --- a/src/api/providers/anthropic.ts +++ b/src/api/providers/anthropic.ts @@ -272,8 +272,9 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa case "message_stop": // No usage data, just an indicator that the message is done. break - case "content_block_start": - switch (chunk.content_block.type) { + case "content_block_start": { + const contentBlock = chunk.content_block as any + switch (contentBlock.type) { case "thinking": // We may receive multiple text blocks, in which // case just insert a line break between them. @@ -281,7 +282,7 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa yield { type: "reasoning", text: "\n" } } - yield { type: "reasoning", text: chunk.content_block.thinking } + yield { type: "reasoning", text: contentBlock.thinking } break case "text": // We may receive multiple text blocks, in which @@ -290,21 +291,67 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa yield { type: "text", text: "\n" } } - yield { type: "text", text: chunk.content_block.text } + yield { type: "text", text: contentBlock.text } break case "tool_use": { // Emit initial tool call partial with id and name yield { type: "tool_call_partial", index: chunk.index, - id: chunk.content_block.id, - name: chunk.content_block.name, + id: contentBlock.id, + name: contentBlock.name, arguments: undefined, } break } + case "server_tool_use": { + const { id, name, input } = contentBlock + yield { + type: "advisor_tool_use", + id, + name, + input: typeof input === "object" ? JSON.stringify(input) : String(input ?? "{}"), + } + break + } + case "advisor_tool_result": { + const block = contentBlock as { + type: "advisor_tool_result" + tool_use_id: string + content: + | string + | { type: string; text?: string } + | Array<{ type: string; text?: string }> + | undefined + } + const rawContent = block.content + let text: string + if (typeof rawContent === "string") { + text = rawContent + } else if (Array.isArray(rawContent)) { + text = rawContent + .filter((b) => b.type === "text") + .map((b) => b.text ?? "") + .join("\n") + } else if (rawContent && typeof rawContent === "object" && "text" in rawContent) { + // advisor_result shape: { type: "advisor_result", text: "..." } + text = (rawContent as { text?: string }).text ?? "" + } else { + text = "" + } + yield { + type: "advisor_tool_result", + tool_use_id: block.tool_use_id, + content: text, + // Pass through the verbatim content object so it can be + // round-tripped on subsequent turns as the Anthropic API requires. + rawContent: rawContent, + } + break + } } break + } case "content_block_delta": switch (chunk.delta.type) { case "thinking_delta": diff --git a/src/api/transform/stream.ts b/src/api/transform/stream.ts index 960ebbe770..daa37fbcdc 100644 --- a/src/api/transform/stream.ts +++ b/src/api/transform/stream.ts @@ -11,6 +11,8 @@ export type ApiStreamChunk = | ApiStreamToolCallDeltaChunk | ApiStreamToolCallEndChunk | ApiStreamToolCallPartialChunk + | ApiStreamAdvisorToolUseChunk + | ApiStreamAdvisorToolResultChunk | ApiStreamError export interface ApiStreamError { @@ -107,6 +109,22 @@ export interface ApiStreamToolCallPartialChunk { arguments?: string } +export interface ApiStreamAdvisorToolUseChunk { + type: "advisor_tool_use" + id: string + name: string + input: string // JSON-serialized +} + +export interface ApiStreamAdvisorToolResultChunk { + type: "advisor_tool_result" + tool_use_id: string + /** Extracted text for display purposes */ + content: string + /** Verbatim original content object from the API, for round-tripping on subsequent turns */ + rawContent: unknown +} + export interface GroundingSource { title: string url: string diff --git a/src/core/task/Task.ts b/src/core/task/Task.ts index 005bb0f292..94d3f2d28d 100644 --- a/src/core/task/Task.ts +++ b/src/core/task/Task.ts @@ -35,6 +35,7 @@ import { type ModelInfo, type ClineApiReqCancelReason, type ClineApiReqInfo, + type ClineAskUseAdvisorTool, RooCodeEventName, TelemetryEventName, TaskStatus, @@ -2792,6 +2793,18 @@ export class Task extends EventEmitter implements TaskLike { let assistantMessage = "" let reasoningMessage = "" let pendingGroundingSources: GroundingSource[] = [] + let hasAdvisorEvents = false + // Accumulate advisor blocks in arrival order so they can be saved to API history. + // Anthropic requires the full assistant content (including server_tool_use / + // advisor_tool_result blocks) to be round-tripped on subsequent turns. + const pendingAdvisorBlocks: Array<{ + type: "server_tool_use" | "advisor_tool_result" + id?: string + name?: string + input?: Record + tool_use_id?: string + content?: unknown + }> = [] this.isStreaming = true try { @@ -3014,6 +3027,37 @@ export class Task extends EventEmitter implements TaskLike { presentAssistantMessage(this) break } + case "advisor_tool_use": { + hasAdvisorEvents = true + pendingAdvisorBlocks.push({ + type: "server_tool_use", + id: chunk.id, + name: chunk.name, + input: + typeof chunk.input === "string" ? {} : (chunk.input as Record), + }) + const payload: ClineAskUseAdvisorTool = { + toolUseId: chunk.id, + name: chunk.name, + input: chunk.input, + } + await this.say("use_advisor_tool", JSON.stringify(payload)) + break + } + case "advisor_tool_result": { + hasAdvisorEvents = true + pendingAdvisorBlocks.push({ + type: "advisor_tool_result", + tool_use_id: chunk.tool_use_id, + // Use rawContent (the verbatim original object from the API) for + // round-tripping to Anthropic. The API requires content to be the + // original discriminated union object (e.g. { type: "advisor_result", text: "..." }) + // not a plain string. chunk.content is the extracted text for display only. + content: chunk.rawContent, + }) + await this.say("advisor_tool_result", chunk.content) + break + } case "text": { assistantMessage += chunk.text @@ -3410,14 +3454,14 @@ export class Task extends EventEmitter implements TaskLike { // the assistant message is already in history. Otherwise, tool_result blocks would appear // BEFORE their corresponding tool_use blocks, causing API errors. - // Check if we have any content to process (text or tool uses) + // Check if we have any content to process (text, tool uses, or advisor interactions) const hasTextContent = assistantMessage.length > 0 const hasToolUses = this.assistantMessageContent.some( (block) => block.type === "tool_use" || block.type === "mcp_tool_use", ) - if (hasTextContent || hasToolUses) { + if (hasTextContent || hasToolUses || hasAdvisorEvents) { // Reset counter when we get a successful response with content this.consecutiveNoAssistantMessagesCount = 0 // Display grounding sources to the user if they exist @@ -3505,6 +3549,13 @@ export class Task extends EventEmitter implements TaskLike { } } + // Append advisor blocks (server_tool_use / advisor_tool_result) that arrived during + // this stream. The Anthropic API requires these to be round-tripped verbatim on + // subsequent turns; omitting them causes a 400 invalid_request_error. + for (const advisorBlock of pendingAdvisorBlocks) { + assistantContent.push(advisorBlock as unknown as Anthropic.ToolUseBlockParam) + } + // Enforce new_task isolation: if new_task is called alongside other tools, // truncate any tools that come after it and inject error tool_results. // This prevents orphaned tools when delegation disposes the parent task. diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 1f3f8f5a4f..9fa9dcaa0e 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -263,6 +263,8 @@ export const ChatRowContent = ({ const isMcpServerResponding = isLast && lastModifiedMessage?.say === "mcp_server_request_started" + const isAdvisorResponding = isLast && message.say === "use_advisor_tool" + const type = message.type === "ask" ? message.ask : message.say const normalColor = "var(--vscode-foreground)" @@ -1040,6 +1042,29 @@ export const ChatRowContent = ({ )} ) + case "use_advisor_tool": + return ( +
+ {isAdvisorResponding ? ( + + ) : ( + + )} + {t("chat:advisor.isConsulting")} +
+ ) + case "advisor_tool_result": + return ( +
+
+ + {t("chat:advisor.resultLabel")} +
+
+ +
+
+ ) case "reasoning": return ( ({ + useTranslation: () => ({ + t: (key: string) => { + const map: Record = { + "chat:advisor.isConsulting": "Consulting advisor...", + "chat:advisor.resultLabel": "Advisor response", + } + return map[key] || key + }, + }), + Trans: ({ children }: { children?: React.ReactNode }) => <>{children}, + initReactI18next: { type: "3rdParty", init: () => {} }, +})) + +// Mock CodeBlock (avoid ESM/highlighter costs) +vi.mock("@src/components/common/CodeBlock", () => ({ + default: () => null, +})) + +// Mock VSCodeBadge +vi.mock("@vscode/webview-ui-toolkit/react", () => ({ + VSCodeBadge: ({ children, ...props }: { children: React.ReactNode }) => {children}, +})) + +const queryClient = new QueryClient() + +function renderChatRow(message: ClineMessage, isLast = false) { + return render( + + + {}} + onSuggestionClick={() => {}} + onBatchFileResponse={() => {}} + onFollowUpUnmount={() => {}} + isFollowUpAnswered={false} + /> + + , + ) +} + +describe("ChatRow - advisor tool messages", () => { + beforeEach(() => { + vi.clearAllMocks() + }) + + it("renders use_advisor_tool as a single header line with no badge", () => { + const message: ClineMessage = { + type: "say", + say: "use_advisor_tool", + ts: Date.now(), + partial: false, + text: JSON.stringify({ + toolUseId: "tool-1", + name: "advisor", + input: "{}", + }), + } + + renderChatRow(message) + + expect(screen.getByText("Consulting advisor...")).toBeInTheDocument() + // No badge rendered — the "advisor" text should NOT appear + expect(screen.queryByText("advisor")).not.toBeInTheDocument() + }) + + it("renders use_advisor_tool with non-empty input as a single header line (no input shown)", () => { + const message: ClineMessage = { + type: "say", + say: "use_advisor_tool", + ts: Date.now(), + partial: false, + text: JSON.stringify({ + toolUseId: "tool-2", + name: "advisor", + input: '{"query":"review this function"}', + }), + } + + renderChatRow(message) + + expect(screen.getByText("Consulting advisor...")).toBeInTheDocument() + // Input text is NOT rendered in the simplified header-only view + expect(screen.queryByText('{"query":"review this function"}')).not.toBeInTheDocument() + }) + + it("renders advisor_tool_result with header and content box", () => { + const message: ClineMessage = { + type: "say", + say: "advisor_tool_result", + ts: Date.now(), + partial: false, + text: "The code looks good overall.", + } + + renderChatRow(message) + + expect(screen.getByText("Advisor response")).toBeInTheDocument() + }) + + it("renders use_advisor_tool header even with invalid JSON (no early return)", () => { + const message: ClineMessage = { + type: "say", + say: "use_advisor_tool", + ts: Date.now(), + partial: false, + text: "not-valid-json", + } + + renderChatRow(message) + + // Header still renders — no JSON parsing required in simplified view + expect(screen.getByText("Consulting advisor...")).toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/i18n/locales/ca/chat.json b/webview-ui/src/i18n/locales/ca/chat.json index 57f0acecd0..88aaeba9ff 100644 --- a/webview-ui/src/i18n/locales/ca/chat.json +++ b/webview-ui/src/i18n/locales/ca/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Afegeix el missatge a la cua (s'enviarà quan acabi la tasca actual)", "scrollToBottom": "Desplaça't al final del xat", "about": "Roo Code és tot un equip de desenvolupament d'IA al teu editor.", + "advisor": { + "isConsulting": "Consultant l'assessor...", + "resultLabel": "Resposta de l'assessor" + }, "docs": "Consulta els nostres documents per a més informació.", "onboarding": "La teva llista de tasques en aquest espai de treball està buida.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/de/chat.json b/webview-ui/src/i18n/locales/de/chat.json index 727ec44c2d..dd8aeff76a 100644 --- a/webview-ui/src/i18n/locales/de/chat.json +++ b/webview-ui/src/i18n/locales/de/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Nachricht zur Warteschlange hinzufügen (wird nach Abschluss der aktuellen Aufgabe gesendet)", "scrollToBottom": "Zum Chat-Ende scrollen", "about": "Roo Code ist ein ganzes KI-Entwicklerteam in deinem Editor.", + "advisor": { + "isConsulting": "Berater wird konsultiert...", + "resultLabel": "Berater-Antwort" + }, "docs": "Schau in unsere Dokumentation, um mehr zu erfahren.", "onboarding": "Deine Aufgabenliste in diesem Arbeitsbereich ist leer.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/en/chat.json b/webview-ui/src/i18n/locales/en/chat.json index 81338ad786..12514188be 100644 --- a/webview-ui/src/i18n/locales/en/chat.json +++ b/webview-ui/src/i18n/locales/en/chat.json @@ -125,6 +125,10 @@ }, "scrollToBottom": "Scroll to bottom of chat", "about": "Roo is a whole AI dev team in your editor", + "advisor": { + "isConsulting": "Consulting advisor...", + "resultLabel": "Advisor response" + }, "docs": "Check our docs to get started", "onboarding": "What would you like to do?", "rooTips": { diff --git a/webview-ui/src/i18n/locales/es/chat.json b/webview-ui/src/i18n/locales/es/chat.json index 1aa49f74f0..d2589d431a 100644 --- a/webview-ui/src/i18n/locales/es/chat.json +++ b/webview-ui/src/i18n/locales/es/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Agregar mensaje a la cola (se enviará después de que termine la tarea actual)", "scrollToBottom": "Desplazarse al final del chat", "about": "Roo Code es todo un equipo de desarrollo de IA en tu editor.", + "advisor": { + "isConsulting": "Consultando asesor...", + "resultLabel": "Respuesta del asesor" + }, "docs": "Consulta nuestra documentación para saber más.", "onboarding": "Tu lista de tareas en este espacio de trabajo está vacía.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/fr/chat.json b/webview-ui/src/i18n/locales/fr/chat.json index 316a846199..0e74acc6db 100644 --- a/webview-ui/src/i18n/locales/fr/chat.json +++ b/webview-ui/src/i18n/locales/fr/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Ajouter le message à la file d'attente (sera envoyé après la fin de la tâche en cours)", "scrollToBottom": "Défiler jusqu'au bas du chat", "about": "Roo Code est une équipe complète de développeurs IA dans votre éditeur.", + "advisor": { + "isConsulting": "Consultation du conseiller...", + "resultLabel": "Réponse du conseiller" + }, "docs": "Consultez notre documentation pour en savoir plus.", "onboarding": "Votre liste de tâches dans cet espace de travail est vide.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/hi/chat.json b/webview-ui/src/i18n/locales/hi/chat.json index 410cd829ad..dc31ed73f9 100644 --- a/webview-ui/src/i18n/locales/hi/chat.json +++ b/webview-ui/src/i18n/locales/hi/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "संदेश को कतार में जोड़ें (वर्तमान कार्य पूरा होने के बाद भेजा जाएगा)", "scrollToBottom": "चैट के निचले हिस्से तक स्क्रॉल करें", "about": "Roo Code आपके संपादक में एक पूरी AI देव टीम है।", + "advisor": { + "isConsulting": "सलाहकार से परामर्श...", + "resultLabel": "सलाहकार प्रतिक्रिया" + }, "docs": "और जानने के लिए हमारे दस्तावेज़ देखें।", "onboarding": "इस कार्यक्षेत्र में आपकी कार्य सूची खाली है।", "rooTips": { diff --git a/webview-ui/src/i18n/locales/id/chat.json b/webview-ui/src/i18n/locales/id/chat.json index c499024e4c..54ad312e1a 100644 --- a/webview-ui/src/i18n/locales/id/chat.json +++ b/webview-ui/src/i18n/locales/id/chat.json @@ -128,6 +128,10 @@ "enqueueMessage": "Tambahkan pesan ke antrean (akan dikirim setelah tugas saat ini selesai)", "scrollToBottom": "Gulir ke bawah chat", "about": "Roo Code adalah seluruh tim pengembang AI di editor Anda.", + "advisor": { + "isConsulting": "Berkonsultasi dengan penasihat...", + "resultLabel": "Respons penasihat" + }, "docs": "Lihat dokumentasi kami untuk mempelajari lebih lanjut.", "onboarding": "Daftar tugas Anda di ruang kerja ini kosong.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/it/chat.json b/webview-ui/src/i18n/locales/it/chat.json index 006ee09a89..9084524ff1 100644 --- a/webview-ui/src/i18n/locales/it/chat.json +++ b/webview-ui/src/i18n/locales/it/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Aggiungi il messaggio alla coda (sarà inviato dopo che l'attività corrente sarà terminata)", "scrollToBottom": "Scorri fino alla fine della chat", "about": "Roo Code è un intero team di sviluppo AI nel tuo editor.", + "advisor": { + "isConsulting": "Consultazione del consulente...", + "resultLabel": "Risposta del consulente" + }, "docs": "Consulta la nostra documentazione per saperne di più.", "onboarding": "La tua lista di attività in questo spazio di lavoro è vuota.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/ja/chat.json b/webview-ui/src/i18n/locales/ja/chat.json index d8bea631f3..6a6a7a14d5 100644 --- a/webview-ui/src/i18n/locales/ja/chat.json +++ b/webview-ui/src/i18n/locales/ja/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "メッセージをキューに追加(現在のタスク完了後に送信されます)", "scrollToBottom": "チャットの最下部にスクロール", "about": "Roo Codeは、エディタに常駐するAI開発チームです。", + "advisor": { + "isConsulting": "アドバイザーに相談中...", + "resultLabel": "アドバイザーからの回答" + }, "docs": "詳細については、ドキュメントをご確認ください。", "onboarding": "このワークスペースのタスクリストは空です。", "rooTips": { diff --git a/webview-ui/src/i18n/locales/ko/chat.json b/webview-ui/src/i18n/locales/ko/chat.json index c16ac36402..c37a5f2ff1 100644 --- a/webview-ui/src/i18n/locales/ko/chat.json +++ b/webview-ui/src/i18n/locales/ko/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "메시지를 대기열에 추가 (현재 작업 완료 후 전송)", "scrollToBottom": "채팅 하단으로 스크롤", "about": "Roo Code는 편집기 안에 있는 전체 AI 개발팀입니다.", + "advisor": { + "isConsulting": "자문가와 상담 중...", + "resultLabel": "자문가 응답" + }, "docs": "더 알아보려면 문서를 확인하세요.", "onboarding": "이 작업 공간의 작업 목록이 비어 있습니다.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/nl/chat.json b/webview-ui/src/i18n/locales/nl/chat.json index 19a128f29d..f3d3a7c7a5 100644 --- a/webview-ui/src/i18n/locales/nl/chat.json +++ b/webview-ui/src/i18n/locales/nl/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Bericht aan de wachtrij toevoegen (wordt verzonden nadat de huidige taak is voltooid)", "scrollToBottom": "Scroll naar onderaan de chat", "about": "Roo Code is een heel AI-ontwikkelteam in je editor.", + "advisor": { + "isConsulting": "Raadpleging van adviseur...", + "resultLabel": "Antwoord van adviseur" + }, "docs": "Bekijk onze documentatie voor meer informatie.", "onboarding": "Je takenlijst in deze werkruimte is leeg.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/pl/chat.json b/webview-ui/src/i18n/locales/pl/chat.json index efbb47dad6..fcfb7ce08c 100644 --- a/webview-ui/src/i18n/locales/pl/chat.json +++ b/webview-ui/src/i18n/locales/pl/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Dodaj wiadomość do kolejki (zostanie wysłana po zakończeniu bieżącego zadania)", "scrollToBottom": "Przewiń do dołu czatu", "about": "Roo Code to cały zespół deweloperów AI w Twoim edytorze.", + "advisor": { + "isConsulting": "Konsultowanie się z doradcą...", + "resultLabel": "Odpowiedź doradcy" + }, "docs": "Sprawdź naszą dokumentację, aby dowiedzieć się więcej.", "onboarding": "Twoja lista zadań w tym obszarze roboczym jest pusta.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/pt-BR/chat.json b/webview-ui/src/i18n/locales/pt-BR/chat.json index 6db70b0342..9805d3152e 100644 --- a/webview-ui/src/i18n/locales/pt-BR/chat.json +++ b/webview-ui/src/i18n/locales/pt-BR/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Adicionar mensagem à fila (será enviada após a conclusão da tarefa atual)", "scrollToBottom": "Rolar para o final do chat", "about": "Roo Code é uma equipe inteira de desenvolvimento de IA em seu editor.", + "advisor": { + "isConsulting": "Consultando conselheiro...", + "resultLabel": "Resposta do conselheiro" + }, "docs": "Confira nossa documentação para saber mais.", "onboarding": "Sua lista de tarefas neste espaço de trabalho está vazia.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/ru/chat.json b/webview-ui/src/i18n/locales/ru/chat.json index 50864bff80..746b73e47c 100644 --- a/webview-ui/src/i18n/locales/ru/chat.json +++ b/webview-ui/src/i18n/locales/ru/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Добавить сообщение в очередь (будет отправлено после завершения текущей задачи)", "scrollToBottom": "Прокрутить чат вниз", "about": "Roo Code — это целая команда разработчиков ИИ в вашем редакторе.", + "advisor": { + "isConsulting": "Консультируемся с советником...", + "resultLabel": "Ответ советника" + }, "docs": "Ознакомьтесь с нашей документацией, чтобы узнать больше.", "onboarding": "Ваш список задач в этом рабочем пространстве пуст.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/tr/chat.json b/webview-ui/src/i18n/locales/tr/chat.json index 545e61d50e..eb2c2e3d24 100644 --- a/webview-ui/src/i18n/locales/tr/chat.json +++ b/webview-ui/src/i18n/locales/tr/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Mesajı kuyruğa ekle (mevcut görev tamamlandıktan sonra gönderilecek)", "scrollToBottom": "Sohbetin altına kaydır", "about": "Roo Code, düzenleyicinizdeki bütün bir yapay zeka geliştirme ekibidir.", + "advisor": { + "isConsulting": "Danışmana danışılıyor...", + "resultLabel": "Danışman yanıtı" + }, "docs": "Daha fazla bilgi için belgelerimize göz atın.", "onboarding": "Bu çalışma alanındaki görev listeniz boş.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/vi/chat.json b/webview-ui/src/i18n/locales/vi/chat.json index e36c58b54a..78928641e8 100644 --- a/webview-ui/src/i18n/locales/vi/chat.json +++ b/webview-ui/src/i18n/locales/vi/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "Thêm tin nhắn vào hàng đợi (sẽ gửi sau khi nhiệm vụ hiện tại hoàn tất)", "scrollToBottom": "Cuộn xuống cuối cuộc trò chuyện", "about": "Roo Code là một đội ngũ phát triển AI đầy đủ trong trình chỉnh sửa của bạn.", + "advisor": { + "isConsulting": "Đang tư vấn với cố vấn...", + "resultLabel": "Phản hồi từ cố vấn" + }, "docs": "Kiểm tra tài liệu của chúng tôi để tìm hiểu thêm.", "onboarding": "Danh sách nhiệm vụ của bạn trong không gian làm việc này đang trống.", "rooTips": { diff --git a/webview-ui/src/i18n/locales/zh-CN/chat.json b/webview-ui/src/i18n/locales/zh-CN/chat.json index 3e6f223e71..1e8b41a8bb 100644 --- a/webview-ui/src/i18n/locales/zh-CN/chat.json +++ b/webview-ui/src/i18n/locales/zh-CN/chat.json @@ -98,6 +98,10 @@ "enqueueMessage": "将消息加入队列(当前任务完成后发送)", "scrollToBottom": "滚动到聊天底部", "about": "Roo Code 是您编辑器中的整个 AI 开发团队。", + "advisor": { + "isConsulting": "正在咨询顾问...", + "resultLabel": "顾问回复" + }, "docs": "查看我们的 文档 了解更多信息。", "onboarding": "此工作区中的任务列表为空。", "rooTips": { diff --git a/webview-ui/src/i18n/locales/zh-TW/chat.json b/webview-ui/src/i18n/locales/zh-TW/chat.json index 4fe9c57843..53e2f441a0 100644 --- a/webview-ui/src/i18n/locales/zh-TW/chat.json +++ b/webview-ui/src/i18n/locales/zh-TW/chat.json @@ -125,6 +125,10 @@ }, "scrollToBottom": "捲動至對話底部", "about": "Roo 是編輯器中的完整 AI 開發團隊。", + "advisor": { + "isConsulting": "正在諮詢顧問...", + "resultLabel": "顧問回應" + }, "docs": "請參閱 說明文件 開始使用。", "onboarding": "想要做什麼呢?", "rooTips": {