@bitkyc08/opencodex 2.86.0 → 2.88.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/gui/dist/assets/App-CHC89EXQ.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-B5bC7AWL.js} +1 -1
- package/gui/dist/assets/index-B5bsvxor.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-D8y2wTsB.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/adapters/anthropic/beta-allowlist.ts +9 -8
- package/src/adapters/anthropic/beta-body-pairs.ts +93 -0
- package/src/adapters/anthropic/passthrough.ts +7 -23
- package/src/adapters/anthropic-progress.ts +84 -0
- package/src/adapters/anthropic-rejection-memo.ts +71 -0
- package/src/adapters/anthropic-replay.ts +135 -0
- package/src/adapters/anthropic-tool-catalog.ts +11 -0
- package/src/adapters/anthropic.ts +151 -33
- package/src/adapters/base.ts +2 -0
- package/src/adapters/claude-cli/adapter.ts +111 -23
- package/src/adapters/claude-cli/profiles.ts +2 -1
- package/src/adapters/claude-cli/stable-replay.ts +108 -0
- package/src/adapters/claude-cli/usage-admission.ts +230 -0
- package/src/adapters/coding-agent/protocol.ts +1 -1
- package/src/adapters/coding-agent/turn.ts +18 -2
- package/src/adapters/dsh-account.ts +97 -0
- package/src/adapters/google-progress-history.ts +53 -0
- package/src/adapters/google.ts +52 -9
- package/src/adapters/openai-chat/messages.ts +4 -3
- package/src/adapters/openai-chat/passthrough.ts +8 -2
- package/src/adapters/openai-chat/reasoning-replay.ts +52 -0
- package/src/adapters/openai-responses/compaction-search-history.ts +14 -7
- package/src/adapters/openai-responses/passthrough.ts +57 -125
- package/src/adapters/openai-responses/request-strips.ts +25 -3
- package/src/adapters/openai-responses/transport.ts +81 -0
- package/src/adapters/qoder/adapter.ts +4 -2
- package/src/adapters/registry.ts +5 -0
- package/src/adapters/zed.ts +26 -1
- package/src/advisor/activity.ts +55 -0
- package/src/advisor/budget.ts +62 -0
- package/src/advisor/consult.ts +4 -1
- package/src/advisor/context.ts +16 -4
- package/src/advisor/evidence-tool.ts +21 -0
- package/src/advisor/evidence.ts +117 -0
- package/src/advisor/runtime.ts +79 -31
- package/src/advisor/settings.ts +11 -2
- package/src/bridge/errors.ts +19 -1
- package/src/bridge/internal.ts +7 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +59 -8
- package/src/bridge/terminal-refusal.ts +59 -0
- package/src/claude/desktop-3p-library.ts +32 -7
- package/src/claude/desktop-3p.ts +55 -13
- package/src/claude/desktop-first-party.ts +1 -1
- package/src/claude/desktop-picker-platform.ts +4 -0
- package/src/claude/desktop-picker.ts +15 -8
- package/src/claude/desktop-system-proxy.ts +50 -4
- package/src/claude/inbound-content-options.ts +77 -0
- package/src/claude/inbound.ts +33 -0
- package/src/claude/intercept/picker-ca-startup.ts +2 -1
- package/src/claude/intercept/picker-runtime.ts +6 -5
- package/src/claude/intercept/picker-trust-windows.ts +338 -0
- package/src/claude/intercept/picker-trust.ts +19 -0
- package/src/claude/intercept/port-policy.ts +30 -0
- package/src/claude/intercept/runtime.ts +97 -94
- package/src/claude/outbound.ts +46 -13
- package/src/cli/account-api.ts +1 -0
- package/src/cli/account-policy-dto.ts +6 -0
- package/src/cli/account-policy.ts +34 -5
- package/src/cli/account-preservation.ts +44 -0
- package/src/cli/account.ts +8 -2
- package/src/cli/advisor.ts +17 -3
- package/src/cli/capabilities-accounts.ts +20 -1
- package/src/cli/capabilities-agents-routing.ts +5 -2
- package/src/cli/capabilities-base.ts +38 -10
- package/src/cli/capabilities-observe-system.ts +63 -3
- package/src/cli/capabilities-provider-models.ts +11 -0
- package/src/cli/codex-cli-update.ts +151 -41
- package/src/cli/codex-provider.ts +44 -0
- package/src/cli/combo-input.ts +10 -1
- package/src/cli/combo-result.ts +1 -1
- package/src/cli/combo.ts +2 -1
- package/src/cli/cross-home-owner.ts +33 -16
- package/src/cli/dispatch.ts +25 -4
- package/src/cli/doctor.ts +6 -1
- package/src/cli/ensure-desktop-cold-start.ts +60 -7
- package/src/cli/help.ts +3 -2
- package/src/cli/index.ts +1 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/log-history.ts +172 -0
- package/src/cli/loopback-family.ts +31 -0
- package/src/cli/models-runtime-subcommands.ts +1 -0
- package/src/cli/models-runtime.ts +72 -8
- package/src/cli/observe-stream.ts +2 -0
- package/src/cli/observe.ts +16 -4
- package/src/cli/provider.ts +27 -5
- package/src/cli/registry.ts +28 -8
- package/src/cli/runtime-api.ts +5 -3
- package/src/cli/system-command.ts +40 -8
- package/src/cli/uninstall-client-state.ts +32 -0
- package/src/cli/usage-compare.ts +309 -0
- package/src/cli/usage-model-search.ts +4 -4
- package/src/cli/usage-report.ts +2 -2
- package/src/cli/v2.ts +1 -1
- package/src/client/machine-listener.ts +1 -3
- package/src/client/runtime.ts +1 -0
- package/src/clients/client-path.ts +51 -0
- package/src/clients/config-export/contracts.ts +2 -1
- package/src/clients/config-export.ts +18 -44
- package/src/clients/gentle-shell-path.ts +87 -0
- package/src/codex/account-auto-switch.ts +2 -0
- package/src/codex/account-identity.ts +29 -0
- package/src/codex/account-label.ts +1 -1
- package/src/codex/account-lifecycle.ts +21 -5
- package/src/codex/account-preservation.ts +28 -0
- package/src/codex/account-use-remaining.ts +23 -0
- package/src/codex/app-server-processes.ts +97 -7
- package/src/codex/app-server-restart-service.ts +55 -4
- package/src/codex/auth-api/account-list.ts +4 -0
- package/src/codex/auth-api/account-pause-group.ts +6 -50
- package/src/codex/auth-api/account-preservation.ts +57 -0
- package/src/codex/auth-api/routes.ts +8 -0
- package/src/codex/auth-api/use-remaining-route.ts +36 -0
- package/src/codex/auth-api.ts +0 -1
- package/src/codex/auth-context.ts +2 -2
- package/src/codex/catalog/auto-review.ts +20 -3
- package/src/codex/catalog/build-entries.ts +9 -1
- package/src/codex/catalog/combo-member.ts +14 -5
- package/src/codex/catalog/configured-tiers.ts +1 -1
- package/src/codex/catalog/gather-capture.ts +2 -0
- package/src/codex/catalog/metadata.ts +11 -9
- package/src/codex/catalog/model-hints.ts +21 -16
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/parsing.ts +30 -86
- package/src/codex/catalog/provider-models.ts +36 -34
- package/src/codex/catalog/routed-gather.ts +14 -13
- package/src/codex/cli-install-provenance.ts +105 -17
- package/src/codex/cli-update-plan.ts +545 -0
- package/src/codex/config-write-lock.ts +23 -2
- package/src/codex/desktop-app/picker-freshness.ts +96 -0
- package/src/codex/desktop-app-restart.ts +21 -0
- package/src/codex/desktop-compatibility/installed-build.ts +24 -6
- package/src/codex/desktop-compatibility/routing-preflight.ts +1 -1
- package/src/codex/desktop-compatibility/runtime.ts +12 -3
- package/src/codex/desktop-compatibility/windows-key-protection.ts +115 -9
- package/src/codex/features.ts +85 -45
- package/src/codex/inject/history-failure.ts +27 -0
- package/src/codex/inject/restore.ts +24 -2
- package/src/codex/inject/routing-classify.ts +4 -3
- package/src/codex/inject.ts +18 -27
- package/src/codex/main-account-hard-lock.ts +30 -1
- package/src/codex/native-profile-startup.ts +1 -1
- package/src/codex/ocx-compaction-history.ts +12 -10
- package/src/codex/paths.ts +86 -1
- package/src/codex/provider-routing.ts +174 -0
- package/src/codex/refresh.ts +2 -2
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/selection.ts +4 -3
- package/src/codex/routing/thread-affinity.ts +1 -1
- package/src/codex/signed-in-identity.ts +43 -0
- package/src/codex/subagent-model-fallback.ts +1 -1
- package/src/codex/sync.ts +6 -1
- package/src/codex/user-identity.ts +16 -5
- package/src/codex/websocket-activity.ts +161 -0
- package/src/codex/websocket-registry.ts +25 -1
- package/src/codex/windows-local-appdata.ts +84 -0
- package/src/combos/failover.ts +6 -1
- package/src/combos/index.ts +1 -0
- package/src/combos/resolve.ts +46 -10
- package/src/combos/types.ts +15 -0
- package/src/config/diagnostics.ts +33 -6
- package/src/config/live-reconcile.ts +38 -2
- package/src/config/load-degrade.ts +17 -0
- package/src/config/opencode-wire-upgrade.ts +54 -0
- package/src/config/opencode-zen-upgrade.ts +53 -0
- package/src/config/owner-registry.ts +22 -17
- package/src/config/provider-validation.ts +45 -2
- package/src/config/schema/config-schema.ts +22 -0
- package/src/config/schema/leaf-validators.ts +38 -9
- package/src/config/schema/main-account-hard-lock-fallback.ts +26 -0
- package/src/config/schema/native-reasoning-retention.ts +26 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +657 -297
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +116 -27
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/mutation-plan.ts +1 -0
- package/src/integrations/registry.ts +8 -0
- package/src/lib/codex-restart-contract.ts +15 -0
- package/src/lib/desktop-upstream-tunnel.ts +18 -53
- package/src/lib/empty-tool-catalog.ts +62 -0
- package/src/lib/errors.ts +19 -3
- package/src/lib/gcp-adc.ts +96 -9
- package/src/lib/gcp-credential-input.ts +12 -0
- package/src/lib/lab-live-route-production.ts +5 -0
- package/src/lib/opaque-proxy-tunnel.ts +217 -0
- package/src/lib/redact.ts +2 -2
- package/src/lib/request-failure-model.ts +2 -0
- package/src/oauth/account-quota-rank.ts +69 -2
- package/src/oauth/anthropic-account-refusal.ts +20 -2
- package/src/oauth/anthropic-oauth-definitions.ts +3 -1
- package/src/oauth/anthropic-routing.ts +1 -21
- package/src/oauth/anthropic.ts +38 -7
- package/src/oauth/callback-server.ts +27 -1
- package/src/oauth/chatgpt-device.ts +1 -1
- package/src/oauth/chatgpt-shared.ts +83 -0
- package/src/oauth/chatgpt.ts +8 -72
- package/src/oauth/dsh.ts +203 -0
- package/src/oauth/generic-account-failover.ts +40 -11
- package/src/oauth/generic-session-affinity.ts +125 -0
- package/src/oauth/index.ts +184 -109
- package/src/oauth/local-token-detect.ts +21 -1
- package/src/oauth/login-cli-errors.ts +44 -0
- package/src/oauth/login-cli.ts +69 -7
- package/src/oauth/login-flow-state.ts +371 -23
- package/src/oauth/meta-muse-errors.ts +41 -0
- package/src/oauth/meta-muse-keychain.ts +52 -0
- package/src/oauth/meta-muse.ts +51 -84
- package/src/oauth/model-discovery-auth.ts +3 -0
- package/src/oauth/pool-settings-capability.ts +4 -2
- package/src/oauth/refresh-flight-retirement.ts +48 -0
- package/src/oauth/session-affinity-key.ts +23 -0
- package/src/oauth/store-anthropic-instance.ts +9 -2
- package/src/oauth/store.ts +12 -5
- package/src/protocols/encoders/adapter-events.ts +6 -5
- package/src/protocols/encoders/messages.ts +9 -6
- package/src/protocols/provider-summary.ts +2 -1
- package/src/providers/api-key-cooldowns.ts +49 -0
- package/src/providers/api-key-resolve.ts +11 -6
- package/src/providers/api-key-response-ownership.ts +57 -0
- package/src/providers/api-key-selection.ts +31 -6
- package/src/providers/api-keys.ts +214 -3
- package/src/providers/claude-cli-usage.ts +96 -0
- package/src/providers/dsh-account-destination.ts +31 -0
- package/src/providers/dsh-account-models.ts +41 -0
- package/src/providers/gcp-credential-cleanup.ts +59 -0
- package/src/providers/key-failover.ts +54 -50
- package/src/providers/key-store.ts +7 -2
- package/src/providers/model-rename-fields.ts +3 -0
- package/src/providers/model-rename-migration.ts +2 -2
- package/src/providers/openai-tiers-destination.ts +11 -2
- package/src/providers/openai-tiers.ts +5 -12
- package/src/providers/opencode-go-transport.ts +3 -1
- package/src/providers/opencode-zen-rate-limit.ts +7 -2
- package/src/providers/quota/account-cache.ts +6 -5
- package/src/providers/quota/account-row.ts +34 -0
- package/src/providers/quota/antigravity.ts +28 -7
- package/src/providers/quota/dsh-account.ts +70 -0
- package/src/providers/quota/vendor-probes-oauth.ts +1 -93
- package/src/providers/quota-types.ts +2 -0
- package/src/providers/quota.ts +14 -20
- package/src/providers/registry/entries-core.ts +37 -8
- package/src/providers/registry/types.ts +1 -1
- package/src/providers/request-pacing.ts +77 -19
- package/src/providers/routed-config.ts +339 -0
- package/src/providers/service-tier.ts +2 -1
- package/src/quota/window-mapping.ts +5 -2
- package/src/responses/anthropic-progress.ts +34 -0
- package/src/responses/assistant-replay.ts +35 -0
- package/src/responses/compaction-history-framing.ts +65 -0
- package/src/responses/compaction.ts +6 -1
- package/src/responses/incomplete-reason.ts +31 -0
- package/src/responses/parser.ts +50 -5
- package/src/responses/reasoning-envelope.ts +4 -0
- package/src/responses/reasoning-replay-cache.ts +74 -6
- package/src/responses/reasoning-retention.ts +173 -0
- package/src/responses/retained-compaction.ts +46 -0
- package/src/responses/schema.ts +23 -0
- package/src/router.ts +8 -321
- package/src/routing/analytics.ts +7 -15
- package/src/routing/health.ts +37 -22
- package/src/routing/history/indexer.ts +6 -5
- package/src/routing/history/ingest.ts +6 -2
- package/src/routing/profile.ts +1 -1
- package/src/server/admission-model-scope.ts +17 -0
- package/src/server/auth-cors.ts +11 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native-reasoning.ts +123 -0
- package/src/server/chat-native-sse.ts +110 -7
- package/src/server/chat-native.ts +76 -17
- package/src/server/claude-messages.ts +56 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -2
- package/src/server/index/live-sideband.ts +3 -2
- package/src/server/index/model-alias-id.ts +36 -0
- package/src/server/index/optional-listeners.ts +2 -0
- package/src/server/index/serve-options.ts +20 -6
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index/websocket-handler.ts +18 -4
- package/src/server/index.ts +8 -8
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- package/src/server/lifecycle.ts +13 -1
- package/src/server/management/advisor-routes.ts +15 -4
- package/src/server/management/claude-desktop-picker-routes.ts +1 -1
- package/src/server/management/combo-routes.ts +176 -50
- package/src/server/management/companion-routes.ts +1 -1
- package/src/server/management/config-routes.ts +75 -4
- package/src/server/management/cursor-integration-routes.ts +1 -1
- package/src/server/management/model-routes.ts +11 -6
- package/src/server/management/model-rows.ts +14 -0
- package/src/server/management/native-integration-routes.ts +2 -7
- package/src/server/management/native-reasoning-retention-routes.ts +41 -0
- package/src/server/management/oauth-account-routes.ts +87 -14
- package/src/server/management/provider-gcp-credential-routes.ts +29 -0
- package/src/server/management/provider-key-pacing-routes.ts +31 -0
- package/src/server/management/provider-overwrite-carry.ts +7 -0
- package/src/server/management/provider-routes.ts +25 -30
- package/src/server/management/route-registry.ts +10 -1
- package/src/server/management-api.ts +3 -1
- package/src/server/messages-native-tool-guard.ts +185 -0
- package/src/server/messages-native.ts +117 -24
- package/src/server/messages-shared.ts +431 -0
- package/src/server/relay.ts +1 -1
- package/src/server/request-log-terminal-status.ts +1 -1
- package/src/server/request-log.ts +6 -1
- package/src/server/responses/account-change-state.ts +8 -2
- package/src/server/responses/adapter-continuation.ts +47 -19
- package/src/server/responses/adapter-delivery.ts +72 -12
- package/src/server/responses/adapter-dispatch.ts +46 -3
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/anthropic-thinking-recovery.ts +153 -0
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/combo-stream-preflight.ts +3 -2
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +20 -10
- package/src/server/responses/compaction-retention.ts +61 -0
- package/src/server/responses/context-overflow.ts +11 -6
- package/src/server/responses/core-auth.ts +6 -0
- package/src/server/responses/core-codex-account.ts +11 -1
- package/src/server/responses/core-combo-failure.ts +2 -0
- package/src/server/responses/core-combo.ts +121 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +8 -4
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +7 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +23 -3
- package/src/server/responses/core.ts +2 -115
- package/src/server/responses/fetch-helpers.ts +73 -19
- package/src/server/responses/main-hard-lock-routing.ts +109 -0
- package/src/server/responses/native-response-control.ts +4 -2
- package/src/server/responses/passthrough-delivery.ts +3 -1
- package/src/server/responses/passthrough-dispatch.ts +15 -13
- package/src/server/responses/passthrough-execution.ts +3 -2
- package/src/server/responses/request-prepare.ts +77 -12
- package/src/server/responses/request-sidecar-auth.ts +2 -2
- package/src/server/responses/request-transport.ts +104 -27
- package/src/server/responses/response-effects.ts +2 -0
- package/src/server/responses/response-tool-ownership.ts +21 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +63 -9
- package/src/server/responses/sidecar-send-budget.ts +2 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- package/src/server/responses-undeclared-tool-guard.ts +1 -1
- package/src/server/ws-bridge.ts +2 -0
- package/src/service/auth-preflight.ts +71 -0
- package/src/service/cli.ts +4 -2
- package/src/service/definition.ts +97 -0
- package/src/service/guards.ts +1 -61
- package/src/service/health-sentinel-linux.ts +112 -0
- package/src/service/health-sentinel-main.ts +76 -57
- package/src/service/health.ts +5 -89
- package/src/service/launchd.ts +27 -7
- package/src/service/logging.ts +72 -9
- package/src/service/repair.ts +2 -1
- package/src/service/state.ts +7 -8
- package/src/service/systemd.ts +1 -1
- package/src/service/windows-guarded-manager.ts +2 -1
- package/src/service/windows-ops.ts +1 -1
- package/src/service/windows-scheduler.ts +2 -7
- package/src/service/windows-taskxml.ts +2 -2
- package/src/service/windows-wscript.ts +14 -0
- package/src/service.ts +4 -2
- package/src/sidecar/auth.ts +2 -0
- package/src/types/config.ts +64 -3
- package/src/types/opencode-go.ts +61 -0
- package/src/types/opencode-zen.ts +35 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +31 -2
- package/src/types/wire.ts +31 -37
- package/src/types.ts +2 -0
- package/src/update/npm-invocation.mjs +12 -2
- package/src/usage/cost.ts +48 -12
- package/src/usage/expected-prices.ts +3 -0
- package/src/usage/ledger-retention.ts +2 -1
- package/src/usage/ledger-scanner.ts +6 -5
- package/src/usage/log.ts +17 -8
- package/src/usage/summary.ts +30 -11
- package/src/usage/telemetry-contract.ts +1 -0
- package/src/usage/user-cost-overlays.ts +14 -19
- package/src/usage/user-cost-validation.ts +38 -0
- package/src/vision/eligibility.ts +13 -0
- package/src/web-search/anthropic-history.ts +59 -0
- package/src/web-search/loop.ts +236 -73
- package/src/web-search/progress-stream.ts +15 -2
- package/src/web-search/run-turn-loop.ts +9 -1
- package/gui/dist/assets/App-BjlttVLy.js +0 -58
- package/gui/dist/assets/App-CSJvmjNs.css +0 -1
- package/gui/dist/assets/index-BB6uK6tU.css +0 -1
- package/gui/dist/assets/index-BMssP4k3.js +0 -94
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +0 -1
|
@@ -23,7 +23,11 @@ import { resolveStallTimeoutMs } from "../../stall-timeout";
|
|
|
23
23
|
import { clientEncoderForDelivery, deliverClientEncodedResponse } from "../inference/client-encoder-delivery";
|
|
24
24
|
import { noteKiroServedSuccess } from "../../providers/kiro-usage";
|
|
25
25
|
import { persistKiroAccountState } from "../../providers/kiro-account-state-disk";
|
|
26
|
+
import { readDisplaySafeErrorText, normalizeUpstreamErrorText } from "./core-errors";
|
|
27
|
+
import { sanitizeNonReplayableUpstreamError } from "./non-replayable-error";
|
|
28
|
+
import { isNonReplayableResponse } from "../../lib/upstream-retry";
|
|
26
29
|
import { isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers";
|
|
30
|
+
import { guardEmptyToolCatalogEvents, guardEmptyToolCatalogStream } from "../../lib/empty-tool-catalog";
|
|
27
31
|
|
|
28
32
|
/** One responsibility of the Responses request pipeline; state owners are explicit. */
|
|
29
33
|
export async function deliverAdapterResponse(
|
|
@@ -34,6 +38,7 @@ export async function deliverAdapterResponse(
|
|
|
34
38
|
| "route"
|
|
35
39
|
| "translatorBudget"
|
|
36
40
|
| "toolBridgeMaps"
|
|
41
|
+
| "clientToolCatalogRestriction"
|
|
37
42
|
| "rememberKiroDeliveredFinalAnswer"
|
|
38
43
|
| "responseStateOptions"
|
|
39
44
|
>,
|
|
@@ -47,7 +52,7 @@ export async function deliverAdapterResponse(
|
|
|
47
52
|
| "notifyResponseComplete"
|
|
48
53
|
>,
|
|
49
54
|
completionPolicy: Pick<ResponsesCompletionPolicy, "emptyCompletionGuardEnabled">,
|
|
50
|
-
adapterExchange: Pick<AdapterExchange, "upstreamResponse" | "upstream" | "cleanupUpstreamAbort" | "localUpstream">,
|
|
55
|
+
adapterExchange: Pick<AdapterExchange, "upstreamResponse" | "upstream" | "cleanupUpstreamAbort" | "localUpstream" | "thinkingSignatureRecovery" | "refetchThinkingSignature">,
|
|
51
56
|
continuationState: Pick<AdapterContinuations, "terminalGuardEnabled" | "fetchTerminalGuardContinuation" | "fetchGuardedEmptyCompletionRetry">,
|
|
52
57
|
): Promise<Response> {
|
|
53
58
|
const { logCtx, options, config } = requestContext;
|
|
@@ -59,7 +64,7 @@ export async function deliverAdapterResponse(
|
|
|
59
64
|
rememberKiroDeliveredFinalAnswer,
|
|
60
65
|
responseStateOptions,
|
|
61
66
|
} = requestState;
|
|
62
|
-
const { upstreamResponse, upstream, cleanupUpstreamAbort, localUpstream } = adapterExchange;
|
|
67
|
+
const { upstreamResponse, upstream, cleanupUpstreamAbort, localUpstream, thinkingSignatureRecovery, refetchThinkingSignature } = adapterExchange;
|
|
63
68
|
const {
|
|
64
69
|
terminalGuardEnabled,
|
|
65
70
|
fetchTerminalGuardContinuation,
|
|
@@ -75,6 +80,47 @@ export async function deliverAdapterResponse(
|
|
|
75
80
|
const { routedCompaction } = sidecarState;
|
|
76
81
|
const bodyInactivityMs = resolveStallTimeoutMs(config.stallTimeoutSec, { localUpstream });
|
|
77
82
|
const upstreamRequestsStream = parsed.stream || isCanonicalOpenAiForwardProvider(route.provider);
|
|
83
|
+
const recoverThinkingSignature = (events: AsyncIterable<AdapterEvent>): AsyncIterable<AdapterEvent> =>
|
|
84
|
+
thinkingSignatureRecovery.wrap(events, async function* () {
|
|
85
|
+
const result = await refetchThinkingSignature();
|
|
86
|
+
let response = "failed" in result ? result.failed : result;
|
|
87
|
+
// A replacement can be an HTTP/auth/budget refusal; never feed its JSON body to an SSE parser.
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
// A local refusal closes the upstream signal before returning its generated JSON.
|
|
90
|
+
// Reading that bounded local body still honors caller cancellation.
|
|
91
|
+
const errorSignal = "failed" in result ? options.abortSignal ?? new AbortController().signal : upstream.signal;
|
|
92
|
+
const nonReplayable = isNonReplayableResponse(response);
|
|
93
|
+
if (nonReplayable) response = await sanitizeNonReplayableUpstreamError(response, errorSignal);
|
|
94
|
+
const body = await readDisplaySafeErrorText(response, errorSignal, "Upstream recovery failed");
|
|
95
|
+
const error = normalizeUpstreamErrorText(body, "Upstream recovery failed");
|
|
96
|
+
yield { type: "error", status: response.status, message: error.message ?? error.safeText,
|
|
97
|
+
errorType: error.type ?? "upstream_error", ...(error.code ? { code: error.code } : {}),
|
|
98
|
+
...(nonReplayable ? { retryable: false } : {}) };
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const retryEvents = upstreamRequestsStream
|
|
103
|
+
? readResponseStreamWithInactivity(response, upstream.signal, bodyInactivityMs,
|
|
104
|
+
guarded => transportState.activeAdapter.parseStream(guarded, translatorBudget, logCtx.activeTierMetadata))
|
|
105
|
+
: (async function* () {
|
|
106
|
+
yield* await readResponseBodyWithInactivity(response, upstream.signal, bodyInactivityMs,
|
|
107
|
+
guarded => transportState.activeAdapter.parseResponse!(guarded, translatorBudget, logCtx.activeTierMetadata));
|
|
108
|
+
})();
|
|
109
|
+
for await (const event of retryEvents) {
|
|
110
|
+
options.onCompactionRecoveryAdapterEvent?.(event);
|
|
111
|
+
yield event;
|
|
112
|
+
}
|
|
113
|
+
} catch (error) {
|
|
114
|
+
if (!(error instanceof ResponseBodyInactivityError)) throw error;
|
|
115
|
+
yield { type: "error", message: "Upstream recovery response body stalled before completing",
|
|
116
|
+
status: 504, errorType: "upstream_error" };
|
|
117
|
+
}
|
|
118
|
+
}, () => transportState.activeAdapter.name, !isNonReplayableResponse(upstreamResponse), upstreamResponse);
|
|
119
|
+
|
|
120
|
+
const clientToolCatalog = requestState.clientToolCatalogRestriction && route.provider.authMode !== "forward"
|
|
121
|
+
? requestState.clientToolCatalogRestriction : undefined;
|
|
122
|
+
const deliveryToolBridgeMaps = clientToolCatalog
|
|
123
|
+
? { ...toolBridgeMaps, ...clientToolCatalog } : toolBridgeMaps;
|
|
78
124
|
|
|
79
125
|
|
|
80
126
|
if (parsed.stream) {
|
|
@@ -104,15 +150,16 @@ export async function deliverAdapterResponse(
|
|
|
104
150
|
throw error;
|
|
105
151
|
}
|
|
106
152
|
})();
|
|
153
|
+
const recoveredEvents = recoverThinkingSignature(initialEventStream);
|
|
107
154
|
const eventStream = terminalGuardEnabled
|
|
108
155
|
? guardTerminalEventStream({
|
|
109
156
|
parsed,
|
|
110
|
-
firstEvents:
|
|
157
|
+
firstEvents: recoveredEvents,
|
|
111
158
|
adapterName: transportState.activeAdapter.name,
|
|
112
159
|
maxAutoContinuations: 1,
|
|
113
160
|
continuation: next => fetchTerminalGuardContinuation(next, undefined, !parsed.stream),
|
|
114
161
|
})
|
|
115
|
-
:
|
|
162
|
+
: recoveredEvents;
|
|
116
163
|
// Optional advisor guard (registered per request by the sidecar planner; absent unless the
|
|
117
164
|
// user enabled the advisor): holds synthetic `advisor` tool calls, consults the configured
|
|
118
165
|
// expert model, and re-dispatches the worker. Sits inside the empty-completion guard so an
|
|
@@ -121,6 +168,7 @@ export async function deliverAdapterResponse(
|
|
|
121
168
|
const advisorStream = parsed._advisorGuard
|
|
122
169
|
? parsed._advisorGuard({
|
|
123
170
|
parsed,
|
|
171
|
+
translatorBudget,
|
|
124
172
|
firstEvents: eventStream,
|
|
125
173
|
continuation: fetchTerminalGuardContinuation,
|
|
126
174
|
})
|
|
@@ -129,16 +177,18 @@ export async function deliverAdapterResponse(
|
|
|
129
177
|
// turn with no text and no tool call is retried with the IDENTICAL request
|
|
130
178
|
// (fetchTerminalGuardContinuation(parsed) replays the cached byte-identical
|
|
131
179
|
// request — same body, same headers, same signal).
|
|
132
|
-
const
|
|
180
|
+
const completionGuardedEventStream = emptyCompletionGuardEnabled
|
|
133
181
|
? guardEmptyCompletionEventStream({
|
|
134
182
|
firstEvents: advisorStream,
|
|
135
183
|
continuation: fetchGuardedEmptyCompletionRetry,
|
|
136
184
|
})
|
|
137
185
|
: advisorStream;
|
|
138
|
-
const
|
|
186
|
+
const guardedEventStream = guardEmptyToolCatalogStream(completionGuardedEventStream, clientToolCatalog, translatorBudget);
|
|
187
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, bareCustomToolNames, toolSearchToolNames } = deliveryToolBridgeMaps;
|
|
139
188
|
// One completion owner for both deliveries: the bridge calls it from its terminal, the
|
|
140
189
|
// direct client encoder from the fold of the same events.
|
|
141
190
|
const onCompletedResponse = (response: Record<string, unknown>, providerState?: OcxProviderContinuationState) => {
|
|
191
|
+
if (!options.abortSignal?.aborted) thinkingSignatureRecovery.rememberSuccess(response);
|
|
142
192
|
const served = transportState.sentOAuthSnapshot;
|
|
143
193
|
if (transportState.activeAdapter.name === "kiro" && response.status === "completed" && served
|
|
144
194
|
&& noteKiroServedSuccess(served.accountId, served.generation)) persistKiroAccountState();
|
|
@@ -157,7 +207,9 @@ export async function deliverAdapterResponse(
|
|
|
157
207
|
}
|
|
158
208
|
notifyResponseComplete(response);
|
|
159
209
|
};
|
|
160
|
-
|
|
210
|
+
// The Responses bridge owns code-mode helper compilation; the direct encoder only renames.
|
|
211
|
+
const clientEncoder = clientToolCatalog?.bareCustomToolNames.has("exec") ? undefined
|
|
212
|
+
: clientEncoderForDelivery(options, logCtx, !!routedCompaction, transportState.activeAdapter.name);
|
|
161
213
|
if (clientEncoder) {
|
|
162
214
|
return deliverClientEncodedResponse({
|
|
163
215
|
encoder: clientEncoder,
|
|
@@ -196,9 +248,10 @@ export async function deliverAdapterResponse(
|
|
|
196
248
|
declaredToolNames,
|
|
197
249
|
bareCustomToolNames,
|
|
198
250
|
enforceDeclaredToolNames: options.inboundWire !== "chat" && options.inboundWire !== "anthropic",
|
|
251
|
+
terminalRefusalAsFailed: options.inboundWire !== "chat" && options.inboundWire !== "anthropic",
|
|
199
252
|
toolParameterSchemas,
|
|
200
253
|
...(options.onFirstOutput ? { onFirstOutput: options.onFirstOutput } : {}),
|
|
201
|
-
...(routedCompaction ? { compaction: true } : {}),
|
|
254
|
+
...(routedCompaction ? { compaction: true, compactionRetention: parsed._compactionRetention } : {}),
|
|
202
255
|
// Same grok-surface split as the runTurn branch above.
|
|
203
256
|
...(logCtx.surface === "grok" ? { heartbeatStyle: "comment" as const } : {}),
|
|
204
257
|
onUsage: usage => {
|
|
@@ -239,24 +292,29 @@ export async function deliverAdapterResponse(
|
|
|
239
292
|
));
|
|
240
293
|
}
|
|
241
294
|
for (const event of initialEvents) options.onCompactionRecoveryAdapterEvent?.(event);
|
|
295
|
+
const recoveredEvents: AdapterEvent[] = [];
|
|
296
|
+
for await (const event of recoverThinkingSignature((async function* () { yield* initialEvents; })())) {
|
|
297
|
+
recoveredEvents.push(event);
|
|
298
|
+
}
|
|
242
299
|
let guardedEvents: AdapterEvent[];
|
|
243
300
|
if (terminalGuardEnabled) {
|
|
244
301
|
guardedEvents = [];
|
|
245
302
|
for await (const event of guardTerminalEventStream({
|
|
246
303
|
parsed,
|
|
247
|
-
firstEvents: (async function* () { yield*
|
|
304
|
+
firstEvents: (async function* () { yield* recoveredEvents; })(),
|
|
248
305
|
adapterName: transportState.activeAdapter.name,
|
|
249
306
|
maxAutoContinuations: 1,
|
|
250
307
|
continuation: next => fetchTerminalGuardContinuation(next, undefined, !parsed.stream),
|
|
251
308
|
})) guardedEvents.push(event);
|
|
252
309
|
} else {
|
|
253
|
-
guardedEvents =
|
|
310
|
+
guardedEvents = recoveredEvents;
|
|
254
311
|
}
|
|
255
312
|
// Optional advisor guard — same semantics as the streaming branch above.
|
|
256
313
|
if (parsed._advisorGuard) {
|
|
257
314
|
const advisorEvents: AdapterEvent[] = [];
|
|
258
315
|
for await (const event of parsed._advisorGuard({
|
|
259
316
|
parsed,
|
|
317
|
+
translatorBudget,
|
|
260
318
|
firstEvents: (async function* () { yield* guardedEvents; })(),
|
|
261
319
|
continuation: fetchTerminalGuardContinuation,
|
|
262
320
|
})) advisorEvents.push(event);
|
|
@@ -279,8 +337,9 @@ export async function deliverAdapterResponse(
|
|
|
279
337
|
} finally {
|
|
280
338
|
cleanupUpstreamAbort();
|
|
281
339
|
}
|
|
282
|
-
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, bareCustomToolNames, toolSearchToolNames } =
|
|
340
|
+
const { toolNsMap, declaredToolNames, toolParameterSchemas, freeformToolNames, bareCustomToolNames, toolSearchToolNames } = deliveryToolBridgeMaps;
|
|
283
341
|
let providerState: OcxProviderContinuationState | undefined;
|
|
342
|
+
events = guardEmptyToolCatalogEvents(events, clientToolCatalog, translatorBudget);
|
|
284
343
|
const json = buildResponseJSON(events, parsed._responseModelId ?? parsed.modelId, {
|
|
285
344
|
translatorBudget,
|
|
286
345
|
replayCacheScope: parsed._reasoningReplayScope,
|
|
@@ -293,7 +352,7 @@ export async function deliverAdapterResponse(
|
|
|
293
352
|
toolParameterSchemas,
|
|
294
353
|
freeformToolNames,
|
|
295
354
|
toolSearchToolNames,
|
|
296
|
-
...(routedCompaction ? { compaction: true } : {}),
|
|
355
|
+
...(routedCompaction ? { compaction: true, compactionRetention: parsed._compactionRetention } : {}),
|
|
297
356
|
onProviderState: state => { providerState = state; },
|
|
298
357
|
onUsage: usage => {
|
|
299
358
|
transportState.bindKeyUsageFromBridge(usage);
|
|
@@ -312,6 +371,7 @@ export async function deliverAdapterResponse(
|
|
|
312
371
|
// #1926 gap 2: same buffered-path durability bound as the primary branch.
|
|
313
372
|
await awaitThoughtSignatureDurability();
|
|
314
373
|
if (adapterResponseReachedServingTerminal(events, json)) {
|
|
374
|
+
if (!options.abortSignal?.aborted) thinkingSignatureRecovery.rememberSuccess(json);
|
|
315
375
|
const served = transportState.sentOAuthSnapshot;
|
|
316
376
|
if (transportState.activeAdapter.name === "kiro" && json.status === "completed" && served
|
|
317
377
|
&& noteKiroServedSuccess(served.accountId, served.generation)) persistKiroAccountState();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bindAnthropicRejectionResponse } from "../../adapters/anthropic-rejection-memo";
|
|
1
2
|
import { deferPhysicalSendReporter } from "./deferred-send-reporter";
|
|
2
3
|
import { spendLedgerStorageErrorResponse } from "./spend-storage-error";
|
|
3
4
|
import { classifyAnthropic429 } from "../../oauth/anthropic-rate-limit-policy";
|
|
@@ -54,7 +55,7 @@ import { describeUpstreamConnectFailure } from "./upstream-error";
|
|
|
54
55
|
import type { OpaqueBlobRecoveryGuard } from "./core-opaque-recovery";
|
|
55
56
|
import type { AttemptRecoveryKind } from "../../usage/log";
|
|
56
57
|
import type { OAuthAccessSnapshot } from "../../oauth";
|
|
57
|
-
import { OAuthAccountPausedError, OAuthLoginRequiredError, publicOAuthAuthenticationErrorMessage } from "../../oauth";
|
|
58
|
+
import { OAuthAccountPausedError, OAuthLoginRequiredError, OAuthTokenRefreshBusyError, publicOAuthAuthenticationErrorMessage } from "../../oauth";
|
|
58
59
|
import { getAccountSet, markAccountNeedsReauthIfGeneration } from "../../oauth/store";
|
|
59
60
|
import { tryKiroAlternateAfterTerminalRefresh } from "../../oauth/kiro-terminal-failover";
|
|
60
61
|
import { classifyKiroRefusal } from "../../adapters/kiro-refusal";
|
|
@@ -87,6 +88,7 @@ import {
|
|
|
87
88
|
reasoningEffortRejectionText,
|
|
88
89
|
anthropicFastRefused,
|
|
89
90
|
} from "./core-opaque-recovery";
|
|
91
|
+
import { createAnthropicThinkingRecovery } from "./anthropic-thinking-recovery";
|
|
90
92
|
import { shouldAttemptImageTierRetry } from "../image-retry";
|
|
91
93
|
import {
|
|
92
94
|
isTransientConsoleGoUploadRejection,
|
|
@@ -591,7 +593,7 @@ export async function prepareAdapterExchange(
|
|
|
591
593
|
let rateLimitRetries = 0;
|
|
592
594
|
// Shared with the terminal-guard continuation below: an image-tier reduction that let the
|
|
593
595
|
// main request clear a 413 must not be forgotten on the very next continuation build.
|
|
594
|
-
|
|
596
|
+
// Recovery helpers also serve initial HTTP-200 adapter errors during delivery.
|
|
595
597
|
// Recovery loop: multi-key 429 failover + at most ONE opaque-state rebuild and ONE
|
|
596
598
|
// anthropic 413 tightened retry
|
|
597
599
|
// (devlog/260714_image_normalization_pipeline/030). One mutable activeAdapter serves
|
|
@@ -600,6 +602,9 @@ export async function prepareAdapterExchange(
|
|
|
600
602
|
// 413→429 rotation cannot silently undo the tightening.
|
|
601
603
|
let imageRetryAttempted = false;
|
|
602
604
|
const opaqueBlobRecoveryGuard: OpaqueBlobRecoveryGuard = { attempted: false };
|
|
605
|
+
const thinkingSignatureRecovery = createAnthropicThinkingRecovery(
|
|
606
|
+
parsed, upstream.signal, opaqueBlobRecoveryGuard, () => !sendBudgetExhausted(),
|
|
607
|
+
);
|
|
603
608
|
// Console Go answers a transient 400 "Invalid upload request." for bodies it accepts
|
|
604
609
|
// moments later; at most one byte-identical replay is allowed per request.
|
|
605
610
|
const consoleGoUploadRetryGuard: { attempted: boolean } = { attempted: false };
|
|
@@ -863,6 +868,11 @@ export async function prepareAdapterExchange(
|
|
|
863
868
|
return { failed: formatErrorResponse(502, "upstream_error", msg) };
|
|
864
869
|
}
|
|
865
870
|
};
|
|
871
|
+
const refetchThinkingSignature = () => {
|
|
872
|
+
invalidateSameTargetRequest();
|
|
873
|
+
return rebuildAndRefetch("opaque-blob-rejection");
|
|
874
|
+
};
|
|
875
|
+
if (!upstreamResponse.ok) {
|
|
866
876
|
const rotateAntigravityAuth = async (
|
|
867
877
|
failedResponse: Response,
|
|
868
878
|
recovery: AttemptRecoveryKind,
|
|
@@ -920,6 +930,17 @@ export async function prepareAdapterExchange(
|
|
|
920
930
|
return await sanitizeNonReplayableUpstreamError(upstreamResponse, upstream.signal);
|
|
921
931
|
} finally { cleanupUpstreamAbort(); }
|
|
922
932
|
}
|
|
933
|
+
bindAnthropicRejectionResponse(transportState.sameTargetRequest, upstreamResponse);
|
|
934
|
+
if (await thinkingSignatureRecovery.prepareHttp(
|
|
935
|
+
upstreamResponse, transportState.activeAdapter.name,
|
|
936
|
+
)) {
|
|
937
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* closed */ }
|
|
938
|
+
invalidateSameTargetRequest();
|
|
939
|
+
const result = await rebuildAndRefetch("opaque-blob-rejection");
|
|
940
|
+
if ("failed" in result) return result.failed;
|
|
941
|
+
upstreamResponse = result;
|
|
942
|
+
continue recovery;
|
|
943
|
+
}
|
|
923
944
|
if (
|
|
924
945
|
upstreamResponse.status === 401
|
|
925
946
|
&& isOAuth401ReplayProvider
|
|
@@ -932,6 +953,15 @@ export async function prepareAdapterExchange(
|
|
|
932
953
|
try {
|
|
933
954
|
refreshed = await refreshResolvedOAuthSelection(transportState.sentOAuthSnapshot);
|
|
934
955
|
} catch (err) {
|
|
956
|
+
const cancelled = options.abortSignal?.aborted || req.signal.aborted;
|
|
957
|
+
if (cancelled || err instanceof OAuthTokenRefreshBusyError) {
|
|
958
|
+
cleanupUpstreamAbort();
|
|
959
|
+
upstream.abort();
|
|
960
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
961
|
+
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
962
|
+
return cancelled ? clientCancelledResponse()
|
|
963
|
+
: formatErrorResponse(503, "server_error", publicOAuthAuthenticationErrorMessage(err), { retryAfter: "1" });
|
|
964
|
+
}
|
|
935
965
|
const failed = transportState.sentOAuthSnapshot;
|
|
936
966
|
if (route.providerName === "google-antigravity" && !antigravityAuthRotationAttempted
|
|
937
967
|
&& err instanceof OAuthLoginRequiredError && failed
|
|
@@ -1337,9 +1367,17 @@ export async function prepareAdapterExchange(
|
|
|
1337
1367
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1338
1368
|
config.cacheRetention,
|
|
1339
1369
|
);
|
|
1370
|
+
bindRouteReasoningReplayScope({
|
|
1371
|
+
parsed,
|
|
1372
|
+
providerName: route.providerName,
|
|
1373
|
+
provider: route.provider,
|
|
1374
|
+
adapterName: transportState.activeAdapter.name,
|
|
1375
|
+
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1376
|
+
});
|
|
1340
1377
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.activeAdapter.name, logCtx.accountLogLabel);
|
|
1341
1378
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
1342
|
-
const result = await rebuildAndRefetch(upstreamResponse.status === 401 ? "oauth-401"
|
|
1379
|
+
const result = await rebuildAndRefetch(upstreamResponse.status === 401 ? "oauth-401"
|
|
1380
|
+
: upstreamResponse.status === 403 ? "oauth-account-403" : "anthropic-oauth-429");
|
|
1343
1381
|
if ("failed" in result) return result.failed;
|
|
1344
1382
|
upstreamResponse = result;
|
|
1345
1383
|
if (isNonReplayableResponse(upstreamResponse)) continue recovery;
|
|
@@ -1860,6 +1898,8 @@ export async function prepareAdapterExchange(
|
|
|
1860
1898
|
authMode: route.provider.authMode,
|
|
1861
1899
|
hasApiKey: Boolean(route.provider.apiKey?.trim()),
|
|
1862
1900
|
upstreamRetryAfter,
|
|
1901
|
+
upstreamErrorType: normalized.type,
|
|
1902
|
+
upstreamErrorCode: normalized.code,
|
|
1863
1903
|
// This recovery path is the HTTP Responses wire; custom runTurn transports
|
|
1864
1904
|
// never reach enrichOpenCodeZenUpstreamMessage here.
|
|
1865
1905
|
supportsHttpSameKeyRetry: true,
|
|
@@ -1885,6 +1925,7 @@ export async function prepareAdapterExchange(
|
|
|
1885
1925
|
}
|
|
1886
1926
|
}
|
|
1887
1927
|
|
|
1928
|
+
bindAnthropicRejectionResponse(transportState.sameTargetRequest, upstreamResponse);
|
|
1888
1929
|
cancelBodyOnAbort(upstreamResponse.body, upstream.signal);
|
|
1889
1930
|
|
|
1890
1931
|
return {
|
|
@@ -1894,6 +1935,8 @@ export async function prepareAdapterExchange(
|
|
|
1894
1935
|
stallTimeoutMs,
|
|
1895
1936
|
localUpstream,
|
|
1896
1937
|
upstreamResponse,
|
|
1938
|
+
thinkingSignatureRecovery,
|
|
1939
|
+
refetchThinkingSignature,
|
|
1897
1940
|
rateLimitPolicy,
|
|
1898
1941
|
get rateLimitRetries(): typeof rateLimitRetries {
|
|
1899
1942
|
return rateLimitRetries;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { jsonUtf8Bytes } from "../../lib/json-byte-size";
|
|
2
|
+
import {
|
|
3
|
+
TRANSLATOR_MAX_TURN_BYTES,
|
|
4
|
+
TranslatorBudgetExceededError,
|
|
5
|
+
type TranslatorBudget,
|
|
6
|
+
} from "../../lib/translator-budget";
|
|
7
|
+
import type { AdapterEvent, OcxMessage, OcxUsage } from "../../types";
|
|
8
|
+
import { mergeUsage } from "./terminal-guard";
|
|
9
|
+
|
|
10
|
+
export const MAX_ADVISOR_RETAINED_EVENTS = 4_096;
|
|
11
|
+
export const MAX_ADVISOR_RETAINED_CALLS = 1_024;
|
|
12
|
+
|
|
13
|
+
/** Independent ownership: never borrow the bridge's event or tool-call leases. */
|
|
14
|
+
export class AdvisorRetention {
|
|
15
|
+
private legBytes = 0;
|
|
16
|
+
private historyBytes = 0;
|
|
17
|
+
private copyBytes = 0;
|
|
18
|
+
private usageBytes = 0;
|
|
19
|
+
private events = 0;
|
|
20
|
+
private calls = 0;
|
|
21
|
+
|
|
22
|
+
constructor(private readonly shared?: TranslatorBudget) {}
|
|
23
|
+
|
|
24
|
+
private charge(bytes: number, kind: "retained_collectors" | "request_copies"): void {
|
|
25
|
+
if (bytes > TRANSLATOR_MAX_TURN_BYTES - this.legBytes - this.historyBytes - this.copyBytes - this.usageBytes) {
|
|
26
|
+
throw new TranslatorBudgetExceededError(kind, TRANSLATOR_MAX_TURN_BYTES);
|
|
27
|
+
}
|
|
28
|
+
this.shared?.chargeRetained(bytes, { kind });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Usage can alias a terminal payload. Keep its own owner after the raw leg ends. */
|
|
32
|
+
usage(first: OcxUsage | undefined, second: OcxUsage | undefined): OcxUsage | undefined {
|
|
33
|
+
if (!second) return first;
|
|
34
|
+
// Two-input merge materializes a new object. Reserve before merging, including
|
|
35
|
+
// a conservative bound for concatenation if a provider supplied malformed scalars.
|
|
36
|
+
const bytes = first
|
|
37
|
+
? (jsonUtf8Bytes(first) + jsonUtf8Bytes(second)) * 2 + 256
|
|
38
|
+
: jsonUtf8Bytes(second) + 1;
|
|
39
|
+
this.charge(bytes, "request_copies");
|
|
40
|
+
this.copyBytes += bytes;
|
|
41
|
+
let retained = 0;
|
|
42
|
+
try {
|
|
43
|
+
const merged = mergeUsage(first, second);
|
|
44
|
+
const measured = jsonUtf8Bytes(merged, bytes) + 1;
|
|
45
|
+
if (measured > bytes) throw new TranslatorBudgetExceededError("request_copies", TRANSLATOR_MAX_TURN_BYTES);
|
|
46
|
+
retained = measured;
|
|
47
|
+
const previous = this.usageBytes;
|
|
48
|
+
this.usageBytes = retained;
|
|
49
|
+
this.shared?.releaseRetained(previous, { kind: "request_copies" });
|
|
50
|
+
return merged;
|
|
51
|
+
} finally {
|
|
52
|
+
this.copyBytes -= bytes;
|
|
53
|
+
this.shared?.releaseRetained(bytes - retained, { kind: "request_copies" });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* `counted: false` is only for non-empty text/thinking deltas: the leg's 16 KiB character
|
|
59
|
+
* caps already bound how many of those can be held, and an ordinary long answer streams
|
|
60
|
+
* thousands of them. They still consume bytes.
|
|
61
|
+
*/
|
|
62
|
+
retain(event: AdapterEvent, call = false, counted = true): void {
|
|
63
|
+
if ((counted && this.events >= MAX_ADVISOR_RETAINED_EVENTS) || (call && this.calls >= MAX_ADVISOR_RETAINED_CALLS)) {
|
|
64
|
+
throw new TranslatorBudgetExceededError("retained_collectors", TRANSLATOR_MAX_TURN_BYTES);
|
|
65
|
+
}
|
|
66
|
+
const bytes = jsonUtf8Bytes(event) + 1;
|
|
67
|
+
this.charge(bytes, "retained_collectors");
|
|
68
|
+
this.legBytes += bytes;
|
|
69
|
+
if (counted) this.events++;
|
|
70
|
+
if (call) this.calls++;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Hold a bounded materialization while its raw sources are still retained. */
|
|
74
|
+
copy<T>(bytes: number, build: () => T): T {
|
|
75
|
+
this.charge(bytes, "request_copies");
|
|
76
|
+
this.copyBytes += bytes;
|
|
77
|
+
try { return build(); }
|
|
78
|
+
finally {
|
|
79
|
+
this.copyBytes -= bytes;
|
|
80
|
+
this.shared?.releaseRetained(bytes, { kind: "request_copies" });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Reserve before constructing replay JSON; transfer the reservation to history. */
|
|
85
|
+
history<T extends OcxMessage | undefined>(bytes: number, build: () => T): T {
|
|
86
|
+
this.charge(bytes, "request_copies");
|
|
87
|
+
this.copyBytes += bytes;
|
|
88
|
+
let retained = 0;
|
|
89
|
+
try {
|
|
90
|
+
const message = build();
|
|
91
|
+
const measured = message ? jsonUtf8Bytes(message, bytes) + 1 : 0;
|
|
92
|
+
if (measured > bytes) throw new TranslatorBudgetExceededError("request_copies", TRANSLATOR_MAX_TURN_BYTES);
|
|
93
|
+
retained = measured;
|
|
94
|
+
this.historyBytes += retained;
|
|
95
|
+
return message;
|
|
96
|
+
} finally {
|
|
97
|
+
this.copyBytes -= bytes;
|
|
98
|
+
this.shared?.releaseRetained(bytes - retained, { kind: "request_copies" });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Event envelopes dominate the replay content; allow new message/call framing too. */
|
|
103
|
+
replayReservation(): number { return this.legBytes + (this.calls + 1) * 256; }
|
|
104
|
+
|
|
105
|
+
releaseLeg(): void {
|
|
106
|
+
this.shared?.releaseRetained(this.legBytes, { kind: "retained_collectors" });
|
|
107
|
+
this.legBytes = this.events = this.calls = 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
release(): void {
|
|
111
|
+
this.releaseLeg();
|
|
112
|
+
this.shared?.releaseRetained(this.historyBytes, { kind: "request_copies" });
|
|
113
|
+
this.historyBytes = 0;
|
|
114
|
+
this.shared?.releaseRetained(this.usageBytes, { kind: "request_copies" });
|
|
115
|
+
this.usageBytes = 0;
|
|
116
|
+
}
|
|
117
|
+
}
|