@bitkyc08/opencodex 2.86.0 → 2.87.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-ZfYbLHkB.js +58 -0
- package/gui/dist/assets/App-wT3mXqFz.css +1 -0
- package/gui/dist/assets/{Tray-DyR9yBee.js → Tray-DCd-A1az.js} +1 -1
- package/gui/dist/assets/index-CfYDMj34.js +94 -0
- package/gui/dist/assets/index-DPeB9HpV.css +1 -0
- package/gui/dist/assets/quota-summary-ChvmKGOk.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.ts +108 -21
- 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.ts +35 -8
- 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 +3 -1
- package/src/bridge/response-json.ts +18 -3
- package/src/bridge/sse.ts +58 -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 +11 -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 +38 -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 +17 -14
- 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/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 +12 -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/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 +52 -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 +608 -284
- package/src/grok/sync.ts +1 -1
- package/src/images/loop.ts +46 -8
- 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/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 +33 -7
- 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 +5 -4
- 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/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 +24 -2
- 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/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 +55 -414
- package/src/server/effort-policy.ts +1 -1
- package/src/server/images.ts +15 -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 +9 -1
- package/src/server/index/startup-warnings.ts +2 -0
- package/src/server/index.ts +1 -1
- package/src/server/inference/advisor-diagnostics.ts +31 -0
- 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 +101 -21
- 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 +13 -1
- package/src/server/responses/adapter-delivery.ts +19 -6
- package/src/server/responses/adapter-dispatch.ts +19 -2
- package/src/server/responses/advisor-retention.ts +117 -0
- package/src/server/responses/advisor-slot.ts +73 -41
- package/src/server/responses/codex-ws-wire.ts +15 -8
- package/src/server/responses/compact.ts +303 -125
- package/src/server/responses/compaction-recovery.ts +18 -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 +120 -36
- package/src/server/responses/core-errors.ts +3 -3
- package/src/server/responses/core-lifetime.ts +5 -2
- package/src/server/responses/core-opaque-recovery.ts +35 -0
- package/src/server/responses/core-options.ts +5 -0
- package/src/server/responses/core-pipeline.ts +141 -0
- package/src/server/responses/core-replay.ts +18 -2
- 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/request-prepare.ts +76 -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 +1 -0
- package/src/server/responses/run-turn-execution.ts +96 -30
- package/src/server/responses/sidecar-execution.ts +20 -2
- package/src/server/responses/terminal-guard.ts +2 -16
- 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 +29 -0
- package/src/types/provider.ts +32 -6
- package/src/types/request.ts +23 -2
- package/src/types/wire.ts +29 -37
- package/src/types.ts +1 -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 +22 -9
- 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/loop.ts +55 -5
- package/src/web-search/progress-stream.ts +4 -1
- 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
|
@@ -54,7 +54,7 @@ import { describeUpstreamConnectFailure } from "./upstream-error";
|
|
|
54
54
|
import type { OpaqueBlobRecoveryGuard } from "./core-opaque-recovery";
|
|
55
55
|
import type { AttemptRecoveryKind } from "../../usage/log";
|
|
56
56
|
import type { OAuthAccessSnapshot } from "../../oauth";
|
|
57
|
-
import { OAuthAccountPausedError, OAuthLoginRequiredError, publicOAuthAuthenticationErrorMessage } from "../../oauth";
|
|
57
|
+
import { OAuthAccountPausedError, OAuthLoginRequiredError, OAuthTokenRefreshBusyError, publicOAuthAuthenticationErrorMessage } from "../../oauth";
|
|
58
58
|
import { getAccountSet, markAccountNeedsReauthIfGeneration } from "../../oauth/store";
|
|
59
59
|
import { tryKiroAlternateAfterTerminalRefresh } from "../../oauth/kiro-terminal-failover";
|
|
60
60
|
import { classifyKiroRefusal } from "../../adapters/kiro-refusal";
|
|
@@ -932,6 +932,15 @@ export async function prepareAdapterExchange(
|
|
|
932
932
|
try {
|
|
933
933
|
refreshed = await refreshResolvedOAuthSelection(transportState.sentOAuthSnapshot);
|
|
934
934
|
} catch (err) {
|
|
935
|
+
const cancelled = options.abortSignal?.aborted || req.signal.aborted;
|
|
936
|
+
if (cancelled || err instanceof OAuthTokenRefreshBusyError) {
|
|
937
|
+
cleanupUpstreamAbort();
|
|
938
|
+
upstream.abort();
|
|
939
|
+
try { void upstreamResponse.body?.cancel().catch(() => {}); } catch { /* already closed */ }
|
|
940
|
+
releaseCodexAuthContextProbeLease(admissionState.authCtx);
|
|
941
|
+
return cancelled ? clientCancelledResponse()
|
|
942
|
+
: formatErrorResponse(503, "server_error", publicOAuthAuthenticationErrorMessage(err), { retryAfter: "1" });
|
|
943
|
+
}
|
|
935
944
|
const failed = transportState.sentOAuthSnapshot;
|
|
936
945
|
if (route.providerName === "google-antigravity" && !antigravityAuthRotationAttempted
|
|
937
946
|
&& err instanceof OAuthLoginRequiredError && failed
|
|
@@ -1337,9 +1346,17 @@ export async function prepareAdapterExchange(
|
|
|
1337
1346
|
resolveWireProtocolOverride(route.providerName, route.modelId, route.provider, inboundWire, route.staticPolicy),
|
|
1338
1347
|
config.cacheRetention,
|
|
1339
1348
|
);
|
|
1349
|
+
bindRouteReasoningReplayScope({
|
|
1350
|
+
parsed,
|
|
1351
|
+
providerName: route.providerName,
|
|
1352
|
+
provider: route.provider,
|
|
1353
|
+
adapterName: transportState.activeAdapter.name,
|
|
1354
|
+
oauthCredentialSnapshot: transportState.replayOAuthCredentialSnapshot,
|
|
1355
|
+
});
|
|
1340
1356
|
sealRequestAttemptIdentity(logCtx.activeAttempt, logCtx.provider, transportState.activeAdapter.name, logCtx.accountLogLabel);
|
|
1341
1357
|
recordAttemptCredentialSource(logCtx.activeAttempt, route.providerName, route.provider, transportState.activeAdapter.name);
|
|
1342
|
-
const result = await rebuildAndRefetch(upstreamResponse.status === 401 ? "oauth-401"
|
|
1358
|
+
const result = await rebuildAndRefetch(upstreamResponse.status === 401 ? "oauth-401"
|
|
1359
|
+
: upstreamResponse.status === 403 ? "oauth-account-403" : "anthropic-oauth-429");
|
|
1343
1360
|
if ("failed" in result) return result.failed;
|
|
1344
1361
|
upstreamResponse = result;
|
|
1345
1362
|
if (isNonReplayableResponse(upstreamResponse)) continue recovery;
|
|
@@ -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
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assistantReplayContent } from "../../responses/assistant-replay";
|
|
1
2
|
/**
|
|
2
3
|
* Core-owned registration slot for the optional advisor subsystem (src/advisor).
|
|
3
4
|
*
|
|
@@ -28,7 +29,9 @@ import type {
|
|
|
28
29
|
OcxToolResultMessage,
|
|
29
30
|
OcxUsage,
|
|
30
31
|
} from "../../types";
|
|
31
|
-
import {
|
|
32
|
+
import { jsonStringPartsUtf8Bytes, jsonUtf8Bytes } from "../../lib/json-byte-size";
|
|
33
|
+
import { isTranslatorBudgetExceededError, TRANSLATOR_MAX_CALL_ARGUMENT_BYTES, TranslatorBudgetExceededError, type TranslatorBudget } from "../../lib/translator-budget";
|
|
34
|
+
import { AdvisorRetention } from "./advisor-retention";
|
|
32
35
|
|
|
33
36
|
export const ADVISOR_TOOL_NAME = "advisor";
|
|
34
37
|
|
|
@@ -53,7 +56,7 @@ export interface AdvisorConsultOutcome {
|
|
|
53
56
|
* Operator config blocked the consultation before any outbound call. Not a provider
|
|
54
57
|
* failure: preflight must release a claim instead of recording a cooldown.
|
|
55
58
|
*/
|
|
56
|
-
blocked?: "consent" | "settings";
|
|
59
|
+
blocked?: "consent" | "settings" | "budget" | "limit";
|
|
57
60
|
}
|
|
58
61
|
|
|
59
62
|
/** The structural plan the optional advisor subsystem registers per request. */
|
|
@@ -78,12 +81,19 @@ export interface AdvisorPlan {
|
|
|
78
81
|
interface HeldAdvisorCall {
|
|
79
82
|
id: string;
|
|
80
83
|
name: string;
|
|
81
|
-
|
|
84
|
+
args: string[];
|
|
85
|
+
argumentBytes: number;
|
|
86
|
+
question?: string;
|
|
82
87
|
closed: boolean;
|
|
83
88
|
providerMetadata?: import("../../types").OcxProviderOpaqueToolCallMetadata;
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
function parseAdvisorArgs(
|
|
91
|
+
function parseAdvisorArgs(call: HeldAdvisorCall, retention: AdvisorRetention): { question?: string } {
|
|
92
|
+
// Reserve both the joined input and parsed JSON before either is materialized.
|
|
93
|
+
return retention.copy(call.argumentBytes * 2 + 4, () => parseArgs(call.args.join("")));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseArgs(argsBuf: string): { question?: string } {
|
|
87
97
|
if (argsBuf.trim() === "") return {};
|
|
88
98
|
try {
|
|
89
99
|
const parsed: unknown = JSON.parse(argsBuf);
|
|
@@ -99,31 +109,18 @@ function assistantMessageFromLeg(
|
|
|
99
109
|
events: readonly AdapterEvent[],
|
|
100
110
|
held: readonly HeldAdvisorCall[],
|
|
101
111
|
timestamp: number,
|
|
112
|
+
retention: AdvisorRetention,
|
|
102
113
|
): OcxAssistantMessage | undefined {
|
|
103
|
-
|
|
104
|
-
let thinking = "";
|
|
105
|
-
let signature: string | undefined;
|
|
106
|
-
const redacted: string[] = [];
|
|
107
|
-
for (const event of events) {
|
|
108
|
-
if (event.type === "text_delta") text += event.text;
|
|
109
|
-
else if (event.type === "thinking_delta") thinking += event.thinking;
|
|
110
|
-
else if (event.type === "thinking_signature") signature = event.signature;
|
|
111
|
-
else if (event.type === "redacted_thinking") redacted.push(event.data);
|
|
112
|
-
}
|
|
113
|
-
if (text.length > MAX_LEG_TEXT_CHARS) text = text.slice(0, MAX_LEG_TEXT_CHARS);
|
|
114
|
-
if (thinking.length > MAX_LEG_THINKING_CHARS) thinking = thinking.slice(0, MAX_LEG_THINKING_CHARS);
|
|
115
|
-
const content: OcxAssistantContentPart[] = [];
|
|
116
|
-
if (thinking || signature || redacted.length > 0) {
|
|
117
|
-
content.push({ type: "thinking", thinking, ...(signature ? { signature } : {}), ...(redacted.length > 0 ? { redacted } : {}) });
|
|
118
|
-
}
|
|
119
|
-
if (text) content.push({ type: "text", text });
|
|
114
|
+
const content = assistantReplayContent(events);
|
|
120
115
|
for (const call of held) {
|
|
121
116
|
if (!call.closed) continue;
|
|
117
|
+
const args = parseAdvisorArgs(call, retention);
|
|
118
|
+
call.question = args.question;
|
|
122
119
|
content.push({
|
|
123
120
|
type: "toolCall",
|
|
124
121
|
id: call.id,
|
|
125
122
|
name: call.name,
|
|
126
|
-
arguments:
|
|
123
|
+
arguments: args,
|
|
127
124
|
...(call.providerMetadata ? { providerMetadata: call.providerMetadata } : {}),
|
|
128
125
|
});
|
|
129
126
|
}
|
|
@@ -150,6 +147,8 @@ export interface AdvisorGuardOptions {
|
|
|
150
147
|
parsed: OcxParsedRequest;
|
|
151
148
|
plan: AdvisorPlan;
|
|
152
149
|
firstEvents: AsyncIterable<AdapterEvent>;
|
|
150
|
+
/** Independent guard ownership within the optional shared request budget. */
|
|
151
|
+
translatorBudget?: TranslatorBudget;
|
|
153
152
|
/** One bounded worker continuation re-dispatch (same machinery as the terminal guard). */
|
|
154
153
|
continuation: (parsed: OcxParsedRequest) => AsyncIterable<AdapterEvent> | Promise<AsyncIterable<AdapterEvent>>;
|
|
155
154
|
}
|
|
@@ -172,7 +171,8 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
172
171
|
let parsed = options.parsed;
|
|
173
172
|
let accumulatedUsage: OcxUsage | undefined;
|
|
174
173
|
let source: AsyncIterable<AdapterEvent> = options.firstEvents;
|
|
175
|
-
|
|
174
|
+
const retention = new AdvisorRetention(options.translatorBudget);
|
|
175
|
+
try {
|
|
176
176
|
while (true) {
|
|
177
177
|
const held: HeldAdvisorCall[] = [];
|
|
178
178
|
const legEvents: AdapterEvent[] = [];
|
|
@@ -189,15 +189,33 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
189
189
|
for await (const event of source) {
|
|
190
190
|
if (event.type === "tool_call_start") {
|
|
191
191
|
if (pending) { held.push(pending); pending = null; }
|
|
192
|
-
pending = { id: event.id, name: event.name, argsBuf: "", closed: false, ...(event.providerMetadata ? { providerMetadata: event.providerMetadata } : {}) };
|
|
193
192
|
holdingCurrent = event.name === ADVISOR_TOOL_NAME;
|
|
194
|
-
if (!holdingCurrent)
|
|
195
|
-
|
|
193
|
+
if (!holdingCurrent) {
|
|
194
|
+
hasRealToolCall = true;
|
|
195
|
+
// No continuation can use this leg. Real-tool arguments are never copied.
|
|
196
|
+
held.length = legEvents.length = 0;
|
|
197
|
+
retention.releaseLeg();
|
|
198
|
+
} else {
|
|
199
|
+
if (!hasRealToolCall) {
|
|
200
|
+
retention.retain(event, true);
|
|
201
|
+
pending = { id: event.id, name: event.name, args: [], argumentBytes: 0, closed: false, ...(event.providerMetadata ? { providerMetadata: event.providerMetadata } : {}) };
|
|
202
|
+
}
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
196
205
|
} else if (event.type === "tool_call_delta") {
|
|
197
|
-
if (pending)
|
|
206
|
+
if (pending) {
|
|
207
|
+
const bytes = jsonStringPartsUtf8Bytes([event.arguments], TRANSLATOR_MAX_CALL_ARGUMENT_BYTES + 2) - 2;
|
|
208
|
+
if (bytes > TRANSLATOR_MAX_CALL_ARGUMENT_BYTES - pending.argumentBytes) {
|
|
209
|
+
throw new TranslatorBudgetExceededError("tool_args", TRANSLATOR_MAX_CALL_ARGUMENT_BYTES);
|
|
210
|
+
}
|
|
211
|
+
retention.retain(event);
|
|
212
|
+
pending.argumentBytes += bytes;
|
|
213
|
+
pending.args.push(event.arguments);
|
|
214
|
+
}
|
|
198
215
|
if (holdingCurrent) continue;
|
|
199
216
|
} else if (event.type === "tool_call_end") {
|
|
200
217
|
if (pending) {
|
|
218
|
+
retention.retain(event);
|
|
201
219
|
pending.closed = true;
|
|
202
220
|
if (pending.name === ADVISOR_TOOL_NAME) held.push(pending);
|
|
203
221
|
pending = null;
|
|
@@ -207,6 +225,7 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
207
225
|
continue;
|
|
208
226
|
}
|
|
209
227
|
} else if (event.type === "done") {
|
|
228
|
+
if (!hasRealToolCall) retention.retain(event);
|
|
210
229
|
if (pending) { held.push(pending); pending = null; }
|
|
211
230
|
terminalEvent = event;
|
|
212
231
|
terminalConsumed = true;
|
|
@@ -214,22 +233,23 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
214
233
|
} else if (event.type === "incomplete" || event.type === "error") {
|
|
215
234
|
// A broken leg cannot safely anchor a continuation: surface the terminal as-is.
|
|
216
235
|
if (pending) { pending = null; }
|
|
217
|
-
|
|
218
|
-
yield
|
|
236
|
+
accumulatedUsage = retention.usage(accumulatedUsage, event.usage);
|
|
237
|
+
yield accumulatedUsage ? { ...event, usage: accumulatedUsage } : event;
|
|
219
238
|
return;
|
|
220
239
|
} else {
|
|
221
240
|
// Retain (bounded) the leg's visible content so the rebuilt assistant message is complete.
|
|
222
241
|
if (event.type === "text_delta") {
|
|
223
242
|
legTextChars += event.text.length;
|
|
224
|
-
|
|
243
|
+
// Empty deltas add nothing to the rebuilt message; non-empty ones are bounded by the cap.
|
|
244
|
+
if (!hasRealToolCall && event.text !== "" && legTextChars <= MAX_LEG_TEXT_CHARS) { retention.retain(event, false, false); legEvents.push(event); }
|
|
225
245
|
} else if (event.type === "thinking_delta") {
|
|
226
246
|
legThinkingChars += event.thinking.length;
|
|
227
|
-
if (legThinkingChars <= MAX_LEG_THINKING_CHARS) legEvents.push(event);
|
|
247
|
+
if (!hasRealToolCall && event.thinking !== "" && legThinkingChars <= MAX_LEG_THINKING_CHARS) { retention.retain(event, false, false); legEvents.push(event); }
|
|
228
248
|
} else if (
|
|
229
249
|
event.type === "thinking_signature"
|
|
230
250
|
|| event.type === "redacted_thinking"
|
|
231
251
|
) {
|
|
232
|
-
legEvents.push(event);
|
|
252
|
+
if (!hasRealToolCall) { retention.retain(event); legEvents.push(event); }
|
|
233
253
|
}
|
|
234
254
|
}
|
|
235
255
|
yield event;
|
|
@@ -245,14 +265,14 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
245
265
|
if (!shouldIntercept) {
|
|
246
266
|
// Plain leg: surface the terminal with merged usage from any earlier intercepted legs.
|
|
247
267
|
if (terminalEvent) {
|
|
248
|
-
|
|
249
|
-
yield
|
|
268
|
+
accumulatedUsage = retention.usage(accumulatedUsage, terminalEvent.usage);
|
|
269
|
+
yield accumulatedUsage ? { ...terminalEvent, usage: accumulatedUsage } : terminalEvent;
|
|
250
270
|
}
|
|
251
271
|
return;
|
|
252
272
|
}
|
|
253
273
|
|
|
254
274
|
// Consume this leg's done — the continuation replaces it.
|
|
255
|
-
accumulatedUsage =
|
|
275
|
+
accumulatedUsage = retention.usage(accumulatedUsage, terminalEvent?.usage);
|
|
256
276
|
if (continuations >= MAX_ADVISOR_CONTINUATIONS_PER_REQUEST || finalContinuationUsed) {
|
|
257
277
|
yield {
|
|
258
278
|
type: "error",
|
|
@@ -269,22 +289,22 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
269
289
|
continuations += 1;
|
|
270
290
|
finalContinuationUsed ||= isFinalContinuation;
|
|
271
291
|
const timestamp = Date.now();
|
|
272
|
-
const assistant = assistantMessageFromLeg(legEvents, advisorCalls, timestamp);
|
|
292
|
+
const assistant = retention.history(retention.replayReservation(), () => assistantMessageFromLeg(legEvents, advisorCalls, timestamp, retention));
|
|
273
293
|
const messages: OcxMessage[] = [...parsed.context.messages];
|
|
274
294
|
if (assistant) messages.push(assistant);
|
|
275
295
|
|
|
276
296
|
for (const call of advisorCalls) {
|
|
277
297
|
if (consultations >= maxConsultations) {
|
|
278
298
|
// Runtime-owned limit text: not advice, and never a genuine advice wrapper.
|
|
279
|
-
|
|
299
|
+
const outcome = {
|
|
280
300
|
ok: false,
|
|
281
301
|
isError: true,
|
|
282
302
|
content: plan.formatUnavailable("limit", "consultation limit reached for this request"),
|
|
283
|
-
}
|
|
303
|
+
};
|
|
304
|
+
messages.push(retention.history(jsonUtf8Bytes(outcome) + jsonUtf8Bytes(call.id) + 256, () => advisorToolResult(call, outcome, timestamp))!);
|
|
284
305
|
continue;
|
|
285
306
|
}
|
|
286
307
|
consultations += 1;
|
|
287
|
-
const args = parseAdvisorArgs(call.argsBuf);
|
|
288
308
|
// Later calls in the same leg must see what came before them: the rebuilt worker turn
|
|
289
309
|
// and every earlier advisor result in this leg's accumulated messages.
|
|
290
310
|
const consultParsed: OcxParsedRequest = {
|
|
@@ -293,8 +313,9 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
293
313
|
};
|
|
294
314
|
let outcome: AdvisorConsultOutcome;
|
|
295
315
|
try {
|
|
296
|
-
outcome = await plan.consult(consultParsed, "manual",
|
|
316
|
+
outcome = await plan.consult(consultParsed, "manual", call.question);
|
|
297
317
|
} catch (error) {
|
|
318
|
+
if (isTranslatorBudgetExceededError(error)) throw error;
|
|
298
319
|
// Runtime-owned failure text only: the implementation neutralizes untrusted exception
|
|
299
320
|
// text so it can never forge a genuine advice wrapper.
|
|
300
321
|
outcome = {
|
|
@@ -303,7 +324,7 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
303
324
|
content: plan.formatUnavailable("manual", error instanceof Error ? error.message : String(error)),
|
|
304
325
|
};
|
|
305
326
|
}
|
|
306
|
-
messages.push(advisorToolResult(call, outcome, timestamp));
|
|
327
|
+
messages.push(retention.history(jsonUtf8Bytes(outcome) + jsonUtf8Bytes(call.id) + 256, () => advisorToolResult(call, outcome, timestamp))!);
|
|
307
328
|
}
|
|
308
329
|
|
|
309
330
|
const exhausted = consultations >= maxConsultations;
|
|
@@ -320,10 +341,16 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
320
341
|
: parsed.options,
|
|
321
342
|
};
|
|
322
343
|
parsed = nextParsed;
|
|
344
|
+
// All replay owners were admitted while the raw leg was still live.
|
|
345
|
+
held.length = legEvents.length = advisorCalls.length = 0;
|
|
346
|
+
pending = null;
|
|
347
|
+
terminalEvent = undefined;
|
|
348
|
+
retention.releaseLeg();
|
|
323
349
|
yield { type: "assistant_boundary" };
|
|
324
350
|
try {
|
|
325
351
|
source = await options.continuation(parsed);
|
|
326
352
|
} catch (error) {
|
|
353
|
+
if (isTranslatorBudgetExceededError(error)) throw error;
|
|
327
354
|
yield {
|
|
328
355
|
type: "error",
|
|
329
356
|
message: error instanceof Error ? error.message : String(error),
|
|
@@ -332,5 +359,10 @@ export function createAdvisorGuard(plan: AdvisorPlan): NonNullable<OcxParsedRequ
|
|
|
332
359
|
return;
|
|
333
360
|
}
|
|
334
361
|
}
|
|
362
|
+
} catch (error) {
|
|
363
|
+
if (!isTranslatorBudgetExceededError(error)) throw error;
|
|
364
|
+
yield { type: "error", status: 502, errorType: "upstream_error", code: "translation_buffer_limit",
|
|
365
|
+
message: "upstream translation buffer exceeded the safe limit", ...(accumulatedUsage ? { usage: accumulatedUsage } : {}) };
|
|
366
|
+
} finally { retention.release(); }
|
|
335
367
|
};
|
|
336
368
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizeResponsesIncompleteReason } from "../../responses/incomplete-reason";
|
|
1
2
|
import { MAX_CLIENT_SSE_FRAME_BYTES } from "../sse-frame-buffer";
|
|
2
3
|
import {
|
|
3
4
|
isTransientUpstreamStatus,
|
|
@@ -8,6 +9,7 @@ import {
|
|
|
8
9
|
import { isRateLimitOrQuotaFailureMessage, recognizedUpstreamError } from "../../lib/errors";
|
|
9
10
|
import type { RequestFailureCause, RequestFailureStage } from "../../lib/request-failure-model";
|
|
10
11
|
import { packageVersion } from "../../lib/package-version";
|
|
12
|
+
import { registerProviderResponseProvenance } from "../../providers/request-pacing";
|
|
11
13
|
// If the 101 never arrives (network black hole), give SSE a chance well before
|
|
12
14
|
// the caller's connect timeout (default 200s) would fire.
|
|
13
15
|
export const UPGRADE_DEADLINE_MS = 10_000;
|
|
@@ -49,9 +51,12 @@ export const CODEX_WS_CREATE_FRAME_LIMIT_BYTES =
|
|
|
49
51
|
/** Close code the backend uses for an oversized message (RFC 6455 "message too big"). */
|
|
50
52
|
const WS_CLOSE_MESSAGE_TOO_BIG = 1009;
|
|
51
53
|
|
|
52
|
-
const codexWsUpstreamResponses = new
|
|
53
|
-
const quotaObservedResponses = new
|
|
54
|
-
const codexWsPreludeProjections = new
|
|
54
|
+
const codexWsUpstreamResponses = new WeakMap<Response, true>();
|
|
55
|
+
const quotaObservedResponses = new WeakMap<Response, true>();
|
|
56
|
+
const codexWsPreludeProjections = new WeakMap<Response, true>();
|
|
57
|
+
registerProviderResponseProvenance(codexWsUpstreamResponses);
|
|
58
|
+
registerProviderResponseProvenance(quotaObservedResponses);
|
|
59
|
+
registerProviderResponseProvenance(codexWsPreludeProjections);
|
|
55
60
|
|
|
56
61
|
/** Quota arrived directly at its captured account; do not replay old HTTP prelude headers. */
|
|
57
62
|
export function isCodexWsQuotaObservedResponse(response: Response): boolean {
|
|
@@ -65,7 +70,7 @@ export function isCodexWsUpstreamResponse(response: Response): boolean {
|
|
|
65
70
|
|
|
66
71
|
/** Pre-response projection; its headers retain the exchange's prelude snapshot. */
|
|
67
72
|
export function markCodexWsPreludeProjection(response: Response): void {
|
|
68
|
-
codexWsPreludeProjections.
|
|
73
|
+
codexWsPreludeProjections.set(response, true);
|
|
69
74
|
}
|
|
70
75
|
|
|
71
76
|
export function isCodexWsPreludeProjection(response: Response): boolean {
|
|
@@ -73,8 +78,8 @@ export function isCodexWsPreludeProjection(response: Response): boolean {
|
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
export function markCodexWsResponse(response: Response, observed: boolean): void {
|
|
76
|
-
codexWsUpstreamResponses.
|
|
77
|
-
if (observed) quotaObservedResponses.
|
|
81
|
+
codexWsUpstreamResponses.set(response, true);
|
|
82
|
+
if (observed) quotaObservedResponses.set(response, true);
|
|
78
83
|
}
|
|
79
84
|
|
|
80
85
|
/**
|
|
@@ -313,6 +318,7 @@ export function restoreCodexWsPreOutputRefusal(response: Response): Response {
|
|
|
313
318
|
}
|
|
314
319
|
|
|
315
320
|
const codexWsStageByResponse = new WeakMap<Response, CodexWsStageRecord>();
|
|
321
|
+
registerProviderResponseProvenance(codexWsStageByResponse);
|
|
316
322
|
|
|
317
323
|
export function markCodexWsStage(response: Response, record: CodexWsStageRecord): void {
|
|
318
324
|
const current = codexWsStageByResponse.get(response);
|
|
@@ -457,6 +463,7 @@ export function projectCodexWsFailure(
|
|
|
457
463
|
}
|
|
458
464
|
|
|
459
465
|
const socketDeathStages = new WeakMap<Response, RequestFailureStage>();
|
|
466
|
+
registerProviderResponseProvenance(socketDeathStages);
|
|
460
467
|
|
|
461
468
|
/**
|
|
462
469
|
* Record that a pre-response settle came from the socket closing or failing under the send (#4191),
|
|
@@ -517,12 +524,12 @@ export function normalizeResponsesWsRelayEvent(text: string): ResponsesWsRelayEv
|
|
|
517
524
|
return null;
|
|
518
525
|
}
|
|
519
526
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return null;
|
|
520
|
-
const record = payload as Record<string, unknown
|
|
527
|
+
const record = normalizeResponsesIncompleteReason(payload as Record<string, unknown>);
|
|
521
528
|
if (typeof record.type !== "string") return null;
|
|
522
529
|
// A native error may be pretty-printed JSON. SSE prefixes one data line;
|
|
523
530
|
// embedded physical newlines would otherwise truncate the JSON for readers.
|
|
524
531
|
if (record.type !== "response.done") return {
|
|
525
|
-
type: record.type, text: /[\r\n]/.test(text) ? JSON.stringify(record) : text, payload: record,
|
|
532
|
+
type: record.type, text: record !== payload || /[\r\n]/.test(text) ? JSON.stringify(record) : text, payload: record,
|
|
526
533
|
};
|
|
527
534
|
|
|
528
535
|
const response = record.response;
|