@bitkyc08/opencodex 2.83.0 → 2.86.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 +0 -4
- package/bin/ocx.mjs +61 -3
- package/gui/dist/assets/App-BjlttVLy.js +58 -0
- package/gui/dist/assets/Tray-DyR9yBee.js +1 -0
- package/gui/dist/assets/{index-BAbcj51J.css → index-BB6uK6tU.css} +1 -1
- package/gui/dist/assets/index-BMssP4k3.js +94 -0
- package/gui/dist/assets/quota-summary-D8M1bc_R.js +1 -0
- package/gui/dist/index.html +2 -2
- package/package.json +2 -3
- package/src/adapters/anthropic/beta-allowlist.ts +87 -8
- package/src/adapters/anthropic/client-identity.ts +1 -1
- package/src/adapters/anthropic/passthrough.ts +33 -4
- package/src/adapters/azure.ts +19 -1
- package/src/adapters/client-fingerprint.ts +24 -0
- package/src/adapters/coding-agent/protocol.ts +15 -8
- package/src/adapters/coding-agent/turn.ts +22 -9
- package/src/adapters/cursor/arg-codec.ts +17 -5
- package/src/adapters/cursor/catalog.ts +8 -0
- package/src/adapters/cursor/effort-map.ts +3 -0
- package/src/adapters/cursor/freeform-args.ts +75 -0
- package/src/adapters/cursor/protobuf-events.ts +36 -23
- package/src/adapters/google-antigravity-wire.ts +22 -1
- package/src/adapters/google.ts +2 -2
- package/src/adapters/ollama-native.ts +29 -1
- package/src/adapters/openai-chat/messages.ts +8 -4
- package/src/adapters/openai-chat/response-events.ts +59 -1
- package/src/adapters/openai-chat.ts +8 -5
- package/src/adapters/openai-responses/empty-assistant.ts +32 -0
- package/src/adapters/openai-responses/passthrough.ts +3 -0
- package/src/adapters/openai-responses/request-strips.ts +10 -2
- package/src/adapters/qoder/adapter.ts +56 -15
- package/src/advisor/consult.ts +220 -0
- package/src/advisor/context.ts +226 -0
- package/src/advisor/disclosure.ts +20 -0
- package/src/advisor/runtime.ts +305 -0
- package/src/advisor/settings.ts +135 -0
- package/src/advisor/state.ts +369 -0
- package/src/advisor/synthetic-tool.ts +33 -0
- package/src/bridge/errors.ts +24 -0
- package/src/bridge.ts +1 -1
- package/src/chat/inbound.ts +30 -6
- package/src/chat/outbound.ts +5 -0
- package/src/claude/context-windows.ts +54 -1
- package/src/claude/desktop-3p-library.ts +43 -3
- package/src/claude/desktop-3p.ts +22 -8
- package/src/claude/desktop-picker-profile.ts +11 -4
- package/src/claude/desktop-profile.ts +10 -1
- package/src/claude/desktop-remote-store-io.ts +5 -3
- package/src/claude/inbound.ts +121 -50
- package/src/claude/intercept/connect-proxy.ts +10 -3
- package/src/claude/intercept/local-ca.ts +8 -1
- package/src/claude/intercept/picker-ca-persistence.ts +3 -1
- package/src/claude/intercept/picker-listener.ts +49 -1
- package/src/claude/intercept/settings-no-proxy.ts +146 -0
- package/src/claude/intercept/settings.ts +101 -9
- package/src/claude/model-default-settings.ts +154 -0
- package/src/claude/model-info.ts +7 -7
- package/src/claude/outbound.ts +13 -6
- package/src/cli/account-api.ts +65 -0
- package/src/cli/account-auth.ts +26 -2
- package/src/cli/account.ts +41 -5
- package/src/cli/advisor.ts +151 -0
- package/src/cli/auxiliary-listener-preflight.ts +37 -0
- package/src/cli/capabilities-access-remote.ts +8 -0
- package/src/cli/capabilities-agents-routing.ts +18 -0
- package/src/cli/capabilities-base.ts +14 -2
- package/src/cli/catalog-command-result.ts +2 -1
- package/src/cli/chatgpt-command.ts +35 -14
- package/src/cli/claude.ts +60 -11
- package/src/cli/config-command.ts +8 -2
- package/src/cli/debug.ts +8 -0
- package/src/cli/dispatch.ts +21 -4
- package/src/cli/doctor.ts +9 -0
- package/src/cli/ensure-desktop-cold-start.ts +329 -0
- package/src/cli/export-command.ts +15 -2
- package/src/cli/help.ts +2 -1
- package/src/cli/hub-disable.ts +99 -0
- package/src/cli/hub.ts +4 -2
- package/src/cli/index.ts +26 -8
- package/src/cli/integration-plan-dto.ts +2 -0
- package/src/cli/link-child.ts +104 -0
- package/src/cli/link-enrollment.ts +96 -0
- package/src/cli/link.ts +39 -24
- package/src/cli/observe-stream.ts +6 -1
- package/src/cli/observe.ts +10 -2
- package/src/cli/provider.ts +12 -0
- package/src/cli/registry.ts +20 -7
- package/src/cli/restart-scope.ts +3 -1
- package/src/cli/runtime-api.ts +27 -2
- package/src/cli/status.ts +104 -20
- package/src/client/connect.ts +39 -4
- package/src/client/hub-client.ts +14 -7
- package/src/client/hub-state.ts +25 -14
- package/src/client/link-home.ts +248 -0
- package/src/client/link-management-relay.ts +61 -0
- package/src/client/link-relay.ts +6 -1
- package/src/client/link-status.ts +14 -0
- package/src/client/machine-api.ts +14 -7
- package/src/client/machine-listener.ts +29 -4
- package/src/clients/config-export/contracts.ts +1 -0
- package/src/clients/config-export/model-metadata.ts +3 -2
- package/src/clients/config-export/qoder.ts +93 -0
- package/src/clients/config-export.ts +59 -16
- package/src/clients/omo-role-models.ts +6 -3
- package/src/codex/agent-role-models.ts +26 -8
- package/src/codex/auth-api/account-list.ts +17 -2
- package/src/codex/auth-api/account-selection.ts +17 -7
- package/src/codex/auth-api/pool-mode-gate.ts +5 -2
- package/src/codex/auth-api/routes.ts +5 -1
- package/src/codex/auth-context.ts +79 -5
- package/src/codex/autostart-health.ts +7 -1
- package/src/codex/catalog/build-entries.ts +11 -2
- package/src/codex/catalog/bundled.ts +5 -1
- package/src/codex/catalog/configured-tiers.ts +50 -0
- package/src/codex/catalog/effort.ts +6 -3
- package/src/codex/catalog/metadata.ts +4 -2
- package/src/codex/catalog/model-hints.ts +21 -3
- package/src/codex/catalog/native-models.ts +16 -2
- package/src/codex/catalog/native-ultrafast.ts +106 -0
- package/src/codex/catalog/parsing.ts +5 -0
- package/src/codex/catalog/provider-models.ts +4 -0
- package/src/codex/catalog/retained-sync.ts +2 -0
- package/src/codex/catalog/routed-gather.ts +22 -4
- package/src/codex/catalog/vendor-context.ts +82 -0
- package/src/codex/config-write-lock.ts +40 -2
- package/src/codex/convergence.ts +2 -0
- package/src/codex/desktop-app/types.ts +2 -0
- package/src/codex/desktop-app/windows.ts +29 -16
- package/src/codex/desktop-app-restart.ts +4 -2
- package/src/codex/desktop-compatibility/certificate-service.ts +99 -0
- package/src/codex/desktop-compatibility/certificate-store.ts +193 -0
- package/src/codex/desktop-compatibility/connection-store.ts +87 -0
- package/src/codex/desktop-compatibility/installed-build.ts +55 -0
- package/src/codex/desktop-compatibility/json-body.ts +29 -0
- package/src/codex/desktop-compatibility/native-identity.ts +62 -0
- package/src/codex/desktop-compatibility/relay-listener.ts +93 -0
- package/src/codex/desktop-compatibility/routing-binding.ts +10 -0
- package/src/codex/desktop-compatibility/routing-preflight.ts +69 -0
- package/src/codex/desktop-compatibility/runtime-ownership.ts +28 -0
- package/src/codex/desktop-compatibility/runtime.ts +173 -0
- package/src/codex/desktop-compatibility/service.ts +30 -0
- package/src/codex/desktop-compatibility/startup-settings.ts +50 -0
- package/src/codex/desktop-compatibility/usage-activation.ts +86 -0
- package/src/codex/desktop-compatibility/usage-controlled-fetch.ts +59 -0
- package/src/codex/desktop-compatibility/usage-controller.ts +107 -0
- package/src/codex/desktop-compatibility/usage-policy.ts +59 -0
- package/src/codex/desktop-compatibility/usage-refresh.ts +33 -0
- package/src/codex/desktop-compatibility/usage-sse-controller.ts +86 -0
- package/src/codex/desktop-compatibility/windows-activation-source.ts +44 -0
- package/src/codex/desktop-compatibility/windows-certificate-trust.ts +81 -0
- package/src/codex/desktop-compatibility/windows-key-protection.ts +59 -0
- package/src/codex/desktop-compatibility/windows-package-command.ts +86 -0
- package/src/codex/desktop-compatibility/windows-package-launch.ts +28 -0
- package/src/codex/history-provider.ts +1 -1
- package/src/codex/inject/config-write-section.ts +4 -1
- package/src/codex/inject/remove.ts +18 -6
- package/src/codex/journal.ts +16 -1
- package/src/codex/main-account.ts +99 -15
- package/src/codex/native-main-refresh-mode.ts +217 -0
- package/src/codex/native-profile-startup.ts +4 -1
- package/src/codex/prompt-journal.ts +20 -2
- package/src/codex/prompt-layers/encoding.ts +49 -0
- package/src/codex/prompt-layers/import-source.ts +30 -0
- package/src/codex/prompt-layers/toml-edit.ts +269 -37
- package/src/codex/prompt-layers/toml-read.ts +52 -4
- package/src/codex/prompt-layers.ts +383 -20
- package/src/codex/prompt-lock-claim.ts +13 -3
- package/src/codex/prompt-lock-evidence.ts +3 -3
- package/src/codex/prompt-lock-owner.ts +14 -6
- package/src/codex/prompt-text-probe.ts +34 -5
- package/src/codex/quota.ts +13 -6
- package/src/codex/role-auto-assign.ts +2 -5
- package/src/codex/routing/active-account.ts +21 -0
- package/src/codex/routing/health-store.ts +1 -1
- package/src/codex/routing/pool-recovery.ts +79 -0
- package/src/codex/routing.ts +1 -1
- package/src/codex/runtime.ts +196 -8
- package/src/codex/shim-diagnostics.ts +6 -0
- package/src/codex/shim-overlay.ts +15 -6
- package/src/codex/shim.ts +1 -1
- package/src/combos/failover.ts +3 -0
- package/src/config/api-key-reconcile.ts +34 -0
- package/src/config/diagnostics.ts +18 -1
- package/src/config/feature-flags.ts +5 -0
- package/src/config/live-reconcile.ts +56 -29
- package/src/config/live-topology.ts +29 -0
- package/src/config/load-degrade.ts +8 -2
- package/src/config/paths.ts +13 -3
- package/src/config/salvage.ts +3 -1
- package/src/config/schema/config-schema.ts +19 -0
- package/src/config/schema/desktop-compatibility.ts +9 -0
- package/src/config/schema/leaf-validators.ts +1 -0
- package/src/generated/compatibility-version.json +713 -285
- package/src/generated/model-metadata.ts +1 -1
- package/src/grok/grpc-web.ts +23 -1
- package/src/grok/reset-coupon-ledger.ts +75 -5
- package/src/grok/reset-coupons.ts +4 -2
- package/src/integrations/catalog-refresh.ts +1 -1
- package/src/integrations/config-io.ts +20 -2
- package/src/integrations/journal.ts +15 -1
- package/src/integrations/mutation-plan.ts +14 -7
- package/src/integrations/registry.ts +52 -11
- package/src/integrations/state.ts +19 -7
- package/src/integrations/store.ts +3 -0
- package/src/integrations/target.ts +42 -5
- package/src/integrations/writer.ts +24 -7
- package/src/lib/advisor-activation.ts +16 -0
- package/src/lib/app-owned-memory-stores.ts +4 -0
- package/src/lib/app-owned-memory.ts +11 -0
- package/src/lib/background-work.ts +65 -0
- package/src/lib/bounded-file-read.ts +58 -0
- package/src/lib/bun-path-runtime.d.mts +10 -1
- package/src/lib/bun-path-runtime.mjs +62 -3
- package/src/lib/desktop-proxy-route.ts +30 -0
- package/src/lib/desktop-upstream-tunnel.ts +107 -0
- package/src/lib/errors.ts +7 -0
- package/src/lib/json-byte-size.ts +31 -0
- package/src/lib/local-internal-call-capability.ts +55 -0
- package/src/lib/optional-shutdown-hooks.ts +35 -0
- package/src/lib/outbound-credential-registry.ts +210 -0
- package/src/lib/package-tree-integrity.ts +2 -2
- package/src/lib/passive-read.ts +35 -0
- package/src/lib/shadow-call.ts +44 -5
- package/src/lib/socks5-fetch.ts +48 -71
- package/src/lib/socks5-handshake.ts +112 -0
- package/src/lib/translator-budget.ts +7 -1
- package/src/link/codex-settings.ts +155 -0
- package/src/link/management-routes.ts +89 -0
- package/src/link/remote-probe.ts +38 -0
- package/src/link/ssh-argv.ts +26 -5
- package/src/link/ssh-runner.ts +58 -27
- package/src/link/status-projection.ts +22 -4
- package/src/link/supervisor.ts +144 -49
- package/src/link/tunnel-state.ts +11 -4
- package/src/oauth/anthropic-continuity.ts +4 -3
- package/src/oauth/anthropic-identity.ts +13 -2
- package/src/oauth/anthropic-model-quota.ts +13 -1
- package/src/oauth/anthropic-routing.ts +230 -23
- package/src/oauth/anthropic.ts +18 -4
- package/src/oauth/generic-account-failover.ts +29 -5
- package/src/oauth/google-antigravity.ts +24 -7
- package/src/oauth/index.ts +28 -17
- package/src/oauth/kiro-account-identity.ts +106 -0
- package/src/oauth/kiro-device-login.ts +2 -1
- package/src/oauth/kiro.ts +55 -6
- package/src/oauth/plan.ts +29 -0
- package/src/oauth/store.ts +40 -3
- package/src/oauth/types.ts +2 -0
- package/src/plugins/loader.ts +11 -0
- package/src/protocols/codecs/chat-ir.ts +467 -0
- package/src/protocols/codecs/chat.ts +10 -1
- package/src/protocols/codecs/messages-ir.ts +500 -0
- package/src/protocols/features.ts +10 -2
- package/src/protocols/path.ts +11 -1
- package/src/protocols/plan-snapshot.ts +39 -16
- package/src/protocols/plan.ts +9 -1
- package/src/protocols/settings.ts +8 -0
- package/src/providers/anthropic-billing-ownership.ts +74 -0
- package/src/providers/anthropic-billing-recovery.ts +31 -0
- package/src/providers/anthropic-billing-refusal.ts +55 -0
- package/src/providers/anthropic-reset-admission.ts +62 -0
- package/src/providers/anthropic-reset-grant-ledger.ts +28 -6
- package/src/providers/antigravity-account-roster.ts +102 -0
- package/src/providers/antigravity-models.ts +85 -7
- package/src/providers/api-key-resolve.ts +17 -2
- package/src/providers/key-failover.ts +52 -5
- package/src/providers/model-rename-fields.ts +2 -0
- package/src/providers/model-rename-migration.ts +5 -3
- package/src/providers/model-rename-startup.ts +5 -3
- package/src/providers/quota/account-cache.ts +11 -0
- package/src/providers/quota/anthropic-account-quota.ts +4 -2
- package/src/providers/quota/report-cache.ts +14 -0
- package/src/providers/quota/vendor-probes-oauth.ts +3 -3
- package/src/providers/quota.ts +4 -1
- package/src/providers/registry/entries-core.ts +21 -20
- package/src/providers/registry/entries-extended.ts +19 -1
- package/src/providers/registry/model-seeds.ts +17 -0
- package/src/providers/request-pacing.ts +9 -0
- package/src/providers/stale-no-vision-seed-migration.ts +72 -0
- package/src/responses/compaction.ts +14 -0
- package/src/responses/parser.ts +37 -20
- package/src/responses/state.ts +32 -29
- package/src/routing/history/build-file.ts +92 -0
- package/src/routing/history/indexer.ts +248 -216
- package/src/routing/history/ingest.ts +238 -0
- package/src/routing/history/rebuild-job.ts +111 -0
- package/src/routing/history/rebuild-protocol.ts +15 -0
- package/src/routing/history/rebuild-worker.ts +34 -0
- package/src/server/admission-model-scope.ts +4 -1
- package/src/server/antigravity-direct-eligibility.ts +208 -0
- package/src/server/auth-cors.ts +46 -0
- package/src/server/chat-completions.ts +75 -9
- package/src/server/claude-messages.ts +110 -20
- package/src/server/configured-credentials.ts +194 -0
- package/src/server/index/desktop-compatibility-startup.ts +56 -0
- package/src/server/index/link-management.ts +58 -0
- package/src/server/index/optional-listeners.ts +5 -1
- package/src/server/index/serve-options.ts +15 -2
- package/src/server/index/startup-warnings.ts +9 -0
- package/src/server/index/websocket-handler.ts +9 -0
- package/src/server/index.ts +13 -10
- package/src/server/inference/client-encoder-delivery.ts +6 -5
- package/src/server/lifecycle.ts +2 -1
- package/src/server/login-shell-path.ts +146 -0
- package/src/server/management/advisor-routes.ts +191 -0
- package/src/server/management/agent-settings-routes.ts +50 -14
- package/src/server/management/anthropic-reset-grant-routes.ts +17 -1
- package/src/server/management/codex-agent-role-routes.ts +19 -6
- package/src/server/management/codex-prompt-routes.ts +136 -5
- package/src/server/management/codex-role-auto-assign.ts +1 -1
- package/src/server/management/companion-routes.ts +4 -0
- package/src/server/management/config-routes.ts +2 -2
- package/src/server/management/context.ts +4 -0
- package/src/server/management/desktop-compatibility-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-runtime-routes.ts +46 -0
- package/src/server/management/desktop-compatibility-settings-routes.ts +27 -0
- package/src/server/management/grok-coupon-routes.ts +78 -27
- package/src/server/management/link-enrollment.ts +192 -0
- package/src/server/management/link-route-state.ts +185 -0
- package/src/server/management/link-routes.ts +82 -402
- package/src/server/management/model-routes.ts +2 -2
- package/src/server/management/model-rows.ts +3 -1
- package/src/server/management/protocol-settings-patch.ts +1 -0
- package/src/server/management/provider-overwrite-carry.ts +10 -0
- package/src/server/management/provider-routes.ts +1 -0
- package/src/server/management/route-registry.ts +16 -3
- package/src/server/management/shared.ts +3 -0
- package/src/server/management/sibling-guard.ts +1 -0
- package/src/server/management-api.ts +9 -1
- package/src/server/messages-antigravity-direct.ts +195 -0
- package/src/server/messages-native.ts +73 -43
- package/src/server/relay-eager.ts +2 -0
- package/src/server/request-log-filter.ts +6 -1
- package/src/server/request-log-terminal-status.ts +63 -6
- package/src/server/request-log.ts +7 -0
- package/src/server/responses/adapter-continuation.ts +35 -18
- package/src/server/responses/adapter-delivery.ts +24 -2
- package/src/server/responses/adapter-dispatch.ts +261 -81
- package/src/server/responses/advisor-plan-slot.ts +27 -0
- package/src/server/responses/advisor-slot.ts +336 -0
- package/src/server/responses/code-mode-collab-surface.ts +313 -0
- package/src/server/responses/codex-ws-exchange.ts +147 -6
- package/src/server/responses/codex-ws-wire.ts +203 -0
- package/src/server/responses/collaboration.ts +36 -7
- package/src/server/responses/compact.ts +126 -48
- package/src/server/responses/context-overflow.ts +13 -1
- package/src/server/responses/core-codex-account.ts +105 -1
- package/src/server/responses/core-combo-failure.ts +24 -1
- package/src/server/responses/core-combo.ts +28 -2
- package/src/server/responses/core-errors.ts +29 -1
- package/src/server/responses/core-lifetime.ts +2 -0
- package/src/server/responses/core-normalize.ts +11 -0
- package/src/server/responses/core-options.ts +48 -0
- package/src/server/responses/core-replay.ts +7 -4
- package/src/server/responses/core.ts +1 -1
- package/src/server/responses/deferred-collab-surface.ts +158 -0
- package/src/server/responses/deferred-send-reporter.ts +25 -0
- package/src/server/responses/fetch-helpers.ts +12 -1
- package/src/server/responses/passthrough-delivery.ts +20 -11
- package/src/server/responses/passthrough-dispatch.ts +29 -7
- package/src/server/responses/request-prepare.ts +74 -9
- package/src/server/responses/request-transport.ts +22 -8
- package/src/server/responses/run-turn-execution.ts +70 -8
- package/src/server/responses/sidecar-execution.ts +89 -5
- package/src/server/responses/terminal-guard.ts +2 -1
- package/src/server/responses/ws-upstream.ts +9 -1
- package/src/server/responses-undeclared-tool-guard.ts +30 -9
- package/src/server/startup-action-control.ts +38 -4
- package/src/server/startup-health-cache.ts +5 -0
- package/src/server/system-env-shell.ts +7 -2
- package/src/server/system-env.ts +11 -1
- package/src/service/cli.ts +5 -1
- package/src/service/desktop-startup.ts +161 -34
- package/src/service/desktop-supervision.d.mts +27 -1
- package/src/service/desktop-supervision.mjs +111 -5
- package/src/service/health-sentinel-launch.ts +176 -0
- package/src/service/health-sentinel-main.ts +466 -0
- package/src/service/health-sentinel-policy.ts +241 -0
- package/src/service/health-sentinel-probe.ts +71 -0
- package/src/service/health-sentinel-windows.ts +87 -0
- package/src/service/linux-linger.ts +20 -0
- package/src/service/logging.ts +72 -0
- package/src/tray/windows-tray.ps1 +43 -24
- package/src/types/config.ts +68 -6
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +15 -0
- package/src/types/tools.ts +2 -0
- package/src/update/async-check.ts +1 -1
- package/src/update/badge.ts +2 -2
- package/src/update/index.ts +88 -15
- package/src/update/job-supervised-restart.ts +107 -0
- package/src/update/job.ts +32 -32
- package/src/update/notify.ts +1 -1
- package/src/update/refresh-scheduler.ts +2 -2
- package/src/update/supervised-recovery.d.mts +27 -0
- package/src/update/supervised-recovery.mjs +157 -0
- package/src/usage/cache-diagnostic.ts +2 -0
- package/src/usage/expected-prices.ts +16 -0
- package/src/usage/log.ts +19 -1
- package/src/web-search/gemini-executor.ts +2 -2
- package/assets/sponsors/packycode.png +0 -0
- package/gui/dist/assets/App-Ba8uG696.js +0 -54
- package/gui/dist/assets/Tray-IImbNPRy.js +0 -1
- package/gui/dist/assets/index-BHRZdJuv.js +0 -94
- package/gui/dist/assets/quota-summary-DiJO6sYY.js +0 -1
|
@@ -47,7 +47,7 @@ const DATA: Record<string, readonly Row[]> = {
|
|
|
47
47
|
"azure-openai": [["gpt-4.1",1047576,32768,"text,image",0,null,2,8,0.5,0],["gpt-4o",128000,16384,"text,image",0,null,2.5,10,1.25,0],["gpt-4o-mini",128000,16384,"text,image",0,null,0.15,0.6,0.075,0],["o3",200000,100000,"text,image",1,null,2,8,0.5,0],["o3-mini",200000,100000,"text",1,null,1.1,4.4,0.55,0]],
|
|
48
48
|
"cerebras": [["gemma-4-31b",131072,40960,"text,image",1,null,0.99,1.49,0,0],["gpt-oss-120b",131072,40960,"text",1,null,0.35,0.75,0,0],["llama3.1-8b",32000,8000,"text",0,null,0.1,0.1,0,0],["qwen-3-235b-a22b-instruct-2507",131000,32000,"text",0,null,0.6,1.2,0,0],["qwen-3-coder-480b",131072,32768,"text",0,null,0,0,0,0],["zai-glm-4.6",131072,32768,"text",0,null,0,0,0,0],["zai-glm-4.7",131072,40960,"text",1,null,2.25,2.75,2.25,0]],
|
|
49
49
|
"deepseek": [["deepseek-v4-flash",1048576,384000,"text",1,null,0.14,0.28,0.0028,0],["deepseek-v4-pro",1048576,384000,"text",1,null,0.435,0.87,0.003625,0]],
|
|
50
|
-
"google": [["deep-research-max-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["deep-research-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["gemini-1.5-flash",1000000,8192,"text,image",0,null,0.075,0.3,0.01875,0],["gemini-1.5-flash-8b",1000000,8192,"text,image",0,null,0.0375,0.15,0.01,0],["gemini-1.5-pro",1000000,8192,"text,image",0,null,1.25,5,0.3125,0],["gemini-2.0-flash",1048576,8192,"text,image",0,null,0.1,0.4,0.025,0],["gemini-2.0-flash-lite",1048576,8192,"text,image",0,null,0.075,0.3,0,0],["gemini-2.5-computer-use-preview-10-2025",131072,65536,"text,image",1,null,1.25,10,0,0],["gemini-2.5-flash",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-2.5-flash-lite",1048576,65536,"text,image",1,null,0.1,0.4,0.01,0],["gemini-2.5-flash-lite-preview-06-17",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-lite-preview-09-2025",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-preview-04-17",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-05-20",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1,null,0.3,2.5,0.075,0],["gemini-2.5-pro",1048576,65536,"text,image",1,null,1.25,10,0.125,0],["gemini-2.5-pro-preview-05-06",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-2.5-pro-preview-06-05",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-3-flash-preview",1048576,65536,"text,image",1,null,0.5,3,0.05,0],["gemini-3-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-flash-lite",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-lite-image",65536,65536,"text,image",1,null,0.25,30,0,0],["gemini-3.1-flash-lite-preview",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-live-preview",131072,65536,"text,image",1,null,0.75,4.5,0,0],["gemini-3.1-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.5-flash",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-3.5-flash-lite",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-3.6-flash",1048576,65536,"text,image",1,null,1.5,7.5,0.15,0],["gemini-3.7-flash",1048576,65536,"text,image",1],["gemini-3.8-flash",1048576,65536,"text,image",1],["gemini-flash-latest",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-flash-lite-latest",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-live-2.5-flash",128000,8000,"text,image",1,null,0.5,2,0,0],["gemini-live-2.5-flash-preview-native-audio",131072,65536,"text",1,null,0.5,2,0,0],["gemini-robotics-er-1.6-preview",131072,65536,"text,image",1,null,1,5,0,0],["gemma-3-27b-it",131072,8192,"text,image",0,null,0,0,0,0],["gemma-4-26b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-26b-a4b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-26b-it",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-E2B-it",131072,8192,"text,image",1,null,0,0,0,0],["gemma-4-E4B-it",131072,8192,"text,image",1,null,0,0,0,0]],
|
|
50
|
+
"google": [["deep-research-max-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["deep-research-preview-04-2026",131072,65536,"text,image",1,null,2,12,0.2,0],["gemini-1.5-flash",1000000,8192,"text,image",0,null,0.075,0.3,0.01875,0],["gemini-1.5-flash-8b",1000000,8192,"text,image",0,null,0.0375,0.15,0.01,0],["gemini-1.5-pro",1000000,8192,"text,image",0,null,1.25,5,0.3125,0],["gemini-2.0-flash",1048576,8192,"text,image",0,null,0.1,0.4,0.025,0],["gemini-2.0-flash-lite",1048576,8192,"text,image",0,null,0.075,0.3,0,0],["gemini-2.5-computer-use-preview-10-2025",131072,65536,"text,image",1,null,1.25,10,0,0],["gemini-2.5-flash",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-2.5-flash-lite",1048576,65536,"text,image",1,null,0.1,0.4,0.01,0],["gemini-2.5-flash-lite-preview-06-17",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-lite-preview-09-2025",1048576,65536,"text,image",1,null,0.1,0.4,0.025,0],["gemini-2.5-flash-preview-04-17",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-05-20",1048576,65536,"text,image",1,null,0.15,0.6,0.0375,0],["gemini-2.5-flash-preview-09-2025",1048576,65536,"text,image",1,null,0.3,2.5,0.075,0],["gemini-2.5-pro",1048576,65536,"text,image",1,null,1.25,10,0.125,0],["gemini-2.5-pro-preview-05-06",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-2.5-pro-preview-06-05",1048576,65536,"text,image",1,null,1.25,10,0.31,0],["gemini-3-flash-preview",1048576,65536,"text,image",1,null,0.5,3,0.05,0],["gemini-3-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-flash-lite",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-lite-image",65536,65536,"text,image",1,null,0.25,30,0,0],["gemini-3.1-flash-lite-preview",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-3.1-flash-live-preview",131072,65536,"text,image",1,null,0.75,4.5,0,0],["gemini-3.1-pro-preview",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.1-pro-preview-customtools",1048576,65536,"text,image",1,null,2,12,0.2,0],["gemini-3.5-flash",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-3.5-flash-lite",1048576,65536,"text,image",1,null,0.3,2.5,0.03,0],["gemini-3.6-flash",1048576,65536,"text,image",1,null,1.5,7.5,0.15,0],["gemini-3.7-flash",1048576,65536,"text,image",1],["gemini-3.8-flash",1048576,65536,"text,image",1],["gemini-4-argon",1048576,65536,"text,image",1],["gemini-flash-latest",1048576,65536,"text,image",1,null,1.5,9,0.15,0],["gemini-flash-lite-latest",1048576,65536,"text,image",1,null,0.25,1.5,0.025,0],["gemini-live-2.5-flash",128000,8000,"text,image",1,null,0.5,2,0,0],["gemini-live-2.5-flash-preview-native-audio",131072,65536,"text",1,null,0.5,2,0,0],["gemini-robotics-er-1.6-preview",131072,65536,"text,image",1,null,1,5,0,0],["gemma-3-27b-it",131072,8192,"text,image",0,null,0,0,0,0],["gemma-4-26b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-26b-a4b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-26b-it",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b",256000,8192,"text,image",1,null,0,0,0,0],["gemma-4-31b-it",262144,32768,"text,image",1,null,0,0,0,0],["gemma-4-E2B-it",131072,8192,"text,image",1,null,0,0,0,0],["gemma-4-E4B-it",131072,8192,"text,image",1,null,0,0,0,0]],
|
|
51
51
|
"minimax": [["MiniMax-M2",196608,128000,"text",1,null,0.3,1.2,0,0],["MiniMax-M2.1",204800,131072,"text",1,null,0.3,1.2,0,0],["MiniMax-M2.5",204800,131072,"text",1,null,0.3,1.2,0.03,0.375],["MiniMax-M2.5-highspeed",204800,131072,"text",1,null,0.6,2.4,0.06,0.375],["MiniMax-M2.5-lightning",204800,32000,"text",1,null,0.3,2.4,0,0],["MiniMax-M2.7",204800,131072,"text",1,null,0.3,1.2,0.06,0.375],["MiniMax-M2.7-highspeed",204800,131072,"text",1,null,0.6,2.4,0.06,0.375],["minimax-m3",512000,128000,"text,image",1,null,0.6,2.4,0.12,0],["MiniMax-M3",1000000,128000,"text,image,video",1,null,0.3,1.2,0.06,0],["MiniMax-M3.1-Flash-Preview",1000000,null,"text,image,video",1]],
|
|
52
52
|
"mistral": [["codestral-latest",256000,4096,"text",0,null,0.3,0.9,0,0],["devstral-2512",262144,262144,"text",0,null,0.4,2,0,0],["devstral-latest",262144,262144,"text",0,null,0.4,2,0,0],["devstral-medium-2507",128000,128000,"text",0,null,0.4,2,0,0],["devstral-medium-latest",262144,262144,"text",0,null,0.4,2,0,0],["devstral-small-2505",128000,128000,"text",0,null,0.1,0.3,0,0],["devstral-small-2507",128000,128000,"text",0,null,0.1,0.3,0,0],["labs-devstral-small-2512",256000,256000,"text,image",0,null,0,0,0,0],["magistral-medium-latest",128000,16384,"text",1,null,2,5,0,0],["magistral-small",128000,128000,"text",1,null,0.5,1.5,0,0],["ministral-3b-latest",128000,128000,"text",0,null,0.04,0.04,0,0],["ministral-8b-latest",128000,128000,"text",0,null,0.1,0.1,0,0],["mistral-large-2411",131072,16384,"text",0,null,2,6,0,0],["mistral-large-2512",262144,262144,"text,image",0,null,0.5,1.5,0,0],["mistral-large-latest",262144,262144,"text,image",0,null,0.5,1.5,0,0],["mistral-medium-2505",131072,131072,"text,image",0,null,0.4,2,0,0],["mistral-medium-2508",262144,262144,"text,image",0,null,0.4,2,0,0],["mistral-medium-2604",262144,262144,"text,image",1,null,1.5,7.5,0,0],["mistral-medium-latest",262144,262144,"text,image",1,null,1.5,7.5,0,0],["mistral-nemo",128000,128000,"text",0,null,0.15,0.15,0,0],["mistral-small-2506",128000,16384,"text,image",0,null,0.1,0.3,0,0],["mistral-small-2603",256000,256000,"text,image",1,null,0.15,0.6,0,0],["mistral-small-latest",256000,256000,"text,image",1,null,0.15,0.6,0,0],["open-mistral-7b",8000,8000,"text",0,null,0.25,0.25,0,0],["open-mistral-nemo",128000,128000,"text",0,null,0.15,0.15,0,0],["open-mixtral-8x22b",64000,64000,"text",0,null,2,6,0,0],["open-mixtral-8x7b",32000,32000,"text",0,null,0.7,0.7,0,0],["pixtral-12b",128000,128000,"text,image",0,null,0.15,0.15,0,0],["pixtral-large-latest",128000,128000,"text,image",0,null,2,6,0,0]],
|
|
53
53
|
"moonshot": [["kimi-k2.5",262144,65536,"text,image",1,null,0,0,0,0]],
|
package/src/grok/grpc-web.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
export interface GrpcWebTrailer {
|
|
7
7
|
status: number;
|
|
8
|
+
statusValues: string[];
|
|
8
9
|
statusMessage?: string;
|
|
9
10
|
metadata: Record<string, string>;
|
|
10
11
|
}
|
|
@@ -12,6 +13,8 @@ export interface GrpcWebTrailer {
|
|
|
12
13
|
export interface DecodedGrpcWebResponse {
|
|
13
14
|
messages: Uint8Array[];
|
|
14
15
|
status: number;
|
|
16
|
+
statusValues: string[];
|
|
17
|
+
framingComplete: boolean;
|
|
15
18
|
statusMessage?: string;
|
|
16
19
|
trailers?: GrpcWebTrailer;
|
|
17
20
|
}
|
|
@@ -51,6 +54,7 @@ export function parseGrpcWebTrailers(bytes: Uint8Array): GrpcWebTrailer {
|
|
|
51
54
|
const text = new TextDecoder("utf-8").decode(bytes);
|
|
52
55
|
const lines = text.split(/\r?\n/);
|
|
53
56
|
const metadata: Record<string, string> = {};
|
|
57
|
+
const statusValues: string[] = [];
|
|
54
58
|
let status = 0;
|
|
55
59
|
let statusMessage: string | undefined;
|
|
56
60
|
|
|
@@ -62,6 +66,7 @@ export function parseGrpcWebTrailers(bytes: Uint8Array): GrpcWebTrailer {
|
|
|
62
66
|
if (!key) continue;
|
|
63
67
|
metadata[key] = value;
|
|
64
68
|
if (key === "grpc-status") {
|
|
69
|
+
statusValues.push(value);
|
|
65
70
|
const parsed = parseInt(value, 10);
|
|
66
71
|
if (!Number.isNaN(parsed)) {
|
|
67
72
|
status = parsed;
|
|
@@ -75,7 +80,7 @@ export function parseGrpcWebTrailers(bytes: Uint8Array): GrpcWebTrailer {
|
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
|
|
78
|
-
return { status, statusMessage, metadata };
|
|
83
|
+
return { status, statusValues, statusMessage, metadata };
|
|
79
84
|
}
|
|
80
85
|
|
|
81
86
|
/**
|
|
@@ -83,8 +88,11 @@ export function parseGrpcWebTrailers(bytes: Uint8Array): GrpcWebTrailer {
|
|
|
83
88
|
*/
|
|
84
89
|
export function decodeGrpcWebResponse(bytes: Uint8Array): DecodedGrpcWebResponse {
|
|
85
90
|
const messages: Uint8Array[] = [];
|
|
91
|
+
const statusValues: string[] = [];
|
|
86
92
|
let offset = 0;
|
|
87
93
|
let trailer: GrpcWebTrailer | undefined;
|
|
94
|
+
let firstTrailerPayload: Uint8Array | undefined;
|
|
95
|
+
let framingComplete = true;
|
|
88
96
|
|
|
89
97
|
while (offset + HEADER_SIZE <= bytes.length) {
|
|
90
98
|
const flag = bytes[offset];
|
|
@@ -99,10 +107,22 @@ export function decodeGrpcWebResponse(bytes: Uint8Array): DecodedGrpcWebResponse
|
|
|
99
107
|
|
|
100
108
|
const payload = bytes.subarray(frameStart, frameEnd);
|
|
101
109
|
|
|
110
|
+
// Preserve legacy decoding; callers needing confirmation inspect framingComplete.
|
|
102
111
|
if (flag === FRAME_DATA) {
|
|
112
|
+
if (firstTrailerPayload !== undefined) framingComplete = false;
|
|
103
113
|
messages.push(payload);
|
|
104
114
|
} else if (flag === FRAME_TRAILER) {
|
|
115
|
+
const firstTrailer = firstTrailerPayload;
|
|
116
|
+
if (firstTrailer === undefined) {
|
|
117
|
+
firstTrailerPayload = payload;
|
|
118
|
+
} else if (payload.length !== firstTrailer.length
|
|
119
|
+
|| payload.some((byte, index) => byte !== firstTrailer[index])) {
|
|
120
|
+
framingComplete = false;
|
|
121
|
+
}
|
|
105
122
|
trailer = parseGrpcWebTrailers(payload);
|
|
123
|
+
for (const value of trailer.statusValues) statusValues.push(value);
|
|
124
|
+
} else {
|
|
125
|
+
framingComplete = false;
|
|
106
126
|
}
|
|
107
127
|
|
|
108
128
|
offset = frameEnd;
|
|
@@ -114,6 +134,8 @@ export function decodeGrpcWebResponse(bytes: Uint8Array): DecodedGrpcWebResponse
|
|
|
114
134
|
return {
|
|
115
135
|
messages,
|
|
116
136
|
status: finalStatus,
|
|
137
|
+
statusValues,
|
|
138
|
+
framingComplete: framingComplete && offset === bytes.length,
|
|
117
139
|
statusMessage: finalMessage,
|
|
118
140
|
trailers: trailer,
|
|
119
141
|
};
|
|
@@ -25,6 +25,8 @@ export interface GrokResetCouponOperationRecord {
|
|
|
25
25
|
attemptedAt?: number;
|
|
26
26
|
code?: string;
|
|
27
27
|
settledAt?: number;
|
|
28
|
+
/** Durable status of a replayed record, so callers apply the terminal-replay predicate. */
|
|
29
|
+
status?: "attempted" | "settled" | "failed";
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
interface GrokResetCouponOperationState {
|
|
@@ -77,8 +79,15 @@ function readGrokCouponLedger(filePath: string): GrokResetCouponLedger {
|
|
|
77
79
|
op.status = "attempted";
|
|
78
80
|
delete op.code;
|
|
79
81
|
}
|
|
82
|
+
// A v1 attempted record never carried a confirmed outcome.
|
|
83
|
+
if (op.status === "attempted") delete op.code;
|
|
80
84
|
}
|
|
81
85
|
}
|
|
86
|
+
// Only a settled or failed record carries an outcome. An open or attempted
|
|
87
|
+
// record with a code would replay an unconfirmed result as definitive.
|
|
88
|
+
for (const op of Object.values(parsed.operations)) {
|
|
89
|
+
if ((op.status === "open" || op.status === "attempted") && op.code !== undefined) throw new Error();
|
|
90
|
+
}
|
|
82
91
|
return { version: 2, operations: parsed.operations };
|
|
83
92
|
} catch {
|
|
84
93
|
throw new Error("Grok coupon ledger is unreadable; existing records were preserved.");
|
|
@@ -163,6 +172,7 @@ export function openGrokResetCouponOperation(
|
|
|
163
172
|
attemptedAt: existing.status === "attempted" ? existing.updatedAt : undefined,
|
|
164
173
|
code: existing.code,
|
|
165
174
|
settledAt: existing.updatedAt,
|
|
175
|
+
status: existing.status,
|
|
166
176
|
};
|
|
167
177
|
}
|
|
168
178
|
return {
|
|
@@ -232,17 +242,37 @@ type GrokResetCouponPreflightResult =
|
|
|
232
242
|
| { kind: "recorded" | "changed" }
|
|
233
243
|
| { kind: "replay"; code: string; tokenId?: string; settledAt: number };
|
|
234
244
|
|
|
235
|
-
|
|
245
|
+
/**
|
|
246
|
+
* Codes that never prove an outcome. A record carrying one is not replayed as
|
|
247
|
+
* definitive, even if a restored or hand-edited ledger stores it as settled/failed.
|
|
248
|
+
*/
|
|
249
|
+
const UNRESOLVED_COUPON_CODES = new Set([
|
|
250
|
+
"redeem_failed", "attempt_unresolved", "attempt_in_progress", "attempt_reconcile_failed", "operation_state_changed",
|
|
251
|
+
"attempt_mark_failed", "operation_token_mismatch", "ledger_unavailable", "capacity",
|
|
252
|
+
]);
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Whether a durable record is a definitive outcome this account and token may replay:
|
|
256
|
+
* settled or failed, same account, same token when both are known, and a code that
|
|
257
|
+
* does not itself mean "unconfirmed".
|
|
258
|
+
*/
|
|
259
|
+
export function isTerminalGrokCouponReplay(
|
|
260
|
+
record: { status?: string; accountId?: string; tokenId?: string; code?: string } | undefined,
|
|
261
|
+
accountId: string, tokenId: string | undefined,
|
|
262
|
+
): boolean {
|
|
263
|
+
if (!record || record.accountId !== accountId) return false;
|
|
264
|
+
if (tokenId !== undefined && record.tokenId !== undefined && record.tokenId !== tokenId) return false;
|
|
265
|
+
return (record.status === "settled" || record.status === "failed")
|
|
266
|
+
&& !!record.code && !UNRESOLVED_COUPON_CODES.has(record.code);
|
|
267
|
+
}
|
|
236
268
|
|
|
237
269
|
/** The durable definitive outcome for this account and token, if the operation has one. */
|
|
238
270
|
function terminalReplay(
|
|
239
271
|
ledger: GrokResetCouponLedger, operationId: string, accountId: string, tokenId: string | undefined,
|
|
240
272
|
): Extract<GrokResetCouponPreflightResult, { kind: "replay" }> | null {
|
|
241
273
|
const existing = ledger.operations[operationId];
|
|
242
|
-
if (!existing || existing
|
|
243
|
-
|
|
244
|
-
if ((existing.status !== "settled" && existing.status !== "failed") || !existing.code || UNRESOLVED_COUPON_CODES.has(existing.code)) return null;
|
|
245
|
-
return { kind: "replay", code: existing.code, tokenId: existing.tokenId, settledAt: existing.updatedAt };
|
|
274
|
+
if (!existing || !isTerminalGrokCouponReplay(existing, accountId, tokenId)) return null;
|
|
275
|
+
return { kind: "replay", code: existing.code!, tokenId: existing.tokenId, settledAt: existing.updatedAt };
|
|
246
276
|
}
|
|
247
277
|
|
|
248
278
|
/** Settle a refusal or inspect its winner without releasing the mutation transaction. */
|
|
@@ -259,6 +289,46 @@ export function settleGrokResetCouponPreflightRefusal(
|
|
|
259
289
|
});
|
|
260
290
|
}
|
|
261
291
|
|
|
292
|
+
export type GrokResetCouponPreOpenRefusalResult =
|
|
293
|
+
| GrokResetCouponPreflightResult
|
|
294
|
+
| { kind: "identity-mismatch" | "capacity" };
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Refuse a request that never opened its supplied operationId. Under the mutation lock
|
|
298
|
+
* an absent id is reserved as a failed record, so a later open of the same id replays
|
|
299
|
+
* the refusal and can never be claimed (within the 30-day retention window). An id
|
|
300
|
+
* that already exists is never overwritten: a definitive outcome replays, anything
|
|
301
|
+
* else reports "changed" so the caller keeps its hold.
|
|
302
|
+
*/
|
|
303
|
+
export function refuseGrokResetCouponBeforeOpen(
|
|
304
|
+
refusal: { operationId: string; accountId: string; tokenId?: string; code: string },
|
|
305
|
+
now = Date.now(),
|
|
306
|
+
journalPath?: string,
|
|
307
|
+
): GrokResetCouponPreOpenRefusalResult {
|
|
308
|
+
return withConfigMutationLockSync(() => {
|
|
309
|
+
const filePath = journalPath ?? grokCouponJournalPath();
|
|
310
|
+
const ledger = readGrokCouponLedger(filePath);
|
|
311
|
+
const retentionCutoff = now - 30 * 24 * 60 * 60_000;
|
|
312
|
+
ledger.operations = Object.fromEntries(
|
|
313
|
+
Object.entries(ledger.operations).filter(([, op]) => op.updatedAt > retentionCutoff),
|
|
314
|
+
);
|
|
315
|
+
const existing = ledger.operations[refusal.operationId];
|
|
316
|
+
if (existing) {
|
|
317
|
+
if (existing.accountId !== refusal.accountId) return { kind: "identity-mismatch" };
|
|
318
|
+
// Same token binding as a normal replay: a different requested coupon never
|
|
319
|
+
// replays another coupon's outcome.
|
|
320
|
+
return terminalReplay(ledger, refusal.operationId, refusal.accountId, refusal.tokenId) ?? { kind: "changed" };
|
|
321
|
+
}
|
|
322
|
+
if (Object.keys(ledger.operations).length >= MAX_GROK_RESET_COUPON_OPERATION_IDS) return { kind: "capacity" };
|
|
323
|
+
ledger.operations[refusal.operationId] = {
|
|
324
|
+
accountId: refusal.accountId, ...(refusal.tokenId !== undefined ? { tokenId: refusal.tokenId } : {}),
|
|
325
|
+
status: "failed", code: refusal.code, createdAt: now, updatedAt: now,
|
|
326
|
+
};
|
|
327
|
+
writeGrokCouponLedger(filePath, ledger, now);
|
|
328
|
+
return { kind: "recorded" };
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
262
332
|
/**
|
|
263
333
|
* After a lost claim: the definitive winner to replay, or null while the
|
|
264
334
|
* operation is still unresolved. Terminal states never revert, so reading after
|
|
@@ -231,10 +231,12 @@ function buildGrokHeaders(accessToken: string): Record<string, string> {
|
|
|
231
231
|
* Reads available Grok reset tokens for the authenticated xAI account.
|
|
232
232
|
*/
|
|
233
233
|
function assertConfirmedCouponStatus(decoded: ReturnType<typeof decodeGrpcWebResponse>): void {
|
|
234
|
-
const rawStatus = decoded.
|
|
234
|
+
const rawStatus = decoded.statusValues[0];
|
|
235
235
|
// The shared decoder defaults a missing/malformed status to zero. Coupon
|
|
236
236
|
// availability and an irreversible redemption need affirmative confirmation.
|
|
237
|
-
if (
|
|
237
|
+
if (decoded.framingComplete !== true || rawStatus === undefined
|
|
238
|
+
|| decoded.statusValues.some((value) => !/^\d+$/.test(value)
|
|
239
|
+
|| !Number.isSafeInteger(Number(value)) || value !== rawStatus)) {
|
|
238
240
|
throw new Error("Grok coupon response has no confirmed gRPC status");
|
|
239
241
|
}
|
|
240
242
|
const status = Number(rawStatus);
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
/** Refresh only previously connected clients; a refused file never blocks its peers. */
|
|
13
13
|
export async function refreshOwnedCatalogIntegrations(
|
|
14
14
|
input: Omit<OwnedIntegrationRefreshInput, "clientId">,
|
|
15
|
-
clientIds: readonly IntegrationClientId[] = ["pi", "aside", "raycast", "omo", "commandcode", "droid", "opencode", "kilo"],
|
|
15
|
+
clientIds: readonly IntegrationClientId[] = ["pi", "aside", "raycast", "omo", "commandcode", "droid", "opencode", "kilo", "qoder"],
|
|
16
16
|
options: { refreshOnly?: boolean; admit?: () => boolean } = {},
|
|
17
17
|
): Promise<OwnedIntegrationRefreshOutcome[]> {
|
|
18
18
|
// Client files are shared with the live proxy a sibling instance runs beside; their entries
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Design of record: devlog/_fin/260802_client_toggle_api/021 §5-6.
|
|
9
9
|
*/
|
|
10
|
-
import { lstatSync, mkdirSync,
|
|
10
|
+
import { lstatSync, mkdirSync, rmSync, statSync } from "node:fs";
|
|
11
11
|
import type { ConfigFormat } from "../clients/config-export";
|
|
12
|
+
import { readBoundedRegularFileSync } from "../lib/bounded-file-read";
|
|
12
13
|
import { canonicalizeJsonc } from "../lib/jsonc";
|
|
13
14
|
import { MAX_JSON_NESTING } from "./serialize";
|
|
14
15
|
import { atomicWriteFileNoFollow, isMissingPathError } from "../config/atomic-write";
|
|
@@ -24,6 +25,10 @@ import type { IntegrationClientId } from "./registry";
|
|
|
24
25
|
* then overwritten.
|
|
25
26
|
*/
|
|
26
27
|
export const PARSE_FAILED = Symbol("parse-failed");
|
|
28
|
+
const MAX_INTEGRATION_CONFIG_FILE_BYTES = 4 * 1024 * 1024;
|
|
29
|
+
|
|
30
|
+
/** Raised before any target mutation, including pending-marker creation. */
|
|
31
|
+
export class IntegrationWriteSizeError extends Error {}
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
34
|
* What `JSON.parse` has already discarded by the time we hold the parsed value,
|
|
@@ -285,7 +290,7 @@ export function fileIO(): Omit<IntegrationIO, "appendJournal" | "putRecord" | "d
|
|
|
285
290
|
lstatKind,
|
|
286
291
|
readText: path => {
|
|
287
292
|
try {
|
|
288
|
-
return { kind: "text", text:
|
|
293
|
+
return { kind: "text", text: readBoundedRegularFileSync(path, MAX_INTEGRATION_CONFIG_FILE_BYTES).toString("utf8") };
|
|
289
294
|
} catch (error) {
|
|
290
295
|
const code = (error as NodeJS.ErrnoException).code;
|
|
291
296
|
return code === "ENOENT" ? { kind: "missing" } : { kind: "failed", ...(code ? { code } : {}) };
|
|
@@ -300,6 +305,19 @@ export function fileIO(): Omit<IntegrationIO, "appendJournal" | "putRecord" | "d
|
|
|
300
305
|
}
|
|
301
306
|
},
|
|
302
307
|
writeText: (path, text) => {
|
|
308
|
+
/*
|
|
309
|
+
* One bound on both halves of the seam: a file past this size is one
|
|
310
|
+
* readText refuses, so writing it would strand the integration against
|
|
311
|
+
* its own configuration — or, for Cline's pending marker, leave a
|
|
312
|
+
* transaction recovery could never read back.
|
|
313
|
+
*/
|
|
314
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
315
|
+
if (bytes > MAX_INTEGRATION_CONFIG_FILE_BYTES) {
|
|
316
|
+
throw new IntegrationWriteSizeError(
|
|
317
|
+
`refusing oversized integration write to ${path}: `
|
|
318
|
+
+ `${bytes} bytes is past the ${MAX_INTEGRATION_CONFIG_FILE_BYTES}-byte read bound`,
|
|
319
|
+
);
|
|
320
|
+
}
|
|
303
321
|
const kind = lstatKind(path);
|
|
304
322
|
if (kind !== "file" && kind !== "missing") {
|
|
305
323
|
throw new Error(`refusing unsafe integration write target: ${path}`);
|
|
@@ -16,7 +16,7 @@ import { randomUUID } from "node:crypto";
|
|
|
16
16
|
import { appendFileSync, existsSync, readFileSync, readdirSync, rmSync } from "node:fs";
|
|
17
17
|
import { join, resolve, sep } from "node:path";
|
|
18
18
|
import { atomicWriteFile } from "../config";
|
|
19
|
-
import { ensureDir, fingerprint, integrationsDir, isOwnershipRecord, type OwnershipRecord } from "./ownership";
|
|
19
|
+
import { ensureDir, fingerprint, integrationsDir, isOwnershipRecord, readRecordsStrict, type OwnershipRecord } from "./ownership";
|
|
20
20
|
import { isIntegrationClientId, type IntegrationClientId } from "./registry";
|
|
21
21
|
|
|
22
22
|
/**
|
|
@@ -171,6 +171,20 @@ export function captureSnapshot(
|
|
|
171
171
|
return { kind: "stored", relPath: join("snapshots", clientId, opId) };
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
+
/** Remove only an unused pre-write snapshot; uncertain or committed evidence survives. */
|
|
175
|
+
export function discardUncommittedSnapshot(
|
|
176
|
+
clientId: IntegrationClientId,
|
|
177
|
+
opId: string,
|
|
178
|
+
dir: string = integrationsDir(),
|
|
179
|
+
): void {
|
|
180
|
+
const target = containedPath(dir, "snapshots", assertSafeComponent(clientId, "clientId"), assertSafeComponent(opId, "opId"));
|
|
181
|
+
if (findCommittedOperation(opId, dir)
|
|
182
|
+
|| Object.values(readRecordsStrict(dir)).some(record => record?.opId === opId)) {
|
|
183
|
+
throw new Error("integration snapshot still belongs to a recorded operation");
|
|
184
|
+
}
|
|
185
|
+
rmSync(target, { force: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
174
188
|
/** Commit the row. Pruning is post-commit and can never fail the append. */
|
|
175
189
|
export function appendOperation(entry: JournalEntry, dir: string = integrationsDir()): void {
|
|
176
190
|
ensureDir(journalPath(dir));
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
assertDroidRecordedSettingsUnambiguous,
|
|
33
33
|
isLoopbackOnly,
|
|
34
34
|
resolveIntegrationPaths,
|
|
35
|
+
type ResolvedIntegrationPaths,
|
|
35
36
|
restoreOwnershipCollision,
|
|
36
37
|
type IntegrationClientId,
|
|
37
38
|
} from "./registry";
|
|
@@ -130,6 +131,7 @@ export const DYNAMIC_SEGMENT = "*";
|
|
|
130
131
|
* paths silently have no declaration.
|
|
131
132
|
*/
|
|
132
133
|
const CLIENT_MANAGED_PATHS = {
|
|
134
|
+
qoder: [["providers", OPENCODE_PROVIDER_ID], ["modelConfigs", "customModels", DYNAMIC_SEGMENT]],
|
|
133
135
|
opencode: [["provider", OPENCODE_PROVIDER_ID], ["providers", OPENCODE_PROVIDER_ID]],
|
|
134
136
|
pi: [["providers", OPENCODE_PROVIDER_ID]],
|
|
135
137
|
omp: [["providers", OPENCODE_PROVIDER_ID]],
|
|
@@ -567,7 +569,7 @@ export function restoreStoreDirectoryRefusal(
|
|
|
567
569
|
if (!declared?.lockFile) return null;
|
|
568
570
|
let storePath: string;
|
|
569
571
|
try {
|
|
570
|
-
storePath = declared.path(input.env, input.home);
|
|
572
|
+
storePath = declared.path(input.env, input.home, io);
|
|
571
573
|
} catch (error) {
|
|
572
574
|
if (error instanceof ClientPathError) return null;
|
|
573
575
|
throw error;
|
|
@@ -602,10 +604,11 @@ export function observeRestore(
|
|
|
602
604
|
const store = selectedOperation?.store ?? input.store ?? createIntegrationStateStore();
|
|
603
605
|
let io = input.io ?? defaultIntegrationIO(store);
|
|
604
606
|
const clientId = input.clientId;
|
|
605
|
-
let resolved:
|
|
607
|
+
let resolved: ResolvedIntegrationPaths;
|
|
606
608
|
try {
|
|
607
609
|
const context = exportContextOf(input);
|
|
608
|
-
resolved =
|
|
610
|
+
resolved = clientId === "zcode" || !input.resolvedPaths
|
|
611
|
+
? resolveIntegrationPaths(clientId, input.env, input.home, context, io) : input.resolvedPaths;
|
|
609
612
|
if (clientId === "droid" && input.resolvedPaths) assertDroidPathsUnambiguous(resolved.detectDir, context);
|
|
610
613
|
} catch (error) {
|
|
611
614
|
if (!(error instanceof ClientPathError)) throw error;
|
|
@@ -630,7 +633,7 @@ export function observeRestore(
|
|
|
630
633
|
* config file. The answer also carries the document shape those bytes are in.
|
|
631
634
|
*/
|
|
632
635
|
const rowTarget = declaredIntegrationTarget({
|
|
633
|
-
clientId, configPath, resolvedConfigPath: resolved.configPath, env: input.env, home: input.home,
|
|
636
|
+
clientId, configPath, resolvedConfigPath: resolved.configPath, env: input.env, home: input.home, io,
|
|
634
637
|
});
|
|
635
638
|
if (rowTarget === null) {
|
|
636
639
|
return {
|
|
@@ -848,7 +851,7 @@ export interface IntegrationWriteInput {
|
|
|
848
851
|
store?: IntegrationStateStore;
|
|
849
852
|
io?: IntegrationIO;
|
|
850
853
|
/** Frozen once by the async coordinator; synchronous callers may omit it. */
|
|
851
|
-
resolvedPaths?:
|
|
854
|
+
resolvedPaths?: ResolvedIntegrationPaths;
|
|
852
855
|
}
|
|
853
856
|
|
|
854
857
|
/** A refusal in the planner's own vocabulary, so observation does not depend on the writer's result type. */
|
|
@@ -922,7 +925,8 @@ export function observeIntegration(
|
|
|
922
925
|
* verify account 1 was installed and then write account 0's catalog.
|
|
923
926
|
*/
|
|
924
927
|
const context = exportContextOf(input);
|
|
925
|
-
const resolved =
|
|
928
|
+
const resolved = clientId === "zcode" || !input.resolvedPaths
|
|
929
|
+
? resolveIntegrationPaths(clientId, input.env, input.home, context, io) : input.resolvedPaths;
|
|
926
930
|
if (clientId === "droid" && input.resolvedPaths) assertDroidPathsUnambiguous(resolved.detectDir, context);
|
|
927
931
|
detectDir = resolved.detectDir;
|
|
928
932
|
if (clientId === "cline") io = createClineIO(io, resolved.configPath, store, effects.recover);
|
|
@@ -945,9 +949,12 @@ export function observeIntegration(
|
|
|
945
949
|
* collection route.
|
|
946
950
|
*/
|
|
947
951
|
effective = resolveIntegrationTarget({
|
|
948
|
-
clientId, configPath: recordedPath, io, record: stored, env: input.env, home: input.home,
|
|
952
|
+
clientId, configPath: recordedPath, io, record: stored, env: input.env, home: input.home, resolvedPaths: resolved,
|
|
949
953
|
});
|
|
950
954
|
configPath = effective.configPath;
|
|
955
|
+
if (effective.pathError && operation !== "disable") {
|
|
956
|
+
return { failed: observationFailure("unsafe", "unsafe", effective.pathError) } as const;
|
|
957
|
+
}
|
|
951
958
|
} catch (error) {
|
|
952
959
|
if (error instanceof ClineTransactionError) {
|
|
953
960
|
return { failed: { ...observationFailure("unsafe", "unsafe", error.message, error.snapshotPath), residual: true } } as const;
|
|
@@ -12,10 +12,13 @@ import { homedir } from "node:os";
|
|
|
12
12
|
import { assertDroidSettingsUnambiguous } from "./droid-settings";
|
|
13
13
|
import { readPath } from "./merge";
|
|
14
14
|
import type { OwnershipRecord } from "./ownership";
|
|
15
|
+
import type { IntegrationIO } from "./config-io";
|
|
15
16
|
import { dirname, join } from "node:path";
|
|
16
17
|
import {
|
|
17
18
|
ClientPathError,
|
|
18
19
|
buildDroidContribution,
|
|
20
|
+
qoderConfigPath,
|
|
21
|
+
qoderHomeDir,
|
|
19
22
|
clineConfigPath,
|
|
20
23
|
clineSettingsDir,
|
|
21
24
|
droidConfigPath,
|
|
@@ -49,6 +52,8 @@ import {
|
|
|
49
52
|
raycastConfigPath,
|
|
50
53
|
zcodeConfigPath,
|
|
51
54
|
zcodeHomeDir,
|
|
55
|
+
zcodeLegacyHomeDir,
|
|
56
|
+
ZcodeSettingsPathError,
|
|
52
57
|
zcodeProviderStorePath,
|
|
53
58
|
buildZcodeStoreContribution,
|
|
54
59
|
zcodeStoreSchemaEstablished,
|
|
@@ -75,12 +80,21 @@ import {
|
|
|
75
80
|
*/
|
|
76
81
|
export type IntegrationClientId = ExportClientId;
|
|
77
82
|
|
|
83
|
+
export interface ResolvedIntegrationPaths {
|
|
84
|
+
configPath: string;
|
|
85
|
+
detectDir: string;
|
|
86
|
+
/** Snapshot of a mutable store resolution from the same bootstrap read. */
|
|
87
|
+
storePath?: string;
|
|
88
|
+
/** The default config path is only a recovery hint when the active root is unknown. */
|
|
89
|
+
pathError?: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
78
92
|
export interface IntegrationClientSpec {
|
|
79
93
|
id: IntegrationClientId;
|
|
80
94
|
/** The client's config file, honoring that client's own environment override. */
|
|
81
|
-
configPath: (env?: NodeJS.ProcessEnv, home?: string) => string;
|
|
95
|
+
configPath: (env?: NodeJS.ProcessEnv, home?: string, io?: Pick<IntegrationIO, "readText">) => string;
|
|
82
96
|
/** Directory whose existence is the cheap "is it installed?" signal. */
|
|
83
|
-
detectDir: (env?: NodeJS.ProcessEnv, home?: string) => string;
|
|
97
|
+
detectDir: (env?: NodeJS.ProcessEnv, home?: string, io?: Pick<IntegrationIO, "readText">) => string;
|
|
84
98
|
/**
|
|
85
99
|
* The provider store this client reads INSTEAD of `configPath`.
|
|
86
100
|
*
|
|
@@ -98,7 +112,7 @@ export interface IntegrationClientSpec {
|
|
|
98
112
|
* client, never merged into on a guess.
|
|
99
113
|
*/
|
|
100
114
|
currentStore?: {
|
|
101
|
-
path: (env?: NodeJS.ProcessEnv, home?: string) => string;
|
|
115
|
+
path: (env?: NodeJS.ProcessEnv, home?: string, io?: Pick<IntegrationIO, "readText">) => string;
|
|
102
116
|
format: ConfigFormat;
|
|
103
117
|
establishes: (parsed: unknown) => boolean;
|
|
104
118
|
buildContribution: BuildContribution;
|
|
@@ -139,7 +153,7 @@ export interface IntegrationClientSpec {
|
|
|
139
153
|
* Droid uses the same seam to check competing settings against the export
|
|
140
154
|
* context before status, preview, or mutation proceeds.
|
|
141
155
|
*/
|
|
142
|
-
resolvePaths?: (env?: NodeJS.ProcessEnv, home?: string, exportContext?: ExportContext
|
|
156
|
+
resolvePaths?: (env?: NodeJS.ProcessEnv, home?: string, exportContext?: ExportContext, io?: Pick<IntegrationIO, "readText">) => ResolvedIntegrationPaths;
|
|
143
157
|
/**
|
|
144
158
|
* Where the client's config WOULD live, for a client whose real path cannot
|
|
145
159
|
* be resolved yet.
|
|
@@ -183,10 +197,11 @@ export function resolveIntegrationPaths(
|
|
|
183
197
|
env: NodeJS.ProcessEnv = process.env,
|
|
184
198
|
home: string = homedir(),
|
|
185
199
|
exportContext?: ExportContext,
|
|
186
|
-
|
|
200
|
+
io?: Pick<IntegrationIO, "readText">,
|
|
201
|
+
): ResolvedIntegrationPaths {
|
|
187
202
|
const spec = INTEGRATION_CLIENTS[clientId];
|
|
188
|
-
if (spec.resolvePaths) return spec.resolvePaths(env, home, exportContext);
|
|
189
|
-
return { configPath: spec.configPath(env, home), detectDir: spec.detectDir(env, home) };
|
|
203
|
+
if (spec.resolvePaths) return spec.resolvePaths(env, home, exportContext, io);
|
|
204
|
+
return { configPath: spec.configPath(env, home, io), detectDir: spec.detectDir(env, home, io) };
|
|
190
205
|
}
|
|
191
206
|
|
|
192
207
|
export function assertDroidPathsUnambiguous(root: string, exportContext?: ExportContext): void {
|
|
@@ -267,6 +282,11 @@ function xdgConfigHome(env: NodeJS.ProcessEnv, home: string): string {
|
|
|
267
282
|
}
|
|
268
283
|
|
|
269
284
|
export const INTEGRATION_CLIENTS: Record<IntegrationClientId, IntegrationClientSpec> = {
|
|
285
|
+
qoder: {
|
|
286
|
+
id: "qoder",
|
|
287
|
+
configPath: qoderConfigPath,
|
|
288
|
+
detectDir: (env = process.env, home = homedir()) => qoderHomeDir(env, home),
|
|
289
|
+
},
|
|
270
290
|
opencode: {
|
|
271
291
|
id: "opencode",
|
|
272
292
|
// These take `home` explicitly. The export registry's `destination` reads
|
|
@@ -357,15 +377,31 @@ export const INTEGRATION_CLIENTS: Record<IntegrationClientId, IntegrationClientS
|
|
|
357
377
|
},
|
|
358
378
|
zcode: {
|
|
359
379
|
id: "zcode",
|
|
360
|
-
configPath: (env = process.env, home = homedir()) => zcodeConfigPath(env, home),
|
|
361
|
-
detectDir: (env = process.env, home = homedir()) => zcodeHomeDir(env, home),
|
|
380
|
+
configPath: (env = process.env, home = homedir(), io) => zcodeConfigPath(env, home, io),
|
|
381
|
+
detectDir: (env = process.env, home = homedir(), io) => zcodeHomeDir(env, home, io),
|
|
382
|
+
resolvePaths: (env = process.env, home = homedir(), _context, io) => {
|
|
383
|
+
let root: string;
|
|
384
|
+
let pathError: string | undefined;
|
|
385
|
+
try { root = zcodeHomeDir(env, home, io); }
|
|
386
|
+
catch (error) {
|
|
387
|
+
if (!(error instanceof ZcodeSettingsPathError)) throw error;
|
|
388
|
+
root = zcodeLegacyHomeDir(env, home);
|
|
389
|
+
pathError = error.message;
|
|
390
|
+
}
|
|
391
|
+
// Passing the resolved root prevents a second bootstrap read. An explicit
|
|
392
|
+
// provider-file override outranks both the setting and ZCODE_DATA_DIR.
|
|
393
|
+
const storePath = zcodeProviderStorePath({ ...env, ZCODE_DATA_DIR: root }, home, io);
|
|
394
|
+
return { configPath: join(root, "v2", "config.json"), detectDir: root, storePath, ...(pathError ? { pathError } : {}) };
|
|
395
|
+
},
|
|
396
|
+
bindsDriftedRecord: (path, env = process.env, home = homedir()) =>
|
|
397
|
+
path === join(zcodeLegacyHomeDir(env, home), "v2", "config.json"),
|
|
362
398
|
/*
|
|
363
399
|
* ZCode 3.14 reads its providers from `v2/provider_config.json` and reaches
|
|
364
400
|
* `v2/config.json` only through the import that seeded it. Where the new
|
|
365
401
|
* file exists the import is spent, so our write is read by nobody (#5348).
|
|
366
402
|
*/
|
|
367
403
|
currentStore: {
|
|
368
|
-
path: (env = process.env, home = homedir()) => zcodeProviderStorePath(env, home),
|
|
404
|
+
path: (env = process.env, home = homedir(), io) => zcodeProviderStorePath(env, home, io),
|
|
369
405
|
format: "json",
|
|
370
406
|
establishes: zcodeStoreSchemaEstablished,
|
|
371
407
|
buildContribution: buildZcodeStoreContribution,
|
|
@@ -496,7 +532,7 @@ export function boundIntegrationConfigPath(input: {
|
|
|
496
532
|
if (
|
|
497
533
|
record && record.clientId === input.clientId &&
|
|
498
534
|
record.configPath !== input.resolvedPath &&
|
|
499
|
-
input.statKind(record.configPath) === "file" &&
|
|
535
|
+
(input.clientId === "zcode" ? input.statKind(record.configPath) !== "missing" : input.statKind(record.configPath) === "file") &&
|
|
500
536
|
INTEGRATION_CLIENTS[input.clientId].bindsDriftedRecord?.(record.configPath, input.env, input.home) === true
|
|
501
537
|
) {
|
|
502
538
|
return record.configPath;
|
|
@@ -529,6 +565,11 @@ export function restoreOwnershipCollision(input: {
|
|
|
529
565
|
}): string | null {
|
|
530
566
|
const currentPath = input.currentPath;
|
|
531
567
|
if (currentPath === null || currentPath === input.journaledPath) return null;
|
|
568
|
+
// A relocated ZCode store must not lose its single record to an older
|
|
569
|
+
// legacy undo, even when both journaled locations are legal for this home.
|
|
570
|
+
if (input.clientId === "zcode") {
|
|
571
|
+
return `that operation was recorded for ${input.journaledPath}, but ${currentPath} currently owns this integration`;
|
|
572
|
+
}
|
|
532
573
|
const binds = INTEGRATION_CLIENTS[input.clientId].bindsDriftedRecord;
|
|
533
574
|
if (!binds) return null;
|
|
534
575
|
if (binds(input.journaledPath, input.env, input.home) !== true) return null;
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
assertDroidPathsUnambiguous,
|
|
40
40
|
assertDroidRecordedSettingsUnambiguous,
|
|
41
41
|
resolveIntegrationPaths,
|
|
42
|
+
type ResolvedIntegrationPaths,
|
|
42
43
|
unresolvedPathHintFor,
|
|
43
44
|
type IntegrationClientId,
|
|
44
45
|
} from "./registry";
|
|
@@ -482,7 +483,7 @@ export interface IntegrationStateInput {
|
|
|
482
483
|
store?: IntegrationStateStore;
|
|
483
484
|
io?: IntegrationIO;
|
|
484
485
|
/** Internal explicit profile target; never accepted as a caller-provided path. */
|
|
485
|
-
resolvedPaths?:
|
|
486
|
+
resolvedPaths?: ResolvedIntegrationPaths;
|
|
486
487
|
}
|
|
487
488
|
|
|
488
489
|
export function exportContextOf(input: {
|
|
@@ -592,11 +593,11 @@ export function readIntegrationState(input: IntegrationStateInput): IntegrationS
|
|
|
592
593
|
*/
|
|
593
594
|
let installed: boolean;
|
|
594
595
|
let effective: IntegrationTarget;
|
|
595
|
-
let record: OwnershipRecord | null;
|
|
596
|
+
let record: OwnershipRecord | null = null;
|
|
596
597
|
try {
|
|
597
598
|
// One resolution for both, so a client whose paths come from mutable state
|
|
598
599
|
// cannot report one account's install beside another account's config path.
|
|
599
|
-
const paths = resolveStatePaths(input);
|
|
600
|
+
const paths = resolveStatePaths(input, io);
|
|
600
601
|
installed = io.statKind(paths.detectDir) === "dir";
|
|
601
602
|
if (input.clientId === "cline") io = createClineIO(io, paths.configPath, store);
|
|
602
603
|
/*
|
|
@@ -611,7 +612,7 @@ export function readIntegrationState(input: IntegrationStateInput): IntegrationS
|
|
|
611
612
|
statKind: io.statKind, env: input.env, home: input.home,
|
|
612
613
|
});
|
|
613
614
|
effective = resolveIntegrationTarget({
|
|
614
|
-
clientId: input.clientId, configPath: recordedPath, io, record, env: input.env, home: input.home,
|
|
615
|
+
clientId: input.clientId, configPath: recordedPath, io, record, env: input.env, home: input.home, resolvedPaths: paths,
|
|
615
616
|
});
|
|
616
617
|
} catch (error) {
|
|
617
618
|
if (!(error instanceof ClientPathError)) throw error;
|
|
@@ -626,6 +627,10 @@ export function readIntegrationState(input: IntegrationStateInput): IntegrationS
|
|
|
626
627
|
* yet. A client that can name where its config would go gets `installed:
|
|
627
628
|
* false` and that location, which reads as "not installed" in the UI.
|
|
628
629
|
*/
|
|
630
|
+
if (input.clientId === "zcode" && record) return {
|
|
631
|
+
clientId: input.clientId, state: "unsafe", installed: false, configPath: record.configPath,
|
|
632
|
+
reason: "unresolvable-path", appliedAt: record.appliedAt, lastOpId: record.opId, ...retention,
|
|
633
|
+
};
|
|
629
634
|
const hint = unresolvedPathHintFor(input.clientId, input.env, input.home);
|
|
630
635
|
return {
|
|
631
636
|
clientId: input.clientId,
|
|
@@ -638,6 +643,12 @@ export function readIntegrationState(input: IntegrationStateInput): IntegrationS
|
|
|
638
643
|
}
|
|
639
644
|
|
|
640
645
|
const configPath = effective.configPath;
|
|
646
|
+
if (effective.pathError) return {
|
|
647
|
+
clientId: input.clientId, state: "unsafe", installed, configPath,
|
|
648
|
+
reason: "unresolvable-path",
|
|
649
|
+
...(record?.configPath === configPath ? { appliedAt: record.appliedAt, lastOpId: record.opId } : {}),
|
|
650
|
+
...retention,
|
|
651
|
+
};
|
|
641
652
|
if (input.clientId === "kilo") {
|
|
642
653
|
const candidates = inspectKiloCandidates({ io, selectedPath: configPath, env: input.env, home: input.home });
|
|
643
654
|
if (candidates.kind !== "ok") return {
|
|
@@ -715,9 +726,10 @@ export function readIntegrationState(input: IntegrationStateInput): IntegrationS
|
|
|
715
726
|
};
|
|
716
727
|
}
|
|
717
728
|
|
|
718
|
-
function resolveStatePaths(input: IntegrationStateInput
|
|
729
|
+
function resolveStatePaths(input: IntegrationStateInput, io: IntegrationIO): ResolvedIntegrationPaths {
|
|
719
730
|
const context = exportContextOf(input);
|
|
720
|
-
const paths = input.
|
|
731
|
+
const paths = input.clientId === "zcode" || !input.resolvedPaths
|
|
732
|
+
? resolveIntegrationPaths(input.clientId, input.env, input.home, context, io) : input.resolvedPaths;
|
|
721
733
|
if (input.clientId === "droid" && input.resolvedPaths) assertDroidPathsUnambiguous(paths.detectDir, context);
|
|
722
734
|
return paths;
|
|
723
735
|
}
|
|
@@ -727,7 +739,7 @@ export function readOwnedDroidReasoningDefaults(input: IntegrationStateInput): R
|
|
|
727
739
|
const store = input.store ?? createIntegrationStateStore();
|
|
728
740
|
const io = input.io ?? store.io();
|
|
729
741
|
try {
|
|
730
|
-
const paths = resolveStatePaths(input);
|
|
742
|
+
const paths = resolveStatePaths(input, io);
|
|
731
743
|
const record = store.readRecords().droid;
|
|
732
744
|
if (!record || record.clientId !== "droid" || record.configPath !== paths.configPath) return {};
|
|
733
745
|
const effective = resolveIntegrationTarget({
|