mixdog 1.3.1 → 1.3.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 (468) hide show
  1. package/README.md +8 -4
  2. package/package.json +8 -8
  3. package/scripts/native-release-status.mjs +238 -0
  4. package/scripts/verify-runtime-dependency-versions.mjs +158 -0
  5. package/src/agents/reviewer/AGENT.md +5 -7
  6. package/src/agents/reviewer/agent.json +1 -1
  7. package/src/defaults/shipped-history.json +9 -1
  8. package/src/defaults/skills/browser-use/SKILL.md +6 -3
  9. package/src/defaults/skills/local-provider/SKILL.md +33 -6
  10. package/src/defaults/skills/pptx/scripts/qc-pages.mjs +3 -2
  11. package/src/defaults/skills/setup/SKILL.md +23 -1
  12. package/src/defaults/skills/setup/references/actions.md +50 -3
  13. package/src/defaults/skills/setup/references/surfaces.md +23 -3
  14. package/src/defaults/skills/setup-guide/SKILL.md +7 -3
  15. package/src/defaults/skills/visualize/SKILL.md +9 -6
  16. package/src/defaults/skills/visualize/references/craft.md +8 -1
  17. package/src/help.mjs +1 -1
  18. package/src/lib/keychain-cjs.cjs +4 -1
  19. package/src/lib/rules-builder.cjs +14 -7
  20. package/src/rules/agent-mode/AGENT_MODE.md +8 -5
  21. package/src/rules/agent-mode/MIXY.md +6 -8
  22. package/src/rules/lead/LEAD.md +7 -10
  23. package/src/rules/shared/10-tool-workflow.md +4 -0
  24. package/src/rules/shared/20-research.md +2 -0
  25. package/src/rules/shared/45-destructive.md +5 -1
  26. package/src/rules/shared/60-verification.md +6 -4
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +5 -0
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +8 -0
  29. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +6 -1
  30. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +114 -26
  31. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +8 -0
  32. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +5 -4
  33. package/src/runtime/agent/orchestrator/agent-trace.mjs +3 -0
  34. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +4 -0
  35. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  36. package/src/runtime/agent/orchestrator/config-storage.mjs +10 -3
  37. package/src/runtime/agent/orchestrator/config.mjs +11 -0
  38. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +9 -2
  39. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +97 -5
  40. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +6 -10
  41. package/src/runtime/agent/orchestrator/mcp/client.mjs +7 -4
  42. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +84 -32
  43. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +38 -11
  44. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +15 -3
  45. package/src/runtime/agent/orchestrator/providers/anthropic-model-resolve.mjs +1 -1
  46. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +84 -42
  48. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -0
  49. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +14 -1
  50. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +6 -2
  51. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +27 -8
  52. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  53. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +31 -4
  54. package/src/runtime/agent/orchestrator/providers/cursor.mjs +4 -1
  55. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +14 -1
  56. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +11 -8
  57. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  58. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -0
  59. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +16 -0
  60. package/src/runtime/agent/orchestrator/providers/lib/tail-reminder-split.mjs +13 -0
  61. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -1
  62. package/src/runtime/agent/orchestrator/providers/mixdog-local.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +1 -0
  64. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +5 -5
  65. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +9 -5
  66. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +9 -1
  67. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +2 -1
  68. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +30 -3
  69. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +1 -1
  70. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +61 -8
  72. package/src/runtime/agent/orchestrator/providers/registry.mjs +19 -0
  73. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +13 -1
  74. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +90 -24
  75. package/src/runtime/agent/orchestrator/runtime-core/fs-utils.mjs +8 -1
  76. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +5 -0
  78. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +68 -22
  79. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +1 -1
  80. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +10 -1
  81. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +4 -6
  82. package/src/runtime/agent/orchestrator/runtime-core/route-context-state.mjs +81 -0
  83. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +52 -6
  85. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +35 -13
  86. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +7 -6
  87. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  88. package/src/runtime/agent/orchestrator/runtime-core/workflow/packs.mjs +8 -4
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +8 -3
  90. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +0 -6
  92. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +1 -1
  93. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +22 -4
  94. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +30 -2
  95. package/src/runtime/agent/orchestrator/session/agent-room.mjs +196 -45
  96. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +80 -0
  97. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/session/compact/continuation.mjs +15 -6
  99. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +45 -19
  100. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +3 -0
  101. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +44 -0
  102. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +185 -37
  103. package/src/runtime/agent/orchestrator/session/compact/summary-mode.mjs +16 -0
  104. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +87 -0
  105. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +2 -0
  106. package/src/runtime/agent/orchestrator/session/compact/text-utils.mjs +6 -2
  107. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +4 -1
  108. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  109. package/src/runtime/agent/orchestrator/session/context-utils.mjs +41 -10
  110. package/src/runtime/agent/orchestrator/session/human-gate/delegation.mjs +240 -0
  111. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +102 -35
  112. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +20 -0
  113. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +19 -9
  114. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +73 -0
  115. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +55 -27
  116. package/src/runtime/agent/orchestrator/session/human-gate/unattended-port.mjs +15 -0
  117. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +4 -1
  118. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +199 -109
  119. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +5 -0
  120. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +2 -0
  121. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/empty-nudge.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +62 -13
  123. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +22 -6
  124. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +6 -2
  125. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +5 -5
  126. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +10 -0
  127. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -0
  128. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +33 -8
  129. package/src/runtime/agent/orchestrator/session/loop/tool-exec/call-prep.mjs +5 -0
  130. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +27 -0
  131. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +2 -1
  132. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +12 -0
  133. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +11 -5
  134. package/src/runtime/agent/orchestrator/session/manager/agent-listing-notice.mjs +102 -0
  135. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +2 -0
  136. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +13 -1
  137. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +13 -2
  138. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +27 -14
  139. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +14 -0
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +4 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +44 -18
  143. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +32 -1
  144. package/src/runtime/agent/orchestrator/session/manager/orchestration-mode-switch-notice.mjs +115 -0
  145. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +42 -0
  146. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +6 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +3 -9
  148. package/src/runtime/agent/orchestrator/session/manager/pending-spool-file.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +5 -3
  150. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +36 -0
  151. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +40 -1
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +12 -0
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +20 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +28 -9
  155. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +55 -27
  156. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +4 -9
  157. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +14 -1
  158. package/src/runtime/agent/orchestrator/session/manager/session-tool-surface.mjs +7 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +6 -15
  160. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +24 -8
  161. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +94 -13
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +52 -11
  163. package/src/runtime/agent/orchestrator/session/provider-prefix-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +18 -3
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +126 -12
  166. package/src/runtime/agent/orchestrator/session/runtime-user-context.mjs +3 -1
  167. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +16 -3
  168. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +3 -1
  169. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +41 -7
  170. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +10 -2
  171. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +14 -2
  172. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +15 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +5 -4
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +140 -0
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +5 -0
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +15 -5
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +49 -1
  178. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +80 -1
  179. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +40 -5
  180. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +47 -2
  181. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +33 -1
  182. package/src/runtime/agent/orchestrator/session/store-summary-visibility.d.mts +5 -0
  183. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +37 -0
  184. package/src/runtime/agent/orchestrator/session/store.mjs +1 -0
  185. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +125 -0
  186. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +4 -0
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +78 -43
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +35 -5
  189. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +42 -2
  190. package/src/runtime/agent/orchestrator/tools/builtin/advisory-lock.mjs +47 -14
  191. package/src/runtime/agent/orchestrator/tools/builtin/atomic-write.mjs +25 -5
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +6 -3
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +13 -14
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/transport-artifacts.mjs +9 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +19 -14
  196. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +7 -1
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +127 -20
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +557 -0
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +10 -12
  200. package/src/runtime/agent/orchestrator/tools/builtin/pdf-test-fixtures.mjs +5 -3
  201. package/src/runtime/agent/orchestrator/tools/builtin/read-image.mjs +4 -2
  202. package/src/runtime/agent/orchestrator/tools/builtin/read-single-media.mjs +47 -2
  203. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +264 -87
  204. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +10 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +42 -23
  206. package/src/runtime/agent/orchestrator/tools/code-graph/constants.mjs +8 -4
  207. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +9 -30
  208. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +24 -12
  209. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +3 -2
  210. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -2
  211. package/src/runtime/agent/orchestrator/tools/patch/delete-quarantine.mjs +2 -1
  212. package/src/runtime/agent/orchestrator/tools/patch/replay-capture.mjs +4 -2
  213. package/src/runtime/agent/orchestrator/tools/patch/rollback-state.mjs +33 -2
  214. package/src/runtime/agent/orchestrator/tools/patch-manifest.json +13 -13
  215. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +5 -3
  216. package/src/runtime/agent/orchestrator/tools/shell-exec-output.mjs +3 -1
  217. package/src/runtime/attachments/attachment-reference.mjs +155 -0
  218. package/src/runtime/attachments/legacy-office.mjs +110 -0
  219. package/src/runtime/attachments/limits.mjs +5 -1
  220. package/src/runtime/attachments/office-extract.mjs +3 -6
  221. package/src/runtime/attachments/pdf-extract.mjs +84 -10
  222. package/src/runtime/attachments/prompt-restore.mjs +70 -0
  223. package/src/runtime/attachments/store.mjs +109 -33
  224. package/src/runtime/browser-bridge/client.mjs +73 -20
  225. package/src/runtime/channels/lib/runtime-paths.mjs +1 -1
  226. package/src/runtime/channels/lib/scheduler.mjs +14 -7
  227. package/src/runtime/channels/lib/state-file.mjs +15 -15
  228. package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +14 -13
  229. package/src/runtime/channels/lib/webhook/relay-tunnel.mjs +22 -18
  230. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +14 -36
  231. package/src/runtime/channels/lib/worker-main.mjs +3 -2
  232. package/src/runtime/effort-judge/compact-tokenizer.mjs +8 -2
  233. package/src/runtime/effort-judge/judge-client.mjs +66 -16
  234. package/src/runtime/effort-judge/model-install.mjs +47 -7
  235. package/src/runtime/local-provider/asset-installer.mjs +10 -4
  236. package/src/runtime/local-provider/catalog.mjs +5 -2
  237. package/src/runtime/local-provider/registered-models.mjs +64 -27
  238. package/src/runtime/local-provider/server.mjs +2 -3
  239. package/src/runtime/media/adapters/image-api.mjs +89 -0
  240. package/src/runtime/media/auth.mjs +64 -1
  241. package/src/runtime/media/catalog.mjs +66 -4
  242. package/src/runtime/media/jobs.mjs +4 -0
  243. package/src/runtime/media/lanes.mjs +46 -5
  244. package/src/runtime/media/media-usage.mjs +17 -0
  245. package/src/runtime/media/renditions.mjs +15 -15
  246. package/src/runtime/media/store.mjs +36 -35
  247. package/src/runtime/media/tool.mjs +8 -1
  248. package/src/runtime/memory/lib/cycle-scheduler/health-ledger.mjs +8 -2
  249. package/src/runtime/memory/lib/embedding-model-files.mjs +30 -7
  250. package/src/runtime/memory/lib/ingest/transcript-tail.mjs +16 -7
  251. package/src/runtime/memory/lib/memory-chunk-quality.mjs +14 -2
  252. package/src/runtime/memory/lib/pg/process.mjs +8 -4
  253. package/src/runtime/memory/lib/project-id-resolver.mjs +5 -1
  254. package/src/runtime/memory/lib/runtime-fetcher.mjs +6 -1
  255. package/src/runtime/office/core/journal.mjs +7 -14
  256. package/src/runtime/office/design/library/design-library-core.mjs +4 -16
  257. package/src/runtime/office/pdf/ocr-engine-worker.mjs +37 -0
  258. package/src/runtime/office/pdf/ocr-engine.mjs +146 -0
  259. package/src/runtime/office/pdf/pdf-ocr.mjs +51 -21
  260. package/src/runtime/office/pdf/pdf-render.mjs +30 -18
  261. package/src/runtime/office/portable/ooxml-validator.mjs +2 -2
  262. package/src/runtime/office/portable/portable-soffice.mjs +135 -29
  263. package/src/runtime/shared/agent-avatar-sprites.mjs +23 -8
  264. package/src/runtime/shared/agent-avatar.mjs +3 -2
  265. package/src/runtime/shared/agent-kind-migration.mjs +200 -0
  266. package/src/runtime/shared/agent-message-envelope.d.mts +6 -0
  267. package/src/runtime/shared/agent-message-envelope.mjs +44 -0
  268. package/src/runtime/shared/agent-reap-state.d.mts +4 -0
  269. package/src/runtime/shared/agent-room-host.d.mts +31 -0
  270. package/src/runtime/shared/agent-room-host.mjs +58 -0
  271. package/src/runtime/shared/agent-store.mjs +24 -7
  272. package/src/runtime/shared/agent-task-starts.mjs +27 -0
  273. package/src/runtime/shared/atomic-file.d.mts +50 -0
  274. package/src/runtime/shared/atomic-file.mjs +394 -132
  275. package/src/runtime/shared/background-tasks.mjs +7 -4
  276. package/src/runtime/shared/bot-hops.mjs +35 -0
  277. package/src/runtime/shared/bot-split-migration.mjs +1 -1
  278. package/src/runtime/shared/bounded-download.mjs +4 -2
  279. package/src/runtime/shared/config.mjs +39 -9
  280. package/src/runtime/shared/defaults-separation.mjs +6 -2
  281. package/src/runtime/shared/delegated-approval-host.mjs +74 -0
  282. package/src/runtime/shared/err-text.mjs +4 -0
  283. package/src/runtime/shared/error-presentation.mjs +22 -4
  284. package/src/runtime/shared/file-has-data.mjs +28 -0
  285. package/src/runtime/shared/llm/usage-ledger-import.mjs +37 -18
  286. package/src/runtime/shared/pending-agent-work.mjs +54 -0
  287. package/src/runtime/shared/process-lifecycle.mjs +8 -12
  288. package/src/runtime/shared/projects.mjs +50 -41
  289. package/src/runtime/shared/provider-accounts.mjs +17 -12
  290. package/src/runtime/shared/redact-directories.mjs +18 -0
  291. package/src/runtime/shared/remove-directory.mjs +28 -0
  292. package/src/runtime/shared/routine-trigger.d.mts +6 -1
  293. package/src/runtime/shared/routine-trigger.mjs +14 -6
  294. package/src/runtime/shared/run-child.d.mts +3 -0
  295. package/src/runtime/shared/run-child.mjs +24 -3
  296. package/src/runtime/shared/session-baseline-store.mjs +8 -2
  297. package/src/runtime/shared/session-sidebar-host.d.mts +11 -2
  298. package/src/runtime/shared/session-sidebar-host.mjs +16 -3
  299. package/src/runtime/shared/singleton-owner.mjs +7 -6
  300. package/src/runtime/shared/skill-tool-dependencies.mjs +4 -10
  301. package/src/runtime/shared/staged-update.mjs +4 -2
  302. package/src/runtime/shared/statusline/statusline-segments.mjs +4 -15
  303. package/src/runtime/shared/tool-execution-contract.mjs +10 -0
  304. package/src/runtime/shared/tool-work-units.mjs +12 -9
  305. package/src/runtime/shared/transcript-metadata.mjs +28 -3
  306. package/src/runtime/shared/transcript-writer/session-record.mjs +7 -6
  307. package/src/runtime/shared/turn-snapshot-store.mjs +5 -4
  308. package/src/runtime/shared/turn-snapshot.mjs +277 -124
  309. package/src/runtime/shared/user-cwd.mjs +9 -4
  310. package/src/runtime/shared/user-data-guard.mjs +92 -61
  311. package/src/runtime/shared/utf8-cut.mjs +10 -0
  312. package/src/runtime/shared/worktrees/activity-gate.mjs +251 -61
  313. package/src/runtime/shared/worktrees/contract.mjs +11 -3
  314. package/src/runtime/shared/worktrees/maintenance.mjs +11 -2
  315. package/src/runtime/shared/worktrees/manager.mjs +419 -52
  316. package/src/runtime/shared/worktrees/registry.mjs +29 -27
  317. package/src/runtime/shared/worktrees/tool-host.mjs +39 -0
  318. package/src/runtime/tidy/core-install.mjs +26 -4
  319. package/src/runtime/tidy/install.mjs +91 -23
  320. package/src/runtime/tidy/process.mjs +41 -8
  321. package/src/runtime/tidy/resolve.mjs +10 -7
  322. package/src/runtime/web-search/lib/config.mjs +4 -16
  323. package/src/session-runtime/agent-home-delivery.mjs +542 -204
  324. package/src/session-runtime/agent-profile-changes.mjs +16 -0
  325. package/src/session-runtime/agent-room-delivery.mjs +81 -35
  326. package/src/session-runtime/agent-task-sessions.mjs +811 -179
  327. package/src/session-runtime/agent-tool-routing.mjs +1 -0
  328. package/src/session-runtime/boot/apis.mjs +48 -24
  329. package/src/session-runtime/boot/config.mjs +1 -0
  330. package/src/session-runtime/boot/facade.mjs +16 -7
  331. package/src/session-runtime/boot/infrastructure.mjs +14 -2
  332. package/src/session-runtime/boot/providers.mjs +2 -2
  333. package/src/session-runtime/boot/shared.mjs +12 -6
  334. package/src/session-runtime/boot/tools.mjs +23 -3
  335. package/src/session-runtime/bot-status.mjs +432 -0
  336. package/src/session-runtime/config-lifecycle/lead-route-sync.mjs +29 -0
  337. package/src/session-runtime/config-lifecycle.mjs +60 -55
  338. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +2 -4
  339. package/src/session-runtime/held-requests.mjs +31 -33
  340. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +10 -1
  341. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +2 -2
  342. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +24 -26
  343. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  344. package/src/session-runtime/lifecycle/teardown.mjs +11 -3
  345. package/src/session-runtime/mcp-glue/connect.mjs +100 -186
  346. package/src/session-runtime/mcp-glue/initial-connect-gate.mjs +57 -0
  347. package/src/session-runtime/mcp-glue/server-connection.mjs +71 -0
  348. package/src/session-runtime/media-api.mjs +5 -2
  349. package/src/session-runtime/model-recency.mjs +1 -0
  350. package/src/session-runtime/model-route/route-tuning.mjs +4 -1
  351. package/src/session-runtime/model-route/set-route.mjs +61 -1
  352. package/src/session-runtime/owned-automation-run.mjs +8 -1
  353. package/src/session-runtime/provider-models/row-hydration.mjs +8 -1
  354. package/src/session-runtime/quick-model-rows/model-meta.mjs +2 -0
  355. package/src/session-runtime/quick-model-rows/provider-rows.mjs +7 -1
  356. package/src/session-runtime/route-resolution.mjs +16 -8
  357. package/src/session-runtime/route-state.mjs +1 -59
  358. package/src/session-runtime/routine-home-turn.mjs +5 -3
  359. package/src/session-runtime/runtime-bootstrap.mjs +5 -3
  360. package/src/session-runtime/runtime-paths.mjs +11 -5
  361. package/src/session-runtime/runtime-review-api.mjs +4 -1
  362. package/src/session-runtime/runtime-tunables.mjs +6 -0
  363. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +11 -2
  364. package/src/session-runtime/services/agent-tool/close-flow.mjs +4 -2
  365. package/src/session-runtime/services/agent-tool/execute-send.mjs +1 -0
  366. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +1 -0
  367. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +34 -19
  368. package/src/session-runtime/services/agent-tool/restart-notify.mjs +10 -0
  369. package/src/session-runtime/services/agent-tool/send-flow.mjs +25 -21
  370. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +7 -0
  371. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +6 -3
  372. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +8 -5
  373. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +22 -2
  374. package/src/session-runtime/services/agent-tool/worker-index.mjs +4 -6
  375. package/src/session-runtime/services/agent-tool.mjs +6 -0
  376. package/src/session-runtime/services/daemon-crash-capture/daemon-exit-record.mjs +1 -1
  377. package/src/session-runtime/services/daemon-crash-capture/retention.mjs +4 -3
  378. package/src/session-runtime/services/hook-bus/config-loader.mjs +14 -5
  379. package/src/session-runtime/services/hook-bus/rule-file.mjs +49 -11
  380. package/src/session-runtime/services/plugin-admin.mjs +26 -26
  381. package/src/session-runtime/services/provider-admin.mjs +26 -6
  382. package/src/session-runtime/services/schedule-access.mjs +33 -4
  383. package/src/session-runtime/services/seeds.mjs +4 -3
  384. package/src/session-runtime/services/usage-dashboard.mjs +1 -1
  385. package/src/session-runtime/services/usage-stats-model.mjs +2 -1
  386. package/src/session-runtime/services/worktree-access.mjs +27 -0
  387. package/src/session-runtime/session-create/materialize.mjs +2 -3
  388. package/src/session-runtime/session-create/session-options.mjs +15 -2
  389. package/src/session-runtime/session-hooks.mjs +9 -17
  390. package/src/session-runtime/settings-builtin-tools-api.mjs +65 -1
  391. package/src/session-runtime/settings-compaction-api.mjs +86 -39
  392. package/src/session-runtime/setup-tool/executor.mjs +12 -2
  393. package/src/session-runtime/setup-tool/settings-contract.mjs +34 -1
  394. package/src/session-runtime/setup-tool/tool-defs.mjs +20 -3
  395. package/src/session-runtime/skills-api.mjs +12 -5
  396. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +349 -0
  397. package/src/session-runtime/tool-policy/mode-switch.mjs +54 -0
  398. package/src/session-runtime/tool-policy/pending-switches.mjs +59 -0
  399. package/src/session-runtime/tool-policy/policy-stage.mjs +183 -0
  400. package/src/session-runtime/tool-policy/session-switches.mjs +169 -0
  401. package/src/session-runtime/tool-policy/surface-helpers.mjs +114 -0
  402. package/src/session-runtime/tool-policy/workflow-overlay.mjs +145 -0
  403. package/src/session-runtime/tool-policy-refresh.mjs +48 -369
  404. package/src/session-runtime/tool-surface.mjs +10 -4
  405. package/src/session-runtime/turn/session-ops.mjs +8 -0
  406. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +95 -88
  407. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  408. package/src/session-runtime/turn/turn-run.mjs +7 -2
  409. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +54 -7
  410. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +527 -333
  411. package/src/session-runtime/workflow-agents-api/agent-route.mjs +85 -63
  412. package/src/session-runtime/workflow-agents-api/shared.mjs +5 -1
  413. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +14 -1
  414. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +6 -3
  415. package/src/session-runtime/workspace/transition-steps.mjs +1 -18
  416. package/src/session-runtime/workspace/transitions.mjs +279 -67
  417. package/src/session-runtime/workspace/workspace-state.mjs +7 -6
  418. package/src/session-runtime/workspace/worktree-tool.mjs +194 -0
  419. package/src/standalone/daemon-agent-control.mjs +4 -1
  420. package/src/standalone/daemon-lag-profiler.mjs +3 -1
  421. package/src/standalone/daemon-session-control.mjs +173 -15
  422. package/src/standalone/daemon-telemetry.mjs +9 -0
  423. package/src/standalone/daemon.mjs +33 -10
  424. package/src/standalone/local-session-runtime.mjs +33 -16
  425. package/src/standalone/session-client.mjs +7 -2
  426. package/src/standalone/session-protocol.mjs +8 -0
  427. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +2 -0
  428. package/src/standalone/session-service/agent-route-follow.mjs +69 -0
  429. package/src/standalone/session-service/session-calls/session-turns.mjs +33 -4
  430. package/src/standalone/session-service.mjs +21 -0
  431. package/src/standalone/session-state-projection.mjs +19 -9
  432. package/src/standalone/session-tool-output.mjs +36 -9
  433. package/src/standalone/staged-import.mjs +47 -0
  434. package/src/tui/App.jsx +2 -0
  435. package/src/tui/app/maintenance-pickers/auto-clear-picker.mjs +19 -13
  436. package/src/tui/app/message-selector.mjs +15 -2
  437. package/src/tui/app/model-options.mjs +16 -2
  438. package/src/tui/app/model-picker.mjs +2 -1
  439. package/src/tui/app/route-pickers.mjs +3 -1
  440. package/src/tui/app/settings-picker/settings-rows.mjs +2 -2
  441. package/src/tui/app/slash-commands.mjs +1 -1
  442. package/src/tui/app/usage-context-panels/context-model.mjs +8 -2
  443. package/src/tui/dist/index.mjs +617 -369
  444. package/src/tui/paste-attachments.mjs +0 -1
  445. package/src/tui/paste-text-policy.mjs +4 -5
  446. package/src/tui/prompt-history-store.mjs +16 -21
  447. package/src/tui/session/agent-job-feed/execution-delivery.mjs +4 -1
  448. package/src/tui/session/context-state.mjs +4 -0
  449. package/src/tui/session/notice-surface.mjs +3 -0
  450. package/src/tui/session/session-api/intake/abort.mjs +52 -18
  451. package/src/tui/session/session-api/intake/rewind.mjs +39 -4
  452. package/src/tui/session/session-api/media.mjs +2 -1
  453. package/src/tui/session/session-api/routes-agent-definitions.mjs +76 -0
  454. package/src/tui/session/session-api/routes-write.mjs +203 -0
  455. package/src/tui/session/session-api/routes.mjs +23 -247
  456. package/src/tui/session/session-api/settings.mjs +14 -1
  457. package/src/tui/session/session-flow/drain.mjs +28 -3
  458. package/src/tui/session/session-flow/failed-turn-rewind.mjs +4 -2
  459. package/src/tui/session/session-flow/queue/take-entries.mjs +8 -0
  460. package/src/tui/session/session-flow/steering.mjs +8 -3
  461. package/src/tui/session/tool-approval.mjs +142 -14
  462. package/src/tui/session/transcript-intake.mjs +2 -0
  463. package/src/tui/session/turn.mjs +23 -14
  464. package/src/tui/transcript-tool-failures.mjs +8 -2
  465. package/src/vendor/statusline/src/gateway/route-meta.mjs +7 -8
  466. package/src/vendor/statusline/src/gateway/session-routes.mjs +6 -7
  467. package/src/rules/agent-mode/ROUTINE.md +0 -6
  468. package/src/session-runtime/room-member-gate.mjs +0 -48
@@ -15,9 +15,11 @@
15
15
  * (runtime/shared/worktrees/manager.mjs), loaded on first use.
16
16
  */
17
17
  import { randomUUID } from 'node:crypto';
18
+ import { setTimeout as delay } from 'node:timers/promises';
18
19
  import { isDeepStrictEqual } from 'node:util';
19
20
  import {
20
21
  WORKTREE_CARRY_CHOICES,
22
+ WORKTREE_SNAPSHOT_KIND,
21
23
  branchMenuEntries,
22
24
  isWorktreeBound,
23
25
  isWorktreeError,
@@ -170,6 +172,27 @@ function assertRevision(revision, expectedRevision) {
170
172
  }
171
173
  }
172
174
 
175
+ /**
176
+ * The workspace methods every external caller (the runtime facade: desktop
177
+ * IPC, the daemon's `session.configure` lane, remote views) reaches: exactly
178
+ * the public input, so internal options (`ownTurn`, the calling-turn
179
+ * allowance of the Lead's own tool call) and the git tool's snapshot listing
180
+ * stay with in-process callers of the workspace API itself.
181
+ */
182
+ export function publicWorkspaceMethods(api) {
183
+ return {
184
+ selectBranch: (input) => api.selectBranch(input),
185
+ requestWorktree: (input) => api.requestWorktree(input),
186
+ mergeWorkspace: (input) => api.mergeWorkspace(input),
187
+ removeWorktree: (input) => api.removeWorktree(input),
188
+ listWorktrees: () => api.listWorktrees(),
189
+ pruneWorktrees: () => api.pruneWorktrees(),
190
+ };
191
+ }
192
+
193
+ // A settled turn's snapshot cleanup waits at most 2^5 times its first delay between attempts.
194
+ const SNAPSHOT_BACKOFF_STEPS = 5;
195
+
173
196
  /** The in-flight marker state of each transition kind. */
174
197
  const TRANSITION_STATE = { worktree: 'provisioning', branch: 'switching', 'remove-worktree': 'removing' };
175
198
 
@@ -220,12 +243,18 @@ export function createWorkspaceTransitions({
220
243
  newTxId = randomUUID,
221
244
  planWorktreeSetup = defaultPlanWorktreeSetup,
222
245
  runWorktreeSetup = defaultRunWorktreeSetup,
246
+ snapshotRetryMs = 2_000,
247
+ snapshotRetryLimit = 20,
223
248
  }) {
224
249
  const worktreeSettings = () => normalizeWorktreeSettings(getConfig?.()?.worktree);
225
250
  let pendingIntent = creationIntent(initialIntent, worktreeSettings());
226
- // The new worktree whose dependencies have not been prepared yet, bound to
227
- // that worktree (worktreeId + cwd): it runs only while the session is there.
228
- let pendingSetup = null;
251
+ // The creation txIds of the read-only snapshots the current admitted turn
252
+ // made. Each turn owns its own set, so its cleanup never touches a later
253
+ // turn's snapshots (a txId, unlike a freed slug, is never reused).
254
+ let turnSnapshots = null;
255
+ // The running cleanups of settled turns' snapshots (controller → session id).
256
+ const snapshotCleanups = new Map();
257
+ let disposed = false;
229
258
  const setupListeners = new Set();
230
259
  let managerPromise = null;
231
260
  const backend = () => {
@@ -480,49 +509,26 @@ export function createWorkspaceTransitions({
480
509
  try {
481
510
  await assertLeadIdle(session, { ownTurn: options.ownTurn === true });
482
511
  const run = { signal: options.signal, txId, journal };
512
+ // A new checkout gets its dependencies prepared at the start of the
513
+ // first turn a session runs there (runPendingSetup, from its registry entry).
483
514
  if (target.kind === 'worktree') {
484
- const { created, ...entered } = await steps.enterWorktree(session, previous, {
515
+ return await steps.enterWorktree(session, previous, {
485
516
  ...run,
486
517
  name: target.name,
487
518
  nameHint: options.nameHint,
488
519
  confirmLarge: options.confirmLarge,
489
520
  changes: options.changes,
490
521
  });
491
- // A new checkout gets its dependencies prepared at the start of the
492
- // session's next turn there (prepareTurnWorkspace).
493
- if (created) {
494
- pendingSetup = {
495
- ...created,
496
- sessionId: session.id,
497
- worktreeId: entered.workspace.worktreeId,
498
- cwd: entered.workspace.cwd,
499
- mainDir: previous.projectPath,
500
- };
501
- }
502
- return entered;
503
522
  }
504
523
  if (target.kind === 'branch') {
505
- const { created, ...entered } = await steps.enterProjectBranch(session, previous, {
524
+ return await steps.enterProjectBranch(session, previous, {
506
525
  ...run,
507
526
  branch: target.branch,
508
527
  create: target.create,
509
528
  confirmLarge: options.confirmLarge,
510
529
  });
511
- if (created && entered.workspace?.worktreeId) {
512
- pendingSetup = {
513
- ...created,
514
- sessionId: session.id,
515
- worktreeId: entered.workspace.worktreeId,
516
- cwd: entered.workspace.cwd,
517
- mainDir: previous.projectPath,
518
- };
519
- }
520
- return entered;
521
530
  }
522
- const removed = await steps.removeRetainedWorktree(session, previous, { ...run, force: target.force });
523
- // The session's retained worktree is gone: its pending setup goes with it.
524
- if (pendingSetup?.sessionId === session.id) pendingSetup = null;
525
- return removed;
531
+ return await steps.removeRetainedWorktree(session, previous, { ...run, force: target.force });
526
532
  } catch (error) {
527
533
  // Whatever already moved (cwd, record, prompts, worker roots, lease)
528
534
  // goes back exactly; the previous workspace stays authoritative.
@@ -534,16 +540,26 @@ export function createWorkspaceTransitions({
534
540
  }
535
541
  }
536
542
 
537
- /** @type {import('../../runtime/shared/worktrees/contract.mjs').ApplyWorkspaceTransition} */
538
- async function applyWorkspaceTransition(sessionRef, target, options = {}) {
543
+ /**
544
+ * The one entry of every transition. `ownTurn` (internal: the Lead's own
545
+ * tool call) tolerates the calling turn; `txId` keys the creation.
546
+ */
547
+ async function runTransition(sessionRef, target, options = {}, { ownTurn = false, txId = '' } = {}) {
539
548
  const session = hostedSession(sessionRef);
540
549
  return transition(session, normalizeTarget(target), {
541
550
  expectedRevision: validRevision(options.expectedRevision),
542
551
  confirmLarge: options.confirmLarge === true,
543
552
  signal: options.signal,
553
+ ownTurn: ownTurn === true,
554
+ txId,
544
555
  });
545
556
  }
546
557
 
558
+ /** @type {import('../../runtime/shared/worktrees/contract.mjs').ApplyWorkspaceTransition} */
559
+ async function applyWorkspaceTransition(sessionRef, target, options = {}) {
560
+ return runTransition(sessionRef, target, options);
561
+ }
562
+
547
563
  /** The pending worktree intent that applies to `session`'s next turn, if any. */
548
564
  function intentFor(session) {
549
565
  const intent = pendingIntent;
@@ -628,29 +644,35 @@ export function createWorkspaceTransitions({
628
644
  * output tail) and what was skipped. A failure or timeout is in the note
629
645
  * and the turn goes on; a cancel aborts the turn like any other stop. The
630
646
  * worktree stays either way. The setup belongs to the worktree it was made
631
- * for: while the session works elsewhere (it returned to a project-folder
632
- * branch before its first turn there) it is deferred, never run in another
633
- * folder, and runs on the first turn after the session re-enters it.
647
+ * for, whichever path made it (a worktree task, a branch selection, `git
648
+ * worktree add`): its registry entry keeps it pending (manager
649
+ * claimWorktreeSetup) until the first turn a session runs there, never in
650
+ * another folder.
634
651
  */
635
652
  async function runPendingSetup(session, signal) {
636
- const pending = pendingSetup;
637
- if (!pending || pending.sessionId !== session.id) return;
638
653
  const here = readSessionWorkspace(session);
639
- if (!isWorktreeBound(here) || here.worktreeId !== pending.worktreeId || !samePath(here.cwd, pending.cwd)) return;
640
- pendingSetup = null;
654
+ if (!isWorktreeBound(here)) return;
655
+ const claimed = await (await backend()).claimWorktreeSetup(here.worktreeId);
656
+ if (!claimed) return;
641
657
  const id = newTxId();
642
- const report = { root: pending.root, prepared: pending.prepared, dependencyNotes: [], installs: null, notes: [] };
658
+ const report = {
659
+ root: claimed.prepared?.root || here.projectPath,
660
+ prepared: claimed.prepared,
661
+ dependencyNotes: [],
662
+ installs: null,
663
+ notes: [],
664
+ };
643
665
  const emit = (event) => {
644
666
  for (const listener of setupListeners) {
645
667
  try {
646
- listener({ ...event, id, sessionId: session.id, cwd: pending.cwd });
668
+ listener({ ...event, id, sessionId: session.id, cwd: here.cwd });
647
669
  } catch {
648
670
  /* a viewer's failure never stops the setup */
649
671
  }
650
672
  }
651
673
  };
652
674
  try {
653
- const plan = await planWorktreeSetup({ worktreeDir: pending.cwd, mainDir: pending.mainDir, signal });
675
+ const plan = await planWorktreeSetup({ worktreeDir: here.cwd, mainDir: here.projectPath, signal });
654
676
  report.dependencyNotes = plan.notes;
655
677
  if (plan.commands.length) {
656
678
  report.installs = await runWorktreeSetup({ commands: plan.commands, signal, onProgress: emit });
@@ -715,30 +737,104 @@ export function createWorkspaceTransitions({
715
737
  return { liveSessionIds, listSessionIds: listStoredSessionIds, manager: await backend() };
716
738
  }
717
739
 
718
- /** Every managed worktree with who retains it (the manual list). */
719
- async function listWorktrees() {
720
- return maintenance.listWorktreesWithRetention(await maintenanceScope());
740
+ /**
741
+ * Every managed worktree with who retains it (the manual list). Read-only
742
+ * snapshots (no branch) only with `includeSnapshots` (the git tool); the
743
+ * desktop list (DesktopManagedWorktreeRow, a branch per row) never gets them.
744
+ */
745
+ async function listWorktrees({ includeSnapshots = false } = {}) {
746
+ const rows = await maintenance.listWorktreesWithRetention(await maintenanceScope());
747
+ return includeSnapshots ? rows : rows.filter((row) => row.kind !== WORKTREE_SNAPSHOT_KIND);
721
748
  }
722
749
 
723
750
  /** The manual prune: clean, merged worktrees no stored or live session retains. */
724
- async function pruneWorktrees() {
725
- return maintenance.pruneWorktrees(await maintenanceScope());
751
+ async function pruneWorktrees({ repoCommonDir = null } = {}) {
752
+ return maintenance.pruneWorktrees({ ...(await maintenanceScope()), repoCommonDir });
726
753
  }
727
754
 
728
755
  /** After `sessionId` was deleted: autoCleanup removes its clean, merged worktree. */
729
756
  async function cleanupDeletedSession(sessionId) {
757
+ // Its snapshots go with it below; no turn cleanup keeps retrying them.
758
+ cancelSnapshotCleanups(sessionId);
730
759
  return maintenance.cleanupDeletedSessionWorktree(sessionId, {
731
760
  manager: await backend(),
732
761
  autoCleanup: worktreeSettings().autoCleanup,
733
762
  });
734
763
  }
735
764
 
736
- /** Turn admission (see admission-gate admitTurn); resolves the release. */
737
- function admitTurnWorkspace({ signal } = {}) {
738
- return admitTurn({ sessionId: getSession()?.id || '', cwd: () => getCurrentCwd() || '', signal });
765
+ /**
766
+ * Turn admission (see admission-gate admitTurn); resolves the release. The
767
+ * release also starts the removal of the read-only snapshots this turn
768
+ * made — only those (removeTurnSnapshots).
769
+ */
770
+ async function admitTurnWorkspace({ signal } = {}) {
771
+ const release = await admitTurn({ sessionId: getSession()?.id || '', cwd: () => getCurrentCwd() || '', signal });
772
+ const made = new Set();
773
+ turnSnapshots = made;
774
+ return () => {
775
+ release();
776
+ if (turnSnapshots === made) turnSnapshots = null;
777
+ const sessionId = getSession()?.id;
778
+ if (made.size && sessionId) void removeTurnSnapshots(sessionId, [...made]);
779
+ };
780
+ }
781
+
782
+ /**
783
+ * Remove a settled turn's snapshots (by creation `txIds`) once nothing runs
784
+ * in them: a worker turn or background shell holding a workspace lease
785
+ * inside one keeps it (manager removeSessionSnapshots reports it `in-use`).
786
+ * Those are retried with a doubling delay (`snapshotRetryMs`, at most
787
+ * 2^SNAPSHOT_BACKOFF_STEPS times it) for at most `snapshotRetryLimit`
788
+ * attempts; a snapshot removal that fails otherwise is not retried. What is
789
+ * left then — or when the runtime is disposed, the session closes, is
790
+ * deleted or is no longer the one hosted here — is the prune's and the
791
+ * startup reconcile's. Admission is not held meanwhile: the removal only
792
+ * ever names this turn's snapshots.
793
+ */
794
+ async function removeTurnSnapshots(sessionId, txIds) {
795
+ if (disposed) return;
796
+ const controller = new AbortController();
797
+ snapshotCleanups.set(controller, sessionId);
798
+ try {
799
+ let pending = txIds;
800
+ for (let attempt = 0; pending.length && attempt < snapshotRetryLimit; attempt++) {
801
+ if (attempt > 0) {
802
+ const wait = snapshotRetryMs * 2 ** Math.min(attempt - 1, SNAPSHOT_BACKOFF_STEPS);
803
+ await delay(wait, undefined, { ref: false, signal: controller.signal });
804
+ }
805
+ const session = getSession();
806
+ if (session?.id !== sessionId || session.closed === true) return;
807
+ const report = await (await backend()).removeSessionSnapshots(sessionId, { txIds: pending });
808
+ pending = report.kept.filter((row) => row.reason === 'in-use').map((row) => row.txId);
809
+ }
810
+ } catch {
811
+ // Cancelled, or the backend failed: the rest is the maintenance's.
812
+ } finally {
813
+ snapshotCleanups.delete(controller);
814
+ }
739
815
  }
740
816
 
741
- async function selectBranch({ branch, create = false, expectedRevision, confirmLarge = false } = {}) {
817
+ /** Stop the snapshot cleanups of `sessionId` (every session's when null). */
818
+ function cancelSnapshotCleanups(sessionId = null) {
819
+ for (const [controller, owner] of snapshotCleanups) {
820
+ if (sessionId === null || owner === sessionId) controller.abort();
821
+ }
822
+ }
823
+
824
+ /** Runtime teardown: no snapshot cleanup runs on (prune / startup reconcile finish them). */
825
+ function dispose() {
826
+ disposed = true;
827
+ cancelSnapshotCleanups();
828
+ }
829
+
830
+ /**
831
+ * Run in `branch`'s checkout (see enterProjectBranch). `ownTurn`
832
+ * (internal: the Lead's own tool call) tolerates the calling turn.
833
+ */
834
+ async function selectBranch(
835
+ { branch, create = false, expectedRevision, confirmLarge = false, txId = '' } = {},
836
+ { ownTurn = false } = {}
837
+ ) {
742
838
  const session = await ensureSession();
743
839
  const current = currentWorkspace(session);
744
840
  if (current.branch === branch && current.worktreeId) {
@@ -747,14 +843,127 @@ export function createWorkspaceTransitions({
747
843
  return { revision: current.revision, workspace: current };
748
844
  }
749
845
  const target = { kind: 'branch', branch, create: create === true };
750
- const result = await applyWorkspaceTransition({ sessionId: session.id }, target, {
751
- expectedRevision,
752
- confirmLarge,
753
- });
846
+ const result = await runTransition(
847
+ { sessionId: session.id },
848
+ target,
849
+ { expectedRevision, confirmLarge },
850
+ { ownTurn, txId: String(txId || '').trim() }
851
+ );
754
852
  pendingIntent = null;
755
853
  return result;
756
854
  }
757
855
 
856
+ /** WORKTREE_PROJECT_MISMATCH unless `repoCommonDir` (when given) is the session Project's repository. */
857
+ async function sessionProject(session, repoCommonDir) {
858
+ const workspace = currentWorkspace(session);
859
+ const project = await inspectProject(workspace.projectPath);
860
+ if (repoCommonDir && !samePath(repoCommonDir, project.repoCommonDir)) {
861
+ throw worktreeError(
862
+ 'WORKTREE_PROJECT_MISMATCH',
863
+ "Managed worktrees belong to this session's Project repository; run the command there.",
864
+ { projectPath: workspace.projectPath, repoCommonDir }
865
+ );
866
+ }
867
+ return { workspace, project };
868
+ }
869
+
870
+ /**
871
+ * A managed worktree for `branch` without moving the session (`git
872
+ * worktree add`): retained by this session like a branch it selected, so
873
+ * entering it later (selectBranch) reuses it. A branch already checked out
874
+ * elsewhere is WORKTREE_BRANCH_OCCUPIED, except in this session's own
875
+ * managed worktree, which is returned as it is. `startPoint` (with
876
+ * `create`): the commit the new branch starts at.
877
+ */
878
+ async function addBranchWorktree({
879
+ branch,
880
+ create = false,
881
+ startPoint = null,
882
+ confirmLarge = false,
883
+ txId = '',
884
+ repoCommonDir = null,
885
+ } = {}) {
886
+ const session = hostedSession(null);
887
+ const { workspace } = await sessionProject(session, repoCommonDir);
888
+ const backendApi = await backend();
889
+ const { occupancy } = await backendApi.listBranchOccupancy({
890
+ projectPath: workspace.projectPath,
891
+ sessionId: session.id,
892
+ });
893
+ const held = occupancy.find((row) => row.branch === branch && row.kind !== 'free');
894
+ if (held && !(held.kind === 'session' && held.sessionId === session.id)) {
895
+ throw worktreeError('WORKTREE_BRANCH_OCCUPIED', `Branch ${branch} is checked out in ${held.path}.`, {
896
+ branch,
897
+ path: held.path,
898
+ kind: held.kind,
899
+ ...(held.sessionId ? { sessionId: held.sessionId } : {}),
900
+ });
901
+ }
902
+ const resolved = await backendApi.resolveBranchWorkspace({
903
+ projectPath: workspace.projectPath,
904
+ branch,
905
+ create: create === true,
906
+ startPoint,
907
+ sessionId: session.id,
908
+ runtimeToken,
909
+ txId: String(txId || '').trim() || newTxId(),
910
+ confirmLarge: confirmLarge === true,
911
+ });
912
+ return {
913
+ path: resolved.path,
914
+ branch,
915
+ worktreeId: resolved.entry?.id ?? null,
916
+ created: resolved.created === true,
917
+ baseCommit: resolved.entry?.baseCommit ?? null,
918
+ mergeTarget: resolved.entry?.mergeTarget ?? null,
919
+ };
920
+ }
921
+
922
+ /**
923
+ * A read-only snapshot of `commit` for this session's turn (`git worktree
924
+ * add --detach`): a detached managed checkout no session works in,
925
+ * removed when the turn settles (and nothing runs in it any more). The
926
+ * session does not move. Only inside an admitted turn: a snapshot always
927
+ * belongs to the turn that made it.
928
+ */
929
+ async function createSnapshot({ commit = 'HEAD', confirmLarge = false, txId = '', repoCommonDir = null } = {}) {
930
+ const session = hostedSession(null);
931
+ const made = turnSnapshots;
932
+ if (!made) throw new Error('A read-only snapshot belongs to a turn; none is running in this session.');
933
+ const { workspace } = await sessionProject(session, repoCommonDir);
934
+ const tx = String(txId || '').trim() || newTxId();
935
+ const entry = await (await backend()).createSnapshotWorktree({
936
+ projectPath: workspace.projectPath,
937
+ sessionId: session.id,
938
+ runtimeToken,
939
+ commit,
940
+ txId: tx,
941
+ confirmLarge: confirmLarge === true,
942
+ });
943
+ made.add(tx);
944
+ return entry;
945
+ }
946
+
947
+ /**
948
+ * Remove this session's snapshot at `path` now; a dirty one only with
949
+ * `force`, and none while a worker or background shell runs in it.
950
+ */
951
+ async function removeSnapshot({ path, force = false } = {}) {
952
+ const session = hostedSession(null);
953
+ const backendApi = await backend();
954
+ const entry = await backendApi.getWorktreeByPath(path);
955
+ if (entry?.kind !== WORKTREE_SNAPSHOT_KIND) throw worktreeError('WORKTREE_MISSING', undefined, { path });
956
+ if (entry.holderSessionId !== session.id) {
957
+ throw worktreeError(
958
+ 'WORKTREE_BUSY_JOBS',
959
+ "This snapshot belongs to another session's turn; it is removed when that turn ends, or by a prune.",
960
+ { reason: 'snapshot-held', sessionId: entry.holderSessionId, entryId: entry.id }
961
+ );
962
+ }
963
+ const removed = await backendApi.removeSnapshotWorktree({ entryId: entry.id, force: force === true });
964
+ return { ...removed, entryId: entry.id, path: entry.path };
965
+ }
966
+
758
967
  /**
759
968
  * "워크트리": re-enter the retained worktree now, or arm the lazy creation
760
969
  * for the next message (confirmLarge acknowledges WORKTREE_LARGE_REPO_CONFIRM).
@@ -772,16 +981,13 @@ export function createWorkspaceTransitions({
772
981
  * `leave` starts the worktree at HEAD and leaves them; `carry` moves them
773
982
  * into it (the result's `changes` reports which). Re-entering a retained
774
983
  * worktree never touches them.
984
+ *
985
+ * `ownTurn` (internal: the Lead's own tool call) tolerates the calling turn.
775
986
  */
776
- async function requestWorktree({
777
- name = null,
778
- confirmLarge = false,
779
- provision = false,
780
- carryChanges,
781
- prompt = '',
782
- txId = '',
783
- expectedRevision,
784
- } = {}) {
987
+ async function requestWorktree(
988
+ { name = null, confirmLarge = false, provision = false, carryChanges, prompt = '', txId = '', expectedRevision } = {},
989
+ { ownTurn = false } = {}
990
+ ) {
785
991
  const nameHint = validName(name);
786
992
  const expected = validRevision(expectedRevision);
787
993
  if (carryChanges !== undefined && carryChanges !== null && !WORKTREE_CARRY_CHOICES.includes(carryChanges)) {
@@ -794,10 +1000,11 @@ export function createWorkspaceTransitions({
794
1000
  if (current) {
795
1001
  if (isWorktreeBound(current)) return { revision: current.revision, workspace: current, pending: false };
796
1002
  if (await (await backend()).getSessionWorktree(session.id)) {
797
- const entered = await applyWorkspaceTransition(
1003
+ const entered = await runTransition(
798
1004
  { sessionId: session.id },
799
1005
  { kind: 'worktree' },
800
- { expectedRevision: expected }
1006
+ { expectedRevision: expected },
1007
+ { ownTurn }
801
1008
  );
802
1009
  return { ...entered, pending: false };
803
1010
  }
@@ -817,6 +1024,7 @@ export function createWorkspaceTransitions({
817
1024
  ...(await provisionIntent(session, intent, txId, prompt, {
818
1025
  expectedRevision: expected,
819
1026
  changes: carryChanges ?? 'ask',
1027
+ ownTurn: ownTurn === true,
820
1028
  })),
821
1029
  pending: false,
822
1030
  };
@@ -997,10 +1205,14 @@ export function createWorkspaceTransitions({
997
1205
  requestWorktree,
998
1206
  mergeWorkspace,
999
1207
  removeWorktree,
1208
+ addBranchWorktree,
1209
+ createSnapshot,
1210
+ removeSnapshot,
1000
1211
  switchProject,
1001
1212
  listWorktrees,
1002
1213
  pruneWorktrees,
1003
1214
  cleanupDeletedSession,
1004
1215
  onWorktreeSetup,
1216
+ dispose,
1005
1217
  };
1006
1218
  }
@@ -4,8 +4,9 @@
4
4
  * resume (missing worktree → recovery), and carrying the Project's memory
5
5
  * identity into a new worktree.
6
6
  */
7
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
8
- import { dirname, join } from 'node:path';
7
+ import { existsSync, readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { holdsNoData, writeFileAtomicSync } from '../../runtime/shared/atomic-file.mjs';
9
10
  import { isWorktreeBound, nextRevision, normalizeSessionWorkspace } from '../../runtime/shared/worktrees/contract.mjs';
10
11
  import { isWithin, samePath } from '../../runtime/shared/worktrees/paths.mjs';
11
12
  import { resolveProjectId } from '../../runtime/memory/lib/project-id-resolver.mjs';
@@ -103,10 +104,10 @@ export function restoreWorkspaceAfterResume(session) {
103
104
  */
104
105
  export function copyProjectIdentity(projectPath, worktreePath) {
105
106
  const target = join(worktreePath, '.mixdog', 'project.id');
106
- if (existsSync(target)) return false;
107
+ // An id file with no data (empty, or NUL-filled by a crash) is missing.
108
+ if (existsSync(target) && !holdsNoData(readFileSync(target))) return false;
107
109
  const id = resolveProjectId(projectPath);
108
- if (!id) return false;
109
- mkdirSync(dirname(target), { recursive: true });
110
- writeFileSync(target, `${id}\n`, 'utf8');
110
+ if (!id || holdsNoData(id)) return false;
111
+ writeFileAtomicSync(target, `${id}\n`, { mode: 0o644 });
111
112
  return true;
112
113
  }