Remove "pattern" from UNSUPPORTED_SCHEMA_CONSTRAINTS so glob/grep tool
schemas keep the required "pattern" property for the antigravity provider.
Fixesdecolua/9router#1368
Co-authored-by: Cursor <cursoragent@cursor.com>
Resolve the Kiro thinking budget from client intent (OpenAI reasoning_effort,
OpenAI Responses reasoning.effort, Claude output_config.effort, and Claude
thinking.budget_tokens) by reusing the shared thinkingUnified extractThinking
parser, then inject the resolved budget into the Kiro thinking system prefix.
Explicit none/off/disabled stops the prefix injection; synthetic -thinking
aliases keep the default budget.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add config-driven stripUnsupportedParams helper and use it in the default and github executors. Removes the deprecated temperature param for claude-opus-4 models (Anthropic 400) and consolidates github's scattered capability checks into one rule table.
Fixes#1748
Co-Authored-By: fjia <fjia@suntekcorps.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Adds long-lived API-key (ksk_) authentication for Kiro/AWS CodeWhisperer
and a direct claude:kiro / kiro:claude translation route that avoids the
lossy OpenAI two-hop pivot.
- translator: claude-to-kiro request + kiro-to-claude response translators,
registered on the exact source:target pair (direct route ahead of the
OpenAI pivot in index.js). claude-to-kiro uses shared schema constants
(ROLE/CLAUDE_BLOCK/DEFAULT_IMAGE_MIME) per app convention.
- auth: POST /api/oauth/kiro/api-key imports + validates a key via
ListAvailableProfiles, persists authMethod="api_key" (no refresh token).
- executor: send tokentype: API_KEY header and try *.amazonaws.com hosts
first for api-key creds; OAuth keeps kiro.dev first.
- fix: never inject the default placeholder profileArn for api-key auth
(CodeWhisperer 403s an ARN not owned by the key's account).
- ui: API Key method in the Kiro connect modal; surface api-key accounts
on the Quota Tracker and provider count.
- stream: env-overridable TTFT vs stall timeouts + Kiro keepalive frame.
- tests: claude-kiro-direct + kiro-profile-arn (11 tests).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Add helpers/usageHelper.js for conditional prompt/completion token details.
Apply to gemini/codex/claude response translators; keep each provider's token
math intact. No behavior change; golden + gate: no regression.
Co-authored-by: Cursor <cursoragent@cursor.com>
Add helpers/chunkBuilder.js; apply to claude/gemini/kiro/ollama/commandcode/
openai-responses response translators. Caller supplies id/created/model so each
keeps exact id-generation + usage semantics. Extend golden response stream to
openai-responses (codex). No behavior change; gate: no regression.
Co-authored-by: Cursor <cursoragent@cursor.com>
Remove dynamic x-anthropic-billing-header lines from Claude system prompts
when translating to OpenAI format to keep prompt prefixes stable and improve
prompt cache hits.
Co-authored-by: Cursor <cursoragent@cursor.com>
convertOpenAIToolChoice mapped {type:"function"} verbatim and cloakClaudeTools
left tool_choice.name unsuffixed, both rejected by Claude on the cc/ path.
Map forced-function to {type:"tool",name}, allowlist Claude-valid types, and
suffix tool_choice.name when it targets a renamed client tool.
Fixes#1592
Co-authored-by: Cursor <cursoragent@cursor.com>
Kiro requires a non-empty currentMessage tools array whenever history
references any tool use, else returns "Improperly formed request" (400).
Clients trip this by omitting tools on follow-ups after client-side
compaction.
- flattenToolInteractions(): no client tools -> collapse tool_use/result
to text so the "tools required" rule never fires
- reconcileOrphanedToolResults(): client tools -> salvage orphaned
results as text, keep matched ones, guard co-located tools array
- safeJSONParse(): guard tool-call argument parsing against bad JSON
- merge consecutive user userInputMessageContext; null-guard
currentMessage for assistant-only input
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: sanitize Read tool args to prevent retry loops from non-Anthropic models
* fix: sanitize invalid Read pages from tool args
Non-Anthropic models sometimes emit optional Read args like pages: "" for
non-PDF files, which Claude Code rejects before the tool runs. Drop invalid
pages values, keep valid PDF page ranges, and coerce numeric string bounds
before clamping limit/offset.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Some OpenAI-compatible providers emit optional string tool parameters
as empty strings (e.g. pages: "") instead of omitting them. Claude
Code rejects pages: "" as invalid, breaking the Read tool for
non-PDF files routed through 9router.
Add sanitizeToolArguments() that parses tool-call arguments and
removes known optional empty-string fields before emitting
input_json_delta back to Claude format. Currently handles the
Read tool pages field specifically.
Includes regression test.
Fixes#1278
Co-authored-by: JoJo <noreply@github.com>
## Features
- Add Vercel AI Gateway provider support (#1183)
- rtk: Kiro format tool result compression — handle conversationState.history & currentMessage, preserve error results, ~13.6% savings (#1194)
## Fixes
- openclaw: normalize agent.model object form `{primary, fallbacks}` before .startsWith → fix TypeError & 'not configured' status (#1216)
- Usage Details pagination: stay inside mobile viewport <640px (#1218)
- Fix test model error
- Fix MIMO provider in Codex
- Disable log file creation when using MITM AG
Deepseek API (and likely other providers) reject messages with
role: 'developer' — only accept system, user, assistant, tool.
filterToOpenAIFormat() normalizes content blocks but never touched
message roles, so developer passed through unmodified and caused
400 errors (issue #773).
Fix: add one-line developer → system mapping in filterToOpenAIFormat()
before role-specific logic. This is the common normalization point
called for all targetFormat=openai providers (Deepseek, Groq, Mistral,
Perplexity, Together, Fireworks, Cerebras, xAI, NVIDIA, etc.)
Closes#773
Add input_audio and audio_url content type handlers to
convertOpenAIContentToParts() in geminiHelper.js, converting
OpenAI audio format to Gemini inlineData format.
Also add audio types to VALID_OPENAI_CONTENT_TYPES in
openaiHelper.js so they are not stripped by filterToOpenAIFormat().
Fixes#912
- BaseUrlSelect: add cloud endpoint option, custom URL local state, always
default to first option; new cliEndpointMatch helper; CLI tool cards refactor
- API: new /v1/audio/voices and /v1/models/info; /v1/models filters disabled
models, drop unused timestamp
- initializeApp: guard tunnel/tailscale auto-resume to once-per-process
- geminiHelper: ensureObjectType for schemas with properties but no type
- skills: minor SKILL.md tweaks (chat/embeddings/image/stt/tts/web-*)
- Refactored handleChatCore to include Caveman functionality, allowing for terse-style system prompts to reduce output token usage.
- Updated APIPageClient to manage Caveman settings, including enabling/disabling and selecting compression levels.
- Adjusted AntigravityExecutor to consolidate function declarations for compatibility with Gemini.
- Removed unnecessary console logs during translator initialization across multiple routes.
- github.js: split thinking vs reasoning_effort stripping
- thinking (Claude-native format) still stripped for all Claude on Copilot
- reasoning_effort now passed through for Opus 4.6 and Sonnet 4.6
- still stripped for Haiku 4.5 and Opus 4.7 (rejected upstream)
- reasoning_effort "none" stripped for all models (not all support it)
- openai-to-claude.js: map reasoning_effort → thinking.budget_tokens
for direct Anthropic backend (none→skip, low→4096, medium→8192,
high→16384, xhigh→32768)
Previously reasoning_effort was stripped for ALL Claude models,
meaning Opus 4.6 via Copilot never received thinking configuration.
AI-generated commit by Claude Opus 4.6 (Anthropic)
Remote HTTP(S) image URLs are fetched and inlined as base64 data URIs
in a new prefetchImages() step run before super.execute(), so the body
sent to Codex contains resolved image bytes instead of URLs the backend
cannot access.
Scope is limited to the Codex executor — base executor and other
providers are untouched.
Co-authored-by: anuragg-saxenaa <anuragg.saxenaa@gmail.com>
Made-with: Cursor