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
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from './settings-contract.mjs';
|
|
7
7
|
import { MEDIA_KINDS } from '../../runtime/media/tool-defs.mjs';
|
|
8
8
|
import { INSTRUCTION_FILES_MODES } from '../../runtime/shared/instruction-files-mode.mjs';
|
|
9
|
+
import { WORKTREE_NEW_TASK_DEFAULTS } from '../../runtime/shared/worktrees/settings.mjs';
|
|
9
10
|
|
|
10
11
|
/** Built-in `setup` tool: the model's handle on a Mixdog user's persisted
|
|
11
12
|
* configuration. The description is a contract only; how to use each action
|
|
@@ -31,6 +32,7 @@ export const SETUP_STATUS_DOMAINS = Object.freeze([
|
|
|
31
32
|
'plugins',
|
|
32
33
|
'update',
|
|
33
34
|
'onboarding',
|
|
35
|
+
'worktree',
|
|
34
36
|
...SETUP_EXTENDED_DOMAINS,
|
|
35
37
|
]);
|
|
36
38
|
|
|
@@ -100,7 +102,7 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
|
|
|
100
102
|
save_mcp_server: 'server',
|
|
101
103
|
remove_mcp_server: 'name',
|
|
102
104
|
set_mcp_enabled: 'name enabled',
|
|
103
|
-
reconnect_mcp: '',
|
|
105
|
+
reconnect_mcp: 'name?',
|
|
104
106
|
set_disabled_skills: 'skills',
|
|
105
107
|
set_extension_scope: 'kind name projects',
|
|
106
108
|
add_plugin: 'source',
|
|
@@ -108,6 +110,7 @@ export const SETUP_ACTION_FIELDS = Object.freeze({
|
|
|
108
110
|
set_plugin_enabled: 'name enabled',
|
|
109
111
|
remove_plugin: 'name',
|
|
110
112
|
enable_plugin_mcp: 'name',
|
|
113
|
+
set_worktree_settings: 'worktree',
|
|
111
114
|
...SETUP_EXTENDED_ACTION_FIELDS,
|
|
112
115
|
});
|
|
113
116
|
|
|
@@ -122,6 +125,7 @@ export const SETUP_BUILTIN_TOGGLE_FEATURES = Object.freeze([
|
|
|
122
125
|
'browser',
|
|
123
126
|
'computer',
|
|
124
127
|
'voice',
|
|
128
|
+
'visualize',
|
|
125
129
|
]);
|
|
126
130
|
|
|
127
131
|
const ROUTE_SCHEMA = {
|
|
@@ -136,6 +140,11 @@ const ROUTE_SCHEMA = {
|
|
|
136
140
|
// A typed enum is unrepresentable on Gemini; the bounds project everywhere
|
|
137
141
|
// and the executor rejects values that are not a multiple of 10.
|
|
138
142
|
contextPercent: { type: 'integer', minimum: 10, maximum: 100, description: 'A multiple of 10.' },
|
|
143
|
+
contextTokens: {
|
|
144
|
+
type: 'integer',
|
|
145
|
+
minimum: 1,
|
|
146
|
+
description: 'Exact context window in tokens; overrides contextPercent, clamped to 10%-100% of the model window.',
|
|
147
|
+
},
|
|
139
148
|
disabled: {
|
|
140
149
|
type: 'boolean',
|
|
141
150
|
description: 'Agent routes only: true disables without losing the route; false re-enables.',
|
|
@@ -176,7 +185,7 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
176
185
|
route: {
|
|
177
186
|
...ROUTE_SCHEMA,
|
|
178
187
|
description:
|
|
179
|
-
'Partial route update; omitted fields are preserved. Agent or Web Search provider "" restores Main inheritance. contextPercent
|
|
188
|
+
'Partial route update; omitted fields are preserved. Agent or Web Search provider "" restores Main inheritance. contextPercent and contextTokens apply to Main and agent routes only.',
|
|
180
189
|
},
|
|
181
190
|
agent: { type: 'string', description: 'set_agent_route: agent id.' },
|
|
182
191
|
workflow: { type: 'string', description: 'set_workflow: pack id.' },
|
|
@@ -222,7 +231,7 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
222
231
|
},
|
|
223
232
|
name: {
|
|
224
233
|
type: 'string',
|
|
225
|
-
description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; else MCP/plugin/provider id.`,
|
|
234
|
+
description: `set_builtin_enabled: ${SETUP_BUILTIN_TOGGLE_FEATURES.join('|')}; install_builtin also accepts memory. Memory toggles use set_memory_enabled. revoke_linked_device: client id from status connection, not its display name; set_developer_option: option id from status developer; read_definition/delete_definition: the definition id; reconnect_mcp: optional MCP server name (omit for all); else MCP/plugin/provider id.`,
|
|
226
235
|
},
|
|
227
236
|
accountId: {
|
|
228
237
|
type: 'string',
|
|
@@ -320,6 +329,25 @@ export const SETUP_TOOL_DEFS = Object.freeze([
|
|
|
320
329
|
items: { type: 'string', minLength: 1, pattern: '\\S' },
|
|
321
330
|
description: 'set_extension_scope: required project roots; [] = every project.',
|
|
322
331
|
},
|
|
332
|
+
worktree: {
|
|
333
|
+
type: 'object',
|
|
334
|
+
additionalProperties: false,
|
|
335
|
+
properties: {
|
|
336
|
+
newTaskDefault: {
|
|
337
|
+
type: 'string',
|
|
338
|
+
enum: [...WORKTREE_NEW_TASK_DEFAULTS],
|
|
339
|
+
description: 'branch = new tasks use the current branch (default); worktree = a new worktree.',
|
|
340
|
+
},
|
|
341
|
+
branchPrefix: { type: 'string', description: 'Ref prefix ending with "/"; default "mixdog/".' },
|
|
342
|
+
sizeWarnBytes: {
|
|
343
|
+
type: 'integer',
|
|
344
|
+
minimum: 0,
|
|
345
|
+
description: 'Repository size that asks for confirmation first; 0 never asks.',
|
|
346
|
+
},
|
|
347
|
+
autoCleanup: { type: 'boolean', description: 'Remove clean, merged worktrees when their task ends.' },
|
|
348
|
+
},
|
|
349
|
+
description: 'set_worktree_settings: only the given fields change.',
|
|
350
|
+
},
|
|
323
351
|
...SETUP_EXTENDED_PROPERTIES,
|
|
324
352
|
},
|
|
325
353
|
},
|
|
@@ -56,6 +56,17 @@ export function createAgentOps(deps) {
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
// Stop one background shell job of THIS session through the task tool's
|
|
60
|
+
// cancel path; the answer is structured so a surface never parses text.
|
|
61
|
+
async function stopShellJob(taskId) {
|
|
62
|
+
const session = getSession();
|
|
63
|
+
const { stopBackgroundShellJob } = await import('../../runtime/agent/orchestrator/tools/builtin/task-tool.mjs');
|
|
64
|
+
return stopBackgroundShellJob(taskId, {
|
|
65
|
+
callerSessionId: session?.id || null,
|
|
66
|
+
clientHostPid: session?.clientHostPid || process.pid,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
59
70
|
function onNotification(listener) {
|
|
60
71
|
if (typeof listener !== 'function') return () => {};
|
|
61
72
|
const sessionId = getSession()?.id || getReservedSessionId?.() || '';
|
|
@@ -94,5 +105,5 @@ export function createAgentOps(deps) {
|
|
|
94
105
|
return { ...result, status: toolsStatus() };
|
|
95
106
|
}
|
|
96
107
|
|
|
97
|
-
return { agentControl, taskControl, onNotification, toolsStatus, selectTools };
|
|
108
|
+
return { agentControl, taskControl, stopShellJob, onNotification, toolsStatus, selectTools };
|
|
98
109
|
}
|
|
@@ -55,18 +55,28 @@ export function createAskCallbacks({ transcript, notifyFnForSession, scheduleSea
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
// The turn's change-record key rides the caller's transcript metadata (in
|
|
59
|
+
// place: the stream stamps assistantAt on this same object), so the
|
|
60
|
+
// persisted user row and completion carry it across restore.
|
|
61
|
+
function stampCheckpoint(options) {
|
|
62
|
+
const meta = options.transcriptMeta;
|
|
63
|
+
const checkpointId = String(options.id || '').trim();
|
|
64
|
+
if (checkpointId && meta && typeof meta === 'object') meta.checkpointId = checkpointId;
|
|
65
|
+
return meta;
|
|
66
|
+
}
|
|
67
|
+
|
|
58
68
|
function askCallbacks(turn, session0, options) {
|
|
59
69
|
return {
|
|
60
70
|
beforeToolExecution: () => turn.snapshot.promise || Promise.resolve(),
|
|
61
|
-
transcriptMeta: options
|
|
71
|
+
transcriptMeta: stampCheckpoint(options),
|
|
62
72
|
promptSource: options.promptSource,
|
|
63
73
|
retryFailedTurn: options.retryFailedTurn === true,
|
|
64
74
|
onTextDelta: options.onTextDelta,
|
|
65
75
|
onTextReset: options.onTextReset,
|
|
66
76
|
onReasoningDelta: options.onReasoningDelta,
|
|
67
|
-
onAssistantText: (text) => {
|
|
77
|
+
onAssistantText: (text, detail) => {
|
|
68
78
|
transcript.appendAssistantText(text);
|
|
69
|
-
return options.onAssistantText?.(text);
|
|
79
|
+
return options.onAssistantText?.(text, detail);
|
|
70
80
|
},
|
|
71
81
|
onUsageDelta: options.onUsageDelta,
|
|
72
82
|
onAssistantToolCallObserved: options.onAssistantToolCallObserved,
|
|
@@ -1,25 +1,21 @@
|
|
|
1
1
|
import { settleWithin } from '../../../runtime/shared/abort-race.mjs';
|
|
2
2
|
|
|
3
|
-
// Worktree snapshot for the turn review. Captured
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// Worktree snapshot for the turn review. Captured as soon as the turn's
|
|
4
|
+
// workspace is final (after any lazy worktree provisioning, so the baseline is
|
|
5
|
+
// taken in the root the turn runs in), but never on the first-token critical
|
|
6
|
+
// path: every actual tool execution joins the same promise, so
|
|
7
|
+
// shell/apply_patch cannot mutate the worktree before the baseline.
|
|
6
8
|
export function createSnapshotTrackerFactory({
|
|
7
9
|
getCurrentCwd,
|
|
8
10
|
beginTurnSnapshotForTurn,
|
|
9
|
-
cancelTurnSnapshotForTurn,
|
|
10
11
|
completeTurnSnapshotForTurn,
|
|
11
12
|
turnCleanupSettleMs,
|
|
12
13
|
}) {
|
|
13
14
|
return function createSnapshotTracker(options) {
|
|
15
|
+
// `promise` resolves to the turn token beginTurnSnapshot returns
|
|
16
|
+
// (`{ generation, checkpointId }`), or undefined when nothing began.
|
|
14
17
|
const tracker = { sessionId: null, promise: null };
|
|
15
|
-
// A completion wake continues the user turn whose review already exists;
|
|
16
|
-
// aborting the wake seals that review instead of discarding it.
|
|
17
18
|
const continueTurn = options.continueTurn === true;
|
|
18
|
-
const cancel = () => {
|
|
19
|
-
try {
|
|
20
|
-
cancelTurnSnapshotForTurn(tracker.sessionId);
|
|
21
|
-
} catch {}
|
|
22
|
-
};
|
|
23
19
|
tracker.start = (sessionId) => {
|
|
24
20
|
const id = typeof sessionId === 'string' ? sessionId.trim() : '';
|
|
25
21
|
if (!id || (tracker.promise && tracker.sessionId === id)) return;
|
|
@@ -36,19 +32,28 @@ export function createSnapshotTrackerFactory({
|
|
|
36
32
|
.catch(() => undefined);
|
|
37
33
|
};
|
|
38
34
|
tracker.finish = async (signal, awaitTurn) => {
|
|
35
|
+
// A turn refused before its workspace was final never began a review.
|
|
36
|
+
if (!tracker.promise) return;
|
|
37
|
+
// Captured now: a later start (or the next turn) cannot redirect this seal.
|
|
38
|
+
const sessionId = tracker.sessionId;
|
|
39
|
+
const started = Promise.resolve(tracker.promise);
|
|
39
40
|
if (signal.aborted && !continueTurn) {
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
// A cancelled turn keeps its edits as a change record. Sealing waits
|
|
42
|
+
// for the snapshot start, never for the abort's settlement, and only
|
|
43
|
+
// seals the generation this turn began: delayed cleanup cannot seal
|
|
44
|
+
// a newer turn.
|
|
45
|
+
void started
|
|
46
|
+
.then((token) => (token ? completeTurnSnapshotForTurn(sessionId, token) : undefined))
|
|
47
|
+
.catch(() => {});
|
|
42
48
|
return;
|
|
43
49
|
}
|
|
44
|
-
const cleanup =
|
|
50
|
+
const cleanup = started.then((token) => completeTurnSnapshotForTurn(sessionId, token));
|
|
45
51
|
cleanup.catch(() => {});
|
|
46
52
|
try {
|
|
47
53
|
await awaitTurn(() => settleWithin(cleanup, turnCleanupSettleMs));
|
|
48
54
|
} catch {
|
|
49
55
|
/* optional review cleanup never overrides turn settlement */
|
|
50
56
|
}
|
|
51
|
-
if (signal.aborted && !continueTurn) cancel();
|
|
52
57
|
};
|
|
53
58
|
return tracker;
|
|
54
59
|
};
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { runAbortable } from '../../../runtime/shared/abort-race.mjs';
|
|
2
2
|
|
|
3
3
|
// Opens one turn: abort controller registration, timing, the worktree
|
|
4
|
-
// snapshot tracker
|
|
5
|
-
// warmup arm.
|
|
4
|
+
// snapshot tracker (started by session preparation once the turn's workspace
|
|
5
|
+
// is final), the active-turn count and the one-shot heavy-runtime warmup arm.
|
|
6
6
|
export function createTurnOpener({
|
|
7
7
|
createTiming,
|
|
8
8
|
createSnapshot,
|
|
9
|
-
getSession,
|
|
10
|
-
getReservedSessionId,
|
|
11
9
|
registerActiveTurnController,
|
|
12
10
|
getActiveTurnCount,
|
|
13
11
|
setActiveTurnCount,
|
|
@@ -41,7 +39,6 @@ export function createTurnOpener({
|
|
|
41
39
|
const signal = controller.signal;
|
|
42
40
|
const snapshot = createSnapshot(options);
|
|
43
41
|
const routeStartedAt = performance.now();
|
|
44
|
-
snapshot.start(getSession()?.id || getReservedSessionId?.());
|
|
45
42
|
setActiveTurnCount(getActiveTurnCount() + 1);
|
|
46
43
|
return {
|
|
47
44
|
signal,
|
|
@@ -54,6 +51,8 @@ export function createTurnOpener({
|
|
|
54
51
|
armHeavyRuntimeWarmup: heavyRuntimeWarmupArm(),
|
|
55
52
|
session0: null,
|
|
56
53
|
releaseFirstTitle: null,
|
|
54
|
+
/** Set by turn admission (admitTurnWorkspace); released by settleTurn. */
|
|
55
|
+
releaseWorkspace: null,
|
|
57
56
|
startedAt: Date.now(),
|
|
58
57
|
};
|
|
59
58
|
};
|
|
@@ -21,6 +21,8 @@ export function createTurnPreparation({
|
|
|
21
21
|
agentTool,
|
|
22
22
|
getConfig,
|
|
23
23
|
assertSessionAdmission,
|
|
24
|
+
prepareTurnWorkspace,
|
|
25
|
+
admitTurnWorkspace,
|
|
24
26
|
}) {
|
|
25
27
|
const scopedMcpTools = (session) => scopedMcpToolsFor(session, getConfig());
|
|
26
28
|
|
|
@@ -84,7 +86,7 @@ export function createTurnPreparation({
|
|
|
84
86
|
// Historical-session resume publishes its transcript immediately while
|
|
85
87
|
// provider/model metadata initializes in the background. Only the next
|
|
86
88
|
// actual turn waits, guaranteeing that it cannot run on a stale route.
|
|
87
|
-
async function prepareSession(turn, prompt) {
|
|
89
|
+
async function prepareSession(turn, prompt, options = {}) {
|
|
88
90
|
// A closing/closed addressed runtime admits no turn (checked again after
|
|
89
91
|
// every await that could have outlived a close).
|
|
90
92
|
assertSessionAdmission?.();
|
|
@@ -96,6 +98,32 @@ export function createTurnPreparation({
|
|
|
96
98
|
}
|
|
97
99
|
throwIfAborted(turn.signal);
|
|
98
100
|
assertSessionAdmission?.();
|
|
101
|
+
// Turn admission: waits while this session's workspace transitions or
|
|
102
|
+
// another session changes the checkout this session works in, then holds
|
|
103
|
+
// the turn registered (released by settleTurn) so no checkout changes
|
|
104
|
+
// under it.
|
|
105
|
+
if (typeof admitTurnWorkspace === 'function') {
|
|
106
|
+
turn.releaseWorkspace = await admitTurnWorkspace({ signal: turn.signal });
|
|
107
|
+
throwIfAborted(turn.signal);
|
|
108
|
+
assertSessionAdmission?.();
|
|
109
|
+
}
|
|
110
|
+
// Workspace gate: a session in recovery refuses the turn, and a pending
|
|
111
|
+
// worktree intent is provisioned and applied (cwd, record, prompt context)
|
|
112
|
+
// BEFORE the user row, the review snapshot, hooks, the provider and any
|
|
113
|
+
// worker spawn. The submission id is the provisioning transaction id, so a
|
|
114
|
+
// retried or duplicate send reuses the same worktree.
|
|
115
|
+
if (typeof prepareTurnWorkspace === 'function') {
|
|
116
|
+
await turn.awaitTurn(() =>
|
|
117
|
+
prepareTurnWorkspace({
|
|
118
|
+
submissionId: String(options.id || '').trim(),
|
|
119
|
+
prompt,
|
|
120
|
+
signal: turn.signal,
|
|
121
|
+
confirmLarge: options.confirmLarge === true,
|
|
122
|
+
})
|
|
123
|
+
);
|
|
124
|
+
throwIfAborted(turn.signal);
|
|
125
|
+
assertSessionAdmission?.();
|
|
126
|
+
}
|
|
99
127
|
ensureSessionTranscriptWriter?.();
|
|
100
128
|
transcript.appendUser(prompt);
|
|
101
129
|
const session0 = getSession();
|
|
@@ -84,7 +84,12 @@ export function createTurnSettlement({
|
|
|
84
84
|
turn.releaseFirstTitle?.();
|
|
85
85
|
turn.emitTiming(turn.timing.status);
|
|
86
86
|
turn.armHeavyRuntimeWarmup('turn-settled');
|
|
87
|
-
|
|
87
|
+
try {
|
|
88
|
+
await turn.snapshot.finish(turn.signal, turn.awaitTurn);
|
|
89
|
+
} finally {
|
|
90
|
+
// The turn no longer works in its checkout: transitions may proceed.
|
|
91
|
+
turn.releaseWorkspace?.();
|
|
92
|
+
}
|
|
88
93
|
try {
|
|
89
94
|
turn.unregister?.();
|
|
90
95
|
} catch {}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/session-runtime/turn/turn-run.mjs - one user turn end to end: viewer
|
|
3
|
-
* forwarding, turn open (abort controller, timing,
|
|
4
|
-
*
|
|
5
|
-
* settlement that always runs. The phases
|
|
3
|
+
* forwarding, turn open (abort controller, timing, warmup arming), session
|
|
4
|
+
* preparation (workspace provisioning, then the worktree review snapshot), the
|
|
5
|
+
* askSession callback bridge, and the settlement that always runs. The phases
|
|
6
|
+
* live under turn-run/.
|
|
6
7
|
*/
|
|
7
|
-
import { beginTurnSnapshot,
|
|
8
|
+
import { beginTurnSnapshot, completeTurnSnapshot } from '../../runtime/shared/turn-snapshot.mjs';
|
|
8
9
|
import { runWithCwdOverride } from '../../runtime/shared/user-cwd.mjs';
|
|
9
10
|
import { createTurnTranscript } from './turn-run/transcript.mjs';
|
|
10
11
|
import { createTurnTimingFactory } from './turn-run/turn-timing.mjs';
|
|
@@ -23,7 +24,6 @@ export function createTurnRunner(deps) {
|
|
|
23
24
|
endComputerExecution = async () => false,
|
|
24
25
|
deferComputerSessionRelease = () => false,
|
|
25
26
|
beginTurnSnapshotForTurn = beginTurnSnapshot,
|
|
26
|
-
cancelTurnSnapshotForTurn = cancelTurnSnapshot,
|
|
27
27
|
completeTurnSnapshotForTurn = completeTurnSnapshot,
|
|
28
28
|
turnCleanupSettleMs = 2_000,
|
|
29
29
|
getConfig = () => null,
|
|
@@ -35,7 +35,6 @@ export function createTurnRunner(deps) {
|
|
|
35
35
|
createSnapshot: createSnapshotTrackerFactory({
|
|
36
36
|
getCurrentCwd,
|
|
37
37
|
beginTurnSnapshotForTurn,
|
|
38
|
-
cancelTurnSnapshotForTurn,
|
|
39
38
|
completeTurnSnapshotForTurn,
|
|
40
39
|
turnCleanupSettleMs,
|
|
41
40
|
}),
|
|
@@ -93,7 +92,7 @@ export function createTurnRunner(deps) {
|
|
|
93
92
|
if (attachedSession?.remoteAttached) return forwardAttachedPrompt(attachedSession, prompt, options);
|
|
94
93
|
const turn = openTurn(options);
|
|
95
94
|
try {
|
|
96
|
-
const session0 = await prepareSession(turn, prompt);
|
|
95
|
+
const session0 = await prepareSession(turn, prompt, options);
|
|
97
96
|
const turnContext = await dispatchPromptSubmit(turn, session0, prompt, options);
|
|
98
97
|
const result = await mgr.askSession(
|
|
99
98
|
session0.id,
|
|
@@ -3,7 +3,7 @@ import { hasOwn } from '../../runtime/shared/object.mjs';
|
|
|
3
3
|
import { isHiddenAgent } from '../../runtime/agent/orchestrator/internal-agents.mjs';
|
|
4
4
|
import { normalizeWorkflowRoute } from '../../runtime/agent/orchestrator/runtime-core/workflow.mjs';
|
|
5
5
|
import { ensureProviderEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
6
|
-
import { selectionFastCapable } from '../route-state.mjs';
|
|
6
|
+
import { resolveRouteContextState, selectionFastCapable } from '../route-state.mjs';
|
|
7
7
|
import { canonicalizeAgentRouteStorage, withAgentDisabled } from '../../runtime/shared/agent-route-config.mjs';
|
|
8
8
|
import { agentEditorId } from './shared.mjs';
|
|
9
9
|
|
|
@@ -43,19 +43,31 @@ export function createAgentRouteApi(deps) {
|
|
|
43
43
|
clean(selectedRoute.provider) === clean(stored.provider) && clean(selectedRoute.model) === clean(stored.model);
|
|
44
44
|
const inheritedEffort = sameModel ? stored.effort || null : null;
|
|
45
45
|
const inheritedModelParameters = sameModel ? stored.modelParameters || {} : {};
|
|
46
|
-
|
|
46
|
+
// A requested percent replaces the stored exact window and requested
|
|
47
|
+
// tokens replace the stored percent.
|
|
48
|
+
const requestsPercent = requested.contextPercent !== undefined;
|
|
49
|
+
const requestsTokens = requested.contextTokens !== undefined;
|
|
50
|
+
const inheritedContextPercent = sameModel && !requestsTokens ? stored.contextPercent : undefined;
|
|
51
|
+
const inheritedContextTokens = sameModel && !requestsPercent ? stored.contextTokens : undefined;
|
|
47
52
|
selectedRoute = {
|
|
48
53
|
...selectedRoute,
|
|
49
54
|
effort: requested.effort !== undefined ? selectedRoute.effort : inheritedEffort,
|
|
50
55
|
fast: requested.fast !== undefined ? selectedRoute.fast === true : sameModel && stored.fast === true,
|
|
56
|
+
// Main's modelSettings bucket must not decide an agent's Auto effort.
|
|
57
|
+
autoEffort:
|
|
58
|
+
requested.autoEffort !== undefined ? requested.autoEffort !== false : !sameModel || stored.autoEffort !== false,
|
|
51
59
|
modelParameters:
|
|
52
60
|
requested.modelParameters !== undefined ? selectedRoute.modelParameters : inheritedModelParameters,
|
|
53
61
|
// resolveRoute reads contextPercent from Main's modelSettings bucket;
|
|
54
62
|
// an agent route owns its own.
|
|
55
|
-
contextPercent:
|
|
63
|
+
contextPercent: requestsPercent ? requested.contextPercent : inheritedContextPercent,
|
|
64
|
+
contextTokens: requestsTokens ? requested.contextTokens : inheritedContextTokens,
|
|
56
65
|
};
|
|
57
66
|
await ensureProvidersReady(ensureProviderEnabled(getConfig(), selectedRoute.provider));
|
|
58
67
|
const modelMeta = await lookupModelMeta(selectedRoute.provider, selectedRoute.model);
|
|
68
|
+
// Stored clamped, and not at all at the default window, as for Main.
|
|
69
|
+
const contextState = resolveRouteContextState(selectedRoute, modelMeta);
|
|
70
|
+
if (contextState.contextDefaultPercent !== undefined) selectedRoute.contextTokens = contextState.contextTokens;
|
|
59
71
|
const fastCapable = selectionFastCapable(
|
|
60
72
|
selectedRoute.provider,
|
|
61
73
|
modelMeta,
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-wide admission gate shared by Lead workspace transitions, turn
|
|
3
|
+
* admission and worker spawning.
|
|
4
|
+
*
|
|
5
|
+
* - A transition and a worker spawn of the same Lead never overlap: a
|
|
6
|
+
* transition refuses while a spawn is being prepared, a spawn refuses while
|
|
7
|
+
* a transition is in flight.
|
|
8
|
+
* - Turns and transitions are mutually exclusive: an admitted turn is
|
|
9
|
+
* registered until it settles; a turn that arrives while its own session
|
|
10
|
+
* transitions, or while another session changes the checkout its cwd lies
|
|
11
|
+
* in (a held checkout folder), waits for that change to settle. A checkout
|
|
12
|
+
* folder can only be held while no other session's admitted turn runs in it.
|
|
13
|
+
* - Each worktree-bound Lead's execution root, which every worker of that Lead
|
|
14
|
+
* runs in.
|
|
15
|
+
*/
|
|
16
|
+
import { worktreeError } from '../../runtime/shared/worktrees/contract.mjs';
|
|
17
|
+
import { throwIfAborted } from '../../runtime/shared/worktrees/git.mjs';
|
|
18
|
+
import { isWithin } from '../../runtime/shared/worktrees/paths.mjs';
|
|
19
|
+
|
|
20
|
+
/** @type {Map<string, { txId: string, settled: Promise<void> }>} Lead session id → in-flight transition. */
|
|
21
|
+
const transitions = new Map();
|
|
22
|
+
/** @type {Map<string, number>} Lead session id → spawns being prepared. */
|
|
23
|
+
const spawns = new Map();
|
|
24
|
+
/** @type {Map<string, string>} Lead session id → bound worktree execution root. */
|
|
25
|
+
const cwdLocks = new Map();
|
|
26
|
+
/** @type {Map<symbol, { sessionId: string, cwd: () => string }>} Admitted turns (live cwd). */
|
|
27
|
+
const turns = new Map();
|
|
28
|
+
/** @type {Map<symbol, { sessionId: string, folder: string, settled: Promise<void> }>} Checkouts being changed. */
|
|
29
|
+
const folderHolds = new Map();
|
|
30
|
+
|
|
31
|
+
const key = (sessionId) => String(sessionId || '').trim();
|
|
32
|
+
|
|
33
|
+
function latch() {
|
|
34
|
+
let resolve;
|
|
35
|
+
const settled = new Promise((done) => {
|
|
36
|
+
resolve = done;
|
|
37
|
+
});
|
|
38
|
+
return { settled, resolve };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function once(fn) {
|
|
42
|
+
let done = false;
|
|
43
|
+
return () => {
|
|
44
|
+
if (done) return;
|
|
45
|
+
done = true;
|
|
46
|
+
fn();
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const within = (folder, cwd) => Boolean(folder && cwd) && isWithin(folder, cwd);
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Enter a transition for `sessionId`; the returned function leaves it.
|
|
54
|
+
* @returns {() => void}
|
|
55
|
+
*/
|
|
56
|
+
export function beginWorkspaceTransition(sessionId, txId) {
|
|
57
|
+
const id = key(sessionId);
|
|
58
|
+
if (!id) throw new TypeError('workspace transition requires a session id.');
|
|
59
|
+
if (transitions.has(id)) {
|
|
60
|
+
throw worktreeError('WORKTREE_BUSY_JOBS', 'Another workspace change is in progress for this session.', {
|
|
61
|
+
reason: 'transition',
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if ((spawns.get(id) || 0) > 0) {
|
|
65
|
+
throw worktreeError('WORKTREE_BUSY_JOBS', undefined, { reason: 'workers', starting: spawns.get(id) });
|
|
66
|
+
}
|
|
67
|
+
const gate = latch();
|
|
68
|
+
transitions.set(id, { txId: String(txId || 'tx'), settled: gate.settled });
|
|
69
|
+
return once(() => {
|
|
70
|
+
transitions.delete(id);
|
|
71
|
+
gate.resolve();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Hold `folder` (a checkout root) while its branch changes or a merge lands
|
|
77
|
+
* in it. Refuses (WORKTREE_BUSY_JOBS, `folder-in-use`) while another session's
|
|
78
|
+
* admitted turn runs inside it, and (`transition`) while another change holds
|
|
79
|
+
* an overlapping checkout; turns arriving meanwhile wait (admitTurn). The
|
|
80
|
+
* holder's own turns are its own business (the Lead busy check).
|
|
81
|
+
* @param {string|null} sessionId The changing session; '' / null for a session-less change.
|
|
82
|
+
* @returns {() => void} Release.
|
|
83
|
+
*/
|
|
84
|
+
export function holdCheckoutFolder(sessionId, folder) {
|
|
85
|
+
const id = key(sessionId);
|
|
86
|
+
const users = new Set();
|
|
87
|
+
for (const turn of turns.values()) {
|
|
88
|
+
if (turn.sessionId !== id && within(folder, turn.cwd())) users.add(turn.sessionId);
|
|
89
|
+
}
|
|
90
|
+
if (users.size) {
|
|
91
|
+
throw worktreeError('WORKTREE_BUSY_JOBS', 'Other sessions are working in the project folder.', {
|
|
92
|
+
reason: 'folder-in-use',
|
|
93
|
+
sessions: [...users],
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
if ([...folderHolds.values()].some((hold) => within(hold.folder, folder) || within(folder, hold.folder))) {
|
|
97
|
+
throw worktreeError('WORKTREE_BUSY_JOBS', 'Another branch change is in progress in the project folder.', {
|
|
98
|
+
reason: 'transition',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
const token = Symbol('checkout-folder');
|
|
102
|
+
const gate = latch();
|
|
103
|
+
folderHolds.set(token, { sessionId: id, folder, settled: gate.settled });
|
|
104
|
+
return once(() => {
|
|
105
|
+
folderHolds.delete(token);
|
|
106
|
+
gate.resolve();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function admissionBlocker(id, cwd) {
|
|
111
|
+
const own = transitions.get(id);
|
|
112
|
+
if (own) return own.settled;
|
|
113
|
+
const path = cwd();
|
|
114
|
+
for (const hold of folderHolds.values()) {
|
|
115
|
+
if (hold.sessionId !== id && within(hold.folder, path)) return hold.settled;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function settledOrAborted(promise, signal) {
|
|
121
|
+
if (!signal) return promise;
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
const abort = () => {
|
|
124
|
+
try {
|
|
125
|
+
throwIfAborted(signal);
|
|
126
|
+
} catch (error) {
|
|
127
|
+
reject(error);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
131
|
+
promise.then(() => {
|
|
132
|
+
signal.removeEventListener('abort', abort);
|
|
133
|
+
resolve();
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Admit one turn of `sessionId`, whose live cwd is `cwd()`: waits while the
|
|
140
|
+
* session itself transitions or another session holds the checkout the cwd
|
|
141
|
+
* lies in, then registers the turn until the returned release is called.
|
|
142
|
+
* An abort while waiting rejects and registers nothing.
|
|
143
|
+
* @param {{ sessionId: string, cwd: () => string, signal?: AbortSignal }} input
|
|
144
|
+
* @returns {Promise<() => void>}
|
|
145
|
+
*/
|
|
146
|
+
export async function admitTurn({ sessionId, cwd, signal }) {
|
|
147
|
+
const id = key(sessionId);
|
|
148
|
+
for (let blocker = admissionBlocker(id, cwd); blocker; blocker = admissionBlocker(id, cwd)) {
|
|
149
|
+
throwIfAborted(signal);
|
|
150
|
+
await settledOrAborted(blocker, signal);
|
|
151
|
+
}
|
|
152
|
+
throwIfAborted(signal);
|
|
153
|
+
const token = Symbol('turn');
|
|
154
|
+
turns.set(token, { sessionId: id, cwd });
|
|
155
|
+
return once(() => turns.delete(token));
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Admit one worker spawn preparation for the Lead(s) in `sessionIds`.
|
|
160
|
+
* @param {Array<string|null|undefined>} sessionIds Owner and caller session ids.
|
|
161
|
+
* @returns {() => void}
|
|
162
|
+
*/
|
|
163
|
+
export function enterWorkerSpawn(sessionIds) {
|
|
164
|
+
const ids = [...new Set(sessionIds.map(key).filter(Boolean))];
|
|
165
|
+
if (ids.some((id) => transitions.has(id))) {
|
|
166
|
+
throw worktreeError('WORKTREE_BUSY_JOBS', 'The Lead workspace is changing; spawn the worker after it settles.', {
|
|
167
|
+
reason: 'transition',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
for (const id of ids) spawns.set(id, (spawns.get(id) || 0) + 1);
|
|
171
|
+
let left = false;
|
|
172
|
+
return () => {
|
|
173
|
+
if (left) return;
|
|
174
|
+
left = true;
|
|
175
|
+
for (const id of ids) {
|
|
176
|
+
const next = (spawns.get(id) || 1) - 1;
|
|
177
|
+
if (next > 0) spawns.set(id, next);
|
|
178
|
+
else spawns.delete(id);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Record (or clear with null) the worktree root a Lead's workers must run in. */
|
|
184
|
+
export function setWorkspaceCwdLock(sessionId, cwd) {
|
|
185
|
+
const id = key(sessionId);
|
|
186
|
+
if (!id) return;
|
|
187
|
+
if (cwd) cwdLocks.set(id, String(cwd));
|
|
188
|
+
else cwdLocks.delete(id);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** The locked worker cwd for the first Lead id that has one, or null. */
|
|
192
|
+
export function workspaceCwdLock(sessionIds) {
|
|
193
|
+
for (const id of sessionIds.map(key)) {
|
|
194
|
+
if (id && cwdLocks.has(id)) return cwdLocks.get(id);
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|