@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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { IncomingMeta, ProviderAdapter } from "./base";
|
|
2
|
+
import { createAnthropicAdapter } from "./anthropic";
|
|
3
|
+
import type { AdapterFactoryContext } from "./registry";
|
|
4
|
+
import type { OcxParsedRequest, OcxProviderConfig } from "../types";
|
|
5
|
+
import { resolveDshAccountMessagesUrl } from "../providers/dsh-account-destination";
|
|
6
|
+
|
|
7
|
+
export { DSH_ACCOUNT_DEFAULT_MESSAGES_URL, resolveDshAccountMessagesUrl } from "../providers/dsh-account-destination";
|
|
8
|
+
|
|
9
|
+
export const DSH_ACCOUNT_RESERVED_HEADERS = new Set([
|
|
10
|
+
"x-dsh-auth-token",
|
|
11
|
+
"anthropic-version",
|
|
12
|
+
"authorization",
|
|
13
|
+
"x-api-key",
|
|
14
|
+
"host",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export function createDshAccountAdapter(
|
|
18
|
+
provider: OcxProviderConfig,
|
|
19
|
+
_context?: AdapterFactoryContext,
|
|
20
|
+
): ProviderAdapter {
|
|
21
|
+
// Use key authMode internally so that createAnthropicAdapter doesn't inject Claude Code headers/prompt.
|
|
22
|
+
const baseAdapter = createAnthropicAdapter({
|
|
23
|
+
...provider,
|
|
24
|
+
authMode: "key",
|
|
25
|
+
}, "none");
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
...baseAdapter,
|
|
29
|
+
name: "dsh-account",
|
|
30
|
+
|
|
31
|
+
formatErrorBody(status: number, headers: Headers, payloadText: string): string {
|
|
32
|
+
if (status === 401) {
|
|
33
|
+
return "DeepSeek Harness account authentication failed (401 / ACCOUNT_TOKEN_INVALID). Please sign in to DeepSeek Harness Desktop and re-import.";
|
|
34
|
+
}
|
|
35
|
+
return baseAdapter.formatErrorBody?.(status, headers, payloadText) ?? payloadText;
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
async buildRequest(parsed: OcxParsedRequest, incoming: IncomingMeta) {
|
|
39
|
+
const token = provider.apiKey?.trim();
|
|
40
|
+
if (!token) {
|
|
41
|
+
throw new Error("DeepSeek Harness account token missing — import your account in Providers > DeepSeek Account (DSH)");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Fail-closed if custom provider configuration attempts to set any reserved header
|
|
45
|
+
if (provider.headers) {
|
|
46
|
+
for (const key of Object.keys(provider.headers)) {
|
|
47
|
+
if (DSH_ACCOUNT_RESERVED_HEADERS.has(key.toLowerCase())) {
|
|
48
|
+
throw new Error(`Reserved header "${key}" cannot be overridden in dsh-account provider configuration`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const req = await baseAdapter.buildRequest(parsed, incoming);
|
|
54
|
+
const url = resolveDshAccountMessagesUrl(provider.baseUrl);
|
|
55
|
+
|
|
56
|
+
const headers: Record<string, string> = { ...req.headers };
|
|
57
|
+
|
|
58
|
+
// Apply custom provider headers strictly before authoritative authentication headers
|
|
59
|
+
if (provider.headers) {
|
|
60
|
+
for (const [key, value] of Object.entries(provider.headers)) {
|
|
61
|
+
if (!DSH_ACCOUNT_RESERVED_HEADERS.has(key.toLowerCase()) && typeof value === "string") {
|
|
62
|
+
headers[key] = value;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Authoritative cleanup: strip any API key or Claude headers
|
|
68
|
+
for (const key of Object.keys(headers)) {
|
|
69
|
+
const lower = key.toLowerCase();
|
|
70
|
+
if (
|
|
71
|
+
lower === "authorization"
|
|
72
|
+
|| lower === "x-api-key"
|
|
73
|
+
|| lower === "x-dsh-auth-token"
|
|
74
|
+
|| lower === "anthropic-version"
|
|
75
|
+
|| lower === "anthropic-beta"
|
|
76
|
+
|| lower === "x-claude-code-session-id"
|
|
77
|
+
|| lower === "x-client-request-id"
|
|
78
|
+
) {
|
|
79
|
+
delete headers[key];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Set authoritative DeepSeek Harness Account headers strictly at the end
|
|
84
|
+
headers["x-dsh-auth-token"] = token;
|
|
85
|
+
headers["anthropic-version"] = "2023-06-01";
|
|
86
|
+
if (!headers["Content-Type"]) headers["Content-Type"] = "application/json";
|
|
87
|
+
if (!headers["Accept"]) headers["Accept"] = parsed.stream ? "text/event-stream" : "application/json";
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
...req,
|
|
91
|
+
url,
|
|
92
|
+
headers,
|
|
93
|
+
redirect: "manual",
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gemini progress-text history trimming.
|
|
3
|
+
*
|
|
4
|
+
* Gemini models, observed on Gemini 4 Argon over Cloud Code Assist, sometimes skip their thinking
|
|
5
|
+
* channel on a tool-calling turn and plan in the visible text instead. The provider sends that
|
|
6
|
+
* planning as an ordinary text part without a thought marker, for example a progress line in the
|
|
7
|
+
* user's language followed by "Let's read google.ts 580-1700 first." Once one turn like that is
|
|
8
|
+
* replayed as history, the model copies the pattern on the turns that follow, so the leak grows
|
|
9
|
+
* through the rest of the session. Replaying the same 300K-token request with the leaked tails
|
|
10
|
+
* removed from the history stopped the leak, while stronger system-prompt wording did not.
|
|
11
|
+
*
|
|
12
|
+
* The fix is on the history side only. Text that precedes the tool call of a replayed model turn is
|
|
13
|
+
* intermediate progress, and the Google brevity instruction already asks for a single short line
|
|
14
|
+
* there, so it is replayed as its lead sentence. A turn Codex marks `final_answer` and text after a
|
|
15
|
+
* tool call are replayed whole. Visible output is never rewritten, final
|
|
16
|
+
* answers (turns without tool calls) are replayed in full, and the split is punctuation-based so
|
|
17
|
+
* it works the same in every language without matching any wording.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sentence end: ASCII terminal punctuation followed by whitespace or the end (so "google.ts" and
|
|
22
|
+
* "3.5" are not boundaries), a script's own sentence terminator (CJK 。!?, Devanagari
|
|
23
|
+
* ।॥, Arabic ۔؟, Armenian ։, Ethiopic ።, Myanmar ။), or a line break. Closing quotes, brackets and
|
|
24
|
+
* Markdown emphasis right after the punctuation belong to the sentence.
|
|
25
|
+
*/
|
|
26
|
+
const SENTENCE_END = /[.!?]["'\u201D\u2019)\]*_`]*(?=\s|$)|[\u3002\uFF01\uFF1F\u0964\u0965\u06D4\u061F\u0589\u1362\u104B]["'\u201D\u2019\u300D\u300F)\]*_`]*|\n/g;
|
|
27
|
+
|
|
28
|
+
/** A boundary that only closes a leading list marker ("1.", "a.") or a common abbreviation ("e.g."). */
|
|
29
|
+
const NOT_A_SENTENCE = /^(?:\d{1,3}|[a-z])\.$|(?:^|\s)(?:e\.g|i\.e|etc|vs)\.$/i;
|
|
30
|
+
|
|
31
|
+
/** Whether replayed progress text for this model is trimmed to its lead sentence. */
|
|
32
|
+
export function trimsGeminiProgressHistory(modelId: string): boolean {
|
|
33
|
+
return /gemini/i.test(modelId);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The lead sentence of a replayed progress text, or undefined when nothing usable remains.
|
|
38
|
+
* Text without a sentence boundary is already a single sentence and is returned whole.
|
|
39
|
+
*/
|
|
40
|
+
export function geminiProgressLead(text: string): string | undefined {
|
|
41
|
+
const trimmed = text.trim();
|
|
42
|
+
if (trimmed.length === 0) return undefined;
|
|
43
|
+
SENTENCE_END.lastIndex = 0;
|
|
44
|
+
let end = trimmed.length;
|
|
45
|
+
for (let match = SENTENCE_END.exec(trimmed); match; match = SENTENCE_END.exec(trimmed)) {
|
|
46
|
+
const boundary = match.index + match[0].length;
|
|
47
|
+
if (!NOT_A_SENTENCE.test(trimmed.slice(0, boundary).trim())) { end = boundary; break; }
|
|
48
|
+
}
|
|
49
|
+
const lead = trimmed.slice(0, end).trim();
|
|
50
|
+
// A code-fence opener is not a sentence; replaying it alone would leave an unclosed fence.
|
|
51
|
+
if (lead.length === 0 || lead.startsWith("```")) return undefined;
|
|
52
|
+
return lead;
|
|
53
|
+
}
|
package/src/adapters/google.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { openCodeZenGeminiEndpoint } from "../types/opencode-zen";
|
|
1
2
|
import type { AdapterFetchContext, AdapterRequest, ProviderAdapter } from "./base";
|
|
2
3
|
import { debugDroppedFrame, debugProviderDiagnosticLazy } from "../lib/debug";
|
|
3
4
|
import { createToolCallIdAllocator } from "./tool-call-id";
|
|
5
|
+
import { openCodeGoGeminiEndpoint } from "../types/opencode-go";
|
|
4
6
|
import { createImageBudget, materializeInlineImage, MAX_ENCODED_BYTES_PER_IMAGE, artifactHttpUrl } from "../images/artifacts";
|
|
5
7
|
import type {
|
|
6
8
|
AdapterEvent,
|
|
@@ -17,7 +19,7 @@ import type {
|
|
|
17
19
|
import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolChoiceToolPredicate } from "../types";
|
|
18
20
|
import type { OcxTool } from "../types";
|
|
19
21
|
import { contentPartsToText, parseDataUrl } from "./image";
|
|
20
|
-
import { getVertexAccessToken } from "../lib/gcp-adc";
|
|
22
|
+
import { GCP_CREDENTIAL_MARKER_PREFIX, gcpCredentialMarkerAccount, getVertexAccessToken } from "../lib/gcp-adc";
|
|
21
23
|
import { fetchAntigravityWithRetry, fetchVertexWithRetry } from "./google-http";
|
|
22
24
|
import { safeAntigravityHttpErrorMessage, safeVertexHttpErrorMessage } from "./google-errors";
|
|
23
25
|
import { isVertexTruncatedTurn, vertexTruncationErrorEvent } from "./google-truncation";
|
|
@@ -42,6 +44,7 @@ import {
|
|
|
42
44
|
type TranslatorBudget,
|
|
43
45
|
} from "../lib/translator-budget";
|
|
44
46
|
import { buildNonOpenAIToolCatalogNudgeForTools } from "./tool-catalog-nudge";
|
|
47
|
+
import { geminiProgressLead, trimsGeminiProgressHistory } from "./google-progress-history";
|
|
45
48
|
import { configuredReasoningEfforts, mapReasoningEffort } from "../reasoning-effort";
|
|
46
49
|
|
|
47
50
|
// Google-family models (Gemini/Vertex/Antigravity) tend to emit long running commentary between
|
|
@@ -174,7 +177,15 @@ function resolveDirectGeminiWireModelId(modelId: string, applyRenames: boolean):
|
|
|
174
177
|
|
|
175
178
|
/** Vertex API key: provider.apiKey if it looks real (not a sentinel), else GOOGLE_CLOUD_API_KEY env. */
|
|
176
179
|
function resolveVertexApiKey(optKey?: string): string | undefined {
|
|
177
|
-
|
|
180
|
+
// A `gcp-sa:` marker is the keychain-backed credential reference (see gcp-adc): it is not
|
|
181
|
+
// API-key material, so it must not be sent as x-goog-api-key — the ADC branch below handles it.
|
|
182
|
+
// AND the marker means the operator named one credential explicitly: the GOOGLE_CLOUD_API_KEY
|
|
183
|
+
// env fallback must NOT fire (it would silently route this provider's inference through the
|
|
184
|
+
// ambient env key instead of the configured service account).
|
|
185
|
+
if (optKey && optKey.startsWith(GCP_CREDENTIAL_MARKER_PREFIX)) return undefined;
|
|
186
|
+
const realKey = optKey && !optKey.startsWith("<") && optKey !== "N/A"
|
|
187
|
+
? optKey
|
|
188
|
+
: undefined;
|
|
178
189
|
return realKey || process.env.GOOGLE_CLOUD_API_KEY;
|
|
179
190
|
}
|
|
180
191
|
|
|
@@ -456,11 +467,26 @@ function messagesToGeminiFormat(
|
|
|
456
467
|
const aMsg = msg as OcxAssistantMessage;
|
|
457
468
|
const parts: unknown[] = [];
|
|
458
469
|
const toolCalls: Array<{ wireCallId: string; wireName: string }> = [];
|
|
470
|
+
// Progress text before a Gemini tool call replays as its lead sentence, so planning the
|
|
471
|
+
// model leaked into visible text is not copied forward (google-progress-history.ts).
|
|
472
|
+
const trimProgress = trimsGeminiProgressHistory(identityModelId)
|
|
473
|
+
&& aMsg.phase !== "final_answer"
|
|
474
|
+
&& aMsg.content.some(part => part.type === "toolCall");
|
|
475
|
+
let progressLeadKept = false;
|
|
476
|
+
let afterToolCall = false;
|
|
459
477
|
for (const p of aMsg.content) {
|
|
460
478
|
if (p.type === "text") {
|
|
461
|
-
|
|
479
|
+
let text: unknown = (p as OcxTextContent).text;
|
|
480
|
+
if (trimProgress && !afterToolCall) {
|
|
481
|
+
if (progressLeadKept || typeof text !== "string") continue;
|
|
482
|
+
text = geminiProgressLead(text);
|
|
483
|
+
if (text === undefined) continue;
|
|
484
|
+
progressLeadKept = true;
|
|
485
|
+
}
|
|
486
|
+
const textPart = geminiTextPart(text);
|
|
462
487
|
if (textPart) parts.push(textPart);
|
|
463
488
|
} else if (p.type === "toolCall") {
|
|
489
|
+
afterToolCall = true;
|
|
464
490
|
const tc = p as OcxToolCall;
|
|
465
491
|
// Preserve the thought signature on the function-call part so Antigravity/Gemini-3
|
|
466
492
|
// reasoning continuity survives history-driven (stateless) turns, not just same-process
|
|
@@ -864,6 +890,7 @@ function invalidGoogleShapeEvent(
|
|
|
864
890
|
};
|
|
865
891
|
}
|
|
866
892
|
|
|
893
|
+
/** Build Gemini transport and response parsing for AI Studio, Vertex, Cloud Code Assist or Go. */
|
|
867
894
|
export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapter {
|
|
868
895
|
const toolSchemaPolicy = provider.googleToolSchemaPolicy ?? "compatible";
|
|
869
896
|
const toolSchemaProfile = {
|
|
@@ -945,6 +972,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
945
972
|
}
|
|
946
973
|
: {}),
|
|
947
974
|
|
|
975
|
+
/** Compile an admitted request into the selected Gemini endpoint, body and authentication. */
|
|
948
976
|
async buildRequest(parsed: OcxParsedRequest) {
|
|
949
977
|
// Structured-output admission runs FIRST, before messagesToGeminiFormat writes
|
|
950
978
|
// lastInjectedCallIds/lastReasoningReplayScope: a refused request must not leave
|
|
@@ -983,7 +1011,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
983
1011
|
mapReasoningEffort(provider, parsed.modelId, parsed.options.reasoning),
|
|
984
1012
|
provider.baseUrl,
|
|
985
1013
|
).wireModelId
|
|
986
|
-
: provider.googleMode === "vertex"
|
|
1014
|
+
: provider.googleMode === "vertex" || openCodeZenGeminiEndpoint(provider, parsed.modelId) !== undefined
|
|
987
1015
|
? parsed.modelId
|
|
988
1016
|
: resolveDirectGeminiWireModelId(parsed.modelId, provider.directGeminiWireRenames !== false);
|
|
989
1017
|
returnsThoughtSummaries = provider.googleMode === "cloud-code-assist"
|
|
@@ -1197,16 +1225,31 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
1197
1225
|
if (locationError) throw new Error(locationError);
|
|
1198
1226
|
const host = location === "global" ? "aiplatform.googleapis.com" : `${location}-aiplatform.googleapis.com`;
|
|
1199
1227
|
const url = `https://${host}/v1/projects/${project}/locations/${location}/publishers/google/models/${parsed.modelId}:${method}${streamParam}`;
|
|
1200
|
-
|
|
1228
|
+
// The marker account travels per-request as an argument (never process-global state): a
|
|
1229
|
+
// module-level "active marker" written at routing time would race across concurrent
|
|
1230
|
+
// requests for different providers and could resolve one call with another's credential.
|
|
1231
|
+
const markerAccount = gcpCredentialMarkerAccount(provider.apiKey);
|
|
1232
|
+
const token = await getVertexAccessToken({ markerAccount });
|
|
1201
1233
|
headers["Authorization"] = `Bearer ${token}`;
|
|
1202
1234
|
return { url, method: "POST", headers, body: JSON.stringify(compiled.body) };
|
|
1203
1235
|
}
|
|
1204
1236
|
|
|
1205
|
-
//
|
|
1206
|
-
const
|
|
1237
|
+
// Go and Zen use separate guarded Gemini endpoints; AI Studio retains /v1beta.
|
|
1238
|
+
const goEndpoint = openCodeGoGeminiEndpoint(provider, routedModelId);
|
|
1239
|
+
const zenEndpoint = openCodeZenGeminiEndpoint(provider, routedModelId);
|
|
1240
|
+
const endpoint = goEndpoint ?? zenEndpoint ?? `${provider.baseUrl}/v1beta/models/${routedModelId}`;
|
|
1241
|
+
const url = `${endpoint}:${method}${streamParam}`;
|
|
1207
1242
|
const apiKey = provider.apiKey?.trim();
|
|
1208
|
-
if (!apiKey) throw new Error("google (AI Studio) requires a non-empty API key
|
|
1209
|
-
|
|
1243
|
+
if (!apiKey) throw new Error(`${goEndpoint ? "OpenCode Go" : zenEndpoint ? "OpenCode Zen" : "google (AI Studio)"} requires a non-empty API key`);
|
|
1244
|
+
if (goEndpoint) {
|
|
1245
|
+
// Console inference documents Bearer auth. Send only the selected Go key.
|
|
1246
|
+
for (const name of Object.keys(headers)) {
|
|
1247
|
+
if (["authorization", "x-goog-api-key", "x-api-key"].includes(name.toLowerCase())) delete headers[name];
|
|
1248
|
+
}
|
|
1249
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1250
|
+
} else {
|
|
1251
|
+
headers["x-goog-api-key"] = apiKey;
|
|
1252
|
+
}
|
|
1210
1253
|
|
|
1211
1254
|
const compiled = compileGoogleWireBody(body, toolSchemaProfile, toolSchemaPolicy);
|
|
1212
1255
|
reportToolSchemaLoss(compiled.toolSchemaLossReport);
|
|
@@ -7,6 +7,7 @@ import { EMPTY_TOOL_OUTPUT_ANNOTATION, isWhitespaceOnlyTextPartArray } from "../
|
|
|
7
7
|
import { identifyRoutedModel } from "../identity";
|
|
8
8
|
import { buildNonOpenAIToolCatalogNudgeForTools, shouldInjectNonOpenAIToolCatalogNudge } from "../tool-catalog-nudge";
|
|
9
9
|
import { peekReasoningForCall } from "../../responses/reasoning-replay-cache";
|
|
10
|
+
import { chatReasoningReplayPolicy } from "./reasoning-replay";
|
|
10
11
|
import type { ClaudeThinkingProjection } from "../../lib/claude-request-projection";
|
|
11
12
|
import { inlineDocumentDataUrl } from "../../responses/inline-document";
|
|
12
13
|
import type { OcxAssistantMessage, OcxContentPart, OcxParsedRequest, OcxProviderConfig, OcxTextContent, OcxThinkingContent, OcxToolCall } from "../../types";
|
|
@@ -75,7 +76,7 @@ export function openAIChatSerializesThinking(
|
|
|
75
76
|
modelId: string,
|
|
76
77
|
): ClaudeThinkingProjection {
|
|
77
78
|
return {
|
|
78
|
-
text:
|
|
79
|
+
text: chatReasoningReplayPolicy(provider, modelId).preserve,
|
|
79
80
|
// The wire has no `signature` field — base64 replay tokens are Anthropic-wire-only.
|
|
80
81
|
signature: false,
|
|
81
82
|
// A `redacted_thinking` block is opaque provider data with no Chat representation: the
|
|
@@ -267,7 +268,7 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
267
268
|
// recorded under every call id — join unique texts only.
|
|
268
269
|
if (cached.length > 0) {
|
|
269
270
|
reasoningContent = [...new Set(cached)].join("\n");
|
|
270
|
-
} else if (
|
|
271
|
+
} else if (chatReasoningReplayPolicy(provider, parsed.modelId).placeholder) {
|
|
271
272
|
// Fallback (extends #950 and #1193; fixes #5421): the replay cache is
|
|
272
273
|
// bounded (64 entries / 256 KiB / 1 h TTL) and always misses on
|
|
273
274
|
// long sessions, and some thinking/tool rounds carry no recorded
|
|
@@ -354,7 +355,7 @@ export function messagesToChatFormat(parsed: OcxParsedRequest, provider: OcxProv
|
|
|
354
355
|
const orphanReasoning =
|
|
355
356
|
cachedReasoning
|
|
356
357
|
|| (wireSerializesThinking
|
|
357
|
-
&&
|
|
358
|
+
&& chatReasoningReplayPolicy(provider, parsed.modelId).placeholder
|
|
358
359
|
? " "
|
|
359
360
|
: undefined);
|
|
360
361
|
const orphanReasoningFields: Record<string, unknown> = !orphanReasoning
|
|
@@ -14,6 +14,8 @@ import { modelRecordValue } from "../../reasoning-effort";
|
|
|
14
14
|
import { modelInList, type OcxProviderConfig } from "../../types";
|
|
15
15
|
import { chatParallelToolCallsWireValue } from "./parallel-tool-calls";
|
|
16
16
|
import { applyExplicitChatReasoningWirePolicy } from "./reasoning-wire";
|
|
17
|
+
import { replayNativeChatReasoning } from "./reasoning-replay";
|
|
18
|
+
import type { OcxReasoningReplayScopeRef } from "../../types";
|
|
17
19
|
|
|
18
20
|
const CHAT_PASSTHROUGH_FIELDS = [
|
|
19
21
|
"audio",
|
|
@@ -55,17 +57,19 @@ export function buildOpenAIChatPassthroughRequest(
|
|
|
55
57
|
fastPolicy: ResolvedFastPolicy = fastPolicyForModel(provider, modelId, undefined, "chat"),
|
|
56
58
|
fastMode?: boolean,
|
|
57
59
|
providerName?: string,
|
|
60
|
+
replayScope?: OcxReasoningReplayScopeRef,
|
|
58
61
|
): AdapterRequest {
|
|
59
62
|
const { url, headers, hasCredential } = openAIChatTransport(provider);
|
|
60
63
|
|
|
61
64
|
const body: Record<string, unknown> = {
|
|
62
65
|
model: provider.modelSuffixBracketStrip ? stripBracketedModelSuffix(modelId) : modelId,
|
|
63
|
-
// The caller's messages
|
|
66
|
+
// The caller's messages retain their wire shape. An operator who
|
|
64
67
|
// recorded that this destination rejects the `developer` role gets that role converted in
|
|
65
68
|
// place. Verbatim was not neutral there — it sent the role anyway and the turn failed
|
|
66
69
|
// upstream with a 400 before the model saw it. An unrecorded destination is still verbatim,
|
|
67
70
|
// and the conversion changes the role of those messages and nothing else, so the position
|
|
68
|
-
// of every message and every other field survive unchanged.
|
|
71
|
+
// of every message and every other field survive unchanged. Missing thinking history is
|
|
72
|
+
// repaired below using the same preserve policy as the translated Chat builder.
|
|
69
73
|
messages: applyExplicitChatDeveloperRole(
|
|
70
74
|
frameAgentRouterMessages(provider.baseUrl, rawBody.messages),
|
|
71
75
|
provider,
|
|
@@ -75,6 +79,8 @@ export function buildOpenAIChatPassthroughRequest(
|
|
|
75
79
|
for (const field of CHAT_PASSTHROUGH_FIELDS) {
|
|
76
80
|
if (rawBody[field] !== undefined) body[field] = rawBody[field];
|
|
77
81
|
}
|
|
82
|
+
body.messages = replayNativeChatReasoning(body.messages, provider, modelId, replayScope,
|
|
83
|
+
Array.isArray(rawBody.tools) && rawBody.tools.length > 0);
|
|
78
84
|
if (protectGlmSummaryBudget(body, provider.baseUrl, body.reasoning_effort)) body.reasoning_effort = "low";
|
|
79
85
|
const rawEfforts = modelRecordValue(provider.modelReasoningEfforts, modelId) ?? provider.reasoningEfforts;
|
|
80
86
|
const reasoningDisabled = modelInList(provider.noReasoningModels, modelId) || rawEfforts?.length === 0;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { modelInList, type OcxProviderConfig, type OcxReasoningReplayScopeRef } from "../../types";
|
|
2
|
+
import { peekReasoningForCall } from "../../responses/reasoning-replay-cache";
|
|
3
|
+
|
|
4
|
+
type Rec = Record<string, unknown>;
|
|
5
|
+
export const isChatRecord = (value: unknown): value is Rec =>
|
|
6
|
+
value !== null && typeof value === "object" && !Array.isArray(value);
|
|
7
|
+
|
|
8
|
+
const isDeepSeekThinkingModel = (modelId: string): boolean =>
|
|
9
|
+
/(?:^|\/)deepseek-(?:v\d+(?:\.\d+)*-(?:flash|pro)|flash|pro)(?:$|[-_:])/i.test(modelId);
|
|
10
|
+
|
|
11
|
+
/** DeepSeek thinking tool replay is a model contract, independent of the gateway's name. */
|
|
12
|
+
export function chatReasoningReplayPolicy(provider: OcxProviderConfig, modelId: string): {
|
|
13
|
+
preserve: boolean; placeholder: boolean;
|
|
14
|
+
} {
|
|
15
|
+
// Match whole model-family tokens, including dotted versions, namespace and deployment suffixes.
|
|
16
|
+
// Legacy chat/reasoner and distilled R1 models do not share the thinking tool-call contract.
|
|
17
|
+
const preserve = provider.preserveReasoningContentModels === undefined
|
|
18
|
+
? isDeepSeekThinkingModel(modelId)
|
|
19
|
+
: modelInList(provider.preserveReasoningContentModels, modelId);
|
|
20
|
+
return {
|
|
21
|
+
preserve,
|
|
22
|
+
placeholder: preserve && (provider.requiresReasoningPlaceholderModels === undefined
|
|
23
|
+
? true
|
|
24
|
+
: modelInList(provider.requiresReasoningPlaceholderModels, modelId)),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Repair missing native history without mutating caller bodies used by retries/combo siblings. */
|
|
29
|
+
export function replayNativeChatReasoning(
|
|
30
|
+
messages: unknown,
|
|
31
|
+
provider: OcxProviderConfig,
|
|
32
|
+
modelId: string,
|
|
33
|
+
scope?: OcxReasoningReplayScopeRef,
|
|
34
|
+
hasTools = false,
|
|
35
|
+
): unknown {
|
|
36
|
+
const policy = chatReasoningReplayPolicy(provider, modelId);
|
|
37
|
+
if (!policy.preserve || !Array.isArray(messages)
|
|
38
|
+
|| modelInList(provider.reasoningDetailsModels, modelId)) return messages;
|
|
39
|
+
return messages.map(message => {
|
|
40
|
+
if (!isChatRecord(message) || message.role !== "assistant"
|
|
41
|
+
|| (typeof message.reasoning_content === "string" && message.reasoning_content.length > 0)) return message;
|
|
42
|
+
const calls = Array.isArray(message.tool_calls) ? message.tool_calls : [];
|
|
43
|
+
// DeepSeek's tools contract covers all prior assistant turns, even plain answers.
|
|
44
|
+
// Those turns have no call id for original replay; only the configured fallback applies.
|
|
45
|
+
if (calls.length === 0 && !(hasTools && isDeepSeekThinkingModel(modelId))) return message;
|
|
46
|
+
const cached = calls.filter(isChatRecord)
|
|
47
|
+
.map(tool => typeof tool.id === "string" ? peekReasoningForCall(tool.id, scope) : undefined)
|
|
48
|
+
.filter((text): text is string => typeof text === "string" && text.length > 0);
|
|
49
|
+
const reasoning = cached.length > 0 ? [...new Set(cached)].join("\n") : policy.placeholder ? " " : undefined;
|
|
50
|
+
return reasoning === undefined ? message : { ...message, reasoning_content: reasoning };
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -5,7 +5,7 @@ export const MAX_SEARCH_NOTE_FIELD_CHARS = 2048;
|
|
|
5
5
|
/** Most `queries` or `sources` entries copied from one hosted call. */
|
|
6
6
|
export const MAX_SEARCH_NOTE_LIST_ENTRIES = 20;
|
|
7
7
|
/**
|
|
8
|
-
* Serialized bytes all notes of one
|
|
8
|
+
* Serialized bytes all projected notes of one request may use, the omission note included. Request
|
|
9
9
|
* body limits are off by default, so the projection carries its own bound.
|
|
10
10
|
*/
|
|
11
11
|
export const MAX_PROJECTED_SEARCH_NOTE_BYTES = 65_536;
|
|
@@ -49,14 +49,19 @@ function noteBytes(note: unknown): number {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function omissionNote(count: number): Record<string, unknown> {
|
|
52
|
-
return referenceNote(`${count} further hosted web search actions omitted from this
|
|
52
|
+
return referenceNote(`${count} further hosted web search actions omitted from this request.`);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
/** Request-local projection
|
|
55
|
+
/** Request-local metadata projection for native Codex replay and portable summaries. */
|
|
56
56
|
export function renderCompactionSearchHistory(item: unknown): unknown {
|
|
57
57
|
if (!isPlainObject(item) || item.type !== "web_search_call") return item;
|
|
58
58
|
// Allowlist action metadata, not opaque state, IDs, or unknown provider fields.
|
|
59
59
|
const metadata: Record<string, unknown> = stringFields(item, ["status"]);
|
|
60
|
+
if (Array.isArray(item.sources)) {
|
|
61
|
+
metadata.sources = firstEntries(item.sources, source => isPlainObject(source) && typeof source.url === "string"
|
|
62
|
+
? stringFields(source, ["type", "url", "title"])
|
|
63
|
+
: undefined);
|
|
64
|
+
}
|
|
60
65
|
if (isPlainObject(item.action)) {
|
|
61
66
|
const action: Record<string, unknown> = stringFields(item.action, ["type", "query", "url", "pattern"]);
|
|
62
67
|
if (Array.isArray(item.action.queries)) {
|
|
@@ -73,13 +78,15 @@ export function renderCompactionSearchHistory(item: unknown): unknown {
|
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
/**
|
|
76
|
-
* Project every hosted call in
|
|
81
|
+
* Project every hosted call in the selected input, in place order, within
|
|
77
82
|
* MAX_PROJECTED_SEARCH_NOTE_BYTES. Once the next note would cross the budget less the reserved
|
|
78
83
|
* omission note, that call and every later hosted call are dropped, and one omission note stands at
|
|
79
84
|
* the first dropped position. Items other than hosted calls pass through untouched.
|
|
80
85
|
*/
|
|
81
|
-
export function renderCompactionSearchHistoryItems(items: readonly unknown[]): unknown[] {
|
|
82
|
-
const
|
|
86
|
+
export function renderCompactionSearchHistoryItems(items: readonly unknown[], referencedHostedIds?: ReadonlySet<string>): unknown[] {
|
|
87
|
+
const shouldProject = (item: unknown) => isPlainObject(item) && item.type === "web_search_call"
|
|
88
|
+
&& !(typeof item.id === "string" && referencedHostedIds?.has(item.id));
|
|
89
|
+
const hostedCount = items.filter(shouldProject).length;
|
|
83
90
|
if (hostedCount === 0) return [...items];
|
|
84
91
|
const reserve = noteBytes(omissionNote(hostedCount));
|
|
85
92
|
const out: unknown[] = [];
|
|
@@ -87,7 +94,7 @@ export function renderCompactionSearchHistoryItems(items: readonly unknown[]): u
|
|
|
87
94
|
let omitted = 0;
|
|
88
95
|
let omissionIndex = -1;
|
|
89
96
|
for (const item of items) {
|
|
90
|
-
if (!
|
|
97
|
+
if (!shouldProject(item)) {
|
|
91
98
|
out.push(item);
|
|
92
99
|
continue;
|
|
93
100
|
}
|