This commit is contained in:
TDFSE 2026-04-11 00:32:39 -04:00 committed by GitHub
commit 1729cc7122
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 851 additions and 9 deletions

View file

@ -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)

View file

@ -98,6 +98,7 @@ export const modelInfoSchema = z.object({
outputPrice: z.number().optional(),
cacheWritesPrice: z.number().optional(),
cacheReadsPrice: z.number().optional(),
supportsAdvisorTool: z.boolean().optional(),
longContextPricing: z
.object({
thresholdTokens: z.number(),

View file

@ -207,6 +207,9 @@ const anthropicSchema = apiModelIdProviderModelSchema.extend({
anthropicBaseUrl: z.string().optional(),
anthropicUseAuthToken: z.boolean().optional(),
anthropicBeta1MContext: z.boolean().optional(), // Enable 'context-1m-2025-08-07' beta for 1M context window.
anthropicAdvisorEnabled: z.boolean().optional(),
anthropicAdvisorModel: z.string().optional(),
anthropicAdvisorMaxUses: z.number().int().min(1).optional(),
})
const openRouterSchema = baseProviderSettingsSchema.extend({

View file

@ -17,6 +17,7 @@ export const anthropicModels = {
cacheWritesPrice: 3.75, // $3.75 per million tokens
cacheReadsPrice: 0.3, // $0.30 per million tokens
supportsReasoningBudget: true,
supportsAdvisorTool: true,
// Tiered pricing for extended context (requires beta flag 'context-1m-2025-08-07')
tiers: [
{
@ -80,6 +81,7 @@ export const anthropicModels = {
cacheWritesPrice: 6.25, // $6.25 per million tokens
cacheReadsPrice: 0.5, // $0.50 per million tokens
supportsReasoningBudget: true,
supportsAdvisorTool: true,
// Tiered pricing for extended context (requires beta flag)
tiers: [
{
@ -196,6 +198,7 @@ export const anthropicModels = {
cacheWritesPrice: 1.25,
cacheReadsPrice: 0.1,
supportsReasoningBudget: true,
supportsAdvisorTool: true,
description:
"Claude Haiku 4.5 delivers near-frontier intelligence at lightning speeds with extended thinking, vision, and multilingual support.",
},

View file

@ -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"

View file

@ -209,6 +209,141 @@ describe("AnthropicHandler", () => {
const requestOptions = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[1]
expect(requestOptions?.headers?.["anthropic-beta"]).toContain("context-1m-2025-08-07")
})
describe("advisor tool feature", () => {
const systemPrompt = "You are a helpful assistant."
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "user",
content: [{ type: "text" as const, text: "Hello" }],
},
]
it("should include advisor tool beta header when advisor tool is enabled", async () => {
const advisorHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicAdvisorEnabled: true,
})
const stream = advisorHandler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const requestOptions = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[1]
expect(requestOptions?.headers?.["anthropic-beta"]).toContain("advisor-tool-2026-03-01")
})
it("should inject advisor tool definition when advisor tool is enabled", async () => {
const advisorHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicAdvisorEnabled: true,
})
const stream = advisorHandler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[0]
expect(callArgs?.tools).toBeDefined()
expect(callArgs?.tools).toEqual(
expect.arrayContaining([
expect.objectContaining({
type: "advisor_20260301",
name: "advisor",
model: "claude-opus-4-6",
}),
]),
)
})
it("should include max_uses in advisor tool definition when configured", async () => {
const customMaxUses = 5
const advisorHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicAdvisorEnabled: true,
anthropicAdvisorMaxUses: customMaxUses,
})
const stream = advisorHandler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[0]
const advisorTool = callArgs?.tools?.find((tool: any) => tool.type === "advisor_20260301")
expect(advisorTool).toBeDefined()
expect(advisorTool?.max_uses).toBe(customMaxUses)
})
it("should use custom advisor model when configured", async () => {
const customModel = "claude-opus-4-5-20251101"
const advisorHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicAdvisorEnabled: true,
anthropicAdvisorModel: customModel,
})
const stream = advisorHandler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[0]
const advisorTool = callArgs?.tools?.find((tool: any) => tool.type === "advisor_20260301")
expect(advisorTool).toBeDefined()
expect(advisorTool?.model).toBe(customModel)
})
it("should not include advisor tool when advisor tool is disabled", async () => {
const stream = handler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[0]
const advisorTool = callArgs?.tools?.find((tool: any) => tool.type === "advisor_20260301")
expect(advisorTool).toBeUndefined()
// Also verify beta header is not present
const requestOptions = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[1]
const betaHeader = requestOptions?.headers?.["anthropic-beta"]
if (betaHeader && typeof betaHeader === "string") {
expect(betaHeader).not.toContain("advisor-tool-2026-03-01")
}
})
it("should use default advisor model and max_uses when not configured", async () => {
const advisorHandler = new AnthropicHandler({
apiKey: "test-api-key",
apiModelId: "claude-sonnet-4-6",
anthropicAdvisorEnabled: true,
// No anthropicAdvisorModel or anthropicAdvisorMaxUses provided
})
const stream = advisorHandler.createMessage(systemPrompt, messages)
for await (const _chunk of stream) {
// Consume stream
}
const callArgs = mockCreate.mock.calls[mockCreate.mock.calls.length - 1]?.[0]
const advisorTool = callArgs?.tools?.find((tool: any) => tool.type === "advisor_20260301")
expect(advisorTool).toBeDefined()
expect(advisorTool?.model).toBe("claude-opus-4-6") // default
expect(advisorTool?.max_uses).toBe(3) // default
})
})
})
describe("completePrompt", () => {
@ -787,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",
})
})
})
})

View file

@ -75,8 +75,30 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
betas.push("context-1m-2025-08-07")
}
// Add advisor tool beta flag if enabled
if (this.options.anthropicAdvisorEnabled) {
betas.push("advisor-tool-2026-03-01")
}
let tools = convertOpenAIToolsToAnthropic(metadata?.tools ?? [])
// Add advisor tool if enabled
if (this.options.anthropicAdvisorEnabled) {
const advisorModel = this.options.anthropicAdvisorModel ?? "claude-opus-4-6"
const maxUses = this.options.anthropicAdvisorMaxUses ?? 3
const advisorToolDef: { type: string; name: string; model: string; max_uses?: number } = {
type: "advisor_20260301",
name: "advisor",
model: advisorModel,
}
if (maxUses >= 1) {
advisorToolDef.max_uses = maxUses
}
tools.push(advisorToolDef as unknown as Anthropic.Tool)
}
const nativeToolParams = {
tools: convertOpenAIToolsToAnthropic(metadata?.tools ?? []),
tools,
tool_choice: convertOpenAIToolChoiceToAnthropic(metadata?.tool_choice, metadata?.parallelToolCalls),
}
@ -250,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.
@ -259,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
@ -268,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":

View file

@ -12,6 +12,13 @@ describe("anthropic-filter", () => {
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("thinking")).toBe(true)
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("redacted_thinking")).toBe(true)
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("document")).toBe(true)
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("server_tool_use")).toBe(true)
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("advisor_tool_result")).toBe(true)
})
it("should contain server_tool_use and advisor_tool_result", () => {
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("server_tool_use")).toBe(true)
expect(VALID_ANTHROPIC_BLOCK_TYPES.has("advisor_tool_result")).toBe(true)
})
it("should not contain internal or provider-specific types", () => {
@ -140,5 +147,59 @@ describe("anthropic-filter", () => {
expect(result).toHaveLength(1)
expect(result[0].content).toEqual([{ type: "text", text: "Valid text" }])
})
it("should preserve server_tool_use blocks in assistant turns", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{
role: "assistant",
content: [
{ type: "server_tool_use" as any, id: "srvtoolu_abc123", name: "advisor", input: {} },
{ type: "text", text: "Response" },
],
},
]
const result = filterNonAnthropicBlocks(messages)
expect(result).toHaveLength(2)
expect(result[1].content).toHaveLength(2)
expect((result[1].content as any[])[0].type).toBe("server_tool_use")
})
it("should preserve advisor_tool_result blocks in assistant turns", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{ role: "user", content: "Hello" },
{
role: "assistant",
content: [
{
type: "advisor_tool_result" as any,
tool_use_id: "srvtoolu_abc123",
content: "Advisor says hi",
},
{ type: "text", text: "Response" },
],
},
]
const result = filterNonAnthropicBlocks(messages)
expect(result).toHaveLength(2)
expect(result[1].content).toHaveLength(2)
expect((result[1].content as any[])[0].type).toBe("advisor_tool_result")
})
it("should preserve both server_tool_use and advisor_tool_result block types alongside text blocks", () => {
const messages: Anthropic.Messages.MessageParam[] = [
{
role: "assistant",
content: [
{ type: "server_tool_use" as any, id: "srvtoolu_abc123", name: "advisor", input: {} },
{ type: "advisor_tool_result" as any, tool_use_id: "srvtoolu_abc123", content: "ok" },
{ type: "text", text: "Final answer" },
],
},
]
const result = filterNonAnthropicBlocks(messages)
expect(result).toHaveLength(1)
expect(result[0].content).toHaveLength(3)
})
})
})

View file

@ -13,6 +13,8 @@ export const VALID_ANTHROPIC_BLOCK_TYPES = new Set([
"thinking",
"redacted_thinking",
"document",
"server_tool_use",
"advisor_tool_result",
])
/**

View file

@ -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

View file

@ -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<TaskEvents> 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<string, unknown>
tool_use_id?: string
content?: unknown
}> = []
this.isStreaming = true
try {
@ -3014,6 +3027,37 @@ export class Task extends EventEmitter<TaskEvents> 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<string, unknown>),
})
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<TaskEvents> 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<TaskEvents> 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.

View file

@ -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 = ({
)}
</div>
)
case "use_advisor_tool":
return (
<div style={headerStyle}>
{isAdvisorResponding ? (
<ProgressIndicator />
) : (
<span className="codicon codicon-mortar-board" />
)}
<span style={{ fontWeight: "bold" }}>{t("chat:advisor.isConsulting")}</span>
</div>
)
case "advisor_tool_result":
return (
<div className="group">
<div style={headerStyle}>
<span className="codicon codicon-mortar-board" />
<span style={{ fontWeight: "bold" }}>{t("chat:advisor.resultLabel")}</span>
</div>
<div className="w-full bg-vscode-editor-background border border-vscode-border rounded-xs p-2 mt-2">
<Markdown markdown={message.text} partial={message.partial} />
</div>
</div>
)
case "reasoning":
return (
<ReasoningBlock

View file

@ -0,0 +1,128 @@
import React from "react"
import { render, screen } from "@/utils/test-utils"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import type { ClineMessage } from "@roo-code/types"
import { ExtensionStateContextProvider } from "@src/context/ExtensionStateContext"
import { ChatRowContent } from "../ChatRow"
// Mock i18n
vi.mock("react-i18next", () => ({
useTranslation: () => ({
t: (key: string) => {
const map: Record<string, string> = {
"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 }) => <span {...props}>{children}</span>,
}))
const queryClient = new QueryClient()
function renderChatRow(message: ClineMessage, isLast = false) {
return render(
<ExtensionStateContextProvider>
<QueryClientProvider client={queryClient}>
<ChatRowContent
message={message}
isExpanded={false}
isLast={isLast}
isStreaming={false}
onToggleExpand={() => {}}
onSuggestionClick={() => {}}
onBatchFileResponse={() => {}}
onFollowUpUnmount={() => {}}
isFollowUpAnswered={false}
/>
</QueryClientProvider>
</ExtensionStateContextProvider>,
)
}
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()
})
})

View file

@ -19,6 +19,7 @@ type AnthropicProps = {
export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: AnthropicProps) => {
const { t } = useAppTranslation()
const selectedModel = useSelectedModel(apiConfiguration)
const modelInfo = selectedModel?.info
const [anthropicBaseUrlSelected, setAnthropicBaseUrlSelected] = useState(!!apiConfiguration?.anthropicBaseUrl)
@ -103,6 +104,53 @@ export const Anthropic = ({ apiConfiguration, setApiConfigurationField }: Anthro
</div>
</div>
)}
{modelInfo?.supportsAdvisorTool && (
<div>
<Checkbox
checked={apiConfiguration?.anthropicAdvisorEnabled ?? false}
onChange={(checked: boolean) => {
setApiConfigurationField("anthropicAdvisorEnabled", checked)
}}>
{t("settings:providers.anthropicAdvisorToolLabel")}
</Checkbox>
{apiConfiguration?.anthropicAdvisorEnabled && (
<div className="mt-2 ml-6 space-y-2">
<div>
<label className="block text-sm font-medium mb-1">
{t("settings:providers.anthropicAdvisorModelLabel")}
</label>
<select
value={apiConfiguration?.anthropicAdvisorModel ?? "claude-opus-4-6"}
onChange={(event) => {
setApiConfigurationField("anthropicAdvisorModel", event.target.value)
}}
className="w-full px-2 py-1 rounded border border-vscode-input-border bg-vscode-input-background text-vscode-foreground">
<option value="claude-opus-4-6">claude-opus-4-6</option>
</select>
</div>
<div>
<label className="block text-sm font-medium mb-1">
{t("settings:providers.anthropicAdvisorMaxUsesLabel")}
</label>
<input
type="number"
min="1"
value={apiConfiguration?.anthropicAdvisorMaxUses ?? 3}
onChange={(event) => {
const value = event.target.value
setApiConfigurationField(
"anthropicAdvisorMaxUses",
value === "" ? undefined : parseInt(value, 10),
)
}}
placeholder={t("settings:providers.anthropicAdvisorMaxUsesPlaceholder")}
className="w-full px-2 py-1 rounded border border-vscode-input-border bg-vscode-input-background text-vscode-foreground"
/>
</div>
</div>
)}
</div>
)}
</>
)
}

View file

@ -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 <DocsLink>documents</DocsLink> per a més informació.",
"onboarding": "La teva llista de tasques en aquest espai de treball està buida.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Passar la clau API d'Anthropic com a capçalera d'autorització en lloc de X-Api-Key",
"anthropic1MContextBetaLabel": "Activa la finestra de context d'1M (Beta)",
"anthropic1MContextBetaDescription": "Amplia la finestra de context a 1 milió de tokens per a Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Activa l'eina Advisor (Beta)",
"anthropicAdvisorModelLabel": "Model Advisor",
"anthropicAdvisorMaxUsesLabel": "Usos Màxims",
"anthropicAdvisorMaxUsesPlaceholder": "Sense límit",
"awsBedrock1MContextBetaLabel": "Activa la finestra de context d'1M (Beta)",
"awsBedrock1MContextBetaDescription": "Amplia la finestra de context a 1 milió de tokens per a Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Activa la finestra de context d'1M (Beta)",

View file

@ -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 <DocsLink>Dokumentation</DocsLink>, um mehr zu erfahren.",
"onboarding": "Deine Aufgabenliste in diesem Arbeitsbereich ist leer.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Anthropic API-Schlüssel als Authorization-Header anstelle von X-Api-Key übergeben",
"anthropic1MContextBetaLabel": "1M Kontextfenster aktivieren (Beta)",
"anthropic1MContextBetaDescription": "Erweitert das Kontextfenster für Claude Sonnet 4.x / Claude Opus 4.6 auf 1 Million Token",
"anthropicAdvisorToolLabel": "Advisor-Tool aktivieren (Beta)",
"anthropicAdvisorModelLabel": "Advisor-Modell",
"anthropicAdvisorMaxUsesLabel": "Maximale Nutzungen",
"anthropicAdvisorMaxUsesPlaceholder": "Unbegrenzt",
"awsBedrock1MContextBetaLabel": "1M Kontextfenster aktivieren (Beta)",
"awsBedrock1MContextBetaDescription": "Erweitert das Kontextfenster für Claude Sonnet 4.x / Claude Opus 4.6 auf 1 Million Token",
"vertex1MContextBetaLabel": "1M Kontextfenster aktivieren (Beta)",

View file

@ -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 <DocsLink>docs</DocsLink> to get started",
"onboarding": "What would you like to do?",
"rooTips": {

View file

@ -437,6 +437,10 @@
"anthropicUseAuthToken": "Pass Anthropic API Key as Authorization header instead of X-Api-Key",
"anthropic1MContextBetaLabel": "Enable 1M context window (Beta)",
"anthropic1MContextBetaDescription": "Extends context window to 1 million tokens for Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Enable Advisor Tool (Beta)",
"anthropicAdvisorModelLabel": "Advisor Model",
"anthropicAdvisorMaxUsesLabel": "Max Uses",
"anthropicAdvisorMaxUsesPlaceholder": "No limit",
"awsBedrock1MContextBetaLabel": "Enable 1M context window (Beta)",
"awsBedrock1MContextBetaDescription": "Extends context window to 1 million tokens for Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Enable 1M context window (Beta)",

View file

@ -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 <DocsLink>documentación</DocsLink> para saber más.",
"onboarding": "Tu lista de tareas en este espacio de trabajo está vacía.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Pasar la clave API de Anthropic como encabezado de autorización en lugar de X-Api-Key",
"anthropic1MContextBetaLabel": "Habilitar ventana de contexto de 1M (Beta)",
"anthropic1MContextBetaDescription": "Amplía la ventana de contexto a 1 millón de tokens para Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Habilitar herramienta Advisor (Beta)",
"anthropicAdvisorModelLabel": "Modelo Advisor",
"anthropicAdvisorMaxUsesLabel": "Usos Máximos",
"anthropicAdvisorMaxUsesPlaceholder": "Sin límite",
"awsBedrock1MContextBetaLabel": "Habilitar ventana de contexto de 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Amplía la ventana de contexto a 1 millón de tokens para Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Habilitar ventana de contexto de 1M (Beta)",

View file

@ -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 <DocsLink>documentation</DocsLink> pour en savoir plus.",
"onboarding": "Votre liste de tâches dans cet espace de travail est vide.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Passer la clé API Anthropic comme en-tête d'autorisation au lieu de X-Api-Key",
"anthropic1MContextBetaLabel": "Activer la fenêtre de contexte de 1M (Bêta)",
"anthropic1MContextBetaDescription": "Étend la fenêtre de contexte à 1 million de tokens pour Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Activer l'outil Advisor (Bêta)",
"anthropicAdvisorModelLabel": "Modèle Advisor",
"anthropicAdvisorMaxUsesLabel": "Utilisations Maximales",
"anthropicAdvisorMaxUsesPlaceholder": "Illimité",
"awsBedrock1MContextBetaLabel": "Activer la fenêtre de contexte de 1M (Bêta)",
"awsBedrock1MContextBetaDescription": "Étend la fenêtre de contexte à 1 million de tokens pour Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Activer la fenêtre de contexte de 1M (Bêta)",

View file

@ -98,6 +98,10 @@
"enqueueMessage": "संदेश को कतार में जोड़ें (वर्तमान कार्य पूरा होने के बाद भेजा जाएगा)",
"scrollToBottom": "चैट के निचले हिस्से तक स्क्रॉल करें",
"about": "Roo Code आपके संपादक में एक पूरी AI देव टीम है।",
"advisor": {
"isConsulting": "सलाहकार से परामर्श...",
"resultLabel": "सलाहकार प्रतिक्रिया"
},
"docs": "और जानने के लिए हमारे <DocsLink>दस्तावेज़</DocsLink> देखें।",
"onboarding": "इस कार्यक्षेत्र में आपकी कार्य सूची खाली है।",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "X-Api-Key के बजाय Anthropic API कुंजी को Authorization हेडर के रूप में पास करें",
"anthropic1MContextBetaLabel": "1M संदर्भ विंडो सक्षम करें (बीटा)",
"anthropic1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6 के लिए संदर्भ विंडो को 1 मिलियन टोकन तक बढ़ाता है",
"anthropicAdvisorToolLabel": "Advisor Tool सक्षम करें (Beta)",
"anthropicAdvisorModelLabel": "Advisor मॉडल",
"anthropicAdvisorMaxUsesLabel": "अधिकतम उपयोग",
"anthropicAdvisorMaxUsesPlaceholder": "कोई सीमा नहीं",
"awsBedrock1MContextBetaLabel": "1M संदर्भ विंडो सक्षम करें (बीटा)",
"awsBedrock1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6 के लिए संदर्भ विंडो को 1 मिलियन टोकन तक बढ़ाता है",
"vertex1MContextBetaLabel": "1M संदर्भ विंडो सक्षम करें (बीटा)",

View file

@ -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 <DocsLink>dokumentasi</DocsLink> kami untuk mempelajari lebih lanjut.",
"onboarding": "Daftar tugas Anda di ruang kerja ini kosong.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Kirim Anthropic API Key sebagai Authorization header alih-alih X-Api-Key",
"anthropic1MContextBetaLabel": "Aktifkan jendela konteks 1M (Beta)",
"anthropic1MContextBetaDescription": "Memperluas jendela konteks menjadi 1 juta token untuk Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Aktifkan Advisor Tool (Beta)",
"anthropicAdvisorModelLabel": "Model Advisor",
"anthropicAdvisorMaxUsesLabel": "Penggunaan Maksimal",
"anthropicAdvisorMaxUsesPlaceholder": "Tanpa batas",
"awsBedrock1MContextBetaLabel": "Aktifkan jendela konteks 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Memperluas jendela konteks menjadi 1 juta token untuk Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Aktifkan jendela konteks 1M (Beta)",

View file

@ -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 <DocsLink>documentazione</DocsLink> per saperne di più.",
"onboarding": "La tua lista di attività in questo spazio di lavoro è vuota.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Passa la chiave API Anthropic come header di autorizzazione invece di X-Api-Key",
"anthropic1MContextBetaLabel": "Abilita finestra di contesto da 1M (Beta)",
"anthropic1MContextBetaDescription": "Estende la finestra di contesto a 1 milione di token per Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Abilita Advisor Tool (Beta)",
"anthropicAdvisorModelLabel": "Modello Advisor",
"anthropicAdvisorMaxUsesLabel": "Utilizzi Massimi",
"anthropicAdvisorMaxUsesPlaceholder": "Senza limite",
"awsBedrock1MContextBetaLabel": "Abilita finestra di contesto da 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Estende la finestra di contesto a 1 milione di token per Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Abilita finestra di contesto da 1M (Beta)",

View file

@ -98,6 +98,10 @@
"enqueueMessage": "メッセージをキューに追加(現在のタスク完了後に送信されます)",
"scrollToBottom": "チャットの最下部にスクロール",
"about": "Roo Codeは、エディタに常駐するAI開発チームです。",
"advisor": {
"isConsulting": "アドバイザーに相談中...",
"resultLabel": "アドバイザーからの回答"
},
"docs": "詳細については、<DocsLink>ドキュメント</DocsLink>をご確認ください。",
"onboarding": "このワークスペースのタスクリストは空です。",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Anthropic APIキーをX-Api-Keyの代わりにAuthorizationヘッダーとして渡す",
"anthropic1MContextBetaLabel": "1Mコンテキストウィンドウを有効にするベータ版",
"anthropic1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6のコンテキストウィンドウを100万トークンに拡張します",
"anthropicAdvisorToolLabel": "Advisor ツールを有効にする (ベータ版)",
"anthropicAdvisorModelLabel": "Advisor モデル",
"anthropicAdvisorMaxUsesLabel": "最大使用回数",
"anthropicAdvisorMaxUsesPlaceholder": "無制限",
"awsBedrock1MContextBetaLabel": "1Mコンテキストウィンドウを有効にするベータ版",
"awsBedrock1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6のコンテキストウィンドウを100万トークンに拡張します",
"vertex1MContextBetaLabel": "1Mコンテキストウィンドウを有効にするベータ版",

View file

@ -98,6 +98,10 @@
"enqueueMessage": "메시지를 대기열에 추가 (현재 작업 완료 후 전송)",
"scrollToBottom": "채팅 하단으로 스크롤",
"about": "Roo Code는 편집기 안에 있는 전체 AI 개발팀입니다.",
"advisor": {
"isConsulting": "자문가와 상담 중...",
"resultLabel": "자문가 응답"
},
"docs": "더 알아보려면 <DocsLink>문서</DocsLink>를 확인하세요.",
"onboarding": "이 작업 공간의 작업 목록이 비어 있습니다.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "X-Api-Key 대신 Authorization 헤더로 Anthropic API 키 전달",
"anthropic1MContextBetaLabel": "1M 컨텍스트 창 활성화 (베타)",
"anthropic1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6의 컨텍스트 창을 100만 토큰으로 확장",
"anthropicAdvisorToolLabel": "Advisor 도구 활성화 (베타)",
"anthropicAdvisorModelLabel": "Advisor 모델",
"anthropicAdvisorMaxUsesLabel": "최대 사용 횟수",
"anthropicAdvisorMaxUsesPlaceholder": "제한 없음",
"awsBedrock1MContextBetaLabel": "1M 컨텍스트 창 활성화 (베타)",
"awsBedrock1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6의 컨텍스트 창을 100만 토큰으로 확장",
"vertex1MContextBetaLabel": "1M 컨텍스트 창 활성화 (베타)",

View file

@ -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 <DocsLink>documentatie</DocsLink> voor meer informatie.",
"onboarding": "Je takenlijst in deze werkruimte is leeg.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Anthropic API-sleutel als Authorization-header doorgeven in plaats van X-Api-Key",
"anthropic1MContextBetaLabel": "1M contextvenster inschakelen (bèta)",
"anthropic1MContextBetaDescription": "Breidt het contextvenster uit tot 1 miljoen tokens voor Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Advisor Tool inschakelen (bèta)",
"anthropicAdvisorModelLabel": "Advisor Model",
"anthropicAdvisorMaxUsesLabel": "Maximale Toepassingen",
"anthropicAdvisorMaxUsesPlaceholder": "Onbeperkt",
"awsBedrock1MContextBetaLabel": "1M contextvenster inschakelen (bèta)",
"awsBedrock1MContextBetaDescription": "Breidt het contextvenster uit tot 1 miljoen tokens voor Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "1M contextvenster inschakelen (bèta)",

View file

@ -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ą <DocsLink>dokumentację</DocsLink>, aby dowiedzieć się więcej.",
"onboarding": "Twoja lista zadań w tym obszarze roboczym jest pusta.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Przekaż klucz API Anthropic jako nagłówek Authorization zamiast X-Api-Key",
"anthropic1MContextBetaLabel": "Włącz okno kontekstowe 1M (Beta)",
"anthropic1MContextBetaDescription": "Rozszerza okno kontekstowe do 1 miliona tokenów dla Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Włącz narzędzie Advisor (Beta)",
"anthropicAdvisorModelLabel": "Model Advisor",
"anthropicAdvisorMaxUsesLabel": "Maksymalne Użycia",
"anthropicAdvisorMaxUsesPlaceholder": "Bez limitu",
"awsBedrock1MContextBetaLabel": "Włącz okno kontekstowe 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Rozszerza okno kontekstowe do 1 miliona tokenów dla Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Włącz okno kontekstowe 1M (Beta)",

View file

@ -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 <DocsLink>documentação</DocsLink> para saber mais.",
"onboarding": "Sua lista de tarefas neste espaço de trabalho está vazia.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Passar a chave de API Anthropic como cabeçalho Authorization em vez de X-Api-Key",
"anthropic1MContextBetaLabel": "Ativar janela de contexto de 1M (Beta)",
"anthropic1MContextBetaDescription": "Estende a janela de contexto para 1 milhão de tokens para o Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Ativar Advisor Tool (Beta)",
"anthropicAdvisorModelLabel": "Modelo Advisor",
"anthropicAdvisorMaxUsesLabel": "Usos Máximos",
"anthropicAdvisorMaxUsesPlaceholder": "Sem limite",
"awsBedrock1MContextBetaLabel": "Ativar janela de contexto de 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Estende a janela de contexto para 1 milhão de tokens para o Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Ativar janela de contexto de 1M (Beta)",

View file

@ -98,6 +98,10 @@
"enqueueMessage": "Добавить сообщение в очередь (будет отправлено после завершения текущей задачи)",
"scrollToBottom": "Прокрутить чат вниз",
"about": "Roo Code — это целая команда разработчиков ИИ в вашем редакторе.",
"advisor": {
"isConsulting": "Консультируемся с советником...",
"resultLabel": "Ответ советника"
},
"docs": "Ознакомьтесь с нашей <DocsLink>документацией</DocsLink>, чтобы узнать больше.",
"onboarding": "Ваш список задач в этом рабочем пространстве пуст.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Передавать Anthropic API-ключ как Authorization-заголовок вместо X-Api-Key",
"anthropic1MContextBetaLabel": "Включить контекстное окно 1M (бета)",
"anthropic1MContextBetaDescription": "Расширяет контекстное окно до 1 миллиона токенов для Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Включить инструмент Advisor (бета)",
"anthropicAdvisorModelLabel": "Модель Advisor",
"anthropicAdvisorMaxUsesLabel": "Максимум использований",
"anthropicAdvisorMaxUsesPlaceholder": "Без ограничений",
"awsBedrock1MContextBetaLabel": "Включить контекстное окно 1M (бета)",
"awsBedrock1MContextBetaDescription": "Расширяет контекстное окно до 1 миллиона токенов для Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Включить контекстное окно 1M (бета)",

View file

@ -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 <DocsLink>belgelerimize</DocsLink> göz atın.",
"onboarding": "Bu çalışma alanındaki görev listeniz boş.",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Anthropic API Anahtarını X-Api-Key yerine Authorization başlığı olarak geçir",
"anthropic1MContextBetaLabel": "1M bağlam penceresini etkinleştir (Beta)",
"anthropic1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6 için bağlam penceresini 1 milyon token'a genişletir",
"anthropicAdvisorToolLabel": "Advisor Aracını Etkinleştir (Beta)",
"anthropicAdvisorModelLabel": "Advisor Modeli",
"anthropicAdvisorMaxUsesLabel": "Maksimum Kullanım",
"anthropicAdvisorMaxUsesPlaceholder": "Sınır Yok",
"awsBedrock1MContextBetaLabel": "1M bağlam penceresini etkinleştir (Beta)",
"awsBedrock1MContextBetaDescription": "Claude Sonnet 4.x / Claude Opus 4.6 için bağlam penceresini 1 milyon token'a genişletir",
"vertex1MContextBetaLabel": "1M bağlam penceresini etkinleştir (Beta)",

View file

@ -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 <DocsLink>tài liệu</DocsLink> 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": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "Truyền khóa API Anthropic dưới dạng tiêu đề Authorization thay vì X-Api-Key",
"anthropic1MContextBetaLabel": "Bật cửa sổ ngữ cảnh 1M (Beta)",
"anthropic1MContextBetaDescription": "Mở rộng cửa sổ ngữ cảnh lên 1 triệu token cho Claude Sonnet 4.x / Claude Opus 4.6",
"anthropicAdvisorToolLabel": "Bật Advisor Tool (Beta)",
"anthropicAdvisorModelLabel": "Mô hình Advisor",
"anthropicAdvisorMaxUsesLabel": "Tối đa Sử dụng",
"anthropicAdvisorMaxUsesPlaceholder": "Không giới hạn",
"awsBedrock1MContextBetaLabel": "Bật cửa sổ ngữ cảnh 1M (Beta)",
"awsBedrock1MContextBetaDescription": "Mở rộng cửa sổ ngữ cảnh lên 1 triệu token cho Claude Sonnet 4.x / Claude Opus 4.6",
"vertex1MContextBetaLabel": "Bật cửa sổ ngữ cảnh 1M (Beta)",

View file

@ -98,6 +98,10 @@
"enqueueMessage": "将消息加入队列(当前任务完成后发送)",
"scrollToBottom": "滚动到聊天底部",
"about": "Roo Code 是您编辑器中的整个 AI 开发团队。",
"advisor": {
"isConsulting": "正在咨询顾问...",
"resultLabel": "顾问回复"
},
"docs": "查看我们的 <DocsLink>文档</DocsLink> 了解更多信息。",
"onboarding": "此工作区中的任务列表为空。",
"rooTips": {

View file

@ -374,6 +374,10 @@
"anthropicUseAuthToken": "将 Anthropic API 密钥作为 Authorization 标头传递,而不是 X-Api-Key",
"anthropic1MContextBetaLabel": "启用 1M 上下文窗口 (Beta)",
"anthropic1MContextBetaDescription": "为 Claude Sonnet 4.x / Claude Opus 4.6 将上下文窗口扩展至 100 万个 token",
"anthropicAdvisorToolLabel": "启用 Advisor 工具 (测试版)",
"anthropicAdvisorModelLabel": "Advisor 模型",
"anthropicAdvisorMaxUsesLabel": "最大使用次数",
"anthropicAdvisorMaxUsesPlaceholder": "无限制",
"awsBedrock1MContextBetaLabel": "启用 1M 上下文窗口 (Beta)",
"awsBedrock1MContextBetaDescription": "为 Claude Sonnet 4.x / Claude Opus 4.6 将上下文窗口扩展至 100 万个 token",
"vertex1MContextBetaLabel": "启用 1M 上下文窗口 (Beta)",

View file

@ -125,6 +125,10 @@
},
"scrollToBottom": "捲動至對話底部",
"about": "Roo 是編輯器中的完整 AI 開發團隊。",
"advisor": {
"isConsulting": "正在諮詢顧問...",
"resultLabel": "顧問回應"
},
"docs": "請參閱 <DocsLink>說明文件</DocsLink> 開始使用。",
"onboarding": "想要做什麼呢?",
"rooTips": {

View file

@ -384,6 +384,10 @@
"anthropicUseAuthToken": "將 Anthropic API 金鑰作為 Authorization 標頭傳遞,而非使用 X-Api-Key",
"anthropic1MContextBetaLabel": "啟用 1M 上下文視窗 (Beta)",
"anthropic1MContextBetaDescription": "為 Claude Sonnet 4.x / Claude Opus 4.6 將上下文視窗擴展至 100 萬個 token",
"anthropicAdvisorToolLabel": "啟用 Advisor 工具 (測試版)",
"anthropicAdvisorModelLabel": "Advisor 模型",
"anthropicAdvisorMaxUsesLabel": "最大使用次數",
"anthropicAdvisorMaxUsesPlaceholder": "無限制",
"awsBedrock1MContextBetaLabel": "啟用 1M 上下文視窗 (Beta)",
"awsBedrock1MContextBetaDescription": "為 Claude Sonnet 4.x / Claude Opus 4.6 將上下文視窗擴展至 100 萬個 token",
"vertex1MContextBetaLabel": "啟用 1M 上下文視窗 (Beta)",