@bitkyc08/opencodex 2.80.0 → 2.82.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/README.md +3 -3
- package/bin/ocx.mjs +166 -73
- package/gui/dist/assets/App-Ds_qL7o8.js +54 -0
- package/gui/dist/assets/Tray-BqHF5Trd.js +1 -0
- package/gui/dist/assets/{index-Brk6F3fn.css → index-BAbcj51J.css} +1 -1
- package/gui/dist/assets/index-D3FBCNiP.js +94 -0
- package/gui/dist/assets/quota-summary-DTBC8n7-.js +1 -0
- package/gui/dist/index.html +2 -2
- package/gui/dist/provider-icons/claude-green.svg +1 -0
- package/package.json +3 -2
- package/src/adapters/anthropic-model-contract.ts +8 -2
- package/src/adapters/anthropic.ts +8 -3
- package/src/adapters/codebuddy/mcp-server.ts +2 -25
- package/src/adapters/codebuddy/tool-bridge.ts +1 -7
- package/src/adapters/coding-agent/mcp-server.ts +33 -0
- package/src/adapters/coding-agent/protocol.ts +122 -17
- package/src/adapters/coding-agent/tool-catalog.ts +75 -0
- package/src/adapters/coding-agent/turn.ts +101 -44
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +2 -0
- package/src/adapters/devin/live-models.ts +2 -0
- package/src/adapters/kiro-retry.ts +9 -4
- package/src/adapters/openai-chat/messages.ts +7 -6
- package/src/adapters/openai-responses/compaction-search-history.ts +109 -0
- package/src/adapters/openai-responses/passthrough.ts +9 -4
- package/src/adapters/openai-responses/tool-output-recovery.ts +20 -0
- package/src/adapters/physical-send.ts +8 -5
- package/src/adapters/qoder/adapter.ts +41 -6
- package/src/adapters/qoder/scaffold-guard.ts +2 -3
- package/src/adapters/zed.ts +16 -1
- package/src/claude/agents-inject.ts +14 -3
- package/src/claude/context-windows.ts +19 -17
- package/src/claude/desktop-3p.ts +88 -12
- package/src/claude/desktop-profile.ts +2 -1
- package/src/claude/gateway-cache.ts +18 -1
- package/src/claude/inbound-cache-stabilize.ts +4 -9
- package/src/claude/inbound.ts +78 -3
- package/src/claude/intercept/cli-catalog.ts +3 -0
- package/src/claude/intercept/model-bindings.ts +1 -0
- package/src/claude/intercept/picker-ca-persistence.ts +7 -6
- package/src/claude/intercept/picker-listener.ts +71 -4
- package/src/claude/intercept/picker-models.ts +14 -6
- package/src/claude/intercept/settings.ts +3 -0
- package/src/claude/long-context.ts +66 -0
- package/src/claude/model-info.ts +19 -16
- package/src/claude/outbound.ts +94 -9
- package/src/claude/subagent-model.ts +40 -11
- package/src/cli/account-anthropic-threshold.ts +14 -5
- package/src/cli/account-api.ts +3 -1
- package/src/cli/account-extended.ts +13 -9
- package/src/cli/account-policy-dto.ts +1 -1
- package/src/cli/account-policy.ts +13 -4
- package/src/cli/account.ts +3 -3
- package/src/cli/agent.ts +9 -5
- package/src/cli/aside-profile-recovery.ts +33 -0
- package/src/cli/capabilities-accounts.ts +2 -2
- package/src/cli/capabilities-agents-routing.ts +24 -0
- package/src/cli/capabilities-base.ts +5 -5
- package/src/cli/capabilities-integrations.ts +18 -2
- package/src/cli/cli-path-diagnostics.ts +132 -0
- package/src/cli/codex-shim-autorestore.ts +3 -0
- package/src/cli/desktop-runtime-guidance.ts +65 -0
- package/src/cli/dispatch.ts +15 -1
- package/src/cli/doctor.ts +16 -3
- package/src/cli/help.ts +4 -1
- package/src/cli/index.ts +25 -14
- package/src/cli/integration-aside-sync.ts +2 -1
- package/src/cli/integration-plan-dto.ts +1 -0
- package/src/cli/integration-preview.ts +8 -1
- package/src/cli/integrations.ts +48 -6
- package/src/cli/message-args.ts +34 -0
- package/src/cli/message-command.ts +43 -0
- package/src/cli/message-runtime.ts +6 -0
- package/src/cli/observe.ts +3 -1
- package/src/cli/provider.ts +29 -6
- package/src/cli/registry.ts +24 -2
- package/src/cli/resolve.ts +25 -2
- package/src/cli/root.ts +4 -2
- package/src/cli/status.ts +64 -12
- package/src/cli/update-restart-child.ts +4 -2
- package/src/cli/update-restart-home.ts +28 -10
- package/src/cli/update-restart-service-record.ts +116 -0
- package/src/cli/update-restart-supervision.ts +96 -0
- package/src/cli/update-restart.ts +59 -13
- package/src/cli/v2.ts +27 -20
- package/src/cli/version-skew-notice.ts +77 -0
- package/src/clients/config-export/commandcode.ts +131 -0
- package/src/clients/config-export/contracts.ts +12 -0
- package/src/clients/config-export.ts +64 -1
- package/src/clients/lazycodex.ts +1 -1
- package/src/clients/omo-role-models.ts +32 -7
- package/src/codex/auth-api/account-list.ts +11 -3
- package/src/codex/auth-api/account-selection.ts +107 -0
- package/src/codex/auth-api/main-account-probe.ts +25 -8
- package/src/codex/auth-api/pool-mode-gate.ts +33 -9
- package/src/codex/auth-api/pool-quota-probe.ts +15 -1
- package/src/codex/auth-api/routes.ts +11 -49
- package/src/codex/auth-context.ts +20 -1
- package/src/codex/autostart-health.ts +12 -3
- package/src/codex/catalog/auto-review.ts +13 -6
- package/src/codex/catalog/build-entries.ts +33 -7
- package/src/codex/catalog/bundled.ts +155 -26
- package/src/codex/catalog/combo-member.ts +3 -2
- package/src/codex/catalog/control-plane.ts +45 -0
- package/src/codex/catalog/effort.ts +37 -6
- package/src/codex/catalog/gather-capture.ts +10 -3
- package/src/codex/catalog/model-hints.ts +15 -8
- package/src/codex/catalog/model-visibility.ts +3 -1
- package/src/codex/catalog/operator-tiers.ts +38 -0
- package/src/codex/catalog/parsing.ts +2 -1
- package/src/codex/catalog/provider-models.ts +58 -44
- package/src/codex/catalog/retained-sync.ts +7 -5
- package/src/codex/catalog/routed-gather.ts +15 -1
- package/src/codex/catalog-auto-refresh-sources.ts +3 -4
- package/src/codex/catalog-auto-refresh.ts +19 -0
- package/src/codex/catalog.ts +1 -1
- package/src/codex/codex-home-owner.ts +3 -0
- package/src/codex/config-write-lock.ts +333 -0
- package/src/codex/control-plane-models.ts +6 -0
- package/src/codex/convergence.ts +2 -1
- package/src/codex/features.ts +257 -200
- package/src/codex/inject/config-write-section.ts +34 -0
- package/src/codex/inject/multi-agent-v2.ts +14 -6
- package/src/codex/inject/remove.ts +36 -11
- package/src/codex/inject/restore.ts +85 -20
- package/src/codex/inject-coordination.ts +15 -6
- package/src/codex/inject.ts +62 -20
- package/src/codex/journal.ts +62 -17
- package/src/codex/main-account-cache.ts +43 -0
- package/src/codex/paths.ts +2 -0
- package/src/codex/prepared-home.ts +18 -0
- package/src/codex/prompt-journal.ts +25 -12
- package/src/codex/prompt-layers.ts +49 -26
- package/src/codex/prompt-lock-claim.ts +134 -0
- package/src/codex/prompt-lock-evidence.ts +26 -0
- package/src/codex/prompt-lock-io.ts +16 -0
- package/src/codex/prompt-lock-owner.ts +130 -0
- package/src/codex/prompt-lock.ts +84 -53
- package/src/codex/routing/failure-window.ts +146 -0
- package/src/codex/routing/selection.ts +4 -1
- package/src/codex/routing.ts +13 -17
- package/src/codex/runtime.ts +106 -18
- package/src/codex/shim.ts +45 -19
- package/src/combos/jev-decision-contract.ts +25 -2
- package/src/combos/jev-service-exchange.ts +281 -0
- package/src/combos/jev.ts +34 -219
- package/src/combos/types.ts +1 -1
- package/src/config/atomic-write.ts +1 -1
- package/src/config/codex-ws-reuse-setting.ts +10 -0
- package/src/config/diagnostics.ts +27 -16
- package/src/config/live-reconcile.ts +17 -0
- package/src/config/load-degrade.ts +42 -0
- package/src/config/rebase-provenance.ts +11 -0
- package/src/config/schema/anthropic-account-pool.ts +143 -0
- package/src/config/schema/config-schema.ts +11 -1
- package/src/config/schema/leaf-validators.ts +4 -0
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +566 -298
- package/src/generated/model-metadata.ts +7 -6
- package/src/grok/inject.ts +50 -4
- package/src/grok/sync.ts +2 -1
- package/src/images/loop.ts +30 -11
- package/src/integrations/aside-profiles.ts +10 -3
- package/src/integrations/catalog-refresh.ts +19 -3
- package/src/integrations/mutation-plan.ts +7 -0
- package/src/integrations/registry.ts +8 -0
- package/src/integrations/state.ts +9 -2
- package/src/integrations/writer.ts +17 -5
- package/src/lib/bun-path-runtime.d.mts +32 -0
- package/src/lib/bun-path-runtime.mjs +95 -0
- package/src/lib/bun-runtime-preflight.ts +140 -0
- package/src/lib/desktop-cli-handoff.d.mts +20 -0
- package/src/lib/desktop-cli-handoff.mjs +92 -0
- package/src/lib/desktop-cli-record.d.mts +29 -0
- package/src/lib/desktop-cli-record.mjs +102 -0
- package/src/lib/lab-live-route-production.ts +9 -1
- package/src/lib/request-execution-budget.ts +244 -58
- package/src/lib/spend-pool-alias-validation.ts +79 -0
- package/src/lib/spend-pool-continuity.ts +34 -0
- package/src/lib/spend-reservation-ledger.ts +412 -50
- package/src/lib/state-store-registrations.ts +20 -3
- package/src/lib/synced-state-location.ts +46 -6
- package/src/lib/test-home-guard.ts +87 -1
- package/src/lib/translator-budget.ts +17 -4
- package/src/lib/upstream-retry.ts +17 -5
- package/src/lib/windows-atomic-replace.ts +38 -16
- package/src/lib/windows-elevation.ts +6 -0
- package/src/lib/windows-secret-acl.ts +92 -25
- package/src/lib/winsw.ts +2 -3
- package/src/lib/workflow-budget.ts +53 -11
- package/src/messaging/budget.ts +45 -0
- package/src/messaging/discovery.ts +57 -0
- package/src/messaging/envelope.ts +49 -0
- package/src/messaging/input.ts +37 -0
- package/src/messaging/rpc.ts +196 -0
- package/src/messaging/send.ts +58 -0
- package/src/messaging/socket.ts +72 -0
- package/src/messaging/types.ts +29 -0
- package/src/oauth/anthropic-account-refusal.ts +90 -18
- package/src/oauth/anthropic-account-threshold.ts +8 -2
- package/src/oauth/anthropic-continuity.ts +15 -1
- package/src/oauth/anthropic-model-quota.ts +120 -48
- package/src/oauth/anthropic-model-routes.ts +11 -3
- package/src/oauth/anthropic-oauth-definitions.ts +98 -0
- package/src/oauth/anthropic-pool-config.ts +30 -0
- package/src/oauth/anthropic-rate-limit-policy.ts +64 -9
- package/src/oauth/anthropic-routing.ts +1202 -866
- package/src/oauth/anthropic-send-ownership.ts +41 -0
- package/src/oauth/anthropic.ts +4 -1
- package/src/oauth/chatgpt.ts +113 -41
- package/src/oauth/generic-account-failover.ts +3 -1
- package/src/oauth/health.ts +8 -6
- package/src/oauth/index.ts +96 -175
- package/src/oauth/kiro.ts +22 -9
- package/src/oauth/login-cli.ts +2 -0
- package/src/oauth/model-discovery-auth.ts +43 -0
- package/src/oauth/pool-kernel.ts +11 -5
- package/src/oauth/pool-settings-capability.ts +5 -3
- package/src/oauth/store-anthropic-instance.ts +87 -0
- package/src/oauth/store.ts +68 -10
- package/src/oauth/token-guardian.ts +2 -0
- package/src/oauth/types.ts +2 -0
- package/src/protocols/plan-snapshot.ts +5 -0
- package/src/protocols/settings.ts +20 -5
- package/src/providers/anthropic-instance-id.ts +47 -0
- package/src/providers/anthropic-instance.ts +48 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +11 -1
- package/src/providers/azure-model-metadata.ts +115 -0
- package/src/providers/derive.ts +23 -0
- package/src/providers/kiro-models.ts +3 -0
- package/src/providers/label.ts +1 -1
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/quota/account-cache.ts +37 -11
- package/src/providers/quota/anthropic-account-quota.ts +93 -0
- package/src/providers/quota/anthropic-cooldown-recovery.ts +222 -111
- package/src/providers/quota/report-cache.ts +24 -1
- package/src/providers/quota/vendor-probes-key.ts +127 -17
- package/src/providers/quota/vendor-probes-oauth.ts +46 -14
- package/src/providers/quota.ts +23 -33
- package/src/providers/registry/entries-core.ts +35 -30
- package/src/providers/registry/model-ids.ts +1 -0
- package/src/providers/registry/model-seeds.ts +7 -4
- package/src/providers/registry/types.ts +3 -0
- package/src/providers/registry.ts +3 -3
- package/src/responses/reasoning-envelope.ts +29 -1
- package/src/responses/reasoning-replay-cache.ts +17 -1
- package/src/responses/spill-store.ts +20 -3
- package/src/responses/state/metrics.ts +95 -0
- package/src/responses/state/snapshot-codec.ts +6 -0
- package/src/responses/state/spill-failure.ts +12 -0
- package/src/responses/state/spill-queue.ts +13 -5
- package/src/responses/state/unforced-store-false.ts +55 -0
- package/src/responses/state.ts +68 -76
- package/src/responses/task-input.ts +17 -0
- package/src/router.ts +28 -4
- package/src/routing/compatibility/assemble.ts +1 -1
- package/src/routing/quota.ts +7 -3
- package/src/server/auth-cors.ts +5 -0
- package/src/server/background-lifecycle.ts +27 -0
- package/src/server/chat-completions.ts +7 -1
- package/src/server/chat-native.ts +64 -28
- package/src/server/claude-messages.ts +59 -8
- package/src/server/gui-static.ts +38 -4
- package/src/server/inbound-body-admission.ts +1 -0
- package/src/server/index/claude-intercept-lifecycle.ts +3 -1
- package/src/server/index/serve-options.ts +10 -2
- package/src/server/index/spend-ledger-lifecycle.ts +12 -5
- package/src/server/index/startup-warnings.ts +19 -0
- package/src/server/index.ts +5 -2
- package/src/server/inference/context.ts +1 -1
- package/src/server/lifecycle.ts +1 -1
- package/src/server/management/agent-settings-routes.ts +123 -38
- package/src/server/management/anthropic-account-threshold.ts +12 -9
- package/src/server/management/anthropic-pool-settings.ts +72 -0
- package/src/server/management/anthropic-reset-grant-routes.ts +98 -39
- package/src/server/management/codex-agent-role-routes.ts +4 -2
- package/src/server/management/codex-prompt-routes.ts +1 -0
- package/src/server/management/codex-role-auto-assign.ts +13 -2
- package/src/server/management/config-routes.ts +85 -14
- package/src/server/management/context.ts +2 -2
- package/src/server/management/decision-routes.ts +2 -1
- package/src/server/management/integration-routes.ts +25 -2
- package/src/server/management/oauth-account-routes.ts +79 -98
- package/src/server/management/provider-instance-ownership.ts +44 -0
- package/src/server/management/provider-routes.ts +21 -28
- package/src/server/management/route-registry.ts +3 -1
- package/src/server/management/routing-profile-routes.ts +4 -4
- package/src/server/management/shared.ts +3 -3
- package/src/server/management/system-restart.ts +44 -14
- package/src/server/management/system-routes.ts +24 -2
- package/src/server/management/vision-sidecar-options.ts +9 -6
- package/src/server/management/web-search-sidecar-options.ts +51 -2
- package/src/server/management-auth.ts +6 -0
- package/src/server/messages-native-eligibility.ts +3 -2
- package/src/server/messages-native-oauth.ts +91 -45
- package/src/server/messages-native-selector.ts +39 -0
- package/src/server/messages-native.ts +133 -38
- package/src/server/messages-response-headers.ts +14 -0
- package/src/server/request-log-conversation.ts +48 -5
- package/src/server/request-log.ts +36 -2
- package/src/server/responses/adapter-continuation.ts +31 -19
- package/src/server/responses/adapter-dispatch.ts +75 -24
- package/src/server/responses/antigravity-validation-refusal.ts +20 -6
- package/src/server/responses/codex-ws-exchange.ts +17 -2
- package/src/server/responses/codex-ws-pool.ts +100 -13
- package/src/server/responses/codex-ws-wire.ts +9 -0
- package/src/server/responses/combo-requested-effort.ts +3 -1
- package/src/server/responses/compact.ts +25 -8
- package/src/server/responses/compaction-recovery.ts +1 -1
- package/src/server/responses/core-codex-account.ts +22 -1
- package/src/server/responses/core-combo-native.ts +24 -4
- package/src/server/responses/core-combo.ts +38 -25
- package/src/server/responses/core-lifetime.ts +3 -36
- package/src/server/responses/core-normalize.ts +1 -0
- package/src/server/responses/core-options.ts +8 -1
- package/src/server/responses/core-replay.ts +78 -0
- package/src/server/responses/core.ts +3 -3
- package/src/server/responses/fetch-helpers.ts +26 -3
- package/src/server/responses/passthrough-delivery.ts +30 -4
- package/src/server/responses/passthrough-dispatch.ts +39 -9
- package/src/server/responses/request-prepare.ts +41 -5
- package/src/server/responses/request-send-budget.ts +150 -50
- package/src/server/responses/request-spend.ts +325 -33
- package/src/server/responses/request-transport.ts +132 -52
- package/src/server/responses/response-effects.ts +1 -0
- package/src/server/responses/run-turn-execution.ts +13 -3
- package/src/server/responses/sidecar-execution.ts +113 -22
- package/src/server/responses/sidecar-send-budget.ts +58 -0
- package/src/server/responses/ws-upstream.ts +19 -6
- package/src/server/responses-undeclared-tool-guard.ts +5 -1
- package/src/server/startup-health-cache.ts +2 -1
- package/src/server/workflow-refusal.ts +33 -0
- package/src/service/cli.ts +20 -4
- package/src/service/desktop-command-guard.ts +32 -0
- package/src/service/desktop-startup.ts +66 -40
- package/src/service/desktop-supervision.d.mts +26 -0
- package/src/service/desktop-supervision.mjs +140 -0
- package/src/service/managing-cli.ts +1 -0
- package/src/service/orchestration.ts +46 -20
- package/src/service/ownership-mutation-lease.d.mts +1 -1
- package/src/service/ownership-mutation-lease.mjs +5 -7
- package/src/service/repair.ts +25 -9
- package/src/service/state.ts +4 -3
- package/src/service/systemd.ts +17 -8
- package/src/service/windows-ops.ts +61 -22
- package/src/sidecar/anthropic-binding.ts +79 -0
- package/src/sidecar/auth.ts +76 -2
- package/src/storage/policy-job.ts +5 -1
- package/src/storage/policy-worker.ts +3 -1
- package/src/storage/policy.ts +9 -0
- package/src/types/anthropic-account-pool.ts +33 -0
- package/src/types/config.ts +33 -28
- package/src/types/provider.ts +5 -0
- package/src/types/request.ts +5 -0
- package/src/types/tools.ts +7 -0
- package/src/types/wire.ts +1 -0
- package/src/types.ts +4 -0
- package/src/update/index.ts +57 -9
- package/src/update/restart-ownership.ts +10 -3
- package/src/update/runtime-ownership.d.mts +6 -0
- package/src/update/runtime-ownership.mjs +23 -7
- package/src/usage/cost.ts +3 -2
- package/src/usage/expected-prices.ts +66 -14
- package/src/vision/anthropic-describe.ts +16 -3
- package/src/vision/index.ts +3 -0
- package/src/vision/plan.ts +19 -6
- package/src/web-search/alpha-search.ts +7 -1
- package/src/web-search/anthropic-executor.ts +16 -4
- package/src/web-search/index.ts +7 -3
- package/src/web-search/loop.ts +32 -7
- package/src/web-search/passthrough-bridge.ts +4 -1
- package/src/web-search/sidecar-providers.ts +4 -3
- package/gui/dist/assets/App-MbDVfUeC.js +0 -54
- package/gui/dist/assets/Tray-CzkxK82A.js +0 -1
- package/gui/dist/assets/index-UJt6npXk.js +0 -94
- package/gui/dist/assets/quota-summary-DU3sD6Z-.js +0 -1
|
@@ -14,7 +14,11 @@
|
|
|
14
14
|
import { jsonResponse } from "../auth-cors";
|
|
15
15
|
import type { ManagementContext } from "./context";
|
|
16
16
|
import { getValidAccessSnapshotForAccount } from "../../oauth";
|
|
17
|
-
import {
|
|
17
|
+
import { configuredAnthropicInstance, isAnthropicInstanceId, type AnthropicInstanceId } from "../../providers/anthropic-instance";
|
|
18
|
+
import { captureAnthropicPhysicalSendOwnership, anthropicPhysicalSendOwnershipIsCurrent } from "../../oauth/anthropic-send-ownership";
|
|
19
|
+
import { anthropicCooldownRecoveryFor } from "../../providers/quota/anthropic-cooldown-recovery";
|
|
20
|
+
import { captureProviderAccountQuotaEpoch } from "../../providers/quota/account-cache";
|
|
21
|
+
import { captureOAuthAccountSelection, getAccountSet, getAccountCredentialWithStatus, listAccounts } from "../../oauth/store";
|
|
18
22
|
import {
|
|
19
23
|
ANTHROPIC_RESET_GRANT_ID_RE,
|
|
20
24
|
AnthropicResetGrantError,
|
|
@@ -28,19 +32,20 @@ import {
|
|
|
28
32
|
AnthropicResetLedgerError,
|
|
29
33
|
anthropicOrgDigest,
|
|
30
34
|
anthropicResetOperationExists,
|
|
35
|
+
anthropicResetJournalPathForInstance,
|
|
31
36
|
beginAnthropicResetOperation,
|
|
32
37
|
pendingAnthropicResetOperation,
|
|
33
38
|
releaseAnthropicResetLease,
|
|
34
39
|
settleAnthropicResetOperation,
|
|
35
40
|
} from "../../providers/anthropic-reset-grant-ledger";
|
|
36
41
|
|
|
37
|
-
const PROVIDER = "anthropic";
|
|
38
42
|
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
39
43
|
|
|
40
44
|
const MESSAGES = {
|
|
41
45
|
method_not_allowed: "Method not allowed",
|
|
42
46
|
session_required: "Spending a reset requires a dashboard session.",
|
|
43
47
|
invalid_json: "Request body must be JSON.",
|
|
48
|
+
invalid_provider: "No matching builtin Anthropic OAuth provider.",
|
|
44
49
|
invalid_grant_id: "grantId is missing or malformed.",
|
|
45
50
|
invalid_operation_id: "operationId must be a UUIDv4.",
|
|
46
51
|
no_account: "No matching Anthropic OAuth account.",
|
|
@@ -60,6 +65,9 @@ const MESSAGES = {
|
|
|
60
65
|
type ErrorCode = keyof typeof MESSAGES;
|
|
61
66
|
|
|
62
67
|
export interface AnthropicResetGrantRouteDeps {
|
|
68
|
+
provider?: AnthropicInstanceId;
|
|
69
|
+
isCurrent?: () => boolean;
|
|
70
|
+
forProvider?: (provider: AnthropicInstanceId) => AnthropicResetGrantRouteDeps;
|
|
63
71
|
fetchFn?: typeof globalThis.fetch;
|
|
64
72
|
journalPath?: string;
|
|
65
73
|
now?: () => number;
|
|
@@ -68,25 +76,64 @@ export interface AnthropicResetGrantRouteDeps {
|
|
|
68
76
|
accessTokenFor: (accountId: string) => Promise<string>;
|
|
69
77
|
}
|
|
70
78
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
function defaultDepsFor(ctx: ManagementContext, provider: AnthropicInstanceId): AnthropicResetGrantRouteDeps | null {
|
|
80
|
+
// One source: the live management config. A keeps its legacy reach without a providers row;
|
|
81
|
+
// B requires its configured builtin row. A fresh file load here would refuse on any drift.
|
|
82
|
+
const live = ctx.config;
|
|
83
|
+
const target = live.providers?.[provider];
|
|
84
|
+
if (target?.disabled || target && target.authMode !== "oauth" || configuredAnthropicInstance(live, provider) !== provider) return null;
|
|
85
|
+
const identity = JSON.stringify(target ?? null);
|
|
86
|
+
let owner: ReturnType<typeof captureAnthropicPhysicalSendOwnership> = null;
|
|
87
|
+
const targetCurrent = () => configuredAnthropicInstance(live, provider) === provider
|
|
88
|
+
&& JSON.stringify(live.providers?.[provider] ?? null) === identity;
|
|
89
|
+
return {
|
|
90
|
+
// Only the journal path: the ledger's numeric `now` option is not the deps clock function.
|
|
91
|
+
provider, journalPath: anthropicResetJournalPathForInstance(provider),
|
|
92
|
+
listAccountIds: () => listAccounts(provider).map(account => account.id),
|
|
93
|
+
activeAccountId: () => captureOAuthAccountSelection(provider)?.accountId ?? null,
|
|
94
|
+
isCurrent: () => {
|
|
95
|
+
if (!targetCurrent() || !owner || !anthropicPhysicalSendOwnershipIsCurrent(owner)) return false;
|
|
96
|
+
const row = getAccountCredentialWithStatus(provider, owner.accountId);
|
|
97
|
+
return !!row && !row.paused && !row.needsReauth;
|
|
98
|
+
},
|
|
99
|
+
accessTokenFor: async accountId => {
|
|
100
|
+
const initial = getAccountSet(provider)?.accounts.find(row => row.id === accountId);
|
|
101
|
+
const recovery = anthropicCooldownRecoveryFor(provider);
|
|
102
|
+
const incarnation = recovery.reserveAnthropicAccountIncarnation(accountId);
|
|
103
|
+
const epoch = captureProviderAccountQuotaEpoch(provider);
|
|
104
|
+
const snapshot = await getValidAccessSnapshotForAccount(provider, accountId, { requireUsableAccount: true });
|
|
105
|
+
const current = getAccountSet(provider)?.accounts.find(row => row.id === accountId);
|
|
106
|
+
if (!initial || !current || initial.loginId !== current.loginId || initial.addedAt !== current.addedAt
|
|
107
|
+
|| recovery.anthropicAccountIncarnation(accountId) !== incarnation || captureProviderAccountQuotaEpoch(provider) !== epoch) throw new Error("reset account replaced");
|
|
108
|
+
owner = captureAnthropicPhysicalSendOwnership(snapshot);
|
|
109
|
+
if (!targetCurrent() || !owner) throw new Error("reset owner changed");
|
|
110
|
+
return snapshot.accessToken;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function selectDeps(ctx: ManagementContext, requested: unknown, deps?: AnthropicResetGrantRouteDeps): AnthropicResetGrantRouteDeps | null {
|
|
116
|
+
const provider = requested === undefined || requested === null ? "anthropic" : requested;
|
|
117
|
+
if (!isAnthropicInstanceId(provider)) return null;
|
|
118
|
+
const selected = deps?.forProvider?.(provider) ?? deps;
|
|
119
|
+
// Legacy injected dependencies are A-only; B must explicitly bind its own journal.
|
|
120
|
+
if (selected) return (selected.provider ?? "anthropic") === provider
|
|
121
|
+
? { journalPath: anthropicResetJournalPathForInstance(provider), ...selected, provider } : null;
|
|
122
|
+
return defaultDepsFor(ctx, provider);
|
|
123
|
+
}
|
|
77
124
|
|
|
78
125
|
function fail(ctx: ManagementContext, status: number, code: ErrorCode, extra: Record<string, unknown> = {}): Response {
|
|
79
126
|
return jsonResponse({ error: { code, message: MESSAGES[code], ...extra } }, status, ctx.req, ctx.config);
|
|
80
127
|
}
|
|
81
128
|
|
|
82
|
-
function ledgerFailure(ctx: ManagementContext, error: unknown): Response {
|
|
83
|
-
if (error instanceof AnthropicResetLedgerError && error.code === "busy") return fail(ctx, 503, "ledger_busy");
|
|
84
|
-
return fail(ctx, 503, "ledger_unavailable");
|
|
129
|
+
function ledgerFailure(ctx: ManagementContext, error: unknown, extra: Record<string, unknown> = {}): Response {
|
|
130
|
+
if (error instanceof AnthropicResetLedgerError && error.code === "busy") return fail(ctx, 503, "ledger_busy", extra);
|
|
131
|
+
return fail(ctx, 503, "ledger_unavailable", extra);
|
|
85
132
|
}
|
|
86
133
|
|
|
87
|
-
function readFailure(ctx: ManagementContext, error: unknown): Response {
|
|
88
|
-
if (error instanceof AnthropicResetGrantError && error.code === "auth") return fail(ctx, 401, "auth_failed");
|
|
89
|
-
return fail(ctx, 502, "upstream_unavailable");
|
|
134
|
+
function readFailure(ctx: ManagementContext, error: unknown, extra: Record<string, unknown> = {}): Response {
|
|
135
|
+
if (error instanceof AnthropicResetGrantError && error.code === "auth") return fail(ctx, 401, "auth_failed", extra);
|
|
136
|
+
return fail(ctx, 502, "upstream_unavailable", extra);
|
|
90
137
|
}
|
|
91
138
|
|
|
92
139
|
function resolveAccountId(deps: AnthropicResetGrantRouteDeps, requested: unknown): string | null {
|
|
@@ -107,16 +154,19 @@ async function tokenFor(deps: AnthropicResetGrantRouteDeps, accountId: string):
|
|
|
107
154
|
}
|
|
108
155
|
|
|
109
156
|
async function handleRead(ctx: ManagementContext, deps: AnthropicResetGrantRouteDeps): Promise<Response> {
|
|
157
|
+
const receipt = { provider: deps.provider };
|
|
110
158
|
const accountId = resolveAccountId(deps, ctx.url.searchParams.get("accountId") ?? undefined);
|
|
111
|
-
if (!accountId) return fail(ctx, 400, "no_account");
|
|
159
|
+
if (!accountId) return fail(ctx, 400, "no_account", receipt);
|
|
112
160
|
const accessToken = await tokenFor(deps, accountId);
|
|
113
|
-
if (!accessToken) return fail(ctx, 401, "auth_failed");
|
|
161
|
+
if (!accessToken) return fail(ctx, 401, "auth_failed", receipt);
|
|
162
|
+
if (deps.isCurrent?.() === false) return fail(ctx, 401, "auth_failed", receipt);
|
|
114
163
|
let status;
|
|
115
164
|
try {
|
|
116
165
|
status = await fetchAnthropicResetGrantStatus({ accessToken, fetchFn: deps.fetchFn });
|
|
117
166
|
} catch (error) {
|
|
118
|
-
return readFailure(ctx, error);
|
|
167
|
+
return readFailure(ctx, error, receipt);
|
|
119
168
|
}
|
|
169
|
+
if (deps.isCurrent?.() === false) return fail(ctx, 401, "auth_failed", receipt);
|
|
120
170
|
let pendingOperation = null;
|
|
121
171
|
let journalAvailable = true;
|
|
122
172
|
try {
|
|
@@ -124,10 +174,10 @@ async function handleRead(ctx: ManagementContext, deps: AnthropicResetGrantRoute
|
|
|
124
174
|
} catch {
|
|
125
175
|
journalAvailable = false;
|
|
126
176
|
}
|
|
127
|
-
return jsonResponse({ accountId, ...status, pendingOperation, journalAvailable }, 200, ctx.req, ctx.config);
|
|
177
|
+
return jsonResponse({ provider: deps.provider, accountId, ...status, pendingOperation, journalAvailable }, 200, ctx.req, ctx.config);
|
|
128
178
|
}
|
|
129
179
|
|
|
130
|
-
async function handleConsume(ctx: ManagementContext,
|
|
180
|
+
async function handleConsume(ctx: ManagementContext, injected?: AnthropicResetGrantRouteDeps): Promise<Response> {
|
|
131
181
|
if (ctx.principal !== "gui-session") return fail(ctx, 403, "session_required");
|
|
132
182
|
let body: Record<string, unknown>;
|
|
133
183
|
try {
|
|
@@ -137,21 +187,27 @@ async function handleConsume(ctx: ManagementContext, deps: AnthropicResetGrantRo
|
|
|
137
187
|
} catch {
|
|
138
188
|
return fail(ctx, 400, "invalid_json");
|
|
139
189
|
}
|
|
190
|
+
const deps = selectDeps(ctx, body.provider, injected);
|
|
191
|
+
if (!deps) return fail(ctx, 400, "invalid_provider");
|
|
192
|
+
const provider = deps.provider;
|
|
193
|
+
const operationReceipt = { provider };
|
|
194
|
+
const scopedFail = (status: number, code: ErrorCode, extra: Record<string, unknown> = {}) => fail(ctx, status, code, { ...operationReceipt, ...extra });
|
|
140
195
|
const { grantId, operationId } = body;
|
|
141
|
-
if (typeof grantId !== "string" || !ANTHROPIC_RESET_GRANT_ID_RE.test(grantId)) return
|
|
142
|
-
if (typeof operationId !== "string" || !UUID_V4_RE.test(operationId)) return
|
|
143
|
-
if (typeof body.accountId !== "string") return
|
|
196
|
+
if (typeof grantId !== "string" || !ANTHROPIC_RESET_GRANT_ID_RE.test(grantId)) return scopedFail(400, "invalid_grant_id");
|
|
197
|
+
if (typeof operationId !== "string" || !UUID_V4_RE.test(operationId)) return scopedFail(400, "invalid_operation_id");
|
|
198
|
+
if (typeof body.accountId !== "string") return scopedFail(400, "no_account");
|
|
144
199
|
const accountId = resolveAccountId(deps, body.accountId);
|
|
145
|
-
if (!accountId) return
|
|
200
|
+
if (!accountId) return scopedFail(400, "no_account");
|
|
146
201
|
|
|
147
202
|
const accessToken = await tokenFor(deps, accountId);
|
|
148
|
-
if (!accessToken) return
|
|
203
|
+
if (!accessToken || deps.isCurrent?.() === false) return scopedFail(401, "auth_failed");
|
|
149
204
|
let organizationUuid: string;
|
|
150
205
|
try {
|
|
151
206
|
organizationUuid = await fetchAnthropicOrganizationUuid({ accessToken, fetchFn: deps.fetchFn });
|
|
152
207
|
} catch (error) {
|
|
153
|
-
return readFailure(ctx, error);
|
|
208
|
+
return readFailure(ctx, error, operationReceipt);
|
|
154
209
|
}
|
|
210
|
+
if (deps.isCurrent?.() === false) return scopedFail(401, "auth_failed");
|
|
155
211
|
const ledger = { journalPath: deps.journalPath, now: deps.now?.() };
|
|
156
212
|
|
|
157
213
|
// A new operation must pass the spend gate on a fresh read. A same-id retry of
|
|
@@ -161,39 +217,40 @@ async function handleConsume(ctx: ManagementContext, deps: AnthropicResetGrantRo
|
|
|
161
217
|
try {
|
|
162
218
|
known = anthropicResetOperationExists(operationId, ledger);
|
|
163
219
|
} catch (error) {
|
|
164
|
-
return ledgerFailure(ctx, error);
|
|
220
|
+
return ledgerFailure(ctx, error, operationReceipt);
|
|
165
221
|
}
|
|
166
222
|
if (!known) {
|
|
167
223
|
let status;
|
|
168
224
|
try {
|
|
169
225
|
status = await fetchAnthropicResetGrantStatus({ accessToken, fetchFn: deps.fetchFn });
|
|
170
226
|
} catch (error) {
|
|
171
|
-
return readFailure(ctx, error);
|
|
227
|
+
return readFailure(ctx, error, operationReceipt);
|
|
172
228
|
}
|
|
173
229
|
const blocker = anthropicResetGrantBlocker(status, grantId);
|
|
174
|
-
if (blocker) return
|
|
230
|
+
if (blocker) return scopedFail(409, "grant_not_usable", { reason: blocker });
|
|
175
231
|
}
|
|
176
232
|
|
|
177
233
|
let begin;
|
|
234
|
+
if (deps.isCurrent?.() === false) return scopedFail(401, "auth_failed");
|
|
178
235
|
try {
|
|
179
236
|
begin = beginAnthropicResetOperation(
|
|
180
237
|
{ operationId, accountId, grantId, orgDigest: anthropicOrgDigest(organizationUuid) },
|
|
181
238
|
{ journalPath: deps.journalPath, now: deps.now?.() },
|
|
182
239
|
);
|
|
183
240
|
} catch (error) {
|
|
184
|
-
return ledgerFailure(ctx, error);
|
|
241
|
+
return ledgerFailure(ctx, error, operationReceipt);
|
|
185
242
|
}
|
|
186
243
|
switch (begin.kind) {
|
|
187
244
|
case "replay":
|
|
188
245
|
return jsonResponse(
|
|
189
|
-
{ code: begin.code, replayed: true, resetsLeft: begin.resetsLeft, accountId, grantId, operationId },
|
|
246
|
+
{ provider, code: begin.code, replayed: true, resetsLeft: begin.resetsLeft, accountId, grantId, operationId },
|
|
190
247
|
200, ctx.req, ctx.config,
|
|
191
248
|
);
|
|
192
|
-
case "identity-mismatch": return
|
|
193
|
-
case "in-flight": return
|
|
194
|
-
case "expired": return
|
|
195
|
-
case "unresolved-prior": return
|
|
196
|
-
case "capacity": return
|
|
249
|
+
case "identity-mismatch": return scopedFail(409, "operation_identity_mismatch");
|
|
250
|
+
case "in-flight": return scopedFail(409, "in_flight");
|
|
251
|
+
case "expired": return scopedFail(409, "unknown_outcome_expired");
|
|
252
|
+
case "unresolved-prior": return scopedFail(409, "unresolved_prior_operation", { pendingOperationId: begin.operationId });
|
|
253
|
+
case "capacity": return scopedFail(503, "ledger_capacity");
|
|
197
254
|
case "execute": break;
|
|
198
255
|
}
|
|
199
256
|
|
|
@@ -206,7 +263,7 @@ async function handleConsume(ctx: ManagementContext, deps: AnthropicResetGrantRo
|
|
|
206
263
|
try {
|
|
207
264
|
releaseAnthropicResetLease(operationId, { journalPath: deps.journalPath, now: deps.now?.() });
|
|
208
265
|
} catch { /* the lease expires on its own */ }
|
|
209
|
-
return
|
|
266
|
+
return scopedFail(502, "unknown_outcome", { operationId, grantId, accountId });
|
|
210
267
|
}
|
|
211
268
|
let settled;
|
|
212
269
|
try {
|
|
@@ -216,13 +273,14 @@ async function handleConsume(ctx: ManagementContext, deps: AnthropicResetGrantRo
|
|
|
216
273
|
);
|
|
217
274
|
} catch {
|
|
218
275
|
// Fail closed: the caller must not read this as a durable result.
|
|
219
|
-
return
|
|
276
|
+
return scopedFail(500, "journal_write_failed", { operationId, grantId, accountId });
|
|
220
277
|
}
|
|
221
278
|
// Report what the journal holds. An overlapping same-id attempt may have
|
|
222
279
|
// settled first; its answer is the canonical one.
|
|
223
280
|
const replayed = settled.code !== answer.code || settled.resetsLeft !== answer.resetsLeft;
|
|
224
281
|
return jsonResponse(
|
|
225
282
|
{
|
|
283
|
+
provider,
|
|
226
284
|
code: settled.code,
|
|
227
285
|
replayed,
|
|
228
286
|
resetsLeft: settled.resetsLeft,
|
|
@@ -237,12 +295,13 @@ async function handleConsume(ctx: ManagementContext, deps: AnthropicResetGrantRo
|
|
|
237
295
|
|
|
238
296
|
export async function handleAnthropicResetGrantRoutes(
|
|
239
297
|
ctx: ManagementContext,
|
|
240
|
-
deps
|
|
298
|
+
deps?: AnthropicResetGrantRouteDeps,
|
|
241
299
|
): Promise<Response | null> {
|
|
242
300
|
const { pathname } = ctx.url;
|
|
243
301
|
if (pathname === "/api/anthropic/reset-grants") {
|
|
244
302
|
if (ctx.req.method !== "GET") return fail(ctx, 405, "method_not_allowed");
|
|
245
|
-
|
|
303
|
+
const selected = selectDeps(ctx, ctx.url.searchParams.get("provider"), deps);
|
|
304
|
+
return selected ? handleRead(ctx, selected) : fail(ctx, 400, "invalid_provider");
|
|
246
305
|
}
|
|
247
306
|
if (pathname === "/api/anthropic/reset-grants/consume") {
|
|
248
307
|
if (ctx.req.method !== "POST") return fail(ctx, 405, "method_not_allowed");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Per-role model for omo (Codex / LazyCodex): `$CODEX_HOME/agents/<role>.toml` plus LazyCodex's
|
|
3
|
-
* `codex.agents.<role
|
|
3
|
+
* `"[codex]".agents.<role>` model and reasoning mirror in omo.jsonc. Both halves exist only when LazyCodex is
|
|
4
4
|
* detected; Pi-based and OpenCode-based omo are never read here.
|
|
5
5
|
*
|
|
6
6
|
* Loaded on demand from `src/server/management-api.ts`, like the quota-reset handler, so a
|
|
@@ -40,6 +40,7 @@ export async function handleCodexAgentRoleRoutes(ctx: ManagementContext): Promis
|
|
|
40
40
|
omoJsonc: { state: omoState.state },
|
|
41
41
|
roles: roles.listCodexAgentRoleModels(codexHome).map(entry => ({
|
|
42
42
|
...entry,
|
|
43
|
+
effort: roles.readCodexAgentRoleEffort(entry.role, codexHome),
|
|
43
44
|
omoJsoncModel: omoModels[entry.role] ?? null,
|
|
44
45
|
})),
|
|
45
46
|
}, 200, req, config);
|
|
@@ -114,7 +115,8 @@ export async function handleCodexAgentRoleRoutes(ctx: ManagementContext): Promis
|
|
|
114
115
|
// The role TOML is what Codex obeys, so its write stands even when the omo mirror cannot follow.
|
|
115
116
|
let omoStatus: ReturnType<typeof omo.writeOmoRoleModel> | "write_failed";
|
|
116
117
|
try {
|
|
117
|
-
|
|
118
|
+
const reasoning = effort === undefined ? undefined : omo.omoReasoningFor(effort);
|
|
119
|
+
omoStatus = omo.writeOmoRoleModel(role, model, omo.omoJsoncPath(), reasoning);
|
|
118
120
|
} catch {
|
|
119
121
|
omoStatus = "write_failed";
|
|
120
122
|
}
|
|
@@ -58,6 +58,10 @@ async function modelCandidates(slugs: readonly string[], models: readonly Catalo
|
|
|
58
58
|
]);
|
|
59
59
|
const nativeSlugs = catalog.listCatalogNativeSlugs();
|
|
60
60
|
const routed = new Map(models.map(model => [catalog.catalogModelSlug(model), model]));
|
|
61
|
+
// A routed model that declares no levels still gets the ladder Codex shows for it, the same one
|
|
62
|
+
// the Models page lists, so its proposal carries an effort instead of none. An explicit empty
|
|
63
|
+
// ladder means the model takes no effort control, and the written catalog lists none for it.
|
|
64
|
+
const written = catalog.catalogModelLadders(slugs);
|
|
61
65
|
const unitPrice = (provider: string, modelId: string): number | null => {
|
|
62
66
|
const cost = resolveMatchedPrice(provider, modelId)?.cost4;
|
|
63
67
|
return cost ? cost.input + cost.output : null;
|
|
@@ -65,11 +69,18 @@ async function modelCandidates(slugs: readonly string[], models: readonly Catalo
|
|
|
65
69
|
return slugs.map(slug => {
|
|
66
70
|
const row = routed.get(slug);
|
|
67
71
|
if (row) {
|
|
72
|
+
const ladder = written.get(slug);
|
|
73
|
+
const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : ladder?.efforts ?? [];
|
|
74
|
+
// A row without its own default keeps the one Codex shows, when that rung is on the ladder.
|
|
75
|
+
const shownDefault = ladder?.defaultEffort !== undefined && efforts.includes(ladder.defaultEffort)
|
|
76
|
+
? ladder.defaultEffort
|
|
77
|
+
: undefined;
|
|
78
|
+
const defaultEffort = row.defaultReasoningEffort ?? shownDefault;
|
|
68
79
|
return {
|
|
69
80
|
model: slug,
|
|
70
81
|
unitPrice: unitPrice(row.provider, row.id),
|
|
71
|
-
efforts
|
|
72
|
-
...(
|
|
82
|
+
efforts,
|
|
83
|
+
...(defaultEffort ? { defaultEffort } : {}),
|
|
73
84
|
};
|
|
74
85
|
}
|
|
75
86
|
if (nativeSlugs.includes(slug)) {
|
|
@@ -4,9 +4,12 @@ import { getMainAccountExternalUsageWarning } from "../../codex/main-account-ext
|
|
|
4
4
|
import { getObservedMainQuotaIdentityKey } from "../../codex/main-account-cache";
|
|
5
5
|
import { compactionRoutingSchema, memoryModelsSchema } from "../../config/schema/leaf-validators";
|
|
6
6
|
import { compactionRecoverySchema } from "../../config/schema/compaction-recovery";
|
|
7
|
+
import { anthropicSidecarPatchError } from "../../config/schema/anthropic-account-pool";
|
|
8
|
+
import { isAnthropicInstanceId } from "../../providers/anthropic-instance-id";
|
|
7
9
|
import { captureConfigTopLevelRollback } from "../../config/rebase-provenance";
|
|
8
10
|
import type { IntegrationClientId } from "../../integrations/registry";
|
|
9
11
|
import { randomUUID } from "node:crypto";
|
|
12
|
+
import { isDeepStrictEqual } from "node:util";
|
|
10
13
|
import { readFileSync } from "node:fs";
|
|
11
14
|
import type { CatalogModel } from "../../codex/catalog";
|
|
12
15
|
import { catalogModelSlug, invalidateCodexModelsCache, nativeContextLimits, nativeModelRows, uniqueCatalogModelsForPublicList } from "../../codex/catalog";
|
|
@@ -85,7 +88,6 @@ import { getProviderRegistryEntry } from "../../providers/registry";
|
|
|
85
88
|
import { VISION_REASONING_EFFORTS, isVisionReasoningEffort } from "../../reasoning-effort";
|
|
86
89
|
import { normalizeVisionReasoningForModel } from "../../vision/reasoning";
|
|
87
90
|
import {
|
|
88
|
-
findAnthropicVisionProvider,
|
|
89
91
|
isValidVisionTimeoutMs,
|
|
90
92
|
MAX_VISION_TIMEOUT_MS,
|
|
91
93
|
MIN_VISION_TIMEOUT_MS,
|
|
@@ -105,6 +107,7 @@ import {
|
|
|
105
107
|
webSearchModelIsRejected,
|
|
106
108
|
webSearchModelOptionsFrom,
|
|
107
109
|
webSearchModelRejection,
|
|
110
|
+
sidecarAnthropicPoolOptions, sidecarOptionsAuth, sidecarSettingsAfterPatch,
|
|
108
111
|
} from "./web-search-sidecar-options";
|
|
109
112
|
import { validateXaiSearchOptions } from "../../web-search/xai-executor";
|
|
110
113
|
import { getDebugLogEntries } from "../../lib/debug-log-buffer";
|
|
@@ -150,7 +153,9 @@ async function sidecarVisionResponseSettings(config: OcxConfig): Promise<{
|
|
|
150
153
|
const vs = config.visionSidecar ?? {};
|
|
151
154
|
// Match the runtime's one selected Anthropic executor for both backend fallback
|
|
152
155
|
// and catalog reachability; resolving it once prevents the two projections drifting.
|
|
153
|
-
const
|
|
156
|
+
const auth = sidecarOptionsAuth(config, vs.anthropicInstance);
|
|
157
|
+
const anthropicSidecar = auth.isAnthropicAuth && auth.anthropicProviderName && auth.anthropicProvider
|
|
158
|
+
? { providerName: auth.anthropicProviderName, provider: auth.anthropicProvider, config } : undefined;
|
|
154
159
|
// The routed backend reports its own namespaced model verbatim: it is the
|
|
155
160
|
// dispatched value, and collapsing it through the legacy resolver would
|
|
156
161
|
// display a describer the runtime is not using (roadmap 190).
|
|
@@ -178,6 +183,17 @@ interface ClientIntegrationSyncOutcome {
|
|
|
178
183
|
readonly profileId?: number;
|
|
179
184
|
}
|
|
180
185
|
|
|
186
|
+
/** Only the Desktop write's own applied markers may change during a background projection. */
|
|
187
|
+
function clientProjectionConfig(config: OcxConfig): OcxConfig {
|
|
188
|
+
const snapshot = structuredClone(config);
|
|
189
|
+
const profile = snapshot.claudeCode?.desktopProfile;
|
|
190
|
+
if (profile) {
|
|
191
|
+
delete profile.appliedFingerprint;
|
|
192
|
+
delete profile.appliedAt;
|
|
193
|
+
}
|
|
194
|
+
return snapshot;
|
|
195
|
+
}
|
|
196
|
+
|
|
181
197
|
/**
|
|
182
198
|
* Re-inject native clients that are switched ON and every file integration whose
|
|
183
199
|
* OpenCodex ownership record is the operator's durable opt-in.
|
|
@@ -201,17 +217,41 @@ export async function syncEnabledClientIntegrations(
|
|
|
201
217
|
config: OcxConfig,
|
|
202
218
|
deps: Pick<ManagementContext["deps"],
|
|
203
219
|
"fetchAllModels" | "refreshOwnedCatalogIntegrations" | "writeDesktop3pConfig"> = {},
|
|
220
|
+
options: { unattended?: { isCurrent: () => boolean } } = {},
|
|
204
221
|
): Promise<ClientIntegrationSyncOutcome[]> {
|
|
205
222
|
// A sibling instance passes its OWN port here; the Grok fence and the Desktop gateway profile
|
|
206
223
|
// stay on the live owner's (`src/codex/sibling-start.ts`).
|
|
207
224
|
if (port === undefined || siblingOfLivePort() !== null) return [];
|
|
208
|
-
const
|
|
225
|
+
const projectionConfig = options.unattended ? clientProjectionConfig(config) : undefined;
|
|
226
|
+
const { claudeDesktopIntegrationEnabled, grokIntegrationEnabled, localClientSyncAllowed } = await import("../../codex/desired-state");
|
|
209
227
|
const out: ClientIntegrationSyncOutcome[] = [];
|
|
228
|
+
const unattended = options.unattended;
|
|
229
|
+
const admit = () => {
|
|
230
|
+
if (!unattended!.isCurrent()) return false;
|
|
231
|
+
const fresh = loadConfig();
|
|
232
|
+
return localClientSyncAllowed(fresh) && isDeepStrictEqual(clientProjectionConfig(fresh), projectionConfig);
|
|
233
|
+
};
|
|
234
|
+
const stale = () => unattended !== undefined && !admit();
|
|
235
|
+
if (stale()) return out;
|
|
236
|
+
const grok = unattended ? await import("../../grok/inject") : undefined;
|
|
210
237
|
|
|
211
|
-
if (grokIntegrationEnabled(config)) {
|
|
238
|
+
if (grokIntegrationEnabled(config) && (!grok || grok.grokManagedBlockPresent())) {
|
|
212
239
|
try {
|
|
213
240
|
const { syncGrokConfig } = await import("../../grok/sync");
|
|
214
|
-
|
|
241
|
+
if (stale()) return out;
|
|
242
|
+
const grokDeps = grok ? {
|
|
243
|
+
fetchAllModels: deps.fetchAllModels ?? (await import("../management-api")).fetchAllModels,
|
|
244
|
+
injectGrokConfig: ((...args: Parameters<typeof grok.injectGrokConfig>) =>
|
|
245
|
+
admit() && grokIntegrationEnabled(loadConfig()) && grok.grokManagedBlockPresent()
|
|
246
|
+
? grok.injectGrokConfig(...args)
|
|
247
|
+
: { ok: true, changed: false, message: "Grok refresh skipped" }),
|
|
248
|
+
} : undefined;
|
|
249
|
+
const r = unattended
|
|
250
|
+
? await syncGrokConfig(port, config, {
|
|
251
|
+
...config.hostname ? { hostname: config.hostname } : {},
|
|
252
|
+
refreshOnly: { admit },
|
|
253
|
+
}, grokDeps)
|
|
254
|
+
: await syncGrokConfig(port, config, config.hostname ? { hostname: config.hostname } : {}, grokDeps);
|
|
215
255
|
out.push(r.ok
|
|
216
256
|
? { client: "grok", ok: true, changed: r.changed === true }
|
|
217
257
|
: { client: "grok", ok: false, reason: r.message });
|
|
@@ -220,9 +260,11 @@ export async function syncEnabledClientIntegrations(
|
|
|
220
260
|
}
|
|
221
261
|
}
|
|
222
262
|
|
|
263
|
+
if (stale()) return out;
|
|
223
264
|
const { observeClaudeDesktopMode, resolveClaudeDesktopMode } = await import("../../claude/desktop-first-party");
|
|
224
265
|
// A first-party Desktop must never get a gateway profile written and selected by a sync.
|
|
225
|
-
if (claudeDesktopIntegrationEnabled(config) && resolveClaudeDesktopMode(config, observeClaudeDesktopMode(config)) !== "first-party"
|
|
266
|
+
if (claudeDesktopIntegrationEnabled(config) && resolveClaudeDesktopMode(config, observeClaudeDesktopMode(config)) !== "first-party"
|
|
267
|
+
&& (!unattended || !!config.claudeCode?.desktopProfile?.appliedFingerprint)) {
|
|
226
268
|
try {
|
|
227
269
|
const { writeDesktop3pConfig } = await import("../../claude/desktop-3p");
|
|
228
270
|
const { desktopVisibleNativeSlugs, filterCatalogVisibleModels } = await import("../../codex/catalog");
|
|
@@ -236,12 +278,15 @@ export async function syncEnabledClientIntegrations(
|
|
|
236
278
|
const latest = loadConfig();
|
|
237
279
|
// Discovery awaited: the mode may have changed meanwhile. Re-resolve on the fresh read,
|
|
238
280
|
// immediately before the writer, so a first-party switch during fetchAllModels still wins.
|
|
239
|
-
if (
|
|
281
|
+
if (stale()) return;
|
|
282
|
+
if (claudeDesktopIntegrationEnabled(latest) && resolveClaudeDesktopMode(latest, observeClaudeDesktopMode(latest)) !== "first-party"
|
|
283
|
+
&& (!unattended || !!latest.claudeCode?.desktopProfile?.appliedFingerprint)) {
|
|
240
284
|
const routed = filterCatalogVisibleModels(models, latest)
|
|
241
285
|
.map(model => ({ provider: model.provider, id: model.id, contextWindow: model.contextWindow }));
|
|
242
286
|
const writtenProfile = latest.claudeCode?.desktopProfile;
|
|
243
287
|
const markerBaseline = captureDesktopAppliedMarker(writtenProfile);
|
|
244
|
-
|
|
288
|
+
// Keep the attended writer's argument count unchanged, including its lock test seam.
|
|
289
|
+
const writeArgs: Parameters<typeof writeDesktop3pConfig> = [
|
|
245
290
|
port,
|
|
246
291
|
[...desktopVisibleNativeSlugs(latest)],
|
|
247
292
|
routed,
|
|
@@ -249,7 +294,13 @@ export async function syncEnabledClientIntegrations(
|
|
|
249
294
|
"static",
|
|
250
295
|
writtenProfile,
|
|
251
296
|
nativeContextLimits(latest),
|
|
252
|
-
|
|
297
|
+
];
|
|
298
|
+
if (unattended) {
|
|
299
|
+
writeArgs[7] = undefined;
|
|
300
|
+
writeArgs[8] = { appliedFingerprint: writtenProfile!.appliedFingerprint!, admit };
|
|
301
|
+
}
|
|
302
|
+
const r = (deps.writeDesktop3pConfig ?? writeDesktop3pConfig)(...writeArgs);
|
|
303
|
+
if (unattended && r.reason === "desktop_refresh_only_skipped") return;
|
|
253
304
|
if (!r.written || !r.fingerprint) {
|
|
254
305
|
out.push({ client: "claude-desktop", ok: false, reason: r.reason ?? "Claude Desktop write failed" });
|
|
255
306
|
} else {
|
|
@@ -267,6 +318,7 @@ export async function syncEnabledClientIntegrations(
|
|
|
267
318
|
}
|
|
268
319
|
}
|
|
269
320
|
|
|
321
|
+
if (stale()) return out;
|
|
270
322
|
const { refreshOwnedCatalogIntegrations } = await import("../../integrations/catalog-refresh");
|
|
271
323
|
const refreshOwned = deps.refreshOwnedCatalogIntegrations ?? refreshOwnedCatalogIntegrations;
|
|
272
324
|
out.push(...await refreshOwned({
|
|
@@ -276,7 +328,10 @@ export async function syncEnabledClientIntegrations(
|
|
|
276
328
|
},
|
|
277
329
|
config,
|
|
278
330
|
port,
|
|
279
|
-
},
|
|
331
|
+
}, unattended
|
|
332
|
+
? ["mcode", "pi", "aside", "raycast", "omo", "commandcode", "droid", "opencode", "kilo"]
|
|
333
|
+
: ["mcode", "pi", "aside", "raycast", "omo", "cline", "commandcode", "droid", "opencode", "kilo"],
|
|
334
|
+
...unattended ? [{ refreshOnly: true, admit }] as const : []));
|
|
280
335
|
|
|
281
336
|
return out;
|
|
282
337
|
}
|
|
@@ -290,6 +345,8 @@ function publicVisionSidecarSettings(
|
|
|
290
345
|
enabled: vs.enabled !== false,
|
|
291
346
|
model: vision.model,
|
|
292
347
|
backend: vs.backend,
|
|
348
|
+
...(vs.anthropicInstance ? { anthropicInstance: vs.anthropicInstance } : {}),
|
|
349
|
+
anthropicPool: sidecarAnthropicPoolOptions(config, { ...vs, backend: vs.backend ?? (vision.models.find(row => row.value === vision.model)?.backend) }),
|
|
293
350
|
reasoning: vision.reasoning,
|
|
294
351
|
maxDescriptionsPerTurn: resolveMaxDescriptionsPerTurn(vs.maxDescriptionsPerTurn),
|
|
295
352
|
timeoutMs: resolveVisionTimeoutMs(vs.timeoutMs),
|
|
@@ -882,6 +939,8 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
882
939
|
enabled: ws.enabled !== false,
|
|
883
940
|
model: ws.model ?? "gpt-5.6-luna",
|
|
884
941
|
backend: ws.backend,
|
|
942
|
+
...(ws.anthropicInstance ? { anthropicInstance: ws.anthropicInstance } : {}),
|
|
943
|
+
anthropicPool: sidecarAnthropicPoolOptions(config, ws),
|
|
885
944
|
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
886
945
|
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
887
946
|
},
|
|
@@ -902,10 +961,11 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
902
961
|
if (raw.webSearch !== undefined && !isPlainRecord(raw.webSearch)) return jsonResponse({ error: "webSearch must be an object" }, 400);
|
|
903
962
|
if (raw.vision !== undefined && !isPlainRecord(raw.vision)) return jsonResponse({ error: "vision must be an object" }, 400);
|
|
904
963
|
const body = raw as {
|
|
905
|
-
webSearch?: { enabled?: unknown; model?: unknown; backend?: unknown; reasoning?: unknown; streamRoutedModelOutput?: unknown; exaApiKey?: unknown; xSearch?: unknown };
|
|
964
|
+
webSearch?: { enabled?: unknown; model?: unknown; backend?: unknown; anthropicInstance?: unknown; reasoning?: unknown; streamRoutedModelOutput?: unknown; exaApiKey?: unknown; xSearch?: unknown };
|
|
906
965
|
vision?: {
|
|
907
966
|
model?: unknown;
|
|
908
967
|
backend?: unknown;
|
|
968
|
+
anthropicInstance?: unknown;
|
|
909
969
|
reasoning?: unknown;
|
|
910
970
|
maxDescriptionsPerTurn?: unknown;
|
|
911
971
|
enabled?: unknown;
|
|
@@ -913,6 +973,11 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
913
973
|
};
|
|
914
974
|
};
|
|
915
975
|
const WEB_SEARCH_BACKENDS_UNION = ["openai", "anthropic", "xai", "gemini", "exa"] as const;
|
|
976
|
+
const instanceError = anthropicSidecarPatchError(config, {
|
|
977
|
+
...(body.webSearch ? { webSearchSidecar: body.webSearch } : {}),
|
|
978
|
+
...(body.vision ? { visionSidecar: body.vision } : {}),
|
|
979
|
+
});
|
|
980
|
+
if (instanceError) return jsonResponse({ error: instanceError }, 400);
|
|
916
981
|
if (body.webSearch && body.webSearch.backend !== undefined && body.webSearch.backend !== null
|
|
917
982
|
&& !WEB_SEARCH_BACKENDS_UNION.includes(body.webSearch.backend as never)) {
|
|
918
983
|
return jsonResponse({ error: "webSearch.backend must be openai, anthropic, xai, gemini, exa, or null" }, 400);
|
|
@@ -956,7 +1021,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
956
1021
|
// rejection body, so a 400 cannot cost two provider fetches.
|
|
957
1022
|
if (body.vision && typeof body.vision.model === "string" && body.vision.model !== "") {
|
|
958
1023
|
const requested = body.vision.model;
|
|
959
|
-
const candidates = await visionCandidateRows(config);
|
|
1024
|
+
const candidates = await visionCandidateRows({ ...config, visionSidecar: sidecarSettingsAfterPatch(config.visionSidecar, body.vision) });
|
|
960
1025
|
const hint = body.vision.backend === "anthropic" || body.vision.backend === "openai"
|
|
961
1026
|
|| body.vision.backend === "routed"
|
|
962
1027
|
? body.vision.backend
|
|
@@ -994,7 +1059,7 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
994
1059
|
// only case that owes a config.toml rewrite.
|
|
995
1060
|
const webSearchEnabledBefore = config.webSearchSidecar?.enabled !== false;
|
|
996
1061
|
if (body.webSearch) {
|
|
997
|
-
const pairTouched = body.webSearch.model !== undefined || body.webSearch.backend !== undefined;
|
|
1062
|
+
const pairTouched = body.webSearch.model !== undefined || body.webSearch.backend !== undefined || body.webSearch.anthropicInstance !== undefined;
|
|
998
1063
|
// Validate against the backend the caller SUBMITTED, across the whole
|
|
999
1064
|
// union — not just openai/anthropic (#2457). The union check above has
|
|
1000
1065
|
// already refused unknown literals, so a surviving string is a member;
|
|
@@ -1013,12 +1078,14 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
1013
1078
|
? body.webSearch.model || undefined
|
|
1014
1079
|
: config.webSearchSidecar?.model;
|
|
1015
1080
|
if (pairTouched && effectiveModel) {
|
|
1016
|
-
const candidates = await webSearchCandidateRows(config);
|
|
1081
|
+
const candidates = await webSearchCandidateRows({ ...config, webSearchSidecar: sidecarSettingsAfterPatch(config.webSearchSidecar, body.webSearch) });
|
|
1017
1082
|
if (webSearchModelIsRejected(effectiveBackend, effectiveModel, candidates)) {
|
|
1018
1083
|
return jsonResponse(webSearchModelRejection("webSearch.model", effectiveBackend, effectiveModel, candidates), 400);
|
|
1019
1084
|
}
|
|
1020
1085
|
}
|
|
1021
1086
|
const webSearchCandidate = { ...config.webSearchSidecar };
|
|
1087
|
+
if (body.webSearch.anthropicInstance === null) delete webSearchCandidate.anthropicInstance;
|
|
1088
|
+
else if (isAnthropicInstanceId(body.webSearch.anthropicInstance)) webSearchCandidate.anthropicInstance = body.webSearch.anthropicInstance;
|
|
1022
1089
|
if (typeof body.webSearch.model === "string") {
|
|
1023
1090
|
if (body.webSearch.model === "") delete webSearchCandidate.model;
|
|
1024
1091
|
else webSearchCandidate.model = body.webSearch.model;
|
|
@@ -1098,6 +1165,8 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
1098
1165
|
}
|
|
1099
1166
|
if (body.vision) {
|
|
1100
1167
|
config.visionSidecar = { ...config.visionSidecar };
|
|
1168
|
+
if (body.vision.anthropicInstance === null) delete config.visionSidecar.anthropicInstance;
|
|
1169
|
+
else if (isAnthropicInstanceId(body.vision.anthropicInstance)) config.visionSidecar.anthropicInstance = body.vision.anthropicInstance;
|
|
1101
1170
|
if (typeof body.vision.model === "string") {
|
|
1102
1171
|
if (body.vision.model === "") delete config.visionSidecar.model;
|
|
1103
1172
|
else config.visionSidecar.model = body.vision.model;
|
|
@@ -1143,6 +1212,8 @@ export async function handleConfigRoutes(ctx: ManagementContext): Promise<Respon
|
|
|
1143
1212
|
enabled: ws.enabled !== false,
|
|
1144
1213
|
model: ws.model ?? "gpt-5.6-luna",
|
|
1145
1214
|
backend: ws.backend,
|
|
1215
|
+
...(ws.anthropicInstance ? { anthropicInstance: ws.anthropicInstance } : {}),
|
|
1216
|
+
anthropicPool: sidecarAnthropicPoolOptions(config, ws),
|
|
1146
1217
|
streamRoutedModelOutput: ws.streamRoutedModelOutput === true,
|
|
1147
1218
|
...(ws.xSearch ? { xSearch: ws.xSearch } : {}),
|
|
1148
1219
|
},
|
|
@@ -64,8 +64,8 @@ export interface ManagementApiDeps {
|
|
|
64
64
|
refreshOwnedCatalogIntegrations?: typeof refreshOwnedCatalogIntegrations;
|
|
65
65
|
/** Platform seam for capability projections; does not alter host-level startup behavior. */
|
|
66
66
|
platform?: NodeJS.Platform;
|
|
67
|
-
toggleCodexMultiAgentV2?: (enabled: boolean) => void;
|
|
68
|
-
toggleDefaultModeRequestUserInput?: (enabled: boolean) => void;
|
|
67
|
+
toggleCodexMultiAgentV2?: (enabled: boolean, env: NodeJS.ProcessEnv, validateBeforeSpawn: () => void) => void;
|
|
68
|
+
toggleDefaultModeRequestUserInput?: (enabled: boolean, env: NodeJS.ProcessEnv, validateBeforeSpawn: () => void) => void;
|
|
69
69
|
createManagementConvergeCodex?: (config: Readonly<OcxConfig>) => ConvergeCodex;
|
|
70
70
|
/** Test-only destination for best-effort Claude agent-definition sync. */
|
|
71
71
|
claudeAgentConfigDir?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsonResponse } from "../auth-cors";
|
|
2
|
+
import { jevDecisionEndpointUrl } from "../../combos/jev-decision-contract";
|
|
2
3
|
import type { ManagementContext } from "./context";
|
|
3
4
|
import { readManagementJsonBody, rethrowManagementBodyTooLarge } from "./body";
|
|
4
5
|
import { isPlainRecord } from "./shared";
|
|
@@ -29,7 +30,7 @@ function decisionServiceIssue(
|
|
|
29
30
|
provider: DecisionRow,
|
|
30
31
|
isSystemOneEndpoint: (url: string) => boolean,
|
|
31
32
|
): { url: string; model: string; issue?: "disabled" | "endpoint" | "model" } {
|
|
32
|
-
const url = typeof provider.baseUrl === "string" ? provider.baseUrl
|
|
33
|
+
const url = typeof provider.baseUrl === "string" ? jevDecisionEndpointUrl(provider.baseUrl) : "";
|
|
33
34
|
const model = provider.defaultModel?.trim() || provider.models?.[0]?.trim() || "";
|
|
34
35
|
const issue = provider.disabled === true
|
|
35
36
|
? "disabled"
|