- Add useAdaptiveThinking and adaptiveThinkingEffort settings to provider config
- Add supportsAdaptiveThinking and supportsAdaptiveThinkingMaxEffort model flags
- Implement getAnthropicReasoning() with {type: 'adaptive'} mode
- Implement getAnthropicOutputConfig() with effort parameter (low/medium/high/max)
- Max effort only on Opus 4.6; falls back to high on Sonnet 4.6
- Add adaptive thinking checkbox and effort selector to ThinkingBudget UI
- Fix: Max Output Tokens slider always visible (independent of adaptive thinking)
- Add i18n translations for all 17 supported locales
- Add comprehensive test coverage for reasoning transforms
Fixes#11732
* fix: correct Bedrock model ID for Claude Opus 4.6 (#11232)
Remove the :0 suffix from the Claude Opus 4.6 model ID to match
the correct AWS Bedrock model identifier.
The model ID was "anthropic.claude-opus-4-6-v1:0" but should be
"anthropic.claude-opus-4-6-v1" per AWS Bedrock documentation.
Fixes#11231
Co-authored-by: Roo Code <roomote@roocode.com>
* fix: guard against empty-string baseURL in provider constructors (#11233)
When the 'custom base URL' checkbox is unchecked in the UI, the setting
is set to '' (empty string). Providers that passed this directly to their
SDK constructors caused 'Failed to parse URL' errors because the SDK
treated '' as a valid but broken base URL override.
- gemini.ts: use || undefined (was passing raw option)
- openai-native.ts: use || undefined (was passing raw option)
- openai.ts: change ?? to || for fallback default
- deepseek.ts: change ?? to || for fallback default
- moonshot.ts: change ?? to || for fallback default
Adds test coverage for Gemini and OpenAI Native constructors verifying
empty-string baseURL is coerced to undefined.
* fix: make defaultTemperature required in getModelParams to prevent silent temperature overrides (#11218)
* fix: DeepSeek temperature defaulting to 0 instead of 0.3
Pass defaultTemperature: DEEP_SEEK_DEFAULT_TEMPERATURE to getModelParams() in
DeepSeekHandler.getModel() to ensure the correct default temperature (0.3)
is used when no user configuration is provided.
Closes#11194
* refactor: make defaultTemperature required in getModelParams
Make the defaultTemperature parameter required in getModelParams() instead
of defaulting to 0. This prevents providers with their own non-zero default
temperature (like DeepSeek's 0.3) from being silently overridden by the
implicit 0 default.
Every provider now explicitly declares its temperature default, making the
temperature resolution chain clear:
user setting → model default → provider default
---------
Co-authored-by: Roo Code <roomote@roocode.com>
Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
* feat: batch consecutive tool calls in chat UI with shared utility (#11245)
* feat: group consecutive list_files tool calls into single UI block
Consolidate consecutive listFilesTopLevel/listFilesRecursive ask messages
into a single 'Roo wants to view multiple directories' block, matching the
existing read_file batching pattern.
* chore: add missing translation keys for all locales
* refactor: consolidate duplicate listFiles batch-handling blocks in ChatRow
Merge the separate listFilesTopLevel and listFilesRecursive case blocks
into a single combined case with shared batch-detection logic, selecting
the icon and translation key based on the tool type. This removes the
duplicated isBatchDirRequest check and BatchListFilesPermission render.
* feat: batch consecutive file-edit tool calls into single UI block
Add edit-file batching in ChatView groupedMessages that consolidates
consecutive editedExistingFile, appliedDiff, newFileCreated,
insertContent, and searchAndReplace asks into a single BatchDiffApproval
block. Move batchDiffs detection in ChatRow above the switch statement
so it applies to any file-edit tool type.
* refactor: extract batchConsecutive utility, fix batch UI issues
- Extract generic batchConsecutive() utility from 3 identical while-loops
- Fix React key collisions in BatchListFilesPermission, BatchFilePermission, BatchDiffApproval
- Normalize language prop to "shellsession" (was "shell-session" for top-level)
- Remove unused _batchedMessages property from synthetic messages
- Remove dead didViewMultipleDirectories i18n key from all 18 locale files
- Add batch button text for listFilesTopLevel/listFilesRecursive
- Add batchConsecutive utility tests (6 cases)
* fix: audit improvements for batch tool-call UI
- Make batchConsecutive() generic instead of ClineMessage-specific
- Add batch-aware button text for edit-file batches ("Save All"/"Deny All")
- Add dedicated list-batch/edit-batch i18n keys (stop reusing read-batch)
- Add JSON.parse defense-in-depth in all three synthesizers
- Fix mixed list_files batch icon to default to FolderTree
- Add 6 missing test cases (all-match, immutability, spy, single-dir)
* chore: minor type cleanup (out-of-scope housekeeping)
- Trim unused recursive/isOutsideWorkspace from DirPermissionItem interface
- Remove 4 pre-existing `as any` casts in ChatView.tsx:
- window cast → precise inline type
- checkpoint bracket access → removed unnecessary casts
- condensing message → `as ClineMessage`
- debounce cancel → `.clear()` (correct API)
- Update BatchListFilesPermission test data to match trimmed interface
* i18n: add list-batch and edit-batch translations for all locales
* feat: add IPC query handlers for commands, modes, and models (#11279)
Add GetCommands, GetModes, and GetModels to the IPC protocol so external
clients can fetch slash commands, available modes, and Roo provider models
without going through the internal webview message channel.
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add lock toggle to pin API config across all modes in workspace (#11295)
* feat: add lock toggle to pin API config across all modes in workspace
Add a lock/unlock toggle inside the API config selector popover (next to
the settings gear) that, when enabled, applies the selected API
configuration to all modes in the current workspace.
- Add lockApiConfigAcrossModes to ExtensionState and WebviewMessage types
- Store setting in workspaceState (per-workspace, not global)
- When locked, activateProviderProfile sets config for all modes
- Lock icon in ApiConfigSelector popover bottom bar next to gear
- Full i18n: English + 17 locale translations (all mention workspace scope)
- 9 new tests: 2 ClineProvider, 2 handler, 5 UI (77 total pass)
* refactor: replace write-fan-out with read-time override for lock API config
The original lock implementation used setModeConfig() fan-out to write the
locked config to ALL modes globally. Since the lock flag lives in workspace-
scoped workspaceState but modeApiConfigs are in global secrets, this caused
cross-workspace data destruction.
Replaced with read-time guards:
- handleModeSwitch: early return when lock is on (skip per-mode config load)
- createTaskWithHistoryItem: skip mode-based config restoration under lock
- activateProviderProfile: removed fan-out block
- lockApiConfigAcrossModes handler: simplified to flag + state post only
- Fixed pre-existing workspaceState mock gap in ClineProvider.spec.ts and
ClineProvider.sticky-profile.spec.ts
* fix: validate Gemini thinkingLevel against model capabilities and handle empty streams (#11303)
* fix: validate Gemini thinkingLevel against model capabilities and handle empty streams
getGeminiReasoning() now validates the selected effort against the model's
supportsReasoningEffort array before sending it as thinkingLevel. When a
stale settings value (e.g. 'medium' from a different model) is not in the
supported set, it falls back to the model's default reasoningEffort.
GeminiHandler.createMessage() now tracks whether any text content was
yielded during streaming and handles NoOutputGeneratedError gracefully
instead of surfacing the cryptic 'No output generated' error.
* fix: guard thinkingLevel fallback against 'none' effort and add i18n TODO
The array validation fallback in getGeminiReasoning() now only triggers
when the selected effort IS a valid Gemini thinking level but not in
the model's supported set. Values like 'none' (explicit no-reasoning
signal) are no longer overridden by the model default.
Also adds a TODO for moving the empty-stream message to i18n.
* fix: track tool_call_start in hasContent to avoid false empty-stream warning
Tool-only responses (no text) are valid content. Without this,
agentic tool-call responses would incorrectly trigger the empty
response warning message.
* chore(cli): prepare release v0.0.53 (#11425)
* feat: add GLM-5 model support to Z.ai provider (#11440)
* chore: regenerate pnpm-lock.yaml
* fix: resolve type errors and remove AI SDK test contamination
* docs: update progress.txt with rebuilt Batch 2 status
---------
Co-authored-by: roomote[bot] <219738659+roomote[bot]@users.noreply.github.com>
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: Claude Opus 4.6 <noreply@anthropic.com>
Revert to pre-AI-SDK state (commit 67e568f6b)
This commit reverts the codebase to the state before AI SDK migration work began.
Target commit: 67e568f6b - refactor: replace fetch_instructions with skill tool and built-in skills (#10913)
Date: January 29, 2026
This removes approximately 152 commits of AI SDK migration work.
A follow-up PR will add back bug fixes and features that are unrelated to AI SDK.
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
* refactor: remove 9 low-usage providers (Phase 0)
Remove Cerebras, Chutes, DeepInfra, Doubao, Featherless, Groq,
Hugging Face, IO Intelligence, and Unbound providers from the codebase.
Each provider removal includes: handler, tests, model definitions,
type schemas, UI settings components, fetchers, i18n references,
and all wiring in shared registration/config files.
- Delete 42 provider-specific files (handlers, tests, fetchers, UI components)
- Remove @ai-sdk/cerebras and @ai-sdk/groq npm dependencies
- Clean provider references from 68 shared files across src/, packages/types/,
webview-ui/, and apps/cli/
- Remove ~490 dead i18n translation keys across 36 locale files
- Add docs/ai-sdk-migration-guide.md with updated migration status
- All TypeScript checks pass, 6505 tests pass with 0 failures
* feat: show retired-provider message for removed provider profiles
Preserve API profiles that reference removed providers instead of
silently stripping their apiProvider. When a user selects a profile
configured for a retired provider, the settings UI now shows an
empathetic message explaining the removal instead of the provider
configuration form.
- Add retiredProviderNames array and isRetiredProvider() helper to
packages/types/src/provider-settings.ts
- Update ProviderSettingsManager sanitization to preserve retired
providers (only strip truly unknown values)
- Update ContextProxy sanitization to preserve retired providers
- Render retired-provider message in ApiOptions.tsx when selected
provider is in the retired list
- Add tests for sanitization, ContextProxy, and UI behavior
* feat: add retired-provider warning banner in chat view
* Revert "feat: add retired-provider warning banner in chat view"
This reverts commit dd593e1056.
* feat: show retired-provider message as inline chat response
* fix: show retired provider warning on home screen
Move WarningRow outside {task && ...} conditional so it renders
regardless of task state. Preserve user input on retired provider
intercept so text isn't lost when switching providers.
- Move showRetiredProviderWarning WarningRow to unconditional render
area near ProfileViolationWarning
- Remove setInputValue/setSelectedImages clearing from retired
provider early return in handleSendMessage
- Delete unused RetiredProviderWarning.tsx (dead code)
* fix: address PR review — passthrough retired-provider fields and i18n strings
- Use passthrough() in saveConfig() and load() so legacy provider-specific
fields (e.g. groqApiKey, deepInfraModelId) are preserved instead of
silently stripped by strict Zod parse()
- Move hardcoded English strings in ApiOptions.tsx and ChatView.tsx to
i18n translation keys (settings:providers.retiredProviderMessage,
chat:retiredProvider.{title,message,openSettings})
- Update tests to assert legacy provider-specific fields survive
save and load round-trips
* i18n: add retired-provider translations for all 17 locales
Translate providers.retiredProviderMessage (settings) and
retiredProvider.{title,message,openSettings} (chat) into ca, de, es,
fr, hi, id, it, ja, ko, nl, pl, pt-BR, ru, tr, vi, zh-CN, zh-TW.
* test: update ApiOptions retired-provider test to expect i18n key
* fix: validate Gemini thinkingLevel against model capabilities and handle empty streams
getGeminiReasoning() now validates the selected effort against the model's
supportsReasoningEffort array before sending it as thinkingLevel. When a
stale settings value (e.g. 'medium' from a different model) is not in the
supported set, it falls back to the model's default reasoningEffort.
GeminiHandler.createMessage() now tracks whether any text content was
yielded during streaming and handles NoOutputGeneratedError gracefully
instead of surfacing the cryptic 'No output generated' error.
* fix: guard thinkingLevel fallback against 'none' effort and add i18n TODO
The array validation fallback in getGeminiReasoning() now only triggers
when the selected effort IS a valid Gemini thinking level but not in
the model's supported set. Values like 'none' (explicit no-reasoning
signal) are no longer overridden by the model default.
Also adds a TODO for moving the empty-stream message to i18n.
* fix: track tool_call_start in hasContent to avoid false empty-stream warning
Tool-only responses (no text) are valid content. Without this,
agentic tool-call responses would incorrectly trigger the empty
response warning message.
* feat: migrate Bedrock provider to AI SDK
Replace the raw AWS SDK (@aws-sdk/client-bedrock-runtime) Bedrock handler
with the Vercel AI SDK (@ai-sdk/amazon-bedrock). Reduces provider from
1,633 lines to 575 lines (65% reduction).
Key changes:
- Use streamText()/generateText() instead of ConverseStreamCommand/ConverseCommand
- Use createAmazonBedrock() with native auth (access key, secret, session,
profile via credentialProvider, API key, VPC endpoint as baseURL)
- Reasoning config via providerOptions.bedrock.reasoningConfig
- Anthropic beta headers via providerOptions.bedrock.anthropicBeta
- Thinking signature captured from providerMetadata.bedrock.signature
on reasoning-delta stream events
- Thinking signature round-tripped via providerOptions.bedrock.signature
on reasoning parts in convertToAiSdkMessages()
- Redacted thinking captured from providerMetadata.bedrock.redactedData
- isAiSdkProvider() returns true for reasoning block preservation
- Keep: getModel, ARN parsing, cross-region inference, cost calculation,
service tier pricing, 1M context beta
Tests: 83 tests skipped (mock old AWS SDK internals, need rewrite for
AI SDK mocking). 106 tests pass. 0 tests fail.
* fix: address review feedback for Bedrock AI SDK migration
- Wire usePromptCache into AI SDK via providerOptions.bedrock.cachePoint
on system prompt and last two user messages
- Remove debug logger.info that fires on every stream event with
providerMetadata
- Tighten isThrottlingError to match 'rate limit' instead of broad
'rate'/'limit' substrings that false-positive on context length errors
- Use shared handleAiSdkError utility for consistent error handling
with status code preservation for retry logic
* fix: bedrock AI SDK migration - fix usage metrics, rewrite tests, remove dead code
- Fix reasoningTokens always 0 (usage.details?.reasoningTokens → usage.reasoningTokens)
- Fix cacheReadInputTokens always 0 (read from usage.inputTokenDetails instead of providerMetadata)
- Fix invokedModelId not extracted for prompt router cost calculation
- Rewrite all 6 skipped bedrock test suites for AI SDK mocking pattern (140 tests pass)
- Remove dead code: bedrock-converse-format.ts, cache-strategy/ (6 files, ~2700 lines)
* chore: remove dead @anthropic-ai/bedrock-sdk dep and stale AWS SDK mocks
* chore: update pnpm-lock.yaml after removing @anthropic-ai/bedrock-sdk
* fix: compute cache point indices from original Anthropic messages before AI SDK conversion
The previous approach naively targeted the last 2 user messages in the
post-conversion AI SDK array, but convertToAiSdkMessages() splits user
messages containing tool_results into separate tool + user messages,
causing cache points to land on the wrong messages (tiny text fragments
instead of the intended meaty user turns).
Now we identify the last 2 user messages in the original Anthropic
message array (matching the Anthropic provider's caching strategy) and
build a parallel-walk mapping to apply cachePoint to the correct
corresponding AI SDK message.
* perf: optimize prompt caching with 3-point message strategy + anchor for 20-block window
Previous approach only cached the last 2 user messages (using 2 of 4
available cache checkpoints for messages). This left significant cache
savings on the table for longer conversations.
New strategy uses up to 3 message cache points (+ 1 system = 4 total):
- Last user message: write to cache for next request
- Second-to-last user message: read from cache for current request
- Anchor message at ~1/3 position: ensures the 20-block lookback window
from the second-to-last breakpoint hits a stable cache entry, covering
all assistant/tool messages in the middle of the conversation
Also extracted the parallel-walk mapping logic into a reusable
applyCachePointsToAiSdkMessages() helper method.
Industry benchmarks show 70-95% token cache rates are achievable;
this change should significantly improve our 39% baseline for longer
multi-turn conversations.
* chore: remove stale bedrock-sdk external, fix arnInfo property name, remove unused exports
---------
Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
* fix: DeepSeek temperature defaulting to 0 instead of 0.3
Pass defaultTemperature: DEEP_SEEK_DEFAULT_TEMPERATURE to getModelParams() in
DeepSeekHandler.getModel() to ensure the correct default temperature (0.3)
is used when no user configuration is provided.
Closes#11194
* refactor: make defaultTemperature required in getModelParams
Make the defaultTemperature parameter required in getModelParams() instead
of defaulting to 0. This prevents providers with their own non-zero default
temperature (like DeepSeek's 0.3) from being silently overridden by the
implicit 0 default.
Every provider now explicitly declares its temperature default, making the
temperature resolution chain clear:
user setting → model default → provider default
---------
Co-authored-by: Roo Code <roomote@roocode.com>
Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
* fix: capture and round-trip thinking signature for Bedrock Claude models
Bedrock handler streams reasoning text from Claude's extended thinking but
never captures the cryptographic signature. This causes 400 errors on
multi-turn conversations with tool use: 'Expected thinking or
redacted_thinking, but found tool_use'.
Changes:
- bedrock.ts: Capture reasoningContent.signature from Converse API stream
deltas, implement getThoughtSignature() so Task.ts stores it as a proper
thinking content block
- bedrock-converse-format.ts: Convert thinking blocks to Bedrock's
reasoningContent format with signature, skip reasoning/redacted_thinking/
thoughtSignature blocks that aren't valid for the API
* fix: add redacted_thinking round-trip, fix interface types, add tests
Address PR review feedback:
- Update ContentBlockDeltaEvent interface to include signature and
redactedContent fields (removes type assertions)
- Add 6 tests for thinking/reasoning block conversions in
bedrock-converse-format.ts
Also add redacted_thinking round-trip support:
- bedrock.ts: Capture redactedContent from stream deltas, base64 encode,
expose via getRedactedThinkingBlocks()
- Task.ts: Insert redacted_thinking blocks after thinking block in
assistant messages
- bedrock-converse-format.ts: Convert redacted_thinking blocks back to
reasoningContent.redactedContent (base64 → Uint8Array)
PR #11180 migrated Gemini/Vertex providers to the AI SDK and deleted
gemini-format.ts which contained the working thought signature round-trip
logic (originally added in PR #10590). This broke all Gemini 3 tool use
with a 400 error: 'Function call is missing a thought_signature'.
Changes:
- Gemini/Vertex handlers: capture thoughtSignature from providerMetadata
on tool-call stream events, expose via getThoughtSignature()
- convertToAiSdkMessages(): extract thoughtSignature content blocks from
history, attach as providerOptions on first tool-call part (per Gemini 3
parallel call rules)
- Add 3 tests verifying thought signature round-trip behavior
* fix(ai-sdk): preserve reasoning parts in message conversion
* fix(ai-sdk): convert message-level reasoning_content to reasoning part
* fix(task): remove invalid openai-compatible from reasoning allowlist
* feat: add isAiSdkProvider() method for dynamic AI SDK provider detection
- Add isAiSdkProvider() method to ApiHandler interface
- Default implementation in BaseProvider returns false
- Override to return true in 11 AI SDK providers:
deepseek, fireworks, mistral, groq, xai, cerebras,
sambanova, huggingface, gemini, vertex, openai-compatible
- Update Task.ts to use dynamic detection instead of hardcoded Set
- Add method to FakeAIHandler and update test mocks
* fix: handle reasoning parts in flattenAiSdkMessagesToStringContent
- Strip reasoning parts when flattening messages for string-only models
- Allow flattening when message contains only text and reasoning parts
- Add tests for reasoning part handling in string-only model contexts
This addresses the review feedback about ensuring flattenAiSdkMessagesToStringContent
works correctly when reasoning parts are present (e.g., SambaNova DeepSeek).
---------
Co-authored-by: daniel-lxs <ricciodaniel98@gmail.com>
* fix(ai-sdk): preserve reasoning parts in message conversion
* fix(ai-sdk): convert message-level reasoning_content to reasoning part
* fix(task): remove invalid openai-compatible from reasoning allowlist
* feat: migrate Gemini and Vertex providers to AI SDK
- Migrate GeminiHandler from @google/genai to @ai-sdk/google
- Create standalone VertexHandler using @ai-sdk/google-vertex
- Use shared AI SDK utilities (streamText, generateText, convertToAiSdkMessages)
- Support thinkingConfig via providerOptions.google.thinkingConfig
- Support Google Search and URL Context grounding tools
- Preserve cost calculation with tiered pricing
- Remove gemini-format.ts (AI SDK handles message conversion)
EXT-643
* fix: remove unused import and implement allowedFunctionNames tool filtering
- Remove unused handleAiSdkError import from gemini.ts
- Implement tool filtering based on allowedFunctionNames in both
GeminiHandler and VertexHandler createMessage methods
- Filter tools before converting to AI SDK format to restrict
model access to only allowed functions