@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
package/src/types/provider.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface ProviderApiKeySelection {
|
|
|
14
14
|
entryId?: string;
|
|
15
15
|
reference?: string;
|
|
16
16
|
revision?: string;
|
|
17
|
+
/** Request-local allocation; does not replace the saved active key. */
|
|
18
|
+
distributed?: boolean;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export interface OpenRouterProviderRouting {
|
|
@@ -164,16 +166,25 @@ export interface ProviderWebSearchBridgeConfig {
|
|
|
164
166
|
endpoint?: string;
|
|
165
167
|
}
|
|
166
168
|
|
|
167
|
-
/**
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
/** Optional user-owned prompt-length policy; rates are USD per 1M tokens. */
|
|
170
|
+
export type PromptLengthPricing =
|
|
171
|
+
| { mode: "automatic" }
|
|
172
|
+
| { mode: "flat" }
|
|
173
|
+
| {
|
|
174
|
+
mode: "custom";
|
|
175
|
+
thresholdTokens: number;
|
|
176
|
+
comparison: "gt" | "gte";
|
|
177
|
+
rates: { input: number; output: number; cacheRead: number; cacheWrite: number };
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** Complete base rates for one model, with automatic pricing when policy is absent. */
|
|
172
181
|
export interface ProviderCostOverlay {
|
|
173
182
|
input: number;
|
|
174
183
|
output: number;
|
|
175
184
|
cacheRead: number;
|
|
176
185
|
cacheWrite: number;
|
|
186
|
+
/** Absent means automatic vendor prompt-length pricing. */
|
|
187
|
+
promptLengthPricing?: PromptLengthPricing;
|
|
177
188
|
}
|
|
178
189
|
|
|
179
190
|
export interface RequestPacingRule {
|
|
@@ -190,6 +201,10 @@ export interface ProviderRequestPacingConfig extends RequestPacingRule {
|
|
|
190
201
|
enabled: boolean;
|
|
191
202
|
/** Exact upstream model-id overrides; other models inherit the provider rule. */
|
|
192
203
|
models?: Record<string, RequestPacingRule>;
|
|
204
|
+
/** Normal request allocation, separate from cooldown recovery ordering. */
|
|
205
|
+
keySelection?: "active" | "round-robin";
|
|
206
|
+
/** Additional uniform limits per pool credential, across all models. */
|
|
207
|
+
keys?: RequestPacingRule;
|
|
193
208
|
}
|
|
194
209
|
|
|
195
210
|
export interface FastWire {
|
|
@@ -432,6 +447,11 @@ export interface OcxProviderConfig {
|
|
|
432
447
|
* to extract turn provenance (#6220).
|
|
433
448
|
*/
|
|
434
449
|
preserveResponsesMessageMetadata?: boolean;
|
|
450
|
+
/**
|
|
451
|
+
* Opt in to lossy hosted-search history notes on ordinary canonical Codex Lite requests.
|
|
452
|
+
* Default off; only literal true enables it, and non-Lite/native compact stay unchanged.
|
|
453
|
+
*/
|
|
454
|
+
projectResponsesLiteHostedSearchHistory?: boolean;
|
|
435
455
|
/**
|
|
436
456
|
* Treat this provider's `modelReasoningEfforts` as authoritative at the wire, not only in the
|
|
437
457
|
* catalog. Adapters that ship their own per-model effort table (currently `command-code`)
|
|
@@ -453,6 +473,10 @@ export interface OcxProviderConfig {
|
|
|
453
473
|
* compaction blobs. Absent or false degrades foreign blobs to an opaque note.
|
|
454
474
|
*/
|
|
455
475
|
decodesNativeCompactionBlobs?: boolean;
|
|
476
|
+
/** Accept native Responses v2 `input` compaction_trigger items. Responses adapter only. */
|
|
477
|
+
supportsNativeCompactionTrigger?: boolean;
|
|
478
|
+
/** Serve native Responses v1 /responses/compact. Responses adapter only; no auth authority. */
|
|
479
|
+
supportsNativeCompactEndpoint?: boolean;
|
|
456
480
|
/**
|
|
457
481
|
* Trust this direct key-auth Responses provider to consume or relay opaque encrypted
|
|
458
482
|
* V2 agent tasks. OpenCodex does not decrypt, translate, or recover an eligible task.
|
|
@@ -712,12 +736,14 @@ export interface OcxProviderConfig {
|
|
|
712
736
|
/** Kiro OAuth only: active serving requests per account, 1..100. */
|
|
713
737
|
maxConcurrentPerAccount?: number;
|
|
714
738
|
enabled?: boolean;
|
|
739
|
+
/** Opt-in process-local conversation ownership; requires proactive enablement. */
|
|
740
|
+
sessionAffinity?: "conversation" | "none";
|
|
715
741
|
/**
|
|
716
742
|
* Generic OAuth pool selection strategy (#695). Persisted through the pool-settings
|
|
717
743
|
* contract. Consumed by the selector only while `pool.kernel` is on; with the flag off
|
|
718
744
|
* it is still merely persisted, so omitted and set behave the same.
|
|
719
745
|
*/
|
|
720
|
-
strategy?: "quota" | "round-robin" | "fill-first" | "least-loaded";
|
|
746
|
+
strategy?: "quota" | "round-robin" | "fill-first" | "reset-first" | "least-loaded";
|
|
721
747
|
/**
|
|
722
748
|
* 0-100 usage percent at which fill-first advances off the active account (#695).
|
|
723
749
|
* Read only under `pool.kernel` with `strategy: "fill-first"`; 80 when unset, matching
|
package/src/types/request.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { KiroOAuthMetadata } from "../oauth/types";
|
|
|
2
2
|
import type { NativeReasoningOwner } from "../responses/reasoning-replay-cache";
|
|
3
3
|
import type { OcxTool, OcxToolChoice } from "./tools";
|
|
4
4
|
import type { TierDecision, TierObservationContext } from "./provider";
|
|
5
|
+
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
5
6
|
|
|
6
7
|
/** Exact provider/credential namespace for process-local reasoning replay. */
|
|
7
8
|
export interface OcxReasoningReplayIdentity {
|
|
@@ -62,6 +63,16 @@ export interface OcxParsedRequest {
|
|
|
62
63
|
_nativeReasoningReplay?: ReadonlyMap<string, string>;
|
|
63
64
|
_nativeReasoningBoundOwner?: NativeReasoningOwner;
|
|
64
65
|
_nativeReasoningMint?: { owner?: NativeReasoningOwner };
|
|
66
|
+
/**
|
|
67
|
+
* The Claude client's typed Anthropic web tools, as declared. Only the anthropic adapter reads
|
|
68
|
+
* them, and only for api.anthropic.com; every other destination never sees them.
|
|
69
|
+
*/
|
|
70
|
+
_anthropicServerTools?: readonly Record<string, unknown>[];
|
|
71
|
+
/**
|
|
72
|
+
* Completed server-tool exchanges from the Claude client's history, keyed by the block that
|
|
73
|
+
* followed them (`call:<tool_use id>` / `text:<text>`). Same audience as `_anthropicServerTools`.
|
|
74
|
+
*/
|
|
75
|
+
_anthropicServerReplay?: ReadonlyMap<string, readonly Record<string, unknown>[]>;
|
|
65
76
|
/**
|
|
66
77
|
* Boundary between replayed history and this turn's newly appended input. Usually the
|
|
67
78
|
* items the proxy restored from local previous_response_id state; also set when the
|
|
@@ -92,11 +103,17 @@ export interface OcxParsedRequest {
|
|
|
92
103
|
_cursorClientThreadId?: string;
|
|
93
104
|
/** Conversation/provider/account/model-bound namespace for reasoning replay state. */
|
|
94
105
|
_reasoningReplayScope?: OcxReasoningReplayScopeRef;
|
|
106
|
+
/** Trusted configuration snapshot; never read from the client's request body. */
|
|
107
|
+
_nativeReasoningRetention?: Readonly<{ modelSwitch: boolean; accountSwitch: boolean }>;
|
|
95
108
|
/**
|
|
96
109
|
* Set by bindRouteReasoningReplayScope after a proven serving-identity change, or by
|
|
97
110
|
* prepareOpaqueBlobRecovery after an authoritative rejection; consumers strip replayed blobs.
|
|
98
111
|
*/
|
|
99
112
|
_stripReasoningEncryptedContent?: boolean;
|
|
113
|
+
/** Native compaction remains nonportable even when reasoning-only retention is enabled. */
|
|
114
|
+
_stripNativeCompactionEncryptedContent?: boolean;
|
|
115
|
+
/** Retention kept reasoning ciphertext across a model/account change; arms streamed rejection recovery. */
|
|
116
|
+
_nativeReasoningRetainedAcrossRoute?: boolean;
|
|
100
117
|
/**
|
|
101
118
|
* Set when replayed reasoning item ids name items in a store this destination cannot read: by
|
|
102
119
|
* prepareOpaqueBlobRecovery before the one recovery rebuild, and by bindRouteReasoningReplayScope
|
|
@@ -150,6 +167,8 @@ export interface OcxParsedRequest {
|
|
|
150
167
|
* (see src/responses/compaction.ts).
|
|
151
168
|
*/
|
|
152
169
|
_compactionRequest?: boolean;
|
|
170
|
+
/** Request-owned encoding of readable routed v2 retention; never used for native ciphertext. */
|
|
171
|
+
_compactionRetention?: { encode(summary: string): string; commit(): void };
|
|
153
172
|
/** Manual compaction moved to another provider: summarize portably even on a canonical ChatGPT target. */
|
|
154
173
|
_portableCompaction?: boolean;
|
|
155
174
|
/**
|
|
@@ -173,6 +192,7 @@ export interface OcxParsedRequest {
|
|
|
173
192
|
*/
|
|
174
193
|
_advisorGuard?: (options: {
|
|
175
194
|
parsed: OcxParsedRequest;
|
|
195
|
+
translatorBudget?: TranslatorBudget;
|
|
176
196
|
firstEvents: AsyncIterable<AdapterEvent>;
|
|
177
197
|
/** One bounded worker continuation re-dispatch (same machinery as the terminal guard). */
|
|
178
198
|
continuation: (parsed: OcxParsedRequest) => AsyncIterable<AdapterEvent> | Promise<AsyncIterable<AdapterEvent>>;
|
|
@@ -399,9 +419,14 @@ export interface OcxProviderContinuationState {
|
|
|
399
419
|
[provider: string]: Record<string, unknown> | undefined;
|
|
400
420
|
}
|
|
401
421
|
|
|
422
|
+
/** One Anthropic server-side call as the API returned it: `server_tool_use` + its `*_tool_result`. */
|
|
423
|
+
export interface AnthropicServerBlocks { use: Record<string, unknown>; result: Record<string, unknown> }
|
|
424
|
+
|
|
402
425
|
export type AdapterEvent =
|
|
403
426
|
| { type: "heartbeat"; replayUnsafe?: true; preflightReady?: true }
|
|
404
|
-
| { type: "text_delta"; text: string; phase?: OcxMessagePhase
|
|
427
|
+
| { type: "text_delta"; text: string; phase?: OcxMessagePhase;
|
|
428
|
+
/** Complete documented Anthropic progress block: text remains signed thinking on replay. */
|
|
429
|
+
anthropicThinkingSignature?: string }
|
|
405
430
|
| { type: "thinking_delta"; thinking: string }
|
|
406
431
|
// Anthropic extended-thinking round-trip: signature_delta for the current thinking block, and
|
|
407
432
|
// opaque redacted_thinking blocks. Both must be replayed verbatim or tool-use turns 400.
|
|
@@ -424,7 +449,11 @@ export type AdapterEvent =
|
|
|
424
449
|
// output_item.added(in_progress) and end → the matching output_item.done(completed|failed) under
|
|
425
450
|
// the SAME output index, so the activity animates instead of flashing completed instantly.
|
|
426
451
|
| { type: "web_search_call_begin"; id: string }
|
|
427
|
-
| {
|
|
452
|
+
| {
|
|
453
|
+
type: "web_search_call_end"; id: string; queries: string[]; status?: "completed" | "failed"; sources?: OcxUrlCitation[];
|
|
454
|
+
/** Anthropic's server-side call (adapters/anthropic.ts): its `server_tool_use` and result block, verbatim. */
|
|
455
|
+
anthropic?: AnthropicServerBlocks;
|
|
456
|
+
}
|
|
428
457
|
| {
|
|
429
458
|
type: "done";
|
|
430
459
|
usage?: OcxUsage;
|
package/src/types/wire.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { isOpenCodeGoGeminiDestination, OPENCODE_GO_BASE_URL, OPENCODE_GO_MODEL_WIRES, OPENCODE_GO_LEGACY_NAME_PINS, type OpenCodeGoDestination } from "./opencode-go";
|
|
2
|
+
import { openCodeZenBaseUrl, openCodeZenKeyBaseUrl, type OpenCodeZenDestination } from "./opencode-zen";
|
|
3
|
+
|
|
1
4
|
/**
|
|
2
5
|
* Accepted values for the per-provider upstream HTTP-version pin (#1668). Shared by the
|
|
3
6
|
* zod load schema, the management write boundary (POST/PATCH), and the fetch runtime, so
|
|
@@ -40,35 +43,16 @@ export const MODEL_ADAPTER_OVERRIDE_ALLOWED: ReadonlySet<string> = new Set([
|
|
|
40
43
|
"openai-responses",
|
|
41
44
|
]);
|
|
42
45
|
|
|
43
|
-
/**
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
"minimax-m3",
|
|
54
|
-
// OpenCode's catalog identifies Union Alpha as @ai-sdk/anthropic while the
|
|
55
|
-
// provider defaults to OpenAI-compatible; direct Chat returns 500 and direct
|
|
56
|
-
// Messages reaches the session check (#4847).
|
|
57
|
-
"union-alpha",
|
|
58
|
-
]),
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
function anthropicWireModelsForProvider(providerName: string): ReadonlySet<string> | undefined {
|
|
62
|
-
return Object.hasOwn(ANTHROPIC_WIRE_MODELS, providerName)
|
|
63
|
-
? ANTHROPIC_WIRE_MODELS[providerName]
|
|
64
|
-
: undefined;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Detached provider-local hard-pin table for pure wire-policy resolution. */
|
|
68
|
-
export function captureWireAdapterHardPins(providerName: string): Readonly<Record<string, string>> {
|
|
69
|
-
const models = anthropicWireModelsForProvider(providerName);
|
|
70
|
-
if (!models) return Object.freeze({});
|
|
71
|
-
return Object.freeze(Object.fromEntries([...models].map(modelId => [modelId, "anthropic"])));
|
|
46
|
+
/** Native Go wire pins share the exact-model table with registry Responses defaults. */
|
|
47
|
+
export function captureWireAdapterHardPins(providerName: string, provider?: WirePinProvider): Readonly<Record<string, string>> {
|
|
48
|
+
if (providerName !== "opencode-go") return Object.freeze({});
|
|
49
|
+
// The existing Go preset pins saved URLs to this endpoint before dispatch. Pure policy
|
|
50
|
+
// captures must use that same destination even when handed an unnormalized config row.
|
|
51
|
+
const destination = provider && { ...provider, baseUrl: OPENCODE_GO_BASE_URL };
|
|
52
|
+
return Object.freeze(Object.fromEntries(Object.entries(OPENCODE_GO_MODEL_WIRES).filter(([modelId, wire]) =>
|
|
53
|
+
(wire === "anthropic" && OPENCODE_GO_LEGACY_NAME_PINS.has(modelId))
|
|
54
|
+
|| (wire !== "openai-responses" && isOpenCodeGoGeminiDestination(destination)),
|
|
55
|
+
)));
|
|
72
56
|
}
|
|
73
57
|
|
|
74
58
|
interface WirePinPrefixRule {
|
|
@@ -98,9 +82,7 @@ const WIRE_ADAPTER_PIN_PREFIXES: Readonly<Record<string, WirePinPrefixRule>> = O
|
|
|
98
82
|
});
|
|
99
83
|
|
|
100
84
|
/** Just enough of a provider config to tell whether it still points at the pinned endpoint. */
|
|
101
|
-
export interface WirePinProvider {
|
|
102
|
-
readonly baseUrl?: unknown;
|
|
103
|
-
}
|
|
85
|
+
export interface WirePinProvider extends OpenCodeGoDestination, OpenCodeZenDestination {}
|
|
104
86
|
|
|
105
87
|
function normalizedEndpoint(value: unknown): string | undefined {
|
|
106
88
|
if (typeof value !== "string") return undefined;
|
|
@@ -112,7 +94,21 @@ function normalizedEndpoint(value: unknown): string | undefined {
|
|
|
112
94
|
}
|
|
113
95
|
}
|
|
114
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Providers whose adapter is their credential boundary: the adapter alone pins the destination and
|
|
99
|
+
* the auth header for an account token. No model may switch wires, whatever `baseUrl` or the row's
|
|
100
|
+
* own `adapter` says, because a generic wire would send that token as a Bearer key to the configured
|
|
101
|
+
* URL. The empty prefix covers every model id.
|
|
102
|
+
*/
|
|
103
|
+
const CREDENTIAL_PINNED_ADAPTERS: Readonly<Record<string, Readonly<Record<string, string>>>> = Object.freeze({
|
|
104
|
+
"dsh-account": Object.freeze({ "": "dsh-account" }),
|
|
105
|
+
});
|
|
106
|
+
|
|
115
107
|
function prefixPinsFor(providerName: string, provider: WirePinProvider | undefined): Readonly<Record<string, string>> | undefined {
|
|
108
|
+
if (Object.hasOwn(CREDENTIAL_PINNED_ADAPTERS, providerName)) return CREDENTIAL_PINNED_ADAPTERS[providerName];
|
|
109
|
+
if (providerName === "opencode-zen" && openCodeZenKeyBaseUrl(provider)) {
|
|
110
|
+
return openCodeZenBaseUrl(provider) ? { "gemini-": "google", "claude-": "anthropic" } : { "claude-": "anthropic" };
|
|
111
|
+
}
|
|
116
112
|
if (!provider || !Object.hasOwn(WIRE_ADAPTER_PIN_PREFIXES, providerName)) return undefined;
|
|
117
113
|
const rule = WIRE_ADAPTER_PIN_PREFIXES[providerName]!;
|
|
118
114
|
return normalizedEndpoint(provider.baseUrl) === rule.endpoint ? rule.prefixes : undefined;
|
|
@@ -148,13 +144,11 @@ function prefixedWireAdapter(providerName: string, modelId: string, provider: Wi
|
|
|
148
144
|
* adapter" would pass on the first pass and then let the override win on the second.
|
|
149
145
|
*/
|
|
150
146
|
export function isWirePinnedModel(providerName: string, modelId: string, provider?: WirePinProvider): boolean {
|
|
151
|
-
return (
|
|
152
|
-
|| prefixedWireAdapter(providerName, modelId, provider) !== undefined;
|
|
147
|
+
return pinnedWireAdapter(providerName, modelId, provider) !== undefined;
|
|
153
148
|
}
|
|
154
149
|
|
|
155
150
|
/** The wire a pinned model must use, or undefined when the model is not pinned. */
|
|
156
151
|
export function pinnedWireAdapter(providerName: string, modelId: string, provider?: WirePinProvider): string | undefined {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
: prefixedWireAdapter(providerName, modelId, provider);
|
|
152
|
+
const pins = captureWireAdapterHardPins(providerName, provider);
|
|
153
|
+
return Object.hasOwn(pins, modelId) ? pins[modelId] : prefixedWireAdapter(providerName, modelId, provider);
|
|
160
154
|
}
|
package/src/types.ts
CHANGED
|
@@ -61,6 +61,7 @@ export type {
|
|
|
61
61
|
OcxProviderContinuationOwner,
|
|
62
62
|
OcxProviderContinuationState,
|
|
63
63
|
AdapterEvent,
|
|
64
|
+
AnthropicServerBlocks,
|
|
64
65
|
OcxUrlCitation,
|
|
65
66
|
OcxUsage,
|
|
66
67
|
} from "./types/request";
|
|
@@ -122,6 +123,7 @@ export type {
|
|
|
122
123
|
ProviderWebSearchBridgeBackend,
|
|
123
124
|
ProviderWebSearchBridgeConfig,
|
|
124
125
|
ProviderCostOverlay,
|
|
126
|
+
PromptLengthPricing,
|
|
125
127
|
RequestPacingRule,
|
|
126
128
|
ProviderRequestPacingConfig,
|
|
127
129
|
FastWire,
|
|
@@ -95,12 +95,22 @@ export function resolveNpmCommand(
|
|
|
95
95
|
return null;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function shellEnvValue(env, name) {
|
|
99
|
+
// Copying process.env loses Windows' case-insensitive property access. Keep
|
|
100
|
+
// the existing exact-key precedence; do not choose between conflicting aliases.
|
|
101
|
+
if (env[name] !== undefined) return env[name];
|
|
102
|
+
const values = new Set(Object.entries(env)
|
|
103
|
+
.filter(([key, value]) => key.toLowerCase() === name.toLowerCase() && value !== undefined)
|
|
104
|
+
.map(([, value]) => value));
|
|
105
|
+
return values.size === 1 ? values.values().next().value : undefined;
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
function systemCommandProcessor(env) {
|
|
99
|
-
const systemRoot = env
|
|
109
|
+
const systemRoot = shellEnvValue(env, "SystemRoot") ?? shellEnvValue(env, "windir");
|
|
100
110
|
if (systemRoot && win32.isAbsolute(systemRoot)) {
|
|
101
111
|
return win32.join(systemRoot, "System32", "cmd.exe");
|
|
102
112
|
}
|
|
103
|
-
const comSpec = env
|
|
113
|
+
const comSpec = shellEnvValue(env, "ComSpec");
|
|
104
114
|
return comSpec && win32.isAbsolute(comSpec) ? win32.resolve(comSpec) : null;
|
|
105
115
|
}
|
|
106
116
|
|
package/src/usage/cost.ts
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
resolveMetadataProvider,
|
|
16
16
|
} from "../generated/model-metadata";
|
|
17
17
|
import type { AttemptTierOutcome, OcxUsage } from "../types";
|
|
18
|
+
import type { PromptLengthPricing } from "../types/provider";
|
|
18
19
|
import { canonicalFastTierMarker } from "../providers/fastwire";
|
|
19
20
|
import { baseProviderLabel } from "../providers/label";
|
|
20
21
|
import type { PersistedUsageAttempt, UsageStatus } from "./log";
|
|
@@ -79,6 +80,7 @@ export interface MatchedPrice {
|
|
|
79
80
|
modelId: string;
|
|
80
81
|
jawcodeProvider?: string;
|
|
81
82
|
cost4: Cost4;
|
|
83
|
+
promptLengthPricing?: PromptLengthPricing;
|
|
82
84
|
source: "jawcode" | "expected" | "user";
|
|
83
85
|
sourceRef?: string;
|
|
84
86
|
verifiedAt?: string;
|
|
@@ -97,6 +99,8 @@ export interface AttemptCostEstimate {
|
|
|
97
99
|
priorityMultiplier?: number;
|
|
98
100
|
/** Set when the published long-context rate was applied (#908). */
|
|
99
101
|
contextTier?: ContextTierName;
|
|
102
|
+
/** User absolute rates selected for this attempt, separate from catalog tiers. */
|
|
103
|
+
customThresholdApplied?: true;
|
|
100
104
|
/** The numeric estimate is a known floor because the exact Priority price is unavailable. */
|
|
101
105
|
priorityLowerBound?: boolean;
|
|
102
106
|
}
|
|
@@ -111,6 +115,8 @@ export interface CostEstimate {
|
|
|
111
115
|
priorityMultiplier?: number;
|
|
112
116
|
/** Set when any priced attempt used the published long-context rate (#908). */
|
|
113
117
|
contextTier?: ContextTierName;
|
|
118
|
+
/** At least one request/attempt selected the user threshold rates. */
|
|
119
|
+
customThresholdApplied?: true;
|
|
114
120
|
/** The aggregate is a known floor because every priced attempt is a lower bound. */
|
|
115
121
|
priorityLowerBound?: boolean;
|
|
116
122
|
}
|
|
@@ -343,6 +349,7 @@ function userOverlayMatch(
|
|
|
343
349
|
modelId,
|
|
344
350
|
cost4: overlay.cost4,
|
|
345
351
|
source: "user",
|
|
352
|
+
...(overlay.promptLengthPricing ? { promptLengthPricing: overlay.promptLengthPricing } : {}),
|
|
346
353
|
sourceRef: overlay.source,
|
|
347
354
|
verifiedAt: overlay.verifiedAt,
|
|
348
355
|
status: "verified",
|
|
@@ -397,6 +404,15 @@ function vendorPrefixedCost(modelId: string): ReturnType<typeof findVendorCostBy
|
|
|
397
404
|
return normalize(found.provider) === normalize(claimedVendor) ? found : undefined;
|
|
398
405
|
}
|
|
399
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Only measured usage is priced. An `unreported` or `unsupported` row can still carry tokens — a
|
|
409
|
+
* combo whose first leg failed without usage keeps the second leg's — and pricing them reports an
|
|
410
|
+
* understated cost as if it were complete.
|
|
411
|
+
*/
|
|
412
|
+
export function isMeasuredStatus(usageStatus: UsageStatus): boolean {
|
|
413
|
+
return usageStatus === "reported" || usageStatus === "estimated";
|
|
414
|
+
}
|
|
415
|
+
|
|
400
416
|
function isEstimated(usage: OcxUsage, usageStatus: UsageStatus, priceStatus: ExpectedPriceStatus | "verified"): boolean {
|
|
401
417
|
return usage.estimated === true || usageStatus === "estimated" || priceStatus === "verified-derived";
|
|
402
418
|
}
|
|
@@ -475,8 +491,8 @@ function isConfirmedFast(tier?: ServiceTierInput): boolean {
|
|
|
475
491
|
}
|
|
476
492
|
|
|
477
493
|
/**
|
|
478
|
-
*
|
|
479
|
-
*
|
|
494
|
+
* Select user prompt-length rates, or apply the published context rule in automatic mode.
|
|
495
|
+
* Returns [effectiveCost4, catalogTier, priorityLowerBound, customThresholdApplied].
|
|
480
496
|
*
|
|
481
497
|
* `rawInputTokens` MUST be `usage.inputTokens` (total prompt size), never the
|
|
482
498
|
* normalized billable input — normalization subtracts cache read/write, so a
|
|
@@ -486,13 +502,27 @@ function isConfirmedFast(tier?: ServiceTierInput): boolean {
|
|
|
486
502
|
* API rows can stack the bands. xAI publishes neither a combined rate nor an exclusion,
|
|
487
503
|
* so its long-context rate remains the known lower bound instead of inventing a stacked multiplier.
|
|
488
504
|
*/
|
|
489
|
-
function
|
|
505
|
+
function applyPromptLengthPricing(
|
|
490
506
|
cost4: Cost4,
|
|
491
507
|
provider: string,
|
|
492
508
|
modelId: string,
|
|
493
509
|
rawInputTokens: number | undefined,
|
|
494
510
|
tier?: ServiceTierInput,
|
|
495
|
-
|
|
511
|
+
policy?: PromptLengthPricing,
|
|
512
|
+
): [Cost4, ContextTierName | undefined, boolean, true?] {
|
|
513
|
+
// A user policy replaces the catalog rule on BOTH sides of its threshold.
|
|
514
|
+
// Keep custom provenance separate while honoring the published Priority relation.
|
|
515
|
+
if (policy?.mode === "flat") return [cost4, undefined, false];
|
|
516
|
+
if (policy?.mode === "custom") {
|
|
517
|
+
const crossed = rawInputTokens !== undefined && Number.isFinite(rawInputTokens)
|
|
518
|
+
&& (policy.comparison === "gte"
|
|
519
|
+
? rawInputTokens >= policy.thresholdTokens : rawInputTokens > policy.thresholdTokens);
|
|
520
|
+
if (!crossed) return [cost4, undefined, false];
|
|
521
|
+
const relation = findContextTier(provider, modelId)?.confirmedPriorityRelation;
|
|
522
|
+
const confirmedFast = isConfirmedFast(tier);
|
|
523
|
+
if (confirmedFast && relation === "exclusive") return [cost4, undefined, false];
|
|
524
|
+
return [policy.rates, undefined, confirmedFast && relation === "lower-bound", true];
|
|
525
|
+
}
|
|
496
526
|
if (rawInputTokens === undefined) return [cost4, undefined, false];
|
|
497
527
|
const rule = findContextTier(provider, modelId);
|
|
498
528
|
if (!rule || !isLongContext(rule, rawInputTokens)) return [cost4, undefined, false];
|
|
@@ -520,9 +550,12 @@ function applyPriorityMultiplier(
|
|
|
520
550
|
modelId: string,
|
|
521
551
|
serviceTier?: ServiceTierInput,
|
|
522
552
|
contextTier?: ContextTierName,
|
|
553
|
+
customThresholdApplied?: true,
|
|
523
554
|
): [Cost4, number] {
|
|
524
555
|
if (canonicalFastTierMarker(tierScalar(serviceTier)) !== "priority") return [cost4, 1];
|
|
525
556
|
if (contextTier && findContextTier(provider, modelId)?.confirmedPriorityRelation !== "stack") return [cost4, 1];
|
|
557
|
+
if (customThresholdApplied && isConfirmedFast(serviceTier)
|
|
558
|
+
&& findContextTier(provider, modelId)?.confirmedPriorityRelation === "lower-bound") return [cost4, 1];
|
|
526
559
|
const rule = findPriorityPricingRule(provider, modelId);
|
|
527
560
|
if (rule?.requiresResponseConfirmation && !isConfirmedFast(serviceTier)) return [cost4, 1];
|
|
528
561
|
const multiplier = rule?.multiplier ?? 1;
|
|
@@ -568,7 +601,7 @@ export function estimateAttemptCost(
|
|
|
568
601
|
serviceTier?: ServiceTierInput,
|
|
569
602
|
userOverlays: readonly ExpectedPriceOverlay[] = activeUserCostOverlays(),
|
|
570
603
|
): AttemptCostEstimate | null {
|
|
571
|
-
if (!attempt.usage) return null;
|
|
604
|
+
if (!isMeasuredStatus(attempt.usageStatus) || !attempt.usage) return null;
|
|
572
605
|
const tokens = normalizeCostTokens(attempt.usage);
|
|
573
606
|
if (!tokens) return null;
|
|
574
607
|
const price = resolveMatchedPrice(attempt.provider, attempt.model, overlays, userOverlays, attempt);
|
|
@@ -576,11 +609,11 @@ export function estimateAttemptCost(
|
|
|
576
609
|
const attemptServiceTier = attempt.tierOutcome
|
|
577
610
|
? serviceTierContextFromOutcome(attempt.tierOutcome)
|
|
578
611
|
: serviceTier;
|
|
579
|
-
const [tieredCost4, contextTier, contextPriorityLowerBound] =
|
|
580
|
-
price.cost4, price.provider, attempt.model, attempt.usage.inputTokens, attemptServiceTier,
|
|
612
|
+
const [tieredCost4, contextTier, contextPriorityLowerBound, customThresholdApplied] = applyPromptLengthPricing(
|
|
613
|
+
price.cost4, price.provider, attempt.model, attempt.usage.inputTokens, attemptServiceTier, price.promptLengthPricing,
|
|
581
614
|
);
|
|
582
615
|
const [effectiveCost4, multiplier] = applyPriorityMultiplier(
|
|
583
|
-
tieredCost4, price.provider, attempt.model, attemptServiceTier, contextTier,
|
|
616
|
+
tieredCost4, price.provider, attempt.model, attemptServiceTier, contextTier, customThresholdApplied,
|
|
584
617
|
);
|
|
585
618
|
const priorityLowerBound = contextPriorityLowerBound
|
|
586
619
|
|| isOpenRouterPriorityLowerBound(price.provider, attempt.tierOutcome);
|
|
@@ -594,6 +627,7 @@ export function estimateAttemptCost(
|
|
|
594
627
|
estimated: isEstimated(attempt.usage, attempt.usageStatus, price.status),
|
|
595
628
|
...(multiplier !== 1 ? { priorityMultiplier: multiplier } : {}),
|
|
596
629
|
...(contextTier ? { contextTier } : {}),
|
|
630
|
+
...(customThresholdApplied ? { customThresholdApplied } : {}),
|
|
597
631
|
...(priorityLowerBound ? { priorityLowerBound: true } : {}),
|
|
598
632
|
};
|
|
599
633
|
}
|
|
@@ -637,6 +671,7 @@ export function estimateComboCost(
|
|
|
637
671
|
? { priorityMultiplier: estimates.find(est => est.priorityMultiplier)?.priorityMultiplier }
|
|
638
672
|
: {}),
|
|
639
673
|
...(estimates.some(est => est.contextTier) ? { contextTier: "long" as const } : {}),
|
|
674
|
+
...(estimates.some(est => est.customThresholdApplied) ? { customThresholdApplied: true as const } : {}),
|
|
640
675
|
...(estimates.every(est => est.priorityLowerBound === true)
|
|
641
676
|
? { priorityLowerBound: true as const }
|
|
642
677
|
: {}),
|
|
@@ -656,16 +691,16 @@ export function estimateRequestCost(
|
|
|
656
691
|
overlays: readonly ExpectedPriceOverlay[] = EXPECTED_PRICE_OVERLAYS,
|
|
657
692
|
userOverlays: readonly ExpectedPriceOverlay[] = activeUserCostOverlays(),
|
|
658
693
|
): CostEstimate | null {
|
|
659
|
-
if (!input.usage) return null;
|
|
694
|
+
if (!isMeasuredStatus(input.usageStatus) || !input.usage) return null;
|
|
660
695
|
const tokens = normalizeCostTokens(input.usage);
|
|
661
696
|
if (!tokens) return null;
|
|
662
697
|
const price = resolveMatchedPrice(input.provider, input.model, overlays, userOverlays, input);
|
|
663
698
|
if (!price) return null;
|
|
664
|
-
const [tieredCost4, contextTier, contextPriorityLowerBound] =
|
|
665
|
-
price.cost4, price.provider, input.model, input.usage.inputTokens, input.serviceTier,
|
|
699
|
+
const [tieredCost4, contextTier, contextPriorityLowerBound, customThresholdApplied] = applyPromptLengthPricing(
|
|
700
|
+
price.cost4, price.provider, input.model, input.usage.inputTokens, input.serviceTier, price.promptLengthPricing,
|
|
666
701
|
);
|
|
667
702
|
const [effectiveCost4, multiplier] = applyPriorityMultiplier(
|
|
668
|
-
tieredCost4, price.provider, input.model, input.serviceTier, contextTier,
|
|
703
|
+
tieredCost4, price.provider, input.model, input.serviceTier, contextTier, customThresholdApplied,
|
|
669
704
|
);
|
|
670
705
|
const priorityLowerBound = contextPriorityLowerBound || isOpenRouterPriorityLowerBound(
|
|
671
706
|
price.provider,
|
|
@@ -678,6 +713,7 @@ export function estimateRequestCost(
|
|
|
678
713
|
estimated: isEstimated(input.usage, input.usageStatus, price.status),
|
|
679
714
|
...(multiplier !== 1 ? { priorityMultiplier: multiplier } : {}),
|
|
680
715
|
...(contextTier ? { contextTier } : {}),
|
|
716
|
+
...(customThresholdApplied ? { customThresholdApplied } : {}),
|
|
681
717
|
...(priorityLowerBound ? { priorityLowerBound: true } : {}),
|
|
682
718
|
};
|
|
683
719
|
}
|
|
@@ -10,6 +10,7 @@ import { isAnthropicInstanceId } from "../providers/anthropic-instance-id";
|
|
|
10
10
|
export function pricingFamilyProvider(provider: string): string {
|
|
11
11
|
return isAnthropicInstanceId(provider) ? "anthropic" : provider;
|
|
12
12
|
}
|
|
13
|
+
import type { PromptLengthPricing } from "../types/provider";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Expected-price overlay for models whose jawcode cost rows are missing or all-zero
|
|
@@ -46,6 +47,8 @@ export interface ExpectedPriceOverlay {
|
|
|
46
47
|
provider: string;
|
|
47
48
|
modelId: string;
|
|
48
49
|
cost4: Cost4;
|
|
50
|
+
/** User-owned context policy; catalog rows leave this absent. */
|
|
51
|
+
promptLengthPricing?: PromptLengthPricing;
|
|
49
52
|
source: string;
|
|
50
53
|
verifiedAt: string;
|
|
51
54
|
status: ExpectedPriceStatus;
|
|
@@ -22,6 +22,7 @@ import { closeSync, fstatSync, openSync, readSync, writeSync } from "node:fs";
|
|
|
22
22
|
import { atomicWriteFileStreamed } from "../config/atomic-write";
|
|
23
23
|
import {
|
|
24
24
|
currentUsageLogRevision,
|
|
25
|
+
exactFileIdentity,
|
|
25
26
|
usageLogIdentityKey,
|
|
26
27
|
usageLogPath,
|
|
27
28
|
usageLogRevisionKey,
|
|
@@ -127,7 +128,7 @@ export function enforceUsageLedgerSizeLimit(
|
|
|
127
128
|
let sourceClosed = false;
|
|
128
129
|
try {
|
|
129
130
|
const opened = fstatSync(sourceFd);
|
|
130
|
-
if (Number(opened.size) !== captured.size ||
|
|
131
|
+
if (Number(opened.size) !== captured.size || exactFileIdentity(sourceFd).ino !== captured.ino) {
|
|
131
132
|
return { kind: "deferred", currentBytes: captured.size, reason: "revision-changed" };
|
|
132
133
|
}
|
|
133
134
|
const start = firstRowBoundary(sourceFd, Math.max(0, captured.size - target), captured.size);
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { closeSync, fstatSync, openSync, readSync } from "node:fs";
|
|
3
3
|
import {
|
|
4
4
|
currentUsageLogRevision,
|
|
5
|
+
exactFileIdentity,
|
|
5
6
|
normalizePersistedUsageRow,
|
|
6
7
|
usageLogIdentityKey,
|
|
7
8
|
usageLogPath,
|
|
@@ -63,13 +64,13 @@ export class UsageLedgerRebuildRequiredError extends Error {
|
|
|
63
64
|
|
|
64
65
|
function revisionFromStat(
|
|
65
66
|
path: string,
|
|
67
|
+
fd: number,
|
|
66
68
|
stat: ReturnType<typeof fstatSync>,
|
|
67
69
|
): UsageLogRevision {
|
|
68
70
|
if (!stat.isFile()) throw new Error("usage log is not a regular file");
|
|
69
71
|
return {
|
|
70
72
|
path,
|
|
71
|
-
|
|
72
|
-
ino: Number(stat.ino),
|
|
73
|
+
...exactFileIdentity(fd),
|
|
73
74
|
birthtimeMs: Number(stat.birthtimeMs),
|
|
74
75
|
size: Number(stat.size),
|
|
75
76
|
mtimeMs: Number(stat.mtimeMs),
|
|
@@ -276,7 +277,7 @@ export async function scanUsageLedgerCooperatively(
|
|
|
276
277
|
let pendingLineBytes = 0;
|
|
277
278
|
|
|
278
279
|
try {
|
|
279
|
-
const openedRevision = revisionFromStat(path, fstatSync(fd));
|
|
280
|
+
const openedRevision = revisionFromStat(path, fd, fstatSync(fd));
|
|
280
281
|
const scanEnd = openedRevision.size;
|
|
281
282
|
const openedIdentityKey = usageLogIdentityKey(openedRevision);
|
|
282
283
|
if (options.expectedIdentityKey && options.expectedIdentityKey !== openedIdentityKey) {
|
|
@@ -391,7 +392,7 @@ export async function scanUsageLedgerCooperatively(
|
|
|
391
392
|
}
|
|
392
393
|
|
|
393
394
|
throwIfAborted(options.signal);
|
|
394
|
-
const endingRevision = revisionFromStat(path, fstatSync(fd));
|
|
395
|
+
const endingRevision = revisionFromStat(path, fd, fstatSync(fd));
|
|
395
396
|
if (usageLogIdentityKey(endingRevision) !== openedIdentityKey) {
|
|
396
397
|
throw rebuildRequired("identity_mismatch");
|
|
397
398
|
}
|
|
@@ -417,7 +418,7 @@ export async function scanUsageLedgerCooperatively(
|
|
|
417
418
|
line,
|
|
418
419
|
options.signal,
|
|
419
420
|
);
|
|
420
|
-
const verifiedFdRevision = revisionFromStat(path, fstatSync(fd));
|
|
421
|
+
const verifiedFdRevision = revisionFromStat(path, fd, fstatSync(fd));
|
|
421
422
|
const verifiedPathRevision = currentUsageLogRevision();
|
|
422
423
|
if (capturedDigest !== verifiedDigest) throw rebuildRequired("content_changed");
|
|
423
424
|
if (!verifiedPathRevision
|
package/src/usage/log.ts
CHANGED
|
@@ -1163,8 +1163,12 @@ export function appendUsageEntry(entry: PersistedUsageEntry): void {
|
|
|
1163
1163
|
|
|
1164
1164
|
export type UsageLogRevision = {
|
|
1165
1165
|
path: string;
|
|
1166
|
-
|
|
1167
|
-
|
|
1166
|
+
/**
|
|
1167
|
+
* Exact decimal device and file id. NTFS file ids carry a sequence number in their
|
|
1168
|
+
* high bits and routinely exceed 2^53, so a Number would let two files compare equal.
|
|
1169
|
+
*/
|
|
1170
|
+
dev: string;
|
|
1171
|
+
ino: string;
|
|
1168
1172
|
birthtimeMs: number;
|
|
1169
1173
|
size: number;
|
|
1170
1174
|
mtimeMs: number;
|
|
@@ -1367,12 +1371,17 @@ function readExactly(fd: number, length: number, position: number): Buffer | nul
|
|
|
1367
1371
|
return output;
|
|
1368
1372
|
}
|
|
1369
1373
|
|
|
1370
|
-
|
|
1374
|
+
/** Exact dev/ino of an open file, read through bigint stats so large ids keep every bit. */
|
|
1375
|
+
export function exactFileIdentity(fd: number): { dev: string; ino: string } {
|
|
1376
|
+
const exact = fstatSync(fd, { bigint: true });
|
|
1377
|
+
return { dev: exact.dev.toString(), ino: exact.ino.toString() };
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
function usageLogRevision(path: string, fd: number, stat: ReturnType<typeof fstatSync>): UsageLogRevision {
|
|
1371
1381
|
if (!stat.isFile()) throw new Error("usage log is not a regular file");
|
|
1372
1382
|
return {
|
|
1373
1383
|
path,
|
|
1374
|
-
|
|
1375
|
-
ino: Number(stat.ino),
|
|
1384
|
+
...exactFileIdentity(fd),
|
|
1376
1385
|
birthtimeMs: Number(stat.birthtimeMs),
|
|
1377
1386
|
size: Number(stat.size),
|
|
1378
1387
|
mtimeMs: Number(stat.mtimeMs),
|
|
@@ -1405,7 +1414,7 @@ export function currentUsageLogRevision(): UsageLogRevision | null {
|
|
|
1405
1414
|
let fd: number | undefined;
|
|
1406
1415
|
try {
|
|
1407
1416
|
fd = openSync(path, "r");
|
|
1408
|
-
return usageLogRevision(path, fstatSync(fd));
|
|
1417
|
+
return usageLogRevision(path, fd, fstatSync(fd));
|
|
1409
1418
|
} finally {
|
|
1410
1419
|
if (fd !== undefined) closeSync(fd);
|
|
1411
1420
|
}
|
|
@@ -1530,7 +1539,7 @@ async function readUsageEntriesFullCooperatively(
|
|
|
1530
1539
|
if (prefixDigest === null) throw new Error("usage log changed while it was being read");
|
|
1531
1540
|
return {
|
|
1532
1541
|
entries: parsed.entries,
|
|
1533
|
-
revision: usageLogRevision(path, stat),
|
|
1542
|
+
revision: usageLogRevision(path, fd, stat),
|
|
1534
1543
|
truncatedPrefixBytes,
|
|
1535
1544
|
entriesTruncated: parsed.entriesDropped > 0,
|
|
1536
1545
|
entriesDropped: parsed.entriesDropped,
|
|
@@ -1562,7 +1571,7 @@ async function readUsageEntriesIncrementally(
|
|
|
1562
1571
|
try {
|
|
1563
1572
|
fd = openSync(path, "r");
|
|
1564
1573
|
const stat = fstatSync(fd);
|
|
1565
|
-
const revision = usageLogRevision(path, stat);
|
|
1574
|
+
const revision = usageLogRevision(path, fd, stat);
|
|
1566
1575
|
if (usageLogIdentityKey(revision) !== retained.identityKey) return null;
|
|
1567
1576
|
const size = Number(stat.size);
|
|
1568
1577
|
// A shrink means truncation or replacement-in-place; the retained rows may no
|