@iislee/opencodex 2.11.0 → 2.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +114 -19
- package/bin/ocx.mjs +164 -36
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-BF38heuV.js +104 -0
- package/gui/dist/assets/index-DMiI18Kv.css +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +19 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +376 -52
- package/src/adapters/base.ts +54 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +129 -6
- package/src/adapters/cursor/discovery.ts +61 -4
- package/src/adapters/cursor/effort-map.ts +27 -3
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +174 -58
- package/src/adapters/cursor/live-transport.ts +609 -170
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +0 -1
- package/src/adapters/cursor/native-exec.ts +101 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +383 -65
- package/src/adapters/cursor/request-builder.ts +237 -31
- package/src/adapters/cursor/tool-definitions.ts +242 -12
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +28 -1
- package/src/adapters/cursor.ts +133 -7
- package/src/adapters/google-antigravity-replay.ts +558 -34
- package/src/adapters/google-antigravity-wire.ts +43 -10
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +618 -74
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +10 -1
- package/src/adapters/kiro.ts +92 -54
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1311 -336
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +830 -56
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +104 -21
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +428 -61
- package/src/chat/inbound.ts +43 -19
- package/src/chat/outbound.ts +82 -26
- package/src/claude/agents-inject.ts +32 -9
- package/src/claude/context-windows.ts +21 -5
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +70 -16
- package/src/cli/account-api.ts +35 -1
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +389 -2
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +30 -6
- package/src/cli/agent.ts +45 -1
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +29 -4
- package/src/cli/claude.ts +103 -34
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +593 -0
- package/src/cli/doctor.ts +315 -43
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +20 -271
- package/src/cli/index.ts +378 -519
- package/src/cli/init.ts +4 -17
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +35 -2
- package/src/cli/models.ts +100 -14
- package/src/cli/observe.ts +92 -3
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +18 -1
- package/src/cli/provider.ts +24 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +437 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +6 -3
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +10 -3
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/v2.ts +105 -10
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1358 -21
- package/src/codex/account-label.ts +14 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +493 -106
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +849 -242
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +345 -32
- package/src/codex/autostart-health.ts +8 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +68 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +121 -30
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +529 -45
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +224 -30
- package/src/codex/catalog/provider-fetch.ts +1460 -134
- package/src/codex/catalog/sync.ts +1449 -186
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +651 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +407 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +26 -20
- package/src/codex/history-provider.ts +231 -28
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1073 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +80 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +506 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +492 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +682 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +175 -13
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +505 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1009 -28
- package/src/codex/subagent-model-fallback.ts +350 -35
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +20 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +81 -9
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +1762 -140
- package/src/generated/compatibility-version.json +3164 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +152 -29
- package/src/images/plan.ts +23 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +159 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +361 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +691 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +153 -9
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +6 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +44 -2
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +4 -1
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +8 -2
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +398 -66
- package/src/oauth/key-providers.ts +16 -0
- package/src/oauth/kimi.ts +16 -2
- package/src/oauth/kiro.ts +50 -6
- package/src/oauth/local-token-detect.ts +11 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/store.ts +119 -21
- package/src/oauth/token-guardian.ts +9 -3
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +22 -5
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +253 -6
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +1 -1
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +29 -0
- package/src/providers/quota.ts +1319 -38
- package/src/providers/registry.ts +1429 -111
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +42 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +11 -4
- package/src/reasoning-effort.ts +49 -1
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +266 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +355 -0
- package/src/responses/parser.ts +220 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +7 -1
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +565 -27
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +366 -30
- package/src/routing/analytics.ts +378 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +267 -46
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +130 -56
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +159 -43
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +865 -181
- package/src/server/lifecycle.ts +284 -13
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +398 -103
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/combo-routes.ts +62 -24
- package/src/server/management/config-routes.ts +464 -51
- package/src/server/management/context.ts +80 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +101 -32
- package/src/server/management/model-routes.ts +189 -131
- package/src/server/management/model-rows.ts +163 -0
- package/src/server/management/native-integration-routes.ts +769 -0
- package/src/server/management/oauth-account-routes.ts +80 -4
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +764 -157
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +288 -32
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +115 -14
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +132 -5
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +82 -42
- package/src/server/relay.ts +236 -76
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log.ts +235 -22
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/collaboration.ts +204 -35
- package/src/server/responses/compact.ts +442 -55
- package/src/server/responses/core.ts +2872 -331
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +79 -4
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +251 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +342 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +153 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +39 -38
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1111 -90
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +845 -0
- package/src/types/provider.ts +545 -0
- package/src/types/request.ts +384 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +104 -1236
- package/src/update/index.ts +32 -19
- package/src/update/job.ts +442 -67
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +268 -16
- package/src/usage/log.ts +606 -41
- package/src/usage/summary.ts +177 -9
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +9 -3
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +11 -3
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +279 -50
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IncomingMeta, ProviderAdapter } from "./base";
|
|
2
|
+
import { createToolCallIdAllocator, type ToolCallIdAllocator } from "./tool-call-id";
|
|
2
3
|
import { debugDroppedFrame } from "../lib/debug";
|
|
3
4
|
import type {
|
|
4
5
|
AdapterEvent,
|
|
@@ -18,7 +19,10 @@ import { ANTHROPIC_OAUTH_BETA, CLAUDE_CODE_SYSTEM_INSTRUCTION, applyClaudeToolPr
|
|
|
18
19
|
import { parseDataUrl } from "./image";
|
|
19
20
|
import { enforceAnthropicImageLimits } from "./anthropic-image-guard";
|
|
20
21
|
import { normalizeAnthropicImages } from "./anthropic-image-normalize";
|
|
21
|
-
import {
|
|
22
|
+
import { normalizeAnthropicOutputSchema } from "./anthropic-output-schema";
|
|
23
|
+
import { stripResponsesOnlyEncryptedMarker } from "./responses-tool-schema";
|
|
24
|
+
import { identifyRoutedModel } from "./identity";
|
|
25
|
+
import { redactSecretString } from "../lib/redact";
|
|
22
26
|
import { CLAUDE_CODE_HEADERS, claudeCodeSessionId } from "./client-fingerprint";
|
|
23
27
|
import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
|
|
24
28
|
import { decodeServerSentEvents } from "../lib/sse-decoder";
|
|
@@ -243,6 +247,43 @@ function isLikelyRealAnthropicThinkingSignature(signature: string | undefined):
|
|
|
243
247
|
return /^[A-Za-z0-9+/_=-]+$/.test(signature);
|
|
244
248
|
}
|
|
245
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Bridge error fidelity (web-search/images loops): extract a display-safe summary from an
|
|
252
|
+
* Anthropic JSON error envelope so `Provider error <status>` carries the upstream reason.
|
|
253
|
+
* JSON-only extraction — HTML/non-JSON bodies yield "" so raw markup is never echoed.
|
|
254
|
+
*/
|
|
255
|
+
export function formatAnthropicErrorBody(status: number, _headers: Headers, payloadText: string): string {
|
|
256
|
+
let parsed: unknown;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(payloadText);
|
|
259
|
+
} catch {
|
|
260
|
+
return "";
|
|
261
|
+
}
|
|
262
|
+
const detail = extractAnthropicErrorDetail(parsed);
|
|
263
|
+
if (!detail) return "";
|
|
264
|
+
return redactSecretString(detail).slice(0, 400);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function extractAnthropicErrorDetail(parsed: unknown): string | undefined {
|
|
268
|
+
if (typeof parsed === "string") return parsed.trim() || undefined;
|
|
269
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return undefined;
|
|
270
|
+
const obj = parsed as Record<string, unknown>;
|
|
271
|
+
// Anthropic envelope: { type: "error", error: { type, message } }; tolerate a bare
|
|
272
|
+
// { error: { message } } and a string error field.
|
|
273
|
+
const err = obj.error;
|
|
274
|
+
if (typeof err === "string" && err.trim()) return err.trim();
|
|
275
|
+
if (err !== null && typeof err === "object" && !Array.isArray(err)) {
|
|
276
|
+
const e = err as Record<string, unknown>;
|
|
277
|
+
const msg = e.message;
|
|
278
|
+
if (typeof msg !== "string" || !msg.trim()) return undefined;
|
|
279
|
+
const type = e.type;
|
|
280
|
+
return typeof type === "string" && type.trim()
|
|
281
|
+
? `${type.trim()}: ${msg.trim()}`
|
|
282
|
+
: msg.trim();
|
|
283
|
+
}
|
|
284
|
+
return undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
246
287
|
function usesNativeAnthropicEndpoint(provider: OcxProviderConfig): boolean {
|
|
247
288
|
try {
|
|
248
289
|
return new URL(provider.baseUrl).hostname === "api.anthropic.com";
|
|
@@ -277,7 +318,69 @@ function usableToolUseId(id: unknown): string {
|
|
|
277
318
|
return typeof id === "string" && id.trim() ? id : synthesizeToolUseId();
|
|
278
319
|
}
|
|
279
320
|
|
|
280
|
-
|
|
321
|
+
/**
|
|
322
|
+
* Bound repair for a malformed tool-arguments string under the compatibility profile (#658):
|
|
323
|
+
* a gateway such as AgentRouter can concatenate JSON objects (`{}{"value":42}`). Find the
|
|
324
|
+
* last parseable JSON object by scanning suffixes from each object-open brace and prefixes
|
|
325
|
+
* ending at each object-close brace. Both scans walk backwards from the end trying at most
|
|
326
|
+
* `maxCandidates` positions, so no offset index is ever materialized: a brace-dense hostile
|
|
327
|
+
* input costs at most 2 × maxCandidates bounded JSON.parse attempts and no extra storage.
|
|
328
|
+
* Inputs above MAX_REPAIRABLE_TOOL_ARGUMENT_BYTES are not repaired at all.
|
|
329
|
+
*/
|
|
330
|
+
const MAX_REPAIRABLE_TOOL_ARGUMENT_BYTES = 1024 * 1024;
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Whether `input` encodes to more than `max` UTF-8 bytes, with an early exit so the check
|
|
334
|
+
* itself never allocates a copy of a hostile string. `string.length` counts UTF-16 code
|
|
335
|
+
* units, which undercounts astral text by 2x against a byte budget.
|
|
336
|
+
*/
|
|
337
|
+
function utf8BytesExceed(input: string, max: number): boolean {
|
|
338
|
+
let bytes = 0;
|
|
339
|
+
for (let i = 0; i < input.length; i++) {
|
|
340
|
+
const code = input.charCodeAt(i);
|
|
341
|
+
if (code < 0x80) bytes += 1;
|
|
342
|
+
else if (code < 0x800) bytes += 2;
|
|
343
|
+
else if (code >= 0xd800 && code <= 0xdbff && i + 1 < input.length
|
|
344
|
+
&& input.charCodeAt(i + 1) >= 0xdc00 && input.charCodeAt(i + 1) <= 0xdfff) {
|
|
345
|
+
// A complete surrogate pair is one 4-byte scalar. Anything else — a high surrogate
|
|
346
|
+
// followed by another high surrogate or a non-surrogate — encodes as two separate
|
|
347
|
+
// U+FFFD replacements, so the next unit must NOT be skipped.
|
|
348
|
+
bytes += 4;
|
|
349
|
+
i++;
|
|
350
|
+
} else bytes += 3; // lone surrogates encode as U+FFFD (3 bytes)
|
|
351
|
+
if (bytes > max) return true;
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function lastValidJsonObject(input: string, maxCandidates: number): string | undefined {
|
|
357
|
+
const tryParseObject = (candidate: string): string | undefined => {
|
|
358
|
+
try {
|
|
359
|
+
const parsed = JSON.parse(candidate) as unknown;
|
|
360
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return candidate;
|
|
361
|
+
} catch { /* keep scanning */ }
|
|
362
|
+
return undefined;
|
|
363
|
+
};
|
|
364
|
+
let scanFrom = input.length - 1;
|
|
365
|
+
for (let tried = 0; tried < maxCandidates && scanFrom >= 0; tried++) {
|
|
366
|
+
const open = input.lastIndexOf("{", scanFrom);
|
|
367
|
+
if (open === -1) break;
|
|
368
|
+
const repaired = tryParseObject(input.slice(open));
|
|
369
|
+
if (repaired !== undefined) return repaired;
|
|
370
|
+
scanFrom = open - 1;
|
|
371
|
+
}
|
|
372
|
+
scanFrom = input.length - 1;
|
|
373
|
+
for (let tried = 0; tried < maxCandidates && scanFrom >= 0; tried++) {
|
|
374
|
+
const close = input.lastIndexOf("}", scanFrom);
|
|
375
|
+
if (close === -1) break;
|
|
376
|
+
const repaired = tryParseObject(input.slice(0, close + 1));
|
|
377
|
+
if (repaired !== undefined) return repaired;
|
|
378
|
+
scanFrom = close - 1;
|
|
379
|
+
}
|
|
380
|
+
return undefined;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function toolUseArguments(input: unknown, lenient = false): string {
|
|
281
384
|
if (typeof input === "string") {
|
|
282
385
|
const trimmed = input.trim();
|
|
283
386
|
if (!trimmed) return "{}";
|
|
@@ -285,6 +388,10 @@ function toolUseArguments(input: unknown): string {
|
|
|
285
388
|
JSON.parse(trimmed);
|
|
286
389
|
return trimmed;
|
|
287
390
|
} catch {
|
|
391
|
+
if (lenient && !utf8BytesExceed(trimmed, MAX_REPAIRABLE_TOOL_ARGUMENT_BYTES)) {
|
|
392
|
+
const repaired = lastValidJsonObject(trimmed, 32);
|
|
393
|
+
if (repaired !== undefined) return repaired;
|
|
394
|
+
}
|
|
288
395
|
// A tool call's arguments must be a JSON object. Re-encoding an unparseable string as a
|
|
289
396
|
// JSON *string* is the double-encoding #765 reports: the caller then receives
|
|
290
397
|
// `"get weather"` where an object was required and the tool call is unusable either way.
|
|
@@ -342,16 +449,68 @@ const ADAPTIVE_THINKING_FAMILY_MINIMUMS: Record<string, readonly [major: number,
|
|
|
342
449
|
fable: [0, 0],
|
|
343
450
|
};
|
|
344
451
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
452
|
+
/**
|
|
453
|
+
* Family/version parse for a Claude model id, tolerant of a routing prefix.
|
|
454
|
+
*
|
|
455
|
+
* `parsed.modelId` is not always bare, and the slash can fall on either side.
|
|
456
|
+
* A `modelMap` entry may point at a routed destination such as
|
|
457
|
+
* `anthropic/claude-sonnet-5` (prefix), while a custom provider may expose a
|
|
458
|
+
* native id such as `claude-sonnet-5/variant` (suffix); both survive routing's
|
|
459
|
+
* known-id decoding. So this matches the segment that actually begins with
|
|
460
|
+
* `claude-` rather than assuming it is the first or the last one. A capability
|
|
461
|
+
* predicate that quietly returns false is worse than one that throws — the
|
|
462
|
+
* request just goes out wrong.
|
|
463
|
+
*
|
|
464
|
+
* Minor is 1-2 digits with a non-digit lookahead so date-pinned ids
|
|
465
|
+
* ("claude-opus-4-20250514") parse as minor 0 instead of minor 20250514;
|
|
466
|
+
* suffixed ids ("claude-opus-4-8[1m]") still match.
|
|
467
|
+
*/
|
|
468
|
+
function claudeFamilyVersion(modelId: string): { family: string; major: number; minor: number } | undefined {
|
|
469
|
+
// Find the segment that actually starts with `claude-`, rather than assuming it is either
|
|
470
|
+
// the first (breaks `anthropic/claude-sonnet-5`) or the last (breaks `claude-sonnet-5/variant`,
|
|
471
|
+
// where the slash carries a vendor suffix rather than a routing prefix).
|
|
472
|
+
const match = /(?:^|\/)claude-([a-z]+)-(\d+)(?:-(\d{1,2}))?(?!\d)/.exec(modelId);
|
|
473
|
+
if (!match) return undefined;
|
|
474
|
+
return {
|
|
475
|
+
family: match[1]!,
|
|
476
|
+
major: Number(match[2]),
|
|
477
|
+
minor: match[3] === undefined ? 0 : Number(match[3]),
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function meetsFamilyMinimum(
|
|
482
|
+
modelId: string,
|
|
483
|
+
minimums: Record<string, readonly [major: number, minor: number]>,
|
|
484
|
+
): boolean {
|
|
485
|
+
const parsed = claudeFamilyVersion(modelId);
|
|
486
|
+
if (!parsed) return false;
|
|
487
|
+
const minimum = minimums[parsed.family];
|
|
351
488
|
if (!minimum) return false;
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
489
|
+
return parsed.major > minimum[0] || (parsed.major === minimum[0] && parsed.minor >= minimum[1]);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function usesAdaptiveThinking(modelId: string): boolean {
|
|
493
|
+
return meetsFamilyMinimum(modelId, ADAPTIVE_THINKING_FAMILY_MINIMUMS);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Claude families that (a) think by DEFAULT when the request omits `thinking`,
|
|
498
|
+
* and (b) accept an explicit `thinking: {type: "disabled"}` to turn it off.
|
|
499
|
+
*
|
|
500
|
+
* Deliberately NOT `usesAdaptiveThinking()`, which answers a different question
|
|
501
|
+
* (which wire shape a family accepts). The two sets differ in both directions:
|
|
502
|
+
* Fable always thinks and REJECTS an explicit disable, while Opus 4.7/4.8 use
|
|
503
|
+
* the adaptive wire but leave thinking off when the field is omitted, so they
|
|
504
|
+
* need no disable at all. Seeded with the family where the defect reproduces
|
|
505
|
+
* (#545); widen only with vendor evidence, since a wrong entry here turns a
|
|
506
|
+
* silent truncation into a 400.
|
|
507
|
+
*/
|
|
508
|
+
const EXPLICIT_THINKING_DISABLE_FAMILY_MINIMUMS: Record<string, readonly [major: number, minor: number]> = {
|
|
509
|
+
sonnet: [5, 0],
|
|
510
|
+
};
|
|
511
|
+
|
|
512
|
+
function supportsExplicitThinkingDisable(modelId: string): boolean {
|
|
513
|
+
return meetsFamilyMinimum(modelId, EXPLICIT_THINKING_DISABLE_FAMILY_MINIMUMS);
|
|
355
514
|
}
|
|
356
515
|
|
|
357
516
|
/** `output_config.effort` accepts low|medium|high|xhigh|max — "minimal" is rejected with a 400. */
|
|
@@ -401,7 +560,7 @@ function buildToolNameTransforms(provider: OcxProviderConfig): { toWire: (name:
|
|
|
401
560
|
return { toWire: (name) => name, fromWire: (name) => name };
|
|
402
561
|
}
|
|
403
562
|
|
|
404
|
-
function toAnthropicToolResult(msg: OcxToolResultMessage): Record<string, unknown> {
|
|
563
|
+
function toAnthropicToolResult(msg: OcxToolResultMessage, wireCallId: string): Record<string, unknown> {
|
|
405
564
|
// Anthropic tool_result accepts a string OR content blocks — render images natively
|
|
406
565
|
// (e.g. Codex view_image output) instead of dropping them.
|
|
407
566
|
let content: string | unknown[];
|
|
@@ -416,12 +575,17 @@ function toAnthropicToolResult(msg: OcxToolResultMessage): Record<string, unknow
|
|
|
416
575
|
}
|
|
417
576
|
return {
|
|
418
577
|
type: "tool_result",
|
|
419
|
-
tool_use_id:
|
|
578
|
+
tool_use_id: wireCallId,
|
|
420
579
|
content,
|
|
421
580
|
...(msg.isError ? { is_error: true } : {}),
|
|
422
581
|
};
|
|
423
582
|
}
|
|
424
583
|
|
|
584
|
+
function unrepresentableToolCallText(tc: OcxToolCall, wireName: string): string {
|
|
585
|
+
const args = typeof tc.arguments === "string" ? tc.arguments : JSON.stringify(tc.arguments);
|
|
586
|
+
return `[tool_use without a usable id: ${wireName}]\n${args}`;
|
|
587
|
+
}
|
|
588
|
+
|
|
425
589
|
function orphanToolResultText(msg: OcxToolResultMessage): string {
|
|
426
590
|
const label = msg.toolName ? `${msg.toolName} (${msg.toolCallId})` : msg.toolCallId;
|
|
427
591
|
const content = typeof msg.content === "string"
|
|
@@ -430,10 +594,80 @@ function orphanToolResultText(msg: OcxToolResultMessage): string {
|
|
|
430
594
|
return `[tool_result without adjacent tool_use: ${label}]\n${content}`;
|
|
431
595
|
}
|
|
432
596
|
|
|
597
|
+
/**
|
|
598
|
+
* AgentRouter answers 400 `content-blocked` when the first user message is not in English
|
|
599
|
+
* (#2074), while the same request in English returns 200. The gateway is inspecting the opening
|
|
600
|
+
* user content, so an Anthropic `system` string cannot reach it — the framing has to sit in the
|
|
601
|
+
* first user turn.
|
|
602
|
+
*/
|
|
603
|
+
const AGENTROUTER_LANGUAGE_PREAMBLE =
|
|
604
|
+
"[Instruction: Process the user request below and respond in the appropriate language.]";
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Exact host match, not a substring.
|
|
608
|
+
*
|
|
609
|
+
* A `hostname.includes("agentrouter")` test also matches `notagentrouter.example` and
|
|
610
|
+
* `agentrouter.org.attacker.example`, which would let an unrelated destination silently
|
|
611
|
+
* receive an injected instruction block. A prompt mutation keyed on a provider's identity
|
|
612
|
+
* must be keyed on that identity exactly.
|
|
613
|
+
*/
|
|
614
|
+
function isAgentRouterEndpoint(baseUrl: string): boolean {
|
|
615
|
+
try {
|
|
616
|
+
const { hostname } = new URL(baseUrl);
|
|
617
|
+
return hostname === "agentrouter.org" || hostname.endsWith(".agentrouter.org");
|
|
618
|
+
} catch {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Prepend the framing as its OWN text block instead of splicing it into the user's string.
|
|
625
|
+
*
|
|
626
|
+
* The distinction matters: rewriting `content` to `${marker}\n\n${original}` edits what the
|
|
627
|
+
* user wrote, and every downstream consumer — logs, retries, an upstream that echoes the turn —
|
|
628
|
+
* then sees a sentence the user never typed as if they had. A separate leading block carries the
|
|
629
|
+
* same signal to the filter while the original text survives byte-for-byte.
|
|
630
|
+
*
|
|
631
|
+
* Only the first user turn is framed, because only the first is what the gateway rejects.
|
|
632
|
+
*/
|
|
633
|
+
function applyAgentRouterLanguageFraming(messages: unknown[]): void {
|
|
634
|
+
const firstUser = messages.find(
|
|
635
|
+
(m): m is { role: string; content: unknown } =>
|
|
636
|
+
typeof m === "object" && m !== null && (m as { role?: unknown }).role === "user",
|
|
637
|
+
);
|
|
638
|
+
if (!firstUser) return;
|
|
639
|
+
const preamble = { type: "text", text: AGENTROUTER_LANGUAGE_PREAMBLE };
|
|
640
|
+
if (typeof firstUser.content === "string") {
|
|
641
|
+
firstUser.content = firstUser.content === ""
|
|
642
|
+
? [preamble]
|
|
643
|
+
: [preamble, { type: "text", text: firstUser.content }];
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (!Array.isArray(firstUser.content)) return;
|
|
647
|
+
// Idempotence is keyed on the LEADING block being exactly the marker. A substring test would
|
|
648
|
+
// let a user who quotes the marker later in their own prompt suppress the framing entirely.
|
|
649
|
+
const [head] = firstUser.content as { type?: unknown; text?: unknown }[];
|
|
650
|
+
if (head?.type === "text" && head.text === AGENTROUTER_LANGUAGE_PREAMBLE) return;
|
|
651
|
+
(firstUser.content as unknown[]).unshift(preamble);
|
|
652
|
+
}
|
|
653
|
+
|
|
433
654
|
function messagesToAnthropicFormat(
|
|
434
655
|
parsed: OcxParsedRequest,
|
|
435
656
|
toolNames: { toWire: (name: string) => string },
|
|
436
657
|
): { system: string | undefined; messages: unknown[] } {
|
|
658
|
+
// One allocator for the whole request: a tool_result must resolve to the SAME wire id its
|
|
659
|
+
// call got, and two distinct raw ids must never collapse into one. Conforming ids are claimed
|
|
660
|
+
// first so a rewritten id can never squat on an id another call legitimately owns.
|
|
661
|
+
const callIds = createToolCallIdAllocator();
|
|
662
|
+
for (const message of parsed.context.messages) {
|
|
663
|
+
if (message.role === "assistant") {
|
|
664
|
+
for (const part of (message as OcxAssistantMessage).content) {
|
|
665
|
+
if (part.type === "toolCall") callIds.reserve((part as OcxToolCall).id);
|
|
666
|
+
}
|
|
667
|
+
} else if (message.role === "toolResult") {
|
|
668
|
+
callIds.reserve((message as OcxToolResultMessage).toolCallId);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
437
671
|
const toolCatalogNudge = buildNonOpenAIToolCatalogNudgeForTools(
|
|
438
672
|
parsed.context.tools,
|
|
439
673
|
parsed.options.toolChoice,
|
|
@@ -441,7 +675,7 @@ function messagesToAnthropicFormat(
|
|
|
441
675
|
);
|
|
442
676
|
const systemParts = [...(parsed.context.systemPrompt ?? []), ...(toolCatalogNudge ? [toolCatalogNudge] : [])];
|
|
443
677
|
const system = systemParts.length
|
|
444
|
-
?
|
|
678
|
+
? identifyRoutedModel(systemParts.join("\n\n"), parsed.modelId) || undefined
|
|
445
679
|
: undefined;
|
|
446
680
|
const messages: unknown[] = [];
|
|
447
681
|
|
|
@@ -485,8 +719,17 @@ function messagesToAnthropicFormat(
|
|
|
485
719
|
} else if (part.type === "toolCall") {
|
|
486
720
|
const tc = part as OcxToolCall;
|
|
487
721
|
const flatName = namespacedToolName(tc.namespace, tc.name);
|
|
488
|
-
|
|
489
|
-
|
|
722
|
+
// Normalized here, and identically for the matching tool_result above, so a history
|
|
723
|
+
// replayed from another provider path keeps its call/result pairing (#1767).
|
|
724
|
+
// No raw fallback: restoring an empty/unusable id puts a value on the wire Anthropic
|
|
725
|
+
// rejects. An unrepresentable call becomes text instead, and its result follows it there.
|
|
726
|
+
const wireCallId = callIds.allocate(tc.id);
|
|
727
|
+
if (wireCallId === undefined) {
|
|
728
|
+
preface.push({ type: "text", text: unrepresentableToolCallText(tc, toolNames.toWire(flatName)) });
|
|
729
|
+
continue;
|
|
730
|
+
}
|
|
731
|
+
toolUseIds.push(wireCallId);
|
|
732
|
+
toolUses.push({ type: "tool_use", id: wireCallId, name: toolNames.toWire(flatName), input: tc.arguments });
|
|
490
733
|
}
|
|
491
734
|
}
|
|
492
735
|
// Anthropic treats text/thinking after tool_use as ending the tool turn, which makes
|
|
@@ -502,9 +745,13 @@ function messagesToAnthropicFormat(
|
|
|
502
745
|
let j = i + 1;
|
|
503
746
|
while (j < parsed.context.messages.length && parsed.context.messages[j].role === "toolResult") {
|
|
504
747
|
const tr = parsed.context.messages[j] as OcxToolResultMessage;
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
748
|
+
// Match on the WIRE id. requiredIds holds normalized ids, so comparing the raw result id
|
|
749
|
+
// made every rewritten pair lose its result to orphan text and gain a synthetic
|
|
750
|
+
// missing-result block. lookup() never mints an id: a result with no call stays orphan.
|
|
751
|
+
const wireResultId = callIds.lookup(tr.toolCallId);
|
|
752
|
+
if (wireResultId !== undefined && requiredIds.has(wireResultId) && !seen.has(wireResultId)) {
|
|
753
|
+
resultBlocks.push(toAnthropicToolResult(tr, wireResultId));
|
|
754
|
+
seen.add(wireResultId);
|
|
508
755
|
} else {
|
|
509
756
|
orphanBlocks.push({ type: "text", text: orphanToolResultText(tr) });
|
|
510
757
|
}
|
|
@@ -553,7 +800,7 @@ function toolsToAnthropicFormat(parsed: OcxParsedRequest, toolNames: { toWire: (
|
|
|
553
800
|
? new Set(parsed.options.toolChoice.allowedTools)
|
|
554
801
|
: undefined;
|
|
555
802
|
const tools = allowed
|
|
556
|
-
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed))
|
|
803
|
+
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed, parsed.context.tools))
|
|
557
804
|
: parsed.context.tools;
|
|
558
805
|
if (tools.length === 0) return undefined;
|
|
559
806
|
const converted = tools.map(t => ({
|
|
@@ -564,35 +811,8 @@ function toolsToAnthropicFormat(parsed: OcxParsedRequest, toolNames: { toWire: (
|
|
|
564
811
|
return converted;
|
|
565
812
|
}
|
|
566
813
|
|
|
567
|
-
// Codex multi-agent v2 stamps a Responses-only `encrypted: true` marker on
|
|
568
|
-
// collaboration tool schemas (openai/codex 5f4d06ef; issue #85). It is an
|
|
569
|
-
// annotation for the ChatGPT backend only. Anthropic input_schema is strict
|
|
570
|
-
// JSON Schema; strip the marker defensively everywhere it can appear as a
|
|
571
|
-
// schema keyword, while preserving properties literally named "encrypted".
|
|
572
|
-
const ENCRYPTED_MARKER_NAME_BAG_KEYS = new Set(["properties", "patternProperties", "$defs", "definitions"]);
|
|
573
|
-
const ENCRYPTED_MARKER_LITERAL_VALUE_KEYS = new Set(["const", "default", "enum", "examples"]);
|
|
574
|
-
|
|
575
|
-
function stripEncryptedMarker(node: unknown, inNameBag = false): unknown {
|
|
576
|
-
if (Array.isArray(node)) return node.map(item => stripEncryptedMarker(item));
|
|
577
|
-
if (!node || typeof node !== "object") return node;
|
|
578
|
-
|
|
579
|
-
const out: Record<string, unknown> = {};
|
|
580
|
-
|
|
581
|
-
for (const [key, value] of Object.entries(node as Record<string, unknown>)) {
|
|
582
|
-
if (inNameBag) {
|
|
583
|
-
out[key] = stripEncryptedMarker(value);
|
|
584
|
-
} else if (key !== "encrypted") {
|
|
585
|
-
out[key] = ENCRYPTED_MARKER_LITERAL_VALUE_KEYS.has(key)
|
|
586
|
-
? value
|
|
587
|
-
: stripEncryptedMarker(value, ENCRYPTED_MARKER_NAME_BAG_KEYS.has(key));
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
return out;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
814
|
function normalizeAnthropicInputSchema(schema: unknown): Record<string, unknown> {
|
|
595
|
-
const stripped =
|
|
815
|
+
const stripped = stripResponsesOnlyEncryptedMarker(schema);
|
|
596
816
|
const obj = stripped && typeof stripped === "object" && !Array.isArray(stripped)
|
|
597
817
|
? stripped as Record<string, unknown>
|
|
598
818
|
: {};
|
|
@@ -659,6 +879,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
659
879
|
return {
|
|
660
880
|
name: "anthropic",
|
|
661
881
|
|
|
882
|
+
formatErrorBody: formatAnthropicErrorBody,
|
|
883
|
+
|
|
662
884
|
async buildRequest(parsed: OcxParsedRequest, incoming?: IncomingMeta) {
|
|
663
885
|
if (typeof provider.apiKey !== "string" || provider.apiKey.trim() === "") {
|
|
664
886
|
if (isOAuth) {
|
|
@@ -668,6 +890,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
668
890
|
}
|
|
669
891
|
|
|
670
892
|
const { system, messages } = messagesToAnthropicFormat(parsed, toolNames);
|
|
893
|
+
// Before image normalization, so the framing block is present for every downstream pass.
|
|
894
|
+
if (isAgentRouterEndpoint(provider.baseUrl)) applyAgentRouterLanguageFraming(messages);
|
|
671
895
|
// Primary image layer: resize/re-encode to fit Anthropic limits without dropping
|
|
672
896
|
// (anthropic-image-normalize.ts); the guard below remains the deterministic backstop.
|
|
673
897
|
// imageTierBias > 0 = upstream-413 tightened retry (030): start every image one tier lower.
|
|
@@ -700,7 +924,14 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
700
924
|
// `reasoning` is a Codex effort string; "none" is the disable sentinel (see parser.ts
|
|
701
925
|
// REASONING_EFFORTS). A bare truthy check would treat "none" as truthy and wrongly enable
|
|
702
926
|
// extended thinking (and strip temperature/top_p), so gate on a real, non-disable effort.
|
|
703
|
-
|
|
927
|
+
//
|
|
928
|
+
// "none" is not the same as absent. Omitting `thinking` lets a default-on model think
|
|
929
|
+
// anyway, and thinking shares the caller's `max_tokens` — which truncates a small-budget
|
|
930
|
+
// request before it can emit its stop sequence (#545). Say "disabled" out loud where the
|
|
931
|
+
// model both defaults to thinking and accepts being told not to.
|
|
932
|
+
if (parsed.options.reasoning === "none" && supportsExplicitThinkingDisable(parsed.modelId)) {
|
|
933
|
+
body.thinking = { type: "disabled" };
|
|
934
|
+
} else if (typeof parsed.options.reasoning === "string" && parsed.options.reasoning !== "none") {
|
|
704
935
|
if (usesAdaptiveThinking(parsed.modelId)) {
|
|
705
936
|
// Adaptive-thinking models replace the token budget with an effort knob and reject
|
|
706
937
|
// `thinking.type: "enabled"` outright. `max_tokens` still caps thinking plus visible
|
|
@@ -733,6 +964,20 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
733
964
|
delete body.top_p;
|
|
734
965
|
}
|
|
735
966
|
|
|
967
|
+
const textFormat = parsed.options.textFormat;
|
|
968
|
+
if (textFormat?.type === "json_schema" && textFormat.schema) {
|
|
969
|
+
const outputConfig = body.output_config;
|
|
970
|
+
body.output_config = {
|
|
971
|
+
...(outputConfig && typeof outputConfig === "object" && !Array.isArray(outputConfig)
|
|
972
|
+
? outputConfig
|
|
973
|
+
: {}),
|
|
974
|
+
format: {
|
|
975
|
+
type: "json_schema",
|
|
976
|
+
schema: normalizeAnthropicOutputSchema(textFormat.schema),
|
|
977
|
+
},
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
|
|
736
981
|
if (parsed.options.toolChoice && (tools || parsed.options.toolChoice === "none")) {
|
|
737
982
|
const tc = parsed.options.toolChoice;
|
|
738
983
|
if (tc === "auto") body.tool_choice = { type: "auto" };
|
|
@@ -798,10 +1043,23 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
798
1043
|
let pendingUsage: Record<string, number> | undefined;
|
|
799
1044
|
let pendingStopReason: string | undefined;
|
|
800
1045
|
let emittedDone = false;
|
|
1046
|
+
let sawVisibleText = false;
|
|
801
1047
|
|
|
802
1048
|
const emitDone = function* (): Generator<AdapterEvent> {
|
|
803
1049
|
if (emittedDone) return;
|
|
804
1050
|
emittedDone = true;
|
|
1051
|
+
// An `error` stop reason is a failed generation, not a stop. Forwarding it as `done`
|
|
1052
|
+
// lets the turn report success and install replacement history on a compaction turn.
|
|
1053
|
+
if (pendingStopReason === "error") {
|
|
1054
|
+
yield {
|
|
1055
|
+
type: "error",
|
|
1056
|
+
message: "upstream ended the turn with stop_reason \"error\"",
|
|
1057
|
+
status: 502,
|
|
1058
|
+
errorType: "upstream_error",
|
|
1059
|
+
usage: usageFromAnthropic(pendingUsage),
|
|
1060
|
+
};
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
805
1063
|
yield {
|
|
806
1064
|
type: "done",
|
|
807
1065
|
usage: usageFromAnthropic(pendingUsage),
|
|
@@ -818,13 +1076,21 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
818
1076
|
const payload = record.data.trim();
|
|
819
1077
|
if (!payload) continue;
|
|
820
1078
|
|
|
821
|
-
let
|
|
1079
|
+
let parsed: unknown;
|
|
822
1080
|
try {
|
|
823
|
-
|
|
1081
|
+
parsed = JSON.parse(payload);
|
|
824
1082
|
} catch {
|
|
825
1083
|
debugDroppedFrame("anthropic", payload);
|
|
826
1084
|
continue;
|
|
827
1085
|
}
|
|
1086
|
+
// `JSON.parse("null")` returns null instead of throwing, so the catch above cannot cover
|
|
1087
|
+
// it and the `data.type` read below crashed the stream. Drop a non-record frame the same
|
|
1088
|
+
// way an unparseable one is dropped, so the message_stop check still governs the outcome.
|
|
1089
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1090
|
+
debugDroppedFrame("anthropic", payload);
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
const data = parsed as Record<string, unknown>;
|
|
828
1094
|
|
|
829
1095
|
switch (record.event || data.type) {
|
|
830
1096
|
case "message_start": {
|
|
@@ -853,6 +1119,10 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
853
1119
|
const delta = data.delta as Record<string, unknown> | undefined;
|
|
854
1120
|
if (!delta) break;
|
|
855
1121
|
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
1122
|
+
// Only non-empty text proves the upstream produced usable output; an empty
|
|
1123
|
+
// delta followed by EOF must stay a truncation error even on the tolerant
|
|
1124
|
+
// profile, or a cut-off turn would surface as a successful empty answer.
|
|
1125
|
+
if (delta.text.length > 0) sawVisibleText = true;
|
|
856
1126
|
yield { type: "text_delta", text: delta.text };
|
|
857
1127
|
} else if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
858
1128
|
yield { type: "thinking_delta", thinking: delta.thinking };
|
|
@@ -934,12 +1204,31 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
934
1204
|
code: "translation_buffer_limit",
|
|
935
1205
|
message: "upstream translation buffer exceeded the safe limit",
|
|
936
1206
|
};
|
|
1207
|
+
// The budget error IS the terminal event for this stream. Falling through to the
|
|
1208
|
+
// EOF handling below could append tool_call_end/done after it, violating the
|
|
1209
|
+
// one-terminal-event contract for consumers that keep draining the generator.
|
|
1210
|
+
return;
|
|
937
1211
|
} finally {
|
|
938
1212
|
if (currentToolCallId) budget.closeCall(currentToolCallId);
|
|
939
1213
|
}
|
|
940
1214
|
if (!emittedDone) {
|
|
941
1215
|
// Fail closed on transport EOF. Compatible providers may omit message_stop after message_delta.stop_reason.
|
|
942
1216
|
if (pendingStopReason !== undefined) {
|
|
1217
|
+
// Same rule as emitDone: an `error` stop reason is a failed generation, not a stop.
|
|
1218
|
+
// This branch bypasses emitDone entirely (it exists for providers that close after
|
|
1219
|
+
// message_delta without message_stop), so the check has to be repeated here or the
|
|
1220
|
+
// EOF route silently reports success.
|
|
1221
|
+
if (pendingStopReason === "error") {
|
|
1222
|
+
emittedDone = true;
|
|
1223
|
+
yield {
|
|
1224
|
+
type: "error",
|
|
1225
|
+
message: "upstream ended the turn with stop_reason \"error\"",
|
|
1226
|
+
status: 502,
|
|
1227
|
+
errorType: "upstream_error",
|
|
1228
|
+
usage: usageFromAnthropic(pendingUsage),
|
|
1229
|
+
};
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
943
1232
|
const stopReason = pendingStopReason === "max_tokens"
|
|
944
1233
|
? "max_tokens"
|
|
945
1234
|
: pendingStopReason === "refusal" || pendingStopReason === "content_filter"
|
|
@@ -951,6 +1240,26 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
951
1240
|
usage: usageFromAnthropic(pendingUsage),
|
|
952
1241
|
...(stopReason ? { stopReason } : {}),
|
|
953
1242
|
};
|
|
1243
|
+
} else if (provider.anthropicEofTolerance === true) {
|
|
1244
|
+
// AgentRouter-style compatibility profile (#658): the upstream can close the stream
|
|
1245
|
+
// after valid content without terminal frames. Complete only when visible text was
|
|
1246
|
+
// received or an open tool call has complete JSON-object arguments; everything else
|
|
1247
|
+
// (incomplete tool JSON, no usable content, transport failure) stays a truncation
|
|
1248
|
+
// error, matching the strict default.
|
|
1249
|
+
if (currentToolCallId) {
|
|
1250
|
+
if (streamedToolArgumentsParse(currentToolCallJson)) {
|
|
1251
|
+
budget.closeCall(currentToolCallId);
|
|
1252
|
+
currentToolCallId = "";
|
|
1253
|
+
yield { type: "tool_call_end" };
|
|
1254
|
+
yield* emitDone();
|
|
1255
|
+
} else {
|
|
1256
|
+
yield { type: "error", message: "upstream stream ended before message_stop — possible truncation" };
|
|
1257
|
+
}
|
|
1258
|
+
} else if (sawVisibleText) {
|
|
1259
|
+
yield* emitDone();
|
|
1260
|
+
} else {
|
|
1261
|
+
yield { type: "error", message: "upstream stream ended before message_stop — possible truncation" };
|
|
1262
|
+
}
|
|
954
1263
|
} else {
|
|
955
1264
|
yield { type: "error", message: "upstream stream ended before message_stop — possible truncation" };
|
|
956
1265
|
}
|
|
@@ -980,13 +1289,28 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
980
1289
|
} else if (block.type === "tool_use") {
|
|
981
1290
|
const id = usableToolUseId(block.id);
|
|
982
1291
|
events.push({ type: "tool_call_start", id, name: toolNames.fromWire(block.name ?? "") });
|
|
983
|
-
events.push({ type: "tool_call_delta", arguments: toolUseArguments(block.input) });
|
|
1292
|
+
events.push({ type: "tool_call_delta", arguments: toolUseArguments(block.input, provider.anthropicEofTolerance === true) });
|
|
984
1293
|
events.push({ type: "tool_call_end" });
|
|
985
1294
|
}
|
|
986
1295
|
}
|
|
987
1296
|
}
|
|
988
1297
|
const usage = json.usage as Record<string, number> | undefined;
|
|
989
1298
|
const stopReason = typeof json.stop_reason === "string" ? json.stop_reason : undefined;
|
|
1299
|
+
// An Anthropic-compatible upstream can forward an `error` stop reason verbatim. As a
|
|
1300
|
+
// `done` it reads as a clean completion, so the turn reports success and — on a compaction
|
|
1301
|
+
// turn — installs its partial summary as replacement history (#422). Usage is preserved:
|
|
1302
|
+
// a failed turn still consumed tokens.
|
|
1303
|
+
if (stopReason === "error") {
|
|
1304
|
+
events.push({
|
|
1305
|
+
type: "error",
|
|
1306
|
+
message: "upstream ended the turn with stop_reason \"error\"",
|
|
1307
|
+
status: 502,
|
|
1308
|
+
errorType: "upstream_error",
|
|
1309
|
+
usage: usageFromAnthropic(usage),
|
|
1310
|
+
});
|
|
1311
|
+
retainTranslatedEventBatch(events, budget);
|
|
1312
|
+
return events;
|
|
1313
|
+
}
|
|
990
1314
|
events.push({
|
|
991
1315
|
type: "done",
|
|
992
1316
|
usage: usageFromAnthropic(usage),
|