@yansigit/opencodex 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/LICENSE +21 -0
- package/README.md +303 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +587 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-BNJ7r4Gd.js +102 -0
- package/gui/dist/assets/index-CGoDO3uO.css +1 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +108 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +1327 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +121 -0
- package/src/adapters/client-fingerprint.ts +65 -0
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +601 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/checkpoint-store.ts +303 -0
- package/src/adapters/cursor/cursor-errors.ts +288 -0
- package/src/adapters/cursor/discovery.ts +333 -0
- package/src/adapters/cursor/effort-map.ts +151 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -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/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +269 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1653 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +76 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +547 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +663 -0
- package/src/adapters/cursor/protobuf-events.ts +1381 -0
- package/src/adapters/cursor/protobuf-request.ts +1032 -0
- package/src/adapters/cursor/request-builder.ts +461 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +735 -0
- package/src/adapters/cursor/tool-result-normalize.ts +92 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +79 -0
- package/src/adapters/cursor/types.ts +79 -0
- package/src/adapters/cursor.ts +322 -0
- package/src/adapters/google-antigravity-hosts.ts +48 -0
- package/src/adapters/google-antigravity-replay.ts +827 -0
- package/src/adapters/google-antigravity-tools.ts +105 -0
- package/src/adapters/google-antigravity-wire.ts +141 -0
- package/src/adapters/google-errors.ts +92 -0
- package/src/adapters/google-http.ts +460 -0
- package/src/adapters/google-tool-schema.ts +238 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +1377 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +112 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1936 -0
- package/src/adapters/mimo-free.ts +280 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1980 -0
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1911 -0
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +154 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/adapters/xai-web-search.ts +185 -0
- package/src/bridge.ts +1986 -0
- package/src/chat/inbound.ts +319 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +266 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +205 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +615 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +107 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +578 -0
- package/src/claude/model-info.ts +174 -0
- package/src/claude/outbound.ts +926 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +302 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +737 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +299 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +290 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +213 -0
- package/src/cli/claude.ts +355 -0
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +47 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +127 -0
- package/src/cli/config-command.ts +209 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/dispatch.ts +585 -0
- package/src/cli/doctor.ts +1202 -0
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +213 -0
- package/src/cli/help.ts +101 -0
- package/src/cli/index.ts +973 -0
- package/src/cli/init.ts +211 -0
- package/src/cli/integrations.ts +260 -0
- package/src/cli/interactive-confirm.ts +133 -0
- 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 +245 -0
- package/src/cli/models.ts +422 -0
- package/src/cli/observe.ts +206 -0
- package/src/cli/opencode.ts +588 -0
- package/src/cli/provider-replit.ts +232 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +492 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +422 -0
- package/src/cli/replit-gateway-key-input.ts +138 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +328 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +199 -0
- package/src/cli/v2.ts +268 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1704 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +47 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +544 -0
- package/src/codex/account-usability.ts +43 -0
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/agent-roles-sync.ts +225 -0
- package/src/codex/agent-roles.ts +238 -0
- package/src/codex/app-server-processes.ts +1143 -0
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +2147 -0
- package/src/codex/auth-collision.ts +109 -0
- package/src/codex/auth-context.ts +665 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +436 -0
- package/src/codex/catalog/bundled.ts +549 -0
- package/src/codex/catalog/effort.ts +446 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +664 -0
- package/src/codex/catalog/native-models.ts +72 -0
- package/src/codex/catalog/parsing.ts +650 -0
- package/src/codex/catalog/provider-fetch.ts +2064 -0
- package/src/codex/catalog/sync.ts +1883 -0
- 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 +14 -0
- 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/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1566 -0
- 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 +108 -0
- package/src/codex/history-provider.ts +979 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +220 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +290 -0
- package/src/codex/inject.ts +1733 -0
- package/src/codex/injected-marker.ts +106 -0
- 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 +225 -0
- 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 +56 -0
- package/src/codex/main-account.ts +68 -0
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +273 -0
- 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 +144 -0
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +425 -0
- 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 +573 -0
- package/src/codex/refresh.ts +62 -0
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +1888 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +2170 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +784 -0
- package/src/codex/sync.ts +319 -0
- 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 +298 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +160 -0
- package/src/combos/index.ts +45 -0
- package/src/combos/request.ts +94 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +398 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +4041 -0
- package/src/fork/register.ts +3 -0
- package/src/generated/compatibility-version.json +3116 -0
- package/src/generated/model-metadata.ts +106 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +530 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +955 -0
- package/src/images/plan.ts +143 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- 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/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +195 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +346 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +130 -0
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +364 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +380 -0
- package/src/lib/errors.ts +406 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -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/open-url.ts +25 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/pinned-http.ts +270 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-outbound.ts +210 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +521 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +61 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +119 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +86 -0
- package/src/lib/tool-argument-integers.ts +202 -0
- package/src/lib/translator-budget.ts +400 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +392 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-atomic-replace.ts +156 -0
- package/src/lib/windows-elevation.ts +773 -0
- package/src/lib/windows-secret-acl.ts +854 -0
- 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 +403 -0
- 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-routing.ts +594 -0
- package/src/oauth/anthropic.ts +188 -0
- package/src/oauth/antigravity-routing.ts +151 -0
- package/src/oauth/callback-server.ts +300 -0
- package/src/oauth/chatgpt.ts +161 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +252 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +262 -0
- package/src/oauth/health.ts +407 -0
- package/src/oauth/index.ts +1504 -0
- package/src/oauth/key-providers.ts +124 -0
- package/src/oauth/kimi.ts +227 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +130 -0
- package/src/oauth/log.ts +50 -0
- package/src/oauth/login-cli.ts +223 -0
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +728 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +695 -0
- package/src/providers/antigravity-quota.ts +216 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +74 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +144 -0
- package/src/providers/context-cap.ts +82 -0
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/derive.ts +586 -0
- package/src/providers/fastwire.ts +501 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +449 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/openai-sidecar.ts +243 -0
- package/src/providers/openai-tier-startup.ts +56 -0
- package/src/providers/openai-tiers.ts +423 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +185 -0
- package/src/providers/quota.ts +2345 -0
- package/src/providers/registry.ts +2918 -0
- package/src/providers/replit/constants.ts +27 -0
- package/src/providers/replit/derive.ts +85 -0
- package/src/providers/replit/headers.ts +28 -0
- package/src/providers/replit/origin.ts +55 -0
- package/src/providers/replit/pair-install-response.ts +72 -0
- package/src/providers/replit/probe.ts +199 -0
- package/src/providers/replit/setup.ts +350 -0
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +103 -0
- 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 +148 -0
- package/src/reasoning-effort.ts +183 -0
- package/src/responses/compaction.ts +142 -0
- 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 +838 -0
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +426 -0
- package/src/responses/schema.ts +165 -0
- package/src/responses/spill-store.ts +459 -0
- package/src/responses/state.ts +1433 -0
- package/src/responses/thought-signature-replay.ts +347 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/router.ts +761 -0
- 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 +53 -0
- package/src/server/auth-cors.ts +751 -0
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +442 -0
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +426 -0
- package/src/server/claude-messages.ts +1030 -0
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +190 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +568 -0
- package/src/server/index.ts +1813 -0
- package/src/server/lifecycle.ts +498 -0
- package/src/server/live.ts +717 -0
- 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 +1433 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +193 -0
- package/src/server/management/body.ts +41 -0
- package/src/server/management/combo-routes.ts +263 -0
- package/src/server/management/config-routes.ts +835 -0
- package/src/server/management/context.ts +113 -0
- 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 +586 -0
- package/src/server/management/model-routes.ts +560 -0
- 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 +637 -0
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +1033 -0
- package/src/server/management/replit-provider-routes.ts +86 -0
- 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 +385 -0
- package/src/server/management/shared.ts +286 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- 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 +435 -0
- package/src/server/management/system-routes.ts +194 -0
- package/src/server/management/usage-summary-cache.ts +94 -0
- 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 +314 -0
- package/src/server/management-auth.ts +482 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +328 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1209 -0
- package/src/server/request-decompress.ts +239 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1259 -0
- 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 +551 -0
- package/src/server/responses/compact.ts +771 -0
- package/src/server/responses/core.ts +5389 -0
- package/src/server/responses/empty-completion-guard.ts +276 -0
- package/src/server/responses/encrypted-payload.ts +331 -0
- package/src/server/responses/fetch-helpers.ts +232 -0
- package/src/server/responses/input-admission.ts +185 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +78 -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 +251 -0
- package/src/server/responses/upstream-error.ts +53 -0
- package/src/server/responses/ws-upstream.ts +308 -0
- package/src/server/responses-custom-tool-repair.ts +282 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- 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 +25 -0
- package/src/server/search.ts +201 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +315 -0
- package/src/server/startup-health-cache.ts +131 -0
- package/src/server/system-env.ts +484 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +472 -0
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +3575 -0
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +757 -0
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +876 -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 +106 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +415 -0
- package/src/update/job.ts +1887 -0
- package/src/update/notify.ts +263 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +259 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +625 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +416 -0
- package/src/usage/log.ts +1223 -0
- package/src/usage/summary.ts +753 -0
- package/src/usage/totals.ts +14 -0
- 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 +189 -0
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +131 -0
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +681 -0
- 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 +195 -0
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +88 -0
- package/src/web-search/executor.ts +113 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +331 -0
- package/src/web-search/loop.ts +896 -0
- package/src/web-search/parse.ts +315 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/synthetic-tool.ts +47 -0
- package/src/web-search/xai-executor.ts +219 -0
|
@@ -0,0 +1,926 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code outbound: internal /v1/responses output -> Anthropic Messages API shapes.
|
|
3
|
+
*
|
|
4
|
+
* Wire contract pinned in devlog/260711_claude_inbound/003_evidence.md (all Tier 2):
|
|
5
|
+
* - SSE order: message_start -> (content_block_start -> deltas -> content_block_stop)*
|
|
6
|
+
* -> message_delta -> message_stop; any number of `ping`.
|
|
7
|
+
* - thinking blocks get thinking_delta(s) then ONE synthetic signature_delta just
|
|
8
|
+
* before content_block_stop (CCR precedent: Claude Code does not verify signatures).
|
|
9
|
+
* - message_delta.usage is cumulative; message_start embeds a full message snapshot.
|
|
10
|
+
* - errors: {type:"error", error:{type,message}}; may arrive mid-stream after HTTP 200.
|
|
11
|
+
*/
|
|
12
|
+
import { createHash } from "node:crypto";
|
|
13
|
+
import { isTransientUpstreamStatus } from "../lib/upstream-retry";
|
|
14
|
+
import {
|
|
15
|
+
isTranslatorBudgetExceededError,
|
|
16
|
+
TRANSLATOR_MAX_TURN_BYTES,
|
|
17
|
+
TranslatorBudgetExceededError,
|
|
18
|
+
type TranslatorBudget,
|
|
19
|
+
} from "../lib/translator-budget";
|
|
20
|
+
import { sseFieldOffset, sseFieldValue } from "../lib/sse-decoder";
|
|
21
|
+
|
|
22
|
+
type Rec = Record<string, unknown>;
|
|
23
|
+
|
|
24
|
+
function isRec(v: unknown): v is Rec {
|
|
25
|
+
return !!v && typeof v === "object" && !Array.isArray(v);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function reasoningIdentityDigest(value: string): string {
|
|
29
|
+
return createHash("sha256").update(value).digest("base64url");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Fixed-size identity that preserves protocol boundaries without retaining upstream strings. */
|
|
33
|
+
function boundedReasoningIdentity(value: unknown): string {
|
|
34
|
+
if (typeof value === "number") {
|
|
35
|
+
if (Number.isSafeInteger(value) && value >= 0) return `n${value}`;
|
|
36
|
+
if (Number.isFinite(value)) return `d${value}`;
|
|
37
|
+
return Number.isNaN(value) ? "dnan" : value > 0 ? "dinf" : "d-inf";
|
|
38
|
+
}
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
return `s${reasoningIdentityDigest(value)}`;
|
|
41
|
+
}
|
|
42
|
+
if (value === null) return "z";
|
|
43
|
+
if (typeof value === "boolean") return value ? "b1" : "b0";
|
|
44
|
+
if (Array.isArray(value)) return `a${reasoningIdentityDigest(JSON.stringify(value) ?? "[]")}`;
|
|
45
|
+
// Preserve the prior String(record) category semantics without serializing untrusted trees.
|
|
46
|
+
return typeof value === "object" ? "o" : "u";
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function uuid(): string {
|
|
50
|
+
return crypto.randomUUID().replace(/-/g, "");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** HTTP status -> Anthropic error taxonomy (010 amendment #4; full official table per devlog 100). */
|
|
54
|
+
export function anthropicErrorType(status: number): string {
|
|
55
|
+
switch (status) {
|
|
56
|
+
case 400: return "invalid_request_error";
|
|
57
|
+
case 401: return "authentication_error";
|
|
58
|
+
case 402: return "billing_error";
|
|
59
|
+
case 403: return "permission_error";
|
|
60
|
+
case 404: return "not_found_error";
|
|
61
|
+
case 409: return "conflict_error";
|
|
62
|
+
case 413: return "request_too_large";
|
|
63
|
+
case 429: return "rate_limit_error";
|
|
64
|
+
case 504: return "timeout_error";
|
|
65
|
+
case 529: return "overloaded_error";
|
|
66
|
+
default: return status >= 500 ? "api_error" : "invalid_request_error";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function anthropicErrorBody(status: number, message: string, type?: string, code?: string): Rec {
|
|
71
|
+
return { type: "error", error: { type: type ?? anthropicErrorType(status), message, ...(code ? { code } : {}) } };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function anthropicErrorResponse(status: number, message: string, type?: string, code?: string): Response {
|
|
75
|
+
return new Response(JSON.stringify(anthropicErrorBody(status, message, type, code)), {
|
|
76
|
+
status,
|
|
77
|
+
headers: { "Content-Type": "application/json" },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Responses usage -> Anthropic usage. Responses `input_tokens` is INCLUSIVE of cache
|
|
83
|
+
* read+write (types.ts convention); Anthropic `input_tokens` excludes both, so
|
|
84
|
+
* subtract the full cache detail (devlog 070 — subtracting reads only inflated the
|
|
85
|
+
* non-cached input Claude Code displays by the write share).
|
|
86
|
+
*/
|
|
87
|
+
export function anthropicUsage(usage: unknown, webSearchRequests = 0): Rec {
|
|
88
|
+
const u = isRec(usage) ? usage : {};
|
|
89
|
+
const details = isRec(u.input_tokens_details) ? u.input_tokens_details : {};
|
|
90
|
+
const cached = typeof details.cached_tokens === "number" ? details.cached_tokens : 0;
|
|
91
|
+
const cacheWrite = typeof details.cache_write_tokens === "number" ? details.cache_write_tokens : 0;
|
|
92
|
+
const input = typeof u.input_tokens === "number" ? u.input_tokens : 0;
|
|
93
|
+
const output = typeof u.output_tokens === "number" ? u.output_tokens : 0;
|
|
94
|
+
return {
|
|
95
|
+
input_tokens: Math.max(0, input - cached - cacheWrite),
|
|
96
|
+
output_tokens: output,
|
|
97
|
+
cache_read_input_tokens: cached,
|
|
98
|
+
cache_creation_input_tokens: cacheWrite,
|
|
99
|
+
// Only successful searches are billed/counted (Anthropic contract; Claude Code cost accounting).
|
|
100
|
+
...(webSearchRequests > 0 ? { server_tool_use: { web_search_requests: webSearchRequests } } : {}),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function sseFrame(name: string, data: Rec): string {
|
|
105
|
+
return `event: ${name}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function appendedUtf8Bytes(previous: string, previousBytes: number, fragment: string): number {
|
|
109
|
+
let nextBytes = previousBytes + Buffer.byteLength(fragment);
|
|
110
|
+
const previousLast = previous.charCodeAt(previous.length - 1);
|
|
111
|
+
const fragmentFirst = fragment.charCodeAt(0);
|
|
112
|
+
if (previousLast >= 0xd800 && previousLast <= 0xdbff
|
|
113
|
+
&& fragmentFirst >= 0xdc00 && fragmentFirst <= 0xdfff) {
|
|
114
|
+
nextBytes -= 2;
|
|
115
|
+
}
|
|
116
|
+
return nextBytes;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Claude Code / Anthropic WebSearch domain filters are optional and mutually exclusive.
|
|
121
|
+
* Empty arrays are rejected ("ambiguous"); both fields together are rejected. Routed models
|
|
122
|
+
* often emit both shapes — sanitize before Claude Code sees the tool_use / server_tool_use
|
|
123
|
+
* input (issue #381).
|
|
124
|
+
*/
|
|
125
|
+
export function isClaudeWebSearchToolName(name: string): boolean {
|
|
126
|
+
const trimmed = name.trim();
|
|
127
|
+
return trimmed === "WebSearch" || /^web_search/i.test(trimmed);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function normalizeWebSearchDomainList(value: unknown): string[] | undefined {
|
|
131
|
+
if (!Array.isArray(value)) return undefined;
|
|
132
|
+
const domains = value
|
|
133
|
+
.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0)
|
|
134
|
+
.map(entry => entry.trim());
|
|
135
|
+
return domains.length > 0 ? domains : undefined;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Strip empty domain filters; if both remain, keep `allowed_domains` and drop `blocked_domains`. */
|
|
139
|
+
export function sanitizeWebSearchInput(input: unknown): Rec {
|
|
140
|
+
const src: Rec = isRec(input) ? { ...input } : {};
|
|
141
|
+
const allowed = normalizeWebSearchDomainList(src.allowed_domains);
|
|
142
|
+
const blocked = normalizeWebSearchDomainList(src.blocked_domains);
|
|
143
|
+
delete src.allowed_domains;
|
|
144
|
+
delete src.blocked_domains;
|
|
145
|
+
if (allowed && blocked) {
|
|
146
|
+
// Prefer allow-list (restrict-to) when a routed model sets both non-empty fields.
|
|
147
|
+
src.allowed_domains = allowed;
|
|
148
|
+
} else if (allowed) {
|
|
149
|
+
src.allowed_domains = allowed;
|
|
150
|
+
} else if (blocked) {
|
|
151
|
+
src.blocked_domains = blocked;
|
|
152
|
+
}
|
|
153
|
+
return src;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Map a Responses `web_search_call` item to its Anthropic pair: the server_tool_use
|
|
158
|
+
* input (query/queries) and the web_search_tool_result content (hits, or the error
|
|
159
|
+
* object when the search failed). Shared by the SSE and JSON translation paths.
|
|
160
|
+
*/
|
|
161
|
+
function webSearchPairFromItem(item: Rec): { id: string; input: Rec; resultContent: unknown; completed: boolean } {
|
|
162
|
+
const action = isRec(item.action) ? item.action : {};
|
|
163
|
+
const queries = Array.isArray(action.queries)
|
|
164
|
+
? action.queries.filter((q): q is string => typeof q === "string" && q.length > 0)
|
|
165
|
+
: [];
|
|
166
|
+
const query = typeof action.query === "string" ? action.query : "";
|
|
167
|
+
const input = sanitizeWebSearchInput(
|
|
168
|
+
queries.length > 1 ? { queries } : { query: queries[0] ?? query },
|
|
169
|
+
);
|
|
170
|
+
const completed = item.status !== "failed";
|
|
171
|
+
let resultContent: unknown;
|
|
172
|
+
if (completed) {
|
|
173
|
+
const hits: Rec[] = [];
|
|
174
|
+
if (Array.isArray(item.sources)) {
|
|
175
|
+
for (const s of item.sources) {
|
|
176
|
+
if (isRec(s) && typeof s.url === "string" && s.url.length > 0) {
|
|
177
|
+
hits.push({ type: "web_search_result", title: typeof s.title === "string" ? s.title : "", url: s.url });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
resultContent = hits;
|
|
182
|
+
} else {
|
|
183
|
+
resultContent = { type: "web_search_tool_result_error", error_code: "unavailable" };
|
|
184
|
+
}
|
|
185
|
+
const id = typeof item.id === "string" && item.id.length > 0 ? item.id : `srvtoolu_${uuid()}`;
|
|
186
|
+
return { id, input, resultContent, completed };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function messageSnapshot(model: string): Rec {
|
|
190
|
+
return {
|
|
191
|
+
id: `msg_${uuid()}`,
|
|
192
|
+
type: "message",
|
|
193
|
+
role: "assistant",
|
|
194
|
+
content: [],
|
|
195
|
+
model,
|
|
196
|
+
stop_reason: null,
|
|
197
|
+
stop_sequence: null,
|
|
198
|
+
usage: { input_tokens: 0, output_tokens: 0 },
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
interface OpenBlock {
|
|
203
|
+
kind: "text" | "thinking" | "tool_use";
|
|
204
|
+
index: number;
|
|
205
|
+
/** Responses item_id (tool calls) so output_item.done can match. */
|
|
206
|
+
itemId?: string;
|
|
207
|
+
/** Buffer WebSearch args and emit one sanitized input_json_delta on close (#381). */
|
|
208
|
+
bufferWebSearchArgs?: boolean;
|
|
209
|
+
argsBuf?: string;
|
|
210
|
+
argsBufBytes?: number;
|
|
211
|
+
webSearchArgsEmitted?: boolean;
|
|
212
|
+
callId?: string;
|
|
213
|
+
/** Last fixed-size reasoning identity (item + summary/content index) seen by this block. */
|
|
214
|
+
reasoningPartKey?: string;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Streaming: Responses SSE bytes -> Anthropic Messages SSE bytes. */
|
|
218
|
+
export function responsesSseToAnthropicSse(
|
|
219
|
+
upstream: ReadableStream<Uint8Array>,
|
|
220
|
+
model: string,
|
|
221
|
+
opts: { pingIntervalMs?: number; translatorBudget: TranslatorBudget },
|
|
222
|
+
): ReadableStream<Uint8Array> {
|
|
223
|
+
const translatorBudget = opts.translatorBudget;
|
|
224
|
+
const pingIntervalMs = opts?.pingIntervalMs ?? 20_000;
|
|
225
|
+
const decoder = new TextDecoder();
|
|
226
|
+
const encoder = new TextEncoder();
|
|
227
|
+
let buffer = "";
|
|
228
|
+
let bufferBytes = 0;
|
|
229
|
+
let started = false;
|
|
230
|
+
let terminated = false;
|
|
231
|
+
let cancelled = false;
|
|
232
|
+
let blockIndex = 0;
|
|
233
|
+
let open: OpenBlock | null = null;
|
|
234
|
+
let sawToolUse = false;
|
|
235
|
+
let webSearchRequests = 0;
|
|
236
|
+
let pingTimer: ReturnType<typeof setInterval> | undefined;
|
|
237
|
+
let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
|
|
238
|
+
const utf8SliceBytes = (value: string, start: number, end: number): number => {
|
|
239
|
+
let bytes = 0;
|
|
240
|
+
for (let index = start; index < end; index++) {
|
|
241
|
+
const codePoint = value.codePointAt(index)!;
|
|
242
|
+
if (codePoint <= 0x7f) bytes += 1;
|
|
243
|
+
else if (codePoint <= 0x7ff) bytes += 2;
|
|
244
|
+
else if (codePoint <= 0xffff) bytes += 3;
|
|
245
|
+
else { bytes += 4; index += 1; }
|
|
246
|
+
}
|
|
247
|
+
return bytes;
|
|
248
|
+
};
|
|
249
|
+
const queuedLiveFrameBytes: number[] = [];
|
|
250
|
+
const releaseDeliveredFrame = () => {
|
|
251
|
+
const bytes = queuedLiveFrameBytes.shift();
|
|
252
|
+
if (bytes !== undefined) translatorBudget.releaseRetained(bytes, { kind: "live_transient" });
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
return new ReadableStream<Uint8Array>({
|
|
256
|
+
start(controller) {
|
|
257
|
+
const emit = (name: string, data: Rec) => {
|
|
258
|
+
const frame = encoder.encode(sseFrame(name, data));
|
|
259
|
+
const reservation = translatorBudget.reserveTransient(frame.byteLength, { kind: "live_transient" });
|
|
260
|
+
controller.enqueue(frame);
|
|
261
|
+
reservation.commitRetained();
|
|
262
|
+
queuedLiveFrameBytes.push(frame.byteLength);
|
|
263
|
+
};
|
|
264
|
+
const ensureStarted = () => {
|
|
265
|
+
if (started) return;
|
|
266
|
+
started = true;
|
|
267
|
+
emit("message_start", { type: "message_start", message: messageSnapshot(model) });
|
|
268
|
+
emit("ping", { type: "ping" });
|
|
269
|
+
};
|
|
270
|
+
// Once a semantic Anthropic message has started, keepalive pings protect remote
|
|
271
|
+
// deployments behind LB/NAT idle timeouts. Transport-only Responses prelude frames
|
|
272
|
+
// must not manufacture a message before a possible initial error.
|
|
273
|
+
if (pingIntervalMs > 0) {
|
|
274
|
+
pingTimer = setInterval(() => {
|
|
275
|
+
if (terminated || !started) return;
|
|
276
|
+
try {
|
|
277
|
+
emit("ping", { type: "ping" });
|
|
278
|
+
} catch { /* controller torn down; the read loop is ending anyway */ }
|
|
279
|
+
}, pingIntervalMs);
|
|
280
|
+
}
|
|
281
|
+
const closeOpenBlock = () => {
|
|
282
|
+
if (!open) return;
|
|
283
|
+
if (open.kind === "tool_use" && open.bufferWebSearchArgs && !open.webSearchArgsEmitted) {
|
|
284
|
+
// Emit sanitized args even if output_item.done never supplied a full arguments field
|
|
285
|
+
// (finish/fail paths, or deltas-only streams).
|
|
286
|
+
let parsed: unknown = {};
|
|
287
|
+
const rawArgs = open.argsBuf ?? "";
|
|
288
|
+
try { parsed = rawArgs.length > 0 ? JSON.parse(rawArgs) : {}; } catch { parsed = {}; }
|
|
289
|
+
emit("content_block_delta", {
|
|
290
|
+
type: "content_block_delta",
|
|
291
|
+
index: open.index,
|
|
292
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(sanitizeWebSearchInput(parsed)) },
|
|
293
|
+
});
|
|
294
|
+
open.webSearchArgsEmitted = true;
|
|
295
|
+
}
|
|
296
|
+
if (open.kind === "thinking") {
|
|
297
|
+
// Synthetic signature: Claude Code accepts it (003 E6); inbound drops replays anyway.
|
|
298
|
+
emit("content_block_delta", {
|
|
299
|
+
type: "content_block_delta", index: open.index,
|
|
300
|
+
delta: { type: "signature_delta", signature: `ocx${Date.now()}` },
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
emit("content_block_stop", { type: "content_block_stop", index: open.index });
|
|
304
|
+
if (open.callId) translatorBudget.closeCall(open.callId);
|
|
305
|
+
open = null;
|
|
306
|
+
};
|
|
307
|
+
const ensureBlock = (kind: "text" | "thinking") => {
|
|
308
|
+
ensureStarted();
|
|
309
|
+
if (open && open.kind === kind) return;
|
|
310
|
+
closeOpenBlock();
|
|
311
|
+
const index = blockIndex++;
|
|
312
|
+
const contentBlock: Rec = kind === "text"
|
|
313
|
+
? { type: "text", text: "" }
|
|
314
|
+
: { type: "thinking", thinking: "", signature: "" };
|
|
315
|
+
emit("content_block_start", { type: "content_block_start", index, content_block: contentBlock });
|
|
316
|
+
open = { kind, index };
|
|
317
|
+
};
|
|
318
|
+
const finish = (stopReason: string, usage: unknown) => {
|
|
319
|
+
if (terminated) return;
|
|
320
|
+
terminated = true;
|
|
321
|
+
ensureStarted();
|
|
322
|
+
closeOpenBlock();
|
|
323
|
+
emit("message_delta", {
|
|
324
|
+
type: "message_delta",
|
|
325
|
+
delta: { stop_reason: stopReason, stop_sequence: null },
|
|
326
|
+
usage: anthropicUsage(usage, webSearchRequests),
|
|
327
|
+
});
|
|
328
|
+
emit("message_stop", { type: "message_stop" });
|
|
329
|
+
};
|
|
330
|
+
// upstreamDerived: transient upstream statuses become overloaded_error so the
|
|
331
|
+
// Anthropic-SDK client retries with backoff; proxy-internal exceptions stay
|
|
332
|
+
// api_error — a deterministic ocx bug must not be masked as retryable
|
|
333
|
+
// (devlog/_plan/260716_claudecode_hardening/020). On win32 mid-stream socket
|
|
334
|
+
// resets reach the reader catch (no failed-tail relay) and stay api_error —
|
|
335
|
+
// same as today, deliberate residual.
|
|
336
|
+
const fail = (status: number, message: string, upstreamDerived = false, code?: string) => {
|
|
337
|
+
if (terminated) return;
|
|
338
|
+
terminated = true;
|
|
339
|
+
if (code === "translation_buffer_limit") {
|
|
340
|
+
if (open?.callId) translatorBudget.closeCall(open.callId);
|
|
341
|
+
open = null;
|
|
342
|
+
// No normal close frames are valid after overflow. Emit exactly one bounded
|
|
343
|
+
// typed terminal without consulting the exhausted budget.
|
|
344
|
+
controller.enqueue(encoder.encode(sseFrame("error", anthropicErrorBody(
|
|
345
|
+
413,
|
|
346
|
+
message,
|
|
347
|
+
"request_too_large",
|
|
348
|
+
"translation_buffer_limit",
|
|
349
|
+
))));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const type = upstreamDerived && isTransientUpstreamStatus(status) ? "overloaded_error" : undefined;
|
|
353
|
+
if (!started) {
|
|
354
|
+
// An initial upstream failure is an Anthropic error stream, not a partial message.
|
|
355
|
+
// Do not manufacture message_start/ping before the terminal error.
|
|
356
|
+
emit("error", anthropicErrorBody(status, message, type, code));
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
closeOpenBlock();
|
|
360
|
+
emit("error", anthropicErrorBody(status, message, type, code));
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const handleFrame = (eventName: string, data: Rec) => {
|
|
364
|
+
switch (eventName) {
|
|
365
|
+
case "response.created":
|
|
366
|
+
// Transport prelude only. Start Anthropic framing on semantic output or completion.
|
|
367
|
+
break;
|
|
368
|
+
case "response.heartbeat":
|
|
369
|
+
if (started) emit("ping", { type: "ping" });
|
|
370
|
+
break;
|
|
371
|
+
case "response.output_text.delta": {
|
|
372
|
+
if (typeof data.delta !== "string" || data.delta.length === 0) break;
|
|
373
|
+
ensureBlock("text");
|
|
374
|
+
emit("content_block_delta", {
|
|
375
|
+
type: "content_block_delta", index: open!.index,
|
|
376
|
+
delta: { type: "text_delta", text: data.delta },
|
|
377
|
+
});
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case "response.reasoning_summary_text.delta":
|
|
381
|
+
case "response.reasoning_text.delta": {
|
|
382
|
+
if (typeof data.delta !== "string" || data.delta.length === 0) break;
|
|
383
|
+
ensureBlock("thinking");
|
|
384
|
+
// The JSON path joins reasoning summary/content parts with "\n\n"
|
|
385
|
+
// (responsesJsonToAnthropicMessage); mirror that at part and item boundaries
|
|
386
|
+
// so multi-part summaries do not glue into one run-on paragraph. Frames
|
|
387
|
+
// without part indices produce a constant key and never get a separator.
|
|
388
|
+
const slot = eventName === "response.reasoning_summary_text.delta"
|
|
389
|
+
? `s${boundedReasoningIdentity(data.summary_index)}`
|
|
390
|
+
: `c${boundedReasoningIdentity(data.content_index)}`;
|
|
391
|
+
// Upstream string metadata can be arbitrarily large. Hash strings into fixed-size
|
|
392
|
+
// components while retaining item and part equality, rather than dropping item_id and
|
|
393
|
+
// accidentally joining distinct malformed reasoning items.
|
|
394
|
+
const partKey = `${boundedReasoningIdentity(data.item_id)}:${slot}`;
|
|
395
|
+
if (open!.reasoningPartKey !== undefined && open!.reasoningPartKey !== partKey) {
|
|
396
|
+
emit("content_block_delta", {
|
|
397
|
+
type: "content_block_delta", index: open!.index,
|
|
398
|
+
delta: { type: "thinking_delta", thinking: "\n\n" },
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
open!.reasoningPartKey = partKey;
|
|
402
|
+
emit("content_block_delta", {
|
|
403
|
+
type: "content_block_delta", index: open!.index,
|
|
404
|
+
delta: { type: "thinking_delta", thinking: data.delta },
|
|
405
|
+
});
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case "response.output_item.added": {
|
|
409
|
+
const item = isRec(data.item) ? data.item : null;
|
|
410
|
+
if (!item || item.type !== "function_call") break;
|
|
411
|
+
ensureStarted();
|
|
412
|
+
closeOpenBlock();
|
|
413
|
+
sawToolUse = true;
|
|
414
|
+
const index = blockIndex++;
|
|
415
|
+
const name = typeof item.name === "string" ? item.name : "";
|
|
416
|
+
const bufferWebSearchArgs = isClaudeWebSearchToolName(name);
|
|
417
|
+
const callId = typeof item.call_id === "string" ? item.call_id : `toolu_${uuid()}`;
|
|
418
|
+
emit("content_block_start", {
|
|
419
|
+
type: "content_block_start", index,
|
|
420
|
+
content_block: {
|
|
421
|
+
type: "tool_use",
|
|
422
|
+
id: callId,
|
|
423
|
+
name,
|
|
424
|
+
input: {},
|
|
425
|
+
},
|
|
426
|
+
});
|
|
427
|
+
translatorBudget.openCall(callId);
|
|
428
|
+
open = {
|
|
429
|
+
kind: "tool_use",
|
|
430
|
+
index,
|
|
431
|
+
callId,
|
|
432
|
+
itemId: typeof item.id === "string" ? item.id : undefined,
|
|
433
|
+
bufferWebSearchArgs,
|
|
434
|
+
argsBuf: "",
|
|
435
|
+
argsBufBytes: 0,
|
|
436
|
+
webSearchArgsEmitted: false,
|
|
437
|
+
};
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
case "response.function_call_arguments.delta": {
|
|
441
|
+
if (typeof data.delta !== "string" || data.delta.length === 0) break;
|
|
442
|
+
if (!open || open.kind !== "tool_use") break;
|
|
443
|
+
if (open.bufferWebSearchArgs) {
|
|
444
|
+
const previous = open.argsBuf ?? "";
|
|
445
|
+
const previousBytes = open.argsBufBytes ?? 0;
|
|
446
|
+
const nextBytes = appendedUtf8Bytes(previous, previousBytes, data.delta);
|
|
447
|
+
const scope = {
|
|
448
|
+
kind: "tool_args",
|
|
449
|
+
...(open.callId ? { callId: open.callId } : {}),
|
|
450
|
+
} as const;
|
|
451
|
+
const reservation = translatorBudget.reserveTransient(nextBytes, scope);
|
|
452
|
+
try {
|
|
453
|
+
open.argsBuf = previous + data.delta;
|
|
454
|
+
open.argsBufBytes = nextBytes;
|
|
455
|
+
reservation.commitRetained();
|
|
456
|
+
translatorBudget.releaseRetained(previousBytes, scope);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
reservation.release();
|
|
459
|
+
throw error;
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
emit("content_block_delta", {
|
|
464
|
+
type: "content_block_delta", index: open.index,
|
|
465
|
+
delta: { type: "input_json_delta", partial_json: data.delta },
|
|
466
|
+
});
|
|
467
|
+
break;
|
|
468
|
+
}
|
|
469
|
+
case "response.output_item.done": {
|
|
470
|
+
const item = isRec(data.item) ? data.item : null;
|
|
471
|
+
if (!item) break;
|
|
472
|
+
// Server-side web search (native passthrough or sidecar bridge): translate the
|
|
473
|
+
// finished call into the Anthropic pair Claude Code natively parses —
|
|
474
|
+
// server_tool_use (query via input_json_delta) + web_search_tool_result.
|
|
475
|
+
// Never marks sawToolUse (stop_reason stays end_turn unless a real tool ran).
|
|
476
|
+
if (item.type === "web_search_call") {
|
|
477
|
+
ensureStarted();
|
|
478
|
+
closeOpenBlock();
|
|
479
|
+
const pair = webSearchPairFromItem(item);
|
|
480
|
+
const toolIndex = blockIndex++;
|
|
481
|
+
emit("content_block_start", {
|
|
482
|
+
type: "content_block_start", index: toolIndex,
|
|
483
|
+
content_block: { type: "server_tool_use", id: pair.id, name: "web_search" },
|
|
484
|
+
});
|
|
485
|
+
emit("content_block_delta", {
|
|
486
|
+
type: "content_block_delta", index: toolIndex,
|
|
487
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(pair.input) },
|
|
488
|
+
});
|
|
489
|
+
emit("content_block_stop", { type: "content_block_stop", index: toolIndex });
|
|
490
|
+
const resultIndex = blockIndex++;
|
|
491
|
+
emit("content_block_start", {
|
|
492
|
+
type: "content_block_start", index: resultIndex,
|
|
493
|
+
content_block: { type: "web_search_tool_result", tool_use_id: pair.id, content: pair.resultContent },
|
|
494
|
+
});
|
|
495
|
+
emit("content_block_stop", { type: "content_block_stop", index: resultIndex });
|
|
496
|
+
if (pair.completed) webSearchRequests++;
|
|
497
|
+
break;
|
|
498
|
+
}
|
|
499
|
+
if (!open) break;
|
|
500
|
+
// Close the matching open block (message/reasoning items close implicitly on
|
|
501
|
+
// the next block; function_call items must close here so tool input parses).
|
|
502
|
+
if (open.kind === "tool_use" && item.type === "function_call") {
|
|
503
|
+
if (open.bufferWebSearchArgs && !open.webSearchArgsEmitted) {
|
|
504
|
+
const rawArgs = typeof item.arguments === "string" && item.arguments.length > 0
|
|
505
|
+
? item.arguments
|
|
506
|
+
: (open.argsBuf ?? "");
|
|
507
|
+
let parsed: unknown = {};
|
|
508
|
+
try { parsed = rawArgs.length > 0 ? JSON.parse(rawArgs) : {}; } catch { parsed = {}; }
|
|
509
|
+
emit("content_block_delta", {
|
|
510
|
+
type: "content_block_delta",
|
|
511
|
+
index: open.index,
|
|
512
|
+
delta: { type: "input_json_delta", partial_json: JSON.stringify(sanitizeWebSearchInput(parsed)) },
|
|
513
|
+
});
|
|
514
|
+
open.webSearchArgsEmitted = true;
|
|
515
|
+
}
|
|
516
|
+
closeOpenBlock();
|
|
517
|
+
}
|
|
518
|
+
else if (open.kind === "text" && item.type === "message") closeOpenBlock();
|
|
519
|
+
else if (open.kind === "thinking" && item.type === "reasoning") closeOpenBlock();
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
case "response.completed": {
|
|
523
|
+
const response = isRec(data.response) ? data.response : {};
|
|
524
|
+
if (response.end_turn === false && !sawToolUse) {
|
|
525
|
+
fail(529, "upstream turn ended without a final answer", true);
|
|
526
|
+
break;
|
|
527
|
+
}
|
|
528
|
+
finish(sawToolUse ? "tool_use" : "end_turn", response.usage);
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
case "response.incomplete": {
|
|
532
|
+
const response = isRec(data.response) ? data.response : {};
|
|
533
|
+
const details = isRec(response.incomplete_details) ? response.incomplete_details : {};
|
|
534
|
+
if (details.reason === "max_output_tokens") {
|
|
535
|
+
finish("max_tokens", response.usage);
|
|
536
|
+
} else if (details.reason === "content_filter") {
|
|
537
|
+
finish("refusal", response.usage);
|
|
538
|
+
} else {
|
|
539
|
+
const message = typeof details.message === "string" && details.message.trim()
|
|
540
|
+
? details.message
|
|
541
|
+
: `upstream response was incomplete${typeof details.reason === "string" ? ` (${details.reason})` : ""}`;
|
|
542
|
+
fail(529, message, true);
|
|
543
|
+
}
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
case "response.failed": {
|
|
547
|
+
const response = isRec(data.response) ? data.response : {};
|
|
548
|
+
const error = isRec(response.error) ? response.error : {};
|
|
549
|
+
const message = typeof error.message === "string" ? error.message : "upstream request failed";
|
|
550
|
+
const code = typeof error.code === "string" ? error.code : undefined;
|
|
551
|
+
if (code === "translation_buffer_limit") {
|
|
552
|
+
throw new TranslatorBudgetExceededError("live_transient", TRANSLATOR_MAX_TURN_BYTES);
|
|
553
|
+
}
|
|
554
|
+
const status = code === "translation_buffer_limit"
|
|
555
|
+
? 413
|
|
556
|
+
: typeof error.status === "number" ? error.status : 500;
|
|
557
|
+
// status-absent response.failed (relaySseWithFailedTail synthetic tail) defaults
|
|
558
|
+
// to 500, which is in the transient set — the mid-stream reset shape maps to
|
|
559
|
+
// overloaded_error by design.
|
|
560
|
+
fail(
|
|
561
|
+
status,
|
|
562
|
+
message,
|
|
563
|
+
true,
|
|
564
|
+
);
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
default:
|
|
568
|
+
break; // web_search_call / custom_tool_call / content_part frames: ignored v1
|
|
569
|
+
}
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
reader = upstream.getReader();
|
|
573
|
+
void (async () => {
|
|
574
|
+
try {
|
|
575
|
+
for (;;) {
|
|
576
|
+
const { done, value } = await reader.read();
|
|
577
|
+
if (done) break;
|
|
578
|
+
const decodedReservation = translatorBudget.reserveTransient(value.byteLength, { kind: "live_transient" });
|
|
579
|
+
let decodedCommitted = false;
|
|
580
|
+
try {
|
|
581
|
+
const fragment = decoder.decode(value, { stream: true });
|
|
582
|
+
decodedReservation.commitRetained();
|
|
583
|
+
decodedCommitted = true;
|
|
584
|
+
const nextBufferBytes = appendedUtf8Bytes(buffer, bufferBytes, fragment);
|
|
585
|
+
const appendReservation = translatorBudget.reserveTransient(nextBufferBytes, { kind: "live_transient" });
|
|
586
|
+
try {
|
|
587
|
+
buffer += fragment;
|
|
588
|
+
appendReservation.commitRetained();
|
|
589
|
+
translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
590
|
+
bufferBytes = nextBufferBytes;
|
|
591
|
+
} catch (error) {
|
|
592
|
+
appendReservation.release();
|
|
593
|
+
throw error;
|
|
594
|
+
}
|
|
595
|
+
let sep: number;
|
|
596
|
+
while ((sep = buffer.indexOf("\n\n")) !== -1) {
|
|
597
|
+
const rawFrameBytes = utf8SliceBytes(buffer, 0, sep);
|
|
598
|
+
const residualBytes = bufferBytes - rawFrameBytes - 2;
|
|
599
|
+
const rawReservation = translatorBudget.reserveTransient(rawFrameBytes, { kind: "live_transient" });
|
|
600
|
+
let residualReservation: ReturnType<TranslatorBudget["reserveTransient"]> | undefined;
|
|
601
|
+
try {
|
|
602
|
+
residualReservation = translatorBudget.reserveTransient(residualBytes, { kind: "live_transient" });
|
|
603
|
+
const rawFrame = buffer.slice(0, sep);
|
|
604
|
+
const residual = buffer.slice(sep + 2);
|
|
605
|
+
rawReservation.commitRetained();
|
|
606
|
+
residualReservation.commitRetained();
|
|
607
|
+
residualReservation = undefined;
|
|
608
|
+
buffer = residual;
|
|
609
|
+
translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
610
|
+
bufferBytes = residualBytes;
|
|
611
|
+
try {
|
|
612
|
+
let eventName = "";
|
|
613
|
+
let dataLine = "";
|
|
614
|
+
let dataLineBytes = 0;
|
|
615
|
+
try {
|
|
616
|
+
let lineStart = 0;
|
|
617
|
+
while (lineStart <= rawFrame.length) {
|
|
618
|
+
const newline = rawFrame.indexOf("\n", lineStart);
|
|
619
|
+
const lineEnd = newline === -1 ? rawFrame.length : newline;
|
|
620
|
+
// The space after the colon is optional in text/event-stream (#1170);
|
|
621
|
+
// compute the value offset the same way sseFieldValue does, without
|
|
622
|
+
// slicing the line first — the byte accounting below is keyed to
|
|
623
|
+
// offsets into rawFrame.
|
|
624
|
+
const eventOffset = sseFieldOffset(rawFrame, lineStart, lineEnd, "event");
|
|
625
|
+
const dataOffset = sseFieldOffset(rawFrame, lineStart, lineEnd, "data");
|
|
626
|
+
if (eventOffset !== -1) {
|
|
627
|
+
eventName = rawFrame.slice(eventOffset, lineEnd).trim();
|
|
628
|
+
} else if (dataOffset !== -1) {
|
|
629
|
+
const fragmentStart = dataOffset;
|
|
630
|
+
const fragmentBytes = utf8SliceBytes(rawFrame, fragmentStart, lineEnd);
|
|
631
|
+
const fragmentReservation = translatorBudget.reserveTransient(fragmentBytes, { kind: "live_transient" });
|
|
632
|
+
let fragmentCommitted = false;
|
|
633
|
+
try {
|
|
634
|
+
const fragment = rawFrame.slice(fragmentStart, lineEnd);
|
|
635
|
+
fragmentReservation.commitRetained();
|
|
636
|
+
fragmentCommitted = true;
|
|
637
|
+
const nextDataLineBytes = appendedUtf8Bytes(dataLine, dataLineBytes, fragment);
|
|
638
|
+
const dataReservation = translatorBudget.reserveTransient(nextDataLineBytes, { kind: "live_transient" });
|
|
639
|
+
try {
|
|
640
|
+
dataLine += fragment;
|
|
641
|
+
dataReservation.commitRetained();
|
|
642
|
+
translatorBudget.releaseRetained(dataLineBytes, { kind: "live_transient" });
|
|
643
|
+
dataLineBytes = nextDataLineBytes;
|
|
644
|
+
} catch (error) {
|
|
645
|
+
dataReservation.release();
|
|
646
|
+
throw error;
|
|
647
|
+
}
|
|
648
|
+
} finally {
|
|
649
|
+
if (fragmentCommitted) {
|
|
650
|
+
translatorBudget.releaseRetained(fragmentBytes, { kind: "live_transient" });
|
|
651
|
+
} else fragmentReservation.release();
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
if (newline === -1) break;
|
|
655
|
+
lineStart = newline + 1;
|
|
656
|
+
}
|
|
657
|
+
if (!dataLine) continue;
|
|
658
|
+
let data: unknown;
|
|
659
|
+
try { data = JSON.parse(dataLine); } catch { continue; }
|
|
660
|
+
if (!isRec(data)) continue;
|
|
661
|
+
// Responses-compatible gateways may omit the optional SSE event field
|
|
662
|
+
// while retaining the event name in the JSON payload's required type.
|
|
663
|
+
const resolvedEventName = eventName || (typeof data.type === "string" ? data.type : "");
|
|
664
|
+
if (!resolvedEventName || terminated) continue;
|
|
665
|
+
handleFrame(resolvedEventName, data);
|
|
666
|
+
} finally {
|
|
667
|
+
translatorBudget.releaseRetained(dataLineBytes, { kind: "live_transient" });
|
|
668
|
+
}
|
|
669
|
+
} finally {
|
|
670
|
+
translatorBudget.releaseRetained(rawFrameBytes, { kind: "live_transient" });
|
|
671
|
+
}
|
|
672
|
+
} catch (error) {
|
|
673
|
+
rawReservation.release();
|
|
674
|
+
residualReservation?.release();
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
} finally {
|
|
679
|
+
if (decodedCommitted) translatorBudget.releaseRetained(value.byteLength, { kind: "live_transient" });
|
|
680
|
+
else decodedReservation.release();
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
// EOF without a terminal frame is a TRUNCATION, not success (devlog 100:
|
|
684
|
+
// gateways that close such streams politely hand Claude Code an empty/partial
|
|
685
|
+
// turn with no retryable error — CLIProxyAPI#2189 failure pattern). Fail closed
|
|
686
|
+
// with a mid-stream Anthropic error event so the client can retry.
|
|
687
|
+
if (!cancelled) fail(502, "upstream stream ended before a terminal frame (truncated response)", true);
|
|
688
|
+
} catch (err) {
|
|
689
|
+
if (isTranslatorBudgetExceededError(err)) {
|
|
690
|
+
try { await reader.cancel(err); } catch { /* already closed */ }
|
|
691
|
+
fail(413, "upstream translation buffer exceeded the safe limit", false, "translation_buffer_limit");
|
|
692
|
+
} else fail(500, err instanceof Error ? err.message : String(err));
|
|
693
|
+
} finally {
|
|
694
|
+
translatorBudget.releaseRetained(bufferBytes, { kind: "live_transient" });
|
|
695
|
+
if (pingTimer !== undefined) clearInterval(pingTimer);
|
|
696
|
+
reader.releaseLock();
|
|
697
|
+
if (!cancelled) controller.close();
|
|
698
|
+
}
|
|
699
|
+
})();
|
|
700
|
+
},
|
|
701
|
+
pull() {
|
|
702
|
+
releaseDeliveredFrame();
|
|
703
|
+
},
|
|
704
|
+
cancel(reason) {
|
|
705
|
+
cancelled = true;
|
|
706
|
+
while (queuedLiveFrameBytes.length > 0) releaseDeliveredFrame();
|
|
707
|
+
if (open?.callId) translatorBudget.closeCall(open.callId);
|
|
708
|
+
if (pingTimer !== undefined) clearInterval(pingTimer);
|
|
709
|
+
return reader?.cancel(reason);
|
|
710
|
+
},
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/** Non-streaming: /v1/responses JSON -> Anthropic message JSON. */
|
|
715
|
+
export function responsesJsonToAnthropicMessage(json: unknown, model: string): Rec {
|
|
716
|
+
const body = isRec(json) ? json : {};
|
|
717
|
+
const output = Array.isArray(body.output) ? body.output : [];
|
|
718
|
+
const content: Rec[] = [];
|
|
719
|
+
let sawToolUse = false;
|
|
720
|
+
let webSearchRequests = 0;
|
|
721
|
+
|
|
722
|
+
for (const raw of output) {
|
|
723
|
+
if (!isRec(raw)) continue;
|
|
724
|
+
switch (raw.type) {
|
|
725
|
+
case "message": {
|
|
726
|
+
if (!Array.isArray(raw.content)) break;
|
|
727
|
+
for (const part of raw.content) {
|
|
728
|
+
if (isRec(part) && part.type === "output_text" && typeof part.text === "string" && part.text.length > 0) {
|
|
729
|
+
content.push({ type: "text", text: part.text });
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
break;
|
|
733
|
+
}
|
|
734
|
+
case "reasoning": {
|
|
735
|
+
const parts: string[] = [];
|
|
736
|
+
if (Array.isArray(raw.summary)) {
|
|
737
|
+
for (const s of raw.summary) {
|
|
738
|
+
if (isRec(s) && typeof s.text === "string" && s.text.length > 0) parts.push(s.text);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (Array.isArray(raw.content)) {
|
|
742
|
+
for (const s of raw.content) {
|
|
743
|
+
if (isRec(s) && typeof s.text === "string" && s.text.length > 0) parts.push(s.text);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
if (parts.length > 0) {
|
|
747
|
+
content.push({ type: "thinking", thinking: parts.join("\n\n"), signature: `ocx${Date.now()}` });
|
|
748
|
+
}
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
case "function_call": {
|
|
752
|
+
sawToolUse = true;
|
|
753
|
+
let input: unknown = {};
|
|
754
|
+
if (typeof raw.arguments === "string" && raw.arguments.length > 0) {
|
|
755
|
+
try { input = JSON.parse(raw.arguments); } catch { input = {}; }
|
|
756
|
+
}
|
|
757
|
+
const name = typeof raw.name === "string" ? raw.name : "";
|
|
758
|
+
content.push({
|
|
759
|
+
type: "tool_use",
|
|
760
|
+
id: typeof raw.call_id === "string" ? raw.call_id : `toolu_${uuid()}`,
|
|
761
|
+
name,
|
|
762
|
+
input: isClaudeWebSearchToolName(name) ? sanitizeWebSearchInput(input) : input,
|
|
763
|
+
});
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
case "web_search_call": {
|
|
767
|
+
// Server-side search: emit the Anthropic pair. Does NOT set sawToolUse.
|
|
768
|
+
const pair = webSearchPairFromItem(raw);
|
|
769
|
+
content.push({ type: "server_tool_use", id: pair.id, name: "web_search", input: pair.input });
|
|
770
|
+
content.push({ type: "web_search_tool_result", tool_use_id: pair.id, content: pair.resultContent });
|
|
771
|
+
if (pair.completed) webSearchRequests++;
|
|
772
|
+
break;
|
|
773
|
+
}
|
|
774
|
+
default:
|
|
775
|
+
break;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
const details = isRec(body.incomplete_details) ? body.incomplete_details : {};
|
|
780
|
+
if (body.status === "incomplete"
|
|
781
|
+
&& details.reason !== "max_output_tokens"
|
|
782
|
+
&& details.reason !== "content_filter") {
|
|
783
|
+
const message = typeof details.message === "string" && details.message.trim()
|
|
784
|
+
? details.message
|
|
785
|
+
: `upstream response was incomplete${typeof details.reason === "string" ? ` (${details.reason})` : ""}`;
|
|
786
|
+
return anthropicErrorBody(529, message, "overloaded_error");
|
|
787
|
+
}
|
|
788
|
+
if (body.status === "completed" && body.end_turn === false && !sawToolUse) {
|
|
789
|
+
return anthropicErrorBody(529, "upstream turn ended without a final answer", "overloaded_error");
|
|
790
|
+
}
|
|
791
|
+
const stopReason = body.status === "incomplete" && details.reason === "max_output_tokens"
|
|
792
|
+
? "max_tokens"
|
|
793
|
+
: body.status === "incomplete" && details.reason === "content_filter"
|
|
794
|
+
? "refusal"
|
|
795
|
+
: sawToolUse ? "tool_use" : "end_turn";
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
id: `msg_${uuid()}`,
|
|
799
|
+
type: "message",
|
|
800
|
+
role: "assistant",
|
|
801
|
+
content,
|
|
802
|
+
model,
|
|
803
|
+
stop_reason: stopReason,
|
|
804
|
+
stop_sequence: null,
|
|
805
|
+
usage: anthropicUsage(body.usage, webSearchRequests),
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Fold an Anthropic SSE stream (our own emission vocabulary) into a message JSON.
|
|
811
|
+
* Used for non-streaming client requests: the internal replay always streams
|
|
812
|
+
* (routed adapters do not support non-stream turns), so the translated stream is
|
|
813
|
+
* aggregated here instead of translating a JSON body.
|
|
814
|
+
*/
|
|
815
|
+
export async function collectAnthropicMessage(
|
|
816
|
+
stream: ReadableStream<Uint8Array>,
|
|
817
|
+
model: string,
|
|
818
|
+
translatorBudget: TranslatorBudget,
|
|
819
|
+
): Promise<Rec> {
|
|
820
|
+
const decoder = new TextDecoder();
|
|
821
|
+
const reader = stream.getReader();
|
|
822
|
+
let buffer = "";
|
|
823
|
+
const content: Rec[] = [];
|
|
824
|
+
let openBlock: Rec | null = null;
|
|
825
|
+
let toolJson = "";
|
|
826
|
+
let stopReason: string | null = "end_turn";
|
|
827
|
+
let usage: Rec = anthropicUsage(undefined);
|
|
828
|
+
let error: Rec | null = null;
|
|
829
|
+
const replaceRetained = (previous: string, next: string, kind: "live_transient" | "retained_collectors") => {
|
|
830
|
+
const reservation = translatorBudget.reserveTransient(Buffer.byteLength(next), { kind });
|
|
831
|
+
reservation.commitRetained();
|
|
832
|
+
translatorBudget.releaseRetained(Buffer.byteLength(previous), { kind });
|
|
833
|
+
return next;
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const closeBlock = () => {
|
|
837
|
+
if (!openBlock) return;
|
|
838
|
+
// server_tool_use streams its query via input_json_delta exactly like tool_use (audit F3).
|
|
839
|
+
if (openBlock.type === "tool_use" || openBlock.type === "server_tool_use") {
|
|
840
|
+
try { openBlock.input = toolJson.length > 0 ? JSON.parse(toolJson) : {}; } catch { openBlock.input = {}; }
|
|
841
|
+
}
|
|
842
|
+
translatorBudget.chargeRetained(Buffer.byteLength(JSON.stringify(openBlock)), { kind: "retained_collectors" });
|
|
843
|
+
content.push(openBlock);
|
|
844
|
+
openBlock = null;
|
|
845
|
+
toolJson = "";
|
|
846
|
+
};
|
|
847
|
+
|
|
848
|
+
const handle = (name: string, data: Rec) => {
|
|
849
|
+
switch (name) {
|
|
850
|
+
case "content_block_start":
|
|
851
|
+
closeBlock();
|
|
852
|
+
if (isRec(data.content_block)) openBlock = { ...data.content_block };
|
|
853
|
+
break;
|
|
854
|
+
case "content_block_delta": {
|
|
855
|
+
const delta = isRec(data.delta) ? data.delta : {};
|
|
856
|
+
if (!openBlock) break;
|
|
857
|
+
if (delta.type === "text_delta" && typeof delta.text === "string") {
|
|
858
|
+
const previous = typeof openBlock.text === "string" ? openBlock.text : "";
|
|
859
|
+
openBlock.text = replaceRetained(previous, previous + delta.text, "retained_collectors");
|
|
860
|
+
} else if (delta.type === "thinking_delta" && typeof delta.thinking === "string") {
|
|
861
|
+
const previous = typeof openBlock.thinking === "string" ? openBlock.thinking : "";
|
|
862
|
+
openBlock.thinking = replaceRetained(previous, previous + delta.thinking, "retained_collectors");
|
|
863
|
+
} else if (delta.type === "signature_delta" && typeof delta.signature === "string") {
|
|
864
|
+
openBlock.signature = delta.signature;
|
|
865
|
+
} else if (delta.type === "input_json_delta" && typeof delta.partial_json === "string") {
|
|
866
|
+
toolJson = replaceRetained(toolJson, toolJson + delta.partial_json, "retained_collectors");
|
|
867
|
+
}
|
|
868
|
+
break;
|
|
869
|
+
}
|
|
870
|
+
case "content_block_stop":
|
|
871
|
+
closeBlock();
|
|
872
|
+
break;
|
|
873
|
+
case "message_delta": {
|
|
874
|
+
const delta = isRec(data.delta) ? data.delta : {};
|
|
875
|
+
if (typeof delta.stop_reason === "string") stopReason = delta.stop_reason;
|
|
876
|
+
if (isRec(data.usage)) usage = data.usage;
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
case "error":
|
|
880
|
+
error = data;
|
|
881
|
+
break;
|
|
882
|
+
default:
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
885
|
+
};
|
|
886
|
+
|
|
887
|
+
try {
|
|
888
|
+
for (;;) {
|
|
889
|
+
const { done, value } = await reader.read();
|
|
890
|
+
if (done) break;
|
|
891
|
+
buffer = replaceRetained(buffer, buffer + decoder.decode(value, { stream: true }), "live_transient");
|
|
892
|
+
let sep: number;
|
|
893
|
+
while ((sep = buffer.indexOf("\n\n")) !== -1) {
|
|
894
|
+
const rawFrame = buffer.slice(0, sep);
|
|
895
|
+
buffer = replaceRetained(buffer, buffer.slice(sep + 2), "live_transient");
|
|
896
|
+
let eventName = "";
|
|
897
|
+
let dataLine = "";
|
|
898
|
+
for (const line of rawFrame.split("\n")) {
|
|
899
|
+
const eventValue = sseFieldValue(line, "event");
|
|
900
|
+
if (eventValue !== null) { eventName = eventValue.trim(); continue; }
|
|
901
|
+
const dataValue = sseFieldValue(line, "data");
|
|
902
|
+
if (dataValue !== null) dataLine += dataValue;
|
|
903
|
+
}
|
|
904
|
+
if (!eventName || !dataLine) continue;
|
|
905
|
+
let data: unknown;
|
|
906
|
+
try { data = JSON.parse(dataLine); } catch { continue; }
|
|
907
|
+
if (isRec(data)) handle(eventName, data);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
} finally {
|
|
911
|
+
reader.releaseLock();
|
|
912
|
+
}
|
|
913
|
+
closeBlock();
|
|
914
|
+
|
|
915
|
+
if (error) return error;
|
|
916
|
+
return {
|
|
917
|
+
id: `msg_${uuid()}`,
|
|
918
|
+
type: "message",
|
|
919
|
+
role: "assistant",
|
|
920
|
+
content,
|
|
921
|
+
model,
|
|
922
|
+
stop_reason: stopReason,
|
|
923
|
+
stop_sequence: null,
|
|
924
|
+
usage,
|
|
925
|
+
};
|
|
926
|
+
}
|