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,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep coalesced child observations private until the caller's exact windows
|
|
3
|
+
* have been restored. A fresh child may outlive the batch's original cache
|
|
4
|
+
* generation; retain that child's lease, never renew the whole batch.
|
|
5
|
+
*/
|
|
6
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
7
|
+
import { canPublishReadCache, runReadCachePublication } from '../cache-layers.mjs';
|
|
8
|
+
import { fileVersion, sameFileVersion } from '../file-version.mjs';
|
|
9
|
+
import { mergeReadRanges } from '../read-ranges.mjs';
|
|
10
|
+
|
|
11
|
+
// Streamed reads have numbered rows but no total-line footer. Prefer the
|
|
12
|
+
// rows themselves so smart-elided gaps never become delivered evidence.
|
|
13
|
+
export function renderedBatchReadRanges(body) {
|
|
14
|
+
if (typeof body !== 'string') return [];
|
|
15
|
+
const rows = [...body.matchAll(/^(\d+)[\t│→]/gm)]
|
|
16
|
+
.map((match) => Number(match[1]))
|
|
17
|
+
.filter((line) => Number.isSafeInteger(line) && line > 0)
|
|
18
|
+
.map((line) => ({ startLine: line, endLine: line }));
|
|
19
|
+
if (rows.length) return mergeReadRanges(rows);
|
|
20
|
+
const footer = /\[lines\s+(\d+)-(\d+)\s+of\s+\d+/.exec(body);
|
|
21
|
+
return footer ? mergeReadRanges([{ startLine: Number(footer[1]), endLine: Number(footer[2]) }]) : [];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function hasRenderedLines(body) {
|
|
25
|
+
return renderedBatchReadRanges(body).length > 0;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function runBatchReadSnapshotPublication(executeChildBuiltinTool, helpers, run) {
|
|
29
|
+
const tasks = [];
|
|
30
|
+
const records = [];
|
|
31
|
+
const corrections = new Map();
|
|
32
|
+
let closed = false;
|
|
33
|
+
|
|
34
|
+
function capture(task, fullPath, st, scope, meta) {
|
|
35
|
+
if (closed) return;
|
|
36
|
+
const identity = fileVersion(st);
|
|
37
|
+
const record = {
|
|
38
|
+
fullPath,
|
|
39
|
+
scope: scope ?? null,
|
|
40
|
+
identity,
|
|
41
|
+
ranges: mergeReadRanges(meta?.ranges),
|
|
42
|
+
isCurrent: () => false,
|
|
43
|
+
publish: () => {},
|
|
44
|
+
release: () => {},
|
|
45
|
+
done: Promise.resolve(),
|
|
46
|
+
};
|
|
47
|
+
task.records.push(record);
|
|
48
|
+
records.push(record);
|
|
49
|
+
// Acquiring a new lease must not rehabilitate an already stale producer.
|
|
50
|
+
if (!identity || !canPublishReadCache(fullPath)) return;
|
|
51
|
+
const decision = Promise.withResolvers();
|
|
52
|
+
record.publish = (replacement = meta) => decision.resolve(replacement);
|
|
53
|
+
record.release = () => decision.resolve(null);
|
|
54
|
+
record.done = runReadCachePublication(fullPath, async () => {
|
|
55
|
+
record.isCurrent = AsyncLocalStorage.bind(() => canPublishReadCache(fullPath));
|
|
56
|
+
const selected = await decision.promise;
|
|
57
|
+
if (selected) helpers._recordReadSnapshot(fullPath, identity, scope, selected);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const executeChild = async (name, args, workDir, options = {}) => {
|
|
62
|
+
if (name !== 'read' || (args.mode && args.mode !== 'full')) {
|
|
63
|
+
return executeChildBuiltinTool(name, args, workDir, options);
|
|
64
|
+
}
|
|
65
|
+
const task = {
|
|
66
|
+
entry: args,
|
|
67
|
+
fullPath: helpers.resolveAgainstCwd(args.path, workDir),
|
|
68
|
+
records: [],
|
|
69
|
+
body: null,
|
|
70
|
+
};
|
|
71
|
+
tasks.push(task);
|
|
72
|
+
task.body = await executeChildBuiltinTool(name, args, workDir, {
|
|
73
|
+
...options,
|
|
74
|
+
_readSnapshotRecorder: (...snapshot) => capture(task, ...snapshot),
|
|
75
|
+
});
|
|
76
|
+
return task.body;
|
|
77
|
+
};
|
|
78
|
+
const snapshotHelpers = {
|
|
79
|
+
...helpers,
|
|
80
|
+
_filterReadSnapshotRanges: (entry, fullPath, scope, ranges) => {
|
|
81
|
+
const task = tasks.find((task) => task.entry === entry);
|
|
82
|
+
const observation = task?.records.findLast(
|
|
83
|
+
(record) => record.fullPath === fullPath && record.scope === (scope ?? null)
|
|
84
|
+
);
|
|
85
|
+
// A numbered row may end halfway through its content at the byte cap.
|
|
86
|
+
// Only this slot's producer can certify complete lines; another read of
|
|
87
|
+
// the same path must not authorize hashing this slot's partial text.
|
|
88
|
+
return mergeReadRanges(ranges.flatMap((range) => (observation?.ranges ?? []).map((complete) => ({
|
|
89
|
+
startLine: Math.max(range.startLine, complete.startLine),
|
|
90
|
+
endLine: Math.min(range.endLine, complete.endLine),
|
|
91
|
+
}))));
|
|
92
|
+
},
|
|
93
|
+
_recordReadSnapshot: (fullPath, _st, scope, meta) => {
|
|
94
|
+
corrections.set(fullPath, { scope: scope ?? null, meta });
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
try {
|
|
99
|
+
const result = await run(executeChild, snapshotHelpers);
|
|
100
|
+
// reject_partial refuses the entire payload: staged bodies were not
|
|
101
|
+
// delivered, so none of their snapshots may become session evidence.
|
|
102
|
+
if (result?.isError === true || (typeof result === 'string' && helpers.classifyResultKind(result) === 'error')) {
|
|
103
|
+
return result;
|
|
104
|
+
}
|
|
105
|
+
for (const [fullPath, correction] of corrections) {
|
|
106
|
+
const delivered = tasks.filter((task) => task.fullPath === fullPath && hasRenderedLines(task.body));
|
|
107
|
+
const observations = delivered.map((task) => task.records.findLast(
|
|
108
|
+
(record) => record.fullPath === fullPath && record.scope === correction.scope
|
|
109
|
+
));
|
|
110
|
+
// Never combine different versions or bless stale/missing observations.
|
|
111
|
+
// The eventual recorder rechecks the retained lease after all awaits.
|
|
112
|
+
if (observations.length && observations.every((record) =>
|
|
113
|
+
record?.isCurrent() && sameFileVersion(record.identity, observations[0].identity)
|
|
114
|
+
)) {
|
|
115
|
+
observations[0].publish(correction.meta);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Rich media/binary results have no line slices. Preserve their original
|
|
119
|
+
// evidence, still under the producer's lease rather than the batch's.
|
|
120
|
+
for (const task of tasks) {
|
|
121
|
+
if (hasRenderedLines(task.body) || task.body?.isError === true ||
|
|
122
|
+
(typeof task.body === 'string' && helpers.classifyResultKind(task.body) === 'error')) continue;
|
|
123
|
+
for (const record of task.records) {
|
|
124
|
+
if (!corrections.has(record.fullPath)) record.publish();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
for (const record of records) record.release();
|
|
128
|
+
await Promise.all(records.map((record) => record.done));
|
|
129
|
+
return result;
|
|
130
|
+
} finally {
|
|
131
|
+
// Promise.all in the dispatcher may reject while other children remain
|
|
132
|
+
// active. Late captures then become no-ops and cannot leak a held lease.
|
|
133
|
+
closed = true;
|
|
134
|
+
for (const record of records) record.release();
|
|
135
|
+
await Promise.allSettled(records.map((record) => record.done));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readFile } from 'node:fs/promises';
|
|
8
8
|
import { rangeTextOf } from '../snapshot-helpers.mjs';
|
|
9
|
+
import { renderedBatchReadRanges } from './batch-publication.mjs';
|
|
9
10
|
|
|
10
11
|
// Coalesced batch reads fetch the union window from disk; every caller slot
|
|
11
12
|
// must be sliced back to its original request window (_orig*), not the
|
|
@@ -32,22 +33,22 @@ export function restoreCallerSlots(results, origEntries, entryMap, options, { sl
|
|
|
32
33
|
function deliveredRangesByPath(
|
|
33
34
|
orderedResults,
|
|
34
35
|
workDir,
|
|
35
|
-
|
|
36
|
+
readStateScope,
|
|
37
|
+
{ classifyResultKind, resolveAgainstCwd, _rangeHashesFromRenderedReadText, _filterReadSnapshotRanges }
|
|
36
38
|
) {
|
|
37
39
|
const exactRangesByPath = new Map();
|
|
38
40
|
const rangeHashesByPath = new Map();
|
|
39
41
|
for (const r of orderedResults) {
|
|
40
42
|
if (r?.mode !== 'full' || classifyResultKind(String(r.body || '')) === 'error') continue;
|
|
41
|
-
const m = String(r.body || '').match(/\[lines\s+(\d+)-(\d+)\s+of\s+(\d+)/);
|
|
42
|
-
if (!m) continue;
|
|
43
|
-
const startLine = Number(m[1]);
|
|
44
|
-
const endLine = Number(m[2]);
|
|
45
|
-
if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) continue;
|
|
46
43
|
const fullPath = resolveAgainstCwd(r.path, workDir);
|
|
44
|
+
const renderedRanges = renderedBatchReadRanges(r.body);
|
|
45
|
+
const ranges = _filterReadSnapshotRanges
|
|
46
|
+
? _filterReadSnapshotRanges(r.readEntry, fullPath, readStateScope, renderedRanges)
|
|
47
|
+
: renderedRanges;
|
|
48
|
+
if (ranges.length === 0) continue;
|
|
47
49
|
if (!exactRangesByPath.has(fullPath)) exactRangesByPath.set(fullPath, []);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const renderedHashes = _rangeHashesFromRenderedReadText(r.body, [range]);
|
|
50
|
+
exactRangesByPath.get(fullPath).push(...ranges);
|
|
51
|
+
const renderedHashes = _rangeHashesFromRenderedReadText(r.body, ranges);
|
|
51
52
|
if (renderedHashes.length > 0) {
|
|
52
53
|
if (!rangeHashesByPath.has(fullPath)) rangeHashesByPath.set(fullPath, []);
|
|
53
54
|
rangeHashesByPath.get(fullPath).push(...renderedHashes);
|
|
@@ -68,7 +69,7 @@ async function rawRangeHashes(fullPath, mergedRanges, { _hashText }) {
|
|
|
68
69
|
|
|
69
70
|
export async function recordSlicedBatchSnapshots(orderedResults, workDir, readStateScope, helpers) {
|
|
70
71
|
const { _mergeReadRanges, _recordReadSnapshot } = helpers;
|
|
71
|
-
const { exactRangesByPath, rangeHashesByPath } = deliveredRangesByPath(orderedResults, workDir, helpers);
|
|
72
|
+
const { exactRangesByPath, rangeHashesByPath } = deliveredRangesByPath(orderedResults, workDir, readStateScope, helpers);
|
|
72
73
|
for (const [fullPath, ranges] of exactRangesByPath) {
|
|
73
74
|
const mergedRanges = _mergeReadRanges(ranges);
|
|
74
75
|
let rangeHashes = rangeHashesByPath.get(fullPath) || [];
|
|
@@ -18,6 +18,8 @@ import { normalizeObjectBatchArgs, batchEntriesFromArgs, primaryReadIndexes } fr
|
|
|
18
18
|
import { dispatchBatchReads } from './read-tool/batch-dispatch.mjs';
|
|
19
19
|
import { restoreCallerSlots, recordSlicedBatchSnapshots } from './read-tool/batch-snapshot.mjs';
|
|
20
20
|
import { renderBatchResults } from './read-tool/batch-render.mjs';
|
|
21
|
+
import { runBatchReadSnapshotPublication } from './read-tool/batch-publication.mjs';
|
|
22
|
+
import { runReadCachePublication } from './cache-layers.mjs';
|
|
21
23
|
|
|
22
24
|
function hasLineCoordinate(path) {
|
|
23
25
|
return typeof path === 'string' && /(?:#L\d+|:\d+(?:-\d+)?(?::|$))/i.test(path);
|
|
@@ -54,17 +56,25 @@ async function executeBatchRead(args, workDir, readStateScope, executeChildBuilt
|
|
|
54
56
|
const origEntries = Array.isArray(args._readsOrigEntries) ? args._readsOrigEntries : null;
|
|
55
57
|
const entryMap = Array.isArray(args._readsEntryToDeduped) ? args._readsEntryToDeduped : null;
|
|
56
58
|
const readIndexFor = primaryReadIndexes(entries, overrides, (p) => helpers.resolveAgainstCwd(p, workDir));
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
const run = async (executeChild = executeChildBuiltinTool, snapshotHelpers = helpers) => {
|
|
60
|
+
const results = await dispatchBatchReads({
|
|
61
|
+
entries,
|
|
62
|
+
readIndexFor,
|
|
63
|
+
workDir,
|
|
64
|
+
options,
|
|
65
|
+
helpers,
|
|
66
|
+
executeChildBuiltinTool: executeChild,
|
|
67
|
+
});
|
|
68
|
+
const orderedResults = origEntries ? restoreCallerSlots(results, origEntries, entryMap, options, helpers) : results;
|
|
69
|
+
if (origEntries) await recordSlicedBatchSnapshots(orderedResults, workDir, readStateScope, snapshotHelpers);
|
|
70
|
+
return renderBatchResults(orderedResults, args, helpers);
|
|
71
|
+
};
|
|
72
|
+
if (!origEntries) return run();
|
|
73
|
+
// Keep the original batch lease for glance/media paths outside the scalar
|
|
74
|
+
// pipeline. Sliced evidence uses the producing child's retained lease, so
|
|
75
|
+
// a hash rejection never requires renewing this potentially stale parent.
|
|
76
|
+
const paths = entries.filter((entry) => entry?.path).map((entry) => helpers.resolveAgainstCwd(entry.path, workDir));
|
|
77
|
+
return runReadCachePublication(paths, () => runBatchReadSnapshotPublication(executeChildBuiltinTool, helpers, run));
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
// Glance modes served by their own child tool: the tool name and the args it
|
|
@@ -102,7 +102,7 @@ function findRepositoryRoot(startDirectory, { maxDepth = 64 } = {}) {
|
|
|
102
102
|
const marker = pathJoin(current, '.git');
|
|
103
103
|
try {
|
|
104
104
|
const stats = statSync(marker);
|
|
105
|
-
return { root: current, gitDirectory: stats.isDirectory() ? marker :
|
|
105
|
+
return { root: current, gitDirectory: stats.isDirectory() ? marker : _linkedGitDirectory(marker, current) };
|
|
106
106
|
} catch {}
|
|
107
107
|
const parent = pathDirname(current);
|
|
108
108
|
if (parent === current) break;
|
|
@@ -111,6 +111,17 @@ function findRepositoryRoot(startDirectory, { maxDepth = 64 } = {}) {
|
|
|
111
111
|
return null;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
+
// A linked worktree's `.git` file names its own git directory (`gitdir: …`),
|
|
115
|
+
// which holds that worktree's HEAD.
|
|
116
|
+
function _linkedGitDirectory(markerFile, root) {
|
|
117
|
+
try {
|
|
118
|
+
const target = /^gitdir:\s*(.+)$/m.exec(readFileSync(markerFile, 'utf8'))?.[1]?.trim();
|
|
119
|
+
return target ? pathResolveAbsolute(root, target) : null;
|
|
120
|
+
} catch {
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
114
125
|
function _headBranch(gitDirectory) {
|
|
115
126
|
if (!gitDirectory) return null;
|
|
116
127
|
try {
|
|
@@ -254,8 +254,8 @@ function absolutePaths(lines, rgCwd) {
|
|
|
254
254
|
return lines.map((line) => (isAbsolute(line) ? line : resolveAgainstCwd(line, rgCwd)));
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
-
function missingGlobRoot(scan, root, rgCwd, err) {
|
|
258
|
-
const hint = buildNotFoundHint(scan.workDir, rgCwd, 'Search', err?.code);
|
|
257
|
+
async function missingGlobRoot(scan, root, rgCwd, err) {
|
|
258
|
+
const hint = await buildNotFoundHint(scan.workDir, rgCwd, 'Search', err?.code);
|
|
259
259
|
return {
|
|
260
260
|
error: `path does not exist: ${normalizeOutputPath(rgCwd)} (${err?.code || 'ENOENT'})${finalizeReadFamilyEnoentTail(hint, root, err?.code)}`,
|
|
261
261
|
paths: [],
|
|
@@ -422,14 +422,14 @@ function globMoreSuffix({ rgWindowIncomplete, remaining, totalBeforeOffset, next
|
|
|
422
422
|
return '';
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
-
function globEmptyDiagnostic(scan, { totalBeforeOffset }) {
|
|
425
|
+
async function globEmptyDiagnostic(scan, { totalBeforeOffset }) {
|
|
426
426
|
const { patterns, basePath, baseEntries, workDir, statCache, offset } = scan;
|
|
427
427
|
const patternStr = patterns.length === 1 ? JSON.stringify(patterns[0]) : JSON.stringify(patterns);
|
|
428
428
|
const baseLabel = normalizeOutputPath(basePath);
|
|
429
429
|
if (totalBeforeOffset > 0 && offset >= totalBeforeOffset) {
|
|
430
430
|
return `(no entries after offset=${offset}; total=${totalBeforeOffset}) pattern=${patternStr} path=${baseLabel}`;
|
|
431
431
|
}
|
|
432
|
-
const diagnostic = basePathDiagnostic(
|
|
432
|
+
const diagnostic = await basePathDiagnostic(
|
|
433
433
|
baseEntries.map((e) => e.root),
|
|
434
434
|
workDir,
|
|
435
435
|
statCache
|
|
@@ -473,7 +473,7 @@ async function scanGlobGroups(scan) {
|
|
|
473
473
|
const shown = await directAbsoluteMatches({ patterns, headLimit, offset });
|
|
474
474
|
if (shown.length > 0) return patternCapNote + shown.join('\n');
|
|
475
475
|
}
|
|
476
|
-
const out = patternCapNote + renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss });
|
|
476
|
+
const out = patternCapNote + (await renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }));
|
|
477
477
|
settleGlobScanCache(scan, merged, out, remaining);
|
|
478
478
|
reportToolProgress(options, `found ${totalBeforeOffset} files`);
|
|
479
479
|
return out;
|
|
@@ -482,7 +482,7 @@ async function scanGlobGroups(scan) {
|
|
|
482
482
|
// The page body with its more/integrity suffixes. Empty partials are not
|
|
483
483
|
// proof of absence: diagnostics are preserved even when there is no path
|
|
484
484
|
// body, rather than silently dropping the integrity suffix.
|
|
485
|
-
function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }) {
|
|
485
|
+
async function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, miss }) {
|
|
486
486
|
const { offset } = scan;
|
|
487
487
|
const errSuffix = globIntegritySuffix(merged);
|
|
488
488
|
const moreSuffix = globMoreSuffix({
|
|
@@ -492,7 +492,7 @@ function renderGlobPage(scan, merged, { capped, remaining, totalBeforeOffset, mi
|
|
|
492
492
|
nextOffset: offset + capped.length,
|
|
493
493
|
});
|
|
494
494
|
if (capped.length > 0) return `${capped.join('\n')}${moreSuffix}${errSuffix}`;
|
|
495
|
-
const emptyDiag = miss ? globEmptyDiagnostic(scan, { totalBeforeOffset }) : '';
|
|
495
|
+
const emptyDiag = miss ? await globEmptyDiagnostic(scan, { totalBeforeOffset }) : '';
|
|
496
496
|
const emptyBody = (emptyDiag || '(no files found)').replace(
|
|
497
497
|
'(no files found)',
|
|
498
498
|
merged.rgStdoutPartial || merged.rgWindowIncomplete ? '(no files found yet)' : '(no files found)'
|
|
@@ -608,9 +608,12 @@ export async function executeGlobTool(args, workDir, options = {}) {
|
|
|
608
608
|
};
|
|
609
609
|
return await runScopedSearchInFlight(
|
|
610
610
|
cacheKey,
|
|
611
|
-
({ signal: sharedSignal, scopedCacheOutcome }) =>
|
|
612
|
-
|
|
613
|
-
|
|
611
|
+
({ signal: sharedSignal, scopedCacheOutcome }) =>
|
|
612
|
+
scanGlobGroups({
|
|
613
|
+
...scan,
|
|
614
|
+
sharedSignal,
|
|
615
|
+
options: { ...options, scopedCacheOutcome },
|
|
616
|
+
}),
|
|
614
617
|
{ signal: options?.signal || options?.abortSignal || null, scopes },
|
|
615
618
|
options?.scopedCacheOutcome
|
|
616
619
|
);
|
|
@@ -211,7 +211,7 @@ async function runGrepSearch(request, sharedSignal) {
|
|
|
211
211
|
|
|
212
212
|
async function grepMissingPathResult({ workDir, grepResolvedPath, searchPath, executeChildBuiltinTool }, err) {
|
|
213
213
|
const msg = `Error: path does not exist: ${normalizeOutputPath(grepResolvedPath)} (${err?.code || 'ENOENT'})`;
|
|
214
|
-
let hint = buildNotFoundHintBody(workDir, grepResolvedPath, 'Search', err?.code, {});
|
|
214
|
+
let hint = await buildNotFoundHintBody(workDir, grepResolvedPath, 'Search', err?.code, {});
|
|
215
215
|
if (!hint) hint = await _suggestIndexedPaths(grepResolvedPath, executeChildBuiltinTool, workDir);
|
|
216
216
|
return msg + finalizeReadFamilyEnoentTail(appendNotFoundCwdNote(hint, workDir, err?.code), searchPath, err?.code);
|
|
217
217
|
}
|
|
@@ -65,10 +65,10 @@ const ENOENT_ABSENT =
|
|
|
65
65
|
|
|
66
66
|
// Share diagnostic lookup results within one invocation without changing
|
|
67
67
|
// the requested operand or executing a replacement operation.
|
|
68
|
-
function cachedFileByBasename(workDir, missingPath, cache) {
|
|
68
|
+
async function cachedFileByBasename(workDir, missingPath, cache) {
|
|
69
69
|
if (!cache) return findFileByBasename(workDir, missingPath);
|
|
70
70
|
if (!('fileHits' in cache)) cache.fileHits = findFileByBasename(workDir, missingPath);
|
|
71
|
-
return cache.fileHits;
|
|
71
|
+
return await cache.fileHits;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// A missing ABSOLUTE path that resolves outside the project cannot be
|
|
@@ -180,15 +180,15 @@ export function appendNotFoundCwdNote(hint, workDir, errCode = 'ENOENT') {
|
|
|
180
180
|
return `${hint} (relative paths resolve from ${normalizeOutputPath(workDir)})`;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
export function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
|
|
183
|
+
export async function buildNotFoundHint(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
|
|
184
184
|
return appendNotFoundCwdNote(
|
|
185
|
-
buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode, cache),
|
|
185
|
+
await buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode, cache),
|
|
186
186
|
workDir,
|
|
187
187
|
errCode
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
export function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
|
|
191
|
+
export async function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode = 'ENOENT', cache = null) {
|
|
192
192
|
if (!NOT_FOUND_CODES.has(String(errCode || 'ENOENT'))) return '';
|
|
193
193
|
// `C:tmpsmp` shape: a Windows path whose separators were eaten by JSON
|
|
194
194
|
// escaping (`"C:\\tmp\\smp"` written as `"C:\tmp\smp"`). Name the cause —
|
|
@@ -200,12 +200,12 @@ export function buildNotFoundHintBody(workDir, missingPath, actionVerb, errCode
|
|
|
200
200
|
if (missingPathIsOutsideProject(workDir, missingPath)) {
|
|
201
201
|
return outsideProjectNotFoundHint(missingPath, actionVerb);
|
|
202
202
|
}
|
|
203
|
-
const elsewhere = cachedFileByBasename(workDir, missingPath, cache);
|
|
203
|
+
const elsewhere = await cachedFileByBasename(workDir, missingPath, cache);
|
|
204
204
|
if (elsewhere.length) {
|
|
205
205
|
return ` Not found at this path; the same filename exists at: ${elsewhere.map((p) => `"${normalizeOutputPath(p)}"`).join(', ')}. ${actionVerb} that path directly.`;
|
|
206
206
|
}
|
|
207
207
|
if (isDirectoryPathGuess(missingPath)) {
|
|
208
|
-
const dirHits = findDirectoryByBasename(workDir, missingPath, { limit: 5 });
|
|
208
|
+
const dirHits = await findDirectoryByBasename(workDir, missingPath, { limit: 5 });
|
|
209
209
|
if (dirHits.length > 1) {
|
|
210
210
|
return ` Not found at this path; same-named directories exist at: ${dirHits
|
|
211
211
|
.slice(0, 5)
|
|
@@ -268,9 +268,9 @@ export function uncRefusalMessage(toolName, original, resolved) {
|
|
|
268
268
|
return `Error: ${toolName} refuses UNC/SMB path ${JSON.stringify(shown)}; remote share access is blocked to prevent NTLM credential leaks`;
|
|
269
269
|
}
|
|
270
270
|
|
|
271
|
-
export function basePathDiagnostic(basePaths, workDir, statCache = null) {
|
|
272
|
-
|
|
273
|
-
.map((basePath) => {
|
|
271
|
+
export async function basePathDiagnostic(basePaths, workDir, statCache = null) {
|
|
272
|
+
const parts = await Promise.all(
|
|
273
|
+
basePaths.map(async (basePath) => {
|
|
274
274
|
const resolved = resolveSearchScope(basePath, workDir);
|
|
275
275
|
// Reuse the caller's call-scoped stat cache (glob preflight + per-group
|
|
276
276
|
// rg runs already stat'd this same resolved root) instead of re-stating.
|
|
@@ -291,8 +291,13 @@ export function basePathDiagnostic(basePaths, workDir, statCache = null) {
|
|
|
291
291
|
}
|
|
292
292
|
return (
|
|
293
293
|
`${normalizeOutputPath(basePath)}: path does not exist (${err?.code || 'ENOENT'})` +
|
|
294
|
-
finalizeReadFamilyEnoentTail(
|
|
294
|
+
finalizeReadFamilyEnoentTail(
|
|
295
|
+
await buildNotFoundHint(workDir, resolved, 'Search', err?.code),
|
|
296
|
+
basePath,
|
|
297
|
+
err?.code
|
|
298
|
+
)
|
|
295
299
|
);
|
|
296
300
|
})
|
|
297
|
-
|
|
301
|
+
);
|
|
302
|
+
return parts.join('; ');
|
|
298
303
|
}
|
|
@@ -180,7 +180,10 @@ async function waitForShellTask(task, taskId, args, options) {
|
|
|
180
180
|
return interruptedByUser;
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
|
|
183
|
+
// Outcome of a shell cancel: `outcome` is 'cancelled' only when the process is
|
|
184
|
+
// confirmed gone (or never had one), 'unconfirmed' when the kill was delivered
|
|
185
|
+
// but the exit could not be confirmed, 'not-found' for an unknown task.
|
|
186
|
+
async function cancelShellTaskOutcome(task, taskId, options) {
|
|
184
187
|
// A tracked task with no native job is still cancellable: the
|
|
185
188
|
// registry's own cancel hook owns it (descendants that outlived
|
|
186
189
|
// their shell). Remember that it WAS live so the outcome is
|
|
@@ -196,11 +199,14 @@ async function cancelShellTask(task, taskId, options) {
|
|
|
196
199
|
if (confirmed && confirmed.status === 'running') {
|
|
197
200
|
// Watcher and notify context stay armed: the eventual exit
|
|
198
201
|
// must still reach the owner.
|
|
199
|
-
return
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
202
|
+
return {
|
|
203
|
+
outcome: 'unconfirmed',
|
|
204
|
+
text: [
|
|
205
|
+
'status: cancel-unconfirmed',
|
|
206
|
+
`task_id: ${taskId}`,
|
|
207
|
+
'the cancellation was delivered but the process has not exited yet. It stays tracked and its completion is still delivered; re-run task cancel or task read to check again.',
|
|
208
|
+
].join('\n'),
|
|
209
|
+
};
|
|
204
210
|
}
|
|
205
211
|
if (!confirmed) {
|
|
206
212
|
// No terminal event and no observable task: absence of
|
|
@@ -209,19 +215,56 @@ async function cancelShellTask(task, taskId, options) {
|
|
|
209
215
|
cancelBackgroundShellJobWatch(taskId);
|
|
210
216
|
clearShellJobNotifyCtx(taskId);
|
|
211
217
|
cancelBackgroundTask(taskId, 'cancelled by task control; termination unconfirmed');
|
|
212
|
-
return
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
218
|
+
return {
|
|
219
|
+
outcome: 'unconfirmed',
|
|
220
|
+
text: [
|
|
221
|
+
'status: cancel-unconfirmed',
|
|
222
|
+
`task_id: ${taskId}`,
|
|
223
|
+
'the cancellation was delivered but this task is no longer observable, so its termination could not be confirmed. Verify the process directly if it must be gone.',
|
|
224
|
+
].join('\n'),
|
|
225
|
+
};
|
|
217
226
|
}
|
|
218
227
|
}
|
|
219
228
|
cancelBackgroundShellJobWatch(taskId);
|
|
220
229
|
clearShellJobNotifyCtx(taskId);
|
|
221
230
|
cancelBackgroundTask(taskId, 'cancelled by task control');
|
|
222
231
|
return job || wasRunning
|
|
223
|
-
?
|
|
224
|
-
|
|
232
|
+
? {
|
|
233
|
+
outcome: 'cancelled',
|
|
234
|
+
text: renderTaskCancelSuccess(taskId, getBackgroundTask(taskId, { context: options }) || task),
|
|
235
|
+
}
|
|
236
|
+
: { outcome: 'not-found', text: buildJobNotFoundMessage(taskId) };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function cancelShellTask(task, taskId, options) {
|
|
240
|
+
return (await cancelShellTaskOutcome(task, taskId, options)).text;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Stop one BACKGROUND shell job of the caller's session (desktop stop button).
|
|
245
|
+
* The same cancel the `task` tool runs, with a structured answer: `stopped` is
|
|
246
|
+
* true only when the job was actually cancelled. Only promoted/async jobs hold
|
|
247
|
+
* a background task, so a foreground command can never match; the session
|
|
248
|
+
* scope is the caller's, so another session's job reads as unknown.
|
|
249
|
+
*/
|
|
250
|
+
export async function stopBackgroundShellJob(taskId, options = {}) {
|
|
251
|
+
const id = typeof taskId === 'string' ? taskId.trim() : '';
|
|
252
|
+
if (!id) return { stopped: false, error: 'task_id is required' };
|
|
253
|
+
const task = getBackgroundTask(id, { context: options });
|
|
254
|
+
if (!task || task.surface !== 'shell') return { stopped: false, error: `task not found: ${id}` };
|
|
255
|
+
const job = peekShellJob(id);
|
|
256
|
+
const status = job ? job.status : task.status;
|
|
257
|
+
if (status !== 'running') return { stopped: false, error: `task already ${status}: ${id}` };
|
|
258
|
+
try {
|
|
259
|
+
const { outcome, text } = await cancelShellTaskOutcome(task, id, options);
|
|
260
|
+
if (outcome === 'cancelled') return { stopped: true };
|
|
261
|
+
return {
|
|
262
|
+
stopped: false,
|
|
263
|
+
error: outcome === 'unconfirmed' ? `cancellation not confirmed: ${id}` : text.replace(/^Error:\s*/, ''),
|
|
264
|
+
};
|
|
265
|
+
} catch (error) {
|
|
266
|
+
return { stopped: false, error: error instanceof Error ? error.message : String(error) };
|
|
267
|
+
}
|
|
225
268
|
}
|
|
226
269
|
|
|
227
270
|
export async function executeTaskTool(args, options = {}) {
|
|
@@ -573,6 +573,64 @@ export function listCachedCodeGraphRoots() {
|
|
|
573
573
|
}
|
|
574
574
|
}
|
|
575
575
|
|
|
576
|
+
// Remove one root (exact canonical match) from the disk cache: its resident
|
|
577
|
+
// entry, its staged call-site sidecar, its manifest row and its <hash>.json /
|
|
578
|
+
// <hash>.calls.json files. The resident drop is synchronous and happens first,
|
|
579
|
+
// so a flush already scheduled for other roots can no longer write this one
|
|
580
|
+
// back; the manifest is edited under the same cross-process lock the persist
|
|
581
|
+
// path takes, so a concurrent writer merges the result instead of restoring
|
|
582
|
+
// the row. Never creates the cache directory. Returns { removed, kept }; kept
|
|
583
|
+
// are cache files that matched but could not be deleted.
|
|
584
|
+
export function forgetDiskCodeGraphRoot(root) {
|
|
585
|
+
const key = _canonicalGraphCwd(root);
|
|
586
|
+
const removed = [];
|
|
587
|
+
const kept = [];
|
|
588
|
+
if (_diskCodeGraphCache.has(key)) removed.push(`resident:${key}`);
|
|
589
|
+
_dropDiskEntry(key);
|
|
590
|
+
if (_pendingCallsSidecars.delete(key)) removed.push(`pending-calls-sidecar:${key}`);
|
|
591
|
+
const dir = _codeGraphDiskDir();
|
|
592
|
+
if (!existsSync(dir)) return { removed, kept };
|
|
593
|
+
withFileLockSync(
|
|
594
|
+
join(dir, '.persist.lock'),
|
|
595
|
+
() => {
|
|
596
|
+
const manifest = _readPreservedManifest(dir);
|
|
597
|
+
const hashes = new Set([_hashCwd(key)]);
|
|
598
|
+
let changed = false;
|
|
599
|
+
for (const [cwd, meta] of Object.entries(manifest)) {
|
|
600
|
+
let same = false;
|
|
601
|
+
try {
|
|
602
|
+
same = _canonicalGraphCwd(cwd) === key;
|
|
603
|
+
} catch {
|
|
604
|
+
/* a malformed key is not this root */
|
|
605
|
+
}
|
|
606
|
+
if (!same) continue;
|
|
607
|
+
if (_isCodeGraphCacheHash(meta?.hash)) hashes.add(meta.hash);
|
|
608
|
+
delete manifest[cwd];
|
|
609
|
+
changed = true;
|
|
610
|
+
removed.push(`manifest:${cwd}`);
|
|
611
|
+
}
|
|
612
|
+
for (const hash of hashes) {
|
|
613
|
+
for (const file of [join(dir, `${hash}.json`), callsSidecarPath(dir, hash)]) {
|
|
614
|
+
if (!existsSync(file)) continue;
|
|
615
|
+
try {
|
|
616
|
+
unlinkSync(file);
|
|
617
|
+
removed.push(file);
|
|
618
|
+
} catch {
|
|
619
|
+
kept.push(file);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (changed) writeJsonAtomicSync(join(dir, 'manifest.json'), manifest, { compact: true, lock: false });
|
|
624
|
+
if (_diskManifest) {
|
|
625
|
+
delete _diskManifest[key];
|
|
626
|
+
if (changed) _diskManifest = manifest;
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
{ timeoutMs: 10_000 }
|
|
630
|
+
);
|
|
631
|
+
return { removed, kept };
|
|
632
|
+
}
|
|
633
|
+
|
|
576
634
|
// ── call-site sidecar ───────────────────────────────────────────────────────
|
|
577
635
|
// Resolve the sidecar file of a canonical cwd. The manifest hash wins when the
|
|
578
636
|
// entry is known (it is what the persist loop wrote); otherwise the hash is
|
|
@@ -79,10 +79,10 @@ export function resolveAggregateAnchorRoot(name, args, baseCwd, { explicitCwdArg
|
|
|
79
79
|
* One `file` anchor: an error string when it does not exist, otherwise the
|
|
80
80
|
* root it re-homes the call to (null keeps the current cwd).
|
|
81
81
|
*/
|
|
82
|
-
export function resolveFileAnchorRoot(name, args, fileArg, baseCwd) {
|
|
82
|
+
export async function resolveFileAnchorRoot(name, args, fileArg, baseCwd) {
|
|
83
83
|
const abs = _absFrom(baseCwd, fileArg);
|
|
84
84
|
if (!existsSync(abs)) {
|
|
85
|
-
const elsewhere = findFileByBasename(pathResolve(baseCwd), abs);
|
|
85
|
+
const elsewhere = await findFileByBasename(pathResolve(baseCwd), abs);
|
|
86
86
|
const hint = elsewhere.length
|
|
87
87
|
? ` Same filename exists at: ${elsewhere.map((p) => `"${toDisplayPath(p, baseCwd).replace(/\\/g, '/')}"`).join(', ')}. Use that path.`
|
|
88
88
|
: '';
|
|
@@ -345,7 +345,7 @@ async function executeCodeGraphToolRaw(name, rawArgs, cwd, signal = null, option
|
|
|
345
345
|
args = resolved.args;
|
|
346
346
|
}
|
|
347
347
|
if (fileArg && !hasAggregateFileArgs) {
|
|
348
|
-
const anchor = resolveFileAnchorRoot(name, args, fileArg, baseCwd);
|
|
348
|
+
const anchor = await resolveFileAnchorRoot(name, args, fileArg, baseCwd);
|
|
349
349
|
if (anchor.error) return anchor.error;
|
|
350
350
|
if (anchor.root) effectiveCwd = anchor.root;
|
|
351
351
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { statSync } from 'node:fs';
|
|
2
2
|
import { basename, dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
3
|
-
import { listProjects } from '../../../../shared/projects.mjs';
|
|
3
|
+
import { listProjects, removeProject } from '../../../../shared/projects.mjs';
|
|
4
4
|
import { explicitSessionCwd, readLastSessionCwd } from '../../../../shared/user-cwd.mjs';
|
|
5
5
|
import { canonicalGraphPath } from '../code-graph-state.mjs';
|
|
6
6
|
import { listCachedCodeGraphRoots } from './disk-cache.mjs';
|
|
@@ -85,6 +85,30 @@ export function collectTrustedCodeGraphRoots(
|
|
|
85
85
|
return [...found.values()].sort((a, b) => a.localeCompare(b));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Stop trusting one root as a federation target. Trust is derived from the
|
|
90
|
+
* Project registry, the session's selected cwd and the cache manifest; the
|
|
91
|
+
* manifest row goes with forgetDiskCodeGraphRoot() and a selected cwd is
|
|
92
|
+
* dropped by the directory check once the folder is gone, so the registry is
|
|
93
|
+
* the only stored source left. Only the exact root is removed (nested Projects
|
|
94
|
+
* stay). Returns { removed, kept }; kept are registered paths that matched but
|
|
95
|
+
* could not be unregistered.
|
|
96
|
+
*/
|
|
97
|
+
export function forgetTrustedCodeGraphRoot(
|
|
98
|
+
root,
|
|
99
|
+
{ registered = () => listProjects().map((entry) => entry.path), unregister = removeProject } = {}
|
|
100
|
+
) {
|
|
101
|
+
const removed = [];
|
|
102
|
+
const kept = [];
|
|
103
|
+
const key = canonicalGraphPath(root);
|
|
104
|
+
for (const path of registered()) {
|
|
105
|
+
if (!path || canonicalGraphPath(path) !== key) continue;
|
|
106
|
+
if (unregister(path)) removed.push(path);
|
|
107
|
+
else kept.push(path);
|
|
108
|
+
}
|
|
109
|
+
return { removed, kept };
|
|
110
|
+
}
|
|
111
|
+
|
|
88
112
|
export function owningTrustedCodeGraphRoot(file, roots) {
|
|
89
113
|
const matches = (roots || []).filter((root) => _pathIsWithin(root, file));
|
|
90
114
|
matches.sort((a, b) => resolve(b).length - resolve(a).length);
|