@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,315 @@
|
|
|
1
|
+
import { sseFieldValue } from "../lib/sse-decoder";
|
|
2
|
+
import {
|
|
3
|
+
appendSafeWebSearchSource,
|
|
4
|
+
safeWebSearchSources,
|
|
5
|
+
type SafeWebSearchSource,
|
|
6
|
+
} from "./sources";
|
|
7
|
+
|
|
8
|
+
/** A single web source backing the sidecar's answer. */
|
|
9
|
+
export type WebSearchSource = SafeWebSearchSource;
|
|
10
|
+
|
|
11
|
+
/** The sidecar's synthesized answer plus its sources (empty `sources` is fine). */
|
|
12
|
+
export interface WebSearchResult {
|
|
13
|
+
text: string;
|
|
14
|
+
sources: WebSearchSource[];
|
|
15
|
+
/** Set only when the stream surfaced an error AND produced no usable answer text. */
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AnnotationLike {
|
|
20
|
+
type?: string;
|
|
21
|
+
url?: string;
|
|
22
|
+
title?: string;
|
|
23
|
+
}
|
|
24
|
+
interface OutputTextBlock {
|
|
25
|
+
type?: string;
|
|
26
|
+
text?: string;
|
|
27
|
+
annotations?: AnnotationLike[];
|
|
28
|
+
}
|
|
29
|
+
interface OutputItem {
|
|
30
|
+
type?: string;
|
|
31
|
+
content?: OutputTextBlock[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ChatGPT's Codex backend does not accept `max_output_tokens` on sidecar requests. Bound the raw
|
|
35
|
+
// streamed response here, before decoded text and authoritative/delta copies can accumulate.
|
|
36
|
+
export const MAX_SIDECAR_RESPONSE_BYTES = 64 * 1024;
|
|
37
|
+
|
|
38
|
+
/** Push a `url_citation` annotation as a source, de-duplicated by URL. */
|
|
39
|
+
function collectAnnotation(ann: AnnotationLike | undefined, sources: WebSearchSource[], seen: Set<string>): void {
|
|
40
|
+
if (!ann || ann.type !== "url_citation" || typeof ann.url !== "string" || seen.has(ann.url)) return;
|
|
41
|
+
if (appendSafeWebSearchSource(sources, {
|
|
42
|
+
url: ann.url,
|
|
43
|
+
...(ann.title !== undefined ? { title: ann.title } : {}),
|
|
44
|
+
})) seen.add(ann.url);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Hosted web_search (gpt-mini) rarely emits structured `url_citation` annotations; instead it ends
|
|
49
|
+
* its answer with a markdown `Sources:` section. Extract sources from that TRAILING section only
|
|
50
|
+
* (a whole-body URL scan would false-positive on URLs the model merely mentions), and return the
|
|
51
|
+
* answer text with that section stripped so the tool_result renderer doesn't double-print sources.
|
|
52
|
+
*
|
|
53
|
+
* Handles the per-line forms seen from the backend: `- title: url`, `- title (url)`,
|
|
54
|
+
* `- [title](url)`, `- <url>`, `- url`, numbered `1. ...` variants, a markdown-prefixed header
|
|
55
|
+
* (`### Sources:`, `**Sources**`), a title line whose URL sits on the FOLLOWING line, and trailing
|
|
56
|
+
* URL punctuation (`;`, `,`, `)`, `]`, `.`). Prose that follows the source list is preserved.
|
|
57
|
+
*/
|
|
58
|
+
const URL_RE = /https?:\/\/[^\s<>()\[\]]+/i;
|
|
59
|
+
// Recognize URI-like candidates separately from the HTTP(S)-only acceptance boundary. A rejected
|
|
60
|
+
// citation (for example `javascript:`) still belongs to the trailing Sources block and must not be
|
|
61
|
+
// left behind as ordinary assistant text.
|
|
62
|
+
const URI_LIKE_RE = /[a-z][a-z0-9+.-]*:[^\s<>()\[\]]+/i;
|
|
63
|
+
// A "Sources:" / "Source:" header, allowing markdown prefixes (#, *, -, >) and bold/italic wrappers.
|
|
64
|
+
const SOURCES_WORD_RE = /^sources?/i;
|
|
65
|
+
|
|
66
|
+
/** Match the legacy Sources-header grammar without overlapping regex quantifiers. */
|
|
67
|
+
function isSourcesHeader(line: string): boolean {
|
|
68
|
+
let cursor = 0;
|
|
69
|
+
/** Match one code unit using JavaScript's existing `\s` semantics. */
|
|
70
|
+
const isWhitespace = (char: string | undefined): boolean => char !== undefined && /\s/u.test(char);
|
|
71
|
+
/** Advance over the current contiguous whitespace run. */
|
|
72
|
+
const skipWhitespace = (): void => {
|
|
73
|
+
while (isWhitespace(line[cursor])) cursor += 1;
|
|
74
|
+
};
|
|
75
|
+
/** Advance over the current contiguous Markdown-star run. */
|
|
76
|
+
const skipStars = (): void => {
|
|
77
|
+
while (line[cursor] === "*") cursor += 1;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
skipWhitespace();
|
|
81
|
+
if (line[cursor] === "#") {
|
|
82
|
+
const start = cursor;
|
|
83
|
+
while (line[cursor] === "#") cursor += 1;
|
|
84
|
+
if (cursor - start > 6) return false;
|
|
85
|
+
skipWhitespace();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
while (isWhitespace(line[cursor]) || line[cursor] === "-" || line[cursor] === "*" || line[cursor] === ">") {
|
|
89
|
+
cursor += 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const word = SOURCES_WORD_RE.exec(line.slice(cursor, cursor + 7));
|
|
93
|
+
if (!word) return false;
|
|
94
|
+
cursor += word[0].length;
|
|
95
|
+
|
|
96
|
+
// Preserve `\s*\**\s*:?\s*\**\s*$`: at most two star runs, with the optional
|
|
97
|
+
// colon between them. Either run may be empty, so `Sources:*` is one trailing run; three
|
|
98
|
+
// separated runs and a colon after the second run remain invalid.
|
|
99
|
+
skipWhitespace();
|
|
100
|
+
skipStars();
|
|
101
|
+
skipWhitespace();
|
|
102
|
+
if (line[cursor] === ":") {
|
|
103
|
+
cursor += 1;
|
|
104
|
+
skipWhitespace();
|
|
105
|
+
}
|
|
106
|
+
skipStars();
|
|
107
|
+
skipWhitespace();
|
|
108
|
+
return cursor === line.length;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Trim wrapping/trailing noise from a captured URL: angle brackets, then trailing punctuation. */
|
|
112
|
+
function cleanUrl(url: string): string {
|
|
113
|
+
return url.replace(/^<+/, "").replace(/[)>\].,;:]+$/, "");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Derive a human title from the list-item text preceding the URL (strip markers, md link, seps). */
|
|
117
|
+
function cleanTitle(prefix: string): string {
|
|
118
|
+
let title = prefix.replace(/^[-*>\d.)\s]+/, "").trim();
|
|
119
|
+
// `[title](` from a markdown link, or a leading `[`.
|
|
120
|
+
title = title.replace(/^\[/, "").replace(/\]\(?$/, "").replace(/[:\-—(<]\s*$/, "").trim();
|
|
121
|
+
return title;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function extractTrailingSources(text: string): { text: string; sources: WebSearchSource[]; stripped: boolean } {
|
|
125
|
+
const lines = text.split("\n");
|
|
126
|
+
// Find the LAST line that is a "Sources:" header (markdown prefixes allowed).
|
|
127
|
+
let headerIdx = -1;
|
|
128
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
129
|
+
if (isSourcesHeader(lines[i])) { headerIdx = i; break; }
|
|
130
|
+
}
|
|
131
|
+
if (headerIdx === -1) return { text, sources: [], stripped: false };
|
|
132
|
+
const sources: WebSearchSource[] = [];
|
|
133
|
+
const seen = new Set<string>();
|
|
134
|
+
// Track the last line index actually consumed as part of the source list so trailing prose after
|
|
135
|
+
// the list survives (we strip the header through the last consumed source line, not to EOF).
|
|
136
|
+
let lastConsumed = headerIdx;
|
|
137
|
+
// A title line whose URL is expected on a following line (multiline entry).
|
|
138
|
+
let pendingTitle: string | null = null;
|
|
139
|
+
let consumedSourceLine = false;
|
|
140
|
+
for (let i = headerIdx + 1; i < lines.length; i++) {
|
|
141
|
+
const raw = lines[i].trim();
|
|
142
|
+
if (raw === "") {
|
|
143
|
+
// Blank line between header and first entry is fine; a blank AFTER entries ends the list.
|
|
144
|
+
if (consumedSourceLine || pendingTitle !== null) break;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const httpMatch = raw.match(URL_RE);
|
|
148
|
+
const m = httpMatch ?? raw.match(URI_LIKE_RE);
|
|
149
|
+
if (!m) {
|
|
150
|
+
// A list-ish line with no URL may be a title whose URL is on the next line. Only treat it as a
|
|
151
|
+
// pending title when it looks like a list item; otherwise it's prose → stop.
|
|
152
|
+
if (/^[-*>\d.)]/.test(raw) || pendingTitle === null) {
|
|
153
|
+
if (/^[-*>\d.)]/.test(raw)) { pendingTitle = raw; lastConsumed = i; continue; }
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
// A non-HTTP URI embedded in prose is not sufficient to classify the line as a citation.
|
|
158
|
+
// Accept it as a consumed source line only when it is a list item or the whole line starts with
|
|
159
|
+
// the URI candidate, matching the existing bare-URL grammar.
|
|
160
|
+
if (!httpMatch && !/^[-*>\d.)]/.test(raw) && m.index !== 0) break;
|
|
161
|
+
const url = cleanUrl(m[0]);
|
|
162
|
+
if (!url) { break; }
|
|
163
|
+
consumedSourceLine = true;
|
|
164
|
+
lastConsumed = i;
|
|
165
|
+
// Title: text before the URL on this line, else a buffered title from a preceding line.
|
|
166
|
+
const inlinePrefix = raw.slice(0, m.index);
|
|
167
|
+
const title = cleanTitle(inlinePrefix) || (pendingTitle ? cleanTitle(pendingTitle) : "");
|
|
168
|
+
pendingTitle = null;
|
|
169
|
+
if (seen.has(url)) continue;
|
|
170
|
+
if (appendSafeWebSearchSource(sources, title ? { url, title } : { url })) seen.add(url);
|
|
171
|
+
}
|
|
172
|
+
if (!consumedSourceLine) return { text, sources: [], stripped: false };
|
|
173
|
+
// Keep text before the header AND any prose after the consumed source lines.
|
|
174
|
+
const before = lines.slice(0, headerIdx).join("\n").replace(/\s+$/, "");
|
|
175
|
+
const after = lines.slice(lastConsumed + 1).join("\n").replace(/^\s+/, "");
|
|
176
|
+
const body = after ? (before ? `${before}\n\n${after}` : after) : before;
|
|
177
|
+
return { text: body, sources, stripped: true };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Pull final text + url_citation sources from a completed Responses `output[]` array. */
|
|
181
|
+
function fromOutputArray(output: OutputItem[], seen: Set<string>): WebSearchResult {
|
|
182
|
+
let text = "";
|
|
183
|
+
const sources: WebSearchSource[] = [];
|
|
184
|
+
for (const item of output) {
|
|
185
|
+
if (item.type !== "message" || !Array.isArray(item.content)) continue;
|
|
186
|
+
for (const block of item.content) {
|
|
187
|
+
if (block.type === "output_text" && typeof block.text === "string") {
|
|
188
|
+
text += block.text;
|
|
189
|
+
for (const ann of block.annotations ?? []) collectAnnotation(ann, sources, seen);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return { text, sources };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function cancelReaderWithoutWaiting(
|
|
197
|
+
reader: ReadableStreamDefaultReader<Uint8Array>,
|
|
198
|
+
reason: string,
|
|
199
|
+
): void {
|
|
200
|
+
try {
|
|
201
|
+
void reader.cancel(reason).catch(() => undefined);
|
|
202
|
+
} catch { /* best-effort body teardown */ }
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Parse the sidecar's streamed Responses SSE into a final answer + sources. Tolerant of the full set of
|
|
207
|
+
* Responses streaming events: prefers the authoritative `response.completed` output[], then the
|
|
208
|
+
* `response.output_text.done` text; falls back to accumulated `response.output_text.delta`. Sources are
|
|
209
|
+
* collected from EVERY shape they arrive in — `response.output_text.annotation.added` events (the
|
|
210
|
+
* streaming path, which earlier testing missed → empty citations), `done`-block `annotations[]`, and
|
|
211
|
+
* the final output[]. `response.failed`/`error` events surface as `error` when no answer text was produced.
|
|
212
|
+
*/
|
|
213
|
+
export async function parseSidecarSSE(response: Response): Promise<WebSearchResult> {
|
|
214
|
+
if (!response.body) return { text: "", sources: [] };
|
|
215
|
+
const reader = response.body.getReader();
|
|
216
|
+
const decoder = new TextDecoder();
|
|
217
|
+
let buffer = "";
|
|
218
|
+
let responseBytes = 0;
|
|
219
|
+
const seen = new Set<string>();
|
|
220
|
+
// Holder object — fields are mutated inside the closure, so they can't live as narrowed locals.
|
|
221
|
+
const acc: {
|
|
222
|
+
deltaText: string;
|
|
223
|
+
doneText: string;
|
|
224
|
+
final: WebSearchResult | null;
|
|
225
|
+
streamSources: WebSearchSource[];
|
|
226
|
+
error: string | null;
|
|
227
|
+
} = { deltaText: "", doneText: "", final: null, streamSources: [], error: null };
|
|
228
|
+
|
|
229
|
+
const handle = (payload: string): void => {
|
|
230
|
+
if (!payload || payload === "[DONE]") return;
|
|
231
|
+
// Neither warning below copies the frame's content. An upstream SSE payload can carry model
|
|
232
|
+
// output or credential material, and a malformed frame is exactly the case where the content
|
|
233
|
+
// is least trustworthy. Length plus a classification separates the two failure modes in a log
|
|
234
|
+
// without reproducing anything from the wire.
|
|
235
|
+
let parsed: unknown;
|
|
236
|
+
try { parsed = JSON.parse(payload); } catch {
|
|
237
|
+
console.warn(`[web-search-parse] malformed SSE JSON (${payload.length} chars)`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
// `JSON.parse("null")` returns null rather than throwing, so the catch above cannot cover it
|
|
241
|
+
// and the `data.type` read below threw out of parseSidecarSSE.
|
|
242
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
243
|
+
const shape = Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed;
|
|
244
|
+
console.warn(`[web-search-parse] non-record SSE JSON frame (${payload.length} chars, ${shape})`);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
const data = parsed as Record<string, unknown>;
|
|
248
|
+
const type = data.type as string | undefined;
|
|
249
|
+
if (type === "response.output_text.delta" && typeof data.delta === "string") {
|
|
250
|
+
acc.deltaText += data.delta;
|
|
251
|
+
} else if (type === "response.output_text.done" && typeof data.text === "string") {
|
|
252
|
+
// The `done` event carries the full, authoritative text for one content part.
|
|
253
|
+
acc.doneText += data.text;
|
|
254
|
+
} else if (type === "response.completed" || type === "response.done") {
|
|
255
|
+
const resp = data.response as { output?: OutputItem[] } | undefined;
|
|
256
|
+
if (resp?.output) acc.final = fromOutputArray(resp.output, seen);
|
|
257
|
+
} else if (type === "response.failed" || type === "response.incomplete" || type === "error") {
|
|
258
|
+
const resp = data.response as { error?: { message?: string } } | undefined;
|
|
259
|
+
const msg = resp?.error?.message
|
|
260
|
+
?? (data.error as { message?: string } | undefined)?.message
|
|
261
|
+
?? (typeof data.message === "string" ? data.message : undefined);
|
|
262
|
+
if (msg) acc.error = msg;
|
|
263
|
+
}
|
|
264
|
+
// Citations stream as a dedicated `response.output_text.annotation.added` event (singular
|
|
265
|
+
// `annotation`); capture it regardless of the exact event name so they aren't lost.
|
|
266
|
+
if (data.annotation) collectAnnotation(data.annotation as AnnotationLike, acc.streamSources, seen);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
try {
|
|
270
|
+
while (true) {
|
|
271
|
+
const { done, value } = await reader.read();
|
|
272
|
+
if (done) break;
|
|
273
|
+
const remaining = MAX_SIDECAR_RESPONSE_BYTES - responseBytes;
|
|
274
|
+
const accepted = value.byteLength <= remaining ? value : value.subarray(0, remaining);
|
|
275
|
+
responseBytes += accepted.byteLength;
|
|
276
|
+
buffer += decoder.decode(accepted, { stream: true });
|
|
277
|
+
const lines = buffer.split("\n");
|
|
278
|
+
buffer = lines.pop() ?? "";
|
|
279
|
+
for (const line of lines) {
|
|
280
|
+
const data = sseFieldValue(line, "data");
|
|
281
|
+
if (data !== null) handle(data.trim());
|
|
282
|
+
}
|
|
283
|
+
if (responseBytes >= MAX_SIDECAR_RESPONSE_BYTES) {
|
|
284
|
+
// Preserve complete events accepted up to the cap, but discard any unterminated line and
|
|
285
|
+
// TextDecoder carry. Do not let a rejecting/hung cancel turn bounded partial output into
|
|
286
|
+
// an error or keep this parser waiting on upstream teardown.
|
|
287
|
+
cancelReaderWithoutWaiting(reader, "sidecar response byte limit reached");
|
|
288
|
+
break;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} finally {
|
|
292
|
+
reader.releaseLock();
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Prefer the authoritative completed output[], then the done text, then accumulated deltas.
|
|
296
|
+
const text = (acc.final?.text.trim() ? acc.final.text : "")
|
|
297
|
+
|| acc.doneText.trim() && acc.doneText
|
|
298
|
+
|| acc.deltaText;
|
|
299
|
+
// Merge sources from the final output[] and the streaming annotation events.
|
|
300
|
+
const sources = safeWebSearchSources(acc.final?.sources ?? []);
|
|
301
|
+
for (const s of acc.streamSources) {
|
|
302
|
+
appendSafeWebSearchSource(sources, s);
|
|
303
|
+
}
|
|
304
|
+
// Hosted web_search usually omits url_citation annotations and lists sources in a trailing
|
|
305
|
+
// `Sources:` markdown block instead. Pull those out (and strip the block from the answer so the
|
|
306
|
+
// tool_result renderer doesn't print sources twice). Annotation titles win; text-block titles
|
|
307
|
+
// only fill a gap. URL-deduped against annotation sources.
|
|
308
|
+
const { text: body, sources: textSources, stripped } = extractTrailingSources(typeof text === "string" ? text : "");
|
|
309
|
+
for (const s of textSources) {
|
|
310
|
+
appendSafeWebSearchSource(sources, s);
|
|
311
|
+
}
|
|
312
|
+
const finalText = stripped ? body : (typeof text === "string" ? text : "");
|
|
313
|
+
if (!finalText.trim() && acc.error) return { text: "", sources, error: acc.error };
|
|
314
|
+
return { text: finalText, sources };
|
|
315
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import type { ProviderAdapter } from "../adapters/base";
|
|
2
|
+
import type { AdapterEvent } from "../types";
|
|
3
|
+
import {
|
|
4
|
+
isTranslatorBudgetExceededError,
|
|
5
|
+
TRANSLATOR_MAX_TURN_BYTES,
|
|
6
|
+
TranslatorBudgetExceededError,
|
|
7
|
+
type TranslatorBudget,
|
|
8
|
+
} from "../lib/translator-budget";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_POST_TERMINAL_DRAIN_TIMEOUT_MS = 5_000;
|
|
11
|
+
|
|
12
|
+
export class RoutedModelInactivityError extends Error {
|
|
13
|
+
readonly timeoutMs: number;
|
|
14
|
+
|
|
15
|
+
constructor(timeoutMs: number) {
|
|
16
|
+
super(`Routed model generation timeout after ${timeoutMs}ms without response bytes during web-search`);
|
|
17
|
+
this.name = "RoutedModelInactivityError";
|
|
18
|
+
this.timeoutMs = timeoutMs;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class WebSearchStreamProtocolError extends Error {
|
|
23
|
+
constructor(message: string) {
|
|
24
|
+
super(`Web-search adapter stream protocol error: ${message}`);
|
|
25
|
+
this.name = "WebSearchStreamProtocolError";
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ParseStreamWithProgressOptions {
|
|
30
|
+
inactivityTimeoutMs: number;
|
|
31
|
+
translatorBudget: TranslatorBudget;
|
|
32
|
+
signal?: AbortSignal;
|
|
33
|
+
/** Kept configurable for focused tests; production callers should use the 5 second default. */
|
|
34
|
+
postTerminalDrainTimeoutMs?: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type ParseStream = ProviderAdapter["parseStream"];
|
|
38
|
+
type Resolver<T> = (value: T | PromiseLike<T>) => void;
|
|
39
|
+
|
|
40
|
+
interface PendingDelivery {
|
|
41
|
+
event: AdapterEvent;
|
|
42
|
+
resolve: () => void;
|
|
43
|
+
reject: (reason: unknown) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Capacity-one handoff. Semantic events are lossless and acknowledged when received; raw-byte
|
|
48
|
+
* progress is only a coalesced liveness bit and never displaces a semantic event.
|
|
49
|
+
*/
|
|
50
|
+
class ProgressHandoff {
|
|
51
|
+
private semantic: PendingDelivery | undefined;
|
|
52
|
+
private progress = false;
|
|
53
|
+
private receiver: { resolve: Resolver<IteratorResult<AdapterEvent>>; reject: (reason: unknown) => void } | undefined;
|
|
54
|
+
private ended = false;
|
|
55
|
+
private failed = false;
|
|
56
|
+
private failure: unknown | undefined;
|
|
57
|
+
|
|
58
|
+
async deliver(event: AdapterEvent): Promise<void> {
|
|
59
|
+
if (this.failed) throw this.failure;
|
|
60
|
+
if (this.ended) throw new WebSearchStreamProtocolError("event delivered after collector closed");
|
|
61
|
+
|
|
62
|
+
if (this.receiver) {
|
|
63
|
+
const receiver = this.receiver;
|
|
64
|
+
this.receiver = undefined;
|
|
65
|
+
receiver.resolve({ done: false, value: event });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await new Promise<void>((resolve, reject) => {
|
|
70
|
+
this.semantic = { event, resolve, reject };
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
offerProgress(): void {
|
|
75
|
+
if (this.ended || this.failed) return;
|
|
76
|
+
if (this.receiver && !this.semantic) {
|
|
77
|
+
const receiver = this.receiver;
|
|
78
|
+
this.receiver = undefined;
|
|
79
|
+
receiver.resolve({ done: false, value: { type: "heartbeat" } });
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
this.progress = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
receive(): Promise<IteratorResult<AdapterEvent>> {
|
|
86
|
+
if (this.failed) return Promise.reject(this.failure);
|
|
87
|
+
if (this.semantic) {
|
|
88
|
+
const delivery = this.semantic;
|
|
89
|
+
this.semantic = undefined;
|
|
90
|
+
delivery.resolve();
|
|
91
|
+
return Promise.resolve({ done: false, value: delivery.event });
|
|
92
|
+
}
|
|
93
|
+
if (this.progress) {
|
|
94
|
+
this.progress = false;
|
|
95
|
+
return Promise.resolve({ done: false, value: { type: "heartbeat" } });
|
|
96
|
+
}
|
|
97
|
+
if (this.ended) return Promise.resolve({ done: true, value: undefined });
|
|
98
|
+
|
|
99
|
+
return new Promise<IteratorResult<AdapterEvent>>((resolve, reject) => {
|
|
100
|
+
this.receiver = { resolve, reject };
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fail(reason: unknown): void {
|
|
105
|
+
if (this.ended || this.failed) return;
|
|
106
|
+
this.failed = true;
|
|
107
|
+
this.failure = reason;
|
|
108
|
+
this.progress = false;
|
|
109
|
+
this.semantic?.reject(reason);
|
|
110
|
+
this.semantic = undefined;
|
|
111
|
+
this.receiver?.reject(reason);
|
|
112
|
+
this.receiver = undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
close(): void {
|
|
116
|
+
if (this.ended || this.failed) return;
|
|
117
|
+
this.ended = true;
|
|
118
|
+
this.progress = false;
|
|
119
|
+
this.semantic?.reject(new WebSearchStreamProtocolError("collector closed with a pending event"));
|
|
120
|
+
this.semantic = undefined;
|
|
121
|
+
this.receiver?.resolve({ done: true, value: undefined });
|
|
122
|
+
this.receiver = undefined;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeTimeout(value: number, label: string): number {
|
|
127
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
128
|
+
throw new RangeError(`${label} must be a positive finite number`);
|
|
129
|
+
}
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Parse an adapter stream while exposing invisible raw-byte liveness heartbeats.
|
|
135
|
+
*
|
|
136
|
+
* The original body is never cloned or tee'd. This function owns its sole reader and supplies the
|
|
137
|
+
* adapter with a demand-driven, highWaterMark-zero response body.
|
|
138
|
+
*/
|
|
139
|
+
export async function* parseStreamWithProgress(
|
|
140
|
+
response: Response,
|
|
141
|
+
parseStream: ParseStream,
|
|
142
|
+
options: ParseStreamWithProgressOptions,
|
|
143
|
+
): AsyncGenerator<AdapterEvent> {
|
|
144
|
+
const inactivityTimeoutMs = normalizeTimeout(options.inactivityTimeoutMs, "inactivityTimeoutMs");
|
|
145
|
+
const postTerminalDrainTimeoutMs = normalizeTimeout(
|
|
146
|
+
options.postTerminalDrainTimeoutMs ?? DEFAULT_POST_TERMINAL_DRAIN_TIMEOUT_MS,
|
|
147
|
+
"postTerminalDrainTimeoutMs",
|
|
148
|
+
);
|
|
149
|
+
const handoff = new ProgressHandoff();
|
|
150
|
+
const reader = response.body?.getReader();
|
|
151
|
+
if (!reader) throw new WebSearchStreamProtocolError("upstream response has no body");
|
|
152
|
+
|
|
153
|
+
let inactivityTimer: ReturnType<typeof setTimeout> | undefined;
|
|
154
|
+
let tappedController: ReadableStreamDefaultController<Uint8Array> | undefined;
|
|
155
|
+
let settled = false;
|
|
156
|
+
let iterator: AsyncGenerator<AdapterEvent> | undefined;
|
|
157
|
+
let iteratorReturnStarted = false;
|
|
158
|
+
let detachAbort = (): void => {};
|
|
159
|
+
let stopSignalled = false;
|
|
160
|
+
let signalStop!: (reason: unknown) => void;
|
|
161
|
+
const stopped = new Promise<unknown>(resolve => { signalStop = resolve; });
|
|
162
|
+
|
|
163
|
+
const clearInactivity = (): void => {
|
|
164
|
+
if (inactivityTimer !== undefined) clearTimeout(inactivityTimer);
|
|
165
|
+
inactivityTimer = undefined;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const cancelReader = (reason: unknown): void => {
|
|
169
|
+
try {
|
|
170
|
+
void reader.cancel(reason).catch(() => {});
|
|
171
|
+
} catch { /* reader cancellation is best-effort and must never replace the foreground outcome */ }
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const closeIterator = (): void => {
|
|
175
|
+
if (!iterator || iteratorReturnStarted) return;
|
|
176
|
+
iteratorReturnStarted = true;
|
|
177
|
+
try {
|
|
178
|
+
const returned = iterator.return(undefined);
|
|
179
|
+
void returned.catch(() => {});
|
|
180
|
+
} catch { /* iterator return is best-effort and must never delay foreground failure */ }
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const stop = (reason: unknown): void => {
|
|
184
|
+
if (stopSignalled) return;
|
|
185
|
+
stopSignalled = true;
|
|
186
|
+
signalStop(reason);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const fail = (reason: unknown): void => {
|
|
190
|
+
// A second failure may arrive from the parser after reader cancellation. Iterator cleanup is
|
|
191
|
+
// still required even when the foreground failure has already won the settlement race.
|
|
192
|
+
closeIterator();
|
|
193
|
+
if (settled) return;
|
|
194
|
+
settled = true;
|
|
195
|
+
clearInactivity();
|
|
196
|
+
detachAbort();
|
|
197
|
+
stop(reason);
|
|
198
|
+
handoff.fail(reason);
|
|
199
|
+
try { tappedController?.error(reason); } catch { /* already closed/cancelled */ }
|
|
200
|
+
cancelReader(reason);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const resetInactivity = (): void => {
|
|
204
|
+
if (settled) return;
|
|
205
|
+
clearInactivity();
|
|
206
|
+
inactivityTimer = setTimeout(() => fail(new RoutedModelInactivityError(inactivityTimeoutMs)), inactivityTimeoutMs);
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const onAbort = (): void => fail(options.signal?.reason);
|
|
210
|
+
detachAbort = (): void => options.signal?.removeEventListener("abort", onAbort);
|
|
211
|
+
if (options.signal?.aborted) fail(options.signal.reason);
|
|
212
|
+
else options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
213
|
+
|
|
214
|
+
const tappedBody = new ReadableStream<Uint8Array>({
|
|
215
|
+
start(controller) {
|
|
216
|
+
tappedController = controller;
|
|
217
|
+
},
|
|
218
|
+
async pull(controller) {
|
|
219
|
+
try {
|
|
220
|
+
while (!settled) {
|
|
221
|
+
const result = await reader.read();
|
|
222
|
+
if (result.done) {
|
|
223
|
+
controller.close();
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (result.value.byteLength === 0) continue;
|
|
227
|
+
resetInactivity();
|
|
228
|
+
handoff.offerProgress();
|
|
229
|
+
controller.enqueue(result.value);
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
} catch (error) {
|
|
233
|
+
fail(error);
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
cancel(reason) {
|
|
237
|
+
cancelReader(reason);
|
|
238
|
+
closeIterator();
|
|
239
|
+
},
|
|
240
|
+
}, { highWaterMark: 0 });
|
|
241
|
+
|
|
242
|
+
const tappedResponse = new Response(tappedBody, {
|
|
243
|
+
status: response.status,
|
|
244
|
+
statusText: response.statusText,
|
|
245
|
+
headers: response.headers,
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
resetInactivity();
|
|
249
|
+
|
|
250
|
+
const parserPump = (async (): Promise<void> => {
|
|
251
|
+
let heldTerminal: Extract<AdapterEvent, { type: "done" | "incomplete" }> | undefined;
|
|
252
|
+
try {
|
|
253
|
+
iterator = parseStream(tappedResponse, options.translatorBudget);
|
|
254
|
+
if (settled) {
|
|
255
|
+
closeIterator();
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
while (true) {
|
|
259
|
+
let result: IteratorResult<AdapterEvent>;
|
|
260
|
+
if (heldTerminal) {
|
|
261
|
+
let drainTimer: ReturnType<typeof setTimeout> | undefined;
|
|
262
|
+
const drainTimeout = new Promise<never>((_, reject) => {
|
|
263
|
+
drainTimer = setTimeout(() => reject(new WebSearchStreamProtocolError(
|
|
264
|
+
`adapter did not return within ${postTerminalDrainTimeoutMs}ms after ${heldTerminal?.type ?? "terminal event"}`,
|
|
265
|
+
)), postTerminalDrainTimeoutMs);
|
|
266
|
+
});
|
|
267
|
+
const stoppedDuringDrain = stopped.then(reason => { throw reason; });
|
|
268
|
+
try {
|
|
269
|
+
result = await Promise.race([iterator.next(), drainTimeout, stoppedDuringDrain]);
|
|
270
|
+
} finally {
|
|
271
|
+
if (drainTimer !== undefined) clearTimeout(drainTimer);
|
|
272
|
+
}
|
|
273
|
+
} else {
|
|
274
|
+
result = await iterator.next();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (result.done) {
|
|
278
|
+
if (!heldTerminal) throw new WebSearchStreamProtocolError("adapter returned without a terminal event");
|
|
279
|
+
await handoff.deliver(heldTerminal);
|
|
280
|
+
if (!settled) {
|
|
281
|
+
settled = true;
|
|
282
|
+
clearInactivity();
|
|
283
|
+
detachAbort();
|
|
284
|
+
handoff.close();
|
|
285
|
+
cancelReader(undefined);
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const event = result.value;
|
|
291
|
+
if (heldTerminal) {
|
|
292
|
+
throw new WebSearchStreamProtocolError(
|
|
293
|
+
event.type === "done" || event.type === "incomplete"
|
|
294
|
+
? "adapter yielded more than one terminal event"
|
|
295
|
+
: "adapter yielded an event after its terminal event",
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
if (event.type === "error") {
|
|
299
|
+
fail(event.code === "translation_buffer_limit"
|
|
300
|
+
? new TranslatorBudgetExceededError("live_transient", TRANSLATOR_MAX_TURN_BYTES)
|
|
301
|
+
: new Error(event.message));
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
if (event.type === "done" || event.type === "incomplete") {
|
|
305
|
+
heldTerminal = event;
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
await handoff.deliver(event);
|
|
309
|
+
}
|
|
310
|
+
} catch (error) {
|
|
311
|
+
fail(isTranslatorBudgetExceededError(error)
|
|
312
|
+
? error
|
|
313
|
+
: error instanceof WebSearchStreamProtocolError
|
|
314
|
+
? error
|
|
315
|
+
: new WebSearchStreamProtocolError(
|
|
316
|
+
`adapter threw${heldTerminal ? " after its terminal event" : ""}: ${error instanceof Error ? error.message : String(error)}`,
|
|
317
|
+
));
|
|
318
|
+
}
|
|
319
|
+
})();
|
|
320
|
+
// The foreground intentionally never awaits this task during cancellation; keep every rejection observed.
|
|
321
|
+
void parserPump.catch(() => {});
|
|
322
|
+
|
|
323
|
+
try {
|
|
324
|
+
while (true) {
|
|
325
|
+
const result = await handoff.receive();
|
|
326
|
+
if (result.done) return;
|
|
327
|
+
yield result.value;
|
|
328
|
+
}
|
|
329
|
+
} finally {
|
|
330
|
+
detachAbort();
|
|
331
|
+
if (!settled) {
|
|
332
|
+
const reason = new Error("Web-search stream consumer stopped");
|
|
333
|
+
settled = true;
|
|
334
|
+
clearInactivity();
|
|
335
|
+
stop(reason);
|
|
336
|
+
handoff.close();
|
|
337
|
+
cancelReader(reason);
|
|
338
|
+
}
|
|
339
|
+
closeIterator();
|
|
340
|
+
try { reader.releaseLock(); } catch { /* an outstanding read may still own the lock */ }
|
|
341
|
+
}
|
|
342
|
+
}
|