@iislee/opencodex 2.11.0 → 2.35.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 +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -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 +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -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 +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- 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 +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- 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 +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- 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 +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- 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 +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- 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 +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- 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 +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- 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 +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -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 +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- 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 +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -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/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -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 +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -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 +360 -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 +622 -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 +675 -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 +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -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 +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -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 +157 -33
- package/src/images/plan.ts +24 -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-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -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/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- 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 +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- 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/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- 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/service-secrets.ts +15 -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 +253 -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 +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -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/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- 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/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -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 +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -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 +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -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 +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -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 +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -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 +155 -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 +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- 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 +1021 -190
- package/src/server/lifecycle.ts +336 -14
- 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 +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -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 +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -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 +289 -33
- 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 +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- 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/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -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 +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -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 +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -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 +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- 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 +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- 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 +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- 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 +27 -16
- 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 +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- 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,637 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { opendir } from "node:fs/promises";
|
|
5
|
+
import type { AdapterEvent, OcxContentPart, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTool, OcxUsage } from "../types";
|
|
6
|
+
import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolChoiceToolPredicate } from "../types";
|
|
7
|
+
import type { AdapterFetchContext, AdapterRequest, ProviderAdapter } from "./base";
|
|
8
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
9
|
+
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
10
|
+
import { debugDroppedFrame } from "../lib/debug";
|
|
11
|
+
import { configuredReasoningEfforts } from "../reasoning-effort";
|
|
12
|
+
import { commandCodeReasoningEfforts, refreshCommandCodeReasoningEfforts } from "../providers/command-code-efforts";
|
|
13
|
+
import { identifyRoutedModel } from "./identity";
|
|
14
|
+
import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
|
|
15
|
+
import { parseDataUrl } from "./image";
|
|
16
|
+
|
|
17
|
+
// Retain the short ids emitted by the first local integration. New requests use the live catalog's
|
|
18
|
+
// provider-native IDs directly; this map is compatibility-only and is not a model fallback list.
|
|
19
|
+
const COMMAND_CODE_MODEL_ALIASES: Readonly<Record<string, string>> = {
|
|
20
|
+
"deepseek-v4-flash": "deepseek/deepseek-v4-flash",
|
|
21
|
+
"kimi-k3": "moonshotai/Kimi-K3",
|
|
22
|
+
"glm-5.3": "zai-org/GLM-5.3",
|
|
23
|
+
"glm-5.2": "zai-org/GLM-5.2",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function canonicalCommandCodeModelId(modelId: string): string {
|
|
27
|
+
return Object.hasOwn(COMMAND_CODE_MODEL_ALIASES, modelId) ? COMMAND_CODE_MODEL_ALIASES[modelId]! : modelId;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Flatten tool-result content for the text-only wire output, keeping an `[image]` marker per image part in content order. */
|
|
31
|
+
function toolResultText(content: string | OcxContentPart[]): string {
|
|
32
|
+
if (typeof content === "string") return content;
|
|
33
|
+
return content.map(part => (part.type === "text" ? part.text : "[image]")).join("");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Best-effort media type from a remote https URL extension, e.g. image/png. */
|
|
37
|
+
function mediaTypeFromUrl(url: string): string | undefined {
|
|
38
|
+
const ext = url.split(/[?#]/)[0]!.match(/\.([a-zA-Z0-9]+)$/)?.[1]?.toLowerCase();
|
|
39
|
+
if (!ext) return undefined;
|
|
40
|
+
const known: Record<string, string> = {
|
|
41
|
+
png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", gif: "image/gif", webp: "image/webp", bmp: "image/bmp",
|
|
42
|
+
};
|
|
43
|
+
return known[ext];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Wire image part: `{ type, image, mediaType? }` per the /alpha/generate ModelMessage schema. */
|
|
47
|
+
function wireImagePart(imageUrl: string): Record<string, unknown> {
|
|
48
|
+
const parsed = parseDataUrl(imageUrl);
|
|
49
|
+
const mediaType = parsed?.mediaType ?? mediaTypeFromUrl(imageUrl);
|
|
50
|
+
return { type: "image", image: imageUrl, ...(mediaType ? { mediaType } : {}) };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The /alpha/generate wire pairs every assistant `tool-call` with a following `tool` message
|
|
55
|
+
* whose `tool-result.toolCallId` matches it. Codex history is not guaranteed to carry that
|
|
56
|
+
* pairing (interrupted turns, compacted threads, and multi-step tool rounds all can leave an
|
|
57
|
+
* assistant call with no recorded result), and the upstream rejects an unpaired call with
|
|
58
|
+
* `Tool result is missing for tool call <id>`, which currently surfaces as a generic 502
|
|
59
|
+
* (#1383). This builder keeps the pairing invariant:
|
|
60
|
+
*
|
|
61
|
+
* - a `toolResult` that matches a declared assistant call emits the native `tool-result`;
|
|
62
|
+
* - a `toolResult` with no matching declared call degrades to a text carrier so the model
|
|
63
|
+
* still sees the outcome without a 400-prone standalone `tool` message;
|
|
64
|
+
* - every declared assistant call that never received a result gets an explicit error
|
|
65
|
+
* `tool-result`, so the upstream never sees an unpaired call.
|
|
66
|
+
*/
|
|
67
|
+
function wireMessages(messages: OcxMessage[]): Array<Record<string, unknown>> {
|
|
68
|
+
const out: Array<Record<string, unknown>> = [];
|
|
69
|
+
const pendingCalls: Array<{ id: string; name: string }> = [];
|
|
70
|
+
// Image parts returned by a tool cannot live inside the text-only `tool-result` wire
|
|
71
|
+
// output. They ride a follow-up user message, but that user message must not break the
|
|
72
|
+
// adjacency of the assistant turn's tool results, so carriers are buffered and flushed
|
|
73
|
+
// only after every declared call has its native or synthesized result.
|
|
74
|
+
const pendingImageCarriers: Array<Record<string, unknown>> = [];
|
|
75
|
+
// The /alpha/generate wire requires every assistant tool-call to be closed by a matching
|
|
76
|
+
// `tool-result` immediately after the declaring assistant message. Close any declared call
|
|
77
|
+
// that never received a result before a non-toolResult message moves the turn forward, so a
|
|
78
|
+
// synthesized `tool` message never lands after a user message or a new assistant turn.
|
|
79
|
+
const closePendingCalls = (): void => {
|
|
80
|
+
for (const call of pendingCalls) {
|
|
81
|
+
out.push({ role: "tool", content: [{
|
|
82
|
+
type: "tool-result",
|
|
83
|
+
toolCallId: call.id,
|
|
84
|
+
toolName: call.name,
|
|
85
|
+
output: { type: "error-text", value: "[ocx] no tool result was recorded for this tool call; execution status unknown." },
|
|
86
|
+
}] });
|
|
87
|
+
}
|
|
88
|
+
pendingCalls.length = 0;
|
|
89
|
+
if (pendingImageCarriers.length > 0) {
|
|
90
|
+
out.push(...pendingImageCarriers);
|
|
91
|
+
pendingImageCarriers.length = 0;
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
for (const message of messages) {
|
|
95
|
+
if (message.role === "assistant") {
|
|
96
|
+
closePendingCalls();
|
|
97
|
+
const content: Array<Record<string, unknown>> = [];
|
|
98
|
+
for (const part of message.content) {
|
|
99
|
+
if (part.type === "text") content.push({ type: "text", text: part.text });
|
|
100
|
+
else if (part.type === "thinking") content.push({ type: "reasoning", text: part.thinking });
|
|
101
|
+
else {
|
|
102
|
+
const wireName = namespacedToolName(part.namespace, part.name);
|
|
103
|
+
content.push({ type: "tool-call", toolCallId: part.id, toolName: wireName, input: part.arguments });
|
|
104
|
+
pendingCalls.push({ id: part.id, name: wireName });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
out.push({ role: "assistant", content });
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (message.role === "toolResult") {
|
|
111
|
+
const callIndex = pendingCalls.findIndex(call => call.id === message.toolCallId);
|
|
112
|
+
const paired = callIndex >= 0;
|
|
113
|
+
if (paired) pendingCalls.splice(callIndex, 1);
|
|
114
|
+
if (!paired) {
|
|
115
|
+
// Pending calls from an earlier assistant turn must still be closed before any user
|
|
116
|
+
// message lands, or their synthesized results would follow the orphan carrier.
|
|
117
|
+
closePendingCalls();
|
|
118
|
+
// The upstream rejects a standalone tool message whose call was never declared by an
|
|
119
|
+
// assistant turn. Preserve the outcome as text so the model can still act on it.
|
|
120
|
+
const label = message.toolName ? `${message.toolName} (${message.toolCallId})` : message.toolCallId;
|
|
121
|
+
const text = toolResultText(message.content);
|
|
122
|
+
// The orphan result cannot ride a `tool` message; carry it in a user message instead.
|
|
123
|
+
out.push({ role: "user", content: [{ type: "text", text: `[tool result without adjacent tool call: ${label}]\n${text}` }] });
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
out.push({ role: "tool", content: [{
|
|
127
|
+
type: "tool-result",
|
|
128
|
+
toolCallId: message.toolCallId,
|
|
129
|
+
toolName: namespacedToolName(message.toolNamespace, message.toolName),
|
|
130
|
+
output: { type: message.isError ? "error-text" : "text", value: toolResultText(message.content) },
|
|
131
|
+
}] });
|
|
132
|
+
// The proprietary wire's tool-result output is text-only; image parts returned by a
|
|
133
|
+
// tool (e.g. Codex view_image) cannot live inside it. Carry them in a follow-up user
|
|
134
|
+
// message using the same image encoding as the user branch so the bytes reach the model.
|
|
135
|
+
const images = typeof message.content === "string" ? [] : message.content.filter(part => part.type === "image");
|
|
136
|
+
if (images.length > 0) {
|
|
137
|
+
pendingImageCarriers.push({ role: "user", content: images.map(part => wireImagePart((part as { imageUrl: string }).imageUrl)) });
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// User/developer message: no pending tool results may follow it on the wire.
|
|
142
|
+
closePendingCalls();
|
|
143
|
+
const content: Array<Record<string, unknown>> = [];
|
|
144
|
+
if (typeof message.content === "string") content.push({ type: "text", text: message.content });
|
|
145
|
+
else for (const part of message.content) {
|
|
146
|
+
if (part.type === "text") content.push({ type: "text", text: part.text });
|
|
147
|
+
else if (part.type === "image") content.push(wireImagePart(part.imageUrl));
|
|
148
|
+
else content.push({ type: "text", text: "[video]" });
|
|
149
|
+
}
|
|
150
|
+
out.push({ role: "user", content });
|
|
151
|
+
}
|
|
152
|
+
closePendingCalls();
|
|
153
|
+
return out;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function visibleTools(parsed: OcxParsedRequest): OcxTool[] {
|
|
157
|
+
const choice = parsed.options.toolChoice;
|
|
158
|
+
if (choice === "none") return [];
|
|
159
|
+
const tools = parsed.context.tools ?? [];
|
|
160
|
+
if (isAllowedToolChoice(choice)) {
|
|
161
|
+
return tools.filter(toolChoiceToolPredicate(choice, tools));
|
|
162
|
+
}
|
|
163
|
+
if (choice && typeof choice !== "string") {
|
|
164
|
+
const selected = resolveToolChoiceWireName(tools, choice.name);
|
|
165
|
+
return tools.filter(tool => namespacedToolName(tool.namespace, tool.name) === selected);
|
|
166
|
+
}
|
|
167
|
+
return tools;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function toolChoiceInstruction(parsed: OcxParsedRequest): string | undefined {
|
|
171
|
+
const choice = parsed.options.toolChoice;
|
|
172
|
+
if (choice === "required" || (isAllowedToolChoice(choice) && choice.mode === "required")) {
|
|
173
|
+
return "Tool choice is required for this turn. Make at least one call from the advertised tool catalog before answering.";
|
|
174
|
+
}
|
|
175
|
+
if (choice && typeof choice !== "string" && !isAllowedToolChoice(choice)) {
|
|
176
|
+
// Resolve the forced name (bare, namespace__name, or namespace.name) to the advertised
|
|
177
|
+
// wire name so the instruction names a tool that is actually in the catalog.
|
|
178
|
+
const wireName = resolveToolChoiceWireName(parsed.context.tools, choice.name);
|
|
179
|
+
return `Tool choice is required for this turn. Call the advertised tool named ${wireName} before answering.`;
|
|
180
|
+
}
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function wireTools(tools: OcxTool[]): Array<Record<string, unknown>> {
|
|
185
|
+
return tools.map(tool => ({
|
|
186
|
+
name: namespacedToolName(tool.namespace, tool.name),
|
|
187
|
+
description: tool.description,
|
|
188
|
+
input_schema: tool.parameters,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function currentWorkingDirectory(): string | undefined {
|
|
193
|
+
try { return process.cwd(); } catch { return undefined; }
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/** Cap the workspace listing so a large directory does not ship every entry name upstream. */
|
|
197
|
+
const MAX_WORKSPACE_STRUCTURE_ENTRIES = 64;
|
|
198
|
+
/** Cap how many recent commit subjects the config carries. */
|
|
199
|
+
const MAX_RECENT_COMMITS = 8;
|
|
200
|
+
/** Cap each recent commit entry to keep the request bounded even for long subjects. */
|
|
201
|
+
const MAX_RECENT_COMMIT_LENGTH = 512;
|
|
202
|
+
/** Cap the git status text sent upstream. */
|
|
203
|
+
const MAX_GIT_STATUS_LENGTH = 2048;
|
|
204
|
+
/** Keep collected workspace/git metadata fresh for this long (ms) so repeated requests reuse it. */
|
|
205
|
+
const WORKSPACE_METADATA_TTL_MS = 30_000;
|
|
206
|
+
/** Hard cap on cached workspace metadata entries to prevent unbounded growth across distinct cwds. */
|
|
207
|
+
export const MAX_WORKSPACE_METADATA_ENTRIES = 128;
|
|
208
|
+
|
|
209
|
+
/** Derive a bounded project slug from the working directory for the `x-project-slug` header. */
|
|
210
|
+
function projectSlug(cwd: string): string {
|
|
211
|
+
return cwd.replace(/[^a-zA-Z0-9]+/g, "-").replace(/^-|-$/g, "").toLowerCase().slice(0, 64) || "workspace";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
interface GitWorkspaceInfo {
|
|
215
|
+
isGitRepo: boolean;
|
|
216
|
+
currentBranch: string;
|
|
217
|
+
mainBranch: string;
|
|
218
|
+
gitStatus: string;
|
|
219
|
+
recentCommits: string[];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export const workspaceMetadataCache = new Map<string, { collectedAt: number; value: GitWorkspaceInfo }>();
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Evict expired entries first, then the oldest live entry if at capacity.
|
|
226
|
+
* Called before inserting a new key so the cache never exceeds the cap.
|
|
227
|
+
*/
|
|
228
|
+
export function pruneWorkspaceMetadataCache(now: number): void {
|
|
229
|
+
// Pass 1: remove expired entries.
|
|
230
|
+
for (const [key, entry] of workspaceMetadataCache) {
|
|
231
|
+
if (now - entry.collectedAt >= WORKSPACE_METADATA_TTL_MS) {
|
|
232
|
+
workspaceMetadataCache.delete(key);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Pass 2: if still at capacity, evict the oldest live entry.
|
|
236
|
+
if (workspaceMetadataCache.size >= MAX_WORKSPACE_METADATA_ENTRIES) {
|
|
237
|
+
let oldestKey: string | null = null;
|
|
238
|
+
let oldestAt = Infinity;
|
|
239
|
+
for (const [key, entry] of workspaceMetadataCache) {
|
|
240
|
+
if (entry.collectedAt < oldestAt) {
|
|
241
|
+
oldestAt = entry.collectedAt;
|
|
242
|
+
oldestKey = key;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (oldestKey !== null) workspaceMetadataCache.delete(oldestKey);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const execFile = promisify(execFileCallback);
|
|
250
|
+
|
|
251
|
+
/** Best-effort git metadata for the upstream config contract; every read fails safe and stays off the event loop. */
|
|
252
|
+
async function gitWorkspaceInfo(cwd: string | undefined): Promise<GitWorkspaceInfo> {
|
|
253
|
+
const fallback: GitWorkspaceInfo = { isGitRepo: false, currentBranch: "", mainBranch: "", gitStatus: "", recentCommits: [] };
|
|
254
|
+
if (!cwd) return fallback;
|
|
255
|
+
const cached = workspaceMetadataCache.get(cwd);
|
|
256
|
+
if (cached && Date.now() - cached.collectedAt < WORKSPACE_METADATA_TTL_MS) return cached.value;
|
|
257
|
+
const run = async (args: string[]): Promise<string> => {
|
|
258
|
+
try {
|
|
259
|
+
const { stdout } = await execFile("git", args, { cwd, encoding: "utf8", timeout: 2000, windowsHide: true });
|
|
260
|
+
return stdout.trim();
|
|
261
|
+
} catch {
|
|
262
|
+
return "";
|
|
263
|
+
}
|
|
264
|
+
};
|
|
265
|
+
const root = await run(["rev-parse", "--show-toplevel"]);
|
|
266
|
+
const value = root
|
|
267
|
+
? {
|
|
268
|
+
isGitRepo: true,
|
|
269
|
+
currentBranch: (await run(["rev-parse", "--abbrev-ref", "HEAD"])) || "HEAD",
|
|
270
|
+
mainBranch: (await run(["symbolic-ref", "--short", "refs/remotes/origin/HEAD"])).replace(/^origin\//, "") || (await run(["rev-parse", "--abbrev-ref", "HEAD"])) || "",
|
|
271
|
+
gitStatus: (await run(["status", "--porcelain"])).slice(0, MAX_GIT_STATUS_LENGTH),
|
|
272
|
+
recentCommits: (await run(["log", "--oneline", `-${MAX_RECENT_COMMITS}`]))
|
|
273
|
+
.split("\n")
|
|
274
|
+
.filter(Boolean)
|
|
275
|
+
.slice(0, MAX_RECENT_COMMITS)
|
|
276
|
+
.map(commit => commit.slice(0, MAX_RECENT_COMMIT_LENGTH)),
|
|
277
|
+
}
|
|
278
|
+
: fallback;
|
|
279
|
+
const now = Date.now();
|
|
280
|
+
if (!workspaceMetadataCache.has(cwd)) pruneWorkspaceMetadataCache(now);
|
|
281
|
+
workspaceMetadataCache.set(cwd, { collectedAt: now, value });
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
async function commandCodeConfig(cwd: string | undefined): Promise<Record<string, unknown>> {
|
|
286
|
+
let structure: string[] = [];
|
|
287
|
+
if (cwd) {
|
|
288
|
+
try {
|
|
289
|
+
// Iterate and stop after the cap instead of materializing every entry: a directory with a
|
|
290
|
+
// huge number of names must not stall the request path for 64 metadata rows.
|
|
291
|
+
const dir = await opendir(cwd);
|
|
292
|
+
try {
|
|
293
|
+
for await (const entry of dir) {
|
|
294
|
+
if (entry.name.startsWith(".")) continue;
|
|
295
|
+
structure.push(entry.name);
|
|
296
|
+
if (structure.length >= MAX_WORKSPACE_STRUCTURE_ENTRIES) break;
|
|
297
|
+
}
|
|
298
|
+
} finally {
|
|
299
|
+
await dir.close().catch(() => undefined);
|
|
300
|
+
}
|
|
301
|
+
} catch { /* workspace metadata is optional */ }
|
|
302
|
+
}
|
|
303
|
+
const git = await gitWorkspaceInfo(cwd);
|
|
304
|
+
return {
|
|
305
|
+
...(cwd ? { workingDir: cwd } : {}),
|
|
306
|
+
date: new Date().toISOString().slice(0, 10),
|
|
307
|
+
environment: process.platform,
|
|
308
|
+
structure,
|
|
309
|
+
...git,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function usage(value: unknown): OcxUsage | undefined {
|
|
314
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
315
|
+
const row = value as Record<string, unknown>;
|
|
316
|
+
const inputTokens = typeof row.inputTokens === "number" ? row.inputTokens : 0;
|
|
317
|
+
const outputTokens = typeof row.outputTokens === "number" ? row.outputTokens : 0;
|
|
318
|
+
const details = row.inputTokenDetails && typeof row.inputTokenDetails === "object" && !Array.isArray(row.inputTokenDetails)
|
|
319
|
+
? row.inputTokenDetails as Record<string, unknown> : {};
|
|
320
|
+
const cachedInputTokens = typeof details.cacheReadTokens === "number" ? details.cacheReadTokens : undefined;
|
|
321
|
+
const cacheCreationInputTokens = typeof details.cacheWriteTokens === "number" ? details.cacheWriteTokens : undefined;
|
|
322
|
+
return {
|
|
323
|
+
inputTokens, outputTokens, totalTokens: inputTokens + outputTokens,
|
|
324
|
+
...(cachedInputTokens !== undefined ? { cachedInputTokens, cacheReadInputTokens: cachedInputTokens } : {}),
|
|
325
|
+
...(cacheCreationInputTokens !== undefined ? { cacheCreationInputTokens } : {}),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function eventError(value: unknown): string {
|
|
330
|
+
if (typeof value === "string" && value) return value;
|
|
331
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
332
|
+
const message = (value as Record<string, unknown>).message;
|
|
333
|
+
if (typeof message === "string" && message) return message;
|
|
334
|
+
}
|
|
335
|
+
return "Command Code stream error";
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* True when the upstream rejected a tool-result continuation because an assistant tool call
|
|
340
|
+
* had no matching result (`Tool result is missing for tool call <id>`). The proxy now keeps
|
|
341
|
+
* that pairing invariant before sending, so this error is a distinct provider-side
|
|
342
|
+
* validation failure rather than a generic stream fault; classify it as such for the
|
|
343
|
+
* dashboard/logs instead of a plain upstream 502.
|
|
344
|
+
*/
|
|
345
|
+
function isMissingToolResultError(value: unknown): boolean {
|
|
346
|
+
const text = eventError(value).toLowerCase();
|
|
347
|
+
return text.includes("tool result is missing") || text.includes("tool_result is missing");
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
async function*ndjson(response: Response, budget: TranslatorBudget): AsyncGenerator<Record<string, unknown>> {
|
|
351
|
+
if (!response.body) throw new Error("Command Code response body missing");
|
|
352
|
+
const reader = response.body.getReader();
|
|
353
|
+
const decoder = new TextDecoder();
|
|
354
|
+
const encoder = new TextEncoder();
|
|
355
|
+
let buffer = "";
|
|
356
|
+
let bufferBytes = 0;
|
|
357
|
+
try {
|
|
358
|
+
for (;;) {
|
|
359
|
+
const { value, done } = await reader.read();
|
|
360
|
+
const next = buffer + decoder.decode(value, { stream: !done });
|
|
361
|
+
const nextBytes = encoder.encode(next).byteLength;
|
|
362
|
+
const reservation = budget.reserveTransient(nextBytes, { kind: "live_transient" });
|
|
363
|
+
buffer = next;
|
|
364
|
+
reservation.commitRetained();
|
|
365
|
+
budget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
366
|
+
bufferBytes = nextBytes;
|
|
367
|
+
let newline = buffer.indexOf("\n");
|
|
368
|
+
while (newline >= 0) {
|
|
369
|
+
const line = buffer.slice(0, newline).trim(); buffer = buffer.slice(newline + 1);
|
|
370
|
+
if (line) yield* decodeEventLine(line);
|
|
371
|
+
newline = buffer.indexOf("\n");
|
|
372
|
+
}
|
|
373
|
+
const residualBytes = encoder.encode(buffer).byteLength;
|
|
374
|
+
const residualReservation = budget.reserveTransient(residualBytes, { kind: "live_transient" });
|
|
375
|
+
residualReservation.commitRetained();
|
|
376
|
+
budget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
377
|
+
bufferBytes = residualBytes;
|
|
378
|
+
if (done) break;
|
|
379
|
+
}
|
|
380
|
+
const final = buffer.trim();
|
|
381
|
+
if (final) yield* decodeEventLine(final);
|
|
382
|
+
} finally {
|
|
383
|
+
budget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
384
|
+
try { await reader.cancel(); } catch { /* already closed */ }
|
|
385
|
+
reader.releaseLock();
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Yield one NDJSON line as an event record, or nothing.
|
|
391
|
+
*
|
|
392
|
+
* `JSON.parse("null")` returns `null` instead of throwing, so the `try/catch` around the parse
|
|
393
|
+
* cannot see it and the `event.type` read in parseStream crashed the turn — the #1219 defect, on
|
|
394
|
+
* the one streaming transport the #1240 audit did not cover because it is NDJSON rather than SSE.
|
|
395
|
+
*
|
|
396
|
+
* A frame that does not parse to a record is padding, not an event: drop it and continue exactly
|
|
397
|
+
* as an unparseable line is already dropped, so a stream whose only frames are junk ends in the
|
|
398
|
+
* same single terminal `done` as an empty body. Skipping is what preserves an answer whose deltas
|
|
399
|
+
* have already arrived — the observed #1219 case is `null` padding BETWEEN content deltas, where
|
|
400
|
+
* terminating would discard a complete response (#1240).
|
|
401
|
+
*
|
|
402
|
+
* Note this deliberately makes a junk-only stream a quiet `[done]` where it previously threw. That
|
|
403
|
+
* throw was an unguarded type assumption, not a designed failure signal, and `[done]` is already
|
|
404
|
+
* what an empty body, a blank-line-only body and an unparseable-only body all produce here. The
|
|
405
|
+
* broader question — whether this adapter should report *any* no-valid-event stream as a failure
|
|
406
|
+
* rather than an empty success — is pre-existing, applies to all four of those inputs equally, and
|
|
407
|
+
* is deliberately not decided by this change.
|
|
408
|
+
*/
|
|
409
|
+
function* decodeEventLine(line: string): Generator<Record<string, unknown>> {
|
|
410
|
+
let parsed: unknown;
|
|
411
|
+
try {
|
|
412
|
+
parsed = JSON.parse(stripEventFrame(line));
|
|
413
|
+
} catch {
|
|
414
|
+
debugDroppedFrame("command-code", line);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
418
|
+
debugDroppedFrame("command-code", line);
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
yield parsed as Record<string, unknown>;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** The endpoint is newline-delimited JSON; defensively strip an SSE `data:` frame if the gateway ever switches shapes. */
|
|
425
|
+
function stripEventFrame(line: string): string {
|
|
426
|
+
return line.startsWith("data:") ? line.slice("data:".length).trim() : line;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function isReasoningEffortRejection(status: number, payload: string): boolean {
|
|
430
|
+
return (status === 400 || status === 422) && /reasoning[_ -]?effort|unsupported effort|invalid effort/i.test(payload);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
function requestWithoutReasoningEffort(request: AdapterRequest): AdapterRequest | undefined {
|
|
434
|
+
try {
|
|
435
|
+
const body = JSON.parse(request.body) as { params?: Record<string, unknown> };
|
|
436
|
+
if (!body.params?.reasoning_effort) return undefined;
|
|
437
|
+
delete body.params.reasoning_effort;
|
|
438
|
+
return { ...request, body: JSON.stringify(body), reasoningLog: undefined };
|
|
439
|
+
} catch {
|
|
440
|
+
return undefined;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async function fetchCommandCode(request: AdapterRequest, ctx: AdapterFetchContext | undefined, executor: typeof globalThis.fetch): Promise<Response> {
|
|
445
|
+
const timeout = new AbortController();
|
|
446
|
+
const timer = setTimeout(() => timeout.abort(new DOMException("Timeout elapsed", "TimeoutError")), ctx?.timeoutMs ?? 200_000);
|
|
447
|
+
const callerSignal = ctx?.abortSignal ?? new AbortController().signal;
|
|
448
|
+
try {
|
|
449
|
+
return await executor(request.url, {
|
|
450
|
+
method: request.method,
|
|
451
|
+
headers: request.headers,
|
|
452
|
+
body: request.body,
|
|
453
|
+
redirect: "manual",
|
|
454
|
+
signal: AbortSignal.any([callerSignal, timeout.signal]),
|
|
455
|
+
});
|
|
456
|
+
} finally {
|
|
457
|
+
clearTimeout(timer);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function supportedCommandCodeEffort(provider: OcxProviderConfig, modelId: string, requested: string | undefined): string | undefined {
|
|
462
|
+
if (!requested || requested === "none") return undefined;
|
|
463
|
+
// Compatibility ids (deepseek-v4-flash / glm-5.2) must resolve to their canonical
|
|
464
|
+
// Command Code id before the effort lookup, or legacy requests silently lose the
|
|
465
|
+
// reasoning effort because the official table is keyed by the canonical ids.
|
|
466
|
+
const canonicalId = canonicalCommandCodeModelId(modelId);
|
|
467
|
+
const supported = commandCodeReasoningEfforts(canonicalId) ?? configuredReasoningEfforts(provider, canonicalId);
|
|
468
|
+
if (!supported) return undefined;
|
|
469
|
+
// Only remap xhigh/ultra→max for models whose official profile documents that
|
|
470
|
+
// aliasing (deepseek v4, glm-5.2). Muse Spark's upstream accepts xhigh as a
|
|
471
|
+
// distinct wire value and rejects ultra, so it must not be collapsed.
|
|
472
|
+
let wire = requested;
|
|
473
|
+
const lower = canonicalId.toLowerCase();
|
|
474
|
+
const needsAlias =
|
|
475
|
+
lower === "deepseek/deepseek-v4-pro" ||
|
|
476
|
+
lower === "deepseek/deepseek-v4-flash" ||
|
|
477
|
+
lower === "zai-org/glm-5.2";
|
|
478
|
+
if (requested === "xhigh" && !supported.includes("xhigh") && supported.includes("max")) {
|
|
479
|
+
wire = "max";
|
|
480
|
+
} else if (requested === "ultra" && needsAlias && supported.includes("max")) {
|
|
481
|
+
wire = "max";
|
|
482
|
+
}
|
|
483
|
+
return (supported as readonly string[]).includes(wire) ? wire : undefined;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export function createCommandCodeAdapter(provider: OcxProviderConfig): ProviderAdapter {
|
|
487
|
+
const executor = (provider as OcxProviderConfig & { fetch?: typeof globalThis.fetch }).fetch ?? globalThis.fetch;
|
|
488
|
+
return {
|
|
489
|
+
name: "command-code",
|
|
490
|
+
async buildRequest(parsed: OcxParsedRequest): Promise<AdapterRequest> {
|
|
491
|
+
if (!provider.apiKey) throw new Error("Command Code credential missing — run ocx login command-code");
|
|
492
|
+
const cwd = currentWorkingDirectory();
|
|
493
|
+
const tools = visibleTools(parsed);
|
|
494
|
+
const toolNudge = buildNonOpenAIToolCatalogNudgeForTools(tools, parsed.options.toolChoice);
|
|
495
|
+
const choiceInstruction = toolChoiceInstruction(parsed);
|
|
496
|
+
const system = identifyRoutedModel([
|
|
497
|
+
...(parsed.context.systemPrompt ?? []),
|
|
498
|
+
...(toolNudge ? [toolNudge] : []),
|
|
499
|
+
...(choiceInstruction ? [choiceInstruction] : []),
|
|
500
|
+
].join("\n\n"), parsed.modelId);
|
|
501
|
+
const reasoningEffort = supportedCommandCodeEffort(provider, parsed.modelId, parsed.options.reasoning);
|
|
502
|
+
const body = {
|
|
503
|
+
config: await commandCodeConfig(cwd), memory: "", taste: null, skills: null,
|
|
504
|
+
permissionMode: "standard", mode: "agent",
|
|
505
|
+
params: {
|
|
506
|
+
model: canonicalCommandCodeModelId(parsed.modelId),
|
|
507
|
+
messages: wireMessages(parsed.context.messages),
|
|
508
|
+
tools: wireTools(tools),
|
|
509
|
+
system,
|
|
510
|
+
max_tokens: parsed.options.maxOutputTokens ?? provider.defaultMaxOutputTokens ?? 64_000,
|
|
511
|
+
// The proprietary /alpha/generate endpoint is NDJSON-stream-only; the proxy converts
|
|
512
|
+
// the buffered/streamed events to the client's requested shape (parsed.stream).
|
|
513
|
+
stream: true,
|
|
514
|
+
...(parsed.options.temperature !== undefined ? { temperature: parsed.options.temperature } : {}),
|
|
515
|
+
...(reasoningEffort ? { reasoning_effort: reasoningEffort } : {}),
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
const headers: Record<string, string> = {
|
|
519
|
+
Authorization: `Bearer ${provider.apiKey}`,
|
|
520
|
+
"Content-Type": "application/json",
|
|
521
|
+
"User-Agent": "cli",
|
|
522
|
+
"x-command-code-version": provider.commandCodeVersion ?? "0.52.1",
|
|
523
|
+
"x-cli-environment": "production",
|
|
524
|
+
"x-taste-learning": "false",
|
|
525
|
+
"x-co-flag": "false",
|
|
526
|
+
"x-session-id": randomUUID(),
|
|
527
|
+
};
|
|
528
|
+
if (cwd) headers["x-project-slug"] = projectSlug(cwd);
|
|
529
|
+
return {
|
|
530
|
+
url: `${provider.baseUrl.replace(/\/$/, "")}/alpha/generate`, method: "POST",
|
|
531
|
+
headers,
|
|
532
|
+
body: JSON.stringify(body),
|
|
533
|
+
...(reasoningEffort ? { reasoningLog: { effectiveEffort: reasoningEffort, wireField: "reasoning_effort" as const, wireValue: reasoningEffort } } : {}),
|
|
534
|
+
};
|
|
535
|
+
},
|
|
536
|
+
async fetchResponse(request: AdapterRequest, ctx?: AdapterFetchContext): Promise<Response> {
|
|
537
|
+
const response = await fetchCommandCode(request, ctx, executor);
|
|
538
|
+
if (response.ok) return response;
|
|
539
|
+
const currentEffort = (() => {
|
|
540
|
+
try { return (JSON.parse(request.body) as { params?: { reasoning_effort?: unknown } }).params?.reasoning_effort; } catch { return undefined; }
|
|
541
|
+
})();
|
|
542
|
+
if (typeof currentEffort !== "string") return response;
|
|
543
|
+
let body = "";
|
|
544
|
+
try {
|
|
545
|
+
const observed = await readBoundedResponseBody(response.clone(), { signal: ctx?.abortSignal, maxBytes: 8 * 1024 });
|
|
546
|
+
if (!observed.displaySafe) return response;
|
|
547
|
+
body = observed.text;
|
|
548
|
+
} catch { return response; }
|
|
549
|
+
if (!isReasoningEffortRejection(response.status, body)) return response;
|
|
550
|
+
const modelId = (() => {
|
|
551
|
+
try { return (JSON.parse(request.body) as { params?: { model?: unknown } }).params?.model; } catch { return undefined; }
|
|
552
|
+
})();
|
|
553
|
+
if (typeof modelId !== "string") return response;
|
|
554
|
+
const refreshed = await refreshCommandCodeReasoningEfforts(modelId, executor);
|
|
555
|
+
if (!refreshed || refreshed.includes(currentEffort)) return response;
|
|
556
|
+
const retry = requestWithoutReasoningEffort(request);
|
|
557
|
+
if (!retry) return response;
|
|
558
|
+
try { void response.body?.cancel(); } catch { /* already closed */ }
|
|
559
|
+
return fetchCommandCode(retry, ctx, executor);
|
|
560
|
+
},
|
|
561
|
+
async *parseStream(response: Response, budget: TranslatorBudget): AsyncGenerator<AdapterEvent> {
|
|
562
|
+
let sawFinish = false;
|
|
563
|
+
for await (const event of ndjson(response, budget)) {
|
|
564
|
+
switch (event.type) {
|
|
565
|
+
case "text-delta": if (typeof event.text === "string") yield { type: "text_delta", text: event.text }; break;
|
|
566
|
+
case "reasoning-delta": if (typeof event.text === "string") yield { type: "thinking_delta", thinking: event.text }; break;
|
|
567
|
+
case "tool-call": {
|
|
568
|
+
const id = typeof event.toolCallId === "string" ? event.toolCallId : randomUUID();
|
|
569
|
+
const name = typeof event.toolName === "string" ? event.toolName : "tool";
|
|
570
|
+
const input = event.input ?? event.args ?? {};
|
|
571
|
+
const argumentsText = typeof input === "string" ? input : JSON.stringify(input);
|
|
572
|
+
yield { type: "tool_call_start", id, name };
|
|
573
|
+
budget.openCall(id);
|
|
574
|
+
try {
|
|
575
|
+
const reservation = budget.reserveTransient(new TextEncoder().encode(argumentsText).byteLength, { kind: "tool_args", callId: id });
|
|
576
|
+
reservation.commitRetained();
|
|
577
|
+
yield { type: "tool_call_delta", arguments: argumentsText };
|
|
578
|
+
yield { type: "tool_call_end" };
|
|
579
|
+
} finally {
|
|
580
|
+
budget.closeCall(id);
|
|
581
|
+
}
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
case "finish-step":
|
|
585
|
+
case "finish": {
|
|
586
|
+
// Both events are terminal on the current wire; streams commonly carry both, so
|
|
587
|
+
// only the first one emits the done. finish-step carries `usage`; finish may carry
|
|
588
|
+
// `totalUsage` (current flows) or `usage`.
|
|
589
|
+
if (sawFinish) break;
|
|
590
|
+
sawFinish = true;
|
|
591
|
+
const usageValue = event.totalUsage ?? event.usage;
|
|
592
|
+
const stopReason = typeof event.rawFinishReason === "string" ? event.rawFinishReason : typeof event.finishReason === "string" ? event.finishReason : undefined;
|
|
593
|
+
// The AI SDK's `error` finish reason means the generation failed upstream, not that it
|
|
594
|
+
// stopped. Reporting it as a `done` left the bridge to infer failure from a stop-reason
|
|
595
|
+
// string, which either read as a clean completion or (once classified) mislabelled an
|
|
596
|
+
// upstream error as a content filter and rejected it from the replay cache for the
|
|
597
|
+
// wrong reason.
|
|
598
|
+
if (stopReason === "error") {
|
|
599
|
+
// Keep the usage: a failed turn still consumed tokens, and dropping it makes the
|
|
600
|
+
// turn look free in accounting and reports zeros to the client.
|
|
601
|
+
yield {
|
|
602
|
+
type: "error",
|
|
603
|
+
message: "Command Code upstream ended the turn with finishReason \"error\"",
|
|
604
|
+
status: 502,
|
|
605
|
+
errorType: "upstream_error",
|
|
606
|
+
usage: usage(usageValue),
|
|
607
|
+
};
|
|
608
|
+
break;
|
|
609
|
+
}
|
|
610
|
+
yield { type: "done", usage: usage(usageValue), stopReason };
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
613
|
+
case "error": {
|
|
614
|
+
const message = eventError(event.error);
|
|
615
|
+
if (isMissingToolResultError(message)) {
|
|
616
|
+
// Provider-side tool-result validation: the request carried an assistant tool
|
|
617
|
+
// call the upstream refused to accept. This is not a network/stream stall; the
|
|
618
|
+
// proxy normally prevents it by pairing every call, so flag it distinctly.
|
|
619
|
+
yield { type: "error", message, status: 502, errorType: "upstream_error", code: "missing_tool_result" };
|
|
620
|
+
} else {
|
|
621
|
+
yield { type: "error", message, status: 502 };
|
|
622
|
+
}
|
|
623
|
+
break;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
// A stream that ends without a finish event still needs a terminal done so the
|
|
628
|
+
// server does not wait on an adapter that silently stopped emitting.
|
|
629
|
+
if (!sawFinish) yield { type: "done", usage: undefined, stopReason: undefined };
|
|
630
|
+
},
|
|
631
|
+
async parseResponse(response: Response, budget: TranslatorBudget): Promise<AdapterEvent[]> {
|
|
632
|
+
const events: AdapterEvent[] = [];
|
|
633
|
+
for await (const event of this.parseStream(response, budget)) events.push(event);
|
|
634
|
+
return events;
|
|
635
|
+
},
|
|
636
|
+
};
|
|
637
|
+
}
|