mixdog 1.2.0 → 1.2.2
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 +7 -5
- package/package.json +1 -1
- package/scripts/gate-local.mjs +19 -3
- package/scripts/model-catalog-audit.mjs +1 -15
- package/src/agents/reviewer/AGENT.md +3 -3
- package/src/agents/reviewer/agent.json +1 -1
- package/src/defaults/shipped-history.json +5 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +13 -3
- package/src/defaults/skills/visualize/SKILL.md +12 -9
- package/src/repl/slash-commands.mjs +16 -1
- package/src/rules/shared/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -4
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +10 -0
- package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
- package/src/runtime/agent/orchestrator/config.mjs +10 -4
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +122 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +54 -11
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +104 -9
- package/src/runtime/agent/orchestrator/mcp/client.mjs +358 -140
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +103 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +150 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +111 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +87 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +95 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-request.mjs +122 -0
- package/src/runtime/agent/orchestrator/mcp/oauth-shared.mjs +9 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +229 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +294 -44
- package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +94 -31
- package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +96 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-fast-mode.mjs +39 -18
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +55 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +25 -21
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-midstream-recovery.mjs +18 -4
- package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-midstream.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-recovery.mjs +23 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +46 -20
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request.mjs +14 -9
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +73 -35
- package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +69 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-server-fallback.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +8 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +53 -18
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +30 -9
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +7 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +30 -14
- package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +93 -0
- package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +82 -9
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +447 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +38 -5
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-guards.mjs +97 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +102 -23
- package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +103 -33
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +29 -6
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +18 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +86 -19
- package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +29 -8
- package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +191 -0
- package/src/runtime/agent/orchestrator/providers/gemini-family.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/gemini-history-signatures.mjs +9 -4
- package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +3 -1
- package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +180 -0
- package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +21 -82
- package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +4 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +71 -12
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +21 -20
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +97 -51
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +68 -19
- package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/model-list-sanitize.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +15 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +35 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +4 -9
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +12 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +5 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +22 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +1 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +50 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +23 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-state.mjs +3 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +94 -13
- package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +193 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +66 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +6 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +6 -3
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +23 -14
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +42 -25
- package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +274 -0
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +72 -32
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-stamps.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +2 -3
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +1 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +34 -3
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +5 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +22 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +5 -0
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +5 -9
- package/src/runtime/agent/orchestrator/providers/openai-retry-policy.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/openai-ws-handshake.mjs +16 -8
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +1 -2
- package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/policy.mjs +25 -14
- package/src/runtime/agent/orchestrator/providers/openai-ws-terminal.mjs +18 -11
- package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +35 -24
- package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +581 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +35 -9
- package/src/runtime/agent/orchestrator/providers/retry-backoff.mjs +46 -7
- package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +118 -219
- package/src/runtime/agent/orchestrator/providers/retry-classifier.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +148 -45
- package/src/runtime/agent/orchestrator/providers/retry-midstream.mjs +81 -16
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +3 -0
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +0 -18
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +57 -28
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +19 -16
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +268 -181
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +270 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +102 -18
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +42 -5
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +14 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +20 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +50 -23
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +22 -14
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +18 -7
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +4 -3
- package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +155 -29
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +6 -3
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/store/write-guards.mjs +16 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +22 -10
- package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +819 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +183 -374
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +41 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +38 -17
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +12 -15
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +173 -22
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +5 -4
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +15 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/pg/process.mjs +34 -24
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -13
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/office/README.md +1 -1
- package/src/runtime/office/shared/values.mjs +0 -5
- package/src/runtime/shared/atomic-file.mjs +25 -4
- package/src/runtime/shared/background-tasks.mjs +92 -14
- package/src/runtime/shared/err-text.mjs +25 -0
- package/src/runtime/shared/error-presentation.mjs +159 -19
- package/src/runtime/shared/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/llm/anthropic-betas.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +134 -131
- package/src/runtime/shared/llm/anthropic-thinking-contract.mjs +50 -14
- package/src/runtime/shared/llm/model-catalog.mjs +53 -52
- package/src/runtime/shared/llm/openai-daybreak.mjs +4 -16
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +17 -0
- package/src/runtime/shared/llm/provider-model-identities.mjs +13 -4
- package/src/runtime/shared/llm/usage-rollup.mjs +0 -1
- package/src/runtime/shared/model-display.mjs +13 -0
- package/src/runtime/shared/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +124 -20
- package/src/runtime/shared/provider-api-key.mjs +12 -0
- package/src/runtime/shared/quota-resume-store.mjs +11 -0
- package/src/runtime/shared/session-baseline-store.mjs +960 -0
- package/src/runtime/shared/shell-display.mjs +9 -3
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-format.mjs +4 -4
- package/src/runtime/shared/task-notification-envelope.mjs +2 -2
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- package/src/runtime/shared/turn-changes.mjs +447 -219
- package/src/runtime/shared/turn-snapshot-store.mjs +182 -1
- package/src/runtime/shared/turn-snapshot.mjs +170 -48
- package/src/runtime/shared/turn-worktree-snapshot.mjs +160 -51
- package/src/runtime/shared/worker-requests.mjs +15 -2
- package/src/runtime/shared/worktrees/contract.mjs +0 -24
- package/src/runtime/shared/worktrees/test-support.mjs +19 -4
- package/src/session-runtime/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +61 -0
- package/src/session-runtime/boot/config.mjs +5 -2
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/infrastructure.mjs +35 -18
- package/src/session-runtime/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +10 -5
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- package/src/session-runtime/lifecycle/session-catalog.mjs +2 -0
- package/src/session-runtime/lifecycle/teardown.mjs +1 -1
- package/src/session-runtime/mcp-glue/connect.mjs +83 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +135 -33
- package/src/session-runtime/mcp-glue.mjs +25 -2
- package/src/session-runtime/mcp-oauth-login.mjs +174 -0
- package/src/session-runtime/model-route/model-switch-target.mjs +34 -0
- package/src/session-runtime/model-route/set-route.mjs +83 -15
- package/src/session-runtime/model-route/web-search-route.mjs +1 -1
- package/src/session-runtime/native-web-search/routes.mjs +22 -3
- package/src/session-runtime/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +9 -2
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/model-meta.mjs +12 -3
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/quick-model-rows/web-search-rows.mjs +13 -8
- package/src/session-runtime/quick-web-search-models.mjs +13 -38
- package/src/session-runtime/resource-extension-sync.mjs +24 -5
- package/src/session-runtime/resource-mcp-api.mjs +198 -78
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/runtime-review-api.mjs +13 -0
- package/src/session-runtime/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +13 -1
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +7 -2
- package/src/session-runtime/services/agent-tool/send-flow.mjs +22 -4
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +2 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +22 -5
- package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +8 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -2
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +17 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/settings-compaction-api.mjs +1 -0
- package/src/session-runtime/setup-tool/executor.mjs +12 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +1 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +7 -1
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
- package/src/session-runtime/webhook-session-run.mjs +16 -4
- package/src/skill-script-entry.mjs +36 -0
- package/src/standalone/daemon-drain-policy.mjs +8 -0
- package/src/standalone/daemon-stored-session-views.mjs +11 -3
- package/src/standalone/daemon-telemetry.mjs +55 -0
- package/src/standalone/daemon.mjs +44 -11
- package/src/standalone/session-protocol.mjs +12 -0
- package/src/standalone/session-service/agent-tree/agent-cancel.mjs +33 -2
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +26 -10
- package/src/standalone/session-service/agent-tree.mjs +12 -0
- package/src/standalone/session-service/quota-resume.mjs +8 -0
- package/src/standalone/session-service/retention/busy.mjs +15 -1
- package/src/standalone/session-service/session-calls/session-action.mjs +6 -1
- package/src/standalone/session-service-api.mjs +5 -0
- package/src/standalone/session-service.mjs +1 -0
- package/src/tui/App.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +7 -0
- package/src/tui/app/create-app-pickers.mjs +4 -0
- package/src/tui/app/extension-pickers/mcp-login.mjs +107 -0
- package/src/tui/app/extension-pickers/mcp-servers-picker.mjs +2 -1
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/extension-pickers.mjs +2 -1
- package/src/tui/app/model-options.mjs +13 -16
- package/src/tui/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-commands.mjs +7 -2
- package/src/tui/app/slash-dispatch/panel-commands.mjs +16 -1
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/StatusLine.jsx +2 -0
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/status-line/local-line.mjs +5 -2
- package/src/tui/components/status-line/use-statusline-text.mjs +7 -1
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +1323 -214
- package/src/tui/session/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +46 -14
- package/src/tui/session/session-api/commands.mjs +33 -24
- package/src/tui/session/session-api/extensions.mjs +26 -2
- package/src/tui/session/session-api/integrations.mjs +7 -0
- package/src/tui/session/session-api/lifecycle.mjs +5 -1
- package/src/tui/session/session-api/routes.mjs +71 -7
- package/src/tui/session/session-api/settings.mjs +12 -14
- package/src/tui/session/session-flow/drain.mjs +21 -5
- package/src/tui/session/session-flow/failed-turn-rewind.mjs +10 -4
- package/src/tui/session/tui-steering-persist.mjs +25 -9
- package/src/tui/session/turn.mjs +55 -15
- package/src/tui/statusline-ansi-bridge.mjs +1 -1
- package/src/ui/statusline-agents.mjs +15 -4
- package/src/ui/statusline.mjs +7 -3
- package/src/ui/streaming-markdown-heal.mjs +34 -3
- package/src/vendor/statusline/src/gateway/claude-current.mjs +14 -4
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
- package/src/runtime/memory/lib/memory-session-merge.mjs +0 -32
- package/src/runtime/shared/llm/usage-session-history.mjs +0 -266
|
@@ -85,6 +85,12 @@ const PROVIDER_CATALOGS = Object.freeze({
|
|
|
85
85
|
// Effort tiers live in the wire id; a row without levels has no effort
|
|
86
86
|
// surface (resolveAntigravityWireModel drops the effort).
|
|
87
87
|
'antigravity-oauth': { relay: true, rosterFiles: ['antigravity-oauth-models.json'], rosterLadderFinal: true },
|
|
88
|
+
// api.commandcode.ai/provider/v1 relays other vendors' models.
|
|
89
|
+
'command-code-oauth': {
|
|
90
|
+
relay: true,
|
|
91
|
+
rosterFiles: ['command-code-oauth-models.json'],
|
|
92
|
+
rosterLimitsAuthoritative: true,
|
|
93
|
+
},
|
|
88
94
|
'cursor-oauth': { relay: true },
|
|
89
95
|
'cursor-api': { relay: true },
|
|
90
96
|
'opencode-go': { relay: true },
|
|
@@ -126,6 +132,17 @@ export function providerCatalogEntry(provider) {
|
|
|
126
132
|
};
|
|
127
133
|
}
|
|
128
134
|
|
|
135
|
+
/** models.dev ids of the registered non-relay providers: vendors that serve their own models. */
|
|
136
|
+
export function firstPartyModelsDevIds() {
|
|
137
|
+
return [
|
|
138
|
+
...new Set(
|
|
139
|
+
Object.values(PROVIDER_CATALOGS)
|
|
140
|
+
.filter((entry) => entry.relay !== true && entry.modelsDev)
|
|
141
|
+
.map((entry) => entry.modelsDev)
|
|
142
|
+
),
|
|
143
|
+
].sort();
|
|
144
|
+
}
|
|
145
|
+
|
|
129
146
|
export function modelsDevProviderId(provider) {
|
|
130
147
|
return providerCatalogEntry(provider)?.modelsDev || null;
|
|
131
148
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Model identities are shared by transport and pricing. Keep this module
|
|
2
2
|
// independent of credentials, tracing and provider initialization.
|
|
3
|
-
|
|
3
|
+
// `levels` names the thinkingLevel values a single-wire model takes.
|
|
4
|
+
function fallbackModel(id, display, family, contextWindow, wire, levels = []) {
|
|
4
5
|
const tiered = wire && typeof wire === 'object';
|
|
5
6
|
return {
|
|
6
7
|
id,
|
|
@@ -12,7 +13,7 @@ function fallbackModel(id, display, family, contextWindow, wire) {
|
|
|
12
13
|
supportsVision: true,
|
|
13
14
|
supportsReasoning: true,
|
|
14
15
|
supportsFunctionCalling: true,
|
|
15
|
-
reasoningLevels: tiered ? Object.keys(wire) : [],
|
|
16
|
+
reasoningLevels: tiered ? Object.keys(wire) : [...levels],
|
|
16
17
|
wire,
|
|
17
18
|
};
|
|
18
19
|
}
|
|
@@ -27,8 +28,16 @@ export const ANTIGRAVITY_MODELS = Object.freeze([
|
|
|
27
28
|
low: 'gemini-3.1-pro-low',
|
|
28
29
|
high: 'gemini-3.1-pro-high',
|
|
29
30
|
}),
|
|
30
|
-
fallbackModel('claude-opus-4-6-thinking', 'Claude Opus 4.6 (Thinking)', null, 250000, 'claude-opus-4-6-thinking'
|
|
31
|
-
|
|
31
|
+
fallbackModel('claude-opus-4-6-thinking', 'Claude Opus 4.6 (Thinking)', null, 250000, 'claude-opus-4-6-thinking', [
|
|
32
|
+
'low',
|
|
33
|
+
'medium',
|
|
34
|
+
'high',
|
|
35
|
+
]),
|
|
36
|
+
fallbackModel('claude-sonnet-4-6', 'Claude Sonnet 4.6 (Thinking)', null, 250000, 'claude-sonnet-4-6', [
|
|
37
|
+
'low',
|
|
38
|
+
'medium',
|
|
39
|
+
'high',
|
|
40
|
+
]),
|
|
32
41
|
fallbackModel('gpt-oss-120b-medium', 'GPT-OSS 120B (Medium)', 'gpt-oss', 131072, 'gpt-oss-120b-medium'),
|
|
33
42
|
]);
|
|
34
43
|
|
|
@@ -59,7 +59,6 @@ const MAX_SESSION_TOKENS_PER_DAY = 400;
|
|
|
59
59
|
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
60
60
|
const UNSAFE_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
|
|
61
61
|
|
|
62
|
-
// Shared with usage-session-history.mjs, which folds the same bucket shape.
|
|
63
62
|
export function num(value) {
|
|
64
63
|
const n = Number(value);
|
|
65
64
|
return Number.isFinite(n) && n > 0 ? n : 0;
|
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
* No I/O, catalog, or gateway imports — safe for static TUI bundling.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
const titleCaseOption = (value) =>
|
|
7
|
+
String(value || '')
|
|
8
|
+
.split(/([\s_-]+)/)
|
|
9
|
+
.map((part) => (/^[\s_-]+$/.test(part) ? part : `${part.charAt(0).toUpperCase()}${part.slice(1).toLowerCase()}`))
|
|
10
|
+
.join('');
|
|
11
|
+
|
|
12
|
+
export const effortDisplayLabel = (value) => {
|
|
13
|
+
const text = String(value || '').trim();
|
|
14
|
+
if (!text) return '';
|
|
15
|
+
if (text.toLowerCase() === 'xhigh') return 'XHigh';
|
|
16
|
+
return titleCaseOption(text);
|
|
17
|
+
};
|
|
18
|
+
|
|
6
19
|
export function titleModelPart(part) {
|
|
7
20
|
const text = String(part || '').trim();
|
|
8
21
|
if (!text) return '';
|
|
@@ -7,10 +7,19 @@
|
|
|
7
7
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
8
8
|
import { readJsonSafe } from './json-file.mjs';
|
|
9
9
|
|
|
10
|
-
/** `.codex-plugin/plugin.json
|
|
11
|
-
*
|
|
10
|
+
/** Manifest files in precedence order: `.codex-plugin/plugin.json`, a
|
|
11
|
+
* root-level `plugin.json`, then Claude Code's `.claude-plugin/plugin.json`. */
|
|
12
|
+
export const PLUGIN_MANIFEST_FILES = ['.codex-plugin/plugin.json', 'plugin.json', '.claude-plugin/plugin.json'];
|
|
13
|
+
|
|
14
|
+
/** First readable manifest in `PLUGIN_MANIFEST_FILES` order; a plugin without
|
|
15
|
+
* any is an empty manifest, never an error. A Claude Code manifest's `skills`
|
|
16
|
+
* is a plugin-relative path (e.g. `./.claude/skills/`). */
|
|
12
17
|
export function pluginManifest(root) {
|
|
13
|
-
|
|
18
|
+
for (const rel of PLUGIN_MANIFEST_FILES) {
|
|
19
|
+
const manifest = readJsonSafe(join(root, ...rel.split('/')));
|
|
20
|
+
if (manifest) return manifest;
|
|
21
|
+
}
|
|
22
|
+
return {};
|
|
14
23
|
}
|
|
15
24
|
|
|
16
25
|
/** Resolve a manifest-relative path, refusing absolute paths and anything
|
|
@@ -21,22 +30,22 @@ export function resolveContainedPluginPath(root, rel) {
|
|
|
21
30
|
const base = resolve(root);
|
|
22
31
|
const abs = resolve(base, trimmed);
|
|
23
32
|
const relToBase = relative(base, abs);
|
|
24
|
-
if (relToBase.
|
|
33
|
+
if (relToBase.split(/[\\/]/)[0] === '..' || isAbsolute(relToBase)) return null;
|
|
25
34
|
return abs;
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
/** Skill roots a plugin contributes: the conventional `<root>/skills/` plus
|
|
29
|
-
*
|
|
37
|
+
/** Skill roots a plugin contributes: the conventional `<root>/skills/` plus the
|
|
38
|
+
* manifest `skills` field, a path or a list of paths. Deduplicated, absolute,
|
|
39
|
+
* root-contained. */
|
|
30
40
|
export function pluginSkillsRoots(root) {
|
|
31
41
|
const manifest = pluginManifest(root);
|
|
32
42
|
const roots = new Set();
|
|
33
43
|
const add = (rel) => {
|
|
44
|
+
if (typeof rel !== 'string' || !rel.trim()) return;
|
|
34
45
|
const abs = resolveContainedPluginPath(root, rel);
|
|
35
46
|
if (abs) roots.add(abs);
|
|
36
47
|
};
|
|
37
48
|
add('./skills/');
|
|
38
|
-
|
|
39
|
-
add(manifest.skills.trim());
|
|
40
|
-
}
|
|
49
|
+
for (const rel of Array.isArray(manifest.skills) ? manifest.skills : [manifest.skills]) add(rel);
|
|
41
50
|
return [...roots];
|
|
42
51
|
}
|
|
@@ -66,11 +66,18 @@
|
|
|
66
66
|
},
|
|
67
67
|
"muse-code-oauth": {
|
|
68
68
|
"credentialFile": "muse-code-oauth.json",
|
|
69
|
-
"credentialPathEnv": "MUSE_CODE_OAUTH_CREDENTIALS_PATH"
|
|
69
|
+
"credentialPathEnv": "MUSE_CODE_OAUTH_CREDENTIALS_PATH",
|
|
70
|
+
"modelCatalogFile": "muse-code-oauth-models.json"
|
|
71
|
+
},
|
|
72
|
+
"command-code-oauth": {
|
|
73
|
+
"credentialFile": "command-code-oauth.json",
|
|
74
|
+
"credentialPathEnv": "COMMAND_CODE_OAUTH_CREDENTIALS_PATH",
|
|
75
|
+
"modelCatalogFile": "command-code-oauth-models.json"
|
|
70
76
|
},
|
|
71
77
|
"mimo-oauth": {
|
|
72
78
|
"credentialFile": "mimo-oauth.json",
|
|
73
|
-
"credentialPathEnv": "MIMO_OAUTH_CREDENTIALS_PATH"
|
|
79
|
+
"credentialPathEnv": "MIMO_OAUTH_CREDENTIALS_PATH",
|
|
80
|
+
"modelCatalogFile": "mimo-oauth-models.json"
|
|
74
81
|
},
|
|
75
82
|
"cursor-oauth": {
|
|
76
83
|
"credentialFile": "cursor-oauth.json",
|
|
@@ -8,6 +8,12 @@ import { ACCOUNT_PROVIDERS, providerAccountPath, readProviderAccountPool } from
|
|
|
8
8
|
import { AGENT_PROVIDER_ENV_ALIASES, getAgentApiKey } from './provider-api-key.mjs';
|
|
9
9
|
import { platformEntryKey } from './native-asset.mjs';
|
|
10
10
|
import { clean } from './clean.mjs';
|
|
11
|
+
import { MODEL_PROXY_ENV, modelProxyClientMode } from './model-proxy-mode.mjs';
|
|
12
|
+
import { installModelProxyChannel } from '../agent/orchestrator/providers/model-proxy-channel.mjs';
|
|
13
|
+
import {
|
|
14
|
+
ModelProxyBridgeChannel,
|
|
15
|
+
readModelProxyBridgeDiscovery,
|
|
16
|
+
} from '../agent/orchestrator/providers/model-proxy-bridge.mjs';
|
|
11
17
|
|
|
12
18
|
const contractPath = fileURLToPath(new URL('./pristine-execution-contract.json', import.meta.url));
|
|
13
19
|
|
|
@@ -205,6 +211,12 @@ export function createPristineExecutionBoundary({
|
|
|
205
211
|
|
|
206
212
|
const contract = PRISTINE_EXECUTION_CONTRACT;
|
|
207
213
|
const hostEnv = { ...env };
|
|
214
|
+
// Remote (SSH) mode: this server holds no model credentials, so the child
|
|
215
|
+
// runtime's model calls go through the daemon's model proxy bridge. Its
|
|
216
|
+
// endpoint is read from the HOST runtime root, before it is retargeted
|
|
217
|
+
// below; a missing or stale one fails closed (never a local credential).
|
|
218
|
+
const proxyMode = modelProxyClientMode(hostEnv);
|
|
219
|
+
const bridgeEndpoint = proxyMode ? readModelProxyBridgeDiscovery(hostEnv.MIXDOG_RUNTIME_ROOT) : null;
|
|
208
220
|
const originalEnv = new Map();
|
|
209
221
|
const touchedKeys = new Set();
|
|
210
222
|
const rememberEnv = (name) => {
|
|
@@ -227,6 +239,8 @@ export function createPristineExecutionBoundary({
|
|
|
227
239
|
mkdirSync(runtimeRoot, { recursive: true, mode: 0o700 });
|
|
228
240
|
let cleaned = false;
|
|
229
241
|
let restoreAuthBindings = () => {};
|
|
242
|
+
let bridgeChannel = null;
|
|
243
|
+
let restoreModelProxyChannel = () => {};
|
|
230
244
|
// rmSync backs off linearly (retryDelay × attempt), so 50 retries at 100ms
|
|
231
245
|
// is a ≈128s budget per attempt. Callers whose result is already complete
|
|
232
246
|
// (headless exec) pass a smaller budget and leave stragglers to the sweep.
|
|
@@ -238,6 +252,8 @@ export function createPristineExecutionBoundary({
|
|
|
238
252
|
else env[name] = value;
|
|
239
253
|
}
|
|
240
254
|
restoreAuthBindings();
|
|
255
|
+
restoreModelProxyChannel();
|
|
256
|
+
bridgeChannel?.dispose('the headless run finished');
|
|
241
257
|
const removal = { recursive: true, force: true, maxRetries: rootRemovalRetries, retryDelay: 100 };
|
|
242
258
|
// Windows releases file handles (pg.log, sockets) a beat after the owning
|
|
243
259
|
// process exits; rmSync retries absorb that EBUSY/EPERM window.
|
|
@@ -280,7 +296,7 @@ export function createPristineExecutionBoundary({
|
|
|
280
296
|
const oauth = contract.oauthProviders[selectedProvider];
|
|
281
297
|
const apiKeyEnv = contract.apiKeyProviders[selectedProvider];
|
|
282
298
|
let selectedApiKey = null;
|
|
283
|
-
if (apiKeyEnv) {
|
|
299
|
+
if (apiKeyEnv && !proxyMode) {
|
|
284
300
|
// Resolve before scrubbing so the selected provider may use its standard,
|
|
285
301
|
// MIXDOG_AGENT_*, alias, or one provider-scoped keychain account. No
|
|
286
302
|
// general config loader or all-provider credential scan is involved.
|
|
@@ -311,7 +327,15 @@ export function createPristineExecutionBoundary({
|
|
|
311
327
|
|
|
312
328
|
let authMode = 'provider-managed';
|
|
313
329
|
let catalogCount = 0;
|
|
314
|
-
if (
|
|
330
|
+
if (proxyMode) {
|
|
331
|
+
// No API key, OAuth account or credential binding: the PC behind the
|
|
332
|
+
// daemon runs every call. The runtime root stays the pristine one, for
|
|
333
|
+
// the services this run owns; only model calls reach the daemon.
|
|
334
|
+
setEnv(MODEL_PROXY_ENV, 'client');
|
|
335
|
+
bridgeChannel = new ModelProxyBridgeChannel(bridgeEndpoint);
|
|
336
|
+
restoreModelProxyChannel = installModelProxyChannel(bridgeChannel);
|
|
337
|
+
authMode = 'model-proxy-bridge';
|
|
338
|
+
} else if (oauth) {
|
|
315
339
|
// Sign-in writes the selected account of the host's provider-accounts
|
|
316
340
|
// pool; the single legacy file remains for hosts that predate the pool.
|
|
317
341
|
// An explicit credential path still overrides both. Read the pool from
|
|
@@ -340,8 +364,8 @@ export function createPristineExecutionBoundary({
|
|
|
340
364
|
[selectedProvider]: sourceCredential,
|
|
341
365
|
});
|
|
342
366
|
authMode = 'in-process-host-oauth-binding';
|
|
343
|
-
const sourceCatalog = join(sourceDataDir, oauth.modelCatalogFile);
|
|
344
|
-
if (
|
|
367
|
+
const sourceCatalog = oauth.modelCatalogFile && join(sourceDataDir, oauth.modelCatalogFile);
|
|
368
|
+
if (sourceCatalog && existsSync(sourceCatalog)) {
|
|
345
369
|
copyFileSync(sourceCatalog, join(dataDir, oauth.modelCatalogFile));
|
|
346
370
|
catalogCount = 1;
|
|
347
371
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
|
-
import { parseJsonText,
|
|
4
|
+
import { parseJsonText, updateJsonAtomic } from './atomic-file.mjs';
|
|
5
5
|
import { resolvePluginData } from './plugin-paths.mjs';
|
|
6
6
|
|
|
7
7
|
export const ACCOUNT_PROVIDERS = Object.freeze([
|
|
@@ -9,6 +9,7 @@ export const ACCOUNT_PROVIDERS = Object.freeze([
|
|
|
9
9
|
'anthropic-oauth',
|
|
10
10
|
'grok-oauth',
|
|
11
11
|
'muse-code-oauth',
|
|
12
|
+
'command-code-oauth',
|
|
12
13
|
'mimo-oauth',
|
|
13
14
|
'cursor-oauth',
|
|
14
15
|
'antigravity-oauth',
|
|
@@ -74,16 +75,39 @@ function normalizeLabels(pool) {
|
|
|
74
75
|
return { ...pool, accounts };
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
// How the roster picks an account. `manual` serves from the selected account
|
|
79
|
+
// only; `limit` keeps the selected account until it reaches a limit and then
|
|
80
|
+
// moves to the account with the most headroom; `headroom` starts every new
|
|
81
|
+
// session on the account with the most headroom and keeps it for that session.
|
|
82
|
+
export const ACCOUNT_POLICIES = Object.freeze(['manual', 'limit', 'headroom']);
|
|
83
|
+
|
|
84
|
+
// `auto` predates `policy` and is still written beside it, so a build that
|
|
85
|
+
// knows only `auto` reads the same switching on/off. Whenever the two
|
|
86
|
+
// disagree (an older build toggled `auto`), `auto` decides whether switching
|
|
87
|
+
// is on at all.
|
|
88
|
+
export function providerAccountPolicy(pool) {
|
|
89
|
+
if (pool?.auto === false) return 'manual';
|
|
90
|
+
return pool?.policy === 'headroom' ? 'headroom' : 'limit';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function normalizePool(pool) {
|
|
94
|
+
if (!pool) return pool;
|
|
95
|
+
const policy = providerAccountPolicy(pool);
|
|
96
|
+
return { ...normalizeLabels(pool), policy, auto: policy !== 'manual' };
|
|
97
|
+
}
|
|
98
|
+
|
|
77
99
|
export function readProviderAccountPool(provider, dataDir = resolvePluginData()) {
|
|
78
100
|
requireAccountProvider(provider);
|
|
79
|
-
return
|
|
101
|
+
return (
|
|
102
|
+
normalizePool(read(dataDir).providers[provider]) || { accounts: [], selectedId: null, auto: true, policy: 'limit' }
|
|
103
|
+
);
|
|
80
104
|
}
|
|
81
105
|
|
|
82
|
-
function update(provider, mutate) {
|
|
106
|
+
async function update(provider, mutate) {
|
|
83
107
|
requireAccountProvider(provider);
|
|
84
108
|
// Read failures must not be interpreted as an empty account roster.
|
|
85
109
|
read();
|
|
86
|
-
const result =
|
|
110
|
+
const result = await updateJsonAtomic(
|
|
87
111
|
file(),
|
|
88
112
|
(raw) => {
|
|
89
113
|
const state = validate(raw);
|
|
@@ -94,7 +118,7 @@ function update(provider, mutate) {
|
|
|
94
118
|
},
|
|
95
119
|
{ mode: 0o600, secret: true, fsyncDir: true }
|
|
96
120
|
);
|
|
97
|
-
return
|
|
121
|
+
return normalizePool(result.providers[provider]);
|
|
98
122
|
}
|
|
99
123
|
|
|
100
124
|
/**
|
|
@@ -117,7 +141,7 @@ export function newProviderAccountId() {
|
|
|
117
141
|
return randomUUID();
|
|
118
142
|
}
|
|
119
143
|
|
|
120
|
-
export function registerProviderAccount(provider, id, { label = '', includeDefault = false } = {}) {
|
|
144
|
+
export async function registerProviderAccount(provider, id, { label = '', includeDefault = false } = {}) {
|
|
121
145
|
requireAccountId(id);
|
|
122
146
|
return update(provider, (pool) => {
|
|
123
147
|
if (includeDefault && !pool.accounts.some((row) => row.id === 'default')) {
|
|
@@ -131,12 +155,12 @@ export function registerProviderAccount(provider, id, { label = '', includeDefau
|
|
|
131
155
|
});
|
|
132
156
|
}
|
|
133
157
|
|
|
134
|
-
export function changeProviderAccounts(provider, change) {
|
|
158
|
+
export async function changeProviderAccounts(provider, change) {
|
|
135
159
|
if (
|
|
136
160
|
!change ||
|
|
137
161
|
typeof change !== 'object' ||
|
|
138
162
|
Array.isArray(change) ||
|
|
139
|
-
Object.keys(change).some((key) => !['selectedId', 'order', 'auto', 'useCredits', 'rename'].includes(key))
|
|
163
|
+
Object.keys(change).some((key) => !['selectedId', 'order', 'auto', 'policy', 'useCredits', 'rename'].includes(key))
|
|
140
164
|
) {
|
|
141
165
|
throw new TypeError('Invalid provider account change.');
|
|
142
166
|
}
|
|
@@ -175,8 +199,19 @@ export function changeProviderAccounts(provider, change) {
|
|
|
175
199
|
}
|
|
176
200
|
if (change.auto !== undefined) {
|
|
177
201
|
if (typeof change.auto !== 'boolean') throw new TypeError('Automatic account switching must be boolean.');
|
|
202
|
+
// Turning switching on keeps a non-manual policy; on from manual is the
|
|
203
|
+
// classic switch-at-limit behaviour.
|
|
204
|
+
const current = providerAccountPolicy(pool);
|
|
205
|
+
if (!change.auto) pool.policy = 'manual';
|
|
206
|
+
else if (current === 'manual') pool.policy = 'limit';
|
|
207
|
+
else pool.policy = current;
|
|
178
208
|
pool.auto = change.auto;
|
|
179
209
|
}
|
|
210
|
+
if (change.policy !== undefined) {
|
|
211
|
+
if (!ACCOUNT_POLICIES.includes(change.policy)) throw new TypeError('Unknown account switching policy.');
|
|
212
|
+
pool.policy = change.policy;
|
|
213
|
+
pool.auto = change.policy !== 'manual';
|
|
214
|
+
}
|
|
180
215
|
if (change.useCredits !== undefined) {
|
|
181
216
|
if (typeof change.useCredits !== 'boolean') throw new TypeError('Credit use must be boolean.');
|
|
182
217
|
pool.useCredits = change.useCredits;
|
|
@@ -184,7 +219,24 @@ export function changeProviderAccounts(provider, change) {
|
|
|
184
219
|
});
|
|
185
220
|
}
|
|
186
221
|
|
|
187
|
-
|
|
222
|
+
// Selects `id` only while the selection is still `expectedSelectedId`, checked
|
|
223
|
+
// under the write's own lock, so a newer selection (a manual switch) wins. A
|
|
224
|
+
// given `expectedPolicy` must also still hold: a policy change while the
|
|
225
|
+
// request ran (e.g. to Manual) cancels the commit.
|
|
226
|
+
// Returns the pool when the selection moved, otherwise null.
|
|
227
|
+
export async function selectProviderAccountIf(provider, expectedSelectedId, id, expectedPolicy) {
|
|
228
|
+
requireAccountId(id);
|
|
229
|
+
let moved = false;
|
|
230
|
+
const pool = await update(provider, (pool) => {
|
|
231
|
+
if (pool.selectedId !== expectedSelectedId || !pool.accounts.some((row) => row.id === id)) return;
|
|
232
|
+
if (expectedPolicy && providerAccountPolicy(pool) !== expectedPolicy) return;
|
|
233
|
+
pool.selectedId = id;
|
|
234
|
+
moved = true;
|
|
235
|
+
});
|
|
236
|
+
return moved ? pool : null;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export async function removeProviderAccount(provider, id) {
|
|
188
240
|
requireAccountId(id);
|
|
189
241
|
return update(provider, (pool) => {
|
|
190
242
|
pool.accounts = pool.accounts.filter((row) => row.id !== id);
|
|
@@ -192,7 +244,7 @@ export function removeProviderAccount(provider, id) {
|
|
|
192
244
|
});
|
|
193
245
|
}
|
|
194
246
|
|
|
195
|
-
export function recordProviderAccountUsage(provider, id, snapshot) {
|
|
247
|
+
export async function recordProviderAccountUsage(provider, id, snapshot) {
|
|
196
248
|
if (!snapshot || !Array.isArray(snapshot.quotaWindows)) return;
|
|
197
249
|
const windows = snapshot.quotaWindows.slice(0, 12).map((row) => ({
|
|
198
250
|
label: String(row.label || '').slice(0, 40),
|
|
@@ -200,7 +252,7 @@ export function recordProviderAccountUsage(provider, id, snapshot) {
|
|
|
200
252
|
resetAt: typeof row.resetAt === 'number' && Number.isFinite(row.resetAt) ? row.resetAt : null,
|
|
201
253
|
}));
|
|
202
254
|
if (!readProviderAccountPool(provider).accounts.some((row) => row.id === id)) return;
|
|
203
|
-
update(provider, (pool) => {
|
|
255
|
+
await update(provider, (pool) => {
|
|
204
256
|
const row = pool.accounts.find((entry) => entry.id === id);
|
|
205
257
|
if (!row) return;
|
|
206
258
|
row.usage = { windows, checkedAt: Date.now() };
|
|
@@ -225,7 +277,7 @@ const MAX_BLOCK_MS = 6 * 60 * 60_000;
|
|
|
225
277
|
// early can tell us anything.
|
|
226
278
|
// `options.quota` records that the block came from a typed quota refusal or an
|
|
227
279
|
// exhausted usage window, so later sends still count it as quota evidence.
|
|
228
|
-
export function blockProviderAccount(provider, id, until, model = '', source = 'inferred', options = {}) {
|
|
280
|
+
export async function blockProviderAccount(provider, id, until, model = '', source = 'inferred', options = {}) {
|
|
229
281
|
return update(provider, (pool) => {
|
|
230
282
|
const row = pool.accounts.find((entry) => entry.id === id);
|
|
231
283
|
if (!row) return;
|
|
@@ -245,7 +297,7 @@ export function blockProviderAccount(provider, id, until, model = '', source = '
|
|
|
245
297
|
// the refusal window and the meter recorded against the OLD subscription would
|
|
246
298
|
// otherwise outlive it and refuse a request the provider would have served.
|
|
247
299
|
// Both are dropped; the next usage reading refills them.
|
|
248
|
-
export function clearProviderAccountQuotaState(provider, id) {
|
|
300
|
+
export async function clearProviderAccountQuotaState(provider, id) {
|
|
249
301
|
return update(provider, (pool) => {
|
|
250
302
|
const row = pool.accounts.find((entry) => entry.id === id);
|
|
251
303
|
if (!row) return;
|
|
@@ -318,14 +370,53 @@ export function providerAccountsResetAt(rows, now = Date.now(), model = '', opti
|
|
|
318
370
|
return known.length ? Math.min(...known) : null;
|
|
319
371
|
}
|
|
320
372
|
|
|
373
|
+
function windowFull(row, window, now) {
|
|
374
|
+
return window.usedPct >= 100 && (window.resetAt ? window.resetAt > now : now - row.usage.checkedAt < 60_000);
|
|
375
|
+
}
|
|
376
|
+
|
|
321
377
|
export function providerAccountExhausted(row, now = Date.now(), model = '', options = {}) {
|
|
322
378
|
if (row?.blockedUntil > now && blockAppliesToModel(row, model)) return true;
|
|
323
379
|
if (options.useCredits === true) return false;
|
|
324
380
|
return (row?.usage?.windows || []).some(
|
|
325
|
-
(window) =>
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
381
|
+
(window) => quotaWindowAppliesToModel(window, model) && windowFull(row, window, now)
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function quotaWindowFamily(window) {
|
|
386
|
+
return FAMILY_QUOTA_WINDOW.exec(String(window?.label || '').trim())?.[1] || '';
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Which limit currently holds the account, independent of any one model:
|
|
390
|
+
// `{ scope: 'account' }` when every request is refused (an unscoped window is
|
|
391
|
+
// full, or a block with no narrower meter to attribute it to), `{ scope:
|
|
392
|
+
// 'model', families }` when only model-family meters ("7D Fable") or a block
|
|
393
|
+
// attributed to one are spent, so other models still run — else null.
|
|
394
|
+
export function providerAccountLimit(row, now = Date.now(), options = {}) {
|
|
395
|
+
if (!row) return null;
|
|
396
|
+
const windows = row.usage?.windows || [];
|
|
397
|
+
const blocked = row.blockedUntil > now;
|
|
398
|
+
const scope = String(row.blockedModel || '').trim();
|
|
399
|
+
const scopedWindows = windows.filter((window) => quotaWindowFamily(window));
|
|
400
|
+
// A block counts as family-scoped only when a family meter covers the model
|
|
401
|
+
// that earned it; any other refusal is treated as the account's.
|
|
402
|
+
const covering = scope ? scopedWindows.filter((window) => quotaWindowAppliesToModel(window, scope)) : [];
|
|
403
|
+
if (blocked && !covering.length) return { scope: 'account' };
|
|
404
|
+
const credits = options.useCredits === true;
|
|
405
|
+
if (!credits && windows.some((window) => !quotaWindowFamily(window) && windowFull(row, window, now))) {
|
|
406
|
+
return { scope: 'account' };
|
|
407
|
+
}
|
|
408
|
+
const families = new Set(blocked ? covering.map(quotaWindowFamily) : []);
|
|
409
|
+
if (!credits) {
|
|
410
|
+
for (const window of scopedWindows) if (windowFull(row, window, now)) families.add(quotaWindowFamily(window));
|
|
411
|
+
}
|
|
412
|
+
return families.size ? { scope: 'model', families: [...families] } : null;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// The account refuses `model` only through a model-scoped limit: it still
|
|
416
|
+
// serves other models, so it remains the provider's account of record.
|
|
417
|
+
export function providerAccountExhaustedForModelOnly(row, now = Date.now(), model = '', options = {}) {
|
|
418
|
+
return (
|
|
419
|
+
providerAccountExhausted(row, now, model, options) && providerAccountLimit(row, now, options)?.scope === 'model'
|
|
329
420
|
);
|
|
330
421
|
}
|
|
331
422
|
|
|
@@ -347,15 +438,28 @@ function accountHeadroom(row, now, model) {
|
|
|
347
438
|
return used.length ? 100 - Math.max(...used) : null;
|
|
348
439
|
}
|
|
349
440
|
|
|
350
|
-
|
|
441
|
+
// `sessionAccountId` is the account the requesting session already runs on
|
|
442
|
+
// (headroom policy only): kept while usable, so a session switches only on a
|
|
443
|
+
// limit. Without one, the session's first request takes the account with the
|
|
444
|
+
// most headroom.
|
|
445
|
+
export function chooseProviderAccount(
|
|
446
|
+
pool,
|
|
447
|
+
excluded = new Set(),
|
|
448
|
+
now = Date.now(),
|
|
449
|
+
model = '',
|
|
450
|
+
{ sessionAccountId } = {}
|
|
451
|
+
) {
|
|
351
452
|
const options = { useCredits: pool.useCredits === true };
|
|
352
453
|
const usable = (row) => row && !excluded.has(row.id) && !providerAccountExhausted(row, now, model, options);
|
|
353
454
|
const selected = pool.accounts.find((row) => row.id === pool.selectedId);
|
|
354
|
-
|
|
455
|
+
const policy = providerAccountPolicy(pool);
|
|
456
|
+
if (policy === 'manual') {
|
|
355
457
|
if (excluded.has(selected?.id)) return null;
|
|
356
458
|
return options.useCredits || usable(selected) ? selected || null : null;
|
|
357
459
|
}
|
|
358
|
-
|
|
460
|
+
const preferred =
|
|
461
|
+
policy === 'headroom' ? pool.accounts.find((row) => sessionAccountId && row.id === sessionAccountId) : selected;
|
|
462
|
+
if (usable(preferred)) return preferred;
|
|
359
463
|
// Falling back in roster order is blind: the account after the one that just
|
|
360
464
|
// refused may itself be nearly spent, so the request pays a second switch to
|
|
361
465
|
// learn what a recorded meter already knew. Ranking needs evidence for EVERY
|
|
@@ -69,6 +69,18 @@ export function getAgentApiKey(provider) {
|
|
|
69
69
|
return null;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** The one API-key resolver for key-based providers: an explicit config key
|
|
73
|
+
* wins, otherwise the environment/keychain (getAgentApiKey). */
|
|
74
|
+
export function resolveProviderApiKey(provider, config) {
|
|
75
|
+
return config?.apiKey || getAgentApiKey(provider) || '';
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Auth-error reload: the stored/environment key is re-read first so a
|
|
79
|
+
* rotated key beats the stale one captured in config. */
|
|
80
|
+
export function reloadProviderApiKey(provider, config) {
|
|
81
|
+
return getAgentApiKey(provider) || config?.apiKey || '';
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
/** One provider's key read on demand, warming no other account: the
|
|
73
85
|
* environment first (getAgentApiKey's precedence), then only this provider's
|
|
74
86
|
* keychain entry through the async path (no whole-keyring snapshot). */
|
|
@@ -236,6 +236,17 @@ export function forgetQuotaSuspendedGoal(sessionId) {
|
|
|
236
236
|
return true;
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/** Whether a session record belongs to a delegated agent child (declared
|
|
240
|
+
* agent-only visibility, or a legacy non-Lead agent-owned child with a distinct
|
|
241
|
+
* parent). Such a session never owns a quota resume: the Lead decides retries. */
|
|
242
|
+
export function isAgentOnlySession(session) {
|
|
243
|
+
if (!session || typeof session !== 'object') return false;
|
|
244
|
+
const lower = (value) => text(value).toLowerCase();
|
|
245
|
+
if (lower(session.visibility) === 'agent-only') return true;
|
|
246
|
+
const parentId = text(session.parentSessionId || session.ownerSessionId);
|
|
247
|
+
return lower(session.owner) === 'agent' && lower(session.agent) !== 'lead' && parentId !== '' && parentId !== text(session.id);
|
|
248
|
+
}
|
|
249
|
+
|
|
239
250
|
/** Global setting, default on. */
|
|
240
251
|
export function quotaAutoResumeEnabled() {
|
|
241
252
|
return readSection(SETTING_SECTION)?.autoResume !== false;
|