Skip to content

Commit 7bb3133

Browse files
mini2sctegithub-actions[bot]brunobergherroomote-v0[bot]
authored
Roo to main (#871)
* Revert "fix: merge settings and versionedSettings for Roo provider models" (#10034) * Revert the 3.6.5 release (we halted it) (#10036) * Release v3.36.5 (#10037) * Changeset version bump (#10038) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * test: adjust terminal count limits in TerminalRegistry tests * ux: improve auto-approve timer visibility in follow-up suggestions (#10048) Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> Co-authored-by: Roo Code <roomote@roocode.com> * fix: cancel auto-approval timeout when user starts typing (#9937) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix: extract raw error message from OpenRouter metadata (#10039) OpenRouter wraps upstream provider errors in a generic message but includes the actual error in metadata.raw. This change: - Adds OpenRouterErrorResponse interface for proper typing - Creates handleStreamingError() helper for DRY error handling - Extracts metadata.raw for actionable error messages in PostHog - Includes nested error structure so getErrorMessage() can extract raw message Before: PostHog receives '400 Provider returned error' (generic) After: PostHog receives 'Model xyz not found' (actionable) This enables proper error tracking and debugging via PostHog telemetry. * feat: add tool alias support for model-specific tool customization (#9989) Co-authored-by: Hannes Rudolph <hrudolph@gmail.com> * fix: show tool protocol dropdown for LiteLLM provider (#10053) * feat: add WorkspaceTaskVisibility type for organization cloud settings (#10020) * feat: add WorkspaceTaskVisibility type and workspaceTaskVisibility property to OrganizationCloudSettings * refactor: create workspaceTaskVisibilitySchema and derive WorkspaceTaskVisibility type from it --------- Co-authored-by: Roo Code <roomote@roocode.com> * Release: v1.91.0 (#10055) chore: bump version to v1.91.0 * feat: sanitize MCP server/tool names for API compatibility (#10054) * Release v3.36.6 (#10057) * Changeset version bump (#10058) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * fix: use JavaScript-based hover for checkpoint menu visibility (#10056) * feat: remove auto-approve toggles for to-do and retry actions (#10062) * feat(openrouter): add improvements to openrouter provider (#10082) * feat: Add Amazon Nova 2 Lite model to Bedrock provider (#9830) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * feat: add AWS Bedrock service tier support (#9955) Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * Capture more of OpenRouter's provider specific error details (#10073) * Capture more of OpenRouter's provider specific error details * Actually match the openrouter structure * feat(web-evals): improve run logs and formatters (#10081) * Move isToolAllowedForMode out of shared directory (#10089) * chore: add changeset for v3.36.7 (#10091) * Changeset version bump (#10092) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix: prevent duplicate MCP tools error by deduplicating servers at source (#10096) * feat: add metadata to error details dialog (#10050) * feat: add metadata to error details dialog - Prepends extension version, provider, model, and repository info to error details - Helps users provide better bug reports with context - Uses useExtensionState and useSelectedModel hooks for data * Tweaks --------- Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Bruno Bergher <bruno@roocode.com> * web: Fixes link to provider pricing page (#10107) * feat(read-file): implement incremental token-budgeted file reading (#10052) * Add config to control public sharing (#10105) Co-authored-by: Roo Code <roomote@roocode.com> * Release: v1.92.0 (#10116) * Remove the description from bedrock service tiers (#10118) * feat: remove strict ARN validation for Bedrock custom ARN users (#10110) Co-authored-by: Roo Code <roomote@roocode.com> * fix: prevent race condition from deleting wrong API messages (#10113) Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> * feat(anthropic): enable native tools by default and add telemetry tracking (#10021) * feat: enable native tools by default for multiple providers (#10059) * Release v3.36.8 (#10119) * fix: add additionalProperties: false to nested MCP tool schemas (#10109) * fix: normalize tool call IDs for cross-provider compatibility via OpenRouter (#10102) * feat: add full error details to streaming failure dialog (#10131) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: cte <cestreich@gmail.com> * fix: validate tool_result IDs in delegation resume flow (#10135) * feat(evals): improve evals UI with tool groups and duration fix (#10133) Co-authored-by: Roo Code <roomote@roocode.com> * Changeset version bump (#10120) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Release v3.36.9 (#10138) * Changeset version bump (#10137) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * fix: correct token counting for context truncation display (#9961) * feat(deepseek): implement interleaved thinking mode for deepseek-reasoner (#9969) * Update next.js to ~15.2.8 (#10140) * fix(deepseek): preserve reasoning_content during tool call sequences (#10141) Co-authored-by: Roo Code <roomote@roocode.com> * feat: add gemini-3-flash-preview model (#10151) * Release v3.36.10 (#10153) * Changeset version bump (#10154) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix(bedrock): convert tool_result to XML text when native tools disabled (#10155) * fix: remove dots and colons from MCP tool names for Bedrock compatibility (#10152) * fix: improve terminal process error handling and abort operation * fix(ROO-202): refresh Roo models cache with session token on auth state change (#10156) * fix: support AWS GovCloud and China region ARNs in Bedrock provider (#10157) Co-authored-by: Roo Code <roomote@roocode.com> * feat: enable native tool calling by default for Z.ai models (#10158) Co-authored-by: Roo Code <roomote@roocode.com> * [feat] Claude Code Provider Native Tool Calling (#10077) Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> * fix: normalize MCP tool schemas for Bedrock and OpenAI strict mode (#10148) * fix: enable native tools by default for OpenAI compatible provider (#10159) * Release v3.36.11 (#10161) * Changeset version bump (#10162) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * feat(telemetry): extract error messages from JSON payloads for better PostHog grouping (#10163) * fix: add userAgentAppId to Bedrock embedder for code indexing (#10166) Adds userAgentAppId configuration to the BedrockRuntimeClient in the code indexing embedder, matching the implementation pattern already used in the main Bedrock API provider. This enables proper user agent identification in CloudTrail AWS requests when using Bedrock for code indexing embeddings. Fixes #10165 Co-authored-by: Roo Code <roomote@roocode.com> * feat: update OpenAI and Gemini tool preferences (#10170) * Release v3.36.12 (#10181) * Changeset version bump (#10182) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * Revert "Revert "feat: change defaultToolProtocol default from xml to native"" (#10186) Co-authored-by: Roo Code <roomote@roocode.com> * fix(litellm): merge default model info with router models for NTC support (#10187) * feat(types): add defaultToolProtocol: native to providers - Added supportsNativeTools: true and defaultToolProtocol: native to all chutes models - Added defaultToolProtocol: native to moonshot models (already had supportsNativeTools) - Added defaultToolProtocol: native to litellm default model (already had supportsNativeTools) - Added defaultToolProtocol: native to minimax models (already had supportsNativeTools) This enables native tool calling by default for these providers, reducing the number of users falling back to XML tool protocol unnecessarily. * fix(litellm): merge only native tool defaults with router models Only merges supportsNativeTools and defaultToolProtocol from litellmDefaultModelInfo, not prices or other model-specific info that could be incorrect for different models. * Release: v1.93.0 (#10190) * feat(vscode-lm): add native tool support (#10191) * feat: Replace edit_file tool alias with edit_file tool (#9983) * feat: lock task tool protocol for consistent task resumption (#10192) Co-authored-by: Roo Code <roomote@roocode.com> * feat(telemetry): add PostHog exception tracking for consecutive mistake errors (#10193) * Release v3.36.13 (#10194) * Changeset version bump (#10195) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * feat: improve 'no tools used' error handling with grace retry (#10196) Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * feat(vertex): add native tool calling for Claude models on Vertex AI (#10197) Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> * fix: strip unsupported JSON Schema format values for OpenAI compatibility (#10198) * Release v3.36.14 (#10200) * Changeset version bump (#10201) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * feat(telemetry): add telemetry event handling through webview messages * fix: refresh models button not flushing cache properly (#9870) * feat(vertex): add 1M context window beta support for Claude Sonnet 4 (#10209) * feat(providers): add native tool calling support to LM Studio and Qwen-Code (#10208) * ux: improve API error handling and visibility (#10204) Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> * ux: add downloadable error diagnostics from chat errors (#10188) Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * feat: merge native tool defaults for openai-compatible provider (#10213) * fix: force additionalProperties false for strict mode compatibility (#10210) * fix: enable native tool calls for Requesty provider (ROO-235) (#10211) * Release v3.36.15 (#10218) * Changeset version bump (#10219) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Chris Estreich <cestreich@gmail.com> * fix: normalize tool schemas for VS Code LM API to fix error 400 (#10221) * Release v3.36.16 (#10224) * Changeset version bump (#10225) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * Custom tool calling (#10083) * Remove the "test" custom tools (#10255) Co-authored-by: Roo Code <roomote@roocode.com> * Add custom tool definitions to @roo-code/types (#10233) Co-authored-by: Roo Code <roomote@roocode.com> * fix(evals): add missing packages/core to Dockerfile.runner (#10272) * feat: add Cloud Team page with comprehensive team features (#10267) * feat: add Cloud Team page with features and pricing integration * Copy tweaks * Visual tweaks * Content adjustments * Update apps/web-roo-code/src/app/cloud/team/page.tsx Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> --------- Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Bruno Bergher <bruno@roocode.com> Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> * feat: remove parallel_tool_calls parameter from litellm provider (#10274) Co-authored-by: Roo Code <roomote@roocode.com> * feat(build): add types package bundling and alias support * update(settings): change default tool protocol to XML * fix: enable Requesty refresh models with credentials (#10273) * fix: disable strict mode for MCP tools to preserve optional parameters (#10220) Co-authored-by: Roo Code <roomote@roocode.com> * fix: move array-specific properties into anyOf variant in normalizeToolSchema (#10276) * fix: move array-specific properties into anyOf variant in normalizeToolSchema Fixes read_file tool schema rejection with GPT-5-mini which requires items property to be inside the { type: 'array' } variant when using anyOf for nullable arrays. Resolves ROO-262 * refactor: extract array-specific properties constant and helper function * refactor(custom-tools): improve tool loading and instance consistency * fix(chutes): add graceful fallback for model parsing (#10279) Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> * fix: emit tool_call_end events in OpenAI handler when streaming ends (#10280) * feat(core): add support for custom tool parsing in AssistantMessageParser * Merge remote-tracking branch 'upstream/main' into roo-to-main * feat: deprecate XML tool protocol selection, force native for new tasks (#10281) - Disable tool protocol selector UI in ApiOptions.tsx - Force native protocol for all new tasks in resolveToolProtocol() - Keep locked protocol support for resumed tasks that used XML - Remove models without supportsNativeTools: true from providers: - baseten.ts: removed 6 models - bedrock.ts: removed 2 embedding models - featherless.ts: removed 3 models, updated default - groq.ts: removed 5 models - sambanova.ts: removed 2 models - vertex.ts: removed 7 models - vercel-ai-gateway.ts: added supportsNativeTools: true - Update tests to expect native format output * feat(zai): add GLM-4.7 model with thinking mode support (#10282) Co-authored-by: Roo Code <roomote@roocode.com> * fix: improve reasoning_details accumulation and serialization (#10285) * feat(evals): add message log deduper utility (#10286) * feat(minimax): move environment_details to system message for thinking models (#10284) * fix: add CRLF line ending normalization to search_replace and search_and_replace tools (#10288) - Normalize file content to LF after reading to ensure consistent matching - Normalize search/replace strings to handle CRLF from model output - Add comprehensive CRLF normalization tests for both tools - Consistent with existing edit_file tool behavior * refactor(zai): merge environment_details into tool result instead of system message (#10289) * fix: emit tool_call_end events in BaseOpenAiCompatibleProvider (#10293) * fix: preserve reasoning_content in condense summary for DeepSeek-reasoner (#10292) * Release v3.37.0 (#10295) Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Changeset version bump (#10296) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * feat: enable mergeToolResultText for Roo Code Cloud provider (#10301) * feat: add grace retry for empty assistant messages (#10297) Implements grace retry error handling for 'no assistant messages' API errors, following the same pattern as PR #10196 for 'no tools used'. - Add consecutiveNoAssistantMessagesCount counter to Task.ts - First failure: silent retry (grace retry) - After 2+ consecutive failures: show MODEL_NO_ASSISTANT_MESSAGES error - Add UI handling in ChatRow.tsx with ErrorRow component - Add localized strings to all 18 locale files - Add comprehensive tests for the grace retry behavior * Release: v1.95.0 (#10309) chore: bump version to v1.95.0 * feat(prompts): strengthen native tool-use guidance (#10311) * feat: enable mergeToolResultText for all OpenAI-compatible providers (#10299) * fix: preserve reasoning_details shape to prevent malformed responses (#10313) * fix(task): drain queued messages while waiting for ask (#10315) * fix(openai): send native tool definitions by default (#10314) Co-authored-by: Roo Code <roomote@roocode.com> * ux: Provider-centric signup (#10306) Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * chore: add changeset for v3.37.1 (#10316) * Changeset version bump (#10317) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * refactor: simplify tool protocol resolution logic * refactor: optimize stream rendering and file reading limits * refactor(tools): simplify extractTextFromFile parameters * feat(chat): add tool protocol display to task header * fix(test): respect user toolProtocol preference over model capabilities * update(provider): update zgsm model handling and native tool protocol logic * feat: remove OpenRouter Transforms feature (#10341) - Remove openRouterUseMiddleOutTransform checkbox from settings UI - Remove openRouterUseMiddleOutTransform from TypeScript types - Remove transforms parameter logic from OpenRouter handler - Remove setting from EVALS_SETTINGS - Remove translation keys from all locale files (18 locales) Co-authored-by: Roo Code <roomote@roocode.com> * feat: remove simpleReadFileTool completely (#10254) - Delete simpleReadFileTool.ts file - Delete simple-read-file.ts prompt description file - Delete single-file-read-models.ts types file - Remove imports and usage from presentAssistantMessage.ts - Remove imports and usage from prompts/tools/index.ts - Remove export from packages/types/src/index.ts This removes all traces of the legacy single-file read tool implementation that was used for specific models. All models now use the standard read_file tool. Co-authored-by: Roo Code <roomote@roocode.com> * Add support for skills (#10335) * Add support for skills * fix: use type-only import for ClineProvider and relative paths in skills section --------- Co-authored-by: Roo Code <roomote@roocode.com> * Add support for npm packages and .env files to custom tools (#10336) Co-authored-by: Roo Code <roomote@roocode.com> * fix: capture extended thinking signatures for tool use continuations (#10351) * feat: add optional mode field to slash command front matter (#10344) * feat: add optional mode field to slash command front matter - Add mode field to Command interface - Update command parsing to extract mode from frontmatter - Modify RunSlashCommandTool to automatically switch mode when specified - Add comprehensive tests for mode field parsing and switching - Update existing tests to include mode field * Make it work for manual slash commands too --------- Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * Remove the mergeToolResultText in the Roo provider for now (#10359) * Revert "fix: capture extended thinking signatures for tool use continuations" (#10360) * Release v3.38.0 (#10361) * Changeset version bump (#10362) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix: enforce maxConcurrentFileReads limit in read_file tool (#10363) Co-authored-by: Roo Code <roomote@roocode.com> * refactor(zgsm): optimize native protocol handling and model ID usage * docs: clarify path to Security Settings in privacy policy (#10367) Co-authored-by: Roo Code <roomote@roocode.com> * Improve error message when read_file is used on directory (#10371) Co-authored-by: Roo Code <roomote@roocode.com> * Handle custom tool use similarly to MCP tools for ipc schema purposes (#10364) * fix: correct GitHub repository URL in marketing page (#10377) Co-authored-by: Roo Code <roomote@roocode.com> * Revert "feat: enable mergeToolResultText for all OpenAI-compatible providers (#10299)" (#10381) * fix: flush pending tool results before condensing context (#10379) * chore: add changeset for v3.38.1 (#10384) * Changeset version bump (#10385) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix: update Cerebras maxTokens to 16384 (#10387) * docs: Replace Todo Lists video with Context Management video (#10375) Co-authored-by: Sannidhya <sann@Sannidhyas-MacBook-Pro.local> * Release: v1.96.0 (#10395) chore: bump version to v1.96.0 * fix(utils): add optional chaining for provider settings * feat(skills): align with Agent Skills spec (#10409) * chore: remove human-relay provider (#10388) Co-authored-by: Roo Code <roomote@roocode.com> * Fix rate limit wait display (#10389) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix: prevent write_to_file from creating files at truncated paths (#10415) Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> Co-authored-by: Roo Code <roomote@roocode.com> * Release v3.38.2 (#10416) * Changeset version bump (#10417) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * fix(claude-code): stop frequent sign-ins by hardening OAuth refresh (#10410) * fix(claude-code): prevent sign-outs on oauth refresh * test(claude-code): restore fetch after mocking * refactor(claude-code): replace while(true) with bounded for loop for clarity --------- Co-authored-by: Roo Code <roomote@roocode.com> * fix: add type check for lastMessage.text in TTS useEffect (#10431) fix: add type check for lastMessage.text before calling startsWith Fixes #10430 The TTS useEffect was calling .startsWith() on lastMessage.text after only checking if it was truthy. If text was a non-string truthy value (array, object, or number), this would crash with "Q.text.startsWith is not a function". Changed the truthy check to an explicit type check: typeof lastMessage.text === "string" Co-authored-by: Roo Code <roomote@roocode.com> * feat(chat): add collapsible markdown blocks and improve protocol error handling * refactor(task): restructure conversation history handling * feat: recursively load .roo/rules and AGENTS.md from subdirectories (#10446) Co-authored-by: Roo Code <roomote@roocode.com> * Release: v1.99.0 (#10447) * fix: add maxConcurrentFileReads limit to native read_file tool schema (#10449) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * chore: add changeset for v3.38.3 (#10450) * Changeset version bump (#10451) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> * feat: add image support documentation to read_file native tool description (#10442) Co-authored-by: Roo Code <roomote@roocode.com> * fix: add explicit deduplication for duplicate tool_result blocks (#10466) Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> --------- Co-authored-by: Chris Estreich <cestreich@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Bruno Bergher <bruno@roocode.com> Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com> Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> Co-authored-by: Daniel <57051444+daniel-lxs@users.noreply.github.com> Co-authored-by: Hannes Rudolph <hrudolph@gmail.com> Co-authored-by: John Richmond <5629+jr@users.noreply.github.com> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com> Co-authored-by: Patrick Decat <pdecat@gmail.com> Co-authored-by: Seb Duerr <sebastian.duerr@cerebras.net> Co-authored-by: SannidhyaSah <sah_sannidhya@outlook.com> Co-authored-by: Sannidhya <sann@Sannidhyas-MacBook-Pro.local>
1 parent 96770ad commit 7bb3133

File tree

8 files changed

+241
-16
lines changed

8 files changed

+241
-16
lines changed

src/core/prompts/tools/native-tools/__tests__/read_file.spec.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,48 @@ describe("createReadFileTool", () => {
9696
})
9797
})
9898

99+
describe("supportsImages option", () => {
100+
it("should include image format documentation when supportsImages is true", () => {
101+
const tool = createReadFileTool({ supportsImages: true })
102+
const description = getFunctionDef(tool).description
103+
104+
expect(description).toContain(
105+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
106+
)
107+
})
108+
109+
it("should not include image format documentation when supportsImages is false", () => {
110+
const tool = createReadFileTool({ supportsImages: false })
111+
const description = getFunctionDef(tool).description
112+
113+
expect(description).not.toContain(
114+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
115+
)
116+
expect(description).toContain("may not handle other binary files properly")
117+
})
118+
119+
it("should default supportsImages to false", () => {
120+
const tool = createReadFileTool({})
121+
const description = getFunctionDef(tool).description
122+
123+
expect(description).not.toContain(
124+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
125+
)
126+
})
127+
128+
it("should always include PDF and DOCX support in description", () => {
129+
const toolWithImages = createReadFileTool({ supportsImages: true })
130+
const toolWithoutImages = createReadFileTool({ supportsImages: false })
131+
132+
expect(getFunctionDef(toolWithImages).description).toContain(
133+
"Supports text extraction from PDF and DOCX files",
134+
)
135+
expect(getFunctionDef(toolWithoutImages).description).toContain(
136+
"Supports text extraction from PDF and DOCX files",
137+
)
138+
})
139+
})
140+
99141
describe("combined options", () => {
100142
it("should correctly combine low maxConcurrentFileReads with partialReadsEnabled", () => {
101143
const tool = createReadFileTool({
@@ -120,6 +162,49 @@ describe("createReadFileTool", () => {
120162
expect(description).not.toContain("line_ranges")
121163
expect(description).not.toContain("Example multiple files")
122164
})
165+
166+
it("should correctly combine partialReadsEnabled and supportsImages", () => {
167+
const tool = createReadFileTool({
168+
partialReadsEnabled: true,
169+
supportsImages: true,
170+
})
171+
const description = getFunctionDef(tool).description
172+
173+
// Should have both line_ranges and image support
174+
expect(description).toContain("line_ranges")
175+
expect(description).toContain(
176+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
177+
)
178+
})
179+
180+
it("should work with partialReadsEnabled=false and supportsImages=true", () => {
181+
const tool = createReadFileTool({
182+
partialReadsEnabled: false,
183+
supportsImages: true,
184+
})
185+
const description = getFunctionDef(tool).description
186+
187+
// Should have image support but no line_ranges
188+
expect(description).not.toContain("line_ranges")
189+
expect(description).toContain(
190+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
191+
)
192+
})
193+
194+
it("should correctly combine all three options", () => {
195+
const tool = createReadFileTool({
196+
maxConcurrentFileReads: 3,
197+
partialReadsEnabled: true,
198+
supportsImages: true,
199+
})
200+
const description = getFunctionDef(tool).description
201+
202+
expect(description).toContain("maximum of 3 files")
203+
expect(description).toContain("line_ranges")
204+
expect(description).toContain(
205+
"Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis",
206+
)
207+
})
123208
})
124209

125210
describe("tool structure", () => {

src/core/prompts/tools/native-tools/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export interface NativeToolsOptions {
3434
partialReadsEnabled?: boolean
3535
/** Maximum number of files that can be read in a single read_file request (default: 5) */
3636
maxConcurrentFileReads?: number
37+
/** Whether the model supports image processing (default: false) */
38+
supportsImages?: boolean
3739
}
3840

3941
/**
@@ -43,11 +45,12 @@ export interface NativeToolsOptions {
4345
* @returns Array of native tool definitions
4446
*/
4547
export function getNativeTools(options: NativeToolsOptions = {}): OpenAI.Chat.ChatCompletionTool[] {
46-
const { partialReadsEnabled = true, maxConcurrentFileReads = 5 } = options
48+
const { partialReadsEnabled = true, maxConcurrentFileReads = 5, supportsImages = false } = options
4749

4850
const readFileOptions: ReadFileToolOptions = {
4951
partialReadsEnabled,
5052
maxConcurrentFileReads,
53+
supportsImages,
5154
}
5255

5356
return [

src/core/prompts/tools/native-tools/read_file.ts

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
11
import type OpenAI from "openai"
22

3-
const READ_FILE_SUPPORTS_NOTE = `Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.`
3+
/**
4+
* Generates the file support note, optionally including image format support.
5+
*
6+
* @param supportsImages - Whether the model supports image processing
7+
* @returns Support note string
8+
*/
9+
function getReadFileSupportsNote(supportsImages: boolean): string {
10+
if (supportsImages) {
11+
return `Supports text extraction from PDF and DOCX files. Automatically processes and returns image files (PNG, JPG, JPEG, GIF, BMP, SVG, WEBP, ICO, AVIF) for visual analysis. May not handle other binary files properly.`
12+
}
13+
return `Supports text extraction from PDF and DOCX files, but may not handle other binary files properly.`
14+
}
415

516
/**
617
* Options for creating the read_file tool definition.
@@ -10,6 +21,8 @@ export interface ReadFileToolOptions {
1021
partialReadsEnabled?: boolean
1122
/** Maximum number of files that can be read in a single request (default: 5) */
1223
maxConcurrentFileReads?: number
24+
/** Whether the model supports image processing (default: false) */
25+
supportsImages?: boolean
1326
}
1427

1528
/**
@@ -20,7 +33,7 @@ export interface ReadFileToolOptions {
2033
* @returns Native tool definition for read_file
2134
*/
2235
export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Chat.ChatCompletionTool {
23-
const { partialReadsEnabled = true, maxConcurrentFileReads = 5 } = options
36+
const { partialReadsEnabled = true, maxConcurrentFileReads = 5, supportsImages = false } = options
2437
const isMultipleReadsEnabled = maxConcurrentFileReads > 1
2538

2639
// Build description intro with concurrent reads limit message
@@ -50,7 +63,8 @@ export function createReadFileTool(options: ReadFileToolOptions = {}): OpenAI.Ch
5063
? `Example multiple files (within ${maxConcurrentFileReads}-file limit): { files: [{ path: 'file1.ts' }, { path: 'file2.ts' }] }`
5164
: "")
5265

53-
const description = baseDescription + optionalRangesDescription + READ_FILE_SUPPORTS_NOTE + " " + examples
66+
const description =
67+
baseDescription + optionalRangesDescription + getReadFileSupportsNote(supportsImages) + " " + examples
5468

5569
// Build the properties object conditionally
5670
const fileProperties: Record<string, any> = {

src/core/task/Task.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1444,6 +1444,10 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
14441444
this.handleWebviewAskResponse("noButtonClicked", text, images)
14451445
}
14461446

1447+
public supersedePendingAsk(): void {
1448+
this.lastMessageTs = Date.now()
1449+
}
1450+
14471451
/**
14481452
* Updates the API configuration but preserves the locked tool protocol.
14491453
* The task's tool protocol is locked at creation time and should NOT change

src/core/task/__tests__/validateToolResultIds.spec.ts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,96 @@ describe("validateAndFixToolResultIds", () => {
482482
expect(resultContent[1].type).toBe("text")
483483
expect((resultContent[1] as Anthropic.TextBlockParam).text).toBe("Some additional context")
484484
})
485+
486+
// Verifies fix for GitHub #10465: Terminal fallback race condition can generate
487+
// duplicate tool_results with the same valid tool_use_id, causing API protocol violations.
488+
it("should filter out duplicate tool_results with identical valid tool_use_ids (terminal fallback scenario)", () => {
489+
const assistantMessage: Anthropic.MessageParam = {
490+
role: "assistant",
491+
content: [
492+
{
493+
type: "tool_use",
494+
id: "tooluse_QZ-pU8v2QKO8L8fHoJRI2g",
495+
name: "execute_command",
496+
input: { command: "ps aux | grep test", cwd: "/path/to/project" },
497+
},
498+
],
499+
}
500+
501+
// Two tool_results with the SAME valid tool_use_id from terminal fallback race condition
502+
const userMessage: Anthropic.MessageParam = {
503+
role: "user",
504+
content: [
505+
{
506+
type: "tool_result",
507+
tool_use_id: "tooluse_QZ-pU8v2QKO8L8fHoJRI2g", // First result from command execution
508+
content: "No test processes found",
509+
},
510+
{
511+
type: "tool_result",
512+
tool_use_id: "tooluse_QZ-pU8v2QKO8L8fHoJRI2g", // Duplicate from user approval during fallback
513+
content: '{"status":"approved","message":"The user approved this operation"}',
514+
},
515+
],
516+
}
517+
518+
const result = validateAndFixToolResultIds(userMessage, [assistantMessage])
519+
520+
expect(Array.isArray(result.content)).toBe(true)
521+
const resultContent = result.content as Anthropic.ToolResultBlockParam[]
522+
523+
// Only ONE tool_result should remain to prevent API protocol violation
524+
expect(resultContent.length).toBe(1)
525+
expect(resultContent[0].tool_use_id).toBe("tooluse_QZ-pU8v2QKO8L8fHoJRI2g")
526+
expect(resultContent[0].content).toBe("No test processes found")
527+
})
528+
529+
it("should preserve text blocks while deduplicating tool_results with same valid ID", () => {
530+
const assistantMessage: Anthropic.MessageParam = {
531+
role: "assistant",
532+
content: [
533+
{
534+
type: "tool_use",
535+
id: "tool-123",
536+
name: "read_file",
537+
input: { path: "test.txt" },
538+
},
539+
],
540+
}
541+
542+
const userMessage: Anthropic.MessageParam = {
543+
role: "user",
544+
content: [
545+
{
546+
type: "tool_result",
547+
tool_use_id: "tool-123",
548+
content: "First result",
549+
},
550+
{
551+
type: "text",
552+
text: "Environment details here",
553+
},
554+
{
555+
type: "tool_result",
556+
tool_use_id: "tool-123", // Duplicate with same valid ID
557+
content: "Duplicate result from fallback",
558+
},
559+
],
560+
}
561+
562+
const result = validateAndFixToolResultIds(userMessage, [assistantMessage])
563+
564+
expect(Array.isArray(result.content)).toBe(true)
565+
const resultContent = result.content as Array<Anthropic.ToolResultBlockParam | Anthropic.TextBlockParam>
566+
567+
// Should have: 1 tool_result + 1 text block (duplicate filtered out)
568+
expect(resultContent.length).toBe(2)
569+
expect(resultContent[0].type).toBe("tool_result")
570+
expect((resultContent[0] as Anthropic.ToolResultBlockParam).tool_use_id).toBe("tool-123")
571+
expect((resultContent[0] as Anthropic.ToolResultBlockParam).content).toBe("First result")
572+
expect(resultContent[1].type).toBe("text")
573+
expect((resultContent[1] as Anthropic.TextBlockParam).text).toBe("Environment details here")
574+
})
485575
})
486576

487577
describe("when there are more tool_uses than tool_results", () => {

src/core/task/build-tools.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ export async function buildNativeToolsArray(options: BuildToolsOptions): Promise
6464
// Determine if partial reads are enabled based on maxReadFileLine setting.
6565
const partialReadsEnabled = maxReadFileLine !== -1
6666

67+
// Check if the model supports images for read_file tool description.
68+
const supportsImages = modelInfo?.supportsImages ?? false
69+
6770
// Build native tools with dynamic read_file tool based on settings.
6871
const nativeTools = getNativeTools({
6972
partialReadsEnabled,
7073
maxConcurrentFileReads,
74+
supportsImages,
7175
})
7276

7377
// Filter native tools based on mode restrictions.

src/core/task/validateToolResultIds.ts

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,31 @@ export function validateAndFixToolResultIds(
7878
}
7979

8080
// Find tool_result blocks in the user message
81-
const toolResults = userMessage.content.filter(
81+
let toolResults = userMessage.content.filter(
82+
(block): block is Anthropic.ToolResultBlockParam => block.type === "tool_result",
83+
)
84+
85+
// Deduplicate tool_result blocks to prevent API protocol violations (GitHub #10465)
86+
// Terminal fallback race conditions can generate duplicate tool_results with the same tool_use_id.
87+
// Filter out duplicates before validation since Set-based checks below would miss them.
88+
const seenToolResultIds = new Set<string>()
89+
const deduplicatedContent = userMessage.content.filter((block) => {
90+
if (block.type !== "tool_result") {
91+
return true
92+
}
93+
if (seenToolResultIds.has(block.tool_use_id)) {
94+
return false // Duplicate - filter out
95+
}
96+
seenToolResultIds.add(block.tool_use_id)
97+
return true
98+
})
99+
100+
userMessage = {
101+
...userMessage,
102+
content: deduplicatedContent,
103+
}
104+
105+
toolResults = deduplicatedContent.filter(
82106
(block): block is Anthropic.ToolResultBlockParam => block.type === "tool_result",
83107
)
84108

@@ -139,15 +163,12 @@ export function validateAndFixToolResultIds(
139163
)
140164
}
141165

142-
// Create a mapping of tool_result IDs to corrected IDs
143-
// Strategy: Match by position (first tool_result -> first tool_use, etc.)
144-
// This handles most cases where the mismatch is due to ID confusion
145-
//
146-
// Track which tool_use IDs have been used to prevent duplicates
166+
// Match tool_results to tool_uses by position and fix incorrect IDs
147167
const usedToolUseIds = new Set<string>()
168+
const contentArray = userMessage.content as Anthropic.Messages.ContentBlockParam[]
148169

149-
const correctedContent = userMessage.content
150-
.map((block) => {
170+
const correctedContent = contentArray
171+
.map((block: Anthropic.Messages.ContentBlockParam) => {
151172
if (block.type !== "tool_result") {
152173
return block
153174
}
@@ -177,17 +198,18 @@ export function validateAndFixToolResultIds(
177198
}
178199

179200
// No corresponding tool_use for this tool_result, or the ID is already used
180-
// Filter out this orphaned tool_result by returning null
181201
return null
182202
})
183203
.filter((block): block is NonNullable<typeof block> => block !== null)
184204

185205
// Add missing tool_result blocks for any tool_use that doesn't have one
186-
// After the ID correction above, recalculate which tool_use IDs are now covered
187206
const coveredToolUseIds = new Set(
188207
correctedContent
189-
.filter((b): b is Anthropic.ToolResultBlockParam => b.type === "tool_result")
190-
.map((r) => r.tool_use_id),
208+
.filter(
209+
(b: Anthropic.Messages.ContentBlockParam): b is Anthropic.ToolResultBlockParam =>
210+
b.type === "tool_result",
211+
)
212+
.map((r: Anthropic.ToolResultBlockParam) => r.tool_use_id),
191213
)
192214

193215
const stillMissingToolUseIds = toolUseBlocks.filter((toolUse) => !coveredToolUseIds.has(toolUse.id))

src/core/tools/ExecuteCommandTool.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@ export class ExecuteCommandTool extends BaseTool<"execute_command"> {
117117
provider?.postMessageToWebview({ type: "commandExecutionStatus", text: JSON.stringify(status) })
118118
await task.say("shell_integration_warning")
119119

120+
// Invalidate pending ask from first execution to prevent race condition
121+
task.supersedePendingAsk()
122+
120123
if (error instanceof ShellIntegrationError) {
121124
const [rejected, result] = await executeCommandInTerminal(task, {
122125
...options,

0 commit comments

Comments
 (0)