@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
package/src/claude/outbound.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - errors: {type:"error", error:{type,message}}; may arrive mid-stream after HTTP 200.
|
|
12
12
|
*/
|
|
13
13
|
import { createHash } from "node:crypto";
|
|
14
|
-
import { httpStatusFromTerminalError } from "../lib/errors";
|
|
14
|
+
import { httpStatusFromTerminalError, UNDECLARED_TOOL_CALL_ERROR_CODE } from "../lib/errors";
|
|
15
15
|
import { isTransientUpstreamStatus } from "../lib/upstream-retry";
|
|
16
16
|
import {
|
|
17
17
|
isTranslatorBudgetExceededError,
|
|
@@ -81,12 +81,16 @@ export function anthropicErrorType(status: number): string {
|
|
|
81
81
|
* (devlog/_plan/261009_claude_1m_default/010).
|
|
82
82
|
*/
|
|
83
83
|
const CLAUDE_PROMPT_TOO_LONG_RE = /prompt is too long|input is too long for requested model/i;
|
|
84
|
-
const
|
|
84
|
+
const MAX_OVERFLOW_MESSAGE_CHARS = 64 * 1024;
|
|
85
|
+
const STATED_CONTEXT_LIMIT_RE = /maximum context length is (\d+) tokens/i;
|
|
86
|
+
const STATED_REQUEST_COUNT_RE = /(?:resulted in|you requested) (\d+) tokens/i;
|
|
85
87
|
|
|
86
88
|
export function claudePromptTooLongMessage(message: string): string {
|
|
89
|
+
message = message.slice(0, MAX_OVERFLOW_MESSAGE_CHARS);
|
|
87
90
|
if (CLAUDE_PROMPT_TOO_LONG_RE.test(message)) return message;
|
|
88
|
-
const
|
|
89
|
-
|
|
91
|
+
const limit = STATED_CONTEXT_LIMIT_RE.exec(message);
|
|
92
|
+
const requested = limit && STATED_REQUEST_COUNT_RE.exec(message.slice(limit.index + limit[0].length));
|
|
93
|
+
if (limit && requested) return `prompt is too long: ${requested[1]} tokens > ${limit[1]} maximum`;
|
|
90
94
|
return `prompt is too long: ${message}`;
|
|
91
95
|
}
|
|
92
96
|
|
|
@@ -804,11 +808,14 @@ export function responsesSseToAnthropicSse(
|
|
|
804
808
|
// Unclassified status-absent response.failed (relaySseWithFailedTail synthetic
|
|
805
809
|
// tail) still lands on a transient 5xx here — the mid-stream reset shape maps to
|
|
806
810
|
// overloaded_error by design.
|
|
811
|
+
// The undeclared-tool refusal is deterministic and proxy-owned, not an upstream
|
|
812
|
+
// condition: keep its api_error type and code instead of a retryable overload.
|
|
813
|
+
const deterministic = code === UNDECLARED_TOOL_CALL_ERROR_CODE;
|
|
807
814
|
fail(
|
|
808
815
|
status,
|
|
809
816
|
message,
|
|
810
|
-
|
|
811
|
-
code === "context_length_exceeded" ? code : undefined,
|
|
817
|
+
!deterministic,
|
|
818
|
+
deterministic || code === "context_length_exceeded" ? code : undefined,
|
|
812
819
|
);
|
|
813
820
|
break;
|
|
814
821
|
}
|
package/src/cli/account-api.ts
CHANGED
|
@@ -137,6 +137,7 @@ export async function apiJson(
|
|
|
137
137
|
body?: unknown,
|
|
138
138
|
options: { signal?: AbortSignal; redirect?: RequestRedirect } = {},
|
|
139
139
|
): Promise<ApiResult> {
|
|
140
|
+
if (baseUrl === LINK_HOME_BASE) return linkHomeApiJson(deps, method, path);
|
|
140
141
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
141
142
|
try {
|
|
142
143
|
const res = await fetchImpl(`${baseUrl}${path}`, {
|
|
@@ -163,9 +164,73 @@ export async function resolveBaseUrl(deps: AccountDeps): Promise<string | null>
|
|
|
163
164
|
if (deps.baseUrl) return deps.baseUrl;
|
|
164
165
|
const live = await findLiveProxy();
|
|
165
166
|
if (!live) return null;
|
|
167
|
+
if (live.role === "client") {
|
|
168
|
+
const { readLinkChildContext } = await import("./link-child");
|
|
169
|
+
if (readLinkChildContext()) return LINK_HOME_BASE;
|
|
170
|
+
}
|
|
166
171
|
return `http://${probeHostname(live.hostname)}:${live.port}`;
|
|
167
172
|
}
|
|
168
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Remote Link Child (#6957): account reads come from the Home over the link, and every account
|
|
176
|
+
* change is refused with the Child refusal. The marker is never a URL and never reaches fetch.
|
|
177
|
+
*/
|
|
178
|
+
export const LINK_HOME_BASE = "ocx-link-home:";
|
|
179
|
+
|
|
180
|
+
async function linkHomeApiJson(deps: AccountDeps, method: string, path: string): Promise<ApiResult> {
|
|
181
|
+
const { readLinkChildContext, linkChildReadable, linkChildRefusal, readLinkHomeJson } = await import("./link-child");
|
|
182
|
+
const link = readLinkChildContext();
|
|
183
|
+
if (!link) return { status: 503, json: { error: "The Remote Link connection changed while the command ran; retry." } };
|
|
184
|
+
const pathname = new URL(path, "http://cli.invalid").pathname;
|
|
185
|
+
if (!linkChildReadable(method, path)) {
|
|
186
|
+
return { status: 403, json: { error: linkChildRefusal(link, `${method} ${pathname}`) } };
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const { status, body } = await readLinkHomeJson(link, path, deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {});
|
|
190
|
+
return { status, json: (body && typeof body === "object" ? body : { error: String(body ?? "") }) as Record<string, unknown> };
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return { status: 503, json: { error: error instanceof Error ? error.message : String(error) } };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The config account commands classify providers against. On a Remote Link Child the local
|
|
198
|
+
* providers are inactive, so the Home's `/api/providers` projection (presence flags only) takes
|
|
199
|
+
* their place. Anywhere else the local config is returned. When the Home cannot be read the
|
|
200
|
+
* result is an error: classifying against the inactive local providers would answer for the
|
|
201
|
+
* wrong machine.
|
|
202
|
+
*/
|
|
203
|
+
export async function accountClassificationConfig(
|
|
204
|
+
deps: AccountDeps,
|
|
205
|
+
local: OcxConfig,
|
|
206
|
+
): Promise<{ config: OcxConfig } | { error: string }> {
|
|
207
|
+
if (deps.baseUrl) return { config: local };
|
|
208
|
+
const { readLinkChildContext, readLinkHomeJson } = await import("./link-child");
|
|
209
|
+
const link = readLinkChildContext();
|
|
210
|
+
if (!link) return { config: local };
|
|
211
|
+
const unavailable = (detail: string) => ({ error: `Could not read the provider list from ${link.homePhrase} over Remote Link (${link.linkId}): ${detail}.` });
|
|
212
|
+
try {
|
|
213
|
+
const { status, body } = await readLinkHomeJson(link, "/api/providers", deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {});
|
|
214
|
+
if (status !== 200) {
|
|
215
|
+
// Keep the Home's own sentence (for example the upgrade instruction an older Home yields).
|
|
216
|
+
const message = body && typeof body === "object" && typeof (body as { error?: unknown }).error === "string"
|
|
217
|
+
? (body as { error: string }).error : null;
|
|
218
|
+
return unavailable(message ?? `the Home answered HTTP ${status}`);
|
|
219
|
+
}
|
|
220
|
+
if (!Array.isArray(body)) return unavailable("the Home returned an unreadable provider list");
|
|
221
|
+
const providers: Record<string, unknown> = {};
|
|
222
|
+
for (const row of body) {
|
|
223
|
+
if (!row || typeof row !== "object" || typeof (row as { name?: unknown }).name !== "string") continue;
|
|
224
|
+
const { name, hasApiKey, ...rest } = row as Record<string, unknown> & { name: string };
|
|
225
|
+
// A placeholder marks presence for classification; no credential value crosses the link.
|
|
226
|
+
providers[name] = { ...rest, ...(hasApiKey === true ? { apiKey: "home-credential" } : {}) };
|
|
227
|
+
}
|
|
228
|
+
return { config: { ...local, providers: providers as OcxConfig["providers"] } };
|
|
229
|
+
} catch (error) {
|
|
230
|
+
return { error: error instanceof Error ? error.message : String(error) };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
169
234
|
export function proxyUnreachable(transportError?: string): number {
|
|
170
235
|
console.error("Proxy not reachable. Start it with 'ocx start' or 'ocx ensure'.");
|
|
171
236
|
// Naming the transport cause distinguishes "nothing is listening" from a refused
|
package/src/cli/account-auth.ts
CHANGED
|
@@ -441,6 +441,18 @@ async function resetCredits(argv: string[], deps: RuntimeApiDeps): Promise<void>
|
|
|
441
441
|
printData(result, wantsJson);
|
|
442
442
|
}
|
|
443
443
|
|
|
444
|
+
/** Codes that leave a coupon operation unconfirmed: the caller must keep its operationId. */
|
|
445
|
+
const UNCONFIRMED_COUPON_CODES: ReadonlySet<string> = new Set([
|
|
446
|
+
"attempt_unresolved", "attempt_in_progress", "attempt_reconcile_failed", "operation_state_changed",
|
|
447
|
+
"redeem_failed", "attempt_mark_failed", "operation_token_mismatch",
|
|
448
|
+
]);
|
|
449
|
+
/** Pre-dispatch ledger refusals: unconfirmed when anchored to an operation, never a settled code. */
|
|
450
|
+
const LEDGER_REFUSAL_CODES: ReadonlySet<string> = new Set(["ledger_unavailable", "capacity"]);
|
|
451
|
+
/** The only codes the ledger records as a definitive outcome; any other settled code is unconfirmed. */
|
|
452
|
+
const DEFINITIVE_SETTLED_CODES: ReadonlySet<string> = new Set([
|
|
453
|
+
"redeemed", "auth_failed", "fetch_resets_failed", "no_coupons_available", "coupon_unavailable", "token_unresolved",
|
|
454
|
+
]);
|
|
455
|
+
|
|
444
456
|
async function grokResetCoupons(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
445
457
|
const args = [...argv];
|
|
446
458
|
// The account id is optional here (the server falls back to the selected xAI
|
|
@@ -467,6 +479,7 @@ async function grokResetCoupons(argv: string[], deps: RuntimeApiDeps): Promise<v
|
|
|
467
479
|
rejectArgs(args, USAGE);
|
|
468
480
|
const accountId = rawId ? (rawId === "main" ? "__main__" : rawId) : undefined;
|
|
469
481
|
const effectiveOperationId = consume ? operationId ?? crypto.randomUUID() : undefined;
|
|
482
|
+
const unconfirmed = () => new RuntimeApiError(`Coupon redemption outcome is unconfirmed. Preserve --operation-id ${effectiveOperationId}; reuse it to inspect this attempt and do not create a replacement operation.`, 503, null);
|
|
470
483
|
let result: unknown;
|
|
471
484
|
try {
|
|
472
485
|
result = consume
|
|
@@ -476,15 +489,26 @@ async function grokResetCoupons(argv: string[], deps: RuntimeApiDeps): Promise<v
|
|
|
476
489
|
: await runtimeRequest(`/api/grok/reset-coupons${accountId ? `?accountId=${encodeURIComponent(accountId)}` : ""}`, {}, deps);
|
|
477
490
|
} catch (error) {
|
|
478
491
|
const apiError = error instanceof RuntimeApiError ? error : undefined;
|
|
479
|
-
const body = apiError?.body as { error?: { code?: string } } | null | undefined;
|
|
492
|
+
const body = apiError?.body as { operationId?: unknown; error?: { code?: string } } | null | undefined;
|
|
480
493
|
const code = body && typeof body === "object" ? body.error?.code : undefined;
|
|
494
|
+
const carriesOperation = !!body && typeof body === "object" && typeof body.operationId === "string";
|
|
481
495
|
if (consume && (!apiError || (!apiError.code && (
|
|
482
|
-
(!code && apiError.status >= 500) ||
|
|
496
|
+
(!code && apiError.status >= 500) || UNCONFIRMED_COUPON_CODES.has(code ?? "")
|
|
497
|
+
|| (LEDGER_REFUSAL_CODES.has(code ?? "") && carriesOperation)
|
|
483
498
|
)))) {
|
|
484
499
|
throw new RuntimeApiError(`Coupon redemption outcome is unconfirmed. Preserve --operation-id ${effectiveOperationId}; reuse it to inspect this attempt and do not create a replacement operation.`, apiError?.status ?? 503, apiError?.body ?? null);
|
|
485
500
|
}
|
|
486
501
|
throw error;
|
|
487
502
|
}
|
|
503
|
+
if (consume) {
|
|
504
|
+
// A 200 can replay a recorded refusal; only "redeemed" is a successful spend.
|
|
505
|
+
const settled = result && typeof result === "object" ? (result as { code?: unknown }).code : undefined;
|
|
506
|
+
if (typeof settled !== "string" || !DEFINITIVE_SETTLED_CODES.has(settled)) throw unconfirmed();
|
|
507
|
+
if (settled !== "redeemed") {
|
|
508
|
+
printData(result, wantsJson);
|
|
509
|
+
throw new RuntimeApiError(`Coupon was not redeemed (${settled}).`, 409, result);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
488
512
|
printData(result, wantsJson);
|
|
489
513
|
}
|
|
490
514
|
|
package/src/cli/account.ts
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
cmdRoutes,
|
|
22
22
|
cmdStrategy,
|
|
23
23
|
} from "./account-extended";
|
|
24
|
-
import { apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
|
|
24
|
+
import { accountClassificationConfig, apiError, apiJson, classifyAccount, fetchRows, proxyUnreachable, resolveBaseUrl, type AccountDeps, type AccountRow, type AccountType, type ApiResult }
|
|
25
25
|
from "./account-api";
|
|
26
26
|
|
|
27
27
|
export { classifyAccount } from "./account-api";
|
|
@@ -199,7 +199,12 @@ async function cmdList(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
199
199
|
console.error(ACCOUNT_USAGE);
|
|
200
200
|
return 1;
|
|
201
201
|
}
|
|
202
|
-
const
|
|
202
|
+
const classification = await accountClassificationConfig(deps, deps.loadConfigImpl?.() ?? loadConfig());
|
|
203
|
+
if ("error" in classification) {
|
|
204
|
+
console.error(`Error: ${classification.error}`);
|
|
205
|
+
return 1;
|
|
206
|
+
}
|
|
207
|
+
const config = classification.config;
|
|
203
208
|
const baseUrl = await resolveBaseUrl(deps);
|
|
204
209
|
if (!baseUrl) return proxyUnreachable();
|
|
205
210
|
|
|
@@ -289,7 +294,12 @@ async function cmdCurrent(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
289
294
|
console.error(ACCOUNT_USAGE);
|
|
290
295
|
return 1;
|
|
291
296
|
}
|
|
292
|
-
const
|
|
297
|
+
const classification = await accountClassificationConfig(deps, deps.loadConfigImpl?.() ?? loadConfig());
|
|
298
|
+
if ("error" in classification) {
|
|
299
|
+
console.error(`Error: ${classification.error}`);
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
const config = classification.config;
|
|
293
303
|
const c = classifyAccount(config, name);
|
|
294
304
|
if ("error" in c) {
|
|
295
305
|
console.error(`Error: ${c.error}. Known candidates: ${candidateNames(config)}`);
|
|
@@ -332,7 +342,20 @@ async function cmdUse(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
332
342
|
console.error(ACCOUNT_USAGE);
|
|
333
343
|
return 1;
|
|
334
344
|
}
|
|
335
|
-
|
|
345
|
+
// #6957: an account change on a link Child is refused before any Home read.
|
|
346
|
+
if (!deps.baseUrl) {
|
|
347
|
+
const refusal = (await import("./link-child")).linkChildProviderChangeRefusal("ocx account use");
|
|
348
|
+
if (refusal) {
|
|
349
|
+
console.error(`Error: ${refusal}`);
|
|
350
|
+
return 1;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const classification = await accountClassificationConfig(deps, deps.loadConfigImpl?.() ?? loadConfig());
|
|
354
|
+
if ("error" in classification) {
|
|
355
|
+
console.error(`Error: ${classification.error}`);
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
358
|
+
const config = classification.config;
|
|
336
359
|
const c = classifyAccount(config, name);
|
|
337
360
|
if ("error" in c) {
|
|
338
361
|
console.error(`Error: ${c.error}. Known candidates: ${candidateNames(config)}`);
|
|
@@ -392,7 +415,20 @@ async function cmdClear(rest: string[], deps: AccountDeps): Promise<number> {
|
|
|
392
415
|
console.error(ACCOUNT_USAGE);
|
|
393
416
|
return 1;
|
|
394
417
|
}
|
|
395
|
-
|
|
418
|
+
// #6957: an account change on a link Child is refused before any Home read.
|
|
419
|
+
if (!deps.baseUrl) {
|
|
420
|
+
const refusal = (await import("./link-child")).linkChildProviderChangeRefusal("ocx account clear");
|
|
421
|
+
if (refusal) {
|
|
422
|
+
console.error(`Error: ${refusal}`);
|
|
423
|
+
return 1;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const classification = await accountClassificationConfig(deps, deps.loadConfigImpl?.() ?? loadConfig());
|
|
427
|
+
if ("error" in classification) {
|
|
428
|
+
console.error(`Error: ${classification.error}`);
|
|
429
|
+
return 1;
|
|
430
|
+
}
|
|
431
|
+
const config = classification.config;
|
|
396
432
|
const c = classifyAccount(config, name);
|
|
397
433
|
if ("error" in c) {
|
|
398
434
|
console.error(`Error: ${c.error}. Known candidates: ${candidateNames(config)}`);
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { ADVISOR_CONTEXT_SHARING_CONSENT_VERSION, ADVISOR_CONTEXT_SHARING_DISCLOSURE } from "../advisor/disclosure";
|
|
2
|
+
import { CliUsageError, printData, rejectArgs, runCliAction, runtimeRequest, takeFlag, type RuntimeApiDeps } from "./runtime-api";
|
|
3
|
+
|
|
4
|
+
const USAGE = `Usage:
|
|
5
|
+
ocx advisor status [--json]
|
|
6
|
+
ocx advisor on [--ack-context-sharing] [--json]
|
|
7
|
+
ocx advisor off [--json]
|
|
8
|
+
ocx advisor consent [--revoke] [--json]
|
|
9
|
+
ocx advisor set [--model <model>] [--effort <effort>] [--policy <manual|preflight>] [--timeout-ms <ms>] [--json]`;
|
|
10
|
+
|
|
11
|
+
const VALUED_FLAGS = new Set(["--model", "--effort", "--policy", "--timeout-ms"]);
|
|
12
|
+
|
|
13
|
+
interface SetOptions {
|
|
14
|
+
model?: string;
|
|
15
|
+
effort?: string;
|
|
16
|
+
policy?: string;
|
|
17
|
+
timeoutMs?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function parseSetArgs(args: string[]): SetOptions {
|
|
21
|
+
const options: SetOptions = {};
|
|
22
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
23
|
+
const arg = args[index]!;
|
|
24
|
+
if (!VALUED_FLAGS.has(arg)) throw new CliUsageError(`unknown advisor set option ${arg}`, USAGE);
|
|
25
|
+
const value = args[++index];
|
|
26
|
+
if (value === undefined || value.startsWith("--")) throw new CliUsageError(`${arg} requires a value`, USAGE);
|
|
27
|
+
// An explicitly supplied empty value is meaningful for --model alone: the settings route
|
|
28
|
+
// treats "" as the supported "clear the model" state. Every other valued flag still needs one.
|
|
29
|
+
if (value === "" && arg !== "--model") throw new CliUsageError(`${arg} requires a value`, USAGE);
|
|
30
|
+
if (arg === "--model") options.model = value;
|
|
31
|
+
else if (arg === "--effort") options.effort = value;
|
|
32
|
+
else if (arg === "--policy") options.policy = value;
|
|
33
|
+
else options.timeoutMs = value;
|
|
34
|
+
}
|
|
35
|
+
if (Object.keys(options).length === 0) {
|
|
36
|
+
throw new CliUsageError("advisor set requires at least one of --model, --effort, --policy, --timeout-ms", USAGE);
|
|
37
|
+
}
|
|
38
|
+
return options;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function status(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
42
|
+
const args = [...argv];
|
|
43
|
+
const wantsJson = takeFlag(args, "--json");
|
|
44
|
+
rejectArgs(args, USAGE);
|
|
45
|
+
printData(await runtimeRequest("/api/advisor/settings", {}, deps), wantsJson);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function printDisclosure(): void {
|
|
49
|
+
console.error(ADVISOR_CONTEXT_SHARING_DISCLOSURE);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function readConsent(deps: RuntimeApiDeps): Promise<unknown> {
|
|
53
|
+
const current = await runtimeRequest("/api/advisor/settings", {}, deps) as {
|
|
54
|
+
settings?: { contextSharingConsent?: unknown };
|
|
55
|
+
};
|
|
56
|
+
return current.settings?.contextSharingConsent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function setEnabled(enabled: boolean, argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
60
|
+
const args = [...argv];
|
|
61
|
+
const wantsJson = takeFlag(args, "--json");
|
|
62
|
+
const acknowledge = takeFlag(args, "--ack-context-sharing");
|
|
63
|
+
rejectArgs(args, USAGE);
|
|
64
|
+
if (!enabled) {
|
|
65
|
+
if (acknowledge) {
|
|
66
|
+
throw new CliUsageError(
|
|
67
|
+
"--ack-context-sharing has no effect with `ocx advisor off`; it never records consent.",
|
|
68
|
+
USAGE,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
printData(await runtimeRequest("/api/advisor/settings", {
|
|
72
|
+
method: "PUT",
|
|
73
|
+
body: JSON.stringify({ enabled: false }),
|
|
74
|
+
}, deps), wantsJson, ["Advisor disabled."]);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const consent = await readConsent(deps);
|
|
78
|
+
if (acknowledge) {
|
|
79
|
+
printDisclosure();
|
|
80
|
+
printData(await runtimeRequest("/api/advisor/settings", {
|
|
81
|
+
method: "PUT",
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
enabled: true,
|
|
84
|
+
contextSharingConsent: ADVISOR_CONTEXT_SHARING_CONSENT_VERSION,
|
|
85
|
+
}),
|
|
86
|
+
}, deps), wantsJson, ["Advisor enabled with context-sharing consent v1."]);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (consent !== ADVISOR_CONTEXT_SHARING_CONSENT_VERSION) {
|
|
90
|
+
printDisclosure();
|
|
91
|
+
throw new CliUsageError(
|
|
92
|
+
"Advisor context-sharing consent is required before consultations can send task content. "
|
|
93
|
+
+ "Re-run `ocx advisor on --ack-context-sharing` after reading the disclosure, or run `ocx advisor consent`.",
|
|
94
|
+
USAGE,
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
printData(await runtimeRequest("/api/advisor/settings", {
|
|
98
|
+
method: "PUT",
|
|
99
|
+
body: JSON.stringify({ enabled: true }),
|
|
100
|
+
}, deps), wantsJson, ["Advisor enabled."]);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function consent(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
104
|
+
const args = [...argv];
|
|
105
|
+
const wantsJson = takeFlag(args, "--json");
|
|
106
|
+
const revoke = takeFlag(args, "--revoke");
|
|
107
|
+
rejectArgs(args, USAGE);
|
|
108
|
+
if (!revoke) printDisclosure();
|
|
109
|
+
printData(await runtimeRequest("/api/advisor/settings", {
|
|
110
|
+
method: "PUT",
|
|
111
|
+
body: JSON.stringify({
|
|
112
|
+
contextSharingConsent: revoke ? null : ADVISOR_CONTEXT_SHARING_CONSENT_VERSION,
|
|
113
|
+
}),
|
|
114
|
+
}, deps), wantsJson, [revoke
|
|
115
|
+
? "Advisor context-sharing consent removed. Consultations will not send task content."
|
|
116
|
+
: "Advisor context-sharing consent v1 recorded.",
|
|
117
|
+
]);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function set(argv: string[], deps: RuntimeApiDeps): Promise<void> {
|
|
121
|
+
const args = [...argv];
|
|
122
|
+
const wantsJson = takeFlag(args, "--json");
|
|
123
|
+
const options = parseSetArgs(args);
|
|
124
|
+
const patch: Record<string, unknown> = {};
|
|
125
|
+
if (options.model !== undefined) patch.model = options.model;
|
|
126
|
+
if (options.effort !== undefined) patch.effort = options.effort;
|
|
127
|
+
if (options.policy !== undefined) patch.policy = options.policy;
|
|
128
|
+
if (options.timeoutMs !== undefined) {
|
|
129
|
+
const parsed = Number(options.timeoutMs);
|
|
130
|
+
if (!Number.isFinite(parsed)) throw new CliUsageError("--timeout-ms must be a number", USAGE);
|
|
131
|
+
patch.timeoutMs = parsed;
|
|
132
|
+
}
|
|
133
|
+
printData(await runtimeRequest("/api/advisor/settings", {
|
|
134
|
+
method: "PUT",
|
|
135
|
+
body: JSON.stringify(patch),
|
|
136
|
+
}, deps), wantsJson, ["Advisor settings saved."]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function handleAdvisorCommand(argv: string[], deps: RuntimeApiDeps = {}): Promise<number> {
|
|
140
|
+
return runCliAction(async () => {
|
|
141
|
+
const [sub = "status", ...rest] = argv;
|
|
142
|
+
if (sub === "status") await status(rest, deps);
|
|
143
|
+
else if (sub === "on") await setEnabled(true, rest, deps);
|
|
144
|
+
else if (sub === "off") await setEnabled(false, rest, deps);
|
|
145
|
+
else if (sub === "consent") await consent(rest, deps);
|
|
146
|
+
else if (sub === "set") await set(rest, deps);
|
|
147
|
+
else throw new CliUsageError(`unknown advisor command ${sub}`, USAGE);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const ADVISOR_USAGE = USAGE;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { OcxConfig } from "../types";
|
|
2
|
+
import { AuxiliaryListenerBindError } from "../server/ports";
|
|
3
|
+
import { createServer } from "node:net";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reject impossible auxiliary binds before startup creates private state. On Windows
|
|
7
|
+
* that publication requires several synchronous ACL subprocesses. This is only an
|
|
8
|
+
* early rejection: startServer still owns the actual bind and transactional rollback
|
|
9
|
+
* if another process takes the port between this probe and startup.
|
|
10
|
+
*/
|
|
11
|
+
export async function assertAuxiliaryListenerPortsAvailable(config: OcxConfig): Promise<void> {
|
|
12
|
+
const loopback = config.unauthenticatedLoopbackListener;
|
|
13
|
+
const management = config.runtimeRole === "hub" ? config.hub?.managementIngress : undefined;
|
|
14
|
+
const targets = [
|
|
15
|
+
{ listener: "unauthenticatedLoopbackListener", port: loopback?.enabled ? loopback.port : undefined },
|
|
16
|
+
{ listener: "hub.managementIngress", port: management?.enabled ? management.port : undefined },
|
|
17
|
+
] as const;
|
|
18
|
+
// Each probe releases its socket, so two listeners configured for one port would both
|
|
19
|
+
// pass and startup would still fail on the second bind. Reject the collision directly.
|
|
20
|
+
if (targets[0].port !== undefined && targets[0].port === targets[1].port) {
|
|
21
|
+
throw new AuxiliaryListenerBindError(
|
|
22
|
+
"hub.managementIngress", targets[1].port, "127.0.0.1",
|
|
23
|
+
new Error("port is also configured for unauthenticatedLoopbackListener"),
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
for (const { listener, port } of targets) {
|
|
27
|
+
// The port-less companion shares the public bind; it has no independent target.
|
|
28
|
+
if (port === undefined) continue;
|
|
29
|
+
await new Promise<void>((resolve, reject) => {
|
|
30
|
+
const probe = createServer(socket => socket.destroy());
|
|
31
|
+
const fail = (cause: unknown) => reject(new AuxiliaryListenerBindError(listener, port, "127.0.0.1", cause));
|
|
32
|
+
probe.once("error", fail);
|
|
33
|
+
probe.once("listening", () => probe.close(error => error ? fail(error) : resolve()));
|
|
34
|
+
try { probe.listen({ port, host: "127.0.0.1" }); } catch (error) { fail(error); }
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -2,6 +2,14 @@ import type { Capability } from "./capability-types";
|
|
|
2
2
|
|
|
3
3
|
/** Verified existing handlers only; local operations never claim management routes. */
|
|
4
4
|
export const ACCESS_REMOTE_CAPABILITIES: readonly Capability[] = [
|
|
5
|
+
{
|
|
6
|
+
command: ["hub", "disable"], summary: "Return a hub to standalone in one validated config write.",
|
|
7
|
+
usage: "ocx hub disable [--revoke-clients --client-key <id> ...] [--json]",
|
|
8
|
+
routes: [],
|
|
9
|
+
flags: [{ name: "--revoke-clients", value: "boolean", summary: "Revoke only explicitly selected saved client key IDs." }, { name: "--client-key", value: "string", summary: "Exact saved key ID to revoke; repeatable, requires --revoke-clients." }, { name: "--json", value: "boolean", summary: "Emit the result and restart guidance as JSON." }],
|
|
10
|
+
mutates: true, json: "envelope",
|
|
11
|
+
details: ["Removes hub, unauthenticatedLoopbackListener, runtimeRole and hostname atomically after validation; non-hub roles refuse without changes.", "Existing keys have no verified client origin. Names never select keys; --revoke-clients requires exact --client-key IDs when keys exist. Unselected keys remain.", "Prints only safe key metadata, never plaintext keys or pending rotations. Restart advice is informational; the running proxy and external publishing are unchanged."],
|
|
12
|
+
},
|
|
5
13
|
{
|
|
6
14
|
command: ["access", "key"], summary: "Manage API access keys and inspect masked metadata.",
|
|
7
15
|
usage: "ocx access key list [--json]",
|
|
@@ -25,6 +25,24 @@ export const AGENT_ROUTING_CAPABILITIES: readonly Capability[] = [
|
|
|
25
25
|
"queued means submitted, not processed. unknown must not be replayed; no automatic retry, daemon start or thread resume.",
|
|
26
26
|
"Exit 0: queued; 1: not sent; 3: unknown; 64: invalid usage. No remote/Claude transport or skill installation."],
|
|
27
27
|
},
|
|
28
|
+
{
|
|
29
|
+
command: ["advisor"],
|
|
30
|
+
summary: "Inspect and configure the advisor sidecar (expert consultation for routed workers).",
|
|
31
|
+
routes: [
|
|
32
|
+
{ method: "GET", path: "/api/advisor/settings" },
|
|
33
|
+
{ method: "PUT", path: "/api/advisor/settings" },
|
|
34
|
+
],
|
|
35
|
+
flags: [{ name: "--json", value: "boolean", summary: "Emit advisor settings as JSON." }],
|
|
36
|
+
mutates: true,
|
|
37
|
+
json: "payload",
|
|
38
|
+
details: [
|
|
39
|
+
"`status` (the default) reads the resolved settings; `on`/`off` toggle the sidecar; `consent` records or revokes context-sharing consent; `set` updates model, effort, policy, or timeout.",
|
|
40
|
+
"`on` does not grant consent. Without current consent it refuses and prints the disclosure. `on --ack-context-sharing` records consent v1 and enables. `consent --revoke` removes consent and stops task-context transfer.",
|
|
41
|
+
"The advisor model may be any routable model string: a bare native model, an explicit `provider/model`, or an account-qualified native model.",
|
|
42
|
+
"`policy: preflight` makes OpenCodex attempt one automatic consultation per task with a stable conversation identity once the task shows orientation evidence (an assistant tool call or a tool result after the latest user message). Without a stable identity, each eligible request may trigger another consultation. `policy: manual` consults only when the worker calls the synthetic `advisor` tool. Neither path sends task context without current context-sharing consent.",
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
|
|
28
46
|
{
|
|
29
47
|
command: ["agent", "status"],
|
|
30
48
|
usage: "ocx agent status [--json]",
|
|
@@ -26,6 +26,8 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
26
26
|
bannerLines: ["ocx chatgpt <sub> Experimental app-server shim: launch|restore|status (macOS)"],
|
|
27
27
|
details: ["Default off; launch requires chatgptDesktop.appServerShim: true. Restore removes the generated launcher."],
|
|
28
28
|
},
|
|
29
|
+
{"command": ["link","probe"],"summary": "Probe an SSH host fingerprint and OpenCodex version without trusting it.","routes": [{"method": "POST","path": "/api/link/probe"}],"flags": [{"name": "--json","value": "boolean","summary": "Emit alias, fingerprint, key type and version as JSON."}],"mutates": true,"json": "payload","usage": "ocx link probe <alias> [--json]","details": ["Requires loopback admin authority on the Home. Stores only a temporary pending offer; does not persist host trust or issue a link key."]},
|
|
30
|
+
{"command": ["link","add"],"summary": "Connect an SSH Child to this Home after confirming its fingerprint.","routes": [{"method": "POST","path": "/api/link/probe"},{"method": "POST","path": "/api/link/confirm-host"},{"method": "POST","path": "/api/link/apply"}],"flags": [{"name": "--expect-fingerprint","value": "string","summary": "Exact SHA256 host fingerprint; required without an interactive TTY."},{"name": "--json","value": "boolean","summary": "Emit the verified link result as JSON."}],"mutates": true,"json": "payload","usage": "ocx link add <alias> [--expect-fingerprint SHA256:…] [--json]","details": ["Requires loopback admin authority on the Home. Without a fingerprint, requires a TTY and explicit yes after showing the offered fingerprint. Never prints the link key.","Preflights bound Child restart support and curl, connects, restarts once and verifies a fresh authenticated catalog relay before success. Post-connect failures can leave Child config; run ocx disconnect and ocx restart there before retrying."]},
|
|
29
31
|
{
|
|
30
32
|
command: ["link","port"],
|
|
31
33
|
summary: "Allocate a free loopback port for a remote home link.",
|
|
@@ -65,6 +67,16 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
65
67
|
details: ["Requires a valid link ID and loopback admin authority. Explicit force additionally requires --yes.","Ordinary revoke always emits {linkId} JSON; forced revoke has human output or a JSON cleanup receipt. Already-revoked link_not_found is idempotent success.","Ordinary revoke retains its no-body and idempotent behavior. Forced success reports remoteCleanup:skipped from invocation intent; already-missing link reports unverified. Neither proves the remote client disconnected.","If forced cleanup was needed, run ocx disconnect on the remote client. No new enrollment, SSH trust or credential action is implied."],
|
|
66
68
|
usage: "ocx link revoke --link-id <id> [--force --yes] [--json]",
|
|
67
69
|
},
|
|
70
|
+
{
|
|
71
|
+
command: ["link","reconnect"],
|
|
72
|
+
summary: "Restart a Home-initiated link's SSH tunnel now.",
|
|
73
|
+
routes: [{"method":"POST","path":"/api/link/{id}/reconnect"}],
|
|
74
|
+
flags: [{"name":"--link-id","value":"string","required":true,"summary":"Home-initiated link id whose tunnel to restart."},{"name":"--json","value":"boolean","summary":"Emit the restarted link id as JSON."}],
|
|
75
|
+
mutates: true,
|
|
76
|
+
json: "envelope",
|
|
77
|
+
usage: "ocx link reconnect --link-id <id> [--json]",
|
|
78
|
+
details: ["Requires a valid link ID and loopback admin authority. Stops the current ssh child, clears any backoff or failed state and starts a fresh attempt; read ocx link status for the outcome.","A Child-initiated link is refused with link_not_home_initiated: its own computer owns and retries that tunnel.","Always emits {linkId} JSON."],
|
|
79
|
+
},
|
|
68
80
|
{
|
|
69
81
|
command: ["remote-workspace","pair"],
|
|
70
82
|
summary: "Enroll this executor with one Hub using a one-time code from stdin and locally approved roots.",
|
|
@@ -717,7 +729,7 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
717
729
|
flags: [{"name":"--json","value":"boolean","summary":"Emit the role list, the write result or the proposals as JSON."},{"name":"--effort","value":"string","summary":"set: also write the role's reasoning effort."},{"name":"--model","value":"string","summary":"suggest: size the roles with this model instead of the Codex default model."},{"name":"--apply","value":"boolean","summary":"suggest: write every proposal through the role model write."}],
|
|
718
730
|
mutates: true,
|
|
719
731
|
json: "payload",
|
|
720
|
-
details: ["A bare invocation reads and never writes.","Requires Codex-based omo (LazyCodex): the omo@sisyphuslabs Codex plugin enabled in config.toml and installed; otherwise status lists no roles, and set and suggest are refused.","set rewrites only the root model value of $CODEX_HOME/agents/<role>.toml, plus model_reasoning_effort with --effort; omo.jsonc gets the same model and, when LazyCodex has that level, the effort as reasoning; it is skipped when absent or when it contains comments.","suggest sizes every role with one model call and prints proposals without writing; --apply writes each proposed model
|
|
732
|
+
details: ["A bare invocation reads and never writes.","Requires Codex-based omo (LazyCodex): the omo@sisyphuslabs Codex plugin enabled in config.toml and installed; otherwise status lists no roles, and set and suggest are refused.","set rewrites only the root model value of $CODEX_HOME/agents/<role>.toml, plus model_reasoning_effort with --effort; omo.jsonc gets the same model and, when LazyCodex has that level, the effort as reasoning; it is skipped when absent or when it contains comments.","suggest sizes every role with one model call and prints proposals without writing; --apply writes each proposed model and effort."],
|
|
721
733
|
usage: "ocx agent roles [status] [--json]; ocx agent roles set <role> <model> [--effort <level>] [--json]; ocx agent roles suggest [--model <id>] [--apply] [--json]",
|
|
722
734
|
},
|
|
723
735
|
{
|
|
@@ -774,7 +786,7 @@ export const CAPABILITIES: readonly Capability[] = [
|
|
|
774
786
|
command: ["api","policy"],
|
|
775
787
|
summary: "Read the protocol policy, or change the Messages surface, unrepresentable policy and rollout switches.",
|
|
776
788
|
routes: [{"method":"GET","path":"/api/protocols"},{"method":"PATCH","path":"/api/protocols/settings"}],
|
|
777
|
-
flags: [{"name":"--messages","value":"string","summary":"Open or close the Messages API: on or off. Off also turns the Claude integration off."},{"name":"--unrepresentable","value":"string","summary":"legacy keeps today's behavior; reject refuses a request its path cannot carry."},{"name":"--rollout","value":"string","summary":"One switch as name=on or name=off; repeatable. Every switch defaults off."},{"name":"--json","value":"boolean","summary":"Emit the resulting GET /api/protocols body."}],
|
|
789
|
+
flags: [{"name":"--messages","value":"string","summary":"Open or close the Messages API: on or off. Off also turns the Claude integration off."},{"name":"--unrepresentable","value":"string","summary":"legacy keeps today's behavior; reject refuses a request its path cannot carry."},{"name":"--rollout","value":"string","summary":"One switch as name=on or name=off; repeatable. Every switch defaults off except antigravityDirect."},{"name":"--json","value":"boolean","summary":"Emit the resulting GET /api/protocols body."}],
|
|
778
790
|
mutates: true,
|
|
779
791
|
json: "payload",
|
|
780
792
|
details: ["A bare invocation reads and never writes.","A setting flag changes the operator's config; run it only when the operator asks for that change."],
|
|
@@ -23,7 +23,8 @@ export async function runCatalogAction(
|
|
|
23
23
|
if (error instanceof RuntimeApiError) {
|
|
24
24
|
if (error.code) {
|
|
25
25
|
console.error(`Error: ${error.code === "proxy_not_running" ? PROXY_NOT_RUNNING_MESSAGE
|
|
26
|
-
:
|
|
26
|
+
: error.code === "link_child_management_unavailable" ? terminalSafeText(error.message)
|
|
27
|
+
: "This listener has no management API. Run management commands on the connected Hub. No request was sent."}`);
|
|
27
28
|
return 1;
|
|
28
29
|
}
|
|
29
30
|
const code = own(error.body, "code") ?? own(own(error.body, "error"), "code");
|