@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
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-dispatch input admission (#1412).
|
|
3
|
+
*
|
|
4
|
+
* Refuses a turn whose estimated input cannot plausibly fit the model context window,
|
|
5
|
+
* BEFORE auth resolution, circuit admission, or any upstream I/O. #1412 reported ~127k of
|
|
6
|
+
* real context compounding to 1.3M-1.6M tokens and crashing the proxy; the provider would
|
|
7
|
+
* reject such a turn anyway, so paying for the round trip buys nothing.
|
|
8
|
+
*
|
|
9
|
+
* Deliberately narrow. This is not a context manager and not a compaction trigger: it
|
|
10
|
+
* catches the pathological case and stays out of the way otherwise. Every uncertainty
|
|
11
|
+
* resolves toward admitting.
|
|
12
|
+
*/
|
|
13
|
+
import { nativeOpenAiContextWindow, nativeOpenAiMaxInputTokens, type NativeContextLimitsInput } from "../../codex/catalog/metadata";
|
|
14
|
+
import { estimateTokens } from "../../lib/token-estimate";
|
|
15
|
+
import { isCanonicalOpenAiForwardProvider, OPENAI_CODEX_PROVIDER_ID } from "../../providers/openai-tiers";
|
|
16
|
+
import { modelRecordValue } from "../../reasoning-effort";
|
|
17
|
+
import type { OcxContentPart, OcxParsedRequest, OcxProviderConfig } from "../../types";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Multiplier applied to the ceiling before refusing.
|
|
21
|
+
*
|
|
22
|
+
* 2.5, not something tighter, because `estimateTokens` can overshoot by 1.6x on its own.
|
|
23
|
+
* `cjkRatio` samples every `stride`-th character, so a payload of fixed-width records whose
|
|
24
|
+
* length aligns with the stride samples as 100% CJK while being ~1.6% CJK, firing the
|
|
25
|
+
* 2.5-chars/token clamp instead of the 4.0 default. Measured on Bun 1.3.14: 126,046 chars,
|
|
26
|
+
* true CJK ratio 0.0161, sampled ratio 1.0, estimate inflated 1.6x. Since 4.0 / 2.5 = 1.6
|
|
27
|
+
* is that branch maximum divergence, a threshold at or under 1.6 would convert the
|
|
28
|
+
* estimator error bar into false 413s.
|
|
29
|
+
*
|
|
30
|
+
* 2.5 sits above it with room for the ~10% model-family ratio spread, and still refuses the
|
|
31
|
+
* #1412 shape (10x) four times over.
|
|
32
|
+
*/
|
|
33
|
+
export const ADMISSION_TOLERANCE = 2.5;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Token cost charged for a remote image URL. The bytes are not in this request — the
|
|
37
|
+
* provider fetches them — so the URL own length is not the cost. A small flat charge
|
|
38
|
+
* acknowledges the tiles the image will occupy without pretending to know its dimensions.
|
|
39
|
+
*/
|
|
40
|
+
const REMOTE_IMAGE_TOKENS = 850;
|
|
41
|
+
|
|
42
|
+
/** Decoded image bytes per token. Coarse tile-count proxy, not a provider formula. */
|
|
43
|
+
const IMAGE_BYTES_PER_TOKEN = 750;
|
|
44
|
+
|
|
45
|
+
export interface InputAdmissionResult {
|
|
46
|
+
admitted: boolean;
|
|
47
|
+
estimatedTokens: number;
|
|
48
|
+
/** Resolved ceiling, or null when nothing could be resolved (=> always admitted). */
|
|
49
|
+
ceiling: number | null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function positive(value: unknown): number | null {
|
|
53
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Charge a `data:` URL by its DECODED size rather than its character length: base64 inflates
|
|
58
|
+
* by 4/3, so charging the string would overcount by a third. A remote URL is charged flat.
|
|
59
|
+
*/
|
|
60
|
+
function imageTokens(imageUrl: string): number {
|
|
61
|
+
if (!imageUrl.startsWith("data:")) return REMOTE_IMAGE_TOKENS;
|
|
62
|
+
const comma = imageUrl.indexOf(",");
|
|
63
|
+
if (comma < 0) return REMOTE_IMAGE_TOKENS;
|
|
64
|
+
const payload = imageUrl.length - comma - 1;
|
|
65
|
+
if (payload <= 0) return 0;
|
|
66
|
+
const decoded = Math.floor((payload * 3) / 4);
|
|
67
|
+
return Math.max(1, Math.ceil(decoded / IMAGE_BYTES_PER_TOKEN));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function contentPartTokens(part: OcxContentPart, modelId: string): number {
|
|
71
|
+
return part.type === "image" ? imageTokens(part.imageUrl) : estimateTokens(part.text, modelId);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function contentTokens(content: string | readonly OcxContentPart[], modelId: string): number {
|
|
75
|
+
if (typeof content === "string") return estimateTokens(content, modelId);
|
|
76
|
+
let total = 0;
|
|
77
|
+
for (const part of content) total += contentPartTokens(part, modelId);
|
|
78
|
+
return total;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Estimate the input tokens of a parsed request.
|
|
83
|
+
*
|
|
84
|
+
* Walks the whole `OcxMessage` union rather than user text alone. Assistant turns carry
|
|
85
|
+
* their content as `OcxAssistantContentPart[]` — text, thinking blocks, and tool calls whose
|
|
86
|
+
* JSON arguments are frequently the largest single item in an agent conversation. A walk
|
|
87
|
+
* that counted only `{type:"text"}` would undercount exactly the turns that trigger this
|
|
88
|
+
* gate.
|
|
89
|
+
*/
|
|
90
|
+
export function estimateInputTokens(parsed: OcxParsedRequest, modelId: string): number {
|
|
91
|
+
const { context } = parsed;
|
|
92
|
+
let total = 0;
|
|
93
|
+
|
|
94
|
+
for (const prompt of context.systemPrompt ?? []) total += estimateTokens(prompt, modelId);
|
|
95
|
+
|
|
96
|
+
for (const message of context.messages) {
|
|
97
|
+
if (message.role === "assistant") {
|
|
98
|
+
for (const part of message.content) {
|
|
99
|
+
if (part.type === "text") total += estimateTokens(part.text, modelId);
|
|
100
|
+
else if (part.type === "thinking") total += estimateTokens(part.thinking, modelId);
|
|
101
|
+
else total += estimateTokens(part.name, modelId) + estimateTokens(JSON.stringify(part.arguments), modelId);
|
|
102
|
+
}
|
|
103
|
+
// Opaque provider blob replayed verbatim upstream, so it costs real input tokens.
|
|
104
|
+
if (message.kiroRedactedReasoning) total += estimateTokens(message.kiroRedactedReasoning, modelId);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
total += contentTokens(message.content, modelId);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Tool schemas ride every turn: name, description, and the JSON parameter schema all
|
|
111
|
+
// reach the upstream, and a large MCP catalog can dominate a short conversation.
|
|
112
|
+
for (const tool of context.tools ?? []) {
|
|
113
|
+
total += estimateTokens(tool.name, modelId)
|
|
114
|
+
+ estimateTokens(tool.description, modelId)
|
|
115
|
+
+ estimateTokens(JSON.stringify(tool.parameters), modelId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return total;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Resolve the admission ceiling. Pure: no filesystem, no catalog, no registry scan.
|
|
123
|
+
*
|
|
124
|
+
* `provider` must be the ROUTED config (`route.provider`), which `routedProviderConfig`
|
|
125
|
+
* has already transport-guarded and merged. Re-deriving from `config.providers[name]` would
|
|
126
|
+
* reject a user-defined provider that merely shares a built-in name using limits that
|
|
127
|
+
* belong to a different service.
|
|
128
|
+
*/
|
|
129
|
+
export function resolveInputCeiling(
|
|
130
|
+
provider: OcxProviderConfig,
|
|
131
|
+
providerName: string,
|
|
132
|
+
modelId: string,
|
|
133
|
+
// Operator cap for the canonical native provider. Passed in rather than read from a
|
|
134
|
+
// config here so this stays pure: no filesystem, no catalog, no registry scan.
|
|
135
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
136
|
+
): number | null {
|
|
137
|
+
// `modelRecordValue`, not a bare lookup: the catalog resolves these same two maps that
|
|
138
|
+
// way, so a `gpt-oss` entry covers `gpt-oss:120b`. Reading raw here made the gate fall
|
|
139
|
+
// back to the provider-wide window and refuse turns the model can plainly hold.
|
|
140
|
+
const configured = positive(modelRecordValue(provider.modelContextWindows, modelId))
|
|
141
|
+
?? positive(provider.contextWindow);
|
|
142
|
+
|
|
143
|
+
// The canonical `openai` registry entry declares no context fields, so without this the
|
|
144
|
+
// gate would be inert on the default Codex route. All three clauses are load-bearing: a
|
|
145
|
+
// transport-mismatched custom provider named "openai" is preserved verbatim by routing
|
|
146
|
+
// and must not inherit built-in native limits, and a routed `provider/model` id is not a
|
|
147
|
+
// native slug. Static maps only — no catalog read.
|
|
148
|
+
const canonicalNativeBare = providerName === OPENAI_CODEX_PROVIDER_ID
|
|
149
|
+
&& isCanonicalOpenAiForwardProvider(provider)
|
|
150
|
+
&& !modelId.includes("/");
|
|
151
|
+
const nativeLimits = canonicalNativeBare && configured !== null
|
|
152
|
+
? {
|
|
153
|
+
...(typeof nativeContextCap === "number" ? { cap: nativeContextCap } : (nativeContextCap ?? {})),
|
|
154
|
+
modelWindows: { [modelId]: configured },
|
|
155
|
+
}
|
|
156
|
+
: nativeContextCap;
|
|
157
|
+
const native = canonicalNativeBare
|
|
158
|
+
? positive(nativeOpenAiContextWindow(modelId, nativeLimits))
|
|
159
|
+
: null;
|
|
160
|
+
const nativeMaxInput = canonicalNativeBare ? positive(nativeOpenAiMaxInputTokens(modelId, nativeLimits)) : null;
|
|
161
|
+
|
|
162
|
+
const window = canonicalNativeBare ? native : configured;
|
|
163
|
+
// modelMaxInputTokens is an input-only cap, so it can only tighten the window.
|
|
164
|
+
const configuredMaxInput = positive(modelRecordValue(provider.modelMaxInputTokens, modelId));
|
|
165
|
+
const limits = [window, configuredMaxInput, nativeMaxInput].filter((v): v is number => v !== null);
|
|
166
|
+
return limits.length === 0 ? null : Math.min(...limits);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Fail-open when no ceiling is known; refuse only past `ceiling * ADMISSION_TOLERANCE`.
|
|
171
|
+
*
|
|
172
|
+
* The caller is responsible for skipping compaction turns — see the call site in core.ts.
|
|
173
|
+
*/
|
|
174
|
+
export function checkInputAdmission(
|
|
175
|
+
parsed: OcxParsedRequest,
|
|
176
|
+
provider: OcxProviderConfig,
|
|
177
|
+
providerName: string,
|
|
178
|
+
modelId: string,
|
|
179
|
+
nativeContextCap?: NativeContextLimitsInput,
|
|
180
|
+
): InputAdmissionResult {
|
|
181
|
+
const ceiling = resolveInputCeiling(provider, providerName, modelId, nativeContextCap);
|
|
182
|
+
if (ceiling === null) return { admitted: true, estimatedTokens: 0, ceiling: null };
|
|
183
|
+
const estimatedTokens = estimateInputTokens(parsed, modelId);
|
|
184
|
+
return { admitted: estimatedTokens <= ceiling * ADMISSION_TOLERANCE, estimatedTokens, ceiling };
|
|
185
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { formatErrorResponse } from "../../bridge";
|
|
2
|
+
import { RequestPacingQueueOverloadError } from "../../providers/request-pacing";
|
|
3
|
+
|
|
4
|
+
/** Convert local request-pacing admission failures into a retryable HTTP response. */
|
|
5
|
+
export function requestPacingOverloadResponse(error: unknown): Response | undefined {
|
|
6
|
+
if (!(error instanceof RequestPacingQueueOverloadError)) return undefined;
|
|
7
|
+
return formatErrorResponse(
|
|
8
|
+
429,
|
|
9
|
+
"rate_limit_error",
|
|
10
|
+
error.message,
|
|
11
|
+
{ retryAfter: String(error.retryAfterSeconds) },
|
|
12
|
+
);
|
|
13
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { comboFailureDecision } from "../../combos/failover";
|
|
2
|
+
import { readBoundedResponseBody } from "../../lib/bounded-body";
|
|
3
|
+
import { readJsonRequestBody } from "../request-decompress";
|
|
4
|
+
import { finishRequestAttempt, type RequestLogContext } from "../request-log";
|
|
5
|
+
import type { OcxConfig } from "../../types";
|
|
6
|
+
import type { RouteCandidateTrace, RouteDecisionTraceV1 } from "../../routing/trace";
|
|
7
|
+
import { handleResponses as handleResponsesCore } from "./core";
|
|
8
|
+
import { requestPacingOverloadResponse } from "./pacing-overload";
|
|
9
|
+
|
|
10
|
+
type CoreHandler = typeof handleResponsesCore;
|
|
11
|
+
type CoreOptions = Parameters<CoreHandler>[3];
|
|
12
|
+
|
|
13
|
+
export interface PolicyFallbackDeps {
|
|
14
|
+
runCore?: CoreHandler;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function candidateKey(candidate: Pick<RouteCandidateTrace, "provider" | "model">): string {
|
|
18
|
+
return `${candidate.provider}\u0000${candidate.model}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Rank the remaining candidates from the ORIGINAL policy trace. The initial
|
|
23
|
+
* decision stays immutable; fallback execution belongs in attempts[], not in a
|
|
24
|
+
* rewritten decision trace.
|
|
25
|
+
*/
|
|
26
|
+
export function rankPolicyFallbackCandidates(
|
|
27
|
+
trace: RouteDecisionTraceV1,
|
|
28
|
+
tried: ReadonlySet<string>,
|
|
29
|
+
): RouteCandidateTrace[] {
|
|
30
|
+
return trace.candidates
|
|
31
|
+
.map((candidate, index) => ({ candidate, index }))
|
|
32
|
+
.filter(({ candidate }) =>
|
|
33
|
+
candidate.eligible
|
|
34
|
+
&& candidate.exclusions.length === 0
|
|
35
|
+
&& !tried.has(candidateKey(candidate)))
|
|
36
|
+
.sort((left, right) => {
|
|
37
|
+
const scoreDelta = (right.candidate.score?.total ?? Number.NEGATIVE_INFINITY)
|
|
38
|
+
- (left.candidate.score?.total ?? Number.NEGATIVE_INFINITY);
|
|
39
|
+
return scoreDelta || left.index - right.index;
|
|
40
|
+
})
|
|
41
|
+
.map(({ candidate }) => candidate);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requestWithCandidate(
|
|
45
|
+
req: Request,
|
|
46
|
+
rawBody: Record<string, unknown>,
|
|
47
|
+
candidate: Pick<RouteCandidateTrace, "provider" | "model">,
|
|
48
|
+
): Request {
|
|
49
|
+
const headers = new Headers(req.headers);
|
|
50
|
+
headers.delete("content-encoding");
|
|
51
|
+
headers.delete("content-length");
|
|
52
|
+
headers.set("content-type", "application/json");
|
|
53
|
+
return new Request(req.url, {
|
|
54
|
+
method: req.method,
|
|
55
|
+
headers,
|
|
56
|
+
body: JSON.stringify({ ...rawBody, model: `${candidate.provider}/${candidate.model}` }),
|
|
57
|
+
signal: req.signal,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function errorCodeFromText(text: string): string | undefined {
|
|
62
|
+
if (!text) return undefined;
|
|
63
|
+
try {
|
|
64
|
+
const payload = JSON.parse(text) as { error?: { code?: unknown; type?: unknown }; code?: unknown };
|
|
65
|
+
const candidate = payload.error?.code ?? payload.error?.type ?? payload.code;
|
|
66
|
+
return typeof candidate === "string" ? candidate : undefined;
|
|
67
|
+
} catch {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function shouldHopPolicyCandidate(response: Response, signal?: AbortSignal): Promise<boolean> {
|
|
73
|
+
if (response.status < 400 || signal?.aborted) return false;
|
|
74
|
+
try {
|
|
75
|
+
const inspected = await readBoundedResponseBody(response.clone(), { signal });
|
|
76
|
+
const text = inspected.displaySafe ? inspected.text : "";
|
|
77
|
+
return comboFailureDecision(response.status, text, { code: errorCodeFromText(text) }) === "hop";
|
|
78
|
+
} catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function isPolicyDecision(trace: RouteDecisionTraceV1 | undefined): trace is RouteDecisionTraceV1 {
|
|
84
|
+
return trace?.routeKind === "policy" && !!trace.profile;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Finalize the failed physical attempt so the retry receives a fresh attempt row. */
|
|
88
|
+
function finishFailedPolicyAttempt(logCtx: RequestLogContext, status: number): void {
|
|
89
|
+
const attempt = logCtx.activeAttempt;
|
|
90
|
+
if (attempt) {
|
|
91
|
+
const startedAt = logCtx.activeAttemptStartedAt ?? Date.now();
|
|
92
|
+
finishRequestAttempt(attempt, status, Math.max(0, Date.now() - startedAt), attempt.usage ?? logCtx.usage);
|
|
93
|
+
}
|
|
94
|
+
delete logCtx.activeAttempt;
|
|
95
|
+
delete logCtx.activeAttemptStartedAt;
|
|
96
|
+
delete logCtx.usage;
|
|
97
|
+
delete logCtx.usageFromBridge;
|
|
98
|
+
delete logCtx.upstreamError;
|
|
99
|
+
delete logCtx.terminalHttpStatus;
|
|
100
|
+
delete logCtx.terminalErrorCode;
|
|
101
|
+
delete logCtx.terminalIncompleteReason;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Run a Responses request and, only for an explicitly selected policy profile,
|
|
106
|
+
* hop to the next eligible policy candidate after a retryable pre-success
|
|
107
|
+
* failure. The initial policy trace remains the canonical selection evidence;
|
|
108
|
+
* physical retries continue to accumulate in the existing request attempts.
|
|
109
|
+
*/
|
|
110
|
+
export async function handleResponsesWithPolicyFallback(
|
|
111
|
+
req: Request,
|
|
112
|
+
config: OcxConfig,
|
|
113
|
+
logCtx: RequestLogContext,
|
|
114
|
+
options: CoreOptions = {},
|
|
115
|
+
deps: PolicyFallbackDeps = {},
|
|
116
|
+
): Promise<Response> {
|
|
117
|
+
const runCore = deps.runCore ?? handleResponsesCore;
|
|
118
|
+
let requestBodyReadNotified = false;
|
|
119
|
+
const coreOptions: CoreOptions = options.onRequestBodyRead
|
|
120
|
+
? {
|
|
121
|
+
...options,
|
|
122
|
+
onRequestBodyRead: () => {
|
|
123
|
+
if (requestBodyReadNotified) return;
|
|
124
|
+
requestBodyReadNotified = true;
|
|
125
|
+
options.onRequestBodyRead?.();
|
|
126
|
+
},
|
|
127
|
+
}
|
|
128
|
+
: options;
|
|
129
|
+
let rawBody: Record<string, unknown> | null = null;
|
|
130
|
+
try {
|
|
131
|
+
const parsed = await readJsonRequestBody(req.clone());
|
|
132
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) rawBody = parsed as Record<string, unknown>;
|
|
133
|
+
} catch {
|
|
134
|
+
// Core owns the client-facing parse/decompression error.
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
let response: Response;
|
|
138
|
+
try {
|
|
139
|
+
response = await runCore(req, config, logCtx, coreOptions);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const overload = requestPacingOverloadResponse(error);
|
|
142
|
+
if (overload) return overload;
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
const initialTrace = logCtx.routeDecision;
|
|
146
|
+
const initialRequestedModel = logCtx.requestedModel;
|
|
147
|
+
if (!rawBody || !isPolicyDecision(initialTrace)) return response;
|
|
148
|
+
|
|
149
|
+
const tried = new Set<string>([
|
|
150
|
+
candidateKey({ provider: initialTrace.selected.provider, model: initialTrace.selected.model }),
|
|
151
|
+
]);
|
|
152
|
+
|
|
153
|
+
while (await shouldHopPolicyCandidate(response, req.signal)) {
|
|
154
|
+
if (req.signal.aborted) return response;
|
|
155
|
+
const next = rankPolicyFallbackCandidates(initialTrace, tried)[0];
|
|
156
|
+
if (!next) return response;
|
|
157
|
+
tried.add(candidateKey(next));
|
|
158
|
+
|
|
159
|
+
finishFailedPolicyAttempt(logCtx, response.status);
|
|
160
|
+
const retryRequest = requestWithCandidate(req, rawBody, next);
|
|
161
|
+
try {
|
|
162
|
+
try {
|
|
163
|
+
response = await runCore(retryRequest, config, logCtx, coreOptions);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
const overload = requestPacingOverloadResponse(error);
|
|
166
|
+
if (overload) return overload;
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
} finally {
|
|
170
|
+
logCtx.requestedModel = initialRequestedModel;
|
|
171
|
+
logCtx.routeDecision = initialTrace;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return response;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const handleResponses = handleResponsesWithPolicyFallback;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stateless SSE block rewrite that backfills the `annotations` field on
|
|
3
|
+
* `output_text` content parts in Responses SSE events.
|
|
4
|
+
*
|
|
5
|
+
* The Responses API spec defines `annotations` as a required field on
|
|
6
|
+
* `OutputTextContent` (it is `Vec<Annotation>`, not `Option<Vec>`). Some
|
|
7
|
+
* upstream relays omit it when there are no annotations, which is technically
|
|
8
|
+
* spec-non-compliant. Strict deserializers — any client that follows the
|
|
9
|
+
* schema without `#[serde(default)]` on that field — fail with
|
|
10
|
+
* `missing field `annotations`` when the field is absent.
|
|
11
|
+
*
|
|
12
|
+
* This rewrite scans every SSE event for output_text content parts — whether
|
|
13
|
+
* they appear in item.content[], part, or response.output[].content[] — and
|
|
14
|
+
* adds annotations: [] if missing.
|
|
15
|
+
*
|
|
16
|
+
* Stateless: no retained buffers, no lifecycle tracking, no fail-closed.
|
|
17
|
+
* Existing values are always authoritative; only absent fields are added.
|
|
18
|
+
* The field is always valid on the wire, so adding it when absent is safe
|
|
19
|
+
* for all clients including Codex CLI/App.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
replaceSseDataPayload,
|
|
24
|
+
sseDataPayload,
|
|
25
|
+
type SseBlockRewrite,
|
|
26
|
+
} from "../sse-payload-rewrite";
|
|
27
|
+
import { isCompactionItemType } from "../../responses/compaction";
|
|
28
|
+
|
|
29
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
30
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Wire prefixes for Responses output item ids, matching OpenAI's id shapes. */
|
|
34
|
+
const ITEM_ID_PREFIXES: Readonly<Record<string, string>> = {
|
|
35
|
+
message: "msg_",
|
|
36
|
+
reasoning: "rs_",
|
|
37
|
+
function_call: "fc_",
|
|
38
|
+
custom_tool_call: "ctc_",
|
|
39
|
+
// A routed tool_search lowering is restored to `tool_search_call` without an id, so this
|
|
40
|
+
// backfill is what names it. The generic `item_` fallback is not merely cosmetic here:
|
|
41
|
+
// `stripInvalidItemIds` in the Responses adapter deletes any id whose prefix does not match
|
|
42
|
+
// the type, so an `item_`-named tool_search_call silently loses its id on the NEXT turn and
|
|
43
|
+
// the client sees an item it cannot correlate. The prefixes here must stay a superset of the
|
|
44
|
+
// ones that serializer enforces.
|
|
45
|
+
tool_search_call: "tsc_",
|
|
46
|
+
web_search_call: "ws_",
|
|
47
|
+
file_search_call: "fs_",
|
|
48
|
+
code_interpreter_call: "ci_",
|
|
49
|
+
computer_call: "cc_",
|
|
50
|
+
// The Responses wire type is `image_generation_call`; `image_gen_call` is kept only so a
|
|
51
|
+
// relay that emits the short spelling is not silently demoted to the generic `item_`.
|
|
52
|
+
image_generation_call: "ig_",
|
|
53
|
+
image_gen_call: "ig_",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Backfill a required id on an output item when absent. Strict Responses
|
|
58
|
+
* decoders (e.g. grok-build serde types) fail with "missing field id" when a
|
|
59
|
+
* message or reasoning item has no id, which some upstream relays omit. The
|
|
60
|
+
* generated id is deterministic per (type, output index) so it stays stable
|
|
61
|
+
* across streaming events that reference the same item.
|
|
62
|
+
*/
|
|
63
|
+
function backfillItemId(item: Record<string, unknown>, slot: ItemIdSlot): Record<string, unknown> {
|
|
64
|
+
if (typeof item.id === "string" && item.id.length > 0) return item;
|
|
65
|
+
const type = typeof item.type === "string" ? item.type : "";
|
|
66
|
+
const prefix = Object.prototype.hasOwnProperty.call(ITEM_ID_PREFIXES, type) ? ITEM_ID_PREFIXES[type] : "item_";
|
|
67
|
+
return { ...item, id: prefix + "ocx_" + (slot.kind === "index" ? String(slot.index) : "fallback_" + slot.ordinal) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Which namespace a synthesized id comes from.
|
|
72
|
+
*
|
|
73
|
+
* Keeping the fallback counter in the SAME numeric namespace as real output indexes only
|
|
74
|
+
* pushed the collision out of reach rather than removing it: a response whose real index
|
|
75
|
+
* happened to be 1_000_001 would produce the same id as the first malformed-index fallback,
|
|
76
|
+
* and a duplicate id is exactly what this backfill exists to prevent. The namespaces are now
|
|
77
|
+
* lexically disjoint, so no index value can ever collide with a fallback.
|
|
78
|
+
*/
|
|
79
|
+
type ItemIdSlot = { kind: "index"; index: number } | { kind: "fallback"; ordinal: number };
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Monotonic ordinal for an event whose `output_index` is absent or malformed.
|
|
83
|
+
*
|
|
84
|
+
* Process-global rather than per-response because this module is stateless by design and the
|
|
85
|
+
* value only has to be unique, not meaningful. It carries its own `fallback_` namespace, so
|
|
86
|
+
* uniqueness no longer depends on a real index never reaching some arbitrary ceiling.
|
|
87
|
+
*/
|
|
88
|
+
let syntheticItemOrdinal = 0;
|
|
89
|
+
function nextSyntheticItemSlot(): ItemIdSlot {
|
|
90
|
+
syntheticItemOrdinal += 1;
|
|
91
|
+
return { kind: "fallback", ordinal: syntheticItemOrdinal };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Backfill annotations: [] on an output_text content part if missing.
|
|
96
|
+
* Returns the same object reference if no change is needed.
|
|
97
|
+
*/
|
|
98
|
+
function backfillOutputTextPart(part: Record<string, unknown>): Record<string, unknown> {
|
|
99
|
+
if (part.type !== "output_text") return part;
|
|
100
|
+
// Only add annotations when the key is entirely absent — preserve any
|
|
101
|
+
// existing value (even null or a malformed type) so we never overwrite
|
|
102
|
+
// what the upstream actually sent.
|
|
103
|
+
if ("annotations" in part) return part;
|
|
104
|
+
return { ...part, annotations: [] };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Walk a content array and backfill output_text parts.
|
|
109
|
+
* Returns the same array reference if nothing changed.
|
|
110
|
+
*/
|
|
111
|
+
function backfillContentArray(content: unknown): unknown {
|
|
112
|
+
if (!Array.isArray(content)) return content;
|
|
113
|
+
let changed = false;
|
|
114
|
+
const repaired = content.map((part) => {
|
|
115
|
+
if (!isPlainObject(part)) return part;
|
|
116
|
+
const next = backfillOutputTextPart(part);
|
|
117
|
+
if (next !== part) changed = true;
|
|
118
|
+
return next;
|
|
119
|
+
});
|
|
120
|
+
return changed ? repaired : content;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Walk an output item and backfill output_text parts in its content.
|
|
125
|
+
* Also backfills a missing required id on the item itself.
|
|
126
|
+
* Returns the same object reference if nothing changed.
|
|
127
|
+
*/
|
|
128
|
+
function backfillOutputItem(item: unknown, slot: ItemIdSlot): unknown {
|
|
129
|
+
if (!isPlainObject(item)) return item;
|
|
130
|
+
// The compact wire family is the `/v1/responses/compact` format, not a Responses output item.
|
|
131
|
+
// Those items have no `id` in that contract, so synthesizing one changes a response body the
|
|
132
|
+
// client compares exactly — and the client replays the item on every later turn, where the
|
|
133
|
+
// minting backend rejects it as modified. The backfill exists to satisfy strict Responses
|
|
134
|
+
// decoders; a shape those decoders never see is outside its remit.
|
|
135
|
+
if (isCompactionItemType(item.type)) return item;
|
|
136
|
+
const content = item.content;
|
|
137
|
+
const repaired = backfillContentArray(content);
|
|
138
|
+
const withId = backfillItemId(item, slot);
|
|
139
|
+
if (repaired === content && withId === item) return item;
|
|
140
|
+
return { ...withId, ...(repaired === content ? {} : { content: repaired }) };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Walk a response object's output[] and backfill output_text parts.
|
|
145
|
+
* Returns the same object reference if nothing changed.
|
|
146
|
+
*/
|
|
147
|
+
function backfillResponseOutput(response: unknown): unknown {
|
|
148
|
+
if (!isPlainObject(response)) return response;
|
|
149
|
+
const output = response.output;
|
|
150
|
+
if (!Array.isArray(output)) return response;
|
|
151
|
+
let changed = false;
|
|
152
|
+
const repaired = output.map((item, idx) => {
|
|
153
|
+
if (!isPlainObject(item)) return item;
|
|
154
|
+
const next = backfillOutputItem(item, { kind: "index", index: idx });
|
|
155
|
+
if (next !== item) changed = true;
|
|
156
|
+
return next;
|
|
157
|
+
});
|
|
158
|
+
return changed ? { ...response, output: repaired } : response;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Statelessly rewrite one SSE event: backfill annotations
|
|
163
|
+
* on any output_text content part found in the event payload.
|
|
164
|
+
*/
|
|
165
|
+
function rewriteEvent(event: Record<string, unknown>): Record<string, unknown> {
|
|
166
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
167
|
+
let next = event;
|
|
168
|
+
let changed = false;
|
|
169
|
+
|
|
170
|
+
// output_item.added / output_item.done: item.content[] -> output_text parts
|
|
171
|
+
if ((type === "response.output_item.added" || type === "response.output_item.done")
|
|
172
|
+
&& isPlainObject(event.item)) {
|
|
173
|
+
const rawIndex = event.output_index;
|
|
174
|
+
// A malformed or absent `output_index` must not collapse to 0: two such events would then
|
|
175
|
+
// both synthesize `msg_ocx_0`, and duplicate ids are the very thing this backfill exists to
|
|
176
|
+
// prevent. Fall back to a per-process counter so the synthesized id stays unique. Position
|
|
177
|
+
// is not recoverable in that case, but a unique id is what strict decoders require, and a
|
|
178
|
+
// well-formed stream still gets the stable index-derived id.
|
|
179
|
+
const item = typeof rawIndex === "number" && Number.isInteger(rawIndex) && rawIndex >= 0
|
|
180
|
+
? backfillOutputItem(event.item, { kind: "index", index: rawIndex })
|
|
181
|
+
: backfillOutputItem(event.item, nextSyntheticItemSlot());
|
|
182
|
+
if (item !== event.item) {
|
|
183
|
+
next = { ...next, item };
|
|
184
|
+
changed = true;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// content_part.added / content_part.done: part -> output_text
|
|
189
|
+
if ((type === "response.content_part.added" || type === "response.content_part.done")
|
|
190
|
+
&& isPlainObject(event.part)) {
|
|
191
|
+
const part = backfillOutputTextPart(event.part);
|
|
192
|
+
if (part !== event.part) {
|
|
193
|
+
next = { ...next, part };
|
|
194
|
+
changed = true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// response.created / in_progress / completed / incomplete / failed:
|
|
199
|
+
// response.output[].content[] -> output_text parts
|
|
200
|
+
if (isPlainObject(event.response)) {
|
|
201
|
+
const response = backfillResponseOutput(event.response);
|
|
202
|
+
if (response !== event.response) {
|
|
203
|
+
next = { ...next, response };
|
|
204
|
+
changed = true;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return changed ? next : event;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Create a stateless SSE block rewrite that backfills annotations and
|
|
213
|
+
* on output_text content parts. Unconditional: the field is a required
|
|
214
|
+
* canonical Responses field, so adding it when absent is safe for all
|
|
215
|
+
* clients.
|
|
216
|
+
*/
|
|
217
|
+
export function createResponsesFieldBackfillBlockRewrite(): SseBlockRewrite {
|
|
218
|
+
const rewrite: SseBlockRewrite = (block: string): readonly string[] => {
|
|
219
|
+
const payload = sseDataPayload(block);
|
|
220
|
+
if (payload === null) return [block];
|
|
221
|
+
let event: unknown;
|
|
222
|
+
try {
|
|
223
|
+
event = JSON.parse(payload);
|
|
224
|
+
} catch {
|
|
225
|
+
return [block];
|
|
226
|
+
}
|
|
227
|
+
if (!isPlainObject(event)) return [block];
|
|
228
|
+
const rewritten = rewriteEvent(event);
|
|
229
|
+
if (rewritten === event) return [block];
|
|
230
|
+
return [replaceSseDataPayload(block, JSON.stringify(rewritten))];
|
|
231
|
+
};
|
|
232
|
+
return rewrite;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Backfill annotations on a non-streaming Responses JSON
|
|
237
|
+
* object. Mirrors the SSE block rewrite for the bounded-JSON passthrough
|
|
238
|
+
* path. Returns the original string if no change is needed.
|
|
239
|
+
*/
|
|
240
|
+
export function backfillResponsesFieldsJson(payload: string): string {
|
|
241
|
+
let response: unknown;
|
|
242
|
+
try {
|
|
243
|
+
response = JSON.parse(payload);
|
|
244
|
+
} catch {
|
|
245
|
+
return payload;
|
|
246
|
+
}
|
|
247
|
+
if (!isPlainObject(response)) return payload;
|
|
248
|
+
const repaired = backfillResponseOutput(response);
|
|
249
|
+
if (repaired === response) return payload;
|
|
250
|
+
return JSON.stringify(repaired);
|
|
251
|
+
}
|