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
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { readdir } from 'node:fs/promises';
|
|
8
8
|
import { isWorktreeError } from './contract.mjs';
|
|
9
|
+
import { pathKey } from './paths.mjs';
|
|
9
10
|
|
|
10
11
|
const loadManager = () => import('./manager.mjs');
|
|
11
12
|
|
|
@@ -35,80 +36,471 @@ export async function storedWorkspaceConsumers() {
|
|
|
35
36
|
.filter((session) => session.closed !== true);
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/** Worktrees younger than this are never cleaned up automatically. */
|
|
40
|
+
export const WORKTREE_CLEANUP_MIN_AGE_MS = 60 * 60_000;
|
|
41
|
+
/** At most this many count/age removals per pass. */
|
|
42
|
+
export const WORKTREE_CLEANUP_MAX_REMOVALS = 20;
|
|
43
|
+
const DAY_MS = 24 * 60 * 60_000;
|
|
44
|
+
|
|
45
|
+
/** Favorite session ids from the attached desktop; null when unknown (no desktop, or one without favorites). */
|
|
46
|
+
async function storedFavoriteSessionIds() {
|
|
47
|
+
const { readSessionSidebarState } = await import('../session-sidebar-host.mjs');
|
|
48
|
+
const state = await readSessionSidebarState();
|
|
49
|
+
return state?.favorites ? Object.keys(state.favorites) : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
38
52
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
|
|
42
|
-
|
|
53
|
+
* `{ id → { lastActiveAt, title } }` for the given stored sessions (missing
|
|
54
|
+
* ones are left out): the last conversation turn or heartbeat.
|
|
55
|
+
*/
|
|
56
|
+
async function storedSessionActivity(ids) {
|
|
57
|
+
const [{ loadSession }, { subAgentActivityAt }] = await Promise.all([
|
|
58
|
+
import('../../agent/orchestrator/session/store/load-session.mjs'),
|
|
59
|
+
import('../../agent/orchestrator/session/store/sweep/sub-agent-retention.mjs'),
|
|
60
|
+
]);
|
|
61
|
+
const out = {};
|
|
62
|
+
for (const id of new Set(ids)) {
|
|
63
|
+
const doc = loadSession(id);
|
|
64
|
+
if (doc) out[id] = { lastActiveAt: subAgentActivityAt(doc) || null, title: doc.title || doc.name || null };
|
|
65
|
+
}
|
|
66
|
+
return out;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Running background shell jobs (`{ cwd, ownerSessionId }`); an unreadable record throws (fail closed). */
|
|
70
|
+
async function runningShellJobs() {
|
|
71
|
+
const { listShellJobRecords } = await import('../../agent/orchestrator/tools/builtin/lib/shell-job-records.mjs');
|
|
72
|
+
return (await listShellJobRecords({ strict: true })).filter((job) => !job.terminal);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const within = (folder, path) => {
|
|
76
|
+
if (typeof path !== 'string' || !path) return false;
|
|
77
|
+
const a = folder
|
|
78
|
+
.replace(/[\\/]+$/, '')
|
|
79
|
+
.toLowerCase()
|
|
80
|
+
.replace(/\\/g, '/');
|
|
81
|
+
const b = path.toLowerCase().replace(/\\/g, '/');
|
|
82
|
+
return b === a || b.startsWith(`${a}/`);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/** A row's last activity: its newest session's last turn, else its creation. */
|
|
86
|
+
function rowActivity(row, activity) {
|
|
87
|
+
const sessions = [row.ownerSessionId, ...(row.sharedSessionIds ?? [])].filter(Boolean);
|
|
88
|
+
return Math.max(Number(row.createdAt) || 0, ...sessions.map((id) => Number(activity[id]?.lastActiveAt) || 0));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The count/age limits' choice (pure): which managed worktrees to clean up,
|
|
93
|
+
* least recently used first, at most WORKTREE_CLEANUP_MAX_REMOVALS.
|
|
94
|
+
* Candidates are ready branch worktrees that are clean (`dirty === 0`),
|
|
95
|
+
* merged (`unmerged === 0`), not in use (`ownerLive`, `inUse`), not shared,
|
|
96
|
+
* not a favorite session's and at least WORKTREE_CLEANUP_MIN_AGE_MS old.
|
|
97
|
+
* `maxWorktrees` counts every managed branch worktree; `unusedDays` compares
|
|
98
|
+
* the row's last activity (rowActivity). Off (autoCleanup false) selects none.
|
|
99
|
+
* @param {object[]} rows listManagedWorktrees({ inspect: true }) rows (+ `inUse`)
|
|
100
|
+
* @param {{ settings: object, now?: number, activity?: Record<string, { lastActiveAt?: number|null }>, favoriteSessionIds?: Iterable<string> }} options
|
|
101
|
+
* @returns {{ id: string, reason: 'count'|'age', lastActiveAt: number }[]}
|
|
102
|
+
*/
|
|
103
|
+
export function selectWorktreeCleanup(
|
|
104
|
+
rows,
|
|
105
|
+
{
|
|
106
|
+
settings,
|
|
107
|
+
now = Date.now(),
|
|
108
|
+
activity = {},
|
|
109
|
+
favoriteSessionIds = [],
|
|
110
|
+
potential = false,
|
|
111
|
+
repoCommonDir = null,
|
|
112
|
+
maxRemovals = WORKTREE_CLEANUP_MAX_REMOVALS,
|
|
113
|
+
}
|
|
114
|
+
) {
|
|
115
|
+
if (settings?.autoCleanup !== true) return [];
|
|
116
|
+
const favorites = new Set(favoriteSessionIds);
|
|
117
|
+
const managed = rows.filter((row) => row.kind !== 'snapshot');
|
|
118
|
+
const candidates = managed
|
|
119
|
+
.filter(
|
|
120
|
+
(row) =>
|
|
121
|
+
row.state === 'ready' &&
|
|
122
|
+
row.dirty === 0 &&
|
|
123
|
+
row.unmerged === 0 &&
|
|
124
|
+
row.ownerLive !== true &&
|
|
125
|
+
row.inUse !== true &&
|
|
126
|
+
!row.sharedSessionIds?.length &&
|
|
127
|
+
![row.ownerSessionId].some((id) => id && favorites.has(id)) &&
|
|
128
|
+
now - (Number(row.createdAt) || 0) >= WORKTREE_CLEANUP_MIN_AGE_MS
|
|
129
|
+
)
|
|
130
|
+
.map((row) => ({ id: row.id, repoCommonDir: row.repoCommonDir, lastActiveAt: rowActivity(row, activity) }))
|
|
131
|
+
.sort((a, b) => a.lastActiveAt - b.lastActiveAt || a.id.localeCompare(b.id));
|
|
132
|
+
const reasons = new Map();
|
|
133
|
+
if (settings.unusedDays > 0) {
|
|
134
|
+
const cutoff = now - settings.unusedDays * DAY_MS;
|
|
135
|
+
for (const row of candidates) if (row.lastActiveAt < cutoff) reasons.set(row.id, 'age');
|
|
136
|
+
}
|
|
137
|
+
if (settings.maxWorktrees > 0) {
|
|
138
|
+
let excess = managed.length - settings.maxWorktrees;
|
|
139
|
+
for (const row of candidates) {
|
|
140
|
+
if (excess <= 0) break;
|
|
141
|
+
if (!reasons.has(row.id)) reasons.set(row.id, 'count');
|
|
142
|
+
// `potential` (the unverified preview): which candidates are really dirty or
|
|
143
|
+
// unmerged is unknown, so any of them could be the ones that fill the excess.
|
|
144
|
+
if (!potential) excess -= 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// The limits count every repository; a cleanup scoped to one repository
|
|
148
|
+
// drops the others' picks before the per-pass cap, so its own are reached.
|
|
149
|
+
const scope = repoCommonDir ? pathKey(repoCommonDir) : null;
|
|
150
|
+
const picked = candidates.filter(
|
|
151
|
+
(row) => reasons.has(row.id) && (!scope || pathKey(row.repoCommonDir || '') === scope)
|
|
152
|
+
);
|
|
153
|
+
return (potential ? picked : picked.slice(0, maxRemovals)).map(({ repoCommonDir: _repo, ...row }) => ({
|
|
154
|
+
...row,
|
|
155
|
+
reason: reasons.get(row.id),
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The count/age limits' selection for one cleanup pass (autoCleanup on only).
|
|
161
|
+
* `inspect` runs the per-worktree dirty/merge checks the removal needs; off,
|
|
162
|
+
* every ready worktree counts as clean and merged (the preview's optimistic,
|
|
163
|
+
* git-free reading). Adds the owners of worktrees with a running job to `live`.
|
|
43
164
|
*/
|
|
44
|
-
|
|
165
|
+
async function selectLimitCleanup(
|
|
166
|
+
backend,
|
|
167
|
+
{
|
|
168
|
+
settings,
|
|
169
|
+
live,
|
|
170
|
+
jobs,
|
|
171
|
+
retainedSessionIds,
|
|
172
|
+
now,
|
|
173
|
+
readFavorites,
|
|
174
|
+
readActivity,
|
|
175
|
+
inspect,
|
|
176
|
+
repoCommonDir,
|
|
177
|
+
listedRows = null,
|
|
178
|
+
maxRemovals,
|
|
179
|
+
}
|
|
180
|
+
) {
|
|
181
|
+
if (settings.autoCleanup !== true) return { selected: [], favorites: [], limits: 'off' };
|
|
182
|
+
const known = await readFavorites();
|
|
183
|
+
const favorites = known ?? [];
|
|
184
|
+
const limits = known ? 'applied' : 'favorites-unknown';
|
|
185
|
+
let selected = [];
|
|
186
|
+
if (known && (settings.maxWorktrees > 0 || settings.unusedDays > 0)) {
|
|
187
|
+
let rows =
|
|
188
|
+
listedRows ?? (await backend.listManagedWorktrees({ liveSessionIds: [...live], retainedSessionIds, inspect }));
|
|
189
|
+
if (!inspect) rows = rows.map((row) => ({ ...row, dirty: 0, unmerged: 0 }));
|
|
190
|
+
for (const row of rows) {
|
|
191
|
+
if (jobs.some((job) => within(row.path, job.cwd))) {
|
|
192
|
+
row.inUse = true;
|
|
193
|
+
if (row.ownerSessionId) live.add(row.ownerSessionId);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const owners = rows.flatMap((row) => [row.ownerSessionId, ...(row.sharedSessionIds ?? [])]).filter(Boolean);
|
|
197
|
+
selected = selectWorktreeCleanup(rows, {
|
|
198
|
+
settings,
|
|
199
|
+
now,
|
|
200
|
+
activity: await readActivity(owners),
|
|
201
|
+
favoriteSessionIds: favorites,
|
|
202
|
+
potential: !inspect,
|
|
203
|
+
repoCommonDir,
|
|
204
|
+
...(maxRemovals === undefined ? {} : { maxRemovals }),
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return { selected, favorites, limits };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Whether "Clean up now" could act on anything, without its git
|
|
212
|
+
* verification or any removal: the same selection as runWorktreeCleanup
|
|
213
|
+
* (registrations whose folder is gone, unretained or disposable worktrees,
|
|
214
|
+
* interrupted removals, and with autoCleanup on the limits and archive rules),
|
|
215
|
+
* read from the registry and session store only. A candidate may still be
|
|
216
|
+
* kept when the prune verifies it (dirty, unmerged, in use).
|
|
217
|
+
* @param {Parameters<typeof runWorktreeCleanup>[0]} [options]
|
|
218
|
+
* @returns {Promise<{ hasCandidates: boolean, ids: string[], stale: string[] }>}
|
|
219
|
+
*/
|
|
220
|
+
export async function previewWorktreeCleanup({
|
|
45
221
|
liveSessionIds = [],
|
|
46
222
|
listSessionIds = storedSessionIds,
|
|
47
223
|
manager = null,
|
|
224
|
+
repoCommonDir = null,
|
|
225
|
+
now = Date.now(),
|
|
226
|
+
readFavorites = storedFavoriteSessionIds,
|
|
227
|
+
readActivity = storedSessionActivity,
|
|
228
|
+
listJobs = runningShellJobs,
|
|
229
|
+
settings: given = null,
|
|
48
230
|
} = {}) {
|
|
49
231
|
const backend = manager || (await loadManager());
|
|
50
|
-
const settings = await backend.currentWorktreeSettings();
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
232
|
+
const settings = given ?? (await backend.currentWorktreeSettings());
|
|
233
|
+
const retainedSessionIds = await listSessionIds();
|
|
234
|
+
const live = new Set(liveSessionIds);
|
|
235
|
+
const jobs = await listJobs();
|
|
236
|
+
for (const job of jobs) if (job.ownerSessionId) live.add(job.ownerSessionId);
|
|
237
|
+
const { selected, favorites, limits } = await selectLimitCleanup(backend, {
|
|
238
|
+
settings,
|
|
239
|
+
live,
|
|
240
|
+
jobs,
|
|
241
|
+
retainedSessionIds,
|
|
242
|
+
now,
|
|
243
|
+
readFavorites,
|
|
244
|
+
readActivity,
|
|
245
|
+
inspect: false,
|
|
246
|
+
repoCommonDir,
|
|
55
247
|
});
|
|
248
|
+
const auto = settings.autoCleanup === true;
|
|
249
|
+
const { ids, stale } = await backend.previewManagedWorktreeCleanup({
|
|
250
|
+
liveSessionIds: [...live],
|
|
251
|
+
retainedSessionIds,
|
|
252
|
+
...(repoCommonDir ? { repoCommonDir } : {}),
|
|
253
|
+
...(auto
|
|
254
|
+
? {
|
|
255
|
+
removeEntryIds: selected.map((row) => row.id),
|
|
256
|
+
protectedSessionIds: favorites,
|
|
257
|
+
minAgeMs: WORKTREE_CLEANUP_MIN_AGE_MS,
|
|
258
|
+
now,
|
|
259
|
+
cleanupArchived: settings.cleanupArchived === true && limits === 'applied',
|
|
260
|
+
}
|
|
261
|
+
: {}),
|
|
262
|
+
});
|
|
263
|
+
return { hasCandidates: ids.length + stale.length > 0, ids, stale };
|
|
56
264
|
}
|
|
57
265
|
|
|
58
266
|
/**
|
|
59
|
-
* The
|
|
60
|
-
*
|
|
267
|
+
* The automatic cleanup (daemon startup and the idle maintenance pass), and
|
|
268
|
+
* with `explicit` the manual "Clean up now". Always: the bookkeeping
|
|
269
|
+
* reconcile (interrupted deletions/creations, stale leases, `git worktree
|
|
270
|
+
* prune`). With autoCleanup on (or `explicit`): clean, merged worktrees no
|
|
271
|
+
* stored session retains or a deleted session left disposable; with
|
|
272
|
+
* autoCleanup on also archived sessions' (cleanupArchived) and what the
|
|
273
|
+
* count/age limits select (selectWorktreeCleanup). Never removed: dirty or
|
|
274
|
+
* unmerged work, a live owner (`liveSessionIds`, a running lease holder, an
|
|
275
|
+
* owner with a running background shell job, or any job running inside the
|
|
276
|
+
* folder), a favorite session's, one younger than an hour, a shared one, an
|
|
277
|
+
* unmanaged one. The limits need the favorites: unknown (no desktop
|
|
278
|
+
* attached) skips them (`limits: 'favorites-unknown'`). `deleteBranch` off
|
|
279
|
+
* keeps every removed worktree's branch; on, its tip stays under
|
|
280
|
+
* refs/mixdog/removed/<id>.
|
|
281
|
+
* @param {{ liveSessionIds?: string[], listSessionIds?: () => Promise<string[]>, manager?: object,
|
|
282
|
+
* repoCommonDir?: string|null, explicit?: boolean, now?: number, settings?: object|null,
|
|
283
|
+
* currentLiveSessionIds?: () => Iterable<string>, finalCheckTimeoutMs?: number|null,
|
|
284
|
+
* readFavorites?: () => Promise<string[]|null>, readActivity?: (ids: string[]) => Promise<object>,
|
|
285
|
+
* listJobs?: () => Promise<{ cwd?: string, ownerSessionId?: string }[]> }} [options]
|
|
61
286
|
*/
|
|
62
|
-
export async function
|
|
287
|
+
export async function runWorktreeCleanup({
|
|
63
288
|
liveSessionIds = [],
|
|
64
289
|
listSessionIds = storedSessionIds,
|
|
65
290
|
manager = null,
|
|
291
|
+
repoCommonDir = null,
|
|
292
|
+
explicit = false,
|
|
293
|
+
now = Date.now(),
|
|
294
|
+
readFavorites = storedFavoriteSessionIds,
|
|
295
|
+
readActivity = storedSessionActivity,
|
|
296
|
+
listJobs = runningShellJobs,
|
|
297
|
+
settings: given = null,
|
|
298
|
+
currentLiveSessionIds = () => [],
|
|
299
|
+
finalCheckTimeoutMs = null,
|
|
66
300
|
} = {}) {
|
|
67
301
|
const backend = manager || (await loadManager());
|
|
68
|
-
|
|
302
|
+
const settings = given ?? (await backend.currentWorktreeSettings());
|
|
303
|
+
const retainedSessionIds = await listSessionIds();
|
|
304
|
+
const scope = repoCommonDir ? { repoCommonDir } : {};
|
|
305
|
+
if (settings.autoCleanup !== true && !explicit) {
|
|
306
|
+
return {
|
|
307
|
+
...(await backend.reconcileManagedWorktrees({
|
|
308
|
+
liveSessionIds,
|
|
309
|
+
retainedSessionIds,
|
|
310
|
+
removeOrphans: false,
|
|
311
|
+
...scope,
|
|
312
|
+
})),
|
|
313
|
+
selected: [],
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
const live = new Set(liveSessionIds);
|
|
317
|
+
const jobs = await listJobs();
|
|
318
|
+
for (const job of jobs) if (job.ownerSessionId) live.add(job.ownerSessionId);
|
|
319
|
+
// Asked again under each removal's folder hold (manager reconcile): the
|
|
320
|
+
// sessions live now and the jobs running now, whatever the limits are.
|
|
321
|
+
const inUse = async (record) => {
|
|
322
|
+
const now = new Set([...live, ...currentLiveSessionIds()]);
|
|
323
|
+
const running = await listJobs();
|
|
324
|
+
for (const job of running) if (job.ownerSessionId) now.add(job.ownerSessionId);
|
|
325
|
+
return (
|
|
326
|
+
[record.ownerSessionId, ...(record.sharedSessionIds ?? [])].some((id) => id && now.has(id)) ||
|
|
327
|
+
running.some((job) => within(record.path, job.cwd))
|
|
328
|
+
);
|
|
329
|
+
};
|
|
330
|
+
const { selected, favorites, limits } = await selectLimitCleanup(backend, {
|
|
331
|
+
settings,
|
|
332
|
+
live,
|
|
333
|
+
jobs,
|
|
334
|
+
retainedSessionIds,
|
|
335
|
+
now,
|
|
336
|
+
readFavorites,
|
|
337
|
+
readActivity,
|
|
338
|
+
inspect: true,
|
|
339
|
+
repoCommonDir,
|
|
340
|
+
});
|
|
341
|
+
const report = await backend.reconcileManagedWorktrees({
|
|
342
|
+
liveSessionIds: [...live],
|
|
343
|
+
retainedSessionIds,
|
|
344
|
+
removeOrphans: true,
|
|
345
|
+
...scope,
|
|
346
|
+
...(settings.autoCleanup === true
|
|
347
|
+
? {
|
|
348
|
+
removeEntryIds: selected.map((row) => row.id),
|
|
349
|
+
protectedSessionIds: favorites,
|
|
350
|
+
minAgeMs: WORKTREE_CLEANUP_MIN_AGE_MS,
|
|
351
|
+
now,
|
|
352
|
+
cleanupArchived: settings.cleanupArchived === true && limits === 'applied',
|
|
353
|
+
}
|
|
354
|
+
: {}),
|
|
355
|
+
preserveBranch: settings.deleteBranch !== true,
|
|
356
|
+
keepTip: settings.deleteBranch === true,
|
|
357
|
+
maxRemovals: WORKTREE_CLEANUP_MAX_REMOVALS,
|
|
358
|
+
inUse,
|
|
359
|
+
...(finalCheckTimeoutMs ? { finalCheckTimeoutMs } : {}),
|
|
360
|
+
});
|
|
361
|
+
return { ...report, selected, limits };
|
|
69
362
|
}
|
|
70
363
|
|
|
71
364
|
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* @param {{ liveSessionIds?: string[], listSessionIds?: () => Promise<string[]>, manager?: object, repoCommonDir?: string|null }} [options]
|
|
365
|
+
* Daemon startup: resume interrupted deletions and creations, release stale
|
|
366
|
+
* leases, prune stale registrations; with autoCleanup on, also the automatic
|
|
367
|
+
* cleanup (runWorktreeCleanup).
|
|
368
|
+
* @param {Parameters<typeof runWorktreeCleanup>[0]} [options]
|
|
77
369
|
*/
|
|
78
|
-
export async function
|
|
370
|
+
export async function reconcileWorktreesAtStartup(options = {}) {
|
|
371
|
+
return runWorktreeCleanup(options);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* The manual list: every managed worktree with its retention. `details`
|
|
376
|
+
* adds what the worktree list shows: dirty/unmerged counts, folder size, the
|
|
377
|
+
* owner's title, archive flag and last activity (`lastActiveAt`).
|
|
378
|
+
* @param {{ liveSessionIds?: string[], listSessionIds?: () => Promise<string[]>, manager?: object, details?: boolean,
|
|
379
|
+
* readActivity?: (ids: string[]) => Promise<object>, readArchived?: () => Promise<string[]|null> }} [options]
|
|
380
|
+
*/
|
|
381
|
+
export async function listWorktreesWithRetention({
|
|
79
382
|
liveSessionIds = [],
|
|
80
383
|
listSessionIds = storedSessionIds,
|
|
81
384
|
manager = null,
|
|
82
|
-
|
|
385
|
+
details = false,
|
|
386
|
+
readActivity = storedSessionActivity,
|
|
387
|
+
readArchived = storedArchivedSessionIds,
|
|
388
|
+
readFavorites = storedFavoriteSessionIds,
|
|
389
|
+
listJobs = runningShellJobs,
|
|
390
|
+
settings: given = null,
|
|
391
|
+
now = Date.now(),
|
|
83
392
|
} = {}) {
|
|
84
393
|
const backend = manager || (await loadManager());
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
394
|
+
const retainedSessionIds = await listSessionIds();
|
|
395
|
+
if (!details) return backend.listManagedWorktrees({ liveSessionIds, retainedSessionIds });
|
|
396
|
+
// An unreadable job record stops the cleanup itself (fail closed), so no limit applies then.
|
|
397
|
+
const jobs = await listJobs().catch(() => null);
|
|
398
|
+
const live = new Set(liveSessionIds);
|
|
399
|
+
for (const job of jobs ?? []) if (job.ownerSessionId) live.add(job.ownerSessionId);
|
|
400
|
+
const rows = await backend.listManagedWorktrees({
|
|
401
|
+
liveSessionIds: [...live],
|
|
402
|
+
retainedSessionIds,
|
|
403
|
+
inspect: true,
|
|
404
|
+
size: true,
|
|
90
405
|
});
|
|
406
|
+
const owners = rows.flatMap((row) => [row.ownerSessionId, ...(row.sharedSessionIds ?? [])]).filter(Boolean);
|
|
407
|
+
const activity = await readActivity(owners);
|
|
408
|
+
const archived = new Set((await readArchived()) ?? []);
|
|
409
|
+
// The rows the automatic cleanup's count/age limits would delete: the same selection, uncapped.
|
|
410
|
+
const { selected } = jobs
|
|
411
|
+
? await selectLimitCleanup(backend, {
|
|
412
|
+
settings: given ?? (await backend.currentWorktreeSettings()),
|
|
413
|
+
live,
|
|
414
|
+
jobs,
|
|
415
|
+
retainedSessionIds,
|
|
416
|
+
now,
|
|
417
|
+
readFavorites,
|
|
418
|
+
readActivity: async () => activity,
|
|
419
|
+
inspect: true,
|
|
420
|
+
listedRows: rows.map((row) => ({ ...row })),
|
|
421
|
+
maxRemovals: Infinity,
|
|
422
|
+
})
|
|
423
|
+
: { selected: [] };
|
|
424
|
+
const cleanup = new Map(selected.map((row) => [row.id, row.reason]));
|
|
425
|
+
return rows.map((row) => ({
|
|
426
|
+
...row,
|
|
427
|
+
ownerTitle: (row.ownerSessionId && activity[row.ownerSessionId]?.title) || null,
|
|
428
|
+
ownerArchived: !!row.ownerSessionId && archived.has(row.ownerSessionId),
|
|
429
|
+
lastActiveAt: rowActivity(row, activity) || null,
|
|
430
|
+
cleanupReason: cleanup.get(row.id) ?? null,
|
|
431
|
+
}));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/** Archived session ids from the attached desktop; null when none is attached. */
|
|
435
|
+
async function storedArchivedSessionIds() {
|
|
436
|
+
const { readSessionSidebarState } = await import('../session-sidebar-host.mjs');
|
|
437
|
+
const state = await readSessionSidebarState();
|
|
438
|
+
return state ? Object.keys(state.archived ?? {}) : null;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* The manual prune ("Clean up now"; an explicit request, so independent of
|
|
443
|
+
* autoCleanup): the startup reconcile plus removal of clean, merged worktrees
|
|
444
|
+
* that no stored or live session retains, and with autoCleanup on everything
|
|
445
|
+
* the automatic cleanup would remove (runWorktreeCleanup). Dirty or unmerged
|
|
446
|
+
* ones are reported kept. `repoCommonDir` limits the prune to that repository.
|
|
447
|
+
* @param {Parameters<typeof runWorktreeCleanup>[0]} [options]
|
|
448
|
+
*/
|
|
449
|
+
export async function pruneWorktrees(options = {}) {
|
|
450
|
+
return runWorktreeCleanup({ ...options, explicit: true });
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* After sessions were archived (`archived`) or restored: with autoCleanup and
|
|
455
|
+
* cleanupArchived on, an archive marks their worktrees disposable, so the
|
|
456
|
+
* automatic cleanup removes each once it is clean, merged, an hour old, not a
|
|
457
|
+
* favorite's and its owner is not live; dirty or unmerged work stays. Nothing
|
|
458
|
+
* is removed here. Off: nothing is marked. A restore always clears the mark.
|
|
459
|
+
* @param {string[]} sessionIds
|
|
460
|
+
* @param {boolean} archived
|
|
461
|
+
* @param {{ manager?: object, autoCleanup?: boolean, cleanupArchived?: boolean }} [options]
|
|
462
|
+
*/
|
|
463
|
+
export async function setArchivedSessionWorktrees(
|
|
464
|
+
sessionIds,
|
|
465
|
+
archived,
|
|
466
|
+
{ manager = null, autoCleanup, cleanupArchived } = {}
|
|
467
|
+
) {
|
|
468
|
+
const backend = manager || (await loadManager());
|
|
469
|
+
if (archived === true) {
|
|
470
|
+
const settings =
|
|
471
|
+
autoCleanup === undefined || cleanupArchived === undefined ? await backend.currentWorktreeSettings() : null;
|
|
472
|
+
if ((autoCleanup ?? settings.autoCleanup) !== true) return { changed: [], reason: 'auto-cleanup-off' };
|
|
473
|
+
if ((cleanupArchived ?? settings.cleanupArchived) === false) return { changed: [], reason: 'archive-cleanup-off' };
|
|
474
|
+
}
|
|
475
|
+
return { changed: await backend.setSessionWorktreesDisposable(sessionIds, archived === true) };
|
|
91
476
|
}
|
|
92
477
|
|
|
93
478
|
/**
|
|
94
479
|
* After a session was deleted: with autoCleanup on, its worktree is marked
|
|
95
480
|
* disposable and removed when clean and merged (dirty or unmerged work is
|
|
96
481
|
* kept, still disposable, for a later prune once it is clean). Off: kept.
|
|
482
|
+
* Its branch goes only with deleteBranch on.
|
|
97
483
|
* @param {string} sessionId
|
|
98
|
-
* @param {{ manager?: object, autoCleanup?: boolean }} [options]
|
|
484
|
+
* @param {{ manager?: object, autoCleanup?: boolean, deleteBranch?: boolean }} [options]
|
|
99
485
|
*/
|
|
100
|
-
export async function cleanupDeletedSessionWorktree(sessionId, { manager = null, autoCleanup } = {}) {
|
|
486
|
+
export async function cleanupDeletedSessionWorktree(sessionId, { manager = null, autoCleanup, deleteBranch } = {}) {
|
|
101
487
|
const backend = manager || (await loadManager());
|
|
102
488
|
await backend.forgetSharedWorktreeSession?.(sessionId);
|
|
103
489
|
// Its read-only snapshots go whatever autoCleanup says (they never outlive their holder).
|
|
104
490
|
await backend.removeSessionSnapshots?.(sessionId);
|
|
105
491
|
const entry = await backend.getSessionWorktree(sessionId);
|
|
106
492
|
if (!entry) return null;
|
|
107
|
-
const
|
|
493
|
+
const settings =
|
|
494
|
+
autoCleanup === undefined || deleteBranch === undefined ? await backend.currentWorktreeSettings() : null;
|
|
495
|
+
const enabled = autoCleanup ?? settings.autoCleanup;
|
|
108
496
|
if (enabled !== true) return { entryId: entry.id, removed: false, reason: 'auto-cleanup-off' };
|
|
109
497
|
if (entry.state === 'ready') await backend.markWorktreeDisposable(entry.id);
|
|
498
|
+
const preserveBranch = (deleteBranch ?? settings.deleteBranch) !== true;
|
|
110
499
|
try {
|
|
111
|
-
return {
|
|
500
|
+
return {
|
|
501
|
+
entryId: entry.id,
|
|
502
|
+
...(await backend.removeManagedWorktree({ entryId: entry.id, preserveBranch, automatic: true })),
|
|
503
|
+
};
|
|
112
504
|
} catch (error) {
|
|
113
505
|
if (
|
|
114
506
|
isWorktreeError(error, 'WORKTREE_DIRTY') ||
|