@iislee/opencodex 2.11.0 → 2.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS_INSTALL.md +109 -0
- package/README.md +117 -22
- package/bin/ocx.mjs +245 -88
- package/bin/package-main.mjs +1 -1
- package/gui/dist/assets/index-Be1sDpWw.css +1 -0
- package/gui/dist/assets/index-D4OKgUV4.js +114 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/alibaba-color.svg +1 -1
- package/gui/dist/provider-icons/antigravity-color.svg +1 -1
- package/gui/dist/provider-icons/claude-color.svg +1 -1
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -1
- package/gui/dist/provider-icons/cursor-color.svg +1 -1
- package/gui/dist/provider-icons/deepseek-color.svg +1 -1
- package/gui/dist/provider-icons/firepass-color.svg +1 -1
- package/gui/dist/provider-icons/fireworks-color.svg +1 -1
- package/gui/dist/provider-icons/gemini-color.svg +1 -1
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -1
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -1
- package/gui/dist/provider-icons/grok.svg +1 -1
- package/gui/dist/provider-icons/groq-color.svg +1 -1
- package/gui/dist/provider-icons/huggingface-color.svg +1 -1
- package/gui/dist/provider-icons/kimi-color.svg +1 -1
- package/gui/dist/provider-icons/kiro-color.svg +2 -2
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -1
- package/gui/dist/provider-icons/mistral-color.svg +1 -1
- package/gui/dist/provider-icons/moonshot-color.svg +1 -1
- package/gui/dist/provider-icons/nvidia-color.svg +1 -1
- package/gui/dist/provider-icons/ollama-color.svg +1 -1
- package/gui/dist/provider-icons/openai.svg +1 -1
- package/gui/dist/provider-icons/opencode.svg +2 -1
- package/gui/dist/provider-icons/openrouter-color.svg +1 -1
- package/gui/dist/provider-icons/pi.svg +2 -2
- package/gui/dist/provider-icons/qianfan-color.svg +1 -1
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -1
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -1
- package/gui/dist/provider-icons/vllm-color.svg +1 -1
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -1
- package/package.json +21 -10
- package/src/adapters/anthropic-output-schema.ts +137 -0
- package/src/adapters/anthropic.ts +475 -62
- package/src/adapters/base.ts +82 -7
- package/src/adapters/client-fingerprint.ts +18 -12
- package/src/adapters/cline-pass-deepseek-v4-tool-replay.ts +69 -0
- package/src/adapters/command-code.ts +637 -0
- package/src/adapters/cursor/call-id.ts +44 -0
- package/src/adapters/cursor/catalog.ts +541 -0
- package/src/adapters/cursor/checkpoint-store.ts +308 -0
- package/src/adapters/cursor/cursor-errors.ts +144 -6
- package/src/adapters/cursor/discovery.ts +122 -14
- package/src/adapters/cursor/effort-map.ts +106 -4
- package/src/adapters/cursor/envelope-echo.ts +290 -0
- package/src/adapters/cursor/framing.ts +39 -0
- package/src/adapters/cursor/h2-pool.ts +123 -0
- package/src/adapters/cursor/http1-bidi.ts +361 -0
- package/src/adapters/cursor/images.ts +704 -0
- package/src/adapters/cursor/live-models.ts +180 -59
- package/src/adapters/cursor/live-transport.ts +623 -170
- package/src/adapters/cursor/message-mapper.ts +4 -1
- package/src/adapters/cursor/native-exec-common.ts +23 -2
- package/src/adapters/cursor/native-exec-desktop.ts +23 -0
- package/src/adapters/cursor/native-exec-fs.ts +10 -7
- package/src/adapters/cursor/native-exec-network.ts +1 -1
- package/src/adapters/cursor/native-exec-shell.ts +5 -3
- package/src/adapters/cursor/native-exec.ts +121 -14
- package/src/adapters/cursor/protobuf-events.ts +829 -11
- package/src/adapters/cursor/protobuf-request.ts +447 -75
- package/src/adapters/cursor/request-builder.ts +271 -35
- package/src/adapters/cursor/tool-definitions.ts +252 -12
- package/src/adapters/cursor/tool-result-normalize.ts +115 -0
- package/src/adapters/cursor/transport.ts +22 -0
- package/src/adapters/cursor/types.ts +48 -1
- package/src/adapters/cursor.ts +336 -39
- package/src/adapters/exec-tool-result-normalize.ts +99 -0
- package/src/adapters/google-antigravity-replay.ts +713 -41
- package/src/adapters/google-antigravity-wire.ts +48 -10
- package/src/adapters/google-errors.ts +44 -12
- package/src/adapters/google-http.ts +38 -10
- package/src/adapters/google-tool-schema.ts +80 -15
- package/src/adapters/google-truncation.ts +11 -0
- package/src/adapters/google.ts +742 -86
- package/src/adapters/identity.ts +39 -6
- package/src/adapters/image.ts +1 -1
- package/src/adapters/kiro-constants.ts +27 -0
- package/src/adapters/kiro-errors.ts +11 -0
- package/src/adapters/kiro-events.ts +19 -1
- package/src/adapters/kiro-thinking.ts +18 -2
- package/src/adapters/kiro-tools.ts +65 -17
- package/src/adapters/kiro.ts +270 -73
- package/src/adapters/mimo-free.ts +17 -0
- package/src/adapters/openai-chat-url.ts +11 -0
- package/src/adapters/openai-chat.ts +1377 -344
- package/src/adapters/openai-responses-url.ts +16 -0
- package/src/adapters/openai-responses.ts +1164 -64
- package/src/adapters/registry.ts +175 -0
- package/src/adapters/responses-tool-schema.ts +67 -0
- package/src/adapters/run-turn-queue.ts +36 -1
- package/src/adapters/tool-call-id.ts +119 -0
- package/src/adapters/tool-catalog-nudge.ts +105 -21
- package/src/adapters/xai-tool-schema.ts +436 -0
- package/src/adapters/xai-web-search.ts +186 -0
- package/src/bridge.ts +512 -92
- package/src/chat/inbound.ts +57 -20
- package/src/chat/outbound.ts +97 -33
- package/src/claude/agents-inject.ts +40 -10
- package/src/claude/context-windows.ts +37 -14
- package/src/claude/desktop-3p.ts +243 -9
- package/src/claude/gateway-cache.ts +41 -4
- package/src/claude/inbound.ts +72 -3
- package/src/claude/model-info.ts +38 -15
- package/src/claude/outbound.ts +88 -21
- package/src/cli/access.ts +46 -3
- package/src/cli/account-api.ts +150 -19
- package/src/cli/account-auth.ts +33 -6
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +648 -28
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +97 -19
- package/src/cli/agent.ts +53 -2
- package/src/cli/alias.ts +66 -0
- package/src/cli/capabilities-command.ts +94 -0
- package/src/cli/capabilities.ts +496 -0
- package/src/cli/claude-agent-startup-sync.ts +73 -0
- package/src/cli/claude-desktop.ts +60 -15
- package/src/cli/claude.ts +129 -35
- package/src/cli/codex-log-guard-doctor.ts +103 -0
- package/src/cli/codex-shim-autorestore.ts +2 -0
- package/src/cli/codex-shim-readiness.ts +76 -0
- package/src/cli/combo.ts +8 -0
- package/src/cli/config-command.ts +74 -10
- package/src/cli/dispatch.ts +821 -0
- package/src/cli/doctor.ts +440 -56
- package/src/cli/ensure-desired-integrations.ts +152 -0
- package/src/cli/export-command.ts +46 -20
- package/src/cli/help.ts +34 -274
- package/src/cli/index.ts +417 -528
- package/src/cli/init.ts +5 -17
- package/src/cli/inspect.ts +230 -0
- package/src/cli/integrations.ts +120 -2
- package/src/cli/lab.ts +607 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/minimax.ts +497 -0
- package/src/cli/models-runtime.ts +130 -2
- package/src/cli/models.ts +118 -16
- package/src/cli/observe.ts +123 -11
- package/src/cli/opencode.ts +4 -2
- package/src/cli/provider-runtime.ts +34 -3
- package/src/cli/provider.ts +25 -3
- package/src/cli/ready.ts +301 -0
- package/src/cli/registry.ts +476 -0
- package/src/cli/root.ts +86 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +57 -10
- package/src/cli/star-prompt.ts +71 -15
- package/src/cli/status.ts +51 -4
- package/src/cli/storage.ts +234 -0
- package/src/cli/system-command.ts +16 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +153 -6
- package/src/cli/usage-report.ts +184 -0
- package/src/cli/v2.ts +137 -18
- package/src/cli/version-skew.ts +46 -0
- package/src/cli.ts +1 -1
- package/src/clients/config-export.ts +1442 -23
- package/src/clients/effective-status.ts +30 -2
- package/src/clients/probes/cline.ts +81 -0
- package/src/codex/account-label.ts +35 -1
- package/src/codex/account-lifecycle.ts +130 -13
- package/src/codex/account-namespaces.ts +49 -3
- package/src/codex/account-pause.ts +2 -1
- package/src/codex/account-priority.ts +84 -0
- package/src/codex/account-store.ts +29 -2
- package/src/codex/account-usability.ts +25 -2
- package/src/codex/admission.ts +256 -0
- package/src/codex/affinity-debug.ts +162 -0
- package/src/codex/app-server-processes.ts +570 -107
- package/src/codex/app-server-restart-service.ts +232 -0
- package/src/codex/auth-api.ts +893 -246
- package/src/codex/auth-collision.ts +5 -3
- package/src/codex/auth-context.ts +348 -32
- package/src/codex/autostart-health.ts +24 -1
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +86 -10
- package/src/codex/catalog/bundled.ts +331 -33
- package/src/codex/catalog/effort.ts +138 -32
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +555 -45
- package/src/codex/catalog/native-models.ts +75 -0
- package/src/codex/catalog/parsing.ts +269 -30
- package/src/codex/catalog/provider-fetch.ts +1626 -144
- package/src/codex/catalog/sync.ts +1576 -187
- package/src/codex/catalog-admission.ts +199 -0
- package/src/codex/catalog-refresh-status.ts +105 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +6 -3
- package/src/codex/codex-write-lock.ts +386 -0
- package/src/codex/convergence-types.ts +614 -0
- package/src/codex/convergence.ts +676 -0
- package/src/codex/coordinator-doctor.ts +332 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-app-restart.ts +355 -0
- package/src/codex/features.ts +636 -39
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +436 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-manifest.ts +112 -0
- package/src/codex/history-migration-guardian.ts +30 -24
- package/src/codex/history-provider.ts +1016 -235
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +223 -0
- package/src/codex/inject-coordination.ts +373 -0
- package/src/codex/inject.ts +1114 -152
- package/src/codex/injected-marker.ts +37 -3
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +81 -0
- package/src/codex/journal.ts +66 -4
- package/src/codex/log-guard/inspect.ts +524 -0
- package/src/codex/log-guard/lock.ts +150 -0
- package/src/codex/log-guard/maintenance.ts +403 -0
- package/src/codex/log-guard/path-safety.ts +88 -0
- package/src/codex/log-guard/policy.ts +44 -0
- package/src/codex/log-guard/processes.ts +205 -0
- package/src/codex/log-guard/protection.ts +489 -0
- package/src/codex/log-guard/sqlite-errors.ts +9 -0
- package/src/codex/main-account-cache.ts +24 -0
- package/src/codex/main-account.ts +29 -1
- package/src/codex/management-convergence.ts +167 -0
- package/src/codex/model-cache.ts +56 -10
- package/src/codex/model-entitlements.ts +360 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +178 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +622 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +675 -0
- package/src/codex/paths.ts +80 -1
- package/src/codex/plan-from-token.ts +140 -0
- package/src/codex/plan.ts +40 -0
- package/src/codex/plugins-doctor.ts +1 -1
- package/src/codex/pool-rotation.ts +74 -4
- package/src/codex/project-config-warnings.ts +20 -6
- package/src/codex/prompt-journal.ts +352 -0
- package/src/codex/prompt-layers.ts +1313 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/prompt-text-probe.ts +238 -0
- package/src/codex/quota-rejection.ts +298 -0
- package/src/codex/quota.ts +264 -21
- package/src/codex/refresh.ts +11 -2
- package/src/codex/reset-credit-recovery.ts +1044 -0
- package/src/codex/routing.ts +514 -94
- package/src/codex/runtime.ts +159 -38
- package/src/codex/shim.ts +1065 -31
- package/src/codex/subagent-model-fallback.ts +437 -43
- package/src/codex/sync.ts +191 -2
- package/src/codex/transition-state.ts +720 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +557 -0
- package/src/codex/warmup.ts +187 -81
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +47 -0
- package/src/combos/index.ts +4 -0
- package/src/combos/request.ts +32 -0
- package/src/combos/types.ts +91 -9
- package/src/compatibility/index.ts +26 -0
- package/src/compatibility/manifest.ts +253 -0
- package/src/compatibility/openai-responses.ts +121 -0
- package/src/config/atomic-write.ts +219 -0
- package/src/config/paths.ts +40 -0
- package/src/config/process-state.ts +309 -0
- package/src/config/provider-name.ts +24 -0
- package/src/config/provider-validation.ts +177 -0
- package/src/config/rebase-provenance.ts +68 -0
- package/src/config.ts +1713 -709
- package/src/generated/compatibility-version.json +3324 -0
- package/src/generated/{jawcode-model-metadata.ts → model-metadata.ts} +19 -17
- package/src/grok/inject.ts +16 -5
- package/src/grok/inspect.ts +45 -0
- package/src/grok/sync.ts +2 -2
- package/src/images/loop.ts +157 -33
- package/src/images/plan.ts +24 -13
- package/src/integrations/config-io.ts +269 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/mutation-flight.ts +71 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/omp-yaml-source.ts +358 -0
- package/src/integrations/owned-refresh.ts +74 -0
- package/src/integrations/ownership-policy.ts +160 -0
- package/src/integrations/ownership.ts +155 -0
- package/src/integrations/registry.ts +166 -0
- package/src/integrations/serialize.ts +314 -0
- package/src/integrations/state.ts +433 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer-lock.ts +98 -0
- package/src/integrations/writer.ts +715 -0
- package/src/lab/artifacts/sanitize.ts +586 -0
- package/src/lab/artifacts/secure-fs.ts +475 -0
- package/src/lab/artifacts/store.ts +310 -0
- package/src/lab/automation/budgets.ts +78 -0
- package/src/lab/automation/config-persistence.ts +256 -0
- package/src/lab/automation/constants.ts +39 -0
- package/src/lab/automation/cooldown.ts +103 -0
- package/src/lab/automation/dispatch.ts +211 -0
- package/src/lab/automation/index.ts +13 -0
- package/src/lab/automation/orchestrator.ts +499 -0
- package/src/lab/automation/persistence.ts +512 -0
- package/src/lab/automation/planner.ts +371 -0
- package/src/lab/automation/policy.ts +136 -0
- package/src/lab/automation/queue.ts +191 -0
- package/src/lab/automation/recovery.ts +24 -0
- package/src/lab/automation/route-context.ts +21 -0
- package/src/lab/automation/run-key.ts +44 -0
- package/src/lab/automation/runs-query.ts +34 -0
- package/src/lab/automation/types.ts +160 -0
- package/src/lab/conformance/assertion.ts +325 -0
- package/src/lab/conformance/digest.ts +22 -0
- package/src/lab/conformance/executor.ts +741 -0
- package/src/lab/conformance/fixture-provider.ts +27 -0
- package/src/lab/conformance/fixtures/live-v1-cases.json +175 -0
- package/src/lab/conformance/fixtures/protocol-v1-cases.json +461 -0
- package/src/lab/conformance/harness-budget.ts +47 -0
- package/src/lab/conformance/index.ts +5 -0
- package/src/lab/conformance/jcs.ts +64 -0
- package/src/lab/conformance/json-pointer.ts +39 -0
- package/src/lab/conformance/manifest.ts +180 -0
- package/src/lab/conformance/mcp-stub.ts +179 -0
- package/src/lab/conformance/negative-controls.ts +164 -0
- package/src/lab/conformance/observation.ts +355 -0
- package/src/lab/conformance/runner.ts +68 -0
- package/src/lab/conformance/sse-normalize.ts +59 -0
- package/src/lab/conformance/suite-manifest.ts +78 -0
- package/src/lab/conformance/types.ts +214 -0
- package/src/lab/constants.ts +126 -0
- package/src/lab/digest.ts +64 -0
- package/src/lab/events/errors.ts +9 -0
- package/src/lab/events/limits.ts +117 -0
- package/src/lab/events/types.ts +229 -0
- package/src/lab/events/validate.ts +781 -0
- package/src/lab/fabric/constants.ts +40 -0
- package/src/lab/fabric/executor.ts +492 -0
- package/src/lab/fabric/index.ts +80 -0
- package/src/lab/fabric/manifest.ts +222 -0
- package/src/lab/fabric/observe.ts +489 -0
- package/src/lab/fabric/patch.ts +79 -0
- package/src/lab/fabric/producer-child.ts +139 -0
- package/src/lab/fabric/producer-isolate.ts +276 -0
- package/src/lab/fabric/producer-protocol.ts +61 -0
- package/src/lab/fabric/scratch.ts +439 -0
- package/src/lab/fabric/subject.ts +106 -0
- package/src/lab/fabric/types.ts +134 -0
- package/src/lab/fabric/verifier.ts +98 -0
- package/src/lab/index.ts +54 -0
- package/src/lab/ledger/artifact-refs.ts +127 -0
- package/src/lab/ledger/invalidation.ts +136 -0
- package/src/lab/ledger/purge.ts +310 -0
- package/src/lab/ledger/store.ts +532 -0
- package/src/lab/live/credential-lease.ts +53 -0
- package/src/lab/live/destination.ts +155 -0
- package/src/lab/live/executor.ts +336 -0
- package/src/lab/live/inert-tools.ts +56 -0
- package/src/lab/live/manifest.ts +85 -0
- package/src/lab/live/mcp-loopback.ts +57 -0
- package/src/lab/live/runner.ts +19 -0
- package/src/lab/live/sandbox.ts +61 -0
- package/src/lab/live/suite-manifest.ts +41 -0
- package/src/lab/live/transport.ts +118 -0
- package/src/lab/live/types.ts +197 -0
- package/src/lab/observe/from-conformance.ts +301 -0
- package/src/lab/observe/from-live.ts +117 -0
- package/src/lab/paths.ts +153 -0
- package/src/lab/projection/rebuild.ts +495 -0
- package/src/lab/projection/schema.ts +135 -0
- package/src/lab/projection/verdicts.ts +474 -0
- package/src/lab/projection/verification.ts +412 -0
- package/src/lab/public/bundle.ts +217 -0
- package/src/lab/public/community-authority.ts +175 -0
- package/src/lab/public/community-files.ts +29 -0
- package/src/lab/public/community.ts +479 -0
- package/src/lab/public/file-safety.ts +155 -0
- package/src/lab/public/ids.ts +26 -0
- package/src/lab/public/index.ts +16 -0
- package/src/lab/public/mutation-lock.ts +424 -0
- package/src/lab/public/operator.ts +353 -0
- package/src/lab/public/origin-purge.ts +79 -0
- package/src/lab/public/origin.ts +203 -0
- package/src/lab/public/privacy.ts +143 -0
- package/src/lab/public/private-file.ts +261 -0
- package/src/lab/public/project.ts +124 -0
- package/src/lab/public/purge-test-fault.ts +21 -0
- package/src/lab/public/purge.ts +223 -0
- package/src/lab/public/registry.ts +44 -0
- package/src/lab/public/revocation.ts +252 -0
- package/src/lab/public/signature.ts +243 -0
- package/src/lab/public/storage.ts +105 -0
- package/src/lab/public/strict-json.ts +206 -0
- package/src/lab/public/time.ts +26 -0
- package/src/lab/public/types.ts +172 -0
- package/src/lab/public/validate.ts +391 -0
- package/src/lab/query/catalog.ts +101 -0
- package/src/lab/query/connection.ts +107 -0
- package/src/lab/query/constants.ts +4 -0
- package/src/lab/query/cursor.ts +132 -0
- package/src/lab/query/dto-map.ts +277 -0
- package/src/lab/query/errors.ts +22 -0
- package/src/lab/query/freshness.ts +53 -0
- package/src/lab/query/index.ts +45 -0
- package/src/lab/query/latest-observation.ts +59 -0
- package/src/lab/query/passive-production.ts +159 -0
- package/src/lab/query/queries.ts +444 -0
- package/src/lab/query/types.ts +266 -0
- package/src/lab/subject/behavior-fingerprint.ts +77 -0
- package/src/lab/subject/installation-salt.ts +112 -0
- package/src/lab/subject/protocol-subject.ts +80 -0
- package/src/lab/subject/route-subject.ts +74 -0
- package/src/lib/admin-secrets.ts +24 -0
- package/src/lib/app-owned-memory-stores.ts +22 -0
- package/src/lib/bounded-body.ts +167 -11
- package/src/lib/bun-runtime.ts +125 -12
- package/src/lib/bun-stream-caps.ts +13 -9
- package/src/lib/codex-restart-contract.ts +120 -0
- package/src/lib/config-ownership.ts +26 -2
- package/src/lib/destination-policy.ts +65 -1
- package/src/lib/errors.ts +80 -5
- package/src/lib/fabric-task-execution-authority.ts +7 -0
- package/src/lib/fabric-task-host.ts +29 -0
- package/src/lib/lab-activation.ts +223 -0
- package/src/lib/lab-live-execution-authority.ts +13 -0
- package/src/lib/lab-live-host.ts +30 -0
- package/src/lib/lab-live-pinned-sender.ts +56 -0
- package/src/lib/lab-live-route-production.ts +130 -0
- package/src/lib/lab-passive-linker-registration.ts +26 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/local-management-capability.ts +100 -0
- package/src/lib/local-provider-reload-contract.ts +100 -0
- package/src/lib/optional-shutdown-hooks.ts +57 -0
- package/src/lib/package-tree-integrity.ts +101 -0
- package/src/lib/pinned-http.ts +145 -26
- package/src/lib/process-control.ts +6 -2
- package/src/lib/provider-outbound.ts +49 -9
- package/src/lib/redact.ts +419 -3
- package/src/lib/self-launch-argv.ts +15 -0
- package/src/lib/server-resource-ownership.ts +71 -0
- package/src/lib/service-secrets.ts +15 -0
- package/src/lib/shadow-call.ts +35 -4
- package/src/lib/sse-decoder.ts +41 -0
- package/src/lib/state-store-registrations.ts +10 -2
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/token-estimate.ts +19 -2
- package/src/lib/tool-argument-integers.ts +253 -0
- package/src/lib/translator-budget.ts +44 -0
- package/src/lib/upstream-http-version.ts +57 -0
- package/src/lib/upstream-reachability.ts +95 -0
- package/src/lib/upstream-retry.ts +156 -3
- package/src/lib/windows-atomic-replace.ts +155 -0
- package/src/lib/windows-elevation.ts +70 -2
- package/src/lib/windows-secret-acl.ts +409 -69
- package/src/lib/windows-service-wrappers.ts +72 -0
- package/src/lib/windows-text.ts +106 -0
- package/src/lib/windows-user-principal.ts +341 -0
- package/src/lib/winsw.ts +33 -5
- package/src/oauth/account-import/google-antigravity-adapter.ts +74 -0
- package/src/oauth/account-import/index.ts +15 -0
- package/src/oauth/account-import/parser.ts +83 -0
- package/src/oauth/account-import/registry.ts +18 -0
- package/src/oauth/account-import/service.ts +75 -0
- package/src/oauth/account-import/types.ts +91 -0
- package/src/oauth/anthropic.ts +12 -1
- package/src/oauth/callback-server.ts +30 -4
- package/src/oauth/chatgpt.ts +12 -1
- package/src/oauth/cline.ts +203 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +46 -5
- package/src/oauth/generic-account-failover.ts +231 -0
- package/src/oauth/google-antigravity.ts +35 -3
- package/src/oauth/health.ts +20 -12
- package/src/oauth/index.ts +462 -71
- package/src/oauth/key-providers.ts +25 -0
- package/src/oauth/kimi.ts +25 -3
- package/src/oauth/kiro.ts +95 -6
- package/src/oauth/local-token-detect.ts +13 -2
- package/src/oauth/log.ts +3 -1
- package/src/oauth/login-cli.ts +88 -28
- package/src/oauth/nous.ts +798 -0
- package/src/oauth/open-browser-choice.ts +26 -0
- package/src/oauth/store.ts +133 -26
- package/src/oauth/token-guardian.ts +9 -3
- package/src/oauth/types.ts +15 -0
- package/src/pi/models.ts +2 -2
- package/src/providers/alibaba-region-migration.ts +1 -1
- package/src/providers/antigravity-models.ts +521 -31
- package/src/providers/auto-compact-budget.ts +65 -0
- package/src/providers/base-url-choices.ts +10 -0
- package/src/providers/codex-capacity.ts +292 -0
- package/src/providers/command-code-efforts.ts +176 -0
- package/src/providers/context-cap.ts +26 -8
- package/src/providers/cursor-pool.ts +72 -0
- package/src/providers/default-aliases.ts +65 -0
- package/src/providers/derive.ts +281 -6
- package/src/providers/fastwire.ts +507 -0
- package/src/providers/free-directory.ts +10 -7
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +71 -3
- package/src/providers/label.ts +35 -2
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +115 -22
- package/src/providers/model-presets.ts +119 -0
- package/src/providers/model-rename-migration.ts +255 -0
- package/src/providers/model-rename-startup.ts +28 -0
- package/src/providers/new-model-policy.ts +146 -0
- package/src/providers/openai-sidecar.ts +72 -4
- package/src/providers/openai-tier-startup.ts +31 -2
- package/src/providers/openai-tiers.ts +119 -4
- package/src/providers/openai-virtual-models.ts +1 -0
- package/src/providers/opencode-zen-rate-limit.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +30 -0
- package/src/providers/quota.ts +1379 -40
- package/src/providers/registry.ts +1545 -112
- package/src/providers/request-pacing.ts +310 -0
- package/src/providers/service-tier.ts +277 -0
- package/src/providers/slug-codec.ts +94 -6
- package/src/providers/static-model-discovery.ts +86 -0
- package/src/providers/xai-responses-opt-in.ts +15 -0
- package/src/providers/xai-transport.ts +32 -4
- package/src/reasoning-effort.ts +68 -3
- package/src/responses/apply-patch-envelope.ts +63 -0
- package/src/responses/code-mode-helper-compat.ts +50 -0
- package/src/responses/compaction.ts +26 -1
- package/src/responses/custom-tool-compat.ts +384 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/namespace-tool-compat.ts +435 -0
- package/src/responses/parser.ts +225 -38
- package/src/responses/provider-continuation.ts +98 -0
- package/src/responses/provider-opaque-metadata.ts +73 -0
- package/src/responses/reasoning-envelope.ts +9 -1
- package/src/responses/reasoning-replay-cache.ts +504 -0
- package/src/responses/schema.ts +12 -2
- package/src/responses/spill-store.ts +75 -10
- package/src/responses/state.ts +659 -31
- package/src/responses/thought-signature-replay.ts +364 -0
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/responses/truncated-stop-reason.ts +60 -0
- package/src/responses/turn-termination.ts +107 -0
- package/src/router.ts +405 -31
- package/src/routing/analytics.ts +379 -0
- package/src/routing/capability.ts +244 -0
- package/src/routing/compatibility/assemble.ts +73 -0
- package/src/routing/compatibility/behavior.ts +278 -0
- package/src/routing/compatibility/catalog.ts +99 -0
- package/src/routing/compatibility/endpoint.ts +52 -0
- package/src/routing/compatibility/lab-evidence-provider.ts +130 -0
- package/src/routing/compatibility/policy.ts +181 -0
- package/src/routing/compatibility/provider-slot.ts +56 -0
- package/src/routing/compatibility/reader.ts +110 -0
- package/src/routing/compatibility/subject.ts +191 -0
- package/src/routing/compatibility/types.ts +64 -0
- package/src/routing/compatibility/version.ts +104 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +495 -0
- package/src/routing/health.ts +412 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +547 -0
- package/src/routing/quota.ts +155 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +776 -0
- package/src/server/adapter-resolve.ts +2 -29
- package/src/server/auth-cors.ts +289 -48
- package/src/server/background-lifecycle.ts +182 -0
- package/src/server/chat-completions.ts +154 -72
- package/src/server/chat-native-sse.ts +331 -0
- package/src/server/chat-native.ts +445 -0
- package/src/server/claude-messages.ts +160 -44
- package/src/server/direct-local-http.ts +347 -0
- package/src/server/effort-policy.ts +18 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +39 -10
- package/src/server/images.ts +94 -12
- package/src/server/index.ts +1021 -190
- package/src/server/lifecycle.ts +336 -14
- package/src/server/live.ts +136 -17
- package/src/server/local-management-read-client.ts +90 -0
- package/src/server/local-provider-reload-client.ts +137 -0
- package/src/server/management/agent-settings-routes.ts +433 -116
- package/src/server/management/api-key-usage.ts +31 -5
- package/src/server/management/body.ts +6 -0
- package/src/server/management/codex-prompt-routes.ts +570 -0
- package/src/server/management/combo-routes.ts +64 -25
- package/src/server/management/config-routes.ts +511 -51
- package/src/server/management/context.ts +89 -2
- package/src/server/management/integration-routes.ts +498 -0
- package/src/server/management/lab-automation-routes.ts +206 -0
- package/src/server/management/lab-routes.ts +563 -0
- package/src/server/management/logs-usage-routes.ts +127 -52
- package/src/server/management/model-routes.ts +461 -133
- package/src/server/management/model-rows.ts +167 -0
- package/src/server/management/native-integration-routes.ts +770 -0
- package/src/server/management/oauth-account-routes.ts +90 -8
- package/src/server/management/provider-capability-config.ts +48 -0
- package/src/server/management/provider-routes.ts +836 -159
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/route-registry.ts +332 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +380 -0
- package/src/server/management/shared.ts +27 -11
- package/src/server/management/sidebar-routes.ts +47 -31
- package/src/server/management/storage-log-guard-routes.ts +186 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +289 -33
- package/src/server/management/system-routes.ts +77 -0
- package/src/server/management/usage-summary-cache.ts +9 -1
- package/src/server/management/vision-sidecar-options.ts +167 -0
- package/src/server/management/web-search-sidecar-options.ts +120 -0
- package/src/server/management-api.ts +126 -23
- package/src/server/management-auth.ts +220 -5
- package/src/server/passive-route-linker.ts +66 -0
- package/src/server/port-reclaim.ts +1 -1
- package/src/server/ports.ts +41 -1
- package/src/server/proxy-liveness.ts +161 -10
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +221 -54
- package/src/server/relay.ts +479 -87
- package/src/server/request-decompress.ts +113 -6
- package/src/server/request-log-conversation.ts +51 -0
- package/src/server/request-log.ts +298 -31
- package/src/server/responses/agent-task-recovery-cache.ts +143 -0
- package/src/server/responses/agent-task-recovery.ts +465 -0
- package/src/server/responses/codex-auth-error.ts +55 -0
- package/src/server/responses/collaboration.ts +239 -36
- package/src/server/responses/combo-stream-preflight.ts +178 -0
- package/src/server/responses/compact.ts +444 -72
- package/src/server/responses/core.ts +3579 -436
- package/src/server/responses/empty-completion-guard.ts +333 -0
- package/src/server/responses/encrypted-payload.ts +62 -39
- package/src/server/responses/fetch-helpers.ts +89 -99
- package/src/server/responses/input-admission.ts +187 -0
- package/src/server/responses/pacing-overload.ts +13 -0
- package/src/server/responses/passthrough-error.ts +33 -9
- package/src/server/responses/policy-fallback.ts +178 -0
- package/src/server/responses/responses-field-backfill.ts +343 -0
- package/src/server/responses/terminal-guard.ts +26 -5
- package/src/server/responses/upstream-error.ts +5 -0
- package/src/server/responses/ws-upstream.ts +381 -0
- package/src/server/responses-custom-tool-repair.ts +363 -0
- package/src/server/responses-item-id-repair.ts +54 -6
- package/src/server/responses-json-events.ts +90 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-reasoning-summary-rewrite.ts +178 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses-terminal-repair.ts +363 -0
- package/src/server/responses-tool-search-repair.ts +267 -0
- package/src/server/responses-undeclared-tool-guard.ts +376 -0
- package/src/server/responses.ts +18 -2
- package/src/server/search.ts +78 -13
- package/src/server/sse-frame-buffer.ts +319 -0
- package/src/server/sse-payload-rewrite.ts +110 -22
- package/src/server/startup-action-control.ts +8 -1
- package/src/server/startup-health-cache.ts +19 -1
- package/src/server/system-env.ts +80 -9
- package/src/server/ws-bridge.ts +53 -40
- package/src/service-manager-probe.ts +892 -0
- package/src/service.ts +1153 -94
- package/src/sidecar/auth.ts +92 -0
- package/src/sidecar/candidates.ts +83 -0
- package/src/storage/cleanup.ts +2 -2
- package/src/storage/policy-job.ts +14 -4
- package/src/storage/policy.ts +88 -23
- package/src/storage/scanner.ts +1 -1
- package/src/storage/worker-lifecycle.ts +14 -14
- package/src/tray/windows-tray.ps1 +83 -9
- package/src/tray/windows.ts +43 -16
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +911 -0
- package/src/types/provider.ts +614 -0
- package/src/types/request.ts +397 -0
- package/src/types/tools.ts +236 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +107 -1237
- package/src/update/index.ts +37 -23
- package/src/update/job.ts +445 -68
- package/src/update/notify.ts +12 -6
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/transactional-install.d.mts +22 -0
- package/src/update/transactional-install.mjs +266 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/expected-prices.ts +287 -16
- package/src/usage/log.ts +666 -50
- package/src/usage/summary.ts +388 -17
- package/src/usage/user-cost-overlay-reconciler.ts +313 -0
- package/src/usage/user-cost-overlays.ts +314 -0
- package/src/vision/anthropic-describe.ts +10 -6
- package/src/vision/backends.ts +97 -0
- package/src/vision/describe.ts +27 -16
- package/src/vision/eligibility.ts +250 -0
- package/src/vision/index.ts +238 -24
- package/src/vision/reasoning.ts +55 -0
- package/src/vision/routed-describe.ts +175 -0
- package/src/vision/timeout-bounds.ts +9 -0
- package/src/web-search/anthropic-executor.ts +13 -7
- package/src/web-search/backends.ts +108 -0
- package/src/web-search/exa-executor.ts +119 -0
- package/src/web-search/executor.ts +21 -6
- package/src/web-search/gemini-executor.ts +141 -0
- package/src/web-search/index.ts +150 -15
- package/src/web-search/loop.ts +284 -54
- package/src/web-search/parse.ts +125 -30
- package/src/web-search/sources.ts +60 -0
- package/src/web-search/xai-executor.ts +219 -0
- package/gui/dist/assets/index-DTpMHS4F.js +0 -67
- package/gui/dist/assets/index-ZNVDE3C7.css +0 -1
- package/gui/dist/provider-icons/antigravity.svg +0 -1
- package/gui/dist/provider-icons/claude.svg +0 -1
- package/gui/dist/provider-icons/copilot.svg +0 -1
- package/gui/dist/provider-icons/cursor.svg +0 -2
- package/gui/dist/provider-icons/gemini.svg +0 -1
- package/gui/dist/provider-icons/grok-color.svg +0 -1
- package/gui/dist/provider-icons/kiro.svg +0 -14
- package/src/cli/internal-dispatch.ts +0 -20
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side thought-signature replay store (issue #1735 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* Gemini issues a thoughtSignature on the function-call part of a response and requires it
|
|
5
|
+
* back when the same call is replayed in a later request. The Responses wire carries the
|
|
6
|
+
* signature in extra_content.google.thought_signature, and a conforming client echoes it on
|
|
7
|
+
* the replay. Real clients (codex-rs 0.144.x, Codex desktop) do NOT echo extra_content:
|
|
8
|
+
* they replay history as bare function_call / custom_tool_call items keyed by call_id.
|
|
9
|
+
* Without the signature Gemini rejects the replayed part with
|
|
10
|
+
* "Function call is missing a thought_signature in functionCall parts".
|
|
11
|
+
*
|
|
12
|
+
* This module is the proxy-side fallback: remember the signature we handed out and re-attach
|
|
13
|
+
* it on replay even when the client never echoes it. Values stay opaque (never parsed or
|
|
14
|
+
* re-encoded) and are bounded like the wire metadata.
|
|
15
|
+
*
|
|
16
|
+
* SCOPE: a client-visible `call_id` is NOT unique across conversations, accounts, providers
|
|
17
|
+
* or models. Keying on it alone let one thread's signature overwrite another's, and let a
|
|
18
|
+
* lookup hand a signature from a different account's turn to the current one. The key is the
|
|
19
|
+
* same identity the in-process reasoning cache already uses — thread plus exact provider
|
|
20
|
+
* destination, adapter, model and credential — so a signature can only ever be replayed into
|
|
21
|
+
* the turn that produced it.
|
|
22
|
+
*/
|
|
23
|
+
import { chmodSync, readFileSync, writeFileSync } from "node:fs";
|
|
24
|
+
import { randomBytes } from "node:crypto";
|
|
25
|
+
import { join } from "node:path";
|
|
26
|
+
import { atomicWriteFileAsync, getConfigDir } from "../config";
|
|
27
|
+
import type { OcxProviderOpaqueToolCallMetadata, OcxReasoningReplayScopeRef } from "../types";
|
|
28
|
+
import { isCarryableSignature, responsesExtraContentFromProviderMetadata } from "./provider-opaque-metadata";
|
|
29
|
+
|
|
30
|
+
const STORE_FILE_NAME = "thought-signature-replay.json";
|
|
31
|
+
const SALT_FILE_NAME = "thought-signature-replay.salt";
|
|
32
|
+
/**
|
|
33
|
+
* Bumped whenever `keyFor` changes shape. v3 added the durable destination identity; v4
|
|
34
|
+
* added the salted durable credential identity (#1926), so a v3 file's keys can never
|
|
35
|
+
* match and are dropped on load instead of aging out invisibly. v3 rows carried no
|
|
36
|
+
* credential information, so they are not upgradable — the next Gemini turn re-accumulates
|
|
37
|
+
* its signatures (bounded, best-effort loss identical to the pre-store status quo).
|
|
38
|
+
*/
|
|
39
|
+
const STORE_VERSION = 4;
|
|
40
|
+
|
|
41
|
+
/** Bound on remembered entries; real signatures are a few hundred bytes, so this stays small. */
|
|
42
|
+
const MAX_ENTRIES = 16_384;
|
|
43
|
+
/**
|
|
44
|
+
* Total bytes of remembered signature material.
|
|
45
|
+
*
|
|
46
|
+
* An entry count alone is not a memory bound: a single signature may be 64KiB, so 16,384
|
|
47
|
+
* entries is a ~1GiB ceiling. This is the bound that actually holds.
|
|
48
|
+
*/
|
|
49
|
+
const MAX_TOTAL_BYTES = 32 * 1024 * 1024;
|
|
50
|
+
/** A signature is needed for the immediate next turn; a long TTL also covers resumed threads. */
|
|
51
|
+
const TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
52
|
+
|
|
53
|
+
type StoredEntry = { sig: string; savedAt: number };
|
|
54
|
+
|
|
55
|
+
/** Outcome of a remember attempt. `conflict` is a real signal, not a no-op. */
|
|
56
|
+
export type ThoughtSignatureRememberResult =
|
|
57
|
+
| "stored"
|
|
58
|
+
| "already-equal"
|
|
59
|
+
| "conflict"
|
|
60
|
+
| "unscoped"
|
|
61
|
+
| "ignored";
|
|
62
|
+
|
|
63
|
+
let entries = new Map<string, StoredEntry>();
|
|
64
|
+
let totalBytes = 0;
|
|
65
|
+
let loaded = false;
|
|
66
|
+
let persistChain: Promise<void> = Promise.resolve();
|
|
67
|
+
|
|
68
|
+
function storePath(): string {
|
|
69
|
+
return join(getConfigDir(), STORE_FILE_NAME);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function saltPath(): string {
|
|
73
|
+
return join(getConfigDir(), SALT_FILE_NAME);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let cachedSalt: Buffer | undefined;
|
|
77
|
+
let saltLoaded = false;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Installation-local salt for the durable credential identity (#1926). Created once and
|
|
81
|
+
* persisted beside the store; losing it invalidates every stored key (the entries then
|
|
82
|
+
* never match and age out), which is safe — signatures re-accumulate per turn.
|
|
83
|
+
*/
|
|
84
|
+
export function thoughtSignatureReplaySalt(): Buffer | undefined {
|
|
85
|
+
if (saltLoaded) return cachedSalt;
|
|
86
|
+
saltLoaded = true;
|
|
87
|
+
try {
|
|
88
|
+
const raw = readFileSync(saltPath());
|
|
89
|
+
if (raw.length >= 16) {
|
|
90
|
+
// Re-assert owner-only permissions on every load: a pre-existing file may have
|
|
91
|
+
// been created before this guard or loosened by external tooling.
|
|
92
|
+
try { chmodSync(saltPath(), 0o600); } catch { /* best effort on exotic filesystems */ }
|
|
93
|
+
cachedSalt = raw;
|
|
94
|
+
return cachedSalt;
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// fall through to mint
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
const minted = randomBytes(32);
|
|
101
|
+
writeFileSync(saltPath(), minted, { mode: 0o600 });
|
|
102
|
+
cachedSalt = minted;
|
|
103
|
+
} catch {
|
|
104
|
+
// Unwritable config dir: no durable credential identity this process; the durable
|
|
105
|
+
// store fails closed (keyFor returns undefined) rather than keying under a shared id.
|
|
106
|
+
cachedSalt = undefined;
|
|
107
|
+
}
|
|
108
|
+
return cachedSalt;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function nonEmpty(value: unknown): value is string {
|
|
112
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Durable key for one call, or `undefined` when the scope is incomplete.
|
|
117
|
+
*
|
|
118
|
+
* Incomplete scope means "do not remember" rather than "remember globally": a partially
|
|
119
|
+
* identified entry is exactly the cross-thread collision this store exists to prevent.
|
|
120
|
+
* The reasoning cache's identities are process-local HMACs, so this key deliberately uses
|
|
121
|
+
* only the stable, non-secret fields that survive a restart.
|
|
122
|
+
*/
|
|
123
|
+
function keyFor(callId: string, scope: OcxReasoningReplayScopeRef | undefined): string | undefined {
|
|
124
|
+
const identity = scope?.current;
|
|
125
|
+
if (
|
|
126
|
+
!nonEmpty(callId)
|
|
127
|
+
|| !nonEmpty(scope?.clientThreadId)
|
|
128
|
+
|| !nonEmpty(identity?.providerName)
|
|
129
|
+
|| !nonEmpty(identity?.adapterName)
|
|
130
|
+
|| !nonEmpty(identity?.modelId)
|
|
131
|
+
// v4 (#1926): a missing durable credential identity means we cannot isolate this
|
|
132
|
+
// entry per credential across restarts. Refusing the key is the fail-closed choice —
|
|
133
|
+
// "credential:unknown" would let two different credentials share one durable slot.
|
|
134
|
+
|| !nonEmpty(identity?.credentialDurableIdentity)
|
|
135
|
+
) return undefined;
|
|
136
|
+
return JSON.stringify([
|
|
137
|
+
scope.clientThreadId,
|
|
138
|
+
identity.providerName,
|
|
139
|
+
// Destination, unlike the credential identity, has a restart-stable form: it is a
|
|
140
|
+
// configured endpoint rather than a secret, so a plain digest works where the
|
|
141
|
+
// reasoning cache's randomBytes-keyed HMAC cannot. Without it, one provider NAME
|
|
142
|
+
// serving two endpoints shares signatures across both.
|
|
143
|
+
identity.providerDestinationDurableIdentity ?? "destination:unknown",
|
|
144
|
+
identity.credentialDurableIdentity,
|
|
145
|
+
identity.adapterName,
|
|
146
|
+
identity.modelId,
|
|
147
|
+
callId,
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function load(): void {
|
|
152
|
+
if (loaded) return;
|
|
153
|
+
loaded = true;
|
|
154
|
+
let raw: string;
|
|
155
|
+
try {
|
|
156
|
+
raw = readFileSync(storePath(), "utf8");
|
|
157
|
+
} catch {
|
|
158
|
+
return; // First run or unreadable file: start empty.
|
|
159
|
+
}
|
|
160
|
+
try {
|
|
161
|
+
const parsed: unknown = JSON.parse(raw);
|
|
162
|
+
if (typeof parsed !== "object" || parsed === null || !Array.isArray((parsed as { entries?: unknown }).entries)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
// The version was written but never read, so a key-shape change could not be
|
|
166
|
+
// announced — old entries simply went dead and aged out on TTL, which is silent and
|
|
167
|
+
// indistinguishable from a store that is not working. Reading it makes a shape change
|
|
168
|
+
// an explicit drop: entries keyed by an older scheme are discarded on load rather than
|
|
169
|
+
// lingering as permanent misses.
|
|
170
|
+
if ((parsed as { version?: unknown }).version !== STORE_VERSION) return;
|
|
171
|
+
const nowMs = Date.now();
|
|
172
|
+
for (const entry of (parsed as { entries: unknown[] }).entries) {
|
|
173
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
174
|
+
const { key, sig, savedAt } = entry as { key?: unknown; sig?: unknown; savedAt?: unknown };
|
|
175
|
+
if (typeof key !== "string" || typeof sig !== "string" || typeof savedAt !== "number") continue;
|
|
176
|
+
if (savedAt <= nowMs - TTL_MS) continue;
|
|
177
|
+
if (!isCarryableSignature(sig)) continue;
|
|
178
|
+
entries.set(key, { sig, savedAt });
|
|
179
|
+
totalBytes += sig.length;
|
|
180
|
+
}
|
|
181
|
+
} catch {
|
|
182
|
+
// Corrupt store: ignore it; a later remember() rewrites a clean snapshot.
|
|
183
|
+
}
|
|
184
|
+
// A loaded snapshot can already exceed the bounds if they were lowered, so enforce them
|
|
185
|
+
// here rather than waiting for the next write.
|
|
186
|
+
prune(Date.now());
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function prune(nowMs: number): void {
|
|
190
|
+
for (const [key, entry] of entries) {
|
|
191
|
+
if (nowMs - entry.savedAt > TTL_MS) {
|
|
192
|
+
entries.delete(key);
|
|
193
|
+
totalBytes -= entry.sig.length;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (entries.size <= MAX_ENTRIES && totalBytes <= MAX_TOTAL_BYTES) return;
|
|
197
|
+
const sorted = [...entries.entries()].sort((a, b) => a[1].savedAt - b[1].savedAt);
|
|
198
|
+
for (const [key, entry] of sorted) {
|
|
199
|
+
if (entries.size <= MAX_ENTRIES && totalBytes <= MAX_TOTAL_BYTES) break;
|
|
200
|
+
entries.delete(key);
|
|
201
|
+
totalBytes -= entry.sig.length;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function persist(): Promise<void> {
|
|
206
|
+
persistChain = persistChain
|
|
207
|
+
.then(async () => {
|
|
208
|
+
const snapshot = JSON.stringify({
|
|
209
|
+
version: STORE_VERSION,
|
|
210
|
+
entries: [...entries].map(([key, entry]) => ({ key, sig: entry.sig, savedAt: entry.savedAt })),
|
|
211
|
+
});
|
|
212
|
+
await atomicWriteFileAsync(storePath(), snapshot);
|
|
213
|
+
})
|
|
214
|
+
.catch(() => {
|
|
215
|
+
// Best-effort persistence: the in-memory store still serves the running process.
|
|
216
|
+
});
|
|
217
|
+
return persistChain;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Record the signature that left the proxy on a function-call response item.
|
|
222
|
+
*
|
|
223
|
+
* Returns the outcome so a caller can await durability before exposing the item, and so a
|
|
224
|
+
* genuine conflict is observable instead of silently overwriting. A different signature under
|
|
225
|
+
* the SAME complete key means two different upstream turns claimed one identity: that is a
|
|
226
|
+
* corruption signal, and keeping the first value is the fail-closed choice.
|
|
227
|
+
*/
|
|
228
|
+
export function rememberThoughtSignatureForReplay(
|
|
229
|
+
callId: string,
|
|
230
|
+
signature: string,
|
|
231
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
232
|
+
): { result: ThoughtSignatureRememberResult; durable: Promise<void> } {
|
|
233
|
+
if (!callId || !isCarryableSignature(signature)) {
|
|
234
|
+
return { result: "ignored", durable: Promise.resolve() };
|
|
235
|
+
}
|
|
236
|
+
const key = keyFor(callId, scope);
|
|
237
|
+
if (key === undefined) return { result: "unscoped", durable: Promise.resolve() };
|
|
238
|
+
load();
|
|
239
|
+
const existing = entries.get(key);
|
|
240
|
+
if (existing) {
|
|
241
|
+
if (existing.sig === signature) return { result: "already-equal", durable: Promise.resolve() };
|
|
242
|
+
return { result: "conflict", durable: Promise.resolve() };
|
|
243
|
+
}
|
|
244
|
+
entries.set(key, { sig: signature, savedAt: Date.now() });
|
|
245
|
+
totalBytes += signature.length;
|
|
246
|
+
prune(Date.now());
|
|
247
|
+
return { result: "stored", durable: persist() };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Serialize provider metadata onto an outbound Responses function_call item AND remember the
|
|
252
|
+
* signature server-side, so a client that replays the call without echoing extra_content can
|
|
253
|
+
* still be served from the store.
|
|
254
|
+
*/
|
|
255
|
+
export function rememberAndSerializeExtraContent(
|
|
256
|
+
callId: string,
|
|
257
|
+
metadata: OcxProviderOpaqueToolCallMetadata | undefined,
|
|
258
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
259
|
+
): {
|
|
260
|
+
extra?: { extra_content: { google: { thought_signature: string } } };
|
|
261
|
+
durable: Promise<void>;
|
|
262
|
+
} {
|
|
263
|
+
const extra = responsesExtraContentFromProviderMetadata(metadata);
|
|
264
|
+
if (!extra) return { durable: Promise.resolve() };
|
|
265
|
+
const { durable } = rememberThoughtSignatureForReplay(
|
|
266
|
+
callId,
|
|
267
|
+
extra.extra_content.google.thought_signature,
|
|
268
|
+
scope,
|
|
269
|
+
);
|
|
270
|
+
return { extra, durable };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Remember the signature without serializing it onto the item. Used for freeform tools, whose
|
|
275
|
+
* Responses items are custom_tool_call blocks that cannot carry extra_content — the signature
|
|
276
|
+
* still must be stored so the replayed call (which comes back as custom_tool_call and never
|
|
277
|
+
* echoes metadata) can be re-signed server-side.
|
|
278
|
+
*/
|
|
279
|
+
export function rememberExtraContentForReplay(
|
|
280
|
+
callId: string,
|
|
281
|
+
metadata: OcxProviderOpaqueToolCallMetadata | undefined,
|
|
282
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
283
|
+
): Promise<void> {
|
|
284
|
+
const extra = responsesExtraContentFromProviderMetadata(metadata);
|
|
285
|
+
if (!extra) return Promise.resolve();
|
|
286
|
+
return rememberThoughtSignatureForReplay(
|
|
287
|
+
callId,
|
|
288
|
+
extra.extra_content.google.thought_signature,
|
|
289
|
+
scope,
|
|
290
|
+
).durable;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Look up a signature previously handed out for this call in THIS scope, if still fresh. */
|
|
294
|
+
export function lookupReplayThoughtSignature(
|
|
295
|
+
callId: string,
|
|
296
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
297
|
+
): string | undefined {
|
|
298
|
+
const key = keyFor(callId, scope);
|
|
299
|
+
if (key === undefined) return undefined;
|
|
300
|
+
load();
|
|
301
|
+
const entry = entries.get(key);
|
|
302
|
+
if (!entry) return undefined;
|
|
303
|
+
if (Date.now() - entry.savedAt > TTL_MS) {
|
|
304
|
+
entries.delete(key);
|
|
305
|
+
totalBytes -= entry.sig.length;
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
return entry.sig;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/** Drop a remembered signature for a specific callId and scope (e.g. when upstream rejects it). */
|
|
312
|
+
export function forgetThoughtSignatureForReplay(
|
|
313
|
+
callId: string,
|
|
314
|
+
scope: OcxReasoningReplayScopeRef | undefined,
|
|
315
|
+
): boolean {
|
|
316
|
+
const key = keyFor(callId, scope);
|
|
317
|
+
if (key === undefined) return false;
|
|
318
|
+
load();
|
|
319
|
+
const entry = entries.get(key);
|
|
320
|
+
if (!entry) return false;
|
|
321
|
+
entries.delete(key);
|
|
322
|
+
totalBytes -= entry.sig.length;
|
|
323
|
+
prune(Date.now());
|
|
324
|
+
void persist();
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Test seams: clear in-memory state and the loaded flag without touching the file. */
|
|
329
|
+
export function resetThoughtSignatureReplayForTests(): void {
|
|
330
|
+
entries = new Map();
|
|
331
|
+
totalBytes = 0;
|
|
332
|
+
loaded = false;
|
|
333
|
+
persistChain = Promise.resolve();
|
|
334
|
+
cachedSalt = undefined;
|
|
335
|
+
saltLoaded = false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function thoughtSignatureReplayCountForTests(): number {
|
|
339
|
+
return entries.size;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Test seam: resolve after the queued snapshot write settles. */
|
|
343
|
+
export function flushThoughtSignatureReplayForTests(): Promise<void> {
|
|
344
|
+
return persistChain;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Bounded commit barrier (#1926 gap 2). All durable writes serialize onto
|
|
349
|
+
* `persistChain`, so awaiting it (with a cap) before a turn's terminal frame becomes
|
|
350
|
+
* externally visible bounds the restart window in which a handed-out signature could
|
|
351
|
+
* be lost. Bounded BEST EFFORT: on timeout the turn proceeds — availability wins, and
|
|
352
|
+
* the miss cost is one turn's re-accumulation (the pre-store status quo). The
|
|
353
|
+
* in-memory map is updated synchronously at remember() time, so within one process
|
|
354
|
+
* lifetime replay never races this barrier at all.
|
|
355
|
+
*/
|
|
356
|
+
export function awaitThoughtSignatureDurability(capMs = 250): Promise<void> {
|
|
357
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
358
|
+
const cap = new Promise<void>(resolve => {
|
|
359
|
+
timer = setTimeout(resolve, capMs);
|
|
360
|
+
});
|
|
361
|
+
return Promise.race([persistChain, cap]).then(() => {
|
|
362
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
363
|
+
});
|
|
364
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
export const TOOL_SEARCH_FUNCTION_NAME = "tool_search";
|
|
2
|
+
export const TOOL_SEARCH_DEFAULT_DESCRIPTION = "Search for additional tools to load for the next turn.";
|
|
3
|
+
const TOOL_SEARCH_WIRE_ALIAS_PREFIX = "opencodex_tool_search";
|
|
4
|
+
|
|
5
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
6
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function toolSearchDescription(tool: unknown): string {
|
|
10
|
+
return isPlainObject(tool) && typeof tool.description === "string"
|
|
11
|
+
? tool.description
|
|
12
|
+
: TOOL_SEARCH_DEFAULT_DESCRIPTION;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function toolSearchParameters(tool: unknown): Record<string, unknown> {
|
|
16
|
+
if (isPlainObject(tool) && isPlainObject(tool.parameters)) return tool.parameters;
|
|
17
|
+
// Fresh per parse/build: downstream normalizers are allowed to clone or extend schemas, and a
|
|
18
|
+
// shared mutable default would couple otherwise unrelated requests.
|
|
19
|
+
return {
|
|
20
|
+
type: "object",
|
|
21
|
+
properties: {
|
|
22
|
+
query: { type: "string", description: "Search query for tools to load." },
|
|
23
|
+
limit: { type: "number", description: "Maximum number of tools to return." },
|
|
24
|
+
},
|
|
25
|
+
required: ["query"],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function collectToolDeclarationNames(tools: unknown[], names: Set<string>, namespace?: string): void {
|
|
30
|
+
for (const tool of tools) {
|
|
31
|
+
if (!isPlainObject(tool) || tool.type === "tool_search") continue;
|
|
32
|
+
if (tool.type === "function" && isPlainObject(tool.function) && typeof tool.function.name === "string") {
|
|
33
|
+
names.add(tool.function.name);
|
|
34
|
+
}
|
|
35
|
+
if (typeof tool.name === "string") {
|
|
36
|
+
names.add(tool.name);
|
|
37
|
+
if (namespace) names.add(`${namespace}__${tool.name}`);
|
|
38
|
+
}
|
|
39
|
+
if (tool.type === "namespace" && Array.isArray(tool.tools)) {
|
|
40
|
+
collectToolDeclarationNames(tool.tools, names, typeof tool.name === "string" ? tool.name : undefined);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function declaredToolNames(body: Record<string, unknown>): Set<string> {
|
|
46
|
+
const names = new Set<string>();
|
|
47
|
+
if (Array.isArray(body.tools)) collectToolDeclarationNames(body.tools, names);
|
|
48
|
+
if (Array.isArray(body.input)) {
|
|
49
|
+
for (const item of body.input) {
|
|
50
|
+
if (isPlainObject(item) && item.type === "additional_tools" && Array.isArray(item.tools)) {
|
|
51
|
+
collectToolDeclarationNames(item.tools, names);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return names;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function hasToolSearchDeclaration(tools: unknown[]): boolean {
|
|
59
|
+
return tools.some(tool => isPlainObject(tool) && tool.type === "tool_search");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function chooseToolSearchWireName(usedNames: ReadonlySet<string>): string {
|
|
63
|
+
if (!usedNames.has(TOOL_SEARCH_FUNCTION_NAME)) return TOOL_SEARCH_FUNCTION_NAME;
|
|
64
|
+
if (!usedNames.has(TOOL_SEARCH_WIRE_ALIAS_PREFIX)) return TOOL_SEARCH_WIRE_ALIAS_PREFIX;
|
|
65
|
+
for (let suffix = 2; ; suffix++) {
|
|
66
|
+
const candidate = `${TOOL_SEARCH_WIRE_ALIAS_PREFIX}_${suffix}`;
|
|
67
|
+
if (!usedNames.has(candidate)) return candidate;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function rewriteToolList(tools: unknown[], wireName: string): { tools: unknown[]; changed: boolean } {
|
|
72
|
+
let changed = false;
|
|
73
|
+
const rewritten = tools.map(tool => {
|
|
74
|
+
if (!isPlainObject(tool) || tool.type !== "tool_search") return tool;
|
|
75
|
+
const {
|
|
76
|
+
execution: _execution,
|
|
77
|
+
defer_loading: _deferLoading,
|
|
78
|
+
...rest
|
|
79
|
+
} = tool;
|
|
80
|
+
changed = true;
|
|
81
|
+
return {
|
|
82
|
+
...rest,
|
|
83
|
+
type: "function",
|
|
84
|
+
name: wireName,
|
|
85
|
+
description: toolSearchDescription(tool),
|
|
86
|
+
parameters: toolSearchParameters(tool),
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
return changed ? { tools: rewritten, changed: true } : { tools, changed: false };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function rewriteToolChoice(choice: unknown, wireName: string): unknown {
|
|
93
|
+
if (!isPlainObject(choice)) return choice;
|
|
94
|
+
if (choice.type === "tool_search") {
|
|
95
|
+
return { type: "function", name: wireName };
|
|
96
|
+
}
|
|
97
|
+
if (choice.type !== "allowed_tools" || !Array.isArray(choice.tools)) return choice;
|
|
98
|
+
let changed = false;
|
|
99
|
+
const tools = choice.tools.map(tool => {
|
|
100
|
+
if (!isPlainObject(tool) || tool.type !== "tool_search") return tool;
|
|
101
|
+
changed = true;
|
|
102
|
+
return { type: "function", name: wireName };
|
|
103
|
+
});
|
|
104
|
+
return changed ? { ...choice, tools } : choice;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function toolChoiceAllowsPrivateSearch(choice: unknown): boolean {
|
|
108
|
+
if (choice === undefined || choice === null || choice === "auto" || choice === "required") return true;
|
|
109
|
+
if (choice === "none") return false;
|
|
110
|
+
if (!isPlainObject(choice)) return true;
|
|
111
|
+
if (choice.type === "tool_search") return true;
|
|
112
|
+
if (choice.type === "allowed_tools" && Array.isArray(choice.tools)) {
|
|
113
|
+
return choice.tools.some(tool => isPlainObject(tool) && tool.type === "tool_search");
|
|
114
|
+
}
|
|
115
|
+
// A forced ordinary function named `tool_search` is distinct from the private tool kind.
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function upstreamToolSearchItemId(id: unknown): unknown {
|
|
120
|
+
if (typeof id !== "string") return id;
|
|
121
|
+
return id.startsWith("tsc_") ? `fc_${id.slice(4)}` : id;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function toolSearchArgumentsText(value: unknown): string {
|
|
125
|
+
if (typeof value === "string") return value;
|
|
126
|
+
return JSON.stringify(isPlainObject(value) ? value : {});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function toolSearchOutputText(value: Record<string, unknown>): string {
|
|
130
|
+
const payload: Record<string, unknown> = {
|
|
131
|
+
tools: Array.isArray(value.tools) ? value.tools : [],
|
|
132
|
+
};
|
|
133
|
+
if (typeof value.status === "string") payload.status = value.status;
|
|
134
|
+
return JSON.stringify(payload);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function rewriteHistoryItem(item: Record<string, unknown>, wireName: string): Record<string, unknown> {
|
|
138
|
+
if (item.type === "tool_search_call") {
|
|
139
|
+
const {
|
|
140
|
+
execution: _execution,
|
|
141
|
+
arguments: argumentsValue,
|
|
142
|
+
id,
|
|
143
|
+
...rest
|
|
144
|
+
} = item;
|
|
145
|
+
return {
|
|
146
|
+
...rest,
|
|
147
|
+
type: "function_call",
|
|
148
|
+
...(id === undefined ? {} : { id: upstreamToolSearchItemId(id) }),
|
|
149
|
+
name: wireName,
|
|
150
|
+
arguments: toolSearchArgumentsText(argumentsValue),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (item.type === "tool_search_output") {
|
|
154
|
+
const {
|
|
155
|
+
execution: _execution,
|
|
156
|
+
id: _id,
|
|
157
|
+
tools: _tools,
|
|
158
|
+
status: _status,
|
|
159
|
+
...rest
|
|
160
|
+
} = item;
|
|
161
|
+
return {
|
|
162
|
+
...rest,
|
|
163
|
+
type: "function_call_output",
|
|
164
|
+
output: toolSearchOutputText(item),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return item;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Third-party Responses gateways generally implement the public function-tool schema, not Codex's
|
|
172
|
+
* private client-executed `tool_search` declaration. Lower only request catalogs sent to a
|
|
173
|
+
* noncanonical upstream; the caller-facing response is restored separately.
|
|
174
|
+
*/
|
|
175
|
+
export function rewriteRoutedToolSearchForUpstream(body: unknown): {
|
|
176
|
+
body: unknown;
|
|
177
|
+
names: Set<string>;
|
|
178
|
+
} {
|
|
179
|
+
const names = new Set<string>();
|
|
180
|
+
if (!isPlainObject(body)) return { body, names };
|
|
181
|
+
|
|
182
|
+
const topLevelSearch = Array.isArray(body.tools) && hasToolSearchDeclaration(body.tools);
|
|
183
|
+
const inputItems = Array.isArray(body.input) ? body.input : [];
|
|
184
|
+
const additionalSearch = inputItems.some(item =>
|
|
185
|
+
isPlainObject(item)
|
|
186
|
+
&& item.type === "additional_tools"
|
|
187
|
+
&& Array.isArray(item.tools)
|
|
188
|
+
&& hasToolSearchDeclaration(item.tools));
|
|
189
|
+
const historySearch = inputItems.some(item =>
|
|
190
|
+
isPlainObject(item)
|
|
191
|
+
&& (item.type === "tool_search_call" || item.type === "tool_search_output"));
|
|
192
|
+
if (!topLevelSearch && !additionalSearch && !historySearch) return { body, names };
|
|
193
|
+
|
|
194
|
+
const wireName = chooseToolSearchWireName(declaredToolNames(body));
|
|
195
|
+
const declarationChanged = topLevelSearch || additionalSearch;
|
|
196
|
+
|
|
197
|
+
let tools = body.tools;
|
|
198
|
+
if (Array.isArray(tools)) {
|
|
199
|
+
const result = rewriteToolList(tools, wireName);
|
|
200
|
+
tools = result.tools;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let input = body.input;
|
|
204
|
+
let historyLowered = false;
|
|
205
|
+
if (Array.isArray(input)) {
|
|
206
|
+
input = input.map(item => {
|
|
207
|
+
if (!isPlainObject(item)) return item;
|
|
208
|
+
if (item.type === "additional_tools" && Array.isArray(item.tools)) {
|
|
209
|
+
const result = rewriteToolList(item.tools, wireName);
|
|
210
|
+
return result.changed ? { ...item, tools: result.tools } : item;
|
|
211
|
+
}
|
|
212
|
+
const rewritten = rewriteHistoryItem(item, wireName);
|
|
213
|
+
if (rewritten !== item) historyLowered = true;
|
|
214
|
+
return rewritten;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// A turn can replay `tool_search_call` history WITHOUT re-declaring the tool — Codex normally
|
|
219
|
+
// sends the declaration, but a history-only body is legal. The history is lowered to
|
|
220
|
+
// `function_call` either way, so restoration has to be armed on that too: leaving `names`
|
|
221
|
+
// empty there would hand the client a public `function_call` for what it issued as a private
|
|
222
|
+
// search call, and the round trip would silently stop matching.
|
|
223
|
+
if ((declarationChanged || historyLowered) && toolChoiceAllowsPrivateSearch(body.tool_choice)) {
|
|
224
|
+
names.add(wireName);
|
|
225
|
+
}
|
|
226
|
+
const toolChoice = declarationChanged ? rewriteToolChoice(body.tool_choice, wireName) : body.tool_choice;
|
|
227
|
+
return {
|
|
228
|
+
body: {
|
|
229
|
+
...body,
|
|
230
|
+
...(tools !== body.tools ? { tools } : {}),
|
|
231
|
+
...(input !== body.input ? { input } : {}),
|
|
232
|
+
...(toolChoice !== body.tool_choice ? { tool_choice: toolChoice } : {}),
|
|
233
|
+
},
|
|
234
|
+
names,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function toolSearchItemId(id: unknown): unknown {
|
|
239
|
+
if (typeof id !== "string") return id;
|
|
240
|
+
return id.startsWith("fc_") ? `tsc_${id.slice(3)}` : id;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function toolSearchArguments(value: unknown): Record<string, unknown> {
|
|
244
|
+
if (isPlainObject(value)) return value;
|
|
245
|
+
if (typeof value !== "string" || value.length === 0) return {};
|
|
246
|
+
try {
|
|
247
|
+
const parsed: unknown = JSON.parse(value);
|
|
248
|
+
return isPlainObject(parsed) ? parsed : {};
|
|
249
|
+
} catch {
|
|
250
|
+
return {};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function restoreRoutedToolSearchCalls(
|
|
255
|
+
value: unknown,
|
|
256
|
+
names: ReadonlySet<string>,
|
|
257
|
+
): { value: unknown; changed: boolean } {
|
|
258
|
+
if (Array.isArray(value)) {
|
|
259
|
+
let changed = false;
|
|
260
|
+
const restored = value.map(entry => {
|
|
261
|
+
const result = restoreRoutedToolSearchCalls(entry, names);
|
|
262
|
+
changed ||= result.changed;
|
|
263
|
+
return result.value;
|
|
264
|
+
});
|
|
265
|
+
return changed ? { value: restored, changed: true } : { value, changed: false };
|
|
266
|
+
}
|
|
267
|
+
if (!isPlainObject(value)) return { value, changed: false };
|
|
268
|
+
|
|
269
|
+
let changed = false;
|
|
270
|
+
const restored: Record<string, unknown> = {};
|
|
271
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
272
|
+
const result = restoreRoutedToolSearchCalls(entry, names);
|
|
273
|
+
restored[key] = result.value;
|
|
274
|
+
changed ||= result.changed;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (value.type === "function_call" && typeof value.name === "string" && names.has(value.name)) {
|
|
278
|
+
restored.type = "tool_search_call";
|
|
279
|
+
restored.id = toolSearchItemId(value.id);
|
|
280
|
+
restored.execution = "client";
|
|
281
|
+
restored.arguments = toolSearchArguments(value.arguments);
|
|
282
|
+
delete restored.name;
|
|
283
|
+
changed = true;
|
|
284
|
+
}
|
|
285
|
+
return changed ? { value: restored, changed: true } : { value, changed: false };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function restoreRoutedToolSearchCallsInJson(
|
|
289
|
+
text: string,
|
|
290
|
+
names: ReadonlySet<string>,
|
|
291
|
+
): string {
|
|
292
|
+
if (names.size === 0) return text;
|
|
293
|
+
let payload: unknown;
|
|
294
|
+
try {
|
|
295
|
+
payload = JSON.parse(text);
|
|
296
|
+
} catch {
|
|
297
|
+
return text;
|
|
298
|
+
}
|
|
299
|
+
const restored = restoreRoutedToolSearchCalls(payload, names);
|
|
300
|
+
return restored.changed ? JSON.stringify(restored.value) : text;
|
|
301
|
+
}
|