mixdog 1.1.2 → 1.2.1
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 +8 -5
- 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 +10 -0
- package/src/defaults/skills/pptx/SKILL.md +6 -6
- package/src/defaults/skills/pptx/references/review.md +6 -4
- package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
- package/src/defaults/skills/setup/references/actions.md +18 -5
- package/src/defaults/skills/visualize/SKILL.md +160 -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/60-verification.md +3 -1
- package/src/rules/shared/70-delivery.md +4 -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/cache-mtime.mjs +26 -5
- 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 +19 -5
- package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
- package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
- package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
- package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
- package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
- package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
- package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
- package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- 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 +32 -12
- package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
- package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
- package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
- package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
- package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
- package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
- package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
- 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/lib/gemini-model-catalog.mjs +7 -1
- 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-bridge.mjs +1080 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
- package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -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 +15 -0
- package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
- package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
- 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/openai-ws.mjs +4 -4
- package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
- 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 +15 -11
- package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
- package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
- package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
- package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
- package/src/runtime/agent/orchestrator/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/catalog-db/index.mjs +284 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
- 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/conversation-import/claude.mjs +184 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
- package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
- package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/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 +5 -3
- package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -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/pending-message-entry.mjs +17 -4
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
- package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
- 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 +33 -22
- package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
- package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -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-catalog.mjs +817 -0
- package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
- package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
- package/src/runtime/agent/orchestrator/tools/builtin/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/lib/list-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
- 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 +83 -68
- package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
- 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/search-glob-tool.mjs +13 -10
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
- package/src/runtime/agent/orchestrator/tools/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/dispatch/root-resolution.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
- package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
- package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
- package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
- package/src/runtime/attachments/store.d.mts +3 -0
- package/src/runtime/attachments/store.mjs +38 -0
- package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
- package/src/runtime/channels/lib/worker-main.mjs +17 -14
- package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
- package/src/runtime/effort-judge/judge-client.mjs +56 -18
- package/src/runtime/memory/lib/memory.mjs +1 -1
- package/src/runtime/memory/lib/session-ingest.mjs +2 -2
- package/src/runtime/shared/atomic-file.mjs +33 -6
- 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/extension-scopes.mjs +5 -3
- package/src/runtime/shared/file-lock.mjs +37 -11
- package/src/runtime/shared/git-runner-worker.mjs +3 -2
- package/src/runtime/shared/git-runner.mjs +4 -3
- package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
- 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 -130
- package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
- package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -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/plugin-manifest.mjs +18 -9
- package/src/runtime/shared/pristine-execution-contract.json +9 -2
- package/src/runtime/shared/pristine-execution.mjs +28 -4
- package/src/runtime/shared/provider-accounts.mjs +125 -21
- package/src/runtime/shared/provider-api-key.mjs +33 -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/shell-display.mjs +9 -3
- package/src/runtime/shared/singleton-owner.mjs +36 -6
- package/src/runtime/shared/skill-mentions.d.mts +10 -0
- package/src/runtime/shared/skill-mentions.mjs +133 -0
- package/src/runtime/shared/skill-script-runtime.mjs +264 -0
- package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
- package/src/runtime/shared/tool-primitives.mjs +35 -2
- 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 +185 -15
- package/src/runtime/shared/worker-requests.mjs +15 -2
- 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/automation-agents.mjs +8 -2
- package/src/session-runtime/boot/apis.mjs +90 -0
- package/src/session-runtime/boot/config.mjs +7 -2
- package/src/session-runtime/boot/facade.mjs +21 -0
- package/src/session-runtime/boot/infrastructure.mjs +30 -18
- 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/conversation-import/discovery.mjs +698 -0
- package/src/session-runtime/conversation-import/service.mjs +369 -0
- package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
- package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
- 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 +68 -16
- package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
- package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
- package/src/session-runtime/mcp-glue.mjs +22 -2
- 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/new-session-config.mjs +3 -2
- package/src/session-runtime/notification-bus.mjs +18 -3
- package/src/session-runtime/provider-auth/accounts.mjs +33 -15
- package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
- package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
- package/src/session-runtime/provider-readiness.mjs +16 -10
- package/src/session-runtime/resource-extension-sync.mjs +45 -7
- package/src/session-runtime/resource-mcp-api.mjs +121 -84
- package/src/session-runtime/resource-plugins-api.mjs +29 -41
- package/src/session-runtime/resource-scope-decorate.mjs +6 -8
- package/src/session-runtime/resource-skills-api.mjs +23 -2
- package/src/session-runtime/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/schedule-session-run.mjs +1 -1
- package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
- package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
- package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
- package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
- package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
- package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
- package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
- package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
- package/src/session-runtime/services/agent-tool.mjs +1 -0
- package/src/session-runtime/services/channel-session-router.mjs +4 -1
- package/src/session-runtime/services/daemon-candidate.mjs +3 -2
- package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
- package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
- package/src/session-runtime/services/provider-admin.mjs +64 -18
- package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
- package/src/session-runtime/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 +23 -5
- package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
- package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
- package/src/session-runtime/skills-api.mjs +7 -3
- package/src/session-runtime/turn/agent-ops.mjs +12 -1
- package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -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/webhook-session-run.mjs +16 -4
- 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/skill-script-entry.mjs +36 -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 +15 -4
- package/src/standalone/daemon.mjs +179 -8
- 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 +21 -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 +17 -4
- 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.jsx +9 -1
- package/src/tui/app/app-view/floating-panel.jsx +9 -1
- package/src/tui/app/app-view.jsx +6 -0
- package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
- package/src/tui/app/message-selector.mjs +2 -1
- package/src/tui/app/model-options.mjs +37 -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/app/skill-mention-palette.mjs +88 -0
- package/src/tui/app/slash-palette.mjs +23 -7
- package/src/tui/app/transcript-row-estimate.mjs +11 -3
- package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
- package/src/tui/app/use-prompt-state.mjs +5 -0
- package/src/tui/components/Message.jsx +22 -1
- package/src/tui/components/PromptInput.jsx +21 -3
- package/src/tui/components/SlashCommandPalette.jsx +12 -3
- package/src/tui/components/TranscriptItem.jsx +9 -1
- package/src/tui/components/prompt-input/key-handler.mjs +21 -8
- package/src/tui/components/user-message-skills.mjs +38 -0
- package/src/tui/dist/index.mjs +839 -207
- 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/queue-helpers.mjs +7 -2
- package/src/tui/session/quota-resume.mjs +2 -1
- package/src/tui/session/session-api/commands.mjs +10 -2
- package/src/tui/session/session-api/controls.mjs +4 -0
- package/src/tui/session/session-api/extensions.mjs +4 -2
- 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 +9 -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 +27 -3
- 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/tui-steering-persist.mjs +25 -9
- 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 +56 -10
- package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
- package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* "Import conversations from other apps": the runtime API (list / register /
|
|
3
|
+
* start / status / cancel) and the background import jobs behind it.
|
|
4
|
+
*
|
|
5
|
+
* Jobs are process-wide, so a job keeps running and stays observable after the
|
|
6
|
+
* dialog closes or the user switches conversations (each session runtime of
|
|
7
|
+
* the daemon reads the same registry). Every file passes one serialized
|
|
8
|
+
* admission lane shared by all jobs, where the "already imported" check is
|
|
9
|
+
* repeated right before the session is created, against the session catalog
|
|
10
|
+
* read once per batch and every commit of this process since.
|
|
11
|
+
* An import key only counts as imported while its session still exists in
|
|
12
|
+
* storage, so a deleted import can be imported again.
|
|
13
|
+
* Cancel stops admitting further files; committed imports stay.
|
|
14
|
+
*/
|
|
15
|
+
import { randomUUID } from 'node:crypto';
|
|
16
|
+
import { access, stat } from 'node:fs/promises';
|
|
17
|
+
import {
|
|
18
|
+
buildImportOrigin,
|
|
19
|
+
createImportedSession,
|
|
20
|
+
importOriginKey,
|
|
21
|
+
} from '../../runtime/agent/orchestrator/session/conversation-import/imported-session.mjs';
|
|
22
|
+
import { IMPORT_WARNING_CWD_MISSING } from '../../runtime/agent/orchestrator/session/conversation-import/turns.mjs';
|
|
23
|
+
import { sessionPath } from '../../runtime/agent/orchestrator/session/store/paths-heartbeat.mjs';
|
|
24
|
+
import {
|
|
25
|
+
cachedMeta,
|
|
26
|
+
convertCandidateFile,
|
|
27
|
+
getCandidate,
|
|
28
|
+
importError,
|
|
29
|
+
importedKeyMap,
|
|
30
|
+
listConversations,
|
|
31
|
+
recordConversion,
|
|
32
|
+
registerFile,
|
|
33
|
+
resolveSourceCwd,
|
|
34
|
+
resolveViewSelection,
|
|
35
|
+
} from './discovery.mjs';
|
|
36
|
+
|
|
37
|
+
const MAX_FINISHED_JOBS = 20;
|
|
38
|
+
const MAX_REQUEST_ID_LENGTH = 200;
|
|
39
|
+
|
|
40
|
+
const jobs = new Map(); // jobId → job (insertion order = start order)
|
|
41
|
+
const jobsByRequest = new Map(); // requestId → jobId
|
|
42
|
+
const pendingStarts = new Map(); // requestId → Promise<job> while a selection resolves
|
|
43
|
+
// import key → session id, imports of this process (summaries can lag a save).
|
|
44
|
+
const committedKeys = new Map();
|
|
45
|
+
let admissionTail = Promise.resolve();
|
|
46
|
+
|
|
47
|
+
/** Test seam: forget every job and committed key. */
|
|
48
|
+
export function resetConversationImportJobs() {
|
|
49
|
+
jobs.clear();
|
|
50
|
+
jobsByRequest.clear();
|
|
51
|
+
pendingStarts.clear();
|
|
52
|
+
committedKeys.clear();
|
|
53
|
+
admissionTail = Promise.resolve();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const EXISTS_CONCURRENCY = 16;
|
|
57
|
+
|
|
58
|
+
const sessionFileExists = (id) =>
|
|
59
|
+
access(sessionPath(id)).then(
|
|
60
|
+
() => true,
|
|
61
|
+
() => false
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* `get(key)` → the id of an existing session imported under `key`, else null.
|
|
66
|
+
* Candidates come from this process's commits, then summaries; every candidate
|
|
67
|
+
* id's existence is checked (asynchronously) once, up front. Committed ids
|
|
68
|
+
* whose session no longer exists are dropped from the commit map.
|
|
69
|
+
*/
|
|
70
|
+
async function importedLookup(summaries, sessionExists) {
|
|
71
|
+
const fromSummaries = importedKeyMap(summaries);
|
|
72
|
+
const committedAtStart = new Map(committedKeys);
|
|
73
|
+
const ids = [...new Set([...committedAtStart.values(), ...[...fromSummaries.values()].flat()])];
|
|
74
|
+
const existing = new Set();
|
|
75
|
+
let next = 0;
|
|
76
|
+
const worker = async () => {
|
|
77
|
+
while (next < ids.length) {
|
|
78
|
+
const id = ids[next++];
|
|
79
|
+
if (await sessionExists(id)) existing.add(id);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
await Promise.all(Array.from({ length: Math.min(EXISTS_CONCURRENCY, ids.length) }, worker));
|
|
83
|
+
for (const [key, id] of committedAtStart) {
|
|
84
|
+
if (!existing.has(id) && committedKeys.get(key) === id) committedKeys.delete(key);
|
|
85
|
+
}
|
|
86
|
+
const get = (key) => {
|
|
87
|
+
const committed = committedAtStart.get(key);
|
|
88
|
+
if (committed && existing.has(committed)) return committed;
|
|
89
|
+
return (fromSummaries.get(key) || []).find((id) => existing.has(id)) || null;
|
|
90
|
+
};
|
|
91
|
+
return { get, has: (key) => get(key) !== null };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
class FatalImportError extends Error {
|
|
95
|
+
constructor(code) {
|
|
96
|
+
super(code);
|
|
97
|
+
this.code = code;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function admit(task) {
|
|
102
|
+
const run = admissionTail.then(task, task);
|
|
103
|
+
admissionTail = run.then(
|
|
104
|
+
() => {},
|
|
105
|
+
() => {}
|
|
106
|
+
);
|
|
107
|
+
return run;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function snapshot(job) {
|
|
111
|
+
return {
|
|
112
|
+
jobId: job.jobId,
|
|
113
|
+
seq: job.seq,
|
|
114
|
+
state: job.state,
|
|
115
|
+
total: job.total,
|
|
116
|
+
processed: job.imported + job.skippedAlreadyImported + job.failed,
|
|
117
|
+
imported: job.imported,
|
|
118
|
+
skippedAlreadyImported: job.skippedAlreadyImported,
|
|
119
|
+
failed: job.failed,
|
|
120
|
+
byProject: [...job.byProject.entries()].map(([projectPath, counts]) => ({ projectPath, ...counts })),
|
|
121
|
+
results: job.results.map((result) => ({ ...result, warnings: [...result.warnings] })),
|
|
122
|
+
current: job.current ? { ...job.current } : null,
|
|
123
|
+
...(job.errorCode ? { errorCode: job.errorCode } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function touch(job, patch = {}) {
|
|
128
|
+
Object.assign(job, patch);
|
|
129
|
+
job.seq += 1;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const COUNTER = { imported: 'imported', alreadyImported: 'skippedAlreadyImported', failed: 'failed' };
|
|
133
|
+
|
|
134
|
+
function record(job, { projectPath = null, ...result }) {
|
|
135
|
+
const counter = COUNTER[result.status];
|
|
136
|
+
job[counter] += 1;
|
|
137
|
+
const counts = job.byProject.get(projectPath) || { imported: 0, skippedAlreadyImported: 0, failed: 0 };
|
|
138
|
+
counts[counter] += 1;
|
|
139
|
+
job.byProject.set(projectPath, counts);
|
|
140
|
+
job.results.push(result);
|
|
141
|
+
touch(job);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function pruneFinishedJobs() {
|
|
145
|
+
const finished = [...jobs.values()].filter((job) => job.state !== 'running' && job.state !== 'stopping');
|
|
146
|
+
for (const job of finished.slice(0, Math.max(0, finished.length - MAX_FINISHED_JOBS))) {
|
|
147
|
+
jobs.delete(job.jobId);
|
|
148
|
+
if (jobsByRequest.get(job.requestId) === job.jobId) jobsByRequest.delete(job.requestId);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const failed = (candidateId, errorCode, projectPath = null, warnings = []) => ({
|
|
153
|
+
candidateId,
|
|
154
|
+
status: 'failed',
|
|
155
|
+
errorCode,
|
|
156
|
+
warnings,
|
|
157
|
+
projectPath,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
async function importOne(candidateId, context) {
|
|
161
|
+
const candidate = getCandidate(candidateId);
|
|
162
|
+
if (!candidate) return failed(candidateId, 'unknownCandidate');
|
|
163
|
+
// The chat keeps the title the user picked it by: a long file's listing
|
|
164
|
+
// title comes from its prefix and can differ from the converted branch.
|
|
165
|
+
const listedTitle = cachedMeta(candidate)?.title ?? null;
|
|
166
|
+
let conversion;
|
|
167
|
+
try {
|
|
168
|
+
const info = await stat(candidate.path);
|
|
169
|
+
if (!info.isFile()) return failed(candidateId, 'unreadable');
|
|
170
|
+
candidate.size = info.size;
|
|
171
|
+
candidate.mtimeMs = info.mtimeMs;
|
|
172
|
+
conversion = await convertCandidateFile(candidate, context.limits);
|
|
173
|
+
} catch {
|
|
174
|
+
return failed(candidateId, 'unreadable');
|
|
175
|
+
}
|
|
176
|
+
if (listedTitle) conversion = { ...conversion, title: listedTitle };
|
|
177
|
+
recordConversion(candidate, conversion);
|
|
178
|
+
const cwdInfo = await resolveSourceCwd(conversion.cwd);
|
|
179
|
+
const projectPath = cwdInfo.exists ? cwdInfo.path : null;
|
|
180
|
+
if (conversion.errorCode) return failed(candidateId, conversion.errorCode, projectPath, conversion.warnings);
|
|
181
|
+
const warnings = [...conversion.warnings, ...(projectPath ? [] : [IMPORT_WARNING_CWD_MISSING])];
|
|
182
|
+
const key = importOriginKey(candidate.source, conversion.foreignSessionId, candidate.path);
|
|
183
|
+
// The batch's catalog snapshot, then commits made since (by any job; the
|
|
184
|
+
// admission lane serializes them).
|
|
185
|
+
let existing = context.imported.get(key);
|
|
186
|
+
const committed = committedKeys.get(key);
|
|
187
|
+
if (!existing && committed && (await context.sessionExists(committed))) existing = committed;
|
|
188
|
+
if (existing) return { candidateId, status: 'alreadyImported', sessionId: existing, warnings, projectPath };
|
|
189
|
+
if (!context.leadOptions) {
|
|
190
|
+
try {
|
|
191
|
+
context.leadOptions = await context.prepareLeadOptions();
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new FatalImportError(error?.code || 'noModelRoute');
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
let session;
|
|
197
|
+
try {
|
|
198
|
+
session = await context.createImportedSession({
|
|
199
|
+
leadOptions: context.leadOptions,
|
|
200
|
+
conversation: conversion,
|
|
201
|
+
origin: buildImportOrigin({
|
|
202
|
+
source: candidate.source,
|
|
203
|
+
sourcePath: candidate.path,
|
|
204
|
+
foreignSessionId: conversion.foreignSessionId,
|
|
205
|
+
sourceCwd: conversion.cwd,
|
|
206
|
+
importedAt: Date.now(),
|
|
207
|
+
}),
|
|
208
|
+
cwd: projectPath,
|
|
209
|
+
desktopSession: projectPath
|
|
210
|
+
? { classification: 'project', projectPath }
|
|
211
|
+
: { classification: 'task', projectPath: null },
|
|
212
|
+
});
|
|
213
|
+
} catch (error) {
|
|
214
|
+
return failed(candidateId, typeof error?.code === 'string' ? error.code : 'importFailed', projectPath, warnings);
|
|
215
|
+
}
|
|
216
|
+
committedKeys.set(key, session.id);
|
|
217
|
+
return { candidateId, status: 'imported', sessionId: session.id, warnings, projectPath };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
async function runJob(job, candidateIds, context) {
|
|
221
|
+
try {
|
|
222
|
+
// One catalog read per batch; later commits are seen through committedKeys.
|
|
223
|
+
context.imported = await context.importedLookup();
|
|
224
|
+
for (const candidateId of candidateIds) {
|
|
225
|
+
if (job.cancelRequested) break;
|
|
226
|
+
let result;
|
|
227
|
+
try {
|
|
228
|
+
result = await admit(async () => {
|
|
229
|
+
// A cancel that arrived while queued behind other jobs wins here.
|
|
230
|
+
if (job.cancelRequested) return null;
|
|
231
|
+
const candidate = getCandidate(candidateId);
|
|
232
|
+
touch(job, { current: { candidateId, title: (candidate && cachedMeta(candidate)?.title) ?? null } });
|
|
233
|
+
try {
|
|
234
|
+
return await importOne(candidateId, context);
|
|
235
|
+
} finally {
|
|
236
|
+
job.current = null;
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
} catch (error) {
|
|
240
|
+
if (error instanceof FatalImportError) throw error;
|
|
241
|
+
result = failed(candidateId, 'importFailed');
|
|
242
|
+
}
|
|
243
|
+
if (result === null) break;
|
|
244
|
+
record(job, result);
|
|
245
|
+
}
|
|
246
|
+
touch(job, { state: job.cancelRequested ? 'stopped' : 'completed' });
|
|
247
|
+
} catch (error) {
|
|
248
|
+
touch(job, { state: 'failed', current: null, errorCode: error?.code || 'importFailed' });
|
|
249
|
+
}
|
|
250
|
+
pruneFinishedJobs();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Candidate ids of a selection; a `viewId` resolves the view's complete membership. */
|
|
254
|
+
async function selectionIds(selection, imported) {
|
|
255
|
+
if (!selection || typeof selection !== 'object') throw new TypeError('selection is required.');
|
|
256
|
+
if (Array.isArray(selection.ids)) {
|
|
257
|
+
if (!selection.ids.every((id) => typeof id === 'string' && id)) throw new TypeError('selection.ids must be ids.');
|
|
258
|
+
return [...new Set(selection.ids)];
|
|
259
|
+
}
|
|
260
|
+
if (typeof selection.viewId === 'string') {
|
|
261
|
+
if (selection.excludeIds !== undefined && !Array.isArray(selection.excludeIds)) {
|
|
262
|
+
throw new TypeError('selection.excludeIds must be ids.');
|
|
263
|
+
}
|
|
264
|
+
return resolveViewSelection(selection.viewId, selection.excludeIds, await imported());
|
|
265
|
+
}
|
|
266
|
+
throw new TypeError('selection must carry ids or a viewId.');
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function requireParams(params) {
|
|
270
|
+
if (!params || typeof params !== 'object' || Array.isArray(params)) throw new TypeError('expected one object.');
|
|
271
|
+
return params;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Runtime API slice. deps:
|
|
276
|
+
* listSessionSummaries() async session summary rows (carrying `importKey`), read once per listing/batch
|
|
277
|
+
* prepareLeadOptions() async createSession options of a Lead session on the current route
|
|
278
|
+
* createSession(opts) the session manager's createSession
|
|
279
|
+
* sessionExists(id) async: whether a session is still in storage (default: its file exists)
|
|
280
|
+
* ingestSession(session) content-search ingestion of an imported session (default: ingestSessionTurn)
|
|
281
|
+
* limits convertCandidateFile limits (maxFileBytes, maxLineBytes, maxTranscriptChars, …)
|
|
282
|
+
*/
|
|
283
|
+
export function createConversationImportApi({
|
|
284
|
+
listSessionSummaries,
|
|
285
|
+
prepareLeadOptions,
|
|
286
|
+
createSession,
|
|
287
|
+
env,
|
|
288
|
+
sessionExists = sessionFileExists,
|
|
289
|
+
ingestSession,
|
|
290
|
+
limits,
|
|
291
|
+
}) {
|
|
292
|
+
const summaries = async () => (await listSessionSummaries?.()) || [];
|
|
293
|
+
const imported = async () => importedLookup(await summaries(), sessionExists);
|
|
294
|
+
return {
|
|
295
|
+
async listImportableConversations(params) {
|
|
296
|
+
return listConversations(requireParams(params), { imported: await imported(), env });
|
|
297
|
+
},
|
|
298
|
+
async registerConversationImportFile(params) {
|
|
299
|
+
return registerFile(requireParams(params), { imported: await imported(), limits });
|
|
300
|
+
},
|
|
301
|
+
getConversationImportJobs(params = {}) {
|
|
302
|
+
const { jobId } = requireParams(params ?? {});
|
|
303
|
+
const list = [...jobs.values()].filter((job) => jobId === undefined || jobId === null || job.jobId === jobId);
|
|
304
|
+
return { jobs: list.map(snapshot) };
|
|
305
|
+
},
|
|
306
|
+
async startConversationImport(params) {
|
|
307
|
+
const { requestId, selection } = requireParams(params);
|
|
308
|
+
if (typeof requestId !== 'string' || !requestId.trim() || requestId.length > MAX_REQUEST_ID_LENGTH) {
|
|
309
|
+
throw new TypeError('requestId is required.');
|
|
310
|
+
}
|
|
311
|
+
const existing = jobs.get(jobsByRequest.get(requestId));
|
|
312
|
+
if (existing) return snapshot(existing);
|
|
313
|
+
let pending = pendingStarts.get(requestId);
|
|
314
|
+
if (!pending) {
|
|
315
|
+
pending = (async () => {
|
|
316
|
+
const candidateIds = await selectionIds(selection, imported);
|
|
317
|
+
const job = {
|
|
318
|
+
jobId: randomUUID(),
|
|
319
|
+
requestId,
|
|
320
|
+
seq: 0,
|
|
321
|
+
state: 'running',
|
|
322
|
+
total: candidateIds.length,
|
|
323
|
+
imported: 0,
|
|
324
|
+
skippedAlreadyImported: 0,
|
|
325
|
+
failed: 0,
|
|
326
|
+
byProject: new Map(),
|
|
327
|
+
results: [],
|
|
328
|
+
current: null,
|
|
329
|
+
errorCode: null,
|
|
330
|
+
cancelRequested: false,
|
|
331
|
+
};
|
|
332
|
+
jobs.set(job.jobId, job);
|
|
333
|
+
jobsByRequest.set(requestId, job.jobId);
|
|
334
|
+
void runJob(job, candidateIds, {
|
|
335
|
+
importedLookup: imported,
|
|
336
|
+
imported: null,
|
|
337
|
+
sessionExists,
|
|
338
|
+
limits,
|
|
339
|
+
prepareLeadOptions,
|
|
340
|
+
leadOptions: null,
|
|
341
|
+
createImportedSession: (args) =>
|
|
342
|
+
createImportedSession({
|
|
343
|
+
...args,
|
|
344
|
+
...(createSession ? { createSessionFn: createSession } : {}),
|
|
345
|
+
...(ingestSession ? { ingestFn: ingestSession } : {}),
|
|
346
|
+
}),
|
|
347
|
+
});
|
|
348
|
+
return job;
|
|
349
|
+
})();
|
|
350
|
+
pendingStarts.set(requestId, pending);
|
|
351
|
+
pending.then(
|
|
352
|
+
() => pendingStarts.delete(requestId),
|
|
353
|
+
() => pendingStarts.delete(requestId)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return snapshot(await pending);
|
|
357
|
+
},
|
|
358
|
+
cancelConversationImport(params) {
|
|
359
|
+
const { jobId } = requireParams(params);
|
|
360
|
+
const job = typeof jobId === 'string' ? jobs.get(jobId) : null;
|
|
361
|
+
if (!job) throw importError('jobNotFound');
|
|
362
|
+
if (job.state === 'running') {
|
|
363
|
+
job.cancelRequested = true;
|
|
364
|
+
touch(job, { state: 'stopping' });
|
|
365
|
+
}
|
|
366
|
+
return snapshot(job);
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
// cwd-plugins/cwd-apply.mjs — resolving a requested cwd and retargeting the
|
|
2
2
|
// live session to it (project selection persistence, code-graph prewarm,
|
|
3
3
|
// CwdChanged hook) without recreating the session.
|
|
4
|
+
import { setWorkspaceCwdLock } from '../workspace/admission-gate.mjs';
|
|
5
|
+
import { workspaceForProjectSelection } from '../workspace/workspace-state.mjs';
|
|
6
|
+
|
|
4
7
|
export function createCwdApply({
|
|
5
8
|
getCurrentCwd,
|
|
6
9
|
setCurrentCwd,
|
|
@@ -17,6 +20,7 @@ export function createCwdApply({
|
|
|
17
20
|
writeLastSessionCwd,
|
|
18
21
|
updateCurrentCwdOverride,
|
|
19
22
|
persistSession,
|
|
23
|
+
onCwdChanged,
|
|
20
24
|
clean,
|
|
21
25
|
resolve,
|
|
22
26
|
statSync,
|
|
@@ -31,9 +35,16 @@ export function createCwdApply({
|
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
function persistProjectSelection(session, currentCwd) {
|
|
38
|
+
// A Lead in its worktree keeps the registered Project as projectPath; a
|
|
39
|
+
// switch elsewhere leaves the worktree at a new workspace revision.
|
|
40
|
+
const { workspace, projectPath } = workspaceForProjectSelection(session, currentCwd);
|
|
41
|
+
if (session && workspace !== (session.workspace ?? null)) {
|
|
42
|
+
session.workspace = workspace;
|
|
43
|
+
setWorkspaceCwdLock(session.id, null);
|
|
44
|
+
}
|
|
34
45
|
const desktop = getDesktopSession?.() || session?.desktopSession;
|
|
35
46
|
if (desktop && typeof desktop === 'object') {
|
|
36
|
-
const nextDesktop = { classification: 'project', projectPath
|
|
47
|
+
const nextDesktop = { classification: 'project', projectPath };
|
|
37
48
|
setDesktopSession?.(nextDesktop);
|
|
38
49
|
if (session) session.desktopSession = nextDesktop;
|
|
39
50
|
}
|
|
@@ -43,10 +54,11 @@ export function createCwdApply({
|
|
|
43
54
|
}
|
|
44
55
|
}
|
|
45
56
|
|
|
46
|
-
// cwd changes NEVER recreate the session and never reload
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
//
|
|
57
|
+
// cwd changes NEVER recreate the session and never reload Skills (extension
|
|
58
|
+
// settings are global): a mid-conversation cwd switch must preserve the full
|
|
59
|
+
// message history. The live execution cwd is retargeted in place; the BP3
|
|
60
|
+
// session snapshot remains the session-start environment. `onCwdChanged`
|
|
61
|
+
// lets MCP reconnect only the plugin servers that run in the session cwd.
|
|
50
62
|
function applyResolvedCwd(nextCwd, { persistProjectSelection: persist = false } = {}) {
|
|
51
63
|
const resolved = resolve(nextCwd);
|
|
52
64
|
const stat = statSync(resolved);
|
|
@@ -74,7 +86,10 @@ export function createCwdApply({
|
|
|
74
86
|
if (changed) {
|
|
75
87
|
try {
|
|
76
88
|
void hooks.dispatch('CwdChanged', hookCommonPayload({ cwd: currentCwd }));
|
|
77
|
-
} catch {
|
|
89
|
+
} catch {
|
|
90
|
+
// Hook dispatch is fire-and-forget; a synchronous throw must not block the cwd switch.
|
|
91
|
+
}
|
|
92
|
+
onCwdChanged?.(currentCwd);
|
|
78
93
|
}
|
|
79
94
|
return currentCwd;
|
|
80
95
|
}
|
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
// cwd-plugins/plugin-status.mjs — the registered-plugin status list with its
|
|
2
|
-
//
|
|
3
|
-
// on a short TTL).
|
|
2
|
+
// manifest cache (keyed on mtime); skill count and MCP discovery are live.
|
|
4
3
|
import { readFileSync } from 'node:fs';
|
|
5
4
|
import { discoverPluginMcp } from '../../runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs';
|
|
5
|
+
import { PLUGIN_MANIFEST_FILES, pluginSkillsRoots } from '../../runtime/shared/plugin-manifest.mjs';
|
|
6
6
|
import { pluginMetadata } from '../../runtime/shared/plugin-metadata.mjs';
|
|
7
7
|
|
|
8
8
|
export function createPluginStatus({
|
|
9
|
-
getConfig,
|
|
10
9
|
listRegisteredPlugins,
|
|
11
10
|
pluginAdminStatus,
|
|
12
11
|
pluginManifest,
|
|
13
|
-
|
|
12
|
+
pluginMcpSummary = () => new Map(),
|
|
14
13
|
countSkillFiles,
|
|
15
14
|
clean,
|
|
16
15
|
resolve,
|
|
@@ -18,16 +17,13 @@ export function createPluginStatus({
|
|
|
18
17
|
existsSync,
|
|
19
18
|
cfgMod,
|
|
20
19
|
}) {
|
|
21
|
-
// Per-plugin-root
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// single mtime to key on).
|
|
20
|
+
// Per-plugin-root manifest cache keyed by root path + manifest mtimes
|
|
21
|
+
// (invalidated on manifest edit). Skill count and MCP discovery are
|
|
22
|
+
// recomputed on every call so file add/remove shows up immediately.
|
|
25
23
|
const pluginRootCache = new Map();
|
|
26
|
-
const skillCountCache = new Map();
|
|
27
|
-
const mcpDiscoveryCache = new Map();
|
|
28
24
|
function manifestMtimeKey(root) {
|
|
29
25
|
let key = '';
|
|
30
|
-
for (const rel of
|
|
26
|
+
for (const rel of PLUGIN_MANIFEST_FILES) {
|
|
31
27
|
try {
|
|
32
28
|
key += `${statSync(resolve(root, rel)).mtimeMs}:`;
|
|
33
29
|
} catch {
|
|
@@ -38,7 +34,7 @@ export function createPluginStatus({
|
|
|
38
34
|
}
|
|
39
35
|
// A manifest file that exists but is not a JSON object is breakage.
|
|
40
36
|
function manifestInvalid(root) {
|
|
41
|
-
for (const rel of
|
|
37
|
+
for (const rel of PLUGIN_MANIFEST_FILES) {
|
|
42
38
|
const file = resolve(root, rel);
|
|
43
39
|
if (!existsSync(file)) continue;
|
|
44
40
|
try {
|
|
@@ -58,33 +54,20 @@ export function createPluginStatus({
|
|
|
58
54
|
pluginRootCache.set(root, entry);
|
|
59
55
|
return entry;
|
|
60
56
|
}
|
|
61
|
-
// MCP discovery probes candidate script files (.mcp.json, scripts/run-mcp.mjs,
|
|
62
|
-
// ...) whose add/remove is NOT reflected in the manifest mtime, so key it on a
|
|
63
|
-
// short TTL instead (~5s, like the skill-file count).
|
|
64
|
-
function cachedMcpDiscovery(root) {
|
|
65
|
-
const now = Date.now();
|
|
66
|
-
const hit = mcpDiscoveryCache.get(root);
|
|
67
|
-
if (hit && now - hit.at < 5000) return hit.mcp;
|
|
68
|
-
const mcp = discoverPluginMcp(root);
|
|
69
|
-
mcpDiscoveryCache.set(root, { at: now, mcp });
|
|
70
|
-
return mcp;
|
|
71
|
-
}
|
|
72
|
-
function cachedSkillCount(root) {
|
|
73
|
-
const now = Date.now();
|
|
74
|
-
const hit = skillCountCache.get(root);
|
|
75
|
-
if (hit && now - hit.at < 5000) return hit.count;
|
|
76
|
-
const count = countSkillFiles(root);
|
|
77
|
-
skillCountCache.set(root, { at: now, count });
|
|
78
|
-
return count;
|
|
79
|
-
}
|
|
80
57
|
|
|
81
|
-
function registeredPlugin(entry,
|
|
58
|
+
function registeredPlugin(entry, mcpByPlugin) {
|
|
82
59
|
const root = clean(entry.root);
|
|
83
60
|
if (!root) return null;
|
|
84
|
-
if (!existsSync(root))
|
|
61
|
+
if (!existsSync(root))
|
|
62
|
+
return {
|
|
63
|
+
missing: true,
|
|
64
|
+
id: clean(entry.id) || clean(entry.name) || root,
|
|
65
|
+
name: clean(entry.name) || clean(entry.id) || root.split(/[\\/]/).pop() || root,
|
|
66
|
+
};
|
|
85
67
|
const { manifest, invalid } = cachedPluginData(root);
|
|
86
68
|
const name = clean(manifest.name) || clean(manifest.id) || clean(entry.name) || root.split(/[\\/]/).pop() || root;
|
|
87
|
-
const { mcpScript, mcpInline } =
|
|
69
|
+
const { mcpScript, mcpInline } = discoverPluginMcp(root);
|
|
70
|
+
const skillCount = countSkillFiles(root);
|
|
88
71
|
const plugin = {
|
|
89
72
|
id: clean(entry.id) || name,
|
|
90
73
|
name,
|
|
@@ -101,26 +84,48 @@ export function createPluginStatus({
|
|
|
101
84
|
root,
|
|
102
85
|
installedAt: entry.installedAt || null,
|
|
103
86
|
updatedAt: entry.updatedAt || null,
|
|
104
|
-
skillCount
|
|
87
|
+
skillCount,
|
|
105
88
|
mcpScript,
|
|
106
89
|
mcpInline,
|
|
107
90
|
};
|
|
108
91
|
if (invalid) plugin.invalid = true;
|
|
109
|
-
plugin.
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
92
|
+
if (plugin.enabled && skillCount === 0 && !mcpScript && !mcpInline) {
|
|
93
|
+
plugin.searchedSkillRoots = pluginSkillsRoots(root);
|
|
94
|
+
plugin.warning = `No skills or MCP server found. Searched skill roots: ${plugin.searchedSkillRoots.join(', ')}`;
|
|
95
|
+
}
|
|
96
|
+
// Servers the plugin declares, live: name, declared key, intent/effective
|
|
97
|
+
// state, connection, and declaration problems.
|
|
98
|
+
const mcp = mcpByPlugin.get(plugin.id) || { format: 'none', errors: [], servers: [] };
|
|
99
|
+
plugin.mcpFormat = mcp.format;
|
|
100
|
+
plugin.mcpErrors = mcp.errors;
|
|
101
|
+
plugin.mcpServers = mcp.servers.filter(Boolean).map((row) => ({
|
|
102
|
+
name: row.name,
|
|
103
|
+
key: row.owner?.key || '',
|
|
104
|
+
enabled: row.enabled !== false,
|
|
105
|
+
serverEnabled: row.serverEnabled !== false,
|
|
106
|
+
connected: row.connected === true,
|
|
107
|
+
status: row.status,
|
|
108
|
+
error: row.error || null,
|
|
109
|
+
warnings: row.warnings || [],
|
|
110
|
+
cwdDependent: row.owner?.cwdDependent === true,
|
|
111
|
+
}));
|
|
112
|
+
plugin.mcpServerName = plugin.mcpServers[0]?.name || '';
|
|
113
|
+
plugin.mcpEnabled = plugin.mcpServers.some((server) => server.serverEnabled);
|
|
113
114
|
return plugin;
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
function pluginsStatus() {
|
|
117
|
-
const config = getConfig();
|
|
118
118
|
const dataDir = cfgMod.getPluginData?.();
|
|
119
|
-
|
|
119
|
+
let mcpByPlugin = new Map();
|
|
120
|
+
try {
|
|
121
|
+
mcpByPlugin = pluginMcpSummary();
|
|
122
|
+
} catch {
|
|
123
|
+
/* status still lists plugins without their MCP rows */
|
|
124
|
+
}
|
|
120
125
|
const plugins = [];
|
|
121
126
|
const missing = [];
|
|
122
127
|
for (const entry of listRegisteredPlugins({ dataDir })) {
|
|
123
|
-
const plugin = registeredPlugin(entry,
|
|
128
|
+
const plugin = registeredPlugin(entry, mcpByPlugin);
|
|
124
129
|
if (plugin?.missing) missing.push({ id: plugin.id, name: plugin.name, enabled: entry.enabled !== false });
|
|
125
130
|
else if (plugin) plugins.push(plugin);
|
|
126
131
|
}
|
|
@@ -16,11 +16,11 @@ export function subscribeGlobalExtensionChanges(listener) {
|
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
export async function publishGlobalExtensionChange(kind, originId = null) {
|
|
19
|
+
export async function publishGlobalExtensionChange(kind, originId = null, detail = null) {
|
|
20
20
|
const jobs = [];
|
|
21
21
|
for (const [id, listener] of subscribers) {
|
|
22
22
|
if (id === originId) continue;
|
|
23
|
-
jobs.push(Promise.resolve().then(() => listener(kind)));
|
|
23
|
+
jobs.push(Promise.resolve().then(() => listener(kind, detail)));
|
|
24
24
|
}
|
|
25
25
|
await Promise.allSettled(jobs);
|
|
26
26
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
import { executeBrowserTool } from '../../runtime/browser-bridge/client.mjs';
|
|
4
4
|
import { executeComputerTool } from '../../runtime/computer-bridge/client.mjs';
|
|
5
5
|
import { executeTerminalTool } from '../../runtime/terminal-bridge/client.mjs';
|
|
6
|
-
import {
|
|
6
|
+
import { executeVisualizeTool } from '../../runtime/visualize-bridge/client.mjs';
|
|
7
|
+
import { featureEnvOverride, moduleEnabled } from '../../runtime/agent/orchestrator/runtime-core/config-helpers.mjs';
|
|
7
8
|
import { createCallerContextResolvers } from './caller-context.mjs';
|
|
8
9
|
|
|
9
10
|
export function createBridgeToolHandlers({ rt }) {
|
|
@@ -50,5 +51,29 @@ export function createBridgeToolHandlers({ rt }) {
|
|
|
50
51
|
});
|
|
51
52
|
};
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
const visualize = async (args, { callerCtx, callerCwd }) => {
|
|
55
|
+
if (environmentDisabled(callerCtx, 'MIXDOG_FEATURE_VISUALIZE')) {
|
|
56
|
+
throw new Error('the visualize tool is disabled in this environment');
|
|
57
|
+
}
|
|
58
|
+
// A conversation keeps its frozen tool list, so honor the Settings toggle
|
|
59
|
+
// at execution time too (an env override wins, as in builtinFeatureActive).
|
|
60
|
+
if (!(featureEnvOverride('MIXDOG_FEATURE_VISUALIZE') ?? moduleEnabled(rt.config, 'visualize', true))) {
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: 'Error: Visualize is turned off in Settings by the user. Do not turn it back on unless the user asks; give the file path instead.',
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
isError: true,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return await executeVisualizeTool(args, {
|
|
72
|
+
sessionId: sessionIdFor(callerCtx),
|
|
73
|
+
cwd: callerCwd,
|
|
74
|
+
signal: signalFor(callerCtx),
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
return { browser, browser_devtools: browser, computer, terminal, visualize };
|
|
54
79
|
}
|
|
@@ -5,9 +5,40 @@ import { resolve } from 'node:path';
|
|
|
5
5
|
import { listProjects } from '../../runtime/shared/projects.mjs';
|
|
6
6
|
import { clean } from '../../runtime/agent/orchestrator/runtime-core/session-text.mjs';
|
|
7
7
|
|
|
8
|
-
export function createWorkspaceToolHandlers({
|
|
8
|
+
export function createWorkspaceToolHandlers({
|
|
9
|
+
rt,
|
|
10
|
+
goalRuntime,
|
|
11
|
+
agentTool,
|
|
12
|
+
notifyFnForSession,
|
|
13
|
+
applyResolvedCwd,
|
|
14
|
+
getWorkspaceApi = () => null,
|
|
15
|
+
}) {
|
|
16
|
+
// The Lead's own worktree, on the user's request: the same authority the
|
|
17
|
+
// branch chip menu drives, tolerating the calling turn but nothing else
|
|
18
|
+
// running (workers, background shells).
|
|
19
|
+
const worktreeAction = async (action, args, callerCtx) => {
|
|
20
|
+
const workspace = getWorkspaceApi();
|
|
21
|
+
if (!workspace) throw new Error(`cwd: ${action} is unavailable in this runtime`);
|
|
22
|
+
const callerSessionId = callerCtx?.callerSessionId || rt.session?.id || null;
|
|
23
|
+
if (!rt.session?.id || callerSessionId !== rt.session.id) {
|
|
24
|
+
throw new Error(`cwd: ${action} belongs to the Lead session`);
|
|
25
|
+
}
|
|
26
|
+
const result =
|
|
27
|
+
action === 'merge-worktree'
|
|
28
|
+
? await workspace.mergeWorkspace({ confirmed: true }, { ownTurn: true })
|
|
29
|
+
: action === 'remove-worktree'
|
|
30
|
+
? await workspace.removeWorktree({ force: args?.force === true }, { ownTurn: true })
|
|
31
|
+
: action === 'list-worktrees'
|
|
32
|
+
? await workspace.listWorktrees()
|
|
33
|
+
: await workspace.pruneWorktrees();
|
|
34
|
+
return JSON.stringify({ action, cwd: rt.currentCwd, result }, null, 2);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const WORKTREE_ACTIONS = new Set(['merge-worktree', 'remove-worktree', 'list-worktrees', 'prune-worktrees']);
|
|
38
|
+
|
|
9
39
|
const cwd = async (args, { callerCtx, callerCwd }) => {
|
|
10
40
|
const action = clean(args?.action || (args?.path ? 'set' : 'get')).toLowerCase();
|
|
41
|
+
if (WORKTREE_ACTIONS.has(action)) return worktreeAction(action, args, callerCtx);
|
|
11
42
|
let currentCwd = callerCwd;
|
|
12
43
|
if (action === 'list') {
|
|
13
44
|
return JSON.stringify(
|