mixdog 1.1.1 → 1.2.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 +1 -0
- package/package.json +2 -2
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- package/scripts/release-paths.mjs +39 -2
- package/scripts/test-direct.mjs +4 -2
- package/src/defaults/shipped-history.json +6 -0
- package/src/defaults/skills/setup/references/actions.md +5 -2
- package/src/defaults/skills/visualize/SKILL.md +157 -0
- package/src/defaults/skills/visualize/references/craft.md +88 -0
- package/src/headless-command.mjs +56 -3
- package/src/headless-exec.mjs +17 -1
- package/src/headless-role.mjs +2 -2
- package/src/help.mjs +2 -0
- package/src/lib/rules-builder.cjs +0 -23
- package/src/rules/lead/LEAD.md +2 -2
- package/src/rules/shared/70-delivery.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
- package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
- package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
- package/src/runtime/agent/orchestrator/config.mjs +9 -1
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +47 -7
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +51 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +25 -5
- package/src/runtime/agent/orchestrator/mcp/client.mjs +348 -122
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +13 -1
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +12 -4
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
- package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
- package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +67 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +521 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
- package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +6 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
- package/src/runtime/agent/orchestrator/providers/registry.mjs +15 -1
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
- package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +12 -11
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +11 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +11 -8
- package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
- package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +18 -11
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
- package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
- package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +12 -14
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +17 -7
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +5 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +1 -5
- package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
- package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +29 -19
- package/src/runtime/agent/orchestrator/session/read-dedup.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +29 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +23 -4
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +28 -8
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +140 -0
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
- package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +31 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +80 -66
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +5 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
- package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
- package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -10
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/index.mjs +1 -5
- package/src/runtime/channels/lib/runtime-paths.mjs +8 -23
- package/src/runtime/channels/lib/tool-dispatch.mjs +1 -13
- package/src/runtime/channels/lib/worker-ipc.mjs +2 -2
- package/src/runtime/channels/lib/worker-main.mjs +4 -34
- package/src/runtime/shared/atomic-file.mjs +8 -2
- package/src/runtime/shared/best-effort-stderr.mjs +8 -0
- package/src/runtime/shared/code-graph-state.mjs +12 -0
- package/src/runtime/shared/defaults-separation.mjs +2 -0
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/default-context-window.mjs +6 -0
- package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
- package/src/runtime/shared/llm/model-catalog.mjs +240 -118
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +156 -0
- package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
- package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
- package/src/runtime/shared/model-proxy-mode.mjs +19 -0
- package/src/runtime/shared/provider-accounts.mjs +2 -2
- package/src/runtime/shared/provider-api-key.mjs +21 -1
- package/src/runtime/shared/schedule-model-ref.mjs +11 -2
- package/src/runtime/shared/semver.d.mts +1 -0
- package/src/runtime/shared/semver.mjs +57 -0
- package/src/runtime/shared/session-usage-totals.mjs +19 -0
- package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-surface.mjs +9 -2
- package/src/runtime/shared/tool-work-units.mjs +14 -0
- package/src/runtime/shared/transcript-metadata.mjs +17 -1
- package/src/runtime/shared/turn-changes.mjs +791 -0
- package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
- package/src/runtime/shared/turn-snapshot.mjs +690 -81
- package/src/runtime/shared/turn-worktree-snapshot.mjs +161 -11
- package/src/runtime/shared/update-checker.mjs +2 -31
- package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
- package/src/runtime/shared/worktrees/contract.mjs +551 -0
- package/src/runtime/shared/worktrees/disk.mjs +57 -0
- package/src/runtime/shared/worktrees/git.mjs +247 -0
- package/src/runtime/shared/worktrees/include.mjs +480 -0
- package/src/runtime/shared/worktrees/lock.mjs +46 -0
- package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
- package/src/runtime/shared/worktrees/manager.mjs +1215 -0
- package/src/runtime/shared/worktrees/merge.mjs +182 -0
- package/src/runtime/shared/worktrees/owner.mjs +46 -0
- package/src/runtime/shared/worktrees/paths.mjs +62 -0
- package/src/runtime/shared/worktrees/registry.mjs +110 -0
- package/src/runtime/shared/worktrees/settings.mjs +84 -0
- package/src/runtime/shared/worktrees/slug.mjs +62 -0
- package/src/runtime/shared/worktrees/test-support.mjs +96 -0
- package/src/runtime/visualize-bridge/client.mjs +181 -0
- package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
- package/src/session-runtime/boot/apis.mjs +35 -0
- package/src/session-runtime/boot/begin.mjs +0 -2
- package/src/session-runtime/boot/config.mjs +2 -0
- package/src/session-runtime/boot/facade.mjs +20 -0
- package/src/session-runtime/boot/infrastructure.mjs +1 -0
- package/src/session-runtime/boot/session.mjs +1 -0
- package/src/session-runtime/boot/tools.mjs +9 -4
- package/src/session-runtime/config-lifecycle.mjs +10 -1
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +11 -1
- package/src/session-runtime/global-extensions.mjs +2 -2
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
- package/src/session-runtime/internal-tool-executor.mjs +9 -1
- package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
- package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
- package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
- package/src/session-runtime/mcp-glue/connect.mjs +11 -6
- package/src/session-runtime/mcp-glue/server-catalog.mjs +7 -3
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/model-route/route-persist.mjs +3 -0
- package/src/session-runtime/model-route/set-route.mjs +15 -2
- package/src/session-runtime/notification-bus.mjs +9 -1
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/resource-extension-sync.mjs +30 -7
- package/src/session-runtime/resource-mcp-api.mjs +27 -10
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-bootstrap.mjs +0 -4
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- package/src/session-runtime/runtime-paths.mjs +0 -1
- package/src/session-runtime/runtime-review-api.mjs +9 -0
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-worker.mjs +1 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/session-create/session-options.mjs +10 -1
- package/src/session-runtime/session-turn-api.mjs +1 -0
- package/src/session-runtime/settings-api.mjs +4 -2
- package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
- package/src/session-runtime/settings-profile-api.mjs +27 -2
- package/src/session-runtime/settings-worktree-api.mjs +17 -0
- package/src/session-runtime/setup-tool/executor.mjs +11 -4
- package/src/session-runtime/setup-tool/settings-contract.mjs +3 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +31 -3
- package/src/session-runtime/starter-guide-state.mjs +62 -0
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +13 -3
- package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
- package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
- package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
- package/src/session-runtime/turn/turn-run.mjs +6 -7
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
- package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -2
- package/src/session-runtime/workspace/admission-gate.mjs +197 -0
- package/src/session-runtime/workspace/project-branches.mjs +136 -0
- package/src/session-runtime/workspace/transition-steps.mjs +364 -0
- package/src/session-runtime/workspace/transitions.mjs +651 -0
- package/src/session-runtime/workspace/workspace-state.mjs +112 -0
- package/src/standalone/channel-binding.mjs +1 -2
- package/src/standalone/channel-transport/call-dispatch.mjs +4 -10
- package/src/standalone/channel-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- package/src/standalone/daemon-channels-loader.mjs +2 -3
- package/src/standalone/daemon-desktop-runtime.mjs +1 -0
- package/src/standalone/daemon-owner.mjs +372 -0
- package/src/standalone/daemon-stored-session-views.mjs +4 -1
- package/src/standalone/daemon.mjs +139 -5
- package/src/standalone/desktop-service-registry.mjs +20 -0
- package/src/standalone/local-session-runtime.mjs +13 -78
- package/src/standalone/session-client/attachment-calls.mjs +3 -1
- package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
- package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
- package/src/standalone/session-client.mjs +104 -13
- package/src/standalone/session-protocol.mjs +17 -0
- package/src/standalone/session-proxy.mjs +3 -0
- package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
- package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
- package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
- package/src/standalone/session-service/projection/revisions.mjs +2 -0
- package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
- package/src/standalone/session-service/projection/workspace.mjs +14 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +11 -3
- package/src/standalone/session-transport/call-route.mjs +13 -0
- package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
- package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
- package/src/standalone/session-transport/client-registry.mjs +20 -3
- package/src/standalone/session-transport/routes.mjs +79 -2
- package/src/standalone/session-transport/stack.mjs +2 -0
- package/src/standalone/session-transport.mjs +24 -1
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +35 -5
- package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
- package/src/tui/app/model-picker.mjs +4 -2
- package/src/tui/dist/index.mjs +332 -36
- package/src/tui/index.jsx +10 -2
- package/src/tui/session/compaction-view.mjs +147 -0
- package/src/tui/session/context-state.mjs +6 -0
- package/src/tui/session/initial-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +12 -8
- package/src/tui/session/session-api/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +18 -0
- package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
- package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
- package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
- package/src/tui/session/session-api/routes.mjs +10 -1
- package/src/tui/session/session-api/settings.mjs +2 -0
- package/src/tui/session/session-api/shared.mjs +10 -6
- package/src/tui/session/session-api/worktree.mjs +60 -0
- package/src/tui/session/session-api.mjs +2 -0
- package/src/tui/session/session-flow/auto-clear.mjs +31 -12
- package/src/tui/session/session-flow/drain.mjs +15 -1
- package/src/tui/session/session-flow/session-reset.mjs +4 -3
- package/src/tui/session/session-flow.mjs +2 -0
- package/src/tui/session/transcript-intake.mjs +14 -5
- package/src/tui/session/turn-stream.mjs +32 -4
- package/src/tui/session/turn-tool-cards.mjs +10 -2
- package/src/tui/session/turn.mjs +39 -10
- package/src/runtime/channels/tool-defs.mjs +0 -17
|
@@ -31,6 +31,7 @@ export function createInternalToolExecutor({
|
|
|
31
31
|
mcpStatus,
|
|
32
32
|
applyResolvedCwd,
|
|
33
33
|
skillToolContent,
|
|
34
|
+
getWorkspaceApi,
|
|
34
35
|
}) {
|
|
35
36
|
const handlers = {
|
|
36
37
|
...createBridgeToolHandlers({ rt }),
|
|
@@ -46,7 +47,14 @@ export function createInternalToolExecutor({
|
|
|
46
47
|
mcpStatus,
|
|
47
48
|
skillToolContent,
|
|
48
49
|
}),
|
|
49
|
-
...createWorkspaceToolHandlers({
|
|
50
|
+
...createWorkspaceToolHandlers({
|
|
51
|
+
rt,
|
|
52
|
+
goalRuntime,
|
|
53
|
+
agentTool,
|
|
54
|
+
notifyFnForSession,
|
|
55
|
+
applyResolvedCwd,
|
|
56
|
+
getWorkspaceApi,
|
|
57
|
+
}),
|
|
50
58
|
};
|
|
51
59
|
|
|
52
60
|
// Settings-disabled features are refused for model-initiated calls before
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
isRootLeadSession,
|
|
9
9
|
} from '../../runtime/agent/orchestrator/session/store-summary-visibility.mjs';
|
|
10
10
|
import { listSessionHeartbeatMtimes } from '../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
sessionMessageText,
|
|
13
|
+
isSessionPreviewNoise,
|
|
14
|
+
cleanSessionPreview,
|
|
15
|
+
clean,
|
|
16
|
+
} from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
12
17
|
|
|
13
18
|
const VISIBLE_OWNERS = ['cli', 'user', 'mixdog'];
|
|
14
19
|
|
|
@@ -58,6 +63,8 @@ function leadSessionRow(s, heartbeatMtimes) {
|
|
|
58
63
|
// it remains an ordinary resumable session, not a child dispatch.
|
|
59
64
|
if (!isRootLeadSession(s) && owner && !VISIBLE_OWNERS.includes(owner)) return null;
|
|
60
65
|
const sourceType = clean(s.sourceType || '').toLowerCase();
|
|
66
|
+
// One-shot `mixdog exec` runs are never resumable conversations.
|
|
67
|
+
if (sourceType === 'exec') return null;
|
|
61
68
|
if (!isLeadShaped(s, owner, sourceType)) return null;
|
|
62
69
|
const { preview, messageCount } = previewOf(s);
|
|
63
70
|
if (!preview && messageCount === 0) return null;
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* new desktop context (cwd + desktop session) or a fresh session.
|
|
4
4
|
*/
|
|
5
5
|
import { isScratchSession } from '../shared.mjs';
|
|
6
|
+
import { replacementSessionCwd } from '../../workspace/workspace-state.mjs';
|
|
6
7
|
|
|
7
8
|
export function createContextSwitch(deps, { ingestSessionIntoMemory, closeSurfaceSession, releaseSessionWork }) {
|
|
8
9
|
const {
|
|
@@ -59,11 +60,15 @@ export function createContextSwitch(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
59
60
|
async function newSession() {
|
|
60
61
|
deps.assertNotAddressed?.('newSession');
|
|
61
62
|
const session = getSession();
|
|
63
|
+
// Leaving a worktree-bound Lead: the new session starts in the Project
|
|
64
|
+
// folder, never in the worktree the old session retains.
|
|
65
|
+
const projectFolder = replacementSessionCwd(session);
|
|
62
66
|
if (session?.id) {
|
|
63
67
|
void ingestSessionIntoMemory(session);
|
|
64
68
|
closeSurfaceSession(session, 'cli-new', { tombstone: isScratchSession(session) });
|
|
65
69
|
setSession(null);
|
|
66
70
|
}
|
|
71
|
+
if (projectFolder) await applyResolvedCwd(projectFolder);
|
|
67
72
|
invalidateContextStatusCache();
|
|
68
73
|
await createCurrentSession();
|
|
69
74
|
return getSession().id;
|
|
@@ -10,8 +10,16 @@ import {
|
|
|
10
10
|
deferredSurfaceModeForLead,
|
|
11
11
|
} from '../../../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
12
12
|
import { isScratchConversation } from '../shared.mjs';
|
|
13
|
+
import { compactionArchiveRange } from '../../../runtime/agent/orchestrator/session/compaction-archive.mjs';
|
|
14
|
+
import { stampCompactionMessageIds } from '../../../runtime/agent/orchestrator/session/compaction-archive-capture.mjs';
|
|
15
|
+
import { boundWorkspaceCwd, restoreWorkspaceAfterResume } from '../../workspace/workspace-state.mjs';
|
|
13
16
|
|
|
14
17
|
export function resolveResumeCwd(session, currentCwd) {
|
|
18
|
+
// A worktree-bound Lead resumes in its worktree whatever its desktop class;
|
|
19
|
+
// a vanished worktree resumes in the Project folder in `recovery`, where
|
|
20
|
+
// turns are refused (restoreWorkspaceAfterResume) rather than silently run.
|
|
21
|
+
const bound = boundWorkspaceCwd(session);
|
|
22
|
+
if (bound) return bound.cwd;
|
|
15
23
|
const desktop = session?.desktopSession;
|
|
16
24
|
if (desktop?.classification === 'project') {
|
|
17
25
|
// `session.cwd` is the execution source of truth. Older builds updated it
|
|
@@ -60,9 +68,9 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
60
68
|
}
|
|
61
69
|
|
|
62
70
|
// The resumed session's OWN effort wins. resolveRoute always returns an
|
|
63
|
-
// effort key, so
|
|
64
|
-
//
|
|
65
|
-
//
|
|
71
|
+
// effort key, so the route's effort applies only when the session has none —
|
|
72
|
+
// otherwise a schedule/webhook session would reopen with the effort of
|
|
73
|
+
// whichever session happened to be open before.
|
|
66
74
|
function resumedRouteFor(resumed) {
|
|
67
75
|
const route = getRoute();
|
|
68
76
|
const resumeEffort = resumed.effort || (hasOwn(route, 'effort') ? route.effort : undefined);
|
|
@@ -71,6 +79,7 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
71
79
|
model: resumed.model,
|
|
72
80
|
effort: resumeEffort,
|
|
73
81
|
fast: resumed.fast === true,
|
|
82
|
+
autoEffort: resumed.autoEffort !== false,
|
|
74
83
|
modelParameters: resumed.modelParameters || {},
|
|
75
84
|
});
|
|
76
85
|
}
|
|
@@ -92,6 +101,8 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
92
101
|
const activeSession = getSession();
|
|
93
102
|
activeSession.effort = getRoute().effectiveEffort || null;
|
|
94
103
|
activeSession.fast = getRoute().fast === true;
|
|
104
|
+
if (getRoute().autoEffort === false) activeSession.autoEffort = false;
|
|
105
|
+
else delete activeSession.autoEffort;
|
|
95
106
|
activeSession.cwd = getCurrentCwd();
|
|
96
107
|
restoreResumedToolSurface(activeSession, getRoute());
|
|
97
108
|
writeStatuslineRoute(statusRoutes, activeSession, getRoute());
|
|
@@ -124,11 +135,16 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
124
135
|
};
|
|
125
136
|
const resumed = await mgr.resumeSession(id, toolSpecForMode(getMode()), resumeOptions);
|
|
126
137
|
if (!resumed) return null;
|
|
138
|
+
// Restored rows carry their messages' stable ids (legacy transcripts are
|
|
139
|
+
// stamped here and persisted), so a live compaction can grey exactly the
|
|
140
|
+
// rows of what it drops.
|
|
141
|
+
stampCompactionMessageIds(resumed.messages, resumed.id || id);
|
|
127
142
|
retirePrevious(previous, resumed);
|
|
128
143
|
setSession(resumed);
|
|
129
144
|
// Resume is not work: the Lead pool row is written only by turn
|
|
130
145
|
// start/settle, so opening an old conversation never re-lists it in the
|
|
131
146
|
// Agent window for another reap window (user report).
|
|
147
|
+
restoreWorkspaceAfterResume(resumed);
|
|
132
148
|
applyResolvedCwd(resolveResumeCwd(resumed, getCurrentCwd()));
|
|
133
149
|
// Commit the applied cwd before returning the resume transcript.
|
|
134
150
|
resumed.cwd = getCurrentCwd();
|
|
@@ -154,6 +170,8 @@ export function createSessionResume(deps, { ingestSessionIntoMemory, closeSurfac
|
|
|
154
170
|
cwd: getCurrentCwd(),
|
|
155
171
|
provider: resumed.provider,
|
|
156
172
|
model: resumed.model,
|
|
173
|
+
// The display-only compaction archive above the restored rows.
|
|
174
|
+
transcriptArchive: compactionArchiveRange(resumed),
|
|
157
175
|
};
|
|
158
176
|
}
|
|
159
177
|
|
|
@@ -7,6 +7,7 @@ import { clean } from '../../../runtime/agent/orchestrator/runtime-core/session-
|
|
|
7
7
|
import { forgetSessionSearch } from '../../../runtime/session-search/session-search-ingest.mjs';
|
|
8
8
|
import { listLeadSessions } from '../session-catalog.mjs';
|
|
9
9
|
import { SESSION_ID_PATTERN } from '../shared.mjs';
|
|
10
|
+
import { replacementSessionCwd } from '../../workspace/workspace-state.mjs';
|
|
10
11
|
|
|
11
12
|
export function createSessionDelete(deps, { cancelBackgroundTasks }) {
|
|
12
13
|
const {
|
|
@@ -49,6 +50,14 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
// The deleted task's worktree: the autoCleanup setting decides (clean and
|
|
54
|
+
// merged → removed; otherwise kept). Off the deletion path; never fails it.
|
|
55
|
+
function releaseDeletedWorktree(sessionId) {
|
|
56
|
+
return Promise.resolve()
|
|
57
|
+
.then(() => deps.cleanupDeletedSessionWorktree?.(sessionId))
|
|
58
|
+
.catch(() => {});
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
async function deleteSession(id) {
|
|
53
62
|
const sessionId = clean(id);
|
|
54
63
|
if (!sessionId || !SESSION_ID_PATTERN.test(sessionId)) return false;
|
|
@@ -68,8 +77,12 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
|
|
|
68
77
|
mgr.deleteSession(childId);
|
|
69
78
|
} catch {}
|
|
70
79
|
}
|
|
80
|
+
void releaseDeletedWorktree(sessionId);
|
|
71
81
|
return true;
|
|
72
82
|
}
|
|
83
|
+
// Leaving a worktree-bound Lead: the replacement starts in the Project
|
|
84
|
+
// folder, and the worktree can go once nothing runs in it.
|
|
85
|
+
const projectFolder = replacementSessionCwd(current);
|
|
73
86
|
const cleanupReason = 'desktop-session-delete';
|
|
74
87
|
releaseSessionWork(sessionId, cleanupReason);
|
|
75
88
|
// Active sessions retain a tombstone until the normal sweep. Unlinking
|
|
@@ -86,15 +99,18 @@ export function createSessionDelete(deps, { cancelBackgroundTasks }) {
|
|
|
86
99
|
} catch {}
|
|
87
100
|
}
|
|
88
101
|
setSession(null);
|
|
102
|
+
if (projectFolder) await deps.applyResolvedCwd?.(projectFolder);
|
|
89
103
|
invalidateContextStatusCache();
|
|
90
104
|
invalidatePreSessionToolSurface();
|
|
91
105
|
// An addressed runtime never mints a replacement under the deleted
|
|
92
106
|
// address: it becomes terminal and a new conversation needs a new entry.
|
|
93
107
|
if (deps.getAddressedSessionId?.()) {
|
|
94
108
|
deps.markAddressTerminal?.();
|
|
109
|
+
void releaseDeletedWorktree(sessionId);
|
|
95
110
|
return true;
|
|
96
111
|
}
|
|
97
112
|
await createCurrentSession();
|
|
113
|
+
void releaseDeletedWorktree(sessionId);
|
|
98
114
|
return true;
|
|
99
115
|
}
|
|
100
116
|
|
|
@@ -24,11 +24,10 @@ export function createMcpConnect({ mcpClient, getMcpScopeId, state, resolveEffec
|
|
|
24
24
|
}
|
|
25
25
|
const cfg = servers[target];
|
|
26
26
|
if (!cfg) return;
|
|
27
|
-
//
|
|
28
|
-
// the
|
|
29
|
-
await mcpClient.disconnectMcpServer?.(target, scopeOptions());
|
|
27
|
+
// Close-then-connect as one generation-guarded op: a shutdown landing
|
|
28
|
+
// between the close and the connect never resurrects the server.
|
|
30
29
|
try {
|
|
31
|
-
await mcpClient.
|
|
30
|
+
await mcpClient.reconnectSingleMcpServer(getMcpScopeId(), target, cfg);
|
|
32
31
|
} catch (error) {
|
|
33
32
|
const failures = Array.isArray(error?.failures)
|
|
34
33
|
? error.failures
|
|
@@ -126,8 +125,14 @@ export function createMcpConnect({ mcpClient, getMcpScopeId, state, resolveEffec
|
|
|
126
125
|
if (!inFlight) return;
|
|
127
126
|
let budgetMs = 10000;
|
|
128
127
|
try {
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
// The gate must outlast the slowest enabled server's own startup budget;
|
|
129
|
+
// a server with its budget disabled (0) means the connect may never
|
|
130
|
+
// settle, so the whole gate is disabled (handled below).
|
|
131
|
+
const configs = Object.values(resolveEffectiveMcpServers().servers).filter((cfg) => cfg?.enabled !== false);
|
|
132
|
+
const budgets = (configs.length > 0 ? configs : [{}])
|
|
133
|
+
.map((cfg) => mcpClient.resolveMcpStartupTimeoutMs?.(cfg))
|
|
134
|
+
.filter((value) => Number.isFinite(value));
|
|
135
|
+
if (budgets.length > 0) budgetMs = budgets.includes(0) ? 0 : Math.max(...budgets);
|
|
131
136
|
} catch {
|
|
132
137
|
/* fall back to default budget */
|
|
133
138
|
}
|
|
@@ -68,13 +68,17 @@ export function createMcpServerCatalog({ mcpClient, getConfig, getMcpScopeId, st
|
|
|
68
68
|
const servers = [];
|
|
69
69
|
for (const [name, cfg] of Object.entries(configured)) {
|
|
70
70
|
const live = connected.get(name);
|
|
71
|
-
|
|
71
|
+
// A registered server whose client reports it not alive (runtime
|
|
72
|
+
// failure after connect) is surfaced as failed, not connected.
|
|
73
|
+
const dead = live && (live.alive === false || live.connected === false);
|
|
74
|
+
const fail =
|
|
75
|
+
failures.get(name) || (dead ? { msg: live.error || live.lastError || 'MCP server is not alive' } : undefined);
|
|
72
76
|
servers.push({
|
|
73
77
|
name,
|
|
74
78
|
configured: true,
|
|
75
79
|
enabled: cfg?.enabled !== false,
|
|
76
|
-
connected: Boolean(live),
|
|
77
|
-
status: mcpServerStatus(cfg, live, fail),
|
|
80
|
+
connected: Boolean(live) && !dead,
|
|
81
|
+
status: mcpServerStatus(cfg, live && !dead, fail),
|
|
78
82
|
transport: mcpTransportLabel(cfg),
|
|
79
83
|
toolCount: live?.toolCount || 0,
|
|
80
84
|
tools: live?.tools || [],
|
|
@@ -26,8 +26,37 @@ function coerceStringArray(value) {
|
|
|
26
26
|
return Array.isArray(value) ? value.map((entry) => clean(entry)).filter(Boolean) : [];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
function positiveNumber(value, label) {
|
|
30
|
+
const n = Number(value);
|
|
31
|
+
if (!Number.isFinite(n) || n <= 0) throw new Error(`${label} must be a positive number`);
|
|
32
|
+
return n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Per-server timeouts, stored under the keys client-config.mjs already reads:
|
|
36
|
+
// startupTimeoutSec/startupTimeoutMs and timeoutMs. A stored value (save
|
|
37
|
+
// merges the existing entry into the input) round-trips unchanged unless the
|
|
38
|
+
// setup-facing startupTimeoutSec/toolTimeoutSec overrides it.
|
|
39
|
+
function timeoutFields(input) {
|
|
40
|
+
const out = {};
|
|
41
|
+
const startupSec = input.startupTimeoutSec ?? input.startup_timeout_sec;
|
|
42
|
+
if (startupSec != null && startupSec !== '') out.startupTimeoutSec = positiveNumber(startupSec, 'startupTimeoutSec');
|
|
43
|
+
else if (input.startupTimeoutMs != null && input.startupTimeoutMs !== '')
|
|
44
|
+
out.startupTimeoutMs = positiveNumber(input.startupTimeoutMs, 'startupTimeoutMs');
|
|
45
|
+
if (input.toolTimeoutSec != null && input.toolTimeoutSec !== '')
|
|
46
|
+
out.timeoutMs = Math.round(positiveNumber(input.toolTimeoutSec, 'toolTimeoutSec') * 1000);
|
|
47
|
+
else if (input.timeoutMs != null && input.timeoutMs !== '')
|
|
48
|
+
out.timeoutMs = Math.round(positiveNumber(input.timeoutMs, 'timeoutMs'));
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
29
52
|
export function createMcpServerInput({ mcpClient, getCurrentCwd }) {
|
|
30
53
|
function normalizeMcpServerInput(input = {}) {
|
|
54
|
+
const result = normalizeTransport(input);
|
|
55
|
+
Object.assign(result.config, timeoutFields(input));
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeTransport(input) {
|
|
31
60
|
const currentCwd = getCurrentCwd();
|
|
32
61
|
const name = clean(input.name)
|
|
33
62
|
.toLowerCase()
|
|
@@ -52,6 +52,9 @@ export function createRoutePersistence(deps) {
|
|
|
52
52
|
// A selection for the heir must not leak its tuning into the source model.
|
|
53
53
|
if (!sessionUsesRoute(session, route)) return;
|
|
54
54
|
session.fast = route.fast === true;
|
|
55
|
+
// Absent means on; only the off state is stored on the session.
|
|
56
|
+
if (route.autoEffort === false) session.autoEffort = false;
|
|
57
|
+
else delete session.autoEffort;
|
|
55
58
|
session.effort = route.effectiveEffort || null;
|
|
56
59
|
// The request path reads session.modelParameters; keep only serviceTier in step.
|
|
57
60
|
const { serviceTier: _previous, ...otherParameters } = session.modelParameters || {};
|
|
@@ -55,12 +55,23 @@ async function resolveSelectedRoute(deps, next) {
|
|
|
55
55
|
selectedRoute.effort,
|
|
56
56
|
selectedRoute.modelParameters
|
|
57
57
|
);
|
|
58
|
+
const contextState = resolveRouteContextState(selectedRoute, modelMeta);
|
|
59
|
+
// An exact selection is saved clamped, and not at all when it is the
|
|
60
|
+
// default window; an unknown window keeps it as requested.
|
|
61
|
+
const contextTokens =
|
|
62
|
+
contextState.contextDefaultPercent === undefined ? selectedRoute.contextTokens : contextState.contextTokens;
|
|
63
|
+
const { contextTokens: requestedTokens, ...routeFields } = selectedRoute;
|
|
64
|
+
// Tokens that land on the default window select the default, whatever
|
|
65
|
+
// percent travelled with them.
|
|
66
|
+
const tokensAtDefault = requestedTokens && !contextTokens && contextState.contextDefaultPercent !== undefined;
|
|
58
67
|
return {
|
|
59
68
|
selectedRoute: {
|
|
60
|
-
...
|
|
69
|
+
...routeFields,
|
|
70
|
+
...(tokensAtDefault ? { contextPercent: contextState.contextDefaultPercent } : {}),
|
|
61
71
|
fast: fastCapable ? selectedRoute.fast === true : false,
|
|
62
72
|
// Lets the saved model settings tell a chosen window from the default.
|
|
63
|
-
contextDefaultPercent:
|
|
73
|
+
contextDefaultPercent: contextState.contextDefaultPercent,
|
|
74
|
+
...(contextTokens ? { contextTokens } : {}),
|
|
64
75
|
},
|
|
65
76
|
fastCapable,
|
|
66
77
|
modelMeta,
|
|
@@ -92,9 +103,11 @@ function updateSessionInPlace(deps, session, updateOptions) {
|
|
|
92
103
|
provider: route.provider,
|
|
93
104
|
model: route.model,
|
|
94
105
|
fast: route.fast === true,
|
|
106
|
+
autoEffort: route.autoEffort !== false,
|
|
95
107
|
effort: route.effectiveEffort || null,
|
|
96
108
|
modelParameters: route.modelParameters || {},
|
|
97
109
|
contextPercent: route.contextPercent,
|
|
110
|
+
contextTokens: route.contextTokens || null,
|
|
98
111
|
selectedContextWindow: route.selectedContextWindow || null,
|
|
99
112
|
};
|
|
100
113
|
const updated = mgr.updateSessionRoute?.(session.id, fields, updateOptions);
|
|
@@ -29,7 +29,7 @@ function cleanSessionId(value) {
|
|
|
29
29
|
// the wake would leave its durable entry parked until the next user message.
|
|
30
30
|
// A completion whose owner is no longer the live session is still dropped (the
|
|
31
31
|
// durable queue carries it into that session's next turn).
|
|
32
|
-
export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi, getOwnerLifecycle }) {
|
|
32
|
+
export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi, getOwnerLifecycle, beforeWake }) {
|
|
33
33
|
// A closing/terminal owner never starts inference from a completion; the
|
|
34
34
|
// check runs at request time and again inside the delayed callback.
|
|
35
35
|
const ownerOpen = (sessionId) => {
|
|
@@ -55,6 +55,14 @@ export function createCompletionWakeScheduler({ getCurrentSessionId, getTurnApi,
|
|
|
55
55
|
` executionId=${executionId || 'unknown'} queuedMs=${delayMs}\n`
|
|
56
56
|
);
|
|
57
57
|
}
|
|
58
|
+
// Surface hook (idle auto-clear): compacts a cold-cache session before
|
|
59
|
+
// the wake sends it. A failing hook never blocks the wake.
|
|
60
|
+
if (typeof beforeWake === 'function') {
|
|
61
|
+
try {
|
|
62
|
+
await beforeWake(ownerSessionId);
|
|
63
|
+
} catch {}
|
|
64
|
+
if (cleanSessionId(getCurrentSessionId()) !== ownerSessionId || !ownerOpen(ownerSessionId)) return;
|
|
65
|
+
}
|
|
58
66
|
// The wake continues the user turn that launched the worker, so its
|
|
59
67
|
// review keeps that turn's Lead edits and worker diffs.
|
|
60
68
|
await turnApi.ask('', { submittedAt: queuedAt, continueTurn: true });
|
|
@@ -65,8 +65,10 @@ export function hydrateProviderModelRow(cfg, row) {
|
|
|
65
65
|
) || customAutoEffortCapable(row.provider, cfg?.providers?.[row.provider]?.protocol, row),
|
|
66
66
|
savedEffort: saved.effort || null,
|
|
67
67
|
savedFast: Object.hasOwn(saved, 'fast') ? saved.fast === true : undefined,
|
|
68
|
+
savedAutoEffort: Object.hasOwn(saved, 'autoEffort') ? saved.autoEffort !== false : undefined,
|
|
68
69
|
savedModelParameters: saved.modelParameters || {},
|
|
69
70
|
savedContextPercent: Number(saved.contextPercent) || undefined,
|
|
71
|
+
savedContextTokens: Number(saved.contextTokens) || undefined,
|
|
70
72
|
};
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -28,17 +28,27 @@ export function createExtensionSync({
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
// Apply per-server MCP changes in order; every other server stays untouched.
|
|
32
|
+
async function applyMcpChanges(changes) {
|
|
33
|
+
let status;
|
|
34
|
+
for (const { name, enabled } of changes) status = await connectConfiguredMcp({ only: name, enabled });
|
|
35
|
+
return status;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function refreshState(kind, detail = null) {
|
|
32
39
|
reloadFullConfig?.();
|
|
33
40
|
if (kind === 'skills' || kind === 'plugins') invalidateSkills?.();
|
|
34
|
-
|
|
41
|
+
const mcpChanges = kind === 'mcp' ? detail?.mcpChanges : null;
|
|
42
|
+
if (Array.isArray(mcpChanges)) {
|
|
43
|
+
await applyMcpChanges(mcpChanges);
|
|
44
|
+
} else if (kind === 'mcp' || kind === 'plugins') {
|
|
35
45
|
await connectConfiguredMcp({ reset: true });
|
|
36
46
|
}
|
|
37
47
|
await refreshSurface(kind);
|
|
38
48
|
}
|
|
39
49
|
|
|
40
50
|
const subscription = subscribeGlobalExtensionChanges(refreshState);
|
|
41
|
-
const publish = (kind) => publishGlobalExtensionChange(kind, subscription.id);
|
|
51
|
+
const publish = (kind, detail = null) => publishGlobalExtensionChange(kind, subscription.id, detail);
|
|
42
52
|
|
|
43
53
|
function scheduleMcpToggle(serverName, enabled) {
|
|
44
54
|
const chain = mcpToggleChains.get(serverName) || { desired: enabled, running: null };
|
|
@@ -65,14 +75,27 @@ export function createExtensionSync({
|
|
|
65
75
|
|
|
66
76
|
/** Skills/plugins invalidate the skill cache; every kind rebuilds the
|
|
67
77
|
* pre-session surface and notifies the other runtimes. */
|
|
68
|
-
async function announce(kind, { invalidate = kind !== 'mcp' } = {}) {
|
|
78
|
+
async function announce(kind, { invalidate = kind !== 'mcp', detail = null } = {}) {
|
|
69
79
|
if (invalidate) invalidateSkills?.();
|
|
70
80
|
await refreshSurface(kind);
|
|
71
|
-
await publish(kind);
|
|
81
|
+
await publish(kind, detail);
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
/** Reconnect
|
|
75
|
-
|
|
84
|
+
/** Reconnect MCP after a config write, then announce. With `only`, just that
|
|
85
|
+
* server is (re)connected (or disconnected when `enabled` is false) and
|
|
86
|
+
* `drop` names (e.g. a renamed/removed server) are disconnected; every other
|
|
87
|
+
* server stays untouched. Without `only`, every configured server reconnects. */
|
|
88
|
+
async function reconnectMcpAndAnnounce({ only = null, enabled = true, drop = [] } = {}) {
|
|
89
|
+
if (only || drop.length > 0) {
|
|
90
|
+
// Other runtimes receive the same scoped change list, not a full reset.
|
|
91
|
+
const mcpChanges = [
|
|
92
|
+
...drop.map((name) => ({ name, enabled: false })),
|
|
93
|
+
...(only ? [{ name: only, enabled }] : []),
|
|
94
|
+
];
|
|
95
|
+
const status = await applyMcpChanges(mcpChanges);
|
|
96
|
+
await announce('mcp', { detail: { mcpChanges } });
|
|
97
|
+
return status;
|
|
98
|
+
}
|
|
76
99
|
const status = await connectConfiguredMcp({ reset: true });
|
|
77
100
|
await announce('mcp');
|
|
78
101
|
return status;
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
pluginServerMatcher,
|
|
12
12
|
resolveContainedPluginPath,
|
|
13
13
|
mergeMcpServerConfig,
|
|
14
|
+
MCP_TIMEOUT_FIELD_GROUPS,
|
|
14
15
|
} from '../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
|
|
15
16
|
|
|
16
17
|
/** The `mcpServers` map of a config as a fresh object (never the live one). */
|
|
@@ -63,8 +64,13 @@ function mergedServerInput(input, existing, originalName) {
|
|
|
63
64
|
if (Object.hasOwn(input, key)) maps[key] = { ...(existing[key] || {}), ...input[key] };
|
|
64
65
|
}
|
|
65
66
|
}
|
|
67
|
+
// Existing timeouts are kept by mergeMcpServerConfig; the normalizer must only
|
|
68
|
+
// see (and validate) newly supplied ones, so a stored 0/'off' never blocks
|
|
69
|
+
// an unrelated save.
|
|
70
|
+
const { ...base } = transportChanged ? {} : existing;
|
|
71
|
+
for (const field of MCP_TIMEOUT_FIELD_GROUPS.flat()) delete base[field];
|
|
66
72
|
return {
|
|
67
|
-
...
|
|
73
|
+
...base,
|
|
68
74
|
...input,
|
|
69
75
|
...maps,
|
|
70
76
|
name: clean(input.name) || originalName,
|
|
@@ -98,25 +104,32 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
98
104
|
getMcpServerConfig(name) {
|
|
99
105
|
return getMcpServerConfig(name);
|
|
100
106
|
},
|
|
101
|
-
async reconnectMcp() {
|
|
107
|
+
async reconnectMcp(name) {
|
|
102
108
|
await awaitKeychainPrewarm();
|
|
103
109
|
reloadFullConfig();
|
|
104
|
-
|
|
110
|
+
const serverName = clean(name);
|
|
111
|
+
if (!serverName) return sync.reconnectMcpAndAnnounce();
|
|
112
|
+
const { enabled } = getMcpServerConfig(serverName);
|
|
113
|
+
return sync.reconnectMcpAndAnnounce({ only: serverName, enabled });
|
|
105
114
|
},
|
|
106
115
|
async addMcpServer(input = {}) {
|
|
107
116
|
const { name, config: serverConfig } = normalizeMcpServerInput(input);
|
|
108
117
|
const servers = mcpServersOf(getConfig());
|
|
109
118
|
if (Object.hasOwn(servers, name)) throw new Error(`MCP server already exists: ${name}`);
|
|
110
119
|
saveServers({ ...servers, [name]: serverConfig });
|
|
111
|
-
const status = await sync.reconnectMcpAndAnnounce();
|
|
120
|
+
const status = await sync.reconnectMcpAndAnnounce({ only: name, enabled: serverConfig.enabled !== false });
|
|
112
121
|
return { name, status };
|
|
113
122
|
},
|
|
114
123
|
async saveMcpServer(input = {}) {
|
|
115
124
|
const servers = mcpServersOf(getConfig());
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
125
|
+
// An explicit originalName must exist; a bare name edits a matching
|
|
126
|
+
// server and otherwise creates a new one.
|
|
127
|
+
const explicitOriginal = clean(input.originalName);
|
|
128
|
+
if (explicitOriginal && !Object.hasOwn(servers, explicitOriginal)) {
|
|
129
|
+
throw new Error(`MCP server not configured: ${explicitOriginal}`);
|
|
119
130
|
}
|
|
131
|
+
const bareName = clean(input.name);
|
|
132
|
+
const originalName = explicitOriginal || (Object.hasOwn(servers, bareName) ? bareName : '');
|
|
120
133
|
const existing = originalName ? servers[originalName] : {};
|
|
121
134
|
const normalizedInput = normalizeMcpServerInput(mergedServerInput(input, existing, originalName));
|
|
122
135
|
const name = originalName && clean(input.name) === originalName ? originalName : normalizedInput.name;
|
|
@@ -126,7 +139,11 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
126
139
|
if (originalName && originalName !== name) delete servers[originalName];
|
|
127
140
|
servers[name] = mergeMcpServerConfig(existing, normalizedInput.config);
|
|
128
141
|
saveServers(servers);
|
|
129
|
-
const status = await sync.reconnectMcpAndAnnounce(
|
|
142
|
+
const status = await sync.reconnectMcpAndAnnounce({
|
|
143
|
+
only: name,
|
|
144
|
+
enabled: servers[name].enabled !== false,
|
|
145
|
+
drop: originalName && originalName !== name ? [originalName] : [],
|
|
146
|
+
});
|
|
130
147
|
return { name, source: 'config', status };
|
|
131
148
|
},
|
|
132
149
|
async removeMcpServer(name) {
|
|
@@ -138,7 +155,7 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
138
155
|
}
|
|
139
156
|
delete servers[serverName];
|
|
140
157
|
saveServers(servers);
|
|
141
|
-
return sync.reconnectMcpAndAnnounce();
|
|
158
|
+
return sync.reconnectMcpAndAnnounce({ drop: [serverName] });
|
|
142
159
|
},
|
|
143
160
|
async setMcpServerEnabled(name, enabled) {
|
|
144
161
|
const serverName = clean(name);
|
|
@@ -154,7 +171,7 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
154
171
|
servers[serverName] = { ...current, enabled: want };
|
|
155
172
|
saveServers(servers);
|
|
156
173
|
const status = await sync.scheduleMcpToggle(serverName, want);
|
|
157
|
-
await sync.publish('mcp');
|
|
174
|
+
await sync.publish('mcp', { mcpChanges: [{ name: serverName, enabled: want }] });
|
|
158
175
|
return status;
|
|
159
176
|
},
|
|
160
177
|
async enablePluginMcp(plugin = {}) {
|
|
@@ -5,6 +5,7 @@ import { ensureProviderEnabled } from '../runtime/agent/orchestrator/runtime-cor
|
|
|
5
5
|
import { clean } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
6
6
|
import { effortItemsFor } from '../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
7
7
|
import { runAbortable, throwIfAborted } from '../runtime/shared/abort-race.mjs';
|
|
8
|
+
import { normalizeContextTokens } from '../runtime/shared/llm/default-context-window.mjs';
|
|
8
9
|
import { resolveRouteContextState, resolveRouteEffortState, routeModelDisplay } from './route-state.mjs';
|
|
9
10
|
|
|
10
11
|
export function createRouteResolution({ rt, collectProviderModels, ensureProvidersReady, lookupModelMeta }) {
|
|
@@ -53,14 +54,22 @@ export function createRouteResolution({ rt, collectProviderModels, ensureProvide
|
|
|
53
54
|
const selectedContextWindow = [contextState.selectedContextWindow, contextOption?.contextWindow]
|
|
54
55
|
.map(Number)
|
|
55
56
|
.find((value) => value > 0);
|
|
57
|
+
// Without a known window the exact selection is kept as requested; with
|
|
58
|
+
// one it is the clamped value, absent when it is the default window.
|
|
59
|
+
const contextTokens =
|
|
60
|
+
contextState.contextDefaultPercent === undefined
|
|
61
|
+
? normalizeContextTokens(targetRoute.contextTokens)
|
|
62
|
+
: contextState.contextTokens;
|
|
63
|
+
const { contextTokens: _requestedTokens, ...routeFields } = targetRoute;
|
|
56
64
|
rt.route = {
|
|
57
|
-
...
|
|
65
|
+
...routeFields,
|
|
58
66
|
fast: fastCapable ? targetRoute.fast === true : false,
|
|
59
67
|
fastCapable,
|
|
60
68
|
effectiveEffort,
|
|
61
69
|
effortOptions: effortItemsFor(rt.route.provider, modelMeta, effectiveEffort),
|
|
62
70
|
contextPercent: contextState.contextPercent,
|
|
63
71
|
contextDefaultPercent: contextState.contextDefaultPercent,
|
|
72
|
+
...(contextTokens ? { contextTokens } : {}),
|
|
64
73
|
...(selectedContextWindow ? { selectedContextWindow } : {}),
|
|
65
74
|
...(modelDisplay ? { modelDisplay } : {}),
|
|
66
75
|
};
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// Pure route derivations: the effective effort / Fast capability of a route
|
|
2
2
|
// against provider model metadata, and its context-window percentage.
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
modelMetaLooksResolved,
|
|
5
|
+
modelSettingsFor,
|
|
6
|
+
} from '../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
4
7
|
import { clean, hasOwn } from '../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
5
8
|
import { coerceEffortFor } from '../runtime/agent/orchestrator/runtime-core/effort.mjs';
|
|
6
9
|
import { fastCapableFor } from '../runtime/agent/orchestrator/runtime-core/model-capabilities.mjs';
|
|
7
10
|
import { providerCachedModelMetadataSync } from '../runtime/agent/orchestrator/providers/provider-catalog-cache.mjs';
|
|
8
|
-
import { contextWindowRange } from '../runtime/shared/llm/default-context-window.mjs';
|
|
11
|
+
import { contextWindowRange, normalizeContextTokens } from '../runtime/shared/llm/default-context-window.mjs';
|
|
9
12
|
|
|
10
13
|
export function resolveRouteEffortState(targetRoute = {}, modelMeta = null) {
|
|
11
14
|
const requested = hasOwn(targetRoute, 'effort') ? targetRoute.effort : targetRoute.preset?.effort || null;
|
|
@@ -68,6 +71,17 @@ export function resolveRouteContextState(targetRoute = {}, modelMeta = null, win
|
|
|
68
71
|
return { contextPercent: undefined, contextDefaultPercent: undefined, selectedContextWindow: undefined };
|
|
69
72
|
}
|
|
70
73
|
const contextDefaultPercent = Math.max(10, Math.min(100, Math.round((defaultWindow / maxWindow) * 10) * 10));
|
|
74
|
+
// An exact token selection overrides the 10% stops. It is clamped to the
|
|
75
|
+
// selectable range; one that lands on the default window is the default.
|
|
76
|
+
const requestedTokens = normalizeContextTokens(targetRoute?.contextTokens);
|
|
77
|
+
if (requestedTokens) {
|
|
78
|
+
const contextTokens = Math.max(Math.ceil(maxWindow * 0.1), Math.min(maxWindow, requestedTokens));
|
|
79
|
+
if (contextTokens === defaultWindow) {
|
|
80
|
+
return { contextPercent: contextDefaultPercent, contextDefaultPercent, selectedContextWindow: defaultWindow };
|
|
81
|
+
}
|
|
82
|
+
const contextPercent = Math.max(10, Math.min(100, Math.round((contextTokens / maxWindow) * 10) * 10));
|
|
83
|
+
return { contextPercent, contextDefaultPercent, selectedContextWindow: contextTokens, contextTokens };
|
|
84
|
+
}
|
|
71
85
|
const requested = Number(targetRoute?.contextPercent);
|
|
72
86
|
const contextPercent =
|
|
73
87
|
Number.isFinite(requested) && requested > 0
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
RUNTIME,
|
|
11
11
|
WEB_SEARCH_TOOL_DEFS,
|
|
12
12
|
MEMORY_TOOL_DEFS,
|
|
13
|
-
CHANNEL_TOOL_DEFS,
|
|
14
13
|
CODE_GRAPH_TOOL_DEFS,
|
|
15
14
|
STATUSLINE_SESSION_ROUTES,
|
|
16
15
|
STANDALONE_ROOT,
|
|
@@ -49,7 +48,6 @@ export async function loadRuntimeModules() {
|
|
|
49
48
|
statusRoutes,
|
|
50
49
|
webSearchToolDefs,
|
|
51
50
|
memoryToolDefs,
|
|
52
|
-
channelToolDefs,
|
|
53
51
|
codeGraphToolDefs,
|
|
54
52
|
] = await Promise.all([
|
|
55
53
|
profiledImport('config', `${RUNTIME}/config.mjs`),
|
|
@@ -62,7 +60,6 @@ export async function loadRuntimeModules() {
|
|
|
62
60
|
profiledImport('status-routes', STATUSLINE_SESSION_ROUTES, { optional: true }),
|
|
63
61
|
profiledImport('web-search-tool-defs', WEB_SEARCH_TOOL_DEFS, { optional: true }),
|
|
64
62
|
profiledImport('memory-tool-defs', MEMORY_TOOL_DEFS, { optional: true }),
|
|
65
|
-
profiledImport('channel-tool-defs', CHANNEL_TOOL_DEFS, { optional: true }),
|
|
66
63
|
profiledImport('code-graph-tool-defs', CODE_GRAPH_TOOL_DEFS, { optional: true }),
|
|
67
64
|
]);
|
|
68
65
|
bootProfile('imports:ready', { ms: (performance.now() - startedAt).toFixed(1) });
|
|
@@ -88,7 +85,6 @@ export async function loadRuntimeModules() {
|
|
|
88
85
|
statusRoutes,
|
|
89
86
|
webSearchToolDefs,
|
|
90
87
|
memoryToolDefs,
|
|
91
|
-
channelToolDefs,
|
|
92
88
|
codeGraphToolDefs,
|
|
93
89
|
};
|
|
94
90
|
}
|