mixdog 1.4.2 → 1.4.3
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/package.json +2 -2
- package/scripts/build-shipped-history.mjs +15 -5
- package/scripts/gate-local.mjs +65 -19
- package/scripts/graph-parity.mjs +8 -3
- package/scripts/lib/gate-scheduler.mjs +2 -1
- package/scripts/lib/run-node-tests.mjs +17 -5
- package/scripts/lib/test-failure-summary.mjs +1 -8
- package/scripts/lib/test-spawn-ledger.mjs +33 -2
- package/scripts/post-release-check.mjs +26 -16
- package/scripts/provider-pingpong-live.mjs +1 -1
- package/scripts/prune-embedding-runtime.mjs +3 -1
- package/scripts/release-paths.mjs +7 -1
- package/src/defaults/shipped-history.json +20 -0
- package/src/defaults/skills/setup/SKILL.md +2 -2
- package/src/defaults/skills/setup/references/actions.md +16 -3
- package/src/defaults/skills/setup/references/surfaces.md +3 -0
- package/src/defaults/skills/setup-guide/SKILL.md +6 -1
- package/src/lib/instruction-text.cjs +36 -0
- package/src/lib/rules-builder.cjs +34 -20
- package/src/orchestration/balanced.md +23 -0
- package/src/orchestration/focused.md +22 -0
- package/src/orchestration/none.md +5 -0
- package/src/orchestration/swarm.md +24 -0
- package/src/rules/agent-mode/AGENT_MODE.md +3 -1
- package/src/rules/agent-mode/MIXY.md +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
- package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -2
- package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +0 -9
- package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
- package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -4
- package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -1
- package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +199 -46
- package/src/runtime/agent/orchestrator/mcp/client-config.mjs +3 -5
- package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +9 -3
- package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -5
- package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
- package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
- package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
- package/src/runtime/agent/orchestrator/mcp/oauth.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
- package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
- package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
- package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
- package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +10 -1
- package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
- package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +30 -0
- package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
- package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
- package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
- package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
- package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
- package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
- package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
- package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
- package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
- package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
- package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
- package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
- package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -48
- package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
- package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +9 -0
- package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
- package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -1
- package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
- package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
- package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/cache-warm.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
- package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
- package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +1 -2
- package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
- package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
- package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
- package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
- package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +57 -26
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
- package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
- package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
- package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +19 -0
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +19 -10
- package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +31 -0
- package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
- package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +42 -6
- package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
- package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
- package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
- package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
- package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
- package/src/runtime/agent/orchestrator/session/mixy-name.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
- package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
- package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
- package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
- package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
- package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +14 -5
- package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
- package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
- package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
- package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
- package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +7 -3
- package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +27 -9
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
- package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
- package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
- package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +2 -2
- package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
- package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -4
- package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
- package/src/runtime/attachments/attachment-reference.mjs +1 -1
- package/src/runtime/attachments/pdf-extract.mjs +1 -1
- package/src/runtime/attachments/store.d.mts +1 -0
- package/src/runtime/attachments/store.mjs +2 -2
- package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
- package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
- package/src/runtime/channels/lib/scheduler.mjs +33 -0
- package/src/runtime/channels/lib/schedules-db.mjs +11 -4
- package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
- package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
- package/src/runtime/channels/lib/whisper-server.mjs +12 -12
- package/src/runtime/channels/lib/worker-main.mjs +9 -31
- package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
- package/src/runtime/connectors/connector-accounts-mcp.mjs +169 -33
- package/src/runtime/connectors/gmail-app-password.mjs +2 -2
- package/src/runtime/connectors/google-mcp.mjs +1 -1
- package/src/runtime/effort-judge/judge-client.mjs +2 -2
- package/src/runtime/effort-judge/judge-input.mjs +4 -4
- package/src/runtime/explore/explorer.mjs +0 -5
- package/src/runtime/explore/tool.mjs +3 -3
- package/src/runtime/local-provider/hardware.mjs +1 -1
- package/src/runtime/media/adapters/codex-image.mjs +3 -3
- package/src/runtime/media/adapters/gemini-video.mjs +1 -1
- package/src/runtime/media/media-usage.mjs +1 -1
- package/src/runtime/media/store.mjs +1 -1
- package/src/runtime/memory/index.mjs +11 -1
- package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
- package/src/runtime/memory/lib/memory-embed.mjs +83 -14
- package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
- package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
- package/src/runtime/memory/lib/memory.mjs +17 -0
- package/src/runtime/memory/lib/pg/process.mjs +1 -1
- package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
- package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
- package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
- package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
- package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
- package/src/runtime/office/capabilities.mjs +1 -1
- package/src/runtime/office/core/office-core.mjs +1 -1
- package/src/runtime/office/design/content-model.mjs +3 -3
- package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
- package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
- package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
- package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
- package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
- package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
- package/src/runtime/office/portable/portable-soffice.mjs +8 -6
- package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
- package/src/runtime/shared/atomic-file.d.mts +8 -0
- package/src/runtime/shared/background-tasks.mjs +22 -4
- package/src/runtime/shared/background-wait.mjs +66 -0
- package/src/runtime/shared/child-guardian.mjs +17 -7
- package/src/runtime/shared/config.mjs +313 -16
- package/src/runtime/shared/connector-catalog.d.mts +4 -5
- package/src/runtime/shared/connector-catalog.mjs +11 -1
- package/src/runtime/shared/file-lock.mjs +8 -3
- package/src/runtime/shared/git-runner.mjs +7 -2
- package/src/runtime/shared/idle-gc.mjs +30 -7
- package/src/runtime/shared/llm/http-agent.mjs +8 -5
- package/src/runtime/shared/llm/system-proxy.mjs +2 -1
- package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
- package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
- package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
- package/src/runtime/shared/maintenance-schedule.mjs +109 -0
- package/src/runtime/shared/process-runner.mjs +8 -4
- package/src/runtime/shared/routine-trigger.d.mts +13 -0
- package/src/runtime/shared/routine-trigger.mjs +34 -0
- package/src/runtime/shared/run-child.d.mts +2 -1
- package/src/runtime/shared/run-child.mjs +70 -18
- package/src/runtime/shared/session-scratch.mjs +14 -2
- package/src/runtime/shared/shipped-definitions.mjs +7 -1
- package/src/runtime/shared/tool-card-model.golden.json +3 -12
- package/src/runtime/shared/tool-execution-contract.mjs +2 -0
- package/src/runtime/shared/tool-work-units.mjs +31 -6
- package/src/runtime/shared/turn-snapshot.mjs +26 -4
- package/src/runtime/shared/turn-worktree-snapshot.mjs +6 -11
- package/src/runtime/shared/worker-requests.mjs +17 -3
- package/src/runtime/shared/worktrees/branch-management.mjs +1 -5
- package/src/runtime/shared/worktrees/contract.mjs +16 -6
- package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
- package/src/runtime/shared/worktrees/disk.mjs +79 -2
- package/src/runtime/shared/worktrees/git.mjs +44 -11
- package/src/runtime/shared/worktrees/include.mjs +6 -4
- package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
- package/src/runtime/shared/worktrees/manager.mjs +772 -75
- package/src/runtime/shared/worktrees/settings.mjs +33 -7
- package/src/runtime/shared/worktrees/test-support.mjs +5 -1
- package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
- package/src/runtime/tidy/process.mjs +2 -1
- package/src/runtime/web-search/index.mjs +3 -22
- package/src/runtime/web-search/lib/web-tools.mjs +2 -2
- package/src/runtime/web-search/tool-defs.mjs +17 -26
- package/src/session-runtime/agent-profile-changes.mjs +55 -4
- package/src/session-runtime/boot/apis.mjs +27 -4
- package/src/session-runtime/boot/begin.mjs +2 -0
- package/src/session-runtime/boot/config.mjs +10 -1
- package/src/session-runtime/boot/facade.mjs +1 -0
- package/src/session-runtime/boot/providers.mjs +70 -3
- package/src/session-runtime/boot/session.mjs +2 -0
- package/src/session-runtime/boot/tools.mjs +69 -25
- package/src/session-runtime/channel-config-api.mjs +1 -0
- package/src/session-runtime/config-change-effects.mjs +179 -0
- package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
- package/src/session-runtime/config-lifecycle.mjs +160 -24
- package/src/session-runtime/conversation-import/discovery.mjs +86 -7
- package/src/session-runtime/conversation-import/service.mjs +3 -2
- package/src/session-runtime/goal-facade-api.mjs +49 -3
- package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
- package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
- package/src/session-runtime/internal-tool-executor/feature-tools.mjs +3 -11
- package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +18 -4
- package/src/session-runtime/internal-tool-executor.mjs +12 -19
- package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
- package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
- package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
- package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
- package/src/session-runtime/lifecycle/teardown/session-close.mjs +4 -4
- package/src/session-runtime/lifecycle-api.mjs +1 -0
- package/src/session-runtime/mcp-glue/server-catalog.mjs +14 -3
- package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
- package/src/session-runtime/model-route/set-route.mjs +40 -7
- package/src/session-runtime/native-web-search/routes.mjs +3 -3
- package/src/session-runtime/provider-auth/refresh.mjs +6 -3
- package/src/session-runtime/provider-auth/setup.mjs +1 -1
- package/src/session-runtime/provider-readiness.mjs +53 -4
- package/src/session-runtime/resource-api.mjs +1 -1
- package/src/session-runtime/resource-extension-sync.mjs +4 -8
- package/src/session-runtime/resource-mcp-connector-accounts.mjs +5 -1
- package/src/session-runtime/resource-mcp-secrets.mjs +3 -3
- package/src/session-runtime/resource-plugins-api.mjs +4 -0
- package/src/session-runtime/routine-executor.mjs +152 -0
- package/src/session-runtime/routine-home-turn.mjs +46 -5
- package/src/session-runtime/runtime-facade.mjs +5 -0
- package/src/session-runtime/runtime-feature-gates.mjs +2 -11
- package/src/session-runtime/schedule-session-run.mjs +1 -0
- package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
- package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
- package/src/session-runtime/services/agent-tool/send-flow.mjs +110 -6
- package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
- package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +24 -6
- package/src/session-runtime/services/agent-tool.mjs +51 -1
- package/src/session-runtime/services/channel-admin.mjs +18 -0
- package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
- package/src/session-runtime/services/plugin-admin.mjs +4 -0
- package/src/session-runtime/services/seeds.mjs +7 -0
- package/src/session-runtime/services/usage-stats-model.mjs +32 -11
- package/src/session-runtime/session-create/materialize.mjs +4 -2
- package/src/session-runtime/session-create/session-options.mjs +2 -0
- package/src/session-runtime/settings-builtin-tools-api.mjs +1 -9
- package/src/session-runtime/setup-tool/connector-widen-approval.mjs +1 -1
- package/src/session-runtime/setup-tool/executor.mjs +8 -1
- package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
- package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
- package/src/session-runtime/setup-tool/tool-defs.mjs +26 -1
- package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
- package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
- package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
- package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
- package/src/session-runtime/tool-policy-refresh.mjs +182 -5
- package/src/session-runtime/tool-surface.mjs +38 -1
- package/src/session-runtime/turn/agent-ops.mjs +10 -2
- package/src/session-runtime/turn/session-ops.mjs +2 -0
- package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
- package/src/session-runtime/turn/turn-run.mjs +1 -1
- package/src/session-runtime/webhook-session-run.mjs +2 -0
- package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +1 -4
- package/src/session-runtime/workflow-agents-api/agent-route.mjs +3 -10
- package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
- package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
- package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
- package/src/session-runtime/workflow-agents-api.mjs +2 -0
- package/src/session-runtime/workspace/project-branches.mjs +1 -1
- package/src/session-runtime/workspace/session-view.mjs +1 -1
- package/src/session-runtime/workspace/transitions.mjs +20 -4
- package/src/session-runtime/workspace/worktree-maintenance.mjs +126 -6
- package/src/session-runtime/workspace/worktree-setup.mjs +13 -1
- package/src/session-runtime/workspace/worktree-tool.mjs +16 -0
- package/src/standalone/daemon-agent-control.mjs +25 -8
- package/src/standalone/daemon-desktop-runtime.mjs +6 -2
- package/src/standalone/daemon-lag-profiler.mjs +7 -7
- package/src/standalone/daemon.mjs +54 -2
- package/src/standalone/local-session-runtime.mjs +16 -0
- package/src/standalone/session-client/daemon-unavailable.mjs +1 -1
- package/src/standalone/session-protocol.mjs +11 -0
- package/src/standalone/session-service/agent-route-follow.mjs +23 -20
- package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
- package/src/standalone/session-service/quota-resume.mjs +9 -0
- package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
- package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
- package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
- package/src/standalone/session-service/session-calls.mjs +2 -1
- package/src/tui/App.jsx +8 -0
- package/src/tui/app/clipboard.mjs +1 -1
- package/src/tui/app/create-app-pickers.mjs +2 -0
- package/src/tui/app/doctor.mjs +2 -2
- package/src/tui/app/panel-signature.mjs +1 -1
- package/src/tui/app/settings-picker.mjs +53 -8
- package/src/tui/app/shell-layout/row-budgets.mjs +2 -2
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
- package/src/tui/app/slash-dispatch/session-commands.mjs +1 -1
- package/src/tui/app/use-prompt-queue-history.mjs +2 -2
- package/src/tui/app/use-transcript-activity.mjs +1 -1
- package/src/tui/app/use-transcript-window/geometry.mjs +1 -1
- package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
- package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
- package/src/tui/components/question-panel-model.mjs +4 -4
- package/src/tui/components/tool-execution/standard-card.jsx +3 -4
- package/src/tui/components/user-message-skills.mjs +1 -1
- package/src/tui/dist/index.mjs +309 -30
- package/src/tui/markdown/render-ansi.mjs +1 -1
- package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
- package/src/tui/session/agent-job-feed.mjs +2 -0
- package/src/tui/session/context-state.mjs +3 -0
- package/src/tui/session/quota-resume.mjs +1 -1
- package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
- package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
- package/src/tui/session/session-api/routes-agent-definitions.mjs +5 -0
- package/src/tui/session/session-api/worktree.mjs +13 -1
- package/src/tui/session/session-flow/drain.mjs +11 -14
- package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
- package/src/tui/session/turn-quiet.mjs +1 -1
- package/src/tui/session/turn.mjs +3 -0
- package/src/tui/session/worktree-setup-notice.mjs +1 -1
- package/src/tui/spinner-meta.mjs +3 -3
- package/src/tui/spinner-verbs.mjs +2 -2
- package/src/defaults/skills/goal-management/SKILL.md +0 -169
- package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
- package/src/session-runtime/goal-tasks.mjs +0 -1
|
@@ -20,24 +20,21 @@
|
|
|
20
20
|
* Further stacks are one STACKS entry each (see there). Java (Gradle, Maven),
|
|
21
21
|
* Rust and Go fetch on their own and need nothing.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* never linked (a .venv) is noted as not prepared. A tool that is not on PATH
|
|
34
|
-
* is skipped with a note.
|
|
23
|
+
* Nothing is shared with the main checkout: every artefact is installed in
|
|
24
|
+
* the worktree folder at the start of the session's first turn
|
|
25
|
+
* (setup-commands.mjs). A linked node_modules is never safe: `npm ci` lists
|
|
26
|
+
* the folder it finds and deletes each entry, which through a link (junction
|
|
27
|
+
* or symlink) are the main checkout's, and other installs prune or rebuild in
|
|
28
|
+
* place the same way. A folder whose artefact already exists in the worktree
|
|
29
|
+
* is left alone; while any is a link (made by hand, or by an earlier version
|
|
30
|
+
* and not yet detached, manager detachDependencyLinks), nothing in the
|
|
31
|
+
* worktree is installed, since an install can reach other folders than its
|
|
32
|
+
* own. A tool that is not on PATH is skipped with a note.
|
|
35
33
|
*/
|
|
36
34
|
import { lstat, readFile } from 'node:fs/promises';
|
|
37
35
|
import { join } from 'node:path';
|
|
38
36
|
import { which } from '../../tidy/process.mjs';
|
|
39
37
|
import { runGit } from './git.mjs';
|
|
40
|
-
import { linkDirectory } from './include.mjs';
|
|
41
38
|
import { runWorktreeSetupCommands } from './setup-commands.mjs';
|
|
42
39
|
|
|
43
40
|
/** JS lockfile → package manager, in the order a folder with several picks one. */
|
|
@@ -86,9 +83,18 @@ async function yarnInstallFlag(tool, cwd, signal) {
|
|
|
86
83
|
* globs committed basename globs it reads (`*` within a name), optional;
|
|
87
84
|
* artefact the folder an install leaves: the main checkout having it
|
|
88
85
|
* admits a nested folder, the worktree having it skips the folder;
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
86
|
+
* reinstallable the artefact's whole contents come back from the install,
|
|
87
|
+
* so an automatic worktree removal may drop it (dependencyArtefacts);
|
|
88
|
+
* optional. node_modules is one by deliberate choice: hand edits
|
|
89
|
+
* inside it (say, before patch-package saved them) are NOT
|
|
90
|
+
* protected and go with the worktree — protecting it would stop
|
|
91
|
+
* the cleanup of nearly every JS worktree, which only removes
|
|
92
|
+
* merged, disposable or aged ones, and installs treat the folder
|
|
93
|
+
* as their output (npm ci deletes and rebuilds it). Not so: .venv (pip reads a pip.conf inside it, index
|
|
94
|
+
* credentials included), .bundle (local Bundler config, gem
|
|
95
|
+
* source credentials), vendor and deps (dev and git dependencies
|
|
96
|
+
* are git checkouts that can hold local commits), obj and
|
|
97
|
+
* .dart_tool (build and tool state an install does not restore);
|
|
92
98
|
* pick(files) the folder's committed basenames → { manager, lockfile?, … }
|
|
93
99
|
* or null;
|
|
94
100
|
* install(match, { cwd, tool, signal })
|
|
@@ -105,7 +111,7 @@ const STACKS = [
|
|
|
105
111
|
stack: 'js',
|
|
106
112
|
names: ['package.json', ...JS_LOCKFILES.map(([name]) => name)],
|
|
107
113
|
artefact: 'node_modules',
|
|
108
|
-
|
|
114
|
+
reinstallable: true,
|
|
109
115
|
pick(files) {
|
|
110
116
|
if (!files.has('package.json')) return null;
|
|
111
117
|
const match = JS_LOCKFILES.find(([name]) => files.has(name));
|
|
@@ -289,29 +295,21 @@ function workspaceMatcher(patterns) {
|
|
|
289
295
|
return (dir) => include.some((re) => re.test(dir)) && !exclude.some((re) => re.test(dir));
|
|
290
296
|
}
|
|
291
297
|
|
|
292
|
-
/** Whether `dir` holds the installed `artefact`: a real directory, never a link. */
|
|
293
|
-
async function installedArtefact(dir, artefact) {
|
|
294
|
-
const info = await lstatOrNull(join(dir, artefact));
|
|
295
|
-
return !!info?.isDirectory() && !info.isSymbolicLink();
|
|
296
|
-
}
|
|
297
|
-
|
|
298
298
|
/**
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* @returns {Promise<Array<{ dir: string, stack: string, manager: string, artefact: string, linkable?: boolean,
|
|
304
|
-
* lockfile?: string, requirements?: string[] }>>}
|
|
299
|
+
* The folders (forward-slash, relative to `projectDir`, '' itself) holding
|
|
300
|
+
* committed files some stack reads, with those basenames; nothing inside
|
|
301
|
+
* node_modules. Empty when git cannot list them.
|
|
302
|
+
* @returns {Promise<Map<string, Set<string>>>}
|
|
305
303
|
*/
|
|
306
|
-
|
|
304
|
+
async function committedFolders(projectDir, signal) {
|
|
307
305
|
const pathspecs = [...STACK_NAMES, ...STACK_GLOBS].map((name) => `:(glob)**/${name}`);
|
|
308
306
|
const result = await runGit(projectDir, ['-c', 'core.quotePath=false', 'ls-files', '-z', '--', ...pathspecs], {
|
|
309
307
|
signal,
|
|
310
308
|
readOnly: true,
|
|
311
309
|
allowFailure: true,
|
|
312
310
|
});
|
|
313
|
-
if (result.code !== 0) return [];
|
|
314
311
|
const folders = new Map();
|
|
312
|
+
if (result.code !== 0) return folders;
|
|
315
313
|
for (const path of result.stdout.split('\0')) {
|
|
316
314
|
const parts = path.split('/');
|
|
317
315
|
const name = parts.pop();
|
|
@@ -320,6 +318,72 @@ export async function findLockfileFolders(projectDir, { mainDir = null, signal }
|
|
|
320
318
|
if (!folders.has(dir)) folders.set(dir, new Set());
|
|
321
319
|
folders.get(dir).add(name);
|
|
322
320
|
}
|
|
321
|
+
return folders;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/** Whether `stack` reads the committed basename `name`. */
|
|
325
|
+
function stackReads(stack, name) {
|
|
326
|
+
return stack.names.includes(name) || (stack.globs ?? []).some((glob) => matchesSegment(glob, name));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function matchesSegment(glob, name) {
|
|
330
|
+
return new RegExp(`^${segmentPattern(glob)}$`, 'i').test(name);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Every installed artefact in `projectDir` that is a link (forward-slash
|
|
335
|
+
* paths): the artefact of each folder with committed files of its stack, so
|
|
336
|
+
* a node_modules beside every committed package.json, the workspace packages
|
|
337
|
+
* that share the root lockfile too. Only the artefact itself is examined
|
|
338
|
+
* (lstat), never what a link points at. An install reaches beyond its own
|
|
339
|
+
* folder (`npm ci` at the root resets every workspace package's
|
|
340
|
+
* node_modules), so any one blocks every install in the worktree.
|
|
341
|
+
* @returns {Promise<string[]>}
|
|
342
|
+
*/
|
|
343
|
+
export async function linkedArtefacts(projectDir, { signal } = {}) {
|
|
344
|
+
const links = [];
|
|
345
|
+
for (const [dir, files] of await committedFolders(projectDir, signal)) {
|
|
346
|
+
for (const stack of STACKS) {
|
|
347
|
+
if (![...files].some((name) => stackReads(stack, name))) continue;
|
|
348
|
+
const info = await lstatOrNull(join(folderPath(projectDir, dir), stack.artefact));
|
|
349
|
+
if (info?.isSymbolicLink()) links.push([dir, stack.artefact].filter(Boolean).join('/'));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return links;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* The artefacts an install restores whole in `projectDir` (forward-slash,
|
|
357
|
+
* relative to it): the artefact of every folder whose committed files a
|
|
358
|
+
* `reinstallable` stack installs from, wherever the folder is.
|
|
359
|
+
* @returns {Promise<string[]>}
|
|
360
|
+
*/
|
|
361
|
+
export async function dependencyArtefacts(projectDir, { signal } = {}) {
|
|
362
|
+
const artefacts = [];
|
|
363
|
+
for (const [dir, files] of await committedFolders(projectDir, signal)) {
|
|
364
|
+
for (const stack of STACKS) {
|
|
365
|
+
if (stack.reinstallable && stack.pick(files)) artefacts.push([dir, stack.artefact].filter(Boolean).join('/'));
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return artefacts;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/** Whether `dir` holds the installed `artefact`: a real directory, never a link. */
|
|
372
|
+
async function installedArtefact(dir, artefact) {
|
|
373
|
+
const info = await lstatOrNull(join(dir, artefact));
|
|
374
|
+
return !!info?.isDirectory() && !info.isSymbolicLink();
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* What to prepare (forward-slash `dir` relative to `projectDir`; '' is the
|
|
379
|
+
* project itself), root first: one entry per project folder and stack, with
|
|
380
|
+
* the manager its committed files select. `mainDir`: the same folder in the
|
|
381
|
+
* main checkout, whose installed artefacts admit nested folders.
|
|
382
|
+
* @returns {Promise<Array<{ dir: string, stack: string, manager: string, artefact: string,
|
|
383
|
+
* lockfile?: string, requirements?: string[] }>>}
|
|
384
|
+
*/
|
|
385
|
+
export async function findLockfileFolders(projectDir, { mainDir = null, signal } = {}) {
|
|
386
|
+
const folders = await committedFolders(projectDir, signal);
|
|
323
387
|
const isWorkspace = workspaceMatcher(await workspacePatterns(projectDir));
|
|
324
388
|
const found = [];
|
|
325
389
|
for (const [dir, files] of folders) {
|
|
@@ -329,56 +393,17 @@ export async function findLockfileFolders(projectDir, { mainDir = null, signal }
|
|
|
329
393
|
const admitted =
|
|
330
394
|
!dir || isWorkspace(dir) || (mainDir && (await installedArtefact(folderPath(mainDir, dir), stack.artefact)));
|
|
331
395
|
if (admitted) {
|
|
332
|
-
found.push({ dir, stack: stack.stack, artefact: stack.artefact,
|
|
396
|
+
found.push({ dir, stack: stack.stack, artefact: stack.artefact, ...match });
|
|
333
397
|
}
|
|
334
398
|
}
|
|
335
399
|
}
|
|
336
400
|
return found.sort((a, b) => a.dir.split('/').length - b.dir.split('/').length || a.dir.localeCompare(b.dir));
|
|
337
401
|
}
|
|
338
402
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (!lockfile || !(await installedArtefact(folderPath(mainDir, dir), artefact))) return null;
|
|
342
|
-
const [mainLock, worktreeLock] = await Promise.all([
|
|
343
|
-
readOrNull(join(folderPath(mainDir, dir), lockfile)),
|
|
344
|
-
readOrNull(join(folderPath(worktreeDir, dir), lockfile)),
|
|
345
|
-
]);
|
|
346
|
-
return mainLock && worktreeLock && mainLock.equals(worktreeLock) ? join(folderPath(mainDir, dir), artefact) : null;
|
|
347
|
-
}
|
|
403
|
+
const sharedClause = (count) =>
|
|
404
|
+
`${count > 1 ? 'are' : 'is'} shared from the main checkout by .worktreeinclude and not installed here`;
|
|
348
405
|
|
|
349
|
-
|
|
350
|
-
* Link each linkable folder's artefact (node_modules) from the main checkout
|
|
351
|
-
* where its lockfile is identical (creation time). `record(ledger)` is
|
|
352
|
-
* awaited with status 'pending' before each link is made and 'created'
|
|
353
|
-
* after, like an include link; a link that cannot be made is left to the
|
|
354
|
-
* first-turn install.
|
|
355
|
-
* @param {{ mainDir: string, worktreeDir: string, worktreeRel: string, signal?: AbortSignal,
|
|
356
|
-
* record: (ledger: object) => Promise<void> }} input `worktreeRel`: the project
|
|
357
|
-
* folder relative to the worktree's top level ('' at the top).
|
|
358
|
-
* @returns {Promise<string[]>} The linked folders.
|
|
359
|
-
*/
|
|
360
|
-
export async function linkWorktreeDependencies({ mainDir, worktreeDir, worktreeRel, signal, record }) {
|
|
361
|
-
const linked = [];
|
|
362
|
-
for (const folder of await findLockfileFolders(worktreeDir, { mainDir, signal })) {
|
|
363
|
-
if (!folder.linkable) continue;
|
|
364
|
-
const dest = join(folderPath(worktreeDir, folder.dir), folder.artefact);
|
|
365
|
-
if (await lstatOrNull(dest)) continue;
|
|
366
|
-
const source = await linkSource(mainDir, worktreeDir, folder);
|
|
367
|
-
if (!source) continue;
|
|
368
|
-
const rel = [worktreeRel, folder.dir, folder.artefact].filter(Boolean).join('/');
|
|
369
|
-
const ledger = { mode: 'link', rel, path: dest, status: 'pending', digest: null, source, dependency: true };
|
|
370
|
-
await record(ledger);
|
|
371
|
-
try {
|
|
372
|
-
await linkDirectory(source, dest);
|
|
373
|
-
} catch {
|
|
374
|
-
continue;
|
|
375
|
-
}
|
|
376
|
-
ledger.status = 'created';
|
|
377
|
-
await record(ledger);
|
|
378
|
-
linked.push(folder.dir);
|
|
379
|
-
}
|
|
380
|
-
return linked;
|
|
381
|
-
}
|
|
406
|
+
const sharedNote = (artefact, where) => `${artefact} in ${where} ${sharedClause(1)}.`;
|
|
382
407
|
|
|
383
408
|
/** Display text of a command (never run through a shell). */
|
|
384
409
|
const displayLine = (tool, args) => `${tool} ${args.map(quote).join(' ')}`;
|
|
@@ -390,39 +415,53 @@ function commandLine(tool, { args, followUp }) {
|
|
|
390
415
|
/**
|
|
391
416
|
* What the first turn still has to do in the worktree folder `worktreeDir`:
|
|
392
417
|
* the installs (`commands`, setup-commands entries) and one line per folder
|
|
393
|
-
* it leaves alone (`notes`:
|
|
394
|
-
*
|
|
418
|
+
* it leaves alone (`notes`: nothing found, tool not on PATH, an artefact that
|
|
419
|
+
* is a link it must not install through).
|
|
395
420
|
* `mainDir`: the same folder in the main checkout (findLockfileFolders).
|
|
396
421
|
* `env`: where the tools are looked up (PATH).
|
|
397
|
-
*
|
|
422
|
+
* `shared`: artefacts (forward-slash, relative to `worktreeDir`) that are links
|
|
423
|
+
* the user asked for in .worktreeinclude; they are never installed through or
|
|
424
|
+
* replaced, and are reported as shared rather than as a link to remove.
|
|
425
|
+
* @param {{ worktreeDir: string, mainDir?: string|null, shared?: string[], signal?: AbortSignal, env?: NodeJS.ProcessEnv }} input
|
|
398
426
|
*/
|
|
399
|
-
export async function planDependencySetup({ worktreeDir, mainDir = null, signal, env = process.env }) {
|
|
427
|
+
export async function planDependencySetup({ worktreeDir, mainDir = null, shared = [], signal, env = process.env }) {
|
|
400
428
|
const folders = await findLockfileFolders(worktreeDir, { mainDir, signal });
|
|
401
429
|
if (!folders.length) {
|
|
402
430
|
return { commands: [], notes: ['No lockfile or dependency manifest found; nothing was installed.'] };
|
|
403
431
|
}
|
|
404
432
|
const commands = [];
|
|
405
433
|
const notes = [];
|
|
406
|
-
|
|
434
|
+
// While any artefact is a link nothing is installed (linkedArtefacts).
|
|
435
|
+
const links = await linkedArtefacts(worktreeDir, { signal });
|
|
407
436
|
for (const entry of folders) {
|
|
408
437
|
const cwd = folderPath(worktreeDir, entry.dir);
|
|
409
438
|
const where = folderLabel(entry.dir);
|
|
410
|
-
const
|
|
411
|
-
if (
|
|
412
|
-
if (present.isSymbolicLink()) linked.push(where);
|
|
413
|
-
continue;
|
|
414
|
-
}
|
|
439
|
+
const info = await lstatOrNull(join(cwd, entry.artefact));
|
|
440
|
+
if (info && !info.isSymbolicLink()) continue;
|
|
415
441
|
const stack = STACKS.find((candidate) => candidate.stack === entry.stack);
|
|
416
442
|
const named = await stack.install(entry, { cwd, signal });
|
|
417
443
|
if (!named) continue;
|
|
418
|
-
if (
|
|
419
|
-
|
|
420
|
-
|
|
444
|
+
if (info && shared.includes([entry.dir, entry.artefact].filter(Boolean).join('/'))) {
|
|
445
|
+
notes.push(sharedNote(entry.artefact, where));
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
if (info) {
|
|
421
449
|
notes.push(
|
|
422
|
-
`${entry.artefact} in ${where} is a link, not an
|
|
450
|
+
`${entry.artefact} in ${where} is a link, not an install of its own; ${where} was not prepared (replace it with \`${commandLine(named.tools[0], named)}\` there if tests need it).`
|
|
423
451
|
);
|
|
424
452
|
continue;
|
|
425
453
|
}
|
|
454
|
+
const others = links.filter((rel) => !shared.includes(rel));
|
|
455
|
+
if (others.length) {
|
|
456
|
+
notes.push(
|
|
457
|
+
`${where} was not prepared while ${others.join(', ')} ${others.length > 1 ? 'are links' : 'is a link'} an install could reach through (run \`${commandLine(named.tools[0], named)}\` there once replaced if tests need it).`
|
|
458
|
+
);
|
|
459
|
+
continue;
|
|
460
|
+
}
|
|
461
|
+
if (links.length) {
|
|
462
|
+
notes.push(`${where} was not prepared; ${links.join(', ')} ${sharedClause(links.length)}.`);
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
426
465
|
const found = named.tools.find((candidate) => which(candidate, { env }));
|
|
427
466
|
if (!found) {
|
|
428
467
|
notes.push(
|
|
@@ -451,6 +490,5 @@ export async function planDependencySetup({ worktreeDir, mainDir = null, signal,
|
|
|
451
490
|
commands.push({ command: displayLine(`"${file}"`, args), file, args, cwd, label, ...specEnv });
|
|
452
491
|
}
|
|
453
492
|
}
|
|
454
|
-
if (linked.length) notes.unshift(`Linked node_modules from the main checkout: ${linked.join(', ')}.`);
|
|
455
493
|
return { commands, notes };
|
|
456
494
|
}
|
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* space on the DESTINATION volume.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync } from 'node:fs';
|
|
7
|
-
import { statfs } from 'node:fs/promises';
|
|
8
|
-
import { dirname, resolve } from 'node:path';
|
|
7
|
+
import { lstat, readdir, statfs } from 'node:fs/promises';
|
|
8
|
+
import { dirname, join, resolve } from 'node:path';
|
|
9
9
|
import { runGit } from './git.mjs';
|
|
10
|
+
import { pathKey } from './paths.mjs';
|
|
10
11
|
|
|
11
12
|
const MARGIN_MIN_BYTES = 64 * 1024 * 1024;
|
|
12
13
|
const MARGIN_RATIO = 0.1;
|
|
@@ -44,6 +45,82 @@ export function requiredBytes(contentBytes) {
|
|
|
44
45
|
return contentBytes + Math.max(MARGIN_MIN_BYTES, Math.ceil(contentBytes * MARGIN_RATIO));
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
/**
|
|
49
|
+
* Bytes of the files under `path`. Links are not followed (linked dependency
|
|
50
|
+
* folders belong to the project folder); unreadable entries are skipped. The
|
|
51
|
+
* walk stops after `maxEntries`, reporting `complete: false` (a lower bound).
|
|
52
|
+
* @returns {Promise<{ bytes: number, complete: boolean }>}
|
|
53
|
+
*/
|
|
54
|
+
async function folderBytes(path, { maxEntries = 250_000 } = {}) {
|
|
55
|
+
let bytes = 0;
|
|
56
|
+
let seen = 0;
|
|
57
|
+
const pending = [path];
|
|
58
|
+
while (pending.length) {
|
|
59
|
+
const dir = pending.pop();
|
|
60
|
+
let entries;
|
|
61
|
+
try {
|
|
62
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
63
|
+
} catch {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
for (const entry of entries) {
|
|
67
|
+
if (++seen > maxEntries) return { bytes, complete: false };
|
|
68
|
+
const child = join(dir, entry.name);
|
|
69
|
+
if (entry.isDirectory()) pending.push(child);
|
|
70
|
+
else if (entry.isFile())
|
|
71
|
+
bytes += await lstat(child).then(
|
|
72
|
+
(info) => info.size,
|
|
73
|
+
() => 0
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { bytes, complete: true };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// path key → { bytes, complete, at, refresh }: the worktree list's sizes.
|
|
81
|
+
const sizeCache = new Map();
|
|
82
|
+
const FOLDER_SIZE_TTL_MS = 5 * 60_000;
|
|
83
|
+
// The repository's comparison key: canonical, case-folded only on Windows.
|
|
84
|
+
const sizeKey = (path, platform) => pathKey(path, platform);
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* folderBytes through a per-path cache: a fresh entry (younger than
|
|
88
|
+
* `ttlMs`) is returned as is; a stale one is returned at once while one
|
|
89
|
+
* background walk refreshes it; only a path never measured waits for its walk.
|
|
90
|
+
* forgetFolderSize drops an entry (the worktree was removed or created).
|
|
91
|
+
* @returns {Promise<{ bytes: number, complete: boolean, at: number }>}
|
|
92
|
+
*/
|
|
93
|
+
export async function cachedFolderBytes(
|
|
94
|
+
path,
|
|
95
|
+
{ ttlMs = FOLDER_SIZE_TTL_MS, measure = folderBytes, platform = process.platform } = {}
|
|
96
|
+
) {
|
|
97
|
+
const key = sizeKey(path, platform);
|
|
98
|
+
const cached = sizeCache.get(key);
|
|
99
|
+
const walk = () => {
|
|
100
|
+
const refresh = measure(path).then(
|
|
101
|
+
(value) => {
|
|
102
|
+
const entry = { ...value, at: Date.now(), refresh: null };
|
|
103
|
+
if (sizeCache.get(key)?.refresh === refresh) sizeCache.set(key, entry);
|
|
104
|
+
return entry;
|
|
105
|
+
},
|
|
106
|
+
(error) => {
|
|
107
|
+
if (sizeCache.get(key)?.refresh === refresh) sizeCache.delete(key);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
sizeCache.set(key, { ...(cached ?? { bytes: 0, complete: false, at: 0 }), refresh });
|
|
112
|
+
return refresh;
|
|
113
|
+
};
|
|
114
|
+
if (!cached) return walk();
|
|
115
|
+
if (!cached.refresh && Date.now() - cached.at >= ttlMs) walk().catch(() => {});
|
|
116
|
+
if (!cached.at) return cached.refresh;
|
|
117
|
+
return { bytes: cached.bytes, complete: cached.complete, at: cached.at };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function forgetFolderSize(path, platform = process.platform) {
|
|
121
|
+
if (typeof path === 'string' && path) sizeCache.delete(sizeKey(path, platform));
|
|
122
|
+
}
|
|
123
|
+
|
|
47
124
|
/** Free bytes available to this user on the volume holding `path`. */
|
|
48
125
|
export async function freeBytesAt(path) {
|
|
49
126
|
let dir = resolve(path);
|
|
@@ -68,14 +68,16 @@ const READ_ONLY_FLAGS = ['--no-optional-locks', '-c', 'core.fsmonitor=false'];
|
|
|
68
68
|
/**
|
|
69
69
|
* @param {string} cwd
|
|
70
70
|
* @param {string[]} args
|
|
71
|
-
* @param {{ signal?: AbortSignal, timeoutMs?: number, maxBytes?: number, allowFailure?: boolean, readOnly?: boolean, env?: Record<string, string
|
|
71
|
+
* @param {{ signal?: AbortSignal, timeoutMs?: number, maxBytes?: number, allowFailure?: boolean, readOnly?: boolean, env?: Record<string, string>, waitForExitMs?: number }} [options]
|
|
72
72
|
* `env`: variables set on top of the pinned environment (a temporary GIT_INDEX_FILE, a commit identity).
|
|
73
|
+
* `waitForExitMs`: a timeout rejects only once the killed git exited (or after that bound;
|
|
74
|
+
* the error's `childExited` says which).
|
|
73
75
|
* @returns {Promise<{ code: number, stdout: string, stderr: string }>}
|
|
74
76
|
*/
|
|
75
77
|
export async function runGit(
|
|
76
78
|
cwd,
|
|
77
79
|
args,
|
|
78
|
-
{ signal, timeoutMs, maxBytes, allowFailure = false, readOnly = false, env = null } = {}
|
|
80
|
+
{ signal, timeoutMs, maxBytes, allowFailure = false, readOnly = false, env = null, waitForExitMs = 0 } = {}
|
|
79
81
|
) {
|
|
80
82
|
throwIfAborted(signal);
|
|
81
83
|
const result = await runGitOffThread([...(readOnly ? READ_ONLY_FLAGS : []), ...SCOPED_CONFIG, ...args], {
|
|
@@ -83,6 +85,7 @@ export async function runGit(
|
|
|
83
85
|
env: env ? { ...gitEnvironment(), ...env } : gitEnvironment(),
|
|
84
86
|
timeoutMs: timeoutMs ?? DEFAULT_TIMEOUT_MS,
|
|
85
87
|
maxBytes: maxBytes ?? DEFAULT_MAX_BYTES,
|
|
88
|
+
waitForExitMs,
|
|
86
89
|
});
|
|
87
90
|
if (result.code !== 0 && !allowFailure) throw gitFailure(args, result);
|
|
88
91
|
return result;
|
|
@@ -225,10 +228,14 @@ export async function branchCommit(cwd, branch, { signal } = {}) {
|
|
|
225
228
|
* answer is not exactly two commit ids: the caller then reads each with
|
|
226
229
|
* branchCommit(). */
|
|
227
230
|
export async function branchCommitPair(cwd, first, second, { signal } = {}) {
|
|
228
|
-
const result = await runGit(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
231
|
+
const result = await runGit(
|
|
232
|
+
cwd,
|
|
233
|
+
['rev-parse', `refs/heads/${first}^{commit}`, `refs/heads/${second}^{commit}`, '--'],
|
|
234
|
+
{
|
|
235
|
+
signal,
|
|
236
|
+
allowFailure: true,
|
|
237
|
+
}
|
|
238
|
+
);
|
|
232
239
|
const [a, b, dashes, ...rest] = String(result.stdout || '')
|
|
233
240
|
.split(/\r?\n/)
|
|
234
241
|
.map((line) => line.trim());
|
|
@@ -243,6 +250,23 @@ export async function localBranches(cwd, { signal, prefix = '' } = {}) {
|
|
|
243
250
|
return lines(result.stdout);
|
|
244
251
|
}
|
|
245
252
|
|
|
253
|
+
/**
|
|
254
|
+
* Every local branch with the committer time of its tip (ISO-8601), in
|
|
255
|
+
* refname order. Ref names cannot hold a tab, so it separates the fields.
|
|
256
|
+
* @returns {Promise<Array<{ name: string, committedAt: string }>>}
|
|
257
|
+
*/
|
|
258
|
+
export async function localBranchCommits(cwd, { signal } = {}) {
|
|
259
|
+
const result = await runGit(
|
|
260
|
+
cwd,
|
|
261
|
+
['for-each-ref', '--format=%(refname:short)%09%(committerdate:iso-strict)', 'refs/heads/'],
|
|
262
|
+
{ signal }
|
|
263
|
+
);
|
|
264
|
+
return lines(result.stdout).map((line) => {
|
|
265
|
+
const tab = line.indexOf('\t');
|
|
266
|
+
return tab < 0 ? { name: line, committedAt: '' } : { name: line.slice(0, tab), committedAt: line.slice(tab + 1) };
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
246
270
|
/** The local branch names the repository's remotes declare as their default (`refs/remotes/<remote>/HEAD`). */
|
|
247
271
|
export async function remoteDefaultBranches(cwd) {
|
|
248
272
|
const result = await runGit(cwd, ['for-each-ref', '--format=%(refname) %(symref)', 'refs/remotes'], {
|
|
@@ -307,18 +331,27 @@ export async function listWorktrees(cwd, { signal } = {}) {
|
|
|
307
331
|
}
|
|
308
332
|
|
|
309
333
|
/**
|
|
310
|
-
*
|
|
334
|
+
* Entries `{ code, path }` reported by `git status --porcelain=v1 -z` (`!!`:
|
|
335
|
+
* an ignored path, a folder with a trailing slash).
|
|
311
336
|
* @param {string} raw
|
|
312
337
|
*/
|
|
313
|
-
export function
|
|
338
|
+
export function parseStatusEntries(raw) {
|
|
314
339
|
const fields = String(raw || '').split('\0');
|
|
315
|
-
const
|
|
340
|
+
const entries = [];
|
|
316
341
|
for (let index = 0; index < fields.length; index++) {
|
|
317
342
|
const field = fields[index];
|
|
318
343
|
if (field.length < 4) continue;
|
|
319
344
|
const code = field.slice(0, 2);
|
|
320
|
-
|
|
345
|
+
entries.push({ code, path: field.slice(3) });
|
|
321
346
|
if (code.includes('R') || code.includes('C')) index++;
|
|
322
347
|
}
|
|
323
|
-
return
|
|
348
|
+
return entries;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Paths reported by `git status --porcelain=v1 -z`.
|
|
353
|
+
* @param {string} raw
|
|
354
|
+
*/
|
|
355
|
+
export function parseStatusPaths(raw) {
|
|
356
|
+
return parseStatusEntries(raw).map((entry) => entry.path);
|
|
324
357
|
}
|
|
@@ -135,7 +135,7 @@ async function exists(path) {
|
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
/** Link the directory `source` at `dest`: a directory junction on Windows, a symlink elsewhere. */
|
|
138
|
-
|
|
138
|
+
function linkDirectory(source, dest) {
|
|
139
139
|
return symlink(source, dest, process.platform === 'win32' ? 'junction' : 'dir');
|
|
140
140
|
}
|
|
141
141
|
|
|
@@ -559,14 +559,16 @@ export async function removeIncludeLinks(items) {
|
|
|
559
559
|
/**
|
|
560
560
|
* Put back the links removeIncludeLinks took away (those that record their
|
|
561
561
|
* `source`) after a removal that did not go through, each with its recorded
|
|
562
|
-
* `linkType` (a record without one
|
|
563
|
-
*
|
|
562
|
+
* `linkType` (a record without one, an older record, is a directory link). A
|
|
563
|
+
* path that exists again is left alone. An earlier version's dependency link
|
|
564
|
+
* (node_modules into the main checkout) is never put back: a reset through
|
|
565
|
+
* it empties the main checkout's folder.
|
|
564
566
|
* @returns {Promise<Array<{ path: string, error: string }>>} Links that could not be restored.
|
|
565
567
|
*/
|
|
566
568
|
export async function restoreIncludeLinks(items) {
|
|
567
569
|
const failures = [];
|
|
568
570
|
for (const item of items || []) {
|
|
569
|
-
if (item.mode !== 'link' || !item.source || item.status !== 'created') continue;
|
|
571
|
+
if (item.mode !== 'link' || !item.source || item.status !== 'created' || item.dependency) continue;
|
|
570
572
|
if (await exists(item.path)) continue;
|
|
571
573
|
try {
|
|
572
574
|
await makeLinkOfType(item.linkType || 'directory', item.source, item.path);
|