@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,153 @@
|
|
|
1
|
+
import { namespacedToolName } from "../types";
|
|
2
|
+
import { sseDataPayload, type SseBlockRewrite } from "./sse-payload-rewrite";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Item types the CLIENT executes by name. Hosted calls (`web_search_call`,
|
|
6
|
+
* `image_generation_call`, `local_shell_call`, `tool_search_call`, …) are run upstream or
|
|
7
|
+
* carry no tool name, so they are never matched against the request catalog.
|
|
8
|
+
*/
|
|
9
|
+
const CLIENT_EXECUTED_CALL_TYPES = new Set(["function_call", "custom_tool_call"]);
|
|
10
|
+
|
|
11
|
+
/** An upstream-supplied name reaches the error message; keep it bounded. */
|
|
12
|
+
const MAX_REPORTED_NAME_CHARS = 100;
|
|
13
|
+
|
|
14
|
+
export const UNDECLARED_TOOL_CALL_ERROR_CODE = "undeclared_tool_call";
|
|
15
|
+
|
|
16
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
17
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function addWireToolName(names: Set<string>, tool: unknown, namespace?: string): void {
|
|
21
|
+
if (!isPlainObject(tool) || typeof tool.name !== "string" || tool.name.length === 0) return;
|
|
22
|
+
names.add(tool.name);
|
|
23
|
+
// Codex routes MCP calls by an explicit `namespace` field, so the same tool is reachable
|
|
24
|
+
// as a bare inner name or as the flattened form; accept both rather than guess which
|
|
25
|
+
// coordinate system this provider echoes back.
|
|
26
|
+
if (namespace) names.add(namespacedToolName(namespace, tool.name));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function addWireToolSpecs(names: Set<string>, specs: unknown): void {
|
|
30
|
+
if (!Array.isArray(specs)) return;
|
|
31
|
+
for (const spec of specs) {
|
|
32
|
+
if (!isPlainObject(spec)) continue;
|
|
33
|
+
if (spec.type === "namespace" && Array.isArray(spec.tools)) {
|
|
34
|
+
const namespace = typeof spec.name === "string" ? spec.name : undefined;
|
|
35
|
+
for (const inner of spec.tools) addWireToolName(names, inner, namespace);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
addWireToolName(names, spec);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Tool names the OUTBOUND Responses body actually declared.
|
|
44
|
+
*
|
|
45
|
+
* This reads the body that goes upstream rather than the parsed internal tool list: the
|
|
46
|
+
* passthrough forwards wire shapes (namespaced MCP groups, `additional_tools` items carried
|
|
47
|
+
* inside `input`, routed custom-tool rewrites) that the internal list flattens or renames, and
|
|
48
|
+
* only the wire names can be compared against what the provider echoes back.
|
|
49
|
+
*/
|
|
50
|
+
export function collectDeclaredWireToolNames(body: unknown): Set<string> {
|
|
51
|
+
const names = new Set<string>();
|
|
52
|
+
if (!isPlainObject(body)) return names;
|
|
53
|
+
addWireToolSpecs(names, body.tools);
|
|
54
|
+
if (Array.isArray(body.input)) {
|
|
55
|
+
for (const item of body.input) {
|
|
56
|
+
if (isPlainObject(item) && item.type === "additional_tools") addWireToolSpecs(names, item.tools);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return names;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function undeclaredNameInItem(item: unknown, declared: ReadonlySet<string>): string | undefined {
|
|
63
|
+
if (!isPlainObject(item)) return undefined;
|
|
64
|
+
if (typeof item.type !== "string" || !CLIENT_EXECUTED_CALL_TYPES.has(item.type)) return undefined;
|
|
65
|
+
const name = item.name;
|
|
66
|
+
if (typeof name !== "string" || name.length === 0) return undefined;
|
|
67
|
+
if (declared.has(name)) return undefined;
|
|
68
|
+
if (typeof item.namespace === "string" && declared.has(namespacedToolName(item.namespace, name))) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return name;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** First undeclared client tool named by a Responses SSE payload, or undefined. */
|
|
75
|
+
export function undeclaredToolCallName(
|
|
76
|
+
payload: unknown,
|
|
77
|
+
declared: ReadonlySet<string>,
|
|
78
|
+
): string | undefined {
|
|
79
|
+
if (!isPlainObject(payload)) return undefined;
|
|
80
|
+
if (payload.type === "response.output_item.added" || payload.type === "response.output_item.done") {
|
|
81
|
+
return undeclaredNameInItem(payload.item, declared);
|
|
82
|
+
}
|
|
83
|
+
// Sparse gateways skip incremental items and only ever ship the terminal snapshot.
|
|
84
|
+
if (payload.type === "response.completed" || payload.type === "response.incomplete") {
|
|
85
|
+
return undeclaredToolCallNameInResponse(payload.response, declared);
|
|
86
|
+
}
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** First undeclared client tool in a Responses object's `output` array, or undefined. */
|
|
91
|
+
export function undeclaredToolCallNameInResponse(
|
|
92
|
+
response: unknown,
|
|
93
|
+
declared: ReadonlySet<string>,
|
|
94
|
+
): string | undefined {
|
|
95
|
+
if (!isPlainObject(response) || !Array.isArray(response.output)) return undefined;
|
|
96
|
+
for (const item of response.output) {
|
|
97
|
+
const name = undeclaredNameInItem(item, declared);
|
|
98
|
+
if (name !== undefined) return name;
|
|
99
|
+
}
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function undeclaredToolCallMessage(name: string): string {
|
|
104
|
+
const reported = name.slice(0, MAX_REPORTED_NAME_CHARS);
|
|
105
|
+
return `routed provider emitted undeclared client tool "${reported}"; only request-declared tools may be called`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function failedBlocks(name: string, newline: string): readonly string[] {
|
|
109
|
+
const failure = {
|
|
110
|
+
type: "upstream_error",
|
|
111
|
+
code: UNDECLARED_TOOL_CALL_ERROR_CODE,
|
|
112
|
+
message: undeclaredToolCallMessage(name),
|
|
113
|
+
};
|
|
114
|
+
const payload = JSON.stringify({
|
|
115
|
+
type: "response.failed",
|
|
116
|
+
response: { status: "failed", error: failure, last_error: failure },
|
|
117
|
+
});
|
|
118
|
+
return [`event: response.failed${newline}data: ${payload}`, "data: [DONE]"];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fail closed when a routed provider calls a tool the request never declared (#1700).
|
|
123
|
+
*
|
|
124
|
+
* The bridged paths already refuse such a call (`declaredToolNames` in src/bridge.ts), but the
|
|
125
|
+
* native Responses passthrough relayed it verbatim: Codex received a `function_call` for a tool
|
|
126
|
+
* it has no top-level handler for — `apply_patch`, which under code mode exists only as a nested
|
|
127
|
+
* `tools.apply_patch(...)` helper inside `exec` — and the turn surfaced as a bare `aborted` with
|
|
128
|
+
* no output and no explanation. Replacing the offending event with an explicit `response.failed`
|
|
129
|
+
* turns that silent dead end into a compatibility error naming the tool.
|
|
130
|
+
*
|
|
131
|
+
* Everything after the trip is dropped so a later `response.completed` cannot contradict the
|
|
132
|
+
* terminal already sent. Non-JSON and non-item blocks pass through untouched.
|
|
133
|
+
*/
|
|
134
|
+
export function createUndeclaredToolCallGuardBlockRewrite(
|
|
135
|
+
declared: ReadonlySet<string>,
|
|
136
|
+
): SseBlockRewrite {
|
|
137
|
+
let tripped = false;
|
|
138
|
+
return (block: string) => {
|
|
139
|
+
if (tripped) return [];
|
|
140
|
+
const payload = sseDataPayload(block);
|
|
141
|
+
if (payload === null || payload === "[DONE]") return [block];
|
|
142
|
+
let parsed: unknown;
|
|
143
|
+
try {
|
|
144
|
+
parsed = JSON.parse(payload);
|
|
145
|
+
} catch {
|
|
146
|
+
return [block];
|
|
147
|
+
}
|
|
148
|
+
const name = undeclaredToolCallName(parsed, declared);
|
|
149
|
+
if (name === undefined) return [block];
|
|
150
|
+
tripped = true;
|
|
151
|
+
return failedBlocks(name, block.includes("\r\n") ? "\r\n" : "\n");
|
|
152
|
+
};
|
|
153
|
+
}
|
package/src/server/responses.ts
CHANGED
|
@@ -1,9 +1,25 @@
|
|
|
1
1
|
// AUTO-SPLIT facade: original responses.ts body moved into ./responses/* modules.
|
|
2
2
|
// Public surface preserved exactly; importers keep using "src/server/responses".
|
|
3
|
+
import { handleResponsesCompact as handleResponsesCompactImpl } from "./responses/compact";
|
|
4
|
+
import { requestPacingOverloadResponse } from "./responses/pacing-overload";
|
|
5
|
+
|
|
3
6
|
export { buildToolBridgeMaps, isV1CollabSurface, collabSurface, multiAgentGuidanceText, V2_GUIDANCE_CHAR_BUDGET, injectDeveloperMessage } from "./responses/collaboration";
|
|
4
7
|
export type { MultiAgentGuidanceOptions, MultiAgentGuidanceDeps } from "./responses/collaboration";
|
|
5
8
|
export { hasUnreadableEncryptedAgentTask, sanitizeEncryptedContentInPlace } from "./responses/encrypted-payload";
|
|
6
|
-
export { COMPACT_RESPONSE_MAX_BYTES, bufferCompactResponse
|
|
9
|
+
export { COMPACT_RESPONSE_MAX_BYTES, bufferCompactResponse } from "./responses/compact";
|
|
7
10
|
export { disableResponsesRequestTimeout, safeHostLabel, fetchWithHeaderTimeout } from "./responses/fetch-helpers";
|
|
8
|
-
export { sidecarOutcomeRecorder, isShadowSourceModel, codexLogAccountId, usesCodexForwardPoolAuth, codexForwardTerminalOutcomeRecorder, decodeRequestErrorResponse, buildComboChildHeaders,
|
|
11
|
+
export { sidecarOutcomeRecorder, isShadowSourceModel, codexLogAccountId, usesCodexForwardPoolAuth, codexForwardTerminalOutcomeRecorder, decodeRequestErrorResponse, buildComboChildHeaders, linkAbortSignal } from "./responses/core";
|
|
12
|
+
export { handleResponses, handleResponsesWithPolicyFallback, rankPolicyFallbackCandidates } from "./responses/policy-fallback";
|
|
9
13
|
export { adapterNeedsForcedContinuation } from "./responses/core";
|
|
14
|
+
|
|
15
|
+
export async function handleResponsesCompact(
|
|
16
|
+
...args: Parameters<typeof handleResponsesCompactImpl>
|
|
17
|
+
): Promise<Response> {
|
|
18
|
+
try {
|
|
19
|
+
return await handleResponsesCompactImpl(...args);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
const overload = requestPacingOverloadResponse(error);
|
|
22
|
+
if (overload) return overload;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/server/search.ts
CHANGED
|
@@ -11,20 +11,31 @@
|
|
|
11
11
|
import { formatErrorResponse } from "../bridge";
|
|
12
12
|
import {
|
|
13
13
|
CodexAccountCooldownError,
|
|
14
|
+
codexMainProfileDrainingResponse,
|
|
14
15
|
cooldownErrorResponse,
|
|
15
16
|
CodexAuthContextError,
|
|
17
|
+
CodexMainProfileDrainingError,
|
|
16
18
|
CodexPoolAuthenticationError,
|
|
17
19
|
CodexThreadAffinityExpiredError,
|
|
18
20
|
} from "../codex/auth-context";
|
|
21
|
+
import { codexAccountNamespaceForModel } from "../codex/account-namespace-match";
|
|
19
22
|
import { formatCodexProviderForLog } from "../codex/routing";
|
|
20
23
|
import { signalWithTimeout } from "../lib/abort";
|
|
24
|
+
import { readBoundedResponseBytes } from "../lib/bounded-body";
|
|
21
25
|
import { sidecarEnter } from "../lib/sidecar-tracker";
|
|
22
26
|
import type { OcxConfig } from "../types";
|
|
23
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
listOpenAiForwardSidecarCandidates,
|
|
29
|
+
resolveFirstUsableOpenAiSidecar,
|
|
30
|
+
type ExactOpenAiSidecarAccount,
|
|
31
|
+
} from "../providers/openai-sidecar";
|
|
32
|
+
import { routeModel } from "../router";
|
|
24
33
|
import { readJsonRequestBody } from "./request-decompress";
|
|
25
34
|
import { ForwardAdmissionCredentialError, validateForwardAdmissionCredential } from "./auth-cors";
|
|
26
35
|
import type { RequestLogContext } from "./request-log";
|
|
27
36
|
import { codexLogAccountId, decodeRequestErrorResponse } from "./responses";
|
|
37
|
+
import type { AdmissionLease } from "../lib/admission";
|
|
38
|
+
import { codexAccountSelectionForTurn } from "./lifecycle";
|
|
28
39
|
|
|
29
40
|
/**
|
|
30
41
|
* Default TOTAL deadline for one search relay. alpha/search is non-streaming JSON — response
|
|
@@ -34,12 +45,13 @@ import { codexLogAccountId, decodeRequestErrorResponse } from "./responses";
|
|
|
34
45
|
* long-running search).
|
|
35
46
|
*/
|
|
36
47
|
const SEARCH_UPSTREAM_TIMEOUT_MS = 200_000;
|
|
37
|
-
const SEARCH_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
|
|
48
|
+
export const SEARCH_RESPONSE_MAX_BYTES = 16 * 1024 * 1024;
|
|
38
49
|
|
|
39
50
|
export async function handleSearch(
|
|
40
51
|
req: Request,
|
|
41
52
|
config: OcxConfig,
|
|
42
53
|
logCtx: RequestLogContext,
|
|
54
|
+
turnAdmissionLease?: AdmissionLease,
|
|
43
55
|
): Promise<Response> {
|
|
44
56
|
try { validateForwardAdmissionCredential(req.headers, config); }
|
|
45
57
|
catch (err) {
|
|
@@ -55,6 +67,32 @@ export async function handleSearch(
|
|
|
55
67
|
const model = (body as { model?: unknown } | null)?.model;
|
|
56
68
|
if (typeof model === "string" && model) logCtx.model = model;
|
|
57
69
|
|
|
70
|
+
let exactAccount: ExactOpenAiSidecarAccount | undefined;
|
|
71
|
+
let relayBody = body;
|
|
72
|
+
const accountNamespace = typeof model === "string"
|
|
73
|
+
? codexAccountNamespaceForModel(config.codexAccountNamespaces, model)
|
|
74
|
+
: undefined;
|
|
75
|
+
if (accountNamespace && typeof model === "string") {
|
|
76
|
+
try {
|
|
77
|
+
const route = routeModel(config, model);
|
|
78
|
+
if (!route.codexAccountId || route.codexAccountNamespace !== accountNamespace) {
|
|
79
|
+
return formatErrorResponse(400, "invalid_request_error", "Invalid Codex account-qualified search model");
|
|
80
|
+
}
|
|
81
|
+
exactAccount = { accountId: route.codexAccountId, modelId: route.modelId };
|
|
82
|
+
logCtx.provider = `${route.providerName}-${accountNamespace}`;
|
|
83
|
+
logCtx.routeDecision = route.routeDecision;
|
|
84
|
+
// The ChatGPT search endpoint only understands the native model slug. The
|
|
85
|
+
// account namespace is proxy routing syntax and must not cross the wire.
|
|
86
|
+
relayBody = { ...(body as Record<string, unknown>), model: route.modelId };
|
|
87
|
+
} catch (err) {
|
|
88
|
+
return formatErrorResponse(
|
|
89
|
+
400,
|
|
90
|
+
"invalid_request_error",
|
|
91
|
+
err instanceof Error ? err.message : "Invalid Codex account-qualified search model",
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
58
96
|
const candidates = listOpenAiForwardSidecarCandidates(config);
|
|
59
97
|
if (candidates.length === 0) {
|
|
60
98
|
return formatErrorResponse(
|
|
@@ -67,7 +105,10 @@ export async function handleSearch(
|
|
|
67
105
|
|
|
68
106
|
let upstream: Awaited<ReturnType<typeof resolveFirstUsableOpenAiSidecar>>;
|
|
69
107
|
try {
|
|
70
|
-
upstream = await resolveFirstUsableOpenAiSidecar(candidates, req.headers, config
|
|
108
|
+
upstream = await resolveFirstUsableOpenAiSidecar(candidates, req.headers, config, {
|
|
109
|
+
exactAccount,
|
|
110
|
+
beginCodexAccountSelection: codexAccountSelectionForTurn(turnAdmissionLease),
|
|
111
|
+
});
|
|
71
112
|
if (!upstream) {
|
|
72
113
|
return formatErrorResponse(
|
|
73
114
|
401,
|
|
@@ -75,16 +116,21 @@ export async function handleSearch(
|
|
|
75
116
|
"web search relay needs ChatGPT auth (Authorization header)",
|
|
76
117
|
);
|
|
77
118
|
}
|
|
78
|
-
logCtx.provider =
|
|
119
|
+
logCtx.provider = accountNamespace
|
|
120
|
+
? `${upstream.providerName}-${accountNamespace}`
|
|
121
|
+
: formatCodexProviderForLog(upstream.providerName, codexLogAccountId(upstream.authContext), config);
|
|
79
122
|
} catch (err) {
|
|
80
123
|
if (err instanceof CodexAccountCooldownError) {
|
|
81
|
-
return cooldownErrorResponse(err);
|
|
124
|
+
return cooldownErrorResponse(err, Date.now(), accountNamespace);
|
|
82
125
|
}
|
|
126
|
+
if (err instanceof CodexMainProfileDrainingError) return codexMainProfileDrainingResponse();
|
|
83
127
|
if (err instanceof CodexThreadAffinityExpiredError) {
|
|
84
128
|
return formatErrorResponse(409, "invalid_request_error", "Codex thread account affinity expired; start a new session");
|
|
85
129
|
}
|
|
86
130
|
if (err instanceof CodexAuthContextError) {
|
|
87
|
-
const safeAccountLabel =
|
|
131
|
+
const safeAccountLabel = accountNamespace
|
|
132
|
+
? `openai-${accountNamespace}`
|
|
133
|
+
: formatCodexProviderForLog("openai", err.accountId, config);
|
|
88
134
|
console.error(`[search] Pool account ${safeAccountLabel} token failed; reauthentication required`);
|
|
89
135
|
return formatErrorResponse(401, "authentication_error", "Selected Codex account needs reauthentication");
|
|
90
136
|
}
|
|
@@ -99,27 +145,40 @@ export async function handleSearch(
|
|
|
99
145
|
const timeoutMs = config.search?.timeoutMs ?? SEARCH_UPSTREAM_TIMEOUT_MS;
|
|
100
146
|
const linkedSignal = signalWithTimeout(timeoutMs, req.signal);
|
|
101
147
|
const sidecarExit = sidecarEnter("search");
|
|
148
|
+
let upstreamResponse: Response | undefined;
|
|
102
149
|
try {
|
|
103
|
-
|
|
150
|
+
upstreamResponse = await fetch(url, {
|
|
104
151
|
method: "POST",
|
|
105
152
|
headers,
|
|
106
|
-
body: JSON.stringify(
|
|
153
|
+
body: JSON.stringify(relayBody),
|
|
154
|
+
signal: linkedSignal.signal,
|
|
155
|
+
// Credential-bearing: do not follow a cross-origin 3xx. Bun strips `Authorization`
|
|
156
|
+
// across origins but forwards nonstandard headers such as `chatgpt-account-id`,
|
|
157
|
+
// `session_id`, and `x-codex-turn-metadata` to the redirect target.
|
|
158
|
+
redirect: "manual",
|
|
159
|
+
});
|
|
160
|
+
const observed = await readBoundedResponseBytes(upstreamResponse, {
|
|
161
|
+
maxBytes: SEARCH_RESPONSE_MAX_BYTES,
|
|
107
162
|
signal: linkedSignal.signal,
|
|
108
163
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
return formatErrorResponse(
|
|
164
|
+
if (observed.oversized) {
|
|
165
|
+
upstream.recordOutcome?.(upstreamResponse.status);
|
|
166
|
+
return formatErrorResponse(
|
|
167
|
+
502,
|
|
168
|
+
"upstream_error",
|
|
169
|
+
`search response too large (exceeded ${SEARCH_RESPONSE_MAX_BYTES} bytes)`,
|
|
170
|
+
);
|
|
112
171
|
}
|
|
113
172
|
upstream.recordOutcome?.(upstreamResponse.status);
|
|
114
173
|
const relayHeaders: Record<string, string> = {};
|
|
115
174
|
const contentType = upstreamResponse.headers.get("content-type");
|
|
116
175
|
if (contentType) relayHeaders["content-type"] = contentType;
|
|
117
|
-
return new Response(
|
|
176
|
+
return new Response(observed.bytes, { status: upstreamResponse.status, headers: relayHeaders });
|
|
118
177
|
} catch (err) {
|
|
119
178
|
if (req.signal.aborted) {
|
|
120
179
|
return formatErrorResponse(499, "client_closed_request", "search request canceled by client");
|
|
121
180
|
}
|
|
122
|
-
if (err instanceof Error && err.name === "TimeoutError") {
|
|
181
|
+
if (linkedSignal.signal.aborted || (err instanceof Error && err.name === "TimeoutError")) {
|
|
123
182
|
upstream.recordOutcome?.("timeout");
|
|
124
183
|
return formatErrorResponse(504, "upstream_error", "search upstream timed out");
|
|
125
184
|
}
|
|
@@ -132,5 +191,11 @@ export async function handleSearch(
|
|
|
132
191
|
} finally {
|
|
133
192
|
sidecarExit();
|
|
134
193
|
linkedSignal.cleanup();
|
|
194
|
+
// A response that aborted before the reader attached still owns its upstream socket.
|
|
195
|
+
// Consumed/cancelled bodies are already closed; locked bodies remain owned by the reader.
|
|
196
|
+
const pendingBody = upstreamResponse?.body;
|
|
197
|
+
if (pendingBody && !pendingBody.locked) {
|
|
198
|
+
try { void pendingBody.cancel().catch(() => undefined); } catch { /* already closed */ }
|
|
199
|
+
}
|
|
135
200
|
}
|
|
136
201
|
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
export const MAX_CLIENT_SSE_FRAME_BYTES = 4 * 1024 * 1024;
|
|
2
|
+
|
|
3
|
+
const LF_LF = Uint8Array.of(10, 10);
|
|
4
|
+
const LF_CR_LF = Uint8Array.of(10, 13, 10);
|
|
5
|
+
const CR_LF_LF = Uint8Array.of(13, 10, 10);
|
|
6
|
+
const CR_LF_CR_LF = Uint8Array.of(13, 10, 13, 10);
|
|
7
|
+
|
|
8
|
+
export class SseFrameTooLargeError extends Error {
|
|
9
|
+
readonly maxBytes: number;
|
|
10
|
+
|
|
11
|
+
constructor(maxBytes: number) {
|
|
12
|
+
super(`upstream SSE frame exceeded ${maxBytes} bytes`);
|
|
13
|
+
this.name = "SseFrameTooLargeError";
|
|
14
|
+
this.maxBytes = maxBytes;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class SseFrameCountLimitError extends Error {
|
|
19
|
+
readonly maxFrames: number;
|
|
20
|
+
|
|
21
|
+
constructor(maxFrames: number) {
|
|
22
|
+
super(`upstream SSE chunk exceeded ${maxFrames} frame limit`);
|
|
23
|
+
this.name = "SseFrameCountLimitError";
|
|
24
|
+
this.maxFrames = maxFrames;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type BoundedSseFrame = {
|
|
29
|
+
block: Uint8Array;
|
|
30
|
+
delimiter: Uint8Array;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Classify the bytes at `index` as an SSE block delimiter.
|
|
35
|
+
*
|
|
36
|
+
* Returns the delimiter length in bytes, `0` when `index` does not start a
|
|
37
|
+
* delimiter, and `undefined` when more bytes are required to decide.
|
|
38
|
+
*/
|
|
39
|
+
function delimiterLengthAt(
|
|
40
|
+
index: number,
|
|
41
|
+
length: number,
|
|
42
|
+
byteAt: (index: number) => number,
|
|
43
|
+
): number | undefined {
|
|
44
|
+
const first = byteAt(index);
|
|
45
|
+
if (first === 10) {
|
|
46
|
+
if (index + 1 >= length) return undefined;
|
|
47
|
+
const second = byteAt(index + 1);
|
|
48
|
+
if (second === 10) return 2;
|
|
49
|
+
if (second !== 13) return 0;
|
|
50
|
+
if (index + 2 >= length) return undefined;
|
|
51
|
+
return byteAt(index + 2) === 10 ? 3 : 0;
|
|
52
|
+
}
|
|
53
|
+
if (first !== 13) return 0;
|
|
54
|
+
if (index + 1 >= length) return undefined;
|
|
55
|
+
if (byteAt(index + 1) !== 10) return 0;
|
|
56
|
+
if (index + 2 >= length) return undefined;
|
|
57
|
+
const third = byteAt(index + 2);
|
|
58
|
+
if (third === 10) return 3;
|
|
59
|
+
if (third !== 13) return 0;
|
|
60
|
+
if (index + 3 >= length) return undefined;
|
|
61
|
+
return byteAt(index + 3) === 10 ? 4 : 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function delimiterBytesAt(
|
|
65
|
+
index: number,
|
|
66
|
+
delimiterLength: number,
|
|
67
|
+
byteAt: (index: number) => number,
|
|
68
|
+
): Uint8Array {
|
|
69
|
+
if (delimiterLength === 2) return LF_LF;
|
|
70
|
+
if (delimiterLength === 4) return CR_LF_CR_LF;
|
|
71
|
+
return byteAt(index) === 10 ? LF_CR_LF : CR_LF_LF;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function copyRange(
|
|
75
|
+
start: number,
|
|
76
|
+
end: number,
|
|
77
|
+
tailLength: number,
|
|
78
|
+
previousTail: Uint8Array,
|
|
79
|
+
chunk: Uint8Array,
|
|
80
|
+
): Uint8Array {
|
|
81
|
+
const out = new Uint8Array(end - start);
|
|
82
|
+
for (let index = start; index < end; index += 1) {
|
|
83
|
+
out[index - start] = index < tailLength
|
|
84
|
+
? previousTail[index]!
|
|
85
|
+
: chunk[index - tailLength]!;
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* True when a complete SSE block already commits a Responses terminal event.
|
|
92
|
+
*
|
|
93
|
+
* Framing errors in bytes *after* such a block must not retroactively turn an
|
|
94
|
+
* already-completed/failed/incomplete model turn into a transport failure. This
|
|
95
|
+
* helper is used only on the exceptional path, so decoding/JSON parsing has no
|
|
96
|
+
* cost on ordinary framing.
|
|
97
|
+
*/
|
|
98
|
+
function isResponsesTerminalFrame(block: Uint8Array): boolean {
|
|
99
|
+
const data: string[] = [];
|
|
100
|
+
for (const line of new TextDecoder().decode(block).split(/\r?\n/)) {
|
|
101
|
+
if (!line.startsWith("data:")) continue;
|
|
102
|
+
const value = line.slice(5);
|
|
103
|
+
data.push(value.startsWith(" ") ? value.slice(1) : value);
|
|
104
|
+
}
|
|
105
|
+
if (data.length === 0) return false;
|
|
106
|
+
const payload = data.join("\n");
|
|
107
|
+
if (payload === "[DONE]") return false;
|
|
108
|
+
try {
|
|
109
|
+
const parsed = JSON.parse(payload) as { type?: unknown };
|
|
110
|
+
return parsed.type === "response.completed"
|
|
111
|
+
|| parsed.type === "response.failed"
|
|
112
|
+
|| parsed.type === "response.incomplete";
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Byte-bounded SSE block framer for client-facing protocol paths.
|
|
120
|
+
*
|
|
121
|
+
* The delimiter scanner works on raw bytes, so fragmented UTF-8 cannot change
|
|
122
|
+
* accounting and a hostile upstream cannot grow an unterminated JS string
|
|
123
|
+
* without limit. Candidate bytes live in one geometrically grown buffer rather
|
|
124
|
+
* than one allocation per upstream chunk, bounding both bytes and object count.
|
|
125
|
+
* Complete blocks are returned without their delimiter; the exact delimiter
|
|
126
|
+
* bytes are returned separately so callers can relay bytes unchanged.
|
|
127
|
+
*/
|
|
128
|
+
export class BoundedSseFrameBuffer {
|
|
129
|
+
private readonly maxFrameBytes: number;
|
|
130
|
+
private readonly maxFramesPerFeed: number;
|
|
131
|
+
private delimiterTail: Uint8Array = new Uint8Array(0);
|
|
132
|
+
private candidate: Uint8Array = new Uint8Array(0);
|
|
133
|
+
private candidateBytes = 0;
|
|
134
|
+
private disposed = false;
|
|
135
|
+
|
|
136
|
+
constructor(maxFrameBytes = MAX_CLIENT_SSE_FRAME_BYTES) {
|
|
137
|
+
if (!Number.isSafeInteger(maxFrameBytes) || maxFrameBytes <= 0) {
|
|
138
|
+
throw new RangeError("maxFrameBytes must be a positive safe integer");
|
|
139
|
+
}
|
|
140
|
+
this.maxFrameBytes = maxFrameBytes;
|
|
141
|
+
// Delimiter-only input otherwise creates an object-amplification path that
|
|
142
|
+
// is independent of candidate bytes. Keep frame count proportional to the
|
|
143
|
+
// configured byte budget while leaving ample room for real Responses events.
|
|
144
|
+
this.maxFramesPerFeed = Math.max(1, Math.ceil(maxFrameBytes / 1024));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private clear(): void {
|
|
148
|
+
this.delimiterTail = new Uint8Array(0);
|
|
149
|
+
this.candidate = new Uint8Array(0);
|
|
150
|
+
this.candidateBytes = 0;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private ensureCapacity(requiredBytes: number): void {
|
|
154
|
+
if (this.candidate.byteLength >= requiredBytes) return;
|
|
155
|
+
if (requiredBytes > this.maxFrameBytes) {
|
|
156
|
+
throw new SseFrameTooLargeError(this.maxFrameBytes);
|
|
157
|
+
}
|
|
158
|
+
let capacity = this.candidate.byteLength === 0
|
|
159
|
+
? Math.min(this.maxFrameBytes, Math.max(requiredBytes, 4096))
|
|
160
|
+
: this.candidate.byteLength;
|
|
161
|
+
while (capacity < requiredBytes) {
|
|
162
|
+
capacity = Math.min(this.maxFrameBytes, Math.max(requiredBytes, capacity * 2));
|
|
163
|
+
}
|
|
164
|
+
const grown = new Uint8Array(capacity);
|
|
165
|
+
if (this.candidateBytes > 0) {
|
|
166
|
+
grown.set(this.candidate.subarray(0, this.candidateBytes));
|
|
167
|
+
}
|
|
168
|
+
this.candidate = grown;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private retain(slice: Uint8Array): void {
|
|
172
|
+
if (slice.byteLength === 0) return;
|
|
173
|
+
const nextBytes = this.candidateBytes + slice.byteLength;
|
|
174
|
+
if (nextBytes > this.maxFrameBytes) {
|
|
175
|
+
this.clear();
|
|
176
|
+
this.disposed = true;
|
|
177
|
+
throw new SseFrameTooLargeError(this.maxFrameBytes);
|
|
178
|
+
}
|
|
179
|
+
this.ensureCapacity(nextBytes);
|
|
180
|
+
this.candidate.set(slice, this.candidateBytes);
|
|
181
|
+
this.candidateBytes = nextBytes;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
private takeCandidate(): Uint8Array {
|
|
185
|
+
if (this.candidateBytes === 0) return new Uint8Array(0);
|
|
186
|
+
const block = this.candidate.slice(0, this.candidateBytes);
|
|
187
|
+
// Release the working allocation after each complete frame. This avoids
|
|
188
|
+
// retaining a rare multi-MiB frame allocation for the rest of a long-lived
|
|
189
|
+
// stream; normal small-frame allocation remains bounded by feed's frame cap.
|
|
190
|
+
this.candidate = new Uint8Array(0);
|
|
191
|
+
this.candidateBytes = 0;
|
|
192
|
+
return block;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
feed(chunk: Uint8Array): BoundedSseFrame[] {
|
|
196
|
+
if (this.disposed) return [];
|
|
197
|
+
if (chunk.byteLength === 0) return [];
|
|
198
|
+
|
|
199
|
+
const frames: BoundedSseFrame[] = [];
|
|
200
|
+
const previousTail = this.delimiterTail;
|
|
201
|
+
this.delimiterTail = new Uint8Array(0);
|
|
202
|
+
const tailLength = previousTail.byteLength;
|
|
203
|
+
const totalLength = tailLength + chunk.byteLength;
|
|
204
|
+
const byteAt = (index: number): number => index < tailLength
|
|
205
|
+
? previousTail[index]!
|
|
206
|
+
: chunk[index - tailLength]!;
|
|
207
|
+
const retainRange = (start: number, end: number): void => {
|
|
208
|
+
if (end <= start) return;
|
|
209
|
+
if (start < tailLength) {
|
|
210
|
+
this.retain(previousTail.subarray(start, Math.min(end, tailLength)));
|
|
211
|
+
}
|
|
212
|
+
if (end > tailLength) {
|
|
213
|
+
this.retain(chunk.subarray(Math.max(0, start - tailLength), end - tailLength));
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
let index = 0;
|
|
219
|
+
let retainedThrough = 0;
|
|
220
|
+
while (index < totalLength) {
|
|
221
|
+
const delimiterLength = delimiterLengthAt(index, totalLength, byteAt);
|
|
222
|
+
if (delimiterLength === undefined) break;
|
|
223
|
+
if (delimiterLength > 0) {
|
|
224
|
+
if (frames.length >= this.maxFramesPerFeed) {
|
|
225
|
+
this.clear();
|
|
226
|
+
this.disposed = true;
|
|
227
|
+
throw new SseFrameCountLimitError(this.maxFramesPerFeed);
|
|
228
|
+
}
|
|
229
|
+
retainRange(retainedThrough, index);
|
|
230
|
+
const block = this.takeCandidate();
|
|
231
|
+
const delimiter = delimiterBytesAt(index, delimiterLength, byteAt);
|
|
232
|
+
frames.push({ block, delimiter });
|
|
233
|
+
index += delimiterLength;
|
|
234
|
+
retainedThrough = index;
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
index += 1;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
retainRange(retainedThrough, index);
|
|
241
|
+
if (index < totalLength) {
|
|
242
|
+
this.delimiterTail = copyRange(index, totalLength, tailLength, previousTail, chunk);
|
|
243
|
+
}
|
|
244
|
+
return frames;
|
|
245
|
+
} catch (err) {
|
|
246
|
+
const framingError = err instanceof SseFrameTooLargeError
|
|
247
|
+
|| err instanceof SseFrameCountLimitError;
|
|
248
|
+
if (framingError && frames.some(frame => isResponsesTerminalFrame(frame.block))) {
|
|
249
|
+
// A terminal frame is the Responses protocol boundary. Ignore malformed
|
|
250
|
+
// or oversized bytes that occur later in the same upstream chunk rather
|
|
251
|
+
// than retroactively replacing the committed terminal with a 502.
|
|
252
|
+
this.clear();
|
|
253
|
+
this.disposed = true;
|
|
254
|
+
return frames;
|
|
255
|
+
}
|
|
256
|
+
throw err;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** Return the final unterminated block bytes and release all retained state. */
|
|
261
|
+
finish(): Uint8Array {
|
|
262
|
+
if (this.disposed) return new Uint8Array(0);
|
|
263
|
+
try {
|
|
264
|
+
this.retain(this.delimiterTail);
|
|
265
|
+
this.delimiterTail = new Uint8Array(0);
|
|
266
|
+
return this.takeCandidate();
|
|
267
|
+
} finally {
|
|
268
|
+
this.clear();
|
|
269
|
+
this.disposed = true;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
dispose(): void {
|
|
274
|
+
if (this.disposed) return;
|
|
275
|
+
this.clear();
|
|
276
|
+
this.disposed = true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export function joinSseFrameBytes(parts: readonly Uint8Array[]): Uint8Array {
|
|
281
|
+
let byteLength = 0;
|
|
282
|
+
for (const part of parts) byteLength += part.byteLength;
|
|
283
|
+
if (byteLength === 0) return new Uint8Array(0);
|
|
284
|
+
if (parts.length === 1 && parts[0]!.byteLength === byteLength) return parts[0]!;
|
|
285
|
+
const joined = new Uint8Array(byteLength);
|
|
286
|
+
let offset = 0;
|
|
287
|
+
for (const part of parts) {
|
|
288
|
+
joined.set(part, offset);
|
|
289
|
+
offset += part.byteLength;
|
|
290
|
+
}
|
|
291
|
+
return joined;
|
|
292
|
+
}
|