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.
Files changed (417) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-shipped-history.mjs +15 -5
  3. package/scripts/gate-local.mjs +65 -19
  4. package/scripts/graph-parity.mjs +8 -3
  5. package/scripts/lib/gate-scheduler.mjs +2 -1
  6. package/scripts/lib/run-node-tests.mjs +17 -5
  7. package/scripts/lib/test-failure-summary.mjs +1 -8
  8. package/scripts/lib/test-spawn-ledger.mjs +33 -2
  9. package/scripts/post-release-check.mjs +26 -16
  10. package/scripts/provider-pingpong-live.mjs +1 -1
  11. package/scripts/prune-embedding-runtime.mjs +3 -1
  12. package/scripts/release-paths.mjs +7 -1
  13. package/src/defaults/shipped-history.json +20 -0
  14. package/src/defaults/skills/setup/SKILL.md +2 -2
  15. package/src/defaults/skills/setup/references/actions.md +16 -3
  16. package/src/defaults/skills/setup/references/surfaces.md +3 -0
  17. package/src/defaults/skills/setup-guide/SKILL.md +6 -1
  18. package/src/lib/instruction-text.cjs +36 -0
  19. package/src/lib/rules-builder.cjs +34 -20
  20. package/src/orchestration/balanced.md +23 -0
  21. package/src/orchestration/focused.md +22 -0
  22. package/src/orchestration/none.md +5 -0
  23. package/src/orchestration/swarm.md +24 -0
  24. package/src/rules/agent-mode/AGENT_MODE.md +3 -1
  25. package/src/rules/agent-mode/MIXY.md +2 -2
  26. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -2
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +0 -9
  30. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
  31. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
  32. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -4
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -1
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +199 -46
  36. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +3 -5
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +9 -3
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -5
  39. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
  41. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
  42. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
  43. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
  46. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
  49. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
  51. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
  52. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +10 -1
  53. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
  54. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +30 -0
  55. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
  56. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
  57. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
  58. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
  59. package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
  60. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
  61. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
  66. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
  67. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
  68. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
  69. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
  70. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -48
  71. package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
  72. package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
  73. package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +9 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
  79. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
  80. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
  83. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
  88. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
  92. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
  93. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +1 -2
  94. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
  97. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
  99. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
  100. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
  101. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
  102. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
  104. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +6 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  108. package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
  109. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +57 -26
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
  113. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
  114. package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
  115. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
  116. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
  117. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
  118. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
  120. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  121. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
  122. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
  123. package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
  126. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +19 -10
  128. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +31 -0
  131. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +42 -6
  133. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
  134. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +11 -1
  135. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
  139. package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
  140. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
  143. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
  145. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
  146. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
  148. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
  149. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
  150. package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
  151. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
  152. package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
  153. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +14 -5
  154. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +2 -2
  155. package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
  156. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
  157. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
  158. package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
  159. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
  160. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
  162. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
  163. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
  167. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +7 -3
  168. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +27 -9
  169. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
  173. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
  175. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
  176. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
  177. package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
  179. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
  180. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
  181. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
  182. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +2 -2
  183. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -4
  185. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
  186. package/src/runtime/attachments/attachment-reference.mjs +1 -1
  187. package/src/runtime/attachments/pdf-extract.mjs +1 -1
  188. package/src/runtime/attachments/store.d.mts +1 -0
  189. package/src/runtime/attachments/store.mjs +2 -2
  190. package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
  191. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
  192. package/src/runtime/channels/lib/scheduler.mjs +33 -0
  193. package/src/runtime/channels/lib/schedules-db.mjs +11 -4
  194. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
  195. package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
  196. package/src/runtime/channels/lib/whisper-server.mjs +12 -12
  197. package/src/runtime/channels/lib/worker-main.mjs +9 -31
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
  199. package/src/runtime/connectors/connector-accounts-mcp.mjs +169 -33
  200. package/src/runtime/connectors/gmail-app-password.mjs +2 -2
  201. package/src/runtime/connectors/google-mcp.mjs +1 -1
  202. package/src/runtime/effort-judge/judge-client.mjs +2 -2
  203. package/src/runtime/effort-judge/judge-input.mjs +4 -4
  204. package/src/runtime/explore/explorer.mjs +0 -5
  205. package/src/runtime/explore/tool.mjs +3 -3
  206. package/src/runtime/local-provider/hardware.mjs +1 -1
  207. package/src/runtime/media/adapters/codex-image.mjs +3 -3
  208. package/src/runtime/media/adapters/gemini-video.mjs +1 -1
  209. package/src/runtime/media/media-usage.mjs +1 -1
  210. package/src/runtime/media/store.mjs +1 -1
  211. package/src/runtime/memory/index.mjs +11 -1
  212. package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
  213. package/src/runtime/memory/lib/memory-embed.mjs +83 -14
  214. package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
  215. package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
  216. package/src/runtime/memory/lib/memory.mjs +17 -0
  217. package/src/runtime/memory/lib/pg/process.mjs +1 -1
  218. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
  219. package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
  220. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  221. package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
  222. package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
  223. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  224. package/src/runtime/office/capabilities.mjs +1 -1
  225. package/src/runtime/office/core/office-core.mjs +1 -1
  226. package/src/runtime/office/design/content-model.mjs +3 -3
  227. package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
  228. package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
  229. package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
  230. package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
  231. package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
  232. package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
  233. package/src/runtime/office/portable/portable-soffice.mjs +8 -6
  234. package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
  235. package/src/runtime/shared/atomic-file.d.mts +8 -0
  236. package/src/runtime/shared/background-tasks.mjs +22 -4
  237. package/src/runtime/shared/background-wait.mjs +66 -0
  238. package/src/runtime/shared/child-guardian.mjs +17 -7
  239. package/src/runtime/shared/config.mjs +313 -16
  240. package/src/runtime/shared/connector-catalog.d.mts +4 -5
  241. package/src/runtime/shared/connector-catalog.mjs +11 -1
  242. package/src/runtime/shared/file-lock.mjs +8 -3
  243. package/src/runtime/shared/git-runner.mjs +7 -2
  244. package/src/runtime/shared/idle-gc.mjs +30 -7
  245. package/src/runtime/shared/llm/http-agent.mjs +8 -5
  246. package/src/runtime/shared/llm/system-proxy.mjs +2 -1
  247. package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
  248. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
  249. package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
  250. package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
  251. package/src/runtime/shared/maintenance-schedule.mjs +109 -0
  252. package/src/runtime/shared/process-runner.mjs +8 -4
  253. package/src/runtime/shared/routine-trigger.d.mts +13 -0
  254. package/src/runtime/shared/routine-trigger.mjs +34 -0
  255. package/src/runtime/shared/run-child.d.mts +2 -1
  256. package/src/runtime/shared/run-child.mjs +70 -18
  257. package/src/runtime/shared/session-scratch.mjs +14 -2
  258. package/src/runtime/shared/shipped-definitions.mjs +7 -1
  259. package/src/runtime/shared/tool-card-model.golden.json +3 -12
  260. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  261. package/src/runtime/shared/tool-work-units.mjs +31 -6
  262. package/src/runtime/shared/turn-snapshot.mjs +26 -4
  263. package/src/runtime/shared/turn-worktree-snapshot.mjs +6 -11
  264. package/src/runtime/shared/worker-requests.mjs +17 -3
  265. package/src/runtime/shared/worktrees/branch-management.mjs +1 -5
  266. package/src/runtime/shared/worktrees/contract.mjs +16 -6
  267. package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
  268. package/src/runtime/shared/worktrees/disk.mjs +79 -2
  269. package/src/runtime/shared/worktrees/git.mjs +44 -11
  270. package/src/runtime/shared/worktrees/include.mjs +6 -4
  271. package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
  272. package/src/runtime/shared/worktrees/manager.mjs +772 -75
  273. package/src/runtime/shared/worktrees/settings.mjs +33 -7
  274. package/src/runtime/shared/worktrees/test-support.mjs +5 -1
  275. package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
  276. package/src/runtime/tidy/process.mjs +2 -1
  277. package/src/runtime/web-search/index.mjs +3 -22
  278. package/src/runtime/web-search/lib/web-tools.mjs +2 -2
  279. package/src/runtime/web-search/tool-defs.mjs +17 -26
  280. package/src/session-runtime/agent-profile-changes.mjs +55 -4
  281. package/src/session-runtime/boot/apis.mjs +27 -4
  282. package/src/session-runtime/boot/begin.mjs +2 -0
  283. package/src/session-runtime/boot/config.mjs +10 -1
  284. package/src/session-runtime/boot/facade.mjs +1 -0
  285. package/src/session-runtime/boot/providers.mjs +70 -3
  286. package/src/session-runtime/boot/session.mjs +2 -0
  287. package/src/session-runtime/boot/tools.mjs +69 -25
  288. package/src/session-runtime/channel-config-api.mjs +1 -0
  289. package/src/session-runtime/config-change-effects.mjs +179 -0
  290. package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
  291. package/src/session-runtime/config-lifecycle.mjs +160 -24
  292. package/src/session-runtime/conversation-import/discovery.mjs +86 -7
  293. package/src/session-runtime/conversation-import/service.mjs +3 -2
  294. package/src/session-runtime/goal-facade-api.mjs +49 -3
  295. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
  296. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
  297. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +3 -11
  298. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +18 -4
  299. package/src/session-runtime/internal-tool-executor.mjs +12 -19
  300. package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
  301. package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
  302. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
  303. package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
  304. package/src/session-runtime/lifecycle/teardown/session-close.mjs +4 -4
  305. package/src/session-runtime/lifecycle-api.mjs +1 -0
  306. package/src/session-runtime/mcp-glue/server-catalog.mjs +14 -3
  307. package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
  308. package/src/session-runtime/model-route/set-route.mjs +40 -7
  309. package/src/session-runtime/native-web-search/routes.mjs +3 -3
  310. package/src/session-runtime/provider-auth/refresh.mjs +6 -3
  311. package/src/session-runtime/provider-auth/setup.mjs +1 -1
  312. package/src/session-runtime/provider-readiness.mjs +53 -4
  313. package/src/session-runtime/resource-api.mjs +1 -1
  314. package/src/session-runtime/resource-extension-sync.mjs +4 -8
  315. package/src/session-runtime/resource-mcp-connector-accounts.mjs +5 -1
  316. package/src/session-runtime/resource-mcp-secrets.mjs +3 -3
  317. package/src/session-runtime/resource-plugins-api.mjs +4 -0
  318. package/src/session-runtime/routine-executor.mjs +152 -0
  319. package/src/session-runtime/routine-home-turn.mjs +46 -5
  320. package/src/session-runtime/runtime-facade.mjs +5 -0
  321. package/src/session-runtime/runtime-feature-gates.mjs +2 -11
  322. package/src/session-runtime/schedule-session-run.mjs +1 -0
  323. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
  324. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
  325. package/src/session-runtime/services/agent-tool/send-flow.mjs +110 -6
  326. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
  327. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
  328. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +24 -6
  329. package/src/session-runtime/services/agent-tool.mjs +51 -1
  330. package/src/session-runtime/services/channel-admin.mjs +18 -0
  331. package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
  332. package/src/session-runtime/services/plugin-admin.mjs +4 -0
  333. package/src/session-runtime/services/seeds.mjs +7 -0
  334. package/src/session-runtime/services/usage-stats-model.mjs +32 -11
  335. package/src/session-runtime/session-create/materialize.mjs +4 -2
  336. package/src/session-runtime/session-create/session-options.mjs +2 -0
  337. package/src/session-runtime/settings-builtin-tools-api.mjs +1 -9
  338. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +1 -1
  339. package/src/session-runtime/setup-tool/executor.mjs +8 -1
  340. package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
  341. package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
  342. package/src/session-runtime/setup-tool/tool-defs.mjs +26 -1
  343. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
  344. package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
  345. package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
  346. package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
  347. package/src/session-runtime/tool-policy-refresh.mjs +182 -5
  348. package/src/session-runtime/tool-surface.mjs +38 -1
  349. package/src/session-runtime/turn/agent-ops.mjs +10 -2
  350. package/src/session-runtime/turn/session-ops.mjs +2 -0
  351. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
  352. package/src/session-runtime/turn/turn-run.mjs +1 -1
  353. package/src/session-runtime/webhook-session-run.mjs +2 -0
  354. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +1 -4
  355. package/src/session-runtime/workflow-agents-api/agent-route.mjs +3 -10
  356. package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
  357. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
  358. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
  359. package/src/session-runtime/workflow-agents-api.mjs +2 -0
  360. package/src/session-runtime/workspace/project-branches.mjs +1 -1
  361. package/src/session-runtime/workspace/session-view.mjs +1 -1
  362. package/src/session-runtime/workspace/transitions.mjs +20 -4
  363. package/src/session-runtime/workspace/worktree-maintenance.mjs +126 -6
  364. package/src/session-runtime/workspace/worktree-setup.mjs +13 -1
  365. package/src/session-runtime/workspace/worktree-tool.mjs +16 -0
  366. package/src/standalone/daemon-agent-control.mjs +25 -8
  367. package/src/standalone/daemon-desktop-runtime.mjs +6 -2
  368. package/src/standalone/daemon-lag-profiler.mjs +7 -7
  369. package/src/standalone/daemon.mjs +54 -2
  370. package/src/standalone/local-session-runtime.mjs +16 -0
  371. package/src/standalone/session-client/daemon-unavailable.mjs +1 -1
  372. package/src/standalone/session-protocol.mjs +11 -0
  373. package/src/standalone/session-service/agent-route-follow.mjs +23 -20
  374. package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
  375. package/src/standalone/session-service/quota-resume.mjs +9 -0
  376. package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
  377. package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
  378. package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
  379. package/src/standalone/session-service/session-calls.mjs +2 -1
  380. package/src/tui/App.jsx +8 -0
  381. package/src/tui/app/clipboard.mjs +1 -1
  382. package/src/tui/app/create-app-pickers.mjs +2 -0
  383. package/src/tui/app/doctor.mjs +2 -2
  384. package/src/tui/app/panel-signature.mjs +1 -1
  385. package/src/tui/app/settings-picker.mjs +53 -8
  386. package/src/tui/app/shell-layout/row-budgets.mjs +2 -2
  387. package/src/tui/app/slash-commands.mjs +1 -1
  388. package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
  389. package/src/tui/app/slash-dispatch/session-commands.mjs +1 -1
  390. package/src/tui/app/use-prompt-queue-history.mjs +2 -2
  391. package/src/tui/app/use-transcript-activity.mjs +1 -1
  392. package/src/tui/app/use-transcript-window/geometry.mjs +1 -1
  393. package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
  394. package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
  395. package/src/tui/components/question-panel-model.mjs +4 -4
  396. package/src/tui/components/tool-execution/standard-card.jsx +3 -4
  397. package/src/tui/components/user-message-skills.mjs +1 -1
  398. package/src/tui/dist/index.mjs +309 -30
  399. package/src/tui/markdown/render-ansi.mjs +1 -1
  400. package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
  401. package/src/tui/session/agent-job-feed.mjs +2 -0
  402. package/src/tui/session/context-state.mjs +3 -0
  403. package/src/tui/session/quota-resume.mjs +1 -1
  404. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  405. package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
  406. package/src/tui/session/session-api/routes-agent-definitions.mjs +5 -0
  407. package/src/tui/session/session-api/worktree.mjs +13 -1
  408. package/src/tui/session/session-flow/drain.mjs +11 -14
  409. package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
  410. package/src/tui/session/turn-quiet.mjs +1 -1
  411. package/src/tui/session/turn.mjs +3 -0
  412. package/src/tui/session/worktree-setup-notice.mjs +1 -1
  413. package/src/tui/spinner-meta.mjs +3 -3
  414. package/src/tui/spinner-verbs.mjs +2 -2
  415. package/src/defaults/skills/goal-management/SKILL.md +0 -169
  416. package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
  417. 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
- * Daemon startup: resume interrupted deletions and creations, release stale
40
- * leases, prune stale registrations; with autoCleanup on, also remove clean,
41
- * merged worktrees no stored session retains.
42
- * @param {{ liveSessionIds?: string[], listSessionIds?: () => Promise<string[]>, manager?: object }} [options]
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
- export async function reconcileWorktreesAtStartup({
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
- return backend.reconcileManagedWorktrees({
52
- liveSessionIds,
53
- retainedSessionIds: await listSessionIds(),
54
- removeOrphans: settings.autoCleanup === true,
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 manual list: every managed worktree with its retention.
60
- * @param {{ liveSessionIds?: string[], listSessionIds?: () => Promise<string[]>, manager?: object }} [options]
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 listWorktreesWithRetention({
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
- return backend.listManagedWorktrees({ liveSessionIds, retainedSessionIds: await listSessionIds() });
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
- * The manual prune (an explicit request, so independent of autoCleanup):
73
- * the startup reconcile plus removal of clean, merged worktrees that no
74
- * stored or live session retains. Dirty or unmerged ones are reported kept.
75
- * `repoCommonDir` limits the prune to that repository's worktrees.
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 pruneWorktrees({
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
- repoCommonDir = null,
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
- return backend.reconcileManagedWorktrees({
86
- liveSessionIds,
87
- retainedSessionIds: await listSessionIds(),
88
- removeOrphans: true,
89
- ...(repoCommonDir ? { repoCommonDir } : {}),
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 enabled = autoCleanup ?? (await backend.currentWorktreeSettings()).autoCleanup;
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 { entryId: entry.id, ...(await backend.removeManagedWorktree({ entryId: entry.id })) };
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') ||