mixdog 1.1.2 → 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 -1
- package/scripts/lib/run-node-tests.mjs +9 -0
- package/scripts/prune-desktop-runtime.mjs +3 -0
- 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/help.mjs +2 -0
- 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 +10 -3
- 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/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/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/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/attachments/store.mjs +38 -0
- 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/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 +7 -0
- 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/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/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 +4 -0
- 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 +20 -7
- package/src/session-runtime/route-resolution.mjs +10 -1
- package/src/session-runtime/route-state.mjs +16 -2
- package/src/session-runtime/runtime-core.mjs +5 -0
- package/src/session-runtime/runtime-facade.mjs +8 -0
- 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/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-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/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/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-transport/http-routes.mjs +17 -0
- package/src/standalone/channel-transport.mjs +13 -1
- 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 +11 -0
- 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 +16 -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/intake/rewind.mjs +2 -1
- package/src/tui/session/session-api/integrations.mjs +17 -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/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
|
@@ -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,17 +104,20 @@ 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 = {}) {
|
|
@@ -130,7 +139,11 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
130
139
|
if (originalName && originalName !== name) delete servers[originalName];
|
|
131
140
|
servers[name] = mergeMcpServerConfig(existing, normalizedInput.config);
|
|
132
141
|
saveServers(servers);
|
|
133
|
-
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
|
+
});
|
|
134
147
|
return { name, source: 'config', status };
|
|
135
148
|
},
|
|
136
149
|
async removeMcpServer(name) {
|
|
@@ -142,7 +155,7 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
142
155
|
}
|
|
143
156
|
delete servers[serverName];
|
|
144
157
|
saveServers(servers);
|
|
145
|
-
return sync.reconnectMcpAndAnnounce();
|
|
158
|
+
return sync.reconnectMcpAndAnnounce({ drop: [serverName] });
|
|
146
159
|
},
|
|
147
160
|
async setMcpServerEnabled(name, enabled) {
|
|
148
161
|
const serverName = clean(name);
|
|
@@ -158,7 +171,7 @@ export function createMcpResourceApi({ deps, sync, decorate }) {
|
|
|
158
171
|
servers[serverName] = { ...current, enabled: want };
|
|
159
172
|
saveServers(servers);
|
|
160
173
|
const status = await sync.scheduleMcpToggle(serverName, want);
|
|
161
|
-
await sync.publish('mcp');
|
|
174
|
+
await sync.publish('mcp', { mcpChanges: [{ name: serverName, enabled: want }] });
|
|
162
175
|
return status;
|
|
163
176
|
},
|
|
164
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
|
|
@@ -20,6 +20,7 @@ function bootParams({
|
|
|
20
20
|
fast,
|
|
21
21
|
modelParameters,
|
|
22
22
|
contextPercent,
|
|
23
|
+
contextTokens,
|
|
23
24
|
selectedContextWindow,
|
|
24
25
|
cwd = process.cwd(),
|
|
25
26
|
toolMode = 'full',
|
|
@@ -30,6 +31,7 @@ function bootParams({
|
|
|
30
31
|
initialConfig = null,
|
|
31
32
|
desktopSession = null,
|
|
32
33
|
sessionProfile = null,
|
|
34
|
+
worktree = null,
|
|
33
35
|
executeAgentControl = null,
|
|
34
36
|
} = {}) {
|
|
35
37
|
return {
|
|
@@ -39,6 +41,7 @@ function bootParams({
|
|
|
39
41
|
fast,
|
|
40
42
|
modelParameters,
|
|
41
43
|
contextPercent,
|
|
44
|
+
contextTokens,
|
|
42
45
|
selectedContextWindow,
|
|
43
46
|
cwd,
|
|
44
47
|
toolMode,
|
|
@@ -49,6 +52,8 @@ function bootParams({
|
|
|
49
52
|
initialConfig,
|
|
50
53
|
desktopSession,
|
|
51
54
|
sessionProfile,
|
|
55
|
+
// Creation-time worktree intent (workspace/transitions.mjs creationIntent).
|
|
56
|
+
worktree,
|
|
52
57
|
executeAgentControl,
|
|
53
58
|
};
|
|
54
59
|
}
|
|
@@ -74,6 +74,10 @@ export function createRuntimeFacade({
|
|
|
74
74
|
get fast() {
|
|
75
75
|
return state.route.fast === true;
|
|
76
76
|
},
|
|
77
|
+
// The route's Auto effort switch; absent means on.
|
|
78
|
+
get autoEffort() {
|
|
79
|
+
return state.route.autoEffort !== false;
|
|
80
|
+
},
|
|
77
81
|
get fastCapable() {
|
|
78
82
|
return state.route.fastCapable === true;
|
|
79
83
|
},
|
|
@@ -94,6 +98,10 @@ export function createRuntimeFacade({
|
|
|
94
98
|
// Picker preference for a new/inherited session, not the live gauge.
|
|
95
99
|
return state.route?.contextPercent ?? state.session?.contextPercent ?? null;
|
|
96
100
|
},
|
|
101
|
+
get contextTokens() {
|
|
102
|
+
// Exact window preference in tokens; null when the route uses a percent stop.
|
|
103
|
+
return state.route ? state.route.contextTokens || null : state.session?.contextTokens || null;
|
|
104
|
+
},
|
|
97
105
|
get rawContextWindow() {
|
|
98
106
|
return state.session?.rawContextWindow || state.session?.contextWindow || null;
|
|
99
107
|
},
|
|
@@ -2,7 +2,10 @@ import { registerCacheInvalidationListener } from '../runtime/agent/orchestrator
|
|
|
2
2
|
import { turnReviewReply } from '../runtime/shared/turn-review-reply.mjs';
|
|
3
3
|
import {
|
|
4
4
|
getSessionReviewDiff,
|
|
5
|
+
getTurnChanges,
|
|
5
6
|
getTurnReviewDiff,
|
|
7
|
+
reapplyTurnChanges,
|
|
8
|
+
revertTurnChanges,
|
|
6
9
|
revertTurnReview,
|
|
7
10
|
revertTurnReviewFile,
|
|
8
11
|
} from '../runtime/shared/turn-snapshot.mjs';
|
|
@@ -22,5 +25,11 @@ export function createRuntimeReviewApi({ getCwd, getSessionId }) {
|
|
|
22
25
|
getSessionReviewDiff: () => getSessionReviewDiff(getCwd(), getSessionId()),
|
|
23
26
|
revertTurnReview: (checkpointId) => revertTurnReview(getCwd(), getSessionId(), checkpointId),
|
|
24
27
|
revertTurnReviewFile: (file, checkpointId) => revertTurnReviewFile(getCwd(), getSessionId(), file, checkpointId),
|
|
28
|
+
// Historical per-turn changes, addressed by checkpointId (TurnChangesReply).
|
|
29
|
+
getTurnChanges: (options = {}) => getTurnChanges(getSessionId(), options),
|
|
30
|
+
revertTurnChanges: (checkpointId, file, revision) =>
|
|
31
|
+
revertTurnChanges(getSessionId(), checkpointId, file, revision),
|
|
32
|
+
reapplyTurnChanges: (checkpointId, file, revision) =>
|
|
33
|
+
reapplyTurnChanges(getSessionId(), checkpointId, file, revision),
|
|
25
34
|
};
|
|
26
35
|
}
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { PRESET_ALIASES } from './tool-def.mjs';
|
|
13
13
|
import { positiveInt } from '../../../runtime/shared/numbers.mjs';
|
|
14
14
|
import { clean } from '../../../runtime/shared/clean.mjs';
|
|
15
|
+
import { normalizeContextTokens } from '../../../runtime/shared/llm/default-context-window.mjs';
|
|
15
16
|
|
|
16
17
|
export { positiveInt };
|
|
17
18
|
|
|
@@ -220,10 +221,14 @@ export function normalizeAgentRoute(routeLike) {
|
|
|
220
221
|
model,
|
|
221
222
|
effort: clean(routeLike?.effort) || undefined,
|
|
222
223
|
fast: routeLike?.fast === true,
|
|
224
|
+
...(routeLike?.autoEffort === false ? { autoEffort: false } : {}),
|
|
223
225
|
...(routeLike?.modelParameters && typeof routeLike.modelParameters === 'object'
|
|
224
226
|
? { modelParameters: { ...routeLike.modelParameters } }
|
|
225
227
|
: {}),
|
|
226
228
|
...(Number(routeLike?.contextPercent) > 0 ? { contextPercent: Number(routeLike.contextPercent) } : {}),
|
|
229
|
+
...(normalizeContextTokens(routeLike?.contextTokens)
|
|
230
|
+
? { contextTokens: normalizeContextTokens(routeLike.contextTokens) }
|
|
231
|
+
: {}),
|
|
227
232
|
};
|
|
228
233
|
}
|
|
229
234
|
|