mixdog 1.1.2 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (424) hide show
  1. package/README.md +8 -5
  2. package/package.json +2 -1
  3. package/scripts/lib/run-node-tests.mjs +9 -0
  4. package/scripts/prune-desktop-runtime.mjs +3 -0
  5. package/scripts/test-direct.mjs +4 -2
  6. package/src/defaults/shipped-history.json +10 -0
  7. package/src/defaults/skills/pptx/SKILL.md +6 -6
  8. package/src/defaults/skills/pptx/references/review.md +6 -4
  9. package/src/defaults/skills/pptx/scripts/source-extract.mjs +1 -1
  10. package/src/defaults/skills/setup/references/actions.md +18 -5
  11. package/src/defaults/skills/visualize/SKILL.md +160 -0
  12. package/src/defaults/skills/visualize/references/craft.md +88 -0
  13. package/src/headless-command.mjs +56 -3
  14. package/src/headless-exec.mjs +17 -1
  15. package/src/help.mjs +2 -0
  16. package/src/rules/shared/60-verification.md +3 -1
  17. package/src/rules/shared/70-delivery.md +4 -2
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +1 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +2 -0
  21. package/src/runtime/agent/orchestrator/cache-mtime.mjs +26 -5
  22. package/src/runtime/agent/orchestrator/config-presets.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +17 -11
  24. package/src/runtime/agent/orchestrator/config-storage.mjs +1 -0
  25. package/src/runtime/agent/orchestrator/config.mjs +19 -5
  26. package/src/runtime/agent/orchestrator/context/collect.mjs +2 -1
  27. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +11 -5
  28. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +74 -21
  29. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +133 -15
  30. package/src/runtime/agent/orchestrator/mcp/client-handshake.mjs +55 -8
  31. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +86 -8
  32. package/src/runtime/agent/orchestrator/mcp/client.mjs +474 -164
  33. package/src/runtime/agent/orchestrator/mcp/lazy-output-validator.mjs +25 -0
  34. package/src/runtime/agent/orchestrator/mcp/plugin-placeholders.mjs +54 -0
  35. package/src/runtime/agent/orchestrator/mcp/reconnect-singleflight.mjs +4 -0
  36. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +108 -17
  37. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  38. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +3 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +15 -1
  40. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +32 -12
  41. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +2 -0
  42. package/src/runtime/agent/orchestrator/providers/auto-effort.mjs +9 -0
  43. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +337 -0
  44. package/src/runtime/agent/orchestrator/providers/command-code-oauth-tokens.mjs +159 -0
  45. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +444 -0
  46. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +2 -0
  47. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  48. package/src/runtime/agent/orchestrator/providers/cursor-wire-normalization.mjs +2 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -4
  50. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +21 -6
  51. package/src/runtime/agent/orchestrator/providers/effort-rejection.mjs +244 -0
  52. package/src/runtime/agent/orchestrator/providers/gemini.mjs +41 -28
  53. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  54. package/src/runtime/agent/orchestrator/providers/grok-oauth-tokens.mjs +3 -0
  55. package/src/runtime/agent/orchestrator/providers/lib/gemini-model-catalog.mjs +7 -1
  56. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +8 -40
  57. package/src/runtime/agent/orchestrator/providers/mimo-oauth-tokens.mjs +2 -0
  58. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +51 -12
  59. package/src/runtime/agent/orchestrator/providers/model-cache.mjs +37 -2
  60. package/src/runtime/agent/orchestrator/providers/model-proxy-bridge.mjs +1080 -0
  61. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +82 -0
  62. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +556 -0
  63. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +251 -0
  64. package/src/runtime/agent/orchestrator/providers/muse-code-oauth-tokens.mjs +2 -0
  65. package/src/runtime/agent/orchestrator/providers/muse-code-oauth.mjs +69 -102
  66. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +15 -0
  67. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +6 -5
  68. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -1
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +6 -2
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +11 -0
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +2 -1
  72. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +15 -4
  73. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs +1 -1
  74. package/src/runtime/agent/orchestrator/providers/openai-oauth-catalog.mjs +5 -2
  75. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +3 -0
  76. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +4 -4
  77. package/src/runtime/agent/orchestrator/providers/registry.mjs +47 -7
  78. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +10 -0
  79. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +36 -3
  80. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -11
  81. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +15 -55
  82. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +15 -2
  83. package/src/runtime/agent/orchestrator/runtime-core/model-capabilities.mjs +16 -1
  84. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +278 -84
  85. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +4 -1
  86. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +21 -0
  87. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +54 -3
  88. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -3
  89. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -0
  90. package/src/runtime/agent/orchestrator/session/cache/prefetch-cache.mjs +9 -5
  91. package/src/runtime/agent/orchestrator/session/cache/read-cache.mjs +18 -8
  92. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +284 -0
  93. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +268 -0
  94. package/src/runtime/agent/orchestrator/session/catalog-db/sqlite-gate.mjs +27 -0
  95. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +628 -0
  96. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +10 -3
  97. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +24 -13
  98. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +13 -5
  99. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +362 -0
  100. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +253 -0
  101. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +184 -0
  102. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +136 -0
  103. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +128 -0
  104. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +127 -0
  105. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +101 -0
  106. package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +83 -5
  107. package/src/runtime/agent/orchestrator/session/imported-message.mjs +11 -0
  108. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +2 -1
  109. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/continuation.mjs +1 -1
  111. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/diagnostic.mjs +1 -7
  112. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/refusal.mjs +1 -1
  113. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +3 -2
  114. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +5 -3
  115. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +7 -4
  116. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +21 -10
  117. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -0
  118. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -3
  120. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +15 -3
  121. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +22 -12
  122. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +9 -2
  123. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +9 -3
  124. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +5 -2
  125. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +37 -16
  126. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +4 -0
  127. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +17 -4
  128. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +90 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +22 -12
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +32 -3
  131. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +27 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -1
  133. package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +1 -0
  134. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/session/manager.mjs +0 -1
  136. package/src/runtime/agent/orchestrator/session/message-ids.mjs +123 -0
  137. package/src/runtime/agent/orchestrator/session/message-ids.test-support.mjs +29 -0
  138. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +33 -22
  139. package/src/runtime/agent/orchestrator/session/store/listing.mjs +6 -2
  140. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +3 -0
  141. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +6 -0
  142. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +10 -3
  143. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +11 -0
  144. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +817 -0
  145. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +36 -0
  146. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +23 -12
  147. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +158 -0
  148. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +202 -374
  149. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +20 -3
  150. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +3 -1
  151. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +48 -9
  153. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +161 -0
  154. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +17 -1
  155. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -0
  156. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +52 -2
  157. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +32 -22
  158. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +10 -3
  159. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -2
  160. package/src/runtime/agent/orchestrator/tools/builtin/path-diagnostics.mjs +6 -6
  161. package/src/runtime/agent/orchestrator/tools/builtin/read-cache-policy.mjs +8 -0
  162. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +203 -33
  163. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +32 -35
  164. package/src/runtime/agent/orchestrator/tools/builtin/read-single-guards.mjs +8 -4
  165. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +11 -12
  166. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +9 -6
  167. package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +83 -68
  168. package/src/runtime/agent/orchestrator/tools/builtin/read-snapshot-runtime.mjs +8 -3
  169. package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +20 -3
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +16 -7
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +2 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +137 -0
  173. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +11 -10
  174. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +21 -11
  175. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +12 -1
  176. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +13 -10
  177. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +17 -12
  179. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +56 -13
  180. package/src/runtime/agent/orchestrator/tools/code-graph/disk-cache.mjs +58 -0
  181. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +2 -2
  182. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/tools/code-graph/trusted-roots.mjs +25 -1
  184. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/codex-batch.mjs +4 -4
  185. package/src/runtime/agent/orchestrator/tools/patch/apply-patch/route.mjs +2 -2
  186. package/src/runtime/agent/orchestrator/tools/patch/orchestrator.mjs +1 -1
  187. package/src/runtime/agent/orchestrator/tools/patch/read-redirects.mjs +34 -30
  188. package/src/runtime/agent/orchestrator/tools/patch/sequence/units.mjs +7 -7
  189. package/src/runtime/agent/orchestrator/tools/patch/sequence.mjs +1 -1
  190. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +78 -5
  191. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +2 -0
  192. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +4 -0
  193. package/src/runtime/attachments/store.d.mts +3 -0
  194. package/src/runtime/attachments/store.mjs +38 -0
  195. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +1 -1
  196. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +16 -4
  197. package/src/runtime/channels/lib/worker-main.mjs +17 -14
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -2
  199. package/src/runtime/effort-judge/judge-client.mjs +56 -18
  200. package/src/runtime/memory/lib/memory.mjs +1 -1
  201. package/src/runtime/memory/lib/session-ingest.mjs +2 -2
  202. package/src/runtime/shared/atomic-file.mjs +33 -6
  203. package/src/runtime/shared/best-effort-stderr.mjs +8 -0
  204. package/src/runtime/shared/code-graph-state.mjs +12 -0
  205. package/src/runtime/shared/defaults-separation.mjs +2 -0
  206. package/src/runtime/shared/extension-scopes.mjs +5 -3
  207. package/src/runtime/shared/file-lock.mjs +37 -11
  208. package/src/runtime/shared/git-runner-worker.mjs +3 -2
  209. package/src/runtime/shared/git-runner.mjs +4 -3
  210. package/src/runtime/shared/llm/anthropic-effort.mjs +14 -0
  211. package/src/runtime/shared/llm/default-context-window.mjs +6 -0
  212. package/src/runtime/shared/llm/model-catalog-projection.mjs +5 -4
  213. package/src/runtime/shared/llm/model-catalog.mjs +240 -130
  214. package/src/runtime/shared/llm/provider-catalog-cache.mjs +36 -28
  215. package/src/runtime/shared/llm/provider-catalog-registry.mjs +162 -0
  216. package/src/runtime/shared/llm/provider-seed-rosters.mjs +130 -0
  217. package/src/runtime/shared/model-proxy-mode.d.mts +3 -0
  218. package/src/runtime/shared/model-proxy-mode.mjs +19 -0
  219. package/src/runtime/shared/plugin-manifest.mjs +18 -9
  220. package/src/runtime/shared/pristine-execution-contract.json +9 -2
  221. package/src/runtime/shared/pristine-execution.mjs +28 -4
  222. package/src/runtime/shared/provider-accounts.mjs +125 -21
  223. package/src/runtime/shared/provider-api-key.mjs +33 -1
  224. package/src/runtime/shared/schedule-model-ref.mjs +11 -2
  225. package/src/runtime/shared/session-usage-totals.mjs +19 -0
  226. package/src/runtime/shared/sharp-electron-linux.mjs +25 -0
  227. package/src/runtime/shared/shell-display.mjs +9 -3
  228. package/src/runtime/shared/singleton-owner.mjs +36 -6
  229. package/src/runtime/shared/skill-mentions.d.mts +10 -0
  230. package/src/runtime/shared/skill-mentions.mjs +133 -0
  231. package/src/runtime/shared/skill-script-runtime.mjs +264 -0
  232. package/src/runtime/shared/statusline/statusline-segments.mjs +68 -14
  233. package/src/runtime/shared/tool-primitives.mjs +35 -2
  234. package/src/runtime/shared/tool-surface.mjs +7 -0
  235. package/src/runtime/shared/tool-work-units.mjs +14 -0
  236. package/src/runtime/shared/transcript-metadata.mjs +17 -1
  237. package/src/runtime/shared/turn-changes.mjs +791 -0
  238. package/src/runtime/shared/turn-snapshot-store.mjs +346 -12
  239. package/src/runtime/shared/turn-snapshot.mjs +690 -81
  240. package/src/runtime/shared/turn-worktree-snapshot.mjs +185 -15
  241. package/src/runtime/shared/worker-requests.mjs +15 -2
  242. package/src/runtime/shared/worktrees/cache-cleanups.mjs +52 -0
  243. package/src/runtime/shared/worktrees/contract.mjs +551 -0
  244. package/src/runtime/shared/worktrees/disk.mjs +57 -0
  245. package/src/runtime/shared/worktrees/git.mjs +247 -0
  246. package/src/runtime/shared/worktrees/include.mjs +480 -0
  247. package/src/runtime/shared/worktrees/lock.mjs +46 -0
  248. package/src/runtime/shared/worktrees/maintenance.mjs +96 -0
  249. package/src/runtime/shared/worktrees/manager.mjs +1215 -0
  250. package/src/runtime/shared/worktrees/merge.mjs +182 -0
  251. package/src/runtime/shared/worktrees/owner.mjs +46 -0
  252. package/src/runtime/shared/worktrees/paths.mjs +62 -0
  253. package/src/runtime/shared/worktrees/registry.mjs +110 -0
  254. package/src/runtime/shared/worktrees/settings.mjs +84 -0
  255. package/src/runtime/shared/worktrees/slug.mjs +62 -0
  256. package/src/runtime/shared/worktrees/test-support.mjs +96 -0
  257. package/src/runtime/visualize-bridge/client.mjs +181 -0
  258. package/src/runtime/visualize-bridge/tool-defs.mjs +97 -0
  259. package/src/session-runtime/automation-agents.mjs +8 -2
  260. package/src/session-runtime/boot/apis.mjs +90 -0
  261. package/src/session-runtime/boot/config.mjs +7 -2
  262. package/src/session-runtime/boot/facade.mjs +21 -0
  263. package/src/session-runtime/boot/infrastructure.mjs +30 -18
  264. package/src/session-runtime/boot/session.mjs +1 -0
  265. package/src/session-runtime/boot/tools.mjs +4 -0
  266. package/src/session-runtime/config-lifecycle.mjs +10 -1
  267. package/src/session-runtime/conversation-import/discovery.mjs +698 -0
  268. package/src/session-runtime/conversation-import/service.mjs +369 -0
  269. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +21 -6
  270. package/src/session-runtime/cwd-plugins/plugin-status.mjs +47 -42
  271. package/src/session-runtime/global-extensions.mjs +2 -2
  272. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +27 -2
  273. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +32 -1
  274. package/src/session-runtime/internal-tool-executor.mjs +9 -1
  275. package/src/session-runtime/lifecycle/session-catalog.mjs +8 -1
  276. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +5 -0
  277. package/src/session-runtime/lifecycle/session-switch/resume.mjs +21 -3
  278. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +16 -0
  279. package/src/session-runtime/mcp-glue/connect.mjs +68 -16
  280. package/src/session-runtime/mcp-glue/plugin-servers.mjs +499 -0
  281. package/src/session-runtime/mcp-glue/server-catalog.mjs +122 -33
  282. package/src/session-runtime/mcp-glue/server-input.mjs +29 -0
  283. package/src/session-runtime/mcp-glue.mjs +22 -2
  284. package/src/session-runtime/model-route/route-persist.mjs +3 -0
  285. package/src/session-runtime/model-route/set-route.mjs +15 -2
  286. package/src/session-runtime/new-session-config.mjs +3 -2
  287. package/src/session-runtime/notification-bus.mjs +18 -3
  288. package/src/session-runtime/provider-auth/accounts.mjs +33 -15
  289. package/src/session-runtime/provider-models/row-hydration.mjs +2 -0
  290. package/src/session-runtime/provider-models/web-search-catalog.mjs +5 -1
  291. package/src/session-runtime/provider-readiness.mjs +16 -10
  292. package/src/session-runtime/resource-extension-sync.mjs +45 -7
  293. package/src/session-runtime/resource-mcp-api.mjs +121 -84
  294. package/src/session-runtime/resource-plugins-api.mjs +29 -41
  295. package/src/session-runtime/resource-scope-decorate.mjs +6 -8
  296. package/src/session-runtime/resource-skills-api.mjs +23 -2
  297. package/src/session-runtime/route-resolution.mjs +10 -1
  298. package/src/session-runtime/route-state.mjs +16 -2
  299. package/src/session-runtime/runtime-core.mjs +5 -0
  300. package/src/session-runtime/runtime-facade.mjs +8 -0
  301. package/src/session-runtime/runtime-review-api.mjs +9 -0
  302. package/src/session-runtime/schedule-session-run.mjs +1 -1
  303. package/src/session-runtime/services/agent-tool/helpers.mjs +5 -0
  304. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +3 -0
  305. package/src/session-runtime/services/agent-tool/job-views/job-lookup.mjs +1 -0
  306. package/src/session-runtime/services/agent-tool/job-views/spawn-meta.mjs +1 -0
  307. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +15 -0
  308. package/src/session-runtime/services/agent-tool/send-flow.mjs +1 -0
  309. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +10 -2
  310. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -7
  311. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  312. package/src/session-runtime/services/agent-tool/tag-registry.mjs +2 -1
  313. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -0
  314. package/src/session-runtime/services/agent-tool/worker-rows.mjs +1 -0
  315. package/src/session-runtime/services/agent-tool.mjs +1 -0
  316. package/src/session-runtime/services/channel-session-router.mjs +4 -1
  317. package/src/session-runtime/services/daemon-candidate.mjs +3 -2
  318. package/src/session-runtime/services/daemon-crash-capture/spawn-capture.mjs +55 -0
  319. package/src/session-runtime/services/hook-bus/command-handler.mjs +4 -1
  320. package/src/session-runtime/services/hook-bus/handlers.mjs +7 -3
  321. package/src/session-runtime/services/provider-admin.mjs +64 -18
  322. package/src/session-runtime/services/usage-dashboard-model.mjs +3 -0
  323. package/src/session-runtime/session-create/session-options.mjs +10 -1
  324. package/src/session-runtime/session-turn-api.mjs +1 -0
  325. package/src/session-runtime/settings-api.mjs +4 -2
  326. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -2
  327. package/src/session-runtime/settings-worktree-api.mjs +17 -0
  328. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  329. package/src/session-runtime/setup-tool/extended-actions.mjs +1 -0
  330. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -1
  331. package/src/session-runtime/setup-tool/tool-defs.mjs +38 -4
  332. package/src/session-runtime/skills-api.mjs +7 -3
  333. package/src/session-runtime/turn/agent-ops.mjs +12 -1
  334. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +14 -3
  335. package/src/session-runtime/turn/turn-run/snapshot-tracker.mjs +20 -15
  336. package/src/session-runtime/turn/turn-run/turn-open.mjs +4 -5
  337. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +29 -1
  338. package/src/session-runtime/turn/turn-run/turn-settle.mjs +6 -1
  339. package/src/session-runtime/turn/turn-run.mjs +6 -7
  340. package/src/session-runtime/webhook-session-run.mjs +16 -4
  341. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -3
  342. package/src/session-runtime/workspace/admission-gate.mjs +197 -0
  343. package/src/session-runtime/workspace/project-branches.mjs +136 -0
  344. package/src/session-runtime/workspace/transition-steps.mjs +364 -0
  345. package/src/session-runtime/workspace/transitions.mjs +651 -0
  346. package/src/session-runtime/workspace/workspace-state.mjs +112 -0
  347. package/src/skill-script-entry.mjs +36 -0
  348. package/src/standalone/channel-transport/http-routes.mjs +17 -0
  349. package/src/standalone/channel-transport.mjs +13 -1
  350. package/src/standalone/daemon-desktop-runtime.mjs +1 -0
  351. package/src/standalone/daemon-owner.mjs +372 -0
  352. package/src/standalone/daemon-stored-session-views.mjs +15 -4
  353. package/src/standalone/daemon.mjs +179 -8
  354. package/src/standalone/desktop-service-registry.mjs +20 -0
  355. package/src/standalone/local-session-runtime.mjs +11 -0
  356. package/src/standalone/session-client/attachment-calls.mjs +3 -1
  357. package/src/standalone/session-client/daemon-unavailable.mjs +62 -0
  358. package/src/standalone/session-client/lease-heartbeat.mjs +47 -0
  359. package/src/standalone/session-client.mjs +104 -13
  360. package/src/standalone/session-protocol.mjs +21 -0
  361. package/src/standalone/session-proxy.mjs +3 -0
  362. package/src/standalone/session-service/agent-tree/agent-child.mjs +4 -0
  363. package/src/standalone/session-service/agent-tree/agent-rehydrate.mjs +1 -0
  364. package/src/standalone/session-service/entries/lifecycle.mjs +6 -0
  365. package/src/standalone/session-service/projection/revisions.mjs +2 -0
  366. package/src/standalone/session-service/projection/transcript-window.mjs +149 -19
  367. package/src/standalone/session-service/projection/workspace.mjs +14 -0
  368. package/src/standalone/session-service/session-calls/session-action.mjs +17 -4
  369. package/src/standalone/session-transport/call-route.mjs +13 -0
  370. package/src/standalone/session-transport/client-registry/client-grace.mjs +45 -3
  371. package/src/standalone/session-transport/client-registry/registration-replays.mjs +1 -0
  372. package/src/standalone/session-transport/client-registry.mjs +20 -3
  373. package/src/standalone/session-transport/routes.mjs +79 -2
  374. package/src/standalone/session-transport/stack.mjs +2 -0
  375. package/src/standalone/session-transport.mjs +24 -1
  376. package/src/tui/App.jsx +9 -1
  377. package/src/tui/app/app-view/floating-panel.jsx +9 -1
  378. package/src/tui/app/app-view.jsx +6 -0
  379. package/src/tui/app/extension-pickers/plugins-pickers.mjs +35 -16
  380. package/src/tui/app/message-selector.mjs +2 -1
  381. package/src/tui/app/model-options.mjs +37 -5
  382. package/src/tui/app/model-picker/provider-models-list.mjs +2 -2
  383. package/src/tui/app/model-picker.mjs +4 -2
  384. package/src/tui/app/skill-mention-palette.mjs +88 -0
  385. package/src/tui/app/slash-palette.mjs +23 -7
  386. package/src/tui/app/transcript-row-estimate.mjs +11 -3
  387. package/src/tui/app/use-prompt-draft-flow.mjs +7 -2
  388. package/src/tui/app/use-prompt-state.mjs +5 -0
  389. package/src/tui/components/Message.jsx +22 -1
  390. package/src/tui/components/PromptInput.jsx +21 -3
  391. package/src/tui/components/SlashCommandPalette.jsx +12 -3
  392. package/src/tui/components/TranscriptItem.jsx +9 -1
  393. package/src/tui/components/prompt-input/key-handler.mjs +21 -8
  394. package/src/tui/components/user-message-skills.mjs +38 -0
  395. package/src/tui/dist/index.mjs +839 -207
  396. package/src/tui/index.jsx +10 -2
  397. package/src/tui/session/compaction-view.mjs +147 -0
  398. package/src/tui/session/context-state.mjs +6 -0
  399. package/src/tui/session/initial-state.mjs +3 -0
  400. package/src/tui/session/queue-helpers.mjs +7 -2
  401. package/src/tui/session/quota-resume.mjs +2 -1
  402. package/src/tui/session/session-api/commands.mjs +10 -2
  403. package/src/tui/session/session-api/controls.mjs +4 -0
  404. package/src/tui/session/session-api/extensions.mjs +4 -2
  405. package/src/tui/session/session-api/intake/rewind.mjs +2 -1
  406. package/src/tui/session/session-api/integrations.mjs +17 -0
  407. package/src/tui/session/session-api/lifecycle/dispose.mjs +6 -1
  408. package/src/tui/session/session-api/lifecycle/inheritance.mjs +2 -0
  409. package/src/tui/session/session-api/lifecycle/resume.mjs +3 -0
  410. package/src/tui/session/session-api/routes.mjs +10 -1
  411. package/src/tui/session/session-api/settings.mjs +9 -0
  412. package/src/tui/session/session-api/worktree.mjs +60 -0
  413. package/src/tui/session/session-api.mjs +2 -0
  414. package/src/tui/session/session-flow/auto-clear.mjs +31 -12
  415. package/src/tui/session/session-flow/drain.mjs +27 -3
  416. package/src/tui/session/session-flow/session-reset.mjs +4 -3
  417. package/src/tui/session/session-flow.mjs +2 -0
  418. package/src/tui/session/transcript-intake.mjs +14 -5
  419. package/src/tui/session/tui-steering-persist.mjs +25 -9
  420. package/src/tui/session/turn-stream.mjs +32 -4
  421. package/src/tui/session/turn-tool-cards.mjs +10 -2
  422. package/src/tui/session/turn.mjs +56 -10
  423. package/src/runtime/effort-judge/previous-human-request.mjs +0 -162
  424. package/src/runtime/effort-judge/verified-context-input.mjs +0 -10
@@ -1,15 +1,19 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { existsSync, realpathSync } from 'node:fs';
2
+ import { existsSync, lstatSync, realpathSync } from 'node:fs';
3
3
  import { access, copyFile, lstat, mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
- import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
4
+ import { basename, dirname, isAbsolute, join, relative, resolve } from 'node:path';
5
5
  import { PATCH_EDIT_MAX_BYTES } from './bounded-file-read.mjs';
6
6
  import { cleanString as clean } from './clean.mjs';
7
7
  import { runGitOffThread } from './git-runner.mjs';
8
8
  import { resolvePluginData } from './plugin-paths.mjs';
9
+ import { normalizeChangeStatus, splitPatchSections } from './turn-changes.mjs';
9
10
 
10
11
  const COMMAND_TIMEOUT_MS = 30_000;
11
12
  const COMMAND_MAX_BYTES = 8 * 1024 * 1024;
12
13
  const PATCH_MAX_BYTES = 2 * 1024 * 1024;
14
+ // The complete (replay-safe) patch a sealing turn records; per-file and
15
+ // per-session bounds are applied on top of this by the change record.
16
+ const FREEZE_PATCH_MAX_BYTES = 64 * 1024 * 1024;
13
17
  const MAX_UNTRACKED_FILE_BYTES = 2 * 1024 * 1024;
14
18
  // Largest tool-supplied baseline kept for `hash-object`: the apply_patch edit
15
19
  // limit. (2–64 MiB explicit untracked edits keep their exact revert.)
@@ -40,8 +44,26 @@ function pathKey(value) {
40
44
 
41
45
  /** `resolve` plus symlinks of the nearest existing ancestor: git reports the
42
46
  * worktree root canonically (macOS /var -> /private/var), callers may not. */
43
- function canonicalPath(value) {
47
+ function canonicalPath(value, dirs = null) {
44
48
  const full = resolve(value);
49
+ if (dirs) {
50
+ // Same result as the walk below without repeating it per file: the parent
51
+ // directory is canonicalized once per call; the final component only
52
+ // matters when it is itself a link (or its state is unknown).
53
+ const parent = dirname(full);
54
+ if (parent !== full) {
55
+ let canonicalParent = dirs.get(parent);
56
+ if (canonicalParent === undefined) {
57
+ canonicalParent = canonicalPath(parent);
58
+ dirs.set(parent, canonicalParent);
59
+ }
60
+ try {
61
+ if (!lstatSync(full).isSymbolicLink()) return join(canonicalParent, basename(full));
62
+ } catch (error) {
63
+ if (error?.code === 'ENOENT' || error?.code === 'ENOTDIR') return join(canonicalParent, basename(full));
64
+ }
65
+ }
66
+ }
45
67
  let head = full;
46
68
  while (!existsSync(head)) {
47
69
  const parent = dirname(head);
@@ -70,9 +92,16 @@ function commandError(args, stderr, code) {
70
92
  // call that would otherwise stall the event loop each time.
71
93
  async function runGit(
72
94
  args,
73
- { cwd, input = '', timeoutMs = COMMAND_TIMEOUT_MS, maxBytes = COMMAND_MAX_BYTES, allowFailure = false } = {}
95
+ {
96
+ cwd,
97
+ input = '',
98
+ timeoutMs = COMMAND_TIMEOUT_MS,
99
+ maxBytes = COMMAND_MAX_BYTES,
100
+ allowFailure = false,
101
+ stdoutEncoding = 'utf8',
102
+ } = {}
74
103
  ) {
75
- const result = await runGitOffThread(args, { cwd, input, timeoutMs, maxBytes });
104
+ const result = await runGitOffThread(args, { cwd, input, timeoutMs, maxBytes, stdoutEncoding });
76
105
  if (result.code !== 0 && !allowFailure) throw commandError(args, result.stderr, result.code);
77
106
  return result;
78
107
  }
@@ -94,11 +123,14 @@ async function repositoryRoot(worktree) {
94
123
  return root ? resolve(root) : null;
95
124
  }
96
125
 
126
+ function shadowDirForKey(key) {
127
+ return join(snapshotRoot(), createHash('sha256').update(key).digest('hex').slice(0, 24));
128
+ }
129
+
97
130
  function stateForRoot(root) {
98
131
  const key = pathKey(root);
99
132
  let state = states.get(key);
100
- const hash = createHash('sha256').update(key).digest('hex').slice(0, 24);
101
- const gitDir = join(snapshotRoot(), hash);
133
+ const gitDir = shadowDirForKey(key);
102
134
  if (state && state.gitDir === gitDir) return state;
103
135
  state = {
104
136
  root,
@@ -112,6 +144,11 @@ function stateForRoot(root) {
112
144
  // Bumped by every reported write under `root`; a capture records the
113
145
  // value it started from.
114
146
  changeStamp: 0,
147
+ // Set by forgetTurnWorktreeRoot(): the shadow repository must never be
148
+ // recreated for this state. A later snapshot of the same path gets a new one.
149
+ forgotten: false,
150
+ gcTimer: null,
151
+ gcRun: null,
115
152
  };
116
153
  states.set(key, state);
117
154
  return state;
@@ -120,8 +157,10 @@ function stateForRoot(root) {
120
157
  /** Report written paths (or `null`: anything may have changed). The next
121
158
  * review poll of an affected worktree captures instead of reusing. */
122
159
  export function noteTurnWorktreeChange(paths = null) {
160
+ if (states.size === 0) return;
123
161
  const list = Array.isArray(paths) ? paths.filter((value) => clean(value)) : [];
124
- const keys = list.map((value) => pathKey(canonicalPath(value)));
162
+ const dirs = new Map();
163
+ const keys = list.map((value) => pathKey(canonicalPath(value, dirs)));
125
164
  for (const state of states.values()) {
126
165
  const root = pathKey(state.root);
127
166
  if (keys.length === 0 || keys.some((key) => key === root || key.startsWith(`${root}/`))) {
@@ -156,7 +195,19 @@ async function sourceLineEndingArgs(root) {
156
195
  return [...values].flatMap(([key, value]) => ['-c', `${key}=${value}`]);
157
196
  }
158
197
 
198
+ // A forgotten root's shadow repository is gone for good. Every git command and
199
+ // every file write on it goes through shadowArgs/ensureState, so refusing there
200
+ // keeps a snapshot object that outlived forgetTurnWorktreeRoot() from
201
+ // recreating the directory.
202
+ function assertStateLive(state) {
203
+ if (!state.forgotten) return;
204
+ const error = new Error(`turn worktree snapshot for ${state.root} was removed`);
205
+ error.code = 'TURN_WORKTREE_SNAPSHOT_REMOVED';
206
+ throw error;
207
+ }
208
+
159
209
  function shadowArgs(state, args) {
210
+ assertStateLive(state);
160
211
  return [
161
212
  ...state.lineEndingArgs,
162
213
  // A conversion warning must never fail the snapshot `add`.
@@ -239,6 +290,7 @@ async function refreshSourceTracked(state, { force = false } = {}) {
239
290
  }
240
291
 
241
292
  async function ensureState(state) {
293
+ assertStateLive(state);
242
294
  if (state.initialized && (await exists(join(state.gitDir, 'config')))) return;
243
295
  await mkdir(dirname(state.gitDir), { recursive: true });
244
296
  if (!(await exists(join(state.gitDir, 'config')))) {
@@ -643,24 +695,141 @@ async function diffTreesUnlocked(state, baselineTree, currentTree, paths = null,
643
695
  return { patch, files, patchTruncated, currentTree, ...(omitPatch ? { patchOmitted: true } : {}) };
644
696
  }
645
697
 
698
+ /**
699
+ * Freeze the snapshot's last captured net diff into replayable per-file
700
+ * patches: one complete `--binary --full-index` diff (independent of the
701
+ * bounded display patch), split back into its `diff --git` sections. `paths`
702
+ * scopes a contended worktree to the session-owned files. Callers refresh the
703
+ * snapshot first; this never captures. Files whose bytes cannot be replayed
704
+ * (an uncaptured baseline, a type change, an over-limit diff) stay in the
705
+ * record as non-revertable with a reason.
706
+ */
707
+ export async function freezeTurnWorktreeChanges(snapshot, paths = null) {
708
+ if (!snapshot?.state || !snapshot.baselineTree) return null;
709
+ const scope = Array.isArray(paths) ? [...new Set(paths.filter(Boolean))] : null;
710
+ if (scope && scope.length === 0) return { root: snapshot.root, files: [] };
711
+ return await withStateLock(snapshot.state, async () => {
712
+ const { state, baselineTree, currentTree } = snapshot;
713
+ let review = { files: [], patch: '', patchTruncated: false };
714
+ let sections = [];
715
+ let unavailable = '';
716
+ if (currentTree && currentTree !== baselineTree) {
717
+ review = await diffTreesUnlocked(state, baselineTree, currentTree, scope, { omitPatch: true });
718
+ const args = [
719
+ 'diff',
720
+ '--binary',
721
+ '--full-index',
722
+ '--no-color',
723
+ '--no-ext-diff',
724
+ '--no-textconv',
725
+ '--src-prefix=a/',
726
+ '--dst-prefix=b/',
727
+ '--find-renames',
728
+ baselineTree,
729
+ currentTree,
730
+ '--',
731
+ ...(scope || ['.']),
732
+ ];
733
+ try {
734
+ const result = await runGit(shadowArgs(state, args), {
735
+ cwd: state.root,
736
+ maxBytes: FREEZE_PATCH_MAX_BYTES,
737
+ stdoutEncoding: 'base64',
738
+ });
739
+ sections = splitPatchSections(Buffer.from(result.stdout, 'base64'));
740
+ } catch (error) {
741
+ if (error?.code !== 'EMAXBUFFER') throw error;
742
+ unavailable = 'the turn changed too much to keep its patches for undo';
743
+ }
744
+ }
745
+ const listed = withOmittedBaselines({ ...snapshot, scopePaths: scope }, { ...review, patchOmitted: true });
746
+ const byPath = new Map();
747
+ for (const section of sections) {
748
+ const key = pathKey(section.newPath ?? section.oldPath ?? '');
749
+ // A type change diffs as a delete plus an add of one path: not one patch.
750
+ byPath.set(key, byPath.has(key) ? null : section.patch);
751
+ }
752
+ const files = (listed.files || []).map((file) => {
753
+ const entry = {
754
+ path: file.path,
755
+ ...(file.oldPath ? { oldPath: file.oldPath } : {}),
756
+ status: normalizeChangeStatus(file.status),
757
+ additions: Number(file.additions) || 0,
758
+ deletions: Number(file.deletions) || 0,
759
+ ...(file.binary ? { binary: true } : {}),
760
+ };
761
+ if (entry.status !== 'R') delete entry.oldPath;
762
+ if (file.omitted === true) {
763
+ return { ...entry, revertable: false, reason: 'binary/large file content was not captured' };
764
+ }
765
+ if (file.status === 'T') return { ...entry, revertable: false, reason: 'the file type changed' };
766
+ const patch = byPath.get(pathKey(file.path));
767
+ if (!patch) return { ...entry, revertable: false, reason: unavailable || 'no replayable patch was recorded' };
768
+ return { ...entry, revertable: true, patch };
769
+ });
770
+ return { root: snapshot.root, files };
771
+ });
772
+ }
773
+
646
774
  // One maintenance pass per shadow repository per process, off the turn's
647
775
  // critical path. `--auto` does nothing until the repository actually needs it,
648
776
  // so the common case costs a process spawn and nothing else.
649
777
  const maintainedShadowRepositories = new Set();
650
778
 
651
779
  function scheduleShadowGc(state) {
652
- if (maintainedShadowRepositories.has(state.gitDir)) return;
780
+ if (state.forgotten || maintainedShadowRepositories.has(state.gitDir)) return;
653
781
  maintainedShadowRepositories.add(state.gitDir);
654
782
  const timer = setTimeout(() => {
783
+ state.gcTimer = null;
655
784
  // Deliberately outside the state lock: collection can run for minutes and
656
785
  // git serializes itself through gc.lock, so a clash just retries later.
657
- void runGit(shadowArgs(state, ['gc', '--auto', `--prune=${SHADOW_GC_PRUNE}`, '--quiet']), {
658
- cwd: state.root,
659
- allowFailure: true,
660
- timeoutMs: 300_000,
661
- }).catch(() => {});
786
+ // forgetTurnWorktreeRoot() waits for `gcRun` before deleting the repository.
787
+ state.gcRun = Promise.resolve()
788
+ .then(() =>
789
+ runGit(shadowArgs(state, ['gc', '--auto', `--prune=${SHADOW_GC_PRUNE}`, '--quiet']), {
790
+ cwd: state.root,
791
+ allowFailure: true,
792
+ timeoutMs: 300_000,
793
+ })
794
+ )
795
+ .catch(() => {});
662
796
  }, 60_000);
663
797
  timer.unref?.();
798
+ state.gcTimer = timer;
799
+ }
800
+
801
+ async function removeShadowDir(gitDir, removed, kept) {
802
+ if (!(await exists(gitDir))) return;
803
+ await rm(gitDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 }).catch(() => {});
804
+ ((await exists(gitDir)) ? kept : removed).push(gitDir);
805
+ }
806
+
807
+ /** Drop every trace of one worktree root: serialize behind its pending capture
808
+ * work, remove its shadow repository and in-memory state, and refuse any write
809
+ * a snapshot object that still holds the old state attempts afterwards.
810
+ * Only the exact root is touched. Returns `{ removed, kept }` shadow paths;
811
+ * `kept` lists a shadow repository that could not be deleted. */
812
+ export async function forgetTurnWorktreeRoot(root) {
813
+ const text = clean(root);
814
+ const removed = [];
815
+ const kept = [];
816
+ if (!text) return { removed, kept };
817
+ const keys = new Set([pathKey(resolve(text)), pathKey(canonicalPath(text))]);
818
+ const gitDirs = new Set([...keys].map(shadowDirForKey));
819
+ for (const [key, state] of [...states]) {
820
+ if (!keys.has(key) && !keys.has(pathKey(state.root))) continue;
821
+ gitDirs.add(state.gitDir);
822
+ await withStateLock(state, async () => {
823
+ state.forgotten = true;
824
+ if (states.get(key) === state) states.delete(key);
825
+ if (state.gcTimer) clearTimeout(state.gcTimer);
826
+ state.gcTimer = null;
827
+ await state.gcRun;
828
+ maintainedShadowRepositories.delete(state.gitDir);
829
+ });
830
+ }
831
+ for (const gitDir of gitDirs) await removeShadowDir(gitDir, removed, kept);
832
+ return { removed, kept };
664
833
  }
665
834
 
666
835
  export async function createTurnWorktreeSnapshot(worktree) {
@@ -847,7 +1016,8 @@ async function revertPathsUnlocked(snapshot, targets) {
847
1016
  await diffTreesUnlocked(snapshot.state, snapshot.baselineTree, currentTree, snapshot.scopePaths)
848
1017
  )
849
1018
  );
850
- return snapshot;
1019
+ // The absolute paths now back at their baseline bytes.
1020
+ return targets.map((target) => resolve(snapshot.root, target));
851
1021
  }
852
1022
 
853
1023
  export async function revertTurnWorktreeFile(snapshot, value) {
@@ -14,8 +14,11 @@ import { parentPort, Worker } from 'node:worker_threads';
14
14
  * worker is detached before it is terminated, so no request is ever sent to
15
15
  * (or lost with) a worker that is shutting down.
16
16
  * `request.running()` reports whether a worker thread is currently attached.
17
+ *
18
+ * `onEvent` (optional): receives what the worker sends with postWorkerEvent
19
+ * (messages that answer no request).
17
20
  */
18
- export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOptions } = {}) {
21
+ export function createWorkerRequestClient(url, { idleExitMs = 0, onEvent = null, ...workerOptions } = {}) {
19
22
  let worker = null;
20
23
  let nextRequestId = 0;
21
24
  // Requests belong to the worker they were posted to: a replacement worker
@@ -50,7 +53,12 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
50
53
  owned.clear();
51
54
  for (const entry of entries) entry.reject(error);
52
55
  };
53
- created.on('message', ({ id, result, error }) => {
56
+ created.on('message', (message) => {
57
+ if (message && Object.hasOwn(message, 'event')) {
58
+ onEvent?.(message.event);
59
+ return;
60
+ }
61
+ const { id, result, error } = message;
54
62
  const entry = owned.get(id);
55
63
  if (!entry) return;
56
64
  owned.delete(id);
@@ -109,6 +117,11 @@ export function createWorkerRequestClient(url, { idleExitMs = 0, ...workerOption
109
117
  return request;
110
118
  }
111
119
 
120
+ /** Worker side: send `event` to the client's `onEvent`, outside any request. */
121
+ export function postWorkerEvent(event) {
122
+ parentPort.postMessage({ event });
123
+ }
124
+
112
125
  /** Worker side: answer every request with `handler(payload)` (sync or async). */
113
126
  export function serveWorkerRequests(handler) {
114
127
  parentPort.on('message', ({ id, payload }) => {
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Root cleanups for Mixdog's path-keyed caches: when a managed worktree is
3
+ * removed these drop everything the runtime cached under that exact root, so
4
+ * the removal leaves no turn snapshots, snapshot-store scopes or code-graph
5
+ * state behind. Each returns `{ removed, kept }` and never touches other roots.
6
+ * Modules load lazily: registering costs nothing until a worktree is removed.
7
+ */
8
+ import { registerRootCleanup } from './contract.mjs';
9
+
10
+ const merge = (target, part) => {
11
+ target.removed.push(...part.removed);
12
+ target.kept.push(...part.kept);
13
+ };
14
+
15
+ /** @type {Record<string, (root: string) => Promise<{removed: string[], kept: string[]}>>} */
16
+ export const WORKTREE_CACHE_CLEANUPS = Object.freeze({
17
+ 'turn-worktree-snapshots': async (root) => {
18
+ const { forgetTurnWorktreeRoot } = await import('../turn-worktree-snapshot.mjs');
19
+ return await forgetTurnWorktreeRoot(root);
20
+ },
21
+ 'turn-snapshot-store': async (root) => {
22
+ const { forgetTurnSnapshotRoot } = await import('../turn-snapshot-store.mjs');
23
+ return await forgetTurnSnapshotRoot(root);
24
+ },
25
+ 'code-graph': async (root) => {
26
+ const [state, disk, trusted] = await Promise.all([
27
+ import('../code-graph-state.mjs'),
28
+ import('../../agent/orchestrator/tools/code-graph/disk-cache.mjs'),
29
+ import('../../agent/orchestrator/tools/code-graph/trusted-roots.mjs'),
30
+ ]);
31
+ const result = { removed: state.forgetCodeGraphStateRoot(root).map((name) => `${name}:${root}`), kept: [] };
32
+ merge(result, disk.forgetDiskCodeGraphRoot(root));
33
+ merge(result, trusted.forgetTrustedCodeGraphRoot(root));
34
+ return result;
35
+ },
36
+ });
37
+
38
+ const registrations = new WeakSet();
39
+
40
+ /**
41
+ * Register every cache cleanup once per registry function. Idempotent: calling
42
+ * it again (or from several hosts) neither throws nor registers duplicates.
43
+ * @param {typeof registerRootCleanup} [register] Defaults to the process registry.
44
+ * @returns {string[]} Cleanup names.
45
+ */
46
+ export function registerWorktreeCacheCleanups(register = registerRootCleanup) {
47
+ if (!registrations.has(register)) {
48
+ registrations.add(register);
49
+ for (const [name, cleanup] of Object.entries(WORKTREE_CACHE_CLEANUPS)) register(name, cleanup);
50
+ }
51
+ return Object.keys(WORKTREE_CACHE_CLEANUPS);
52
+ }