@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
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { truncationReasonFor } from "../../responses/truncated-stop-reason";
|
|
1
2
|
import { normalizeRoutedAgentMessages } from "../routed-agent-messages";
|
|
3
|
+
import { frameCompactionHistory } from "../../responses/compaction-history-framing";
|
|
2
4
|
import { nameRoutedIdentity, repairIdentityInResponsesBody, stripRoutedIdentity } from "../identity";
|
|
3
5
|
import { stripBracketedModelSuffix } from "../openai-chat";
|
|
4
6
|
import { normalizeOpenCodeGoAdditionalTools } from "../opencode-go-additional-tools";
|
|
@@ -7,7 +9,7 @@ import { applyGithubCopilotContextTier } from "../../providers/github-copilot-co
|
|
|
7
9
|
import { Buffer } from "node:buffer";
|
|
8
10
|
import type { IncomingMeta, ProviderAdapter } from "../base";
|
|
9
11
|
import { namespacedToolName, type AdapterEvent, type OcxParsedRequest, type OcxProviderConfig, type OcxUsage, type TierDecision } from "../../types";
|
|
10
|
-
import { applyCodexRoutingHint, CODEX_RESPONSES_LITE_HEADER, CODEX_ROUTING_HINT_HEADER } from "../../codex/forward-transport-headers";
|
|
12
|
+
import { applyCodexRoutingHint, CODEX_RESPONSES_LITE_HEADER, CODEX_RESPONSES_LITE_METADATA_KEY, CODEX_ROUTING_HINT_HEADER } from "../../codex/forward-transport-headers";
|
|
11
13
|
import { COMPACT_PROMPT, compactionItemToText, decodeCompactionSummary, isCompactionItemType, transferCompactionCiphertextLease } from "../../responses/compaction";
|
|
12
14
|
import { decodeServerSentEvents } from "../../lib/sse-decoder";
|
|
13
15
|
import {
|
|
@@ -15,6 +17,7 @@ import {
|
|
|
15
17
|
destinationDecodesNativeCompactionBlob,
|
|
16
18
|
isCanonicalOpenAiForwardProvider,
|
|
17
19
|
isOpenAiOperatedResponsesDestination,
|
|
20
|
+
supportsNativeResponsesCompactionTrigger,
|
|
18
21
|
} from "../../providers/openai-tiers";
|
|
19
22
|
import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
20
23
|
import { rewriteRoutedCustomToolsForUpstream, validateFinalCustomToolCompatibility } from "../../responses/custom-tool-compat";
|
|
@@ -23,7 +26,8 @@ import { rewriteRoutedNamespaceToolsForUpstream } from "../../responses/namespac
|
|
|
23
26
|
import { repairLegacyDottedToolCallNames } from "../../responses/legacy-dotted-tool-name-repair";
|
|
24
27
|
import { preparePlaintextV2AgentMessages } from "../../responses/plaintext-v2-agent-messages";
|
|
25
28
|
import { isMetaAiResponsesDestination, rewriteMuseToolNamesForUpstream } from "../../responses/muse-tool-name-alias";
|
|
26
|
-
import {
|
|
29
|
+
import { buildResponsesTransport } from "./transport";
|
|
30
|
+
export { FORWARD_HEADERS } from "./transport";
|
|
27
31
|
import { normalizeResponsesCodeMode } from "../responses-code-mode";
|
|
28
32
|
import { injectXaiResponsesXSearch, normalizeXaiResponsesWebSearch } from "../xai-web-search";
|
|
29
33
|
import {
|
|
@@ -36,7 +40,7 @@ import {
|
|
|
36
40
|
emittedFastWire,
|
|
37
41
|
} from "../../providers/fastwire";
|
|
38
42
|
import { dropResponsesReasoningInputItems, mapRoutedResponsesReasoningEffort, normalizeConfiguredReasoningSummaryDelivery, sanitizeReasoningInputContent, stripDisabledReasoningSummaries, stripDisabledVerbosity, stripNoneReasoningSummary, stripUnsupportedReasoningSummaryDelivery } from "./reasoning";
|
|
39
|
-
import { scrubOcxCompactionItems, stripCanonicalOnlyToolFields, stripCanonicalOnlyTopLevelFields, stripInternalChatMessageMetadataPassthrough, stripInvalidItemIds, stripItemIdsWhenUnstored, stripRejectedSamplingParams } from "./request-strips";
|
|
43
|
+
import { scrubOcxCompactionItems, stripCanonicalOnlyToolFields, stripCanonicalOnlyTopLevelFields, stripInternalChatMessageMetadataPassthrough, stripInvalidItemIds, stripItemIdsWhenUnstored, stripProviderInputItemMetadata, stripRejectedSamplingParams } from "./request-strips";
|
|
40
44
|
import { notePlaintextReasoningDropped } from "./plaintext-reasoning-notice";
|
|
41
45
|
import { stripCanonicalForwardPromptCacheOptions, stripDeprecatedPromptCacheRetention } from "./prompt-cache";
|
|
42
46
|
import { isPlainObject } from "./internal";
|
|
@@ -47,7 +51,7 @@ import { applyTierDecisionToResponsesBody, normalizeCanonicalForwardContinuation
|
|
|
47
51
|
import { normalizeImageGenClientTools, preferConfiguredHostedTools } from "./image-gen";
|
|
48
52
|
import { stripMuseSparkUnsupportedWebSearchFields, stripOpenAiOnlyWebSearchFields } from "./web-search";
|
|
49
53
|
import { observeOutbound } from "../../usage/cache-diagnostic";
|
|
50
|
-
|
|
54
|
+
|
|
51
55
|
import { normalizeMuseToolChoice } from "./muse-tool-choice";
|
|
52
56
|
import { renderCompactionSearchHistoryItems } from "./compaction-search-history";
|
|
53
57
|
import { stripKimiEmptyAssistantMessages } from "./empty-assistant";
|
|
@@ -65,55 +69,6 @@ export function requiresPlaintextReasoningReplay(provider: OcxProviderConfig): b
|
|
|
65
69
|
&& provider.requiresAdjacentResponsesToolResults === true;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
// Headers relayed verbatim from the caller in OAuth-passthrough ("forward") mode.
|
|
69
|
-
// Exported so the web-search sidecar reuses the exact same forwarded-auth set for its ChatGPT call.
|
|
70
|
-
export const FORWARD_HEADERS = [
|
|
71
|
-
"authorization",
|
|
72
|
-
"chatgpt-account-id",
|
|
73
|
-
"openai-beta",
|
|
74
|
-
"originator",
|
|
75
|
-
"session_id",
|
|
76
|
-
"session-id",
|
|
77
|
-
"thread-id",
|
|
78
|
-
"x-client-request-id",
|
|
79
|
-
"x-codex-beta-features",
|
|
80
|
-
"x-codex-installation-id",
|
|
81
|
-
"x-codex-parent-thread-id",
|
|
82
|
-
"x-codex-turn-metadata",
|
|
83
|
-
"x-codex-turn-state",
|
|
84
|
-
"x-codex-window-id",
|
|
85
|
-
"x-oai-attestation",
|
|
86
|
-
"x-openai-subagent",
|
|
87
|
-
"x-responsesapi-include-timing-metrics",
|
|
88
|
-
CODEX_RESPONSES_LITE_HEADER,
|
|
89
|
-
];
|
|
90
|
-
|
|
91
|
-
/** Preserve the caller fingerprint unless the provider explicitly owns that header. */
|
|
92
|
-
function applyCallerUserAgentFallback(
|
|
93
|
-
headers: Record<string, string>,
|
|
94
|
-
incoming: IncomingMeta,
|
|
95
|
-
): void {
|
|
96
|
-
if (Object.keys(headers).some(name => name.toLowerCase() === "user-agent")) return;
|
|
97
|
-
const userAgent = incoming.headers.get("user-agent");
|
|
98
|
-
if (userAgent) headers["User-Agent"] = userAgent;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Copy only explicitly opted-in caller metadata; provider-owned headers remain authoritative. */
|
|
102
|
-
function applyConfiguredClientHeaderForwarding(
|
|
103
|
-
headers: Record<string, string>,
|
|
104
|
-
incoming: IncomingMeta,
|
|
105
|
-
provider: OcxProviderConfig,
|
|
106
|
-
): void {
|
|
107
|
-
if (!Array.isArray(provider.forwardClientHeaders)) return;
|
|
108
|
-
for (const rawName of provider.forwardClientHeaders.slice(0, 64)) {
|
|
109
|
-
const name = normalizeForwardedClientHeaderName(rawName);
|
|
110
|
-
if (name === null) continue;
|
|
111
|
-
if (Object.keys(headers).some(existing => existing.toLowerCase() === name)) continue;
|
|
112
|
-
const value = incoming.headers.get(name);
|
|
113
|
-
if (value) headers[name] = value;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
72
|
/** Replace every `input_image` part under a routed-compaction body with a short marker. */
|
|
118
73
|
function stripInputImagesDeep(value: unknown): unknown {
|
|
119
74
|
if (Array.isArray(value)) return value.map(stripInputImagesDeep);
|
|
@@ -126,10 +81,20 @@ function stripInputImagesDeep(value: unknown): unknown {
|
|
|
126
81
|
return out;
|
|
127
82
|
}
|
|
128
83
|
|
|
84
|
+
function codexResponsesLiteSelected(headers: Record<string, string>, body: unknown): boolean {
|
|
85
|
+
const lite = new Headers(headers).get(CODEX_RESPONSES_LITE_HEADER);
|
|
86
|
+
if (lite === "true" || lite === "false") return lite === "true";
|
|
87
|
+
// Match existing canonical WS preparation: explicit HTTP intent wins, otherwise native
|
|
88
|
+
// string-map frame metadata survives. This reads intent without rewriting the transport.
|
|
89
|
+
if (!isPlainObject(body) || !isPlainObject(body.client_metadata)) return false;
|
|
90
|
+
return Object.values(body.client_metadata).every(value => typeof value === "string")
|
|
91
|
+
&& body.client_metadata[CODEX_RESPONSES_LITE_METADATA_KEY] === "true";
|
|
92
|
+
}
|
|
93
|
+
|
|
129
94
|
/**
|
|
130
|
-
* Rewrite a compaction turn
|
|
131
|
-
*
|
|
132
|
-
*
|
|
95
|
+
* Rewrite a compaction turn without native v2 capability, or a forced portable
|
|
96
|
+
* handoff: drop the trigger and the whole tool surface, and ask for the handoff
|
|
97
|
+
* summary in plain terms instead (#422). Native v1 capability is independent.
|
|
133
98
|
*
|
|
134
99
|
* The adapter builds from `parsed._rawBody`, so the summarizer prompt that
|
|
135
100
|
* handleResponses() pushed onto `parsed.context` never reaches the wire — it has to
|
|
@@ -225,61 +190,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
225
190
|
|
|
226
191
|
buildRequest(parsed: OcxParsedRequest, incoming: IncomingMeta) {
|
|
227
192
|
const translatorBudget = incoming.translatorBudget;
|
|
228
|
-
const
|
|
229
|
-
let url: string;
|
|
230
|
-
|
|
231
|
-
if (provider.authMode === "forward") {
|
|
232
|
-
const mayForwardCallerCredentials = isCanonicalOpenAiForwardProvider(provider);
|
|
233
|
-
// OAuth passthrough: ChatGPT backend path is `${baseUrl}/responses` (no /v1).
|
|
234
|
-
const baseUrl = mayForwardCallerCredentials
|
|
235
|
-
? CODEX_FORWARD_BASE_URL
|
|
236
|
-
: provider.baseUrl.replace(/\/+$/, "");
|
|
237
|
-
url = `${baseUrl}/responses`;
|
|
238
|
-
if (provider.headers) Object.assign(headers, provider.headers); // static headers first…
|
|
239
|
-
const runtimeProvider = provider as {
|
|
240
|
-
_codexAccountOverride?: { accessToken: string; chatgptAccountId: string };
|
|
241
|
-
_codexAccountRequired?: boolean;
|
|
242
|
-
};
|
|
243
|
-
if (
|
|
244
|
-
mayForwardCallerCredentials
|
|
245
|
-
&& runtimeProvider._codexAccountRequired
|
|
246
|
-
&& !runtimeProvider._codexAccountOverride
|
|
247
|
-
) {
|
|
248
|
-
throw new Error("Codex pool account auth is required but unavailable");
|
|
249
|
-
}
|
|
250
|
-
if (mayForwardCallerCredentials) {
|
|
251
|
-
for (const h of FORWARD_HEADERS) {
|
|
252
|
-
const v = incoming?.headers.get(h);
|
|
253
|
-
if (v) {
|
|
254
|
-
if (h === CODEX_RESPONSES_LITE_HEADER) {
|
|
255
|
-
for (const name of Object.keys(headers)) {
|
|
256
|
-
if (name.toLowerCase() === h) delete headers[name];
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
headers[h] = v; // …so genuine forwarded fields win.
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
const override = runtimeProvider._codexAccountOverride;
|
|
264
|
-
if (override && mayForwardCallerCredentials) {
|
|
265
|
-
headers["authorization"] = `Bearer ${override.accessToken}`;
|
|
266
|
-
headers["chatgpt-account-id"] = override.chatgptAccountId;
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
if (provider.responsesPath === undefined) {
|
|
270
|
-
url = openaiResponsesUrl(provider.baseUrl);
|
|
271
|
-
} else {
|
|
272
|
-
const base = provider.baseUrl.replace(/\/$/, "");
|
|
273
|
-
url = `${base}${provider.responsesPath}`;
|
|
274
|
-
}
|
|
275
|
-
if (provider.apiKey) headers["Authorization"] = `Bearer ${provider.apiKey}`;
|
|
276
|
-
if (provider.headers) Object.assign(headers, provider.headers);
|
|
277
|
-
}
|
|
278
|
-
// Some Responses-compatible gateways select their Codex compatibility path from the real
|
|
279
|
-
// client fingerprint. Additional caller metadata is opt-in; static provider headers remain
|
|
280
|
-
// authoritative in either auth mode.
|
|
281
|
-
applyConfiguredClientHeaderForwarding(headers, incoming, provider);
|
|
282
|
-
applyCallerUserAgentFallback(headers, incoming);
|
|
193
|
+
const { url, headers } = buildResponsesTransport(provider, incoming);
|
|
283
194
|
|
|
284
195
|
const forward = provider.authMode === "forward";
|
|
285
196
|
let convertedRoutedCustomToolNames: Set<string> | undefined;
|
|
@@ -398,22 +309,27 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
398
309
|
// canonical-destination split below, because the reported failure was a side chat on a plain
|
|
399
310
|
// OpenAI model inheriting history a routed provider had damaged.
|
|
400
311
|
outBody = repairLegacyDottedToolCallNames(outBody);
|
|
312
|
+
// Shared HTTP/WS outbound projection, including concrete combo/fallback destinations.
|
|
313
|
+
if (isOpenAiOperatedResponsesDestination(provider)) {
|
|
314
|
+
outBody = stripProviderInputItemMetadata(outBody);
|
|
315
|
+
}
|
|
401
316
|
if (!isCanonicalOpenAiForwardProvider(provider)) {
|
|
402
317
|
if (provider.preserveResponsesMessageMetadata !== true) {
|
|
403
318
|
outBody = stripInternalChatMessageMetadataPassthrough(outBody);
|
|
404
319
|
}
|
|
405
320
|
// The same class of private field, one level up, but keyed on the DESTINATION rather than
|
|
406
321
|
// on the canonical surface alone. `src/server/responses/compact.ts` spreads the caller's
|
|
407
|
-
// raw body into
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
//
|
|
322
|
+
// raw body into native `/responses/compact` without this adapter, and applies the same
|
|
323
|
+
// destination boundary for opted-in custom gateways. Native endpoint capability does not
|
|
324
|
+
// grant OpenAI-private fields. Stripping on the canonical predicate here would make the
|
|
325
|
+
// two paths disagree for `openai-apikey`; stripping on the destination keeps every
|
|
326
|
+
// OpenAI-operated route byte-identical and removes the field exactly where it is known
|
|
327
|
+
// to break, which is a gateway this proxy does not operate.
|
|
413
328
|
//
|
|
414
329
|
// Placed before the routed compaction body is built and before serialization, so the HTTP,
|
|
415
330
|
// routed-compaction and WebSocket outbounds are all covered by this one call.
|
|
416
331
|
if (!isOpenAiOperatedResponsesDestination(provider)) {
|
|
332
|
+
if (parsed._compactionRequest !== true && isPlainObject(outBody)) outBody = frameCompactionHistory(outBody);
|
|
417
333
|
outBody = stripCanonicalOnlyTopLevelFields(outBody);
|
|
418
334
|
}
|
|
419
335
|
outBody = promoteClientLoadedTools(outBody);
|
|
@@ -466,13 +382,13 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
466
382
|
if (!forward) {
|
|
467
383
|
outBody = normalizeOpenCodeGoAdditionalTools(outBody, url, parsed._replayPrefixLen);
|
|
468
384
|
}
|
|
469
|
-
// Same predicate as the routedCompaction gate in handleResponses():
|
|
470
|
-
//
|
|
471
|
-
//
|
|
385
|
+
// Same native-v2/portable predicate as the routedCompaction gate in handleResponses():
|
|
386
|
+
// neither forward auth nor native v1 endpoint support implies trigger support. The
|
|
387
|
+
// compaction body build removes the tool surface and must
|
|
472
388
|
// therefore be the last routed transform that may depend on those declarations. Structural
|
|
473
389
|
// sanitizers below can still run after it.
|
|
474
390
|
outBody = normalizeResponsesCodeMode(outBody, parsed, provider);
|
|
475
|
-
if (parsed._compactionRequest === true && (!
|
|
391
|
+
if (parsed._compactionRequest === true && (!supportsNativeResponsesCompactionTrigger(provider) || parsed._portableCompaction === true)) {
|
|
476
392
|
outBody = buildRoutedCompactionBody(outBody, new Headers(headers).get(CODEX_RESPONSES_LITE_HEADER) === "true");
|
|
477
393
|
}
|
|
478
394
|
// Run after routed compaction so nested input_image parts are replaced before a malformed
|
|
@@ -488,6 +404,8 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
488
404
|
}
|
|
489
405
|
}
|
|
490
406
|
const threadServingIdentityChanged = parsed._stripReasoningEncryptedContent === true;
|
|
407
|
+
const stripNativeCompaction = parsed._stripNativeCompactionEncryptedContent === true
|
|
408
|
+
|| threadServingIdentityChanged;
|
|
491
409
|
// Providers with the strict plaintext tool-continuation contract cannot consume any
|
|
492
410
|
// encrypted reasoning blob, including one whose provenance is unknown. Combo routing
|
|
493
411
|
// separately refuses a proven cross-route replay when no plaintext exists; this final
|
|
@@ -500,7 +418,7 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
500
418
|
scrubOcxCompactionItems(
|
|
501
419
|
outBody,
|
|
502
420
|
destinationDecodesNativeCompactionBlob(provider),
|
|
503
|
-
|
|
421
|
+
stripNativeCompaction,
|
|
504
422
|
parsed._compactionRequest !== true,
|
|
505
423
|
),
|
|
506
424
|
{
|
|
@@ -574,6 +492,17 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
574
492
|
// here, on the serialized body, not on the parsed selector. One place covers both the
|
|
575
493
|
// HTTP and the WebSocket outbound, because the WS path transports this same request
|
|
576
494
|
// instead of rebuilding it.
|
|
495
|
+
// Opt-in Codex Lite history policy: replay remaining hosted actions as untrusted metadata,
|
|
496
|
+
// after bridge restoration/tool validation. Tool declarations and other input stay intact.
|
|
497
|
+
if (provider.projectResponsesLiteHostedSearchHistory === true && url === `${CODEX_FORWARD_BASE_URL}/responses`
|
|
498
|
+
&& !parsed._compactionRequest && !parsed._portableCompaction
|
|
499
|
+
&& codexResponsesLiteSelected(headers, finalBody)
|
|
500
|
+
&& isPlainObject(finalBody) && Array.isArray(finalBody.input)
|
|
501
|
+
&& finalBody.input.some(item => isPlainObject(item) && item.type === "web_search_call")) {
|
|
502
|
+
const referencedIds = new Set(finalBody.input.flatMap(item => isPlainObject(item)
|
|
503
|
+
&& item.type === "item_reference" && typeof item.id === "string" ? [item.id] : []));
|
|
504
|
+
finalBody = { ...finalBody, input: renderCompactionSearchHistoryItems(finalBody.input, referencedIds) };
|
|
505
|
+
}
|
|
577
506
|
observeOutbound(parsed._rawBody, finalBody, headers);
|
|
578
507
|
if (!isCanonicalOpenAiForwardProvider(provider)) {
|
|
579
508
|
validateFinalCustomToolCompatibility(finalBody, provider.supportsResponsesCustomTools);
|
|
@@ -656,9 +585,11 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
656
585
|
case "error":
|
|
657
586
|
yield { type: "error", message: responsesErrorMessage(payload.response ?? payload) };
|
|
658
587
|
return;
|
|
659
|
-
case "response.incomplete":
|
|
660
|
-
|
|
588
|
+
case "response.incomplete": {
|
|
589
|
+
const reason = responsesErrorMessage(payload.response ?? payload);
|
|
590
|
+
yield { type: "incomplete", reason: truncationReasonFor(reason) ?? reason };
|
|
661
591
|
return;
|
|
592
|
+
}
|
|
662
593
|
case "response.completed":
|
|
663
594
|
{
|
|
664
595
|
completedSeen = true;
|
|
@@ -751,7 +682,8 @@ export function createResponsesPassthroughAdapter(provider: OcxProviderConfig):
|
|
|
751
682
|
return [{ type: "error", message: responsesErrorMessage(payload) }];
|
|
752
683
|
}
|
|
753
684
|
if (payload.status === "incomplete") {
|
|
754
|
-
|
|
685
|
+
const reason = responsesErrorMessage(payload);
|
|
686
|
+
return [{ type: "incomplete", reason: truncationReasonFor(reason) ?? reason }];
|
|
755
687
|
}
|
|
756
688
|
const usage = usageFromResponsesPayload(payload);
|
|
757
689
|
const output = Array.isArray(payload.output) ? payload.output : [];
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { expandRetainedCompactionInput } from "../../responses/retained-compaction";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import { COMPACT_PROMPT, appendCheckpointContinuationNote, compactionItemToText, decodeCompactionSummary, isCompactionItemType } from "../../responses/compaction";
|
|
3
4
|
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
@@ -6,6 +7,26 @@ import { isPlainObject } from "./internal";
|
|
|
6
7
|
import { activateDeferredTool } from "./tool-schema";
|
|
7
8
|
import { stripOpenAiOnlyWebSearchFields } from "./web-search";
|
|
8
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Provider-opaque replay metadata is client history, not an OpenAI input-item field (#7111).
|
|
12
|
+
* The caller gates this on OpenAI-operated destinations; arbitrary gateways may accept it.
|
|
13
|
+
* Strip only the known extension, never an item-field allowlist that can erase newer wire fields
|
|
14
|
+
* or nested tool data. Copy on write so a later Google route can still replay its signature.
|
|
15
|
+
*/
|
|
16
|
+
export function stripProviderInputItemMetadata(body: unknown): unknown {
|
|
17
|
+
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
18
|
+
let input: unknown[] | undefined;
|
|
19
|
+
for (let index = 0; index < body.input.length; index += 1) {
|
|
20
|
+
const item = body.input[index];
|
|
21
|
+
if (!isPlainObject(item) || !Object.hasOwn(item, "extra_content")) continue;
|
|
22
|
+
const next = { ...item };
|
|
23
|
+
delete next.extra_content;
|
|
24
|
+
input ??= [...body.input];
|
|
25
|
+
input[index] = next;
|
|
26
|
+
}
|
|
27
|
+
return input ? { ...body, input } : body;
|
|
28
|
+
}
|
|
29
|
+
|
|
9
30
|
export function stripInvalidItemIds(body: unknown): unknown {
|
|
10
31
|
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
11
32
|
|
|
@@ -246,7 +267,7 @@ export function stripItemIdsWhenUnstored(
|
|
|
246
267
|
* Normalize replayed compaction items for the destination backend.
|
|
247
268
|
*
|
|
248
269
|
* A compaction item carries an `encrypted_content` blob the client replays verbatim on every later
|
|
249
|
-
* turn, and only the backend that minted it can decode it. Proxy-minted `ocx1:` envelopes are
|
|
270
|
+
* turn, and only the backend that minted it can decode it. Proxy-minted `ocx1:` and structured `ocx2:` envelopes are
|
|
250
271
|
* transparent base64 rather than encryption, so no upstream can read them and they always become
|
|
251
272
|
* plain user messages. Native blobs have multiple possible minters, so a destination's ability to
|
|
252
273
|
* decode its own blobs does not make a blob from a previous serving identity portable. On a known
|
|
@@ -264,9 +285,10 @@ export function scrubOcxCompactionItems(
|
|
|
264
285
|
): unknown {
|
|
265
286
|
if (!isPlainObject(body) || !Array.isArray(body.input)) return body;
|
|
266
287
|
|
|
267
|
-
|
|
288
|
+
const expanded = expandRetainedCompactionInput(body.input) as unknown[];
|
|
289
|
+
let changed = expanded !== body.input;
|
|
268
290
|
let decodedCheckpoint = false;
|
|
269
|
-
const input =
|
|
291
|
+
const input = expanded.map(item => {
|
|
270
292
|
if (!isPlainObject(item) || !isCompactionItemType(item.type)) return item;
|
|
271
293
|
const encrypted = typeof item.encrypted_content === "string" ? item.encrypted_content : undefined;
|
|
272
294
|
if (encrypted === undefined) return item;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { IncomingMeta } from "../base";
|
|
2
|
+
import type { OcxProviderConfig } from "../../types";
|
|
3
|
+
import { CODEX_RESPONSES_LITE_HEADER } from "../../codex/forward-transport-headers";
|
|
4
|
+
import { CODEX_FORWARD_BASE_URL, isCanonicalOpenAiForwardProvider } from "../../providers/openai-tiers-destination";
|
|
5
|
+
import { normalizeForwardedClientHeaderName } from "../../lib/provider-client-headers";
|
|
6
|
+
import { openaiResponsesUrl } from "../openai-responses-url";
|
|
7
|
+
|
|
8
|
+
// Caller credentials are forwarded only to the canonical Codex destination, never by capability.
|
|
9
|
+
export const FORWARD_HEADERS = [
|
|
10
|
+
"authorization", "chatgpt-account-id", "openai-beta", "originator", "session_id", "session-id",
|
|
11
|
+
"thread-id", "x-client-request-id", "x-codex-beta-features", "x-codex-installation-id",
|
|
12
|
+
"x-codex-parent-thread-id", "x-codex-turn-metadata", "x-codex-turn-state", "x-codex-window-id",
|
|
13
|
+
"x-oai-attestation", "x-openai-subagent", "x-responsesapi-include-timing-metrics", CODEX_RESPONSES_LITE_HEADER,
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
/** Carry permitted client metadata across an internal compact handoff, never credentials. */
|
|
17
|
+
export function copyResponsesClientMetadata(target: Headers, incoming: Headers, provider: OcxProviderConfig): void {
|
|
18
|
+
const configured = Array.isArray(provider.forwardClientHeaders) ? provider.forwardClientHeaders.slice(0, 64) : [];
|
|
19
|
+
for (const rawName of ["user-agent", ...configured]) {
|
|
20
|
+
const name = normalizeForwardedClientHeaderName(rawName);
|
|
21
|
+
const value = name === null ? null : incoming.get(name);
|
|
22
|
+
if (name !== null && value && !target.has(name)) target.set(name, value);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Shared Responses URL/auth/header construction; capabilities never grant auth authority. */
|
|
27
|
+
export function buildResponsesTransport(provider: OcxProviderConfig, incoming: Pick<IncomingMeta, "headers">): {
|
|
28
|
+
url: string; headers: Record<string, string>;
|
|
29
|
+
} {
|
|
30
|
+
const headers: Record<string, string> = { "Content-Type": "application/json" };
|
|
31
|
+
let url: string;
|
|
32
|
+
if (provider.authMode === "forward") {
|
|
33
|
+
const mayForwardCallerCredentials = isCanonicalOpenAiForwardProvider(provider);
|
|
34
|
+
const baseUrl = mayForwardCallerCredentials ? CODEX_FORWARD_BASE_URL : provider.baseUrl.replace(/\/+$/, "");
|
|
35
|
+
url = `${baseUrl}/responses`;
|
|
36
|
+
if (provider.headers) Object.assign(headers, provider.headers);
|
|
37
|
+
const runtimeProvider = provider as {
|
|
38
|
+
_codexAccountOverride?: { accessToken: string; chatgptAccountId: string };
|
|
39
|
+
_codexAccountRequired?: boolean;
|
|
40
|
+
};
|
|
41
|
+
if (mayForwardCallerCredentials && runtimeProvider._codexAccountRequired && !runtimeProvider._codexAccountOverride) {
|
|
42
|
+
throw new Error("Codex pool account auth is required but unavailable");
|
|
43
|
+
}
|
|
44
|
+
if (mayForwardCallerCredentials) {
|
|
45
|
+
for (const name of FORWARD_HEADERS) {
|
|
46
|
+
const value = incoming.headers.get(name);
|
|
47
|
+
if (!value) continue;
|
|
48
|
+
if (name === CODEX_RESPONSES_LITE_HEADER) {
|
|
49
|
+
for (const existing of Object.keys(headers)) {
|
|
50
|
+
if (existing.toLowerCase() === name) delete headers[existing];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
headers[name] = value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const override = runtimeProvider._codexAccountOverride;
|
|
57
|
+
if (override && mayForwardCallerCredentials) {
|
|
58
|
+
headers.authorization = `Bearer ${override.accessToken}`;
|
|
59
|
+
headers["chatgpt-account-id"] = override.chatgptAccountId;
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
url = provider.responsesPath === undefined
|
|
63
|
+
? openaiResponsesUrl(provider.baseUrl)
|
|
64
|
+
: `${provider.baseUrl.replace(/\/$/, "")}${provider.responsesPath}`;
|
|
65
|
+
if (provider.apiKey) headers.Authorization = `Bearer ${provider.apiKey}`;
|
|
66
|
+
if (provider.headers) Object.assign(headers, provider.headers);
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(provider.forwardClientHeaders)) {
|
|
69
|
+
for (const rawName of provider.forwardClientHeaders.slice(0, 64)) {
|
|
70
|
+
const name = normalizeForwardedClientHeaderName(rawName);
|
|
71
|
+
if (name === null || Object.keys(headers).some(existing => existing.toLowerCase() === name)) continue;
|
|
72
|
+
const value = incoming.headers.get(name);
|
|
73
|
+
if (value) headers[name] = value;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!Object.keys(headers).some(name => name.toLowerCase() === "user-agent")) {
|
|
77
|
+
const userAgent = incoming.headers.get("user-agent");
|
|
78
|
+
if (userAgent) headers["User-Agent"] = userAgent;
|
|
79
|
+
}
|
|
80
|
+
return { url, headers };
|
|
81
|
+
}
|
|
@@ -52,8 +52,10 @@ const QODER_MCP_SERVER_NAME = "opencodex";
|
|
|
52
52
|
const QODER_MCP_SERVER_PATH = fileURLToPath(new URL("../coding-agent/mcp-server.ts", import.meta.url));
|
|
53
53
|
|
|
54
54
|
export function buildQoderChildEnv(profile: QoderProfile, apiKey: string, systemPrompt?: string): Record<string, string> {
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
// Each build reads its own brand prefix with no fallback: qodercn ignores QODER_EXPOSE_TOKEN_USAGE.
|
|
56
|
+
const envPrefix = profile.region === "cn" ? "QODERCN_" : "QODER_";
|
|
57
|
+
const promptEnv = `${envPrefix}APPEND_SYSTEM_PROMPT`;
|
|
58
|
+
return { ...baseScopedEnv(), NO_COLOR: "1", [`${envPrefix}EXPOSE_TOKEN_USAGE`]: "true", [profile.tokenEnv]: apiKey,
|
|
57
59
|
...(systemPrompt ? { [promptEnv]: systemPrompt } : {}) };
|
|
58
60
|
}
|
|
59
61
|
|
package/src/adapters/registry.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAnthropicAdapter } from "./anthropic";
|
|
2
|
+
import { createDshAccountAdapter } from "./dsh-account";
|
|
2
3
|
import { createAzureAdapter } from "./azure";
|
|
3
4
|
import type { ProviderAdapter } from "./base";
|
|
4
5
|
import { createClaudeCliAdapter } from "./claude-cli/adapter";
|
|
@@ -154,6 +155,10 @@ export const ADAPTER_REGISTRY = {
|
|
|
154
155
|
contractParent: "codebuddy",
|
|
155
156
|
create: (provider: OcxProviderConfig, _context: AdapterFactoryContext) => createClaudeCliAdapter(provider),
|
|
156
157
|
},
|
|
158
|
+
"dsh-account": {
|
|
159
|
+
contractParent: "anthropic",
|
|
160
|
+
create: (provider: OcxProviderConfig, context: AdapterFactoryContext) => createDshAccountAdapter(provider, context),
|
|
161
|
+
},
|
|
157
162
|
} as const satisfies Record<string, AdapterDefinition>;
|
|
158
163
|
|
|
159
164
|
export type AdapterId = keyof typeof ADAPTER_REGISTRY;
|
package/src/adapters/zed.ts
CHANGED
|
@@ -53,7 +53,9 @@ function delegateProvider(provider: OcxProviderConfig, zedProvider: ZedProvider)
|
|
|
53
53
|
return { ...common, adapter: "google", baseUrl: "https://generativelanguage.googleapis.com", googleMode: "ai-studio" };
|
|
54
54
|
}
|
|
55
55
|
if (zedProvider === "open_ai") {
|
|
56
|
-
|
|
56
|
+
// The builder targets Zed's /completions envelope. Keep its destination set to Zed as well,
|
|
57
|
+
// so OpenAI-private Responses fields are stripped instead of being sent through to Zed.
|
|
58
|
+
return { ...common, adapter: "openai-responses", baseUrl: provider.baseUrl };
|
|
57
59
|
}
|
|
58
60
|
return { ...common, adapter: "openai-chat", baseUrl: "https://api.x.ai/v1" };
|
|
59
61
|
}
|
|
@@ -300,6 +302,29 @@ export function createZedAdapter(provider: OcxProviderConfig): ProviderAdapter {
|
|
|
300
302
|
}
|
|
301
303
|
}
|
|
302
304
|
if (!isRecord(providerRequest)) throw new Error("Zed delegate produced a non-object request body");
|
|
305
|
+
if (zedProvider === "open_ai" && Array.isArray(providerRequest.input)) {
|
|
306
|
+
// Zed's OpenAI Responses parser accepts user/assistant/system/tool roles but rejects
|
|
307
|
+
// OpenAI's newer developer role. System is the closest supported role for instructions.
|
|
308
|
+
providerRequest.input = providerRequest.input.map(item =>
|
|
309
|
+
isRecord(item) && item.type === "message" && item.role === "developer"
|
|
310
|
+
? { ...item, role: "system" }
|
|
311
|
+
: item,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
if (zedProvider === "open_ai" && Array.isArray(providerRequest.tools)) {
|
|
315
|
+
// Zed's Responses decoder supports function/custom tools, not Codex's built-in web_search.
|
|
316
|
+
const supported = (tool: unknown) => isRecord(tool) && (tool.type === "function" || tool.type === "custom");
|
|
317
|
+
const tools = providerRequest.tools.filter(supported);
|
|
318
|
+
providerRequest.tools = tools;
|
|
319
|
+
const choice = providerRequest.tool_choice;
|
|
320
|
+
if (isRecord(choice) && choice.type === "allowed_tools" && Array.isArray(choice.tools)) {
|
|
321
|
+
const allowed = choice.tools.filter(supported);
|
|
322
|
+
providerRequest.tool_choice = allowed.length ? { ...choice, tools: allowed } : "none";
|
|
323
|
+
} else if ((isRecord(choice) && !supported(choice)) || (choice === "required" && tools.length === 0)) {
|
|
324
|
+
// Match the shared hosted-tool policy when no supported selection remains.
|
|
325
|
+
providerRequest.tool_choice = "none";
|
|
326
|
+
}
|
|
327
|
+
}
|
|
303
328
|
delegate = { provider: zedProvider, adapter: selected };
|
|
304
329
|
const requestUrl = `${provider.baseUrl.replace(/\/+$/, "")}/completions`;
|
|
305
330
|
return {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { OcxConfig } from "../types";
|
|
2
|
+
import { sanitizeLogMetadataString } from "../lib/redact";
|
|
3
|
+
|
|
4
|
+
export type AdvisorTrigger = "manual" | "preflight" | "repair_failure";
|
|
5
|
+
export interface AdvisorActivity {
|
|
6
|
+
id: string;
|
|
7
|
+
startedAt: number;
|
|
8
|
+
trigger: AdvisorTrigger;
|
|
9
|
+
workerModel: string;
|
|
10
|
+
advisorModel: string;
|
|
11
|
+
requestedEffort: string;
|
|
12
|
+
/** Adapter serialization evidence, not a claim about the provider's hidden reasoning. */
|
|
13
|
+
effectiveEffort: string | null;
|
|
14
|
+
status: "running" | "ok" | "failed" | "cancelled" | "blocked";
|
|
15
|
+
durationMs: number | null;
|
|
16
|
+
usage?: { inputTokens?: number; outputTokens?: number; totalTokens?: number };
|
|
17
|
+
blockReason?: "consent" | "settings" | "budget" | "limit" | "duplicate";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Metadata only. No prompts, advice, commands, task/thread ids, errors, or hidden reasoning. */
|
|
21
|
+
export function createAdvisorActivityStore() {
|
|
22
|
+
const entries: AdvisorActivity[] = [];
|
|
23
|
+
const retain = (now: number) => {
|
|
24
|
+
while (entries.length && (entries.length > 100 || entries[0]!.startedAt < now - 86_400_000)) entries.shift();
|
|
25
|
+
};
|
|
26
|
+
return {
|
|
27
|
+
start(input: Pick<AdvisorActivity, "trigger" | "workerModel" | "advisorModel" | "requestedEffort">, now = Date.now()) {
|
|
28
|
+
const entry: AdvisorActivity = {
|
|
29
|
+
id: crypto.randomUUID(), startedAt: now, ...input,
|
|
30
|
+
workerModel: sanitizeLogMetadataString(input.workerModel, 200) ?? "unknown",
|
|
31
|
+
advisorModel: sanitizeLogMetadataString(input.advisorModel, 200) ?? "unknown",
|
|
32
|
+
effectiveEffort: null, status: "running", durationMs: null,
|
|
33
|
+
};
|
|
34
|
+
entries.push(entry);
|
|
35
|
+
retain(now);
|
|
36
|
+
return entry.id;
|
|
37
|
+
},
|
|
38
|
+
finish(id: string, outcome: Pick<AdvisorActivity, "status" | "durationMs"> & Partial<Pick<AdvisorActivity, "usage" | "effectiveEffort" | "blockReason">>) {
|
|
39
|
+
const entry = entries.find(item => item.id === id);
|
|
40
|
+
if (entry) Object.assign(entry, outcome);
|
|
41
|
+
},
|
|
42
|
+
snapshot(now = Date.now()): AdvisorActivity[] {
|
|
43
|
+
retain(now);
|
|
44
|
+
return entries.toReversed().map(entry => ({ ...entry, ...(entry.usage ? { usage: { ...entry.usage } } : {}) }));
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export type AdvisorActivityStore = ReturnType<typeof createAdvisorActivityStore>;
|
|
50
|
+
const stores = new WeakMap<Pick<OcxConfig, "advisor">, AdvisorActivityStore>();
|
|
51
|
+
export function advisorActivityStore(config: Pick<OcxConfig, "advisor">): AdvisorActivityStore {
|
|
52
|
+
let store = stores.get(config);
|
|
53
|
+
if (!store) { store = createAdvisorActivityStore(); stores.set(config, store); }
|
|
54
|
+
return store;
|
|
55
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Database } from "bun:sqlite";
|
|
2
|
+
import { chmodSync, closeSync, lstatSync, mkdirSync, openSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { getConfigDir } from "../config/paths";
|
|
5
|
+
import { recordOwnedConfigPath } from "../lib/config-ownership";
|
|
6
|
+
import { assertNotRealHomeUnderTest } from "../lib/test-home-guard";
|
|
7
|
+
import { hardenSecretDir, hardenSecretPath } from "../lib/windows-secret-acl";
|
|
8
|
+
|
|
9
|
+
export type AdvisorBudgetReservation = { ok: true; used: number } | { ok: false; reason: "exhausted" | "unavailable" };
|
|
10
|
+
export interface AdvisorTaskBudget {
|
|
11
|
+
reserve(task: string, limit: number, now: number): AdvisorBudgetReservation;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reserve BEFORE dispatch. Failed/disconnected calls may still cost money, so reservations
|
|
16
|
+
* are never refunded. Only SHA-256 task digests, counts and expiry times reach disk. Opening
|
|
17
|
+
* lazily and closing before returning also keeps disabled installs and shutdown timer-free.
|
|
18
|
+
*/
|
|
19
|
+
export function createAdvisorTaskBudget(path?: string): AdvisorTaskBudget {
|
|
20
|
+
return {
|
|
21
|
+
reserve(task, limit, now) {
|
|
22
|
+
let db: Database | undefined;
|
|
23
|
+
try {
|
|
24
|
+
const key = new Bun.CryptoHasher("sha256").update(`advisor-budget\0${task}`).digest("hex");
|
|
25
|
+
const file = path ?? join(getConfigDir(), "advisor-budget.sqlite");
|
|
26
|
+
const dir = dirname(file);
|
|
27
|
+
assertNotRealHomeUnderTest(dir);
|
|
28
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
29
|
+
if (!lstatSync(dir).isDirectory() || lstatSync(dir).isSymbolicLink()) throw new Error("invalid directory");
|
|
30
|
+
hardenSecretDir(dir, { required: true });
|
|
31
|
+
if (!path) {
|
|
32
|
+
recordOwnedConfigPath(dir, file);
|
|
33
|
+
recordOwnedConfigPath(dir, `${file}-journal`);
|
|
34
|
+
}
|
|
35
|
+
try { closeSync(openSync(file, "wx", 0o600)); }
|
|
36
|
+
catch (error) { if ((error as { code?: string }).code !== "EEXIST") throw error; }
|
|
37
|
+
const stat = lstatSync(file);
|
|
38
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) throw new Error("invalid file");
|
|
39
|
+
if (process.platform !== "win32") chmodSync(file, 0o600);
|
|
40
|
+
hardenSecretPath(file, { required: true });
|
|
41
|
+
db = new Database(file);
|
|
42
|
+
db.exec("PRAGMA busy_timeout = 0; CREATE TABLE IF NOT EXISTS tasks (task TEXT PRIMARY KEY, used INTEGER NOT NULL, expires INTEGER NOT NULL)");
|
|
43
|
+
const connection = db;
|
|
44
|
+
return connection.transaction((): AdvisorBudgetReservation => {
|
|
45
|
+
connection.query("DELETE FROM tasks WHERE expires <= ?").run(now);
|
|
46
|
+
const current = connection.query<{ used: number }, [string]>("SELECT used FROM tasks WHERE task = ?").get(key);
|
|
47
|
+
if (current && current.used >= limit) return { ok: false, reason: "exhausted" };
|
|
48
|
+
// Never evict a live task to make room: that would reset its spending limit.
|
|
49
|
+
if (!current && connection.query<{ n: number }, []>("SELECT count(*) AS n FROM tasks").get()!.n >= 512) {
|
|
50
|
+
return { ok: false, reason: "unavailable" };
|
|
51
|
+
}
|
|
52
|
+
connection.query("INSERT INTO tasks VALUES (?, 1, ?) ON CONFLICT(task) DO UPDATE SET used = used + 1")
|
|
53
|
+
.run(key, now + 86_400_000);
|
|
54
|
+
return { ok: true, used: (current?.used ?? 0) + 1 };
|
|
55
|
+
}).immediate();
|
|
56
|
+
} catch {
|
|
57
|
+
// An unavailable/corrupt/locked budget blocks the expert call, never the worker.
|
|
58
|
+
return { ok: false, reason: "unavailable" };
|
|
59
|
+
} finally { try { db?.close(); } catch { /* reservation outcome is authoritative */ } }
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}
|