mixdog 1.2.2 → 1.2.4

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 (692) hide show
  1. package/README.md +18 -0
  2. package/package.json +2 -1
  3. package/scripts/gate-local.mjs +6 -4
  4. package/scripts/lib/gate-scheduler.mjs +29 -11
  5. package/scripts/probe-provider-catalog.mjs +31 -0
  6. package/scripts/provider-pingpong-live.mjs +599 -0
  7. package/scripts/session-sweep.mjs +5 -25
  8. package/src/agents/advisor/AGENT.md +1 -1
  9. package/src/agents/advisor/agent.json +1 -1
  10. package/src/agents/maintainer/AGENT.md +1 -1
  11. package/src/agents/maintainer/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +2 -4
  13. package/src/agents/security/AGENT.md +1 -2
  14. package/src/agents/writer/AGENT.md +2 -3
  15. package/src/defaults/shipped-history.json +11 -1
  16. package/src/defaults/skills/image/SKILL.md +21 -23
  17. package/src/defaults/skills/setup/SKILL.md +6 -1
  18. package/src/defaults/skills/setup/references/actions.md +44 -22
  19. package/src/defaults/skills/setup/references/surfaces.md +10 -8
  20. package/src/headless-exec.mjs +15 -8
  21. package/src/lib/keychain-cjs.cjs +20 -15
  22. package/src/output-styles/common.md +11 -10
  23. package/src/rules/agent/AGENT.md +6 -4
  24. package/src/rules/lead/LEAD.md +7 -9
  25. package/src/rules/shared/45-destructive.md +4 -0
  26. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  28. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  29. package/src/runtime/agent/orchestrator/agent-trace-io.mjs +92 -61
  30. package/src/runtime/agent/orchestrator/cache-mtime.mjs +10 -1
  31. package/src/runtime/agent/orchestrator/config-providers-merge.mjs +7 -0
  32. package/src/runtime/agent/orchestrator/config.mjs +5 -0
  33. package/src/runtime/agent/orchestrator/context/collect.mjs +1 -0
  34. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +26 -7
  35. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +112 -7
  36. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  37. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  38. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  39. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +48 -20
  40. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +3 -1
  41. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens.mjs +24 -17
  42. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +9 -9
  43. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  45. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +31 -15
  46. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-blocks.mjs +20 -1
  47. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-delta.mjs +15 -2
  48. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +11 -4
  49. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/message-events.mjs +3 -0
  50. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +12 -5
  51. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-outcome.mjs +1 -0
  52. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-state.mjs +14 -0
  53. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -0
  54. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +9 -0
  55. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +10 -0
  56. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  57. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  58. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +131 -9
  59. package/src/runtime/agent/orchestrator/providers/antigravity-response.mjs +29 -10
  60. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +125 -8
  61. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +12 -62
  62. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +117 -41
  63. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  64. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +117 -72
  65. package/src/runtime/agent/orchestrator/providers/compat-request-policy.mjs +78 -2
  66. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +39 -5
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  69. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  70. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  71. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  72. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +42 -11
  73. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +109 -6
  74. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +17 -4
  75. package/src/runtime/agent/orchestrator/providers/cursor.mjs +22 -2
  76. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +73 -7
  77. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +75 -6
  78. package/src/runtime/agent/orchestrator/providers/custom-reasoning.mjs +34 -7
  79. package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +32 -6
  80. package/src/runtime/agent/orchestrator/providers/deepseek.mjs +67 -0
  81. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +29 -5
  82. package/src/runtime/agent/orchestrator/providers/gemini-cache-policy.mjs +11 -4
  83. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +36 -13
  84. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +101 -11
  85. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +41 -5
  86. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  88. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +19 -14
  89. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +371 -41
  90. package/src/runtime/agent/orchestrator/providers/gemini-sdk-request.mjs +1 -2
  91. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +97 -49
  92. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +81 -10
  93. package/src/runtime/agent/orchestrator/providers/gemini.mjs +35 -8
  94. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/providers/grok-oauth.mjs +1 -0
  96. package/src/runtime/agent/orchestrator/providers/lib/action-input-contract.mjs +60 -0
  97. package/src/runtime/agent/orchestrator/providers/lib/anthropic-native-blocks.mjs +1 -0
  98. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +253 -42
  99. package/src/runtime/agent/orchestrator/providers/lib/anthropic-server-tool-usage.mjs +18 -0
  100. package/src/runtime/agent/orchestrator/providers/lib/anthropic-system-blocks.mjs +27 -6
  101. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +392 -0
  102. package/src/runtime/agent/orchestrator/providers/lib/inclusive-usage.mjs +54 -0
  103. package/src/runtime/agent/orchestrator/providers/lib/origin-bound-fetch.mjs +16 -0
  104. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  105. package/src/runtime/agent/orchestrator/providers/lib/responses-terminal-fields.mjs +11 -0
  106. package/src/runtime/agent/orchestrator/providers/lib/usage-primitives.mjs +8 -0
  107. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +50 -22
  108. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +133 -21
  109. package/src/runtime/agent/orchestrator/providers/mimo-oauth-profile.mjs +13 -15
  110. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  111. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +1 -1
  112. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  113. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +92 -16
  114. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -13
  115. package/src/runtime/agent/orchestrator/providers/openai-codex-metadata.mjs +2 -0
  116. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +133 -62
  117. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +35 -14
  118. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-result.mjs +5 -2
  119. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +39 -7
  120. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/tool-call-acc.mjs +2 -9
  121. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  122. package/src/runtime/agent/orchestrator/providers/openai-compat-error-contract.mjs +1 -1
  123. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +84 -2
  124. package/src/runtime/agent/orchestrator/providers/openai-compat-options.mjs +47 -25
  125. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +5 -1
  126. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +155 -0
  127. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +40 -24
  128. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +63 -25
  129. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2901 -0
  130. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +183 -0
  131. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +87 -6
  132. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  133. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +7 -1
  134. package/src/runtime/agent/orchestrator/providers/openai-compat-trace.mjs +2 -0
  135. package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +103 -26
  136. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +23 -4
  137. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +11 -6
  138. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +17 -2
  139. package/src/runtime/agent/orchestrator/providers/openai-direct-request.mjs +15 -23
  140. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  141. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +23 -10
  142. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +28 -5
  143. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +10 -0
  144. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-items.mjs +18 -7
  145. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-outcome.mjs +4 -0
  146. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-state.mjs +5 -0
  147. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/text-relay.mjs +9 -1
  148. package/src/runtime/agent/orchestrator/providers/openai-model-capabilities.mjs +80 -0
  149. package/src/runtime/agent/orchestrator/providers/openai-oauth-dispatch.mjs +3 -1
  150. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +7 -0
  151. package/src/runtime/agent/orchestrator/providers/openai-oauth.mjs +7 -1
  152. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +72 -24
  153. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +323 -0
  154. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +50 -16
  155. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +7 -19
  156. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/refusals.mjs +60 -0
  157. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +51 -36
  158. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state.mjs +30 -0
  159. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +12 -0
  160. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/terminal-frames.mjs +25 -6
  161. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +18 -9
  162. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +181 -0
  163. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +78 -70
  164. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +74 -0
  165. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +150 -10
  166. package/src/runtime/agent/orchestrator/providers/registry.mjs +13 -2
  167. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +7 -1
  168. package/src/runtime/agent/orchestrator/providers/transport-scope.d.mts +6 -0
  169. package/src/runtime/agent/orchestrator/providers/transport-scope.mjs +28 -0
  170. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +164 -0
  171. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +522 -0
  172. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +1131 -0
  173. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +195 -0
  174. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +104 -0
  175. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  176. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +24 -0
  177. package/src/runtime/agent/orchestrator/runtime-core/effort.mjs +15 -0
  178. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  179. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  180. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  181. package/src/runtime/agent/orchestrator/runtime-core/provider-request-snapshot.mjs +7 -3
  182. package/src/runtime/agent/orchestrator/runtime-core/session-title.mjs +21 -1
  183. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +16 -3
  184. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  185. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +526 -0
  186. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +3 -0
  187. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-schema.mjs +10 -7
  188. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +178 -71
  189. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +102 -12
  190. package/src/runtime/agent/orchestrator/runtime-core/tool-exposure.mjs +96 -0
  191. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  192. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  193. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  194. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  195. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  196. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +5 -11
  197. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  198. package/src/runtime/agent/orchestrator/session/compact/constants.mjs +3 -2
  199. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +16 -93
  200. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  201. package/src/runtime/agent/orchestrator/session/compact/summary-anchor.mjs +5 -0
  202. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +148 -0
  203. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  204. package/src/runtime/agent/orchestrator/session/context-tool-schema.mjs +17 -2
  205. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -4
  206. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  207. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  208. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +3 -2
  209. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +6 -1
  210. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  211. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  212. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +89 -1
  213. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +1 -1
  214. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +56 -0
  215. package/src/runtime/agent/orchestrator/session/loop/deferred-call-through.mjs +18 -8
  216. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +1 -27
  217. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  218. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  219. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  220. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +24 -10
  221. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +135 -0
  222. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +31 -4
  223. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  224. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +67 -20
  225. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +10 -10
  226. package/src/runtime/agent/orchestrator/session/loop/tool-call-resolution.mjs +95 -0
  227. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  228. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +30 -3
  229. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +29 -5
  230. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +15 -1
  231. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +147 -0
  232. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +7 -5
  233. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  234. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  235. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +23 -2
  236. package/src/runtime/agent/orchestrator/session/manager/ask-turn-commit.mjs +34 -12
  237. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +54 -13
  238. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  239. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +57 -25
  240. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +11 -0
  241. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +22 -0
  242. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  243. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +8 -0
  244. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  245. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  246. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  247. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +231 -16
  248. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  249. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  250. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +47 -17
  251. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +26 -4
  252. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +82 -0
  253. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +36 -35
  254. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  255. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +6 -3
  256. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +4 -1
  257. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  258. package/src/runtime/agent/orchestrator/session/manager.mjs +4 -10
  259. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +6 -0
  260. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  261. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +17 -1
  262. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +96 -0
  263. package/src/runtime/agent/orchestrator/session/side-question.mjs +197 -0
  264. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +22 -15
  265. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  266. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  267. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  268. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +23 -0
  269. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  270. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  271. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  272. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +44 -37
  273. package/src/runtime/agent/orchestrator/session/store/session-observers.mjs +4 -2
  274. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  275. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  276. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  277. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -21
  278. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  279. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  280. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  281. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +4 -2
  282. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  283. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +136 -196
  284. package/src/runtime/agent/orchestrator/session/store-summary-archived-agent.mjs +34 -15
  285. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +53 -115
  286. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +8 -0
  287. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +10 -1
  288. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  289. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  290. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +17 -10
  291. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  292. package/src/runtime/agent/orchestrator/session/store.mjs +22 -19
  293. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +12 -12
  294. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +38 -14
  295. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  296. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +1 -1
  297. package/src/runtime/agent/orchestrator/session/tool-batch/state.mjs +7 -1
  298. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +4 -0
  299. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +79 -0
  300. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  301. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  302. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  303. package/src/runtime/agent/orchestrator/tools/bash-policy-scan.mjs +8 -2
  304. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/command-preflight.mjs +3 -3
  305. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  306. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  307. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +4 -1
  308. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +218 -20
  309. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  310. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  311. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  312. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  313. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  314. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  315. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  316. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +247 -49
  317. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  318. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  319. package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +23 -1
  320. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  321. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  322. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/root-resolution.mjs +1 -1
  323. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +1 -1
  324. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  325. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  326. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  327. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  328. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  329. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  330. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  331. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  332. package/src/runtime/agent/orchestrator/tools/patch/ui-diff.mjs +73 -39
  333. package/src/runtime/agent/orchestrator/tools/progress-message.mjs +18 -1
  334. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +7 -4
  335. package/src/runtime/agent/orchestrator/tools/shell-exec-policy.mjs +88 -12
  336. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +2 -0
  337. package/src/runtime/attachments/store.d.mts +9 -0
  338. package/src/runtime/attachments/store.mjs +80 -9
  339. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  340. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  341. package/src/runtime/computer-bridge/action-schema.mjs +219 -196
  342. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  343. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  344. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  345. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  346. package/src/runtime/github/tool.mjs +208 -17
  347. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  348. package/src/runtime/media/catalog.mjs +4 -4
  349. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  350. package/src/runtime/media/defaults.mjs +37 -0
  351. package/src/runtime/media/jobs.mjs +12 -2
  352. package/src/runtime/media/lanes.mjs +12 -0
  353. package/src/runtime/media/tool-defs.mjs +15 -4
  354. package/src/runtime/media/tool.mjs +41 -12
  355. package/src/runtime/media/upstream-error.mjs +72 -6
  356. package/src/runtime/memory/index.mjs +1 -0
  357. package/src/runtime/memory/lib/http-router/ingest-routes.mjs +22 -1
  358. package/src/runtime/memory/lib/ingest/transcript-offsets.mjs +2 -0
  359. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  360. package/src/runtime/memory/lib/pg/adapter.mjs +98 -1
  361. package/src/runtime/memory/lib/pg/process.mjs +36 -33
  362. package/src/runtime/memory/lib/pg/run-child.mjs +49 -0
  363. package/src/runtime/memory/lib/pg/supervisor.mjs +112 -4
  364. package/src/runtime/memory/lib/runtime-fetcher.mjs +12 -13
  365. package/src/runtime/memory/lib/transcript-ingest.mjs +14 -0
  366. package/src/runtime/office/README.md +1 -1
  367. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  368. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  369. package/src/runtime/office/authoring/html-document-author.mjs +2 -0
  370. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  371. package/src/runtime/office/authoring/pptx-contact-sheet.mjs +2 -0
  372. package/src/runtime/office/authoring/pptx-html-measure.mjs +3 -1
  373. package/src/runtime/office/authoring/pptx-html-runner.mjs +2 -0
  374. package/src/runtime/office/authoring/xlsx-html-runner.mjs +3 -1
  375. package/src/runtime/office/core/office-actions-read.mjs +3 -0
  376. package/src/runtime/office/core/office-render-preview.mjs +2 -0
  377. package/src/runtime/office/core/office-session-dispatch.mjs +7 -0
  378. package/src/runtime/office/core/office-sessionless-actions.mjs +8 -0
  379. package/src/runtime/office/core/office-sessions.mjs +5 -0
  380. package/src/runtime/office/core/office-transactions.mjs +3 -0
  381. package/src/runtime/office/core/office-write-ledger.mjs +94 -0
  382. package/src/runtime/office/design/library/templates/mixdog-executive.mixdog-edit.pptx +0 -0
  383. package/src/runtime/office/index.mjs +29 -0
  384. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  385. package/src/runtime/office/pdf/pdf-edit-document.mjs +2 -0
  386. package/src/runtime/office/pdf/pdf-render.mjs +23 -7
  387. package/src/runtime/office/pdf/pdf-security.mjs +2 -0
  388. package/src/runtime/office/portable/portable-opc.mjs +4 -1
  389. package/src/runtime/office/quality/visual-diff.mjs +2 -0
  390. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  391. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  392. package/src/runtime/shared/atomic-file.d.mts +1 -1
  393. package/src/runtime/shared/background-tasks.mjs +6 -1
  394. package/src/runtime/shared/bounded-file-read.mjs +13 -0
  395. package/src/runtime/shared/bounded-line-diff.mjs +191 -0
  396. package/src/runtime/shared/browser-launch.mjs +15 -0
  397. package/src/runtime/shared/buffered-appender.mjs +18 -0
  398. package/src/runtime/shared/config.mjs +78 -18
  399. package/src/runtime/shared/err-text.mjs +48 -4
  400. package/src/runtime/shared/file-lock.mjs +6 -5
  401. package/src/runtime/shared/file-permissions.d.mts +6 -1
  402. package/src/runtime/shared/file-permissions.mjs +54 -6
  403. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  404. package/src/runtime/shared/llm/anthropic-effort.mjs +6 -0
  405. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  406. package/src/runtime/shared/llm/usage-accounting.mjs +5 -2
  407. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +3 -0
  408. package/src/runtime/shared/loopback-listener.mjs +9 -0
  409. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  410. package/src/runtime/shared/model-route-chain.mjs +48 -0
  411. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  412. package/src/runtime/shared/pid-liveness.mjs +12 -0
  413. package/src/runtime/shared/provider-api-key.mjs +8 -0
  414. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  415. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  416. package/src/runtime/shared/quiet-progress.mjs +101 -0
  417. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  418. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  419. package/src/runtime/shared/remote-intent.mjs +2 -42
  420. package/src/runtime/shared/review-diff.mjs +52 -0
  421. package/src/runtime/shared/session-sidebar-host.d.mts +4 -0
  422. package/src/runtime/shared/session-sidebar-host.mjs +38 -0
  423. package/src/runtime/shared/slow-span.mjs +121 -0
  424. package/src/runtime/shared/statusline/statusline-segments.mjs +52 -6
  425. package/src/runtime/shared/tool-card-model/card-header.mjs +7 -1
  426. package/src/runtime/shared/tool-card-model/generic-detail.mjs +39 -5
  427. package/src/runtime/shared/tool-card-model.mjs +5 -2
  428. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  429. package/src/runtime/shared/tool-output-limit.mjs +4 -0
  430. package/src/runtime/shared/tool-primitives.mjs +13 -0
  431. package/src/runtime/shared/tool-result-summary.mjs +22 -1
  432. package/src/runtime/shared/tool-status.mjs +3 -2
  433. package/src/runtime/shared/tool-surface.mjs +20 -6
  434. package/src/runtime/shared/tool-work-units.mjs +54 -11
  435. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  436. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  437. package/src/runtime/shared/transcript-writer/purge.mjs +74 -0
  438. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  439. package/src/runtime/shared/transcript-writer.mjs +24 -6
  440. package/src/runtime/shared/turn-changes.mjs +269 -100
  441. package/src/runtime/shared/turn-review-reply.mjs +8 -39
  442. package/src/runtime/shared/turn-snapshot-store.mjs +19 -108
  443. package/src/runtime/shared/turn-snapshot.mjs +865 -683
  444. package/src/runtime/shared/turn-worktree-snapshot.mjs +183 -255
  445. package/src/runtime/shared/user-data-guard.mjs +262 -49
  446. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  447. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  448. package/src/runtime/shared/worktrees/carry.mjs +533 -0
  449. package/src/runtime/shared/worktrees/contract.mjs +60 -0
  450. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  451. package/src/runtime/shared/worktrees/file-recovery.mjs +84 -0
  452. package/src/runtime/shared/worktrees/git-head.mjs +214 -0
  453. package/src/runtime/shared/worktrees/git.mjs +8 -3
  454. package/src/runtime/shared/worktrees/include.mjs +125 -7
  455. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  456. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  457. package/src/runtime/shared/worktrees/manager.mjs +525 -31
  458. package/src/runtime/shared/worktrees/merge.mjs +31 -21
  459. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  460. package/src/runtime/shared/worktrees/paths.d.mts +6 -0
  461. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  462. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  463. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  464. package/src/runtime/terminal-bridge/client.mjs +57 -20
  465. package/src/runtime/terminal-bridge/tool-defs.mjs +13 -6
  466. package/src/runtime/tidy/apply.mjs +47 -2
  467. package/src/runtime/tidy/run-engines.mjs +11 -5
  468. package/src/session-runtime/agent-tool-routing.mjs +19 -13
  469. package/src/session-runtime/boot/apis.mjs +20 -6
  470. package/src/session-runtime/boot/config.mjs +2 -0
  471. package/src/session-runtime/boot/facade.mjs +4 -0
  472. package/src/session-runtime/boot/infrastructure.mjs +6 -1
  473. package/src/session-runtime/boot/session.mjs +3 -3
  474. package/src/session-runtime/boot/tools.mjs +7 -1
  475. package/src/session-runtime/conversation-import/service.mjs +1 -1
  476. package/src/session-runtime/cwd-plugins/cwd-apply.mjs +12 -5
  477. package/src/session-runtime/goal-facade-api.mjs +2 -2
  478. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +91 -0
  479. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -1
  480. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +15 -5
  481. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +137 -26
  482. package/src/session-runtime/internal-tool-executor.mjs +8 -2
  483. package/src/session-runtime/lifecycle/cold-heir.mjs +187 -0
  484. package/src/session-runtime/lifecycle/inheritance.mjs +71 -27
  485. package/src/session-runtime/lifecycle/session-catalog.mjs +25 -18
  486. package/src/session-runtime/lifecycle/session-switch/resume.mjs +6 -0
  487. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +36 -48
  488. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  489. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  490. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  491. package/src/session-runtime/lifecycle/teardown.mjs +70 -14
  492. package/src/session-runtime/lifecycle-api.mjs +1 -0
  493. package/src/session-runtime/mcp-glue/connect.mjs +17 -5
  494. package/src/session-runtime/media-api.mjs +8 -0
  495. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  496. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  497. package/src/session-runtime/model-route/set-route.mjs +116 -36
  498. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  499. package/src/session-runtime/model-route-api.mjs +4 -2
  500. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  501. package/src/session-runtime/native-web-search-usage.mjs +51 -0
  502. package/src/session-runtime/native-web-search.mjs +19 -9
  503. package/src/session-runtime/new-session-config.mjs +4 -2
  504. package/src/session-runtime/provider-auth/login.mjs +4 -0
  505. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  506. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  507. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  508. package/src/session-runtime/provider-models.mjs +1 -0
  509. package/src/session-runtime/provider-usage.mjs +4 -0
  510. package/src/session-runtime/resource-scope-decorate.mjs +10 -3
  511. package/src/session-runtime/route-resolution.mjs +3 -1
  512. package/src/session-runtime/runtime-core.mjs +4 -0
  513. package/src/session-runtime/runtime-facade.mjs +6 -0
  514. package/src/session-runtime/runtime-review-api.mjs +8 -2
  515. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  516. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  517. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  518. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  519. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  520. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  521. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  522. package/src/session-runtime/services/agent-tool/index-write-queue.mjs +81 -20
  523. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  524. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  525. package/src/session-runtime/services/agent-tool/lead-worker-index/forgotten-leads.mjs +29 -0
  526. package/src/session-runtime/services/agent-tool/lead-worker-index/index-file.mjs +1 -1
  527. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  528. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  529. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  530. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +82 -13
  531. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  532. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  533. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  534. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  535. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  536. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  537. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  538. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  539. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  540. package/src/session-runtime/services/agent-tool/tag-registry.mjs +18 -10
  541. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  542. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  543. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  544. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  545. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  546. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  547. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  548. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +10 -1
  549. package/src/session-runtime/services/agent-tool/worker-index.mjs +74 -15
  550. package/src/session-runtime/services/agent-tool.mjs +7 -0
  551. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  552. package/src/session-runtime/services/channel-admin.mjs +19 -3
  553. package/src/session-runtime/services/memory-runtime-proxy/calls.mjs +4 -0
  554. package/src/session-runtime/services/memory-runtime-proxy.mjs +1 -0
  555. package/src/session-runtime/services/provider-admin.mjs +158 -15
  556. package/src/session-runtime/session-create/session-options.mjs +7 -5
  557. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  558. package/src/session-runtime/session-transcript-purge.mjs +32 -0
  559. package/src/session-runtime/session-transcript.mjs +4 -5
  560. package/src/session-runtime/session-turn-api.mjs +17 -14
  561. package/src/session-runtime/settings-builtin-tools-api.mjs +18 -6
  562. package/src/session-runtime/setup-tool/executor.mjs +57 -9
  563. package/src/session-runtime/setup-tool/extended-actions.mjs +86 -6
  564. package/src/session-runtime/setup-tool/settings-contract.mjs +18 -3
  565. package/src/session-runtime/setup-tool/tool-defs.mjs +25 -4
  566. package/src/session-runtime/skills-api.mjs +3 -5
  567. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  568. package/src/session-runtime/tool-surface.mjs +5 -1
  569. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  570. package/src/session-runtime/turn/compaction-gate.mjs +25 -0
  571. package/src/session-runtime/turn/session-ops.mjs +7 -5
  572. package/src/session-runtime/turn/side-question-ops.mjs +19 -0
  573. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +7 -1
  574. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +12 -3
  575. package/src/session-runtime/turn/turn-run/turn-settle.mjs +18 -2
  576. package/src/session-runtime/turn/turn-run/turn-timing.mjs +4 -3
  577. package/src/session-runtime/turn/turn-run.mjs +26 -45
  578. package/src/session-runtime/usage-stats-api.mjs +46 -11
  579. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  580. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  581. package/src/session-runtime/workspace/project-branches.mjs +35 -41
  582. package/src/session-runtime/workspace/transition-steps.mjs +215 -116
  583. package/src/session-runtime/workspace/transitions.mjs +428 -73
  584. package/src/standalone/channel-transport.mjs +3 -4
  585. package/src/standalone/daemon-agent-control.mjs +9 -1
  586. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  587. package/src/standalone/daemon-local-session-bridge.mjs +14 -1
  588. package/src/standalone/daemon-owner.mjs +17 -4
  589. package/src/standalone/daemon-pg-stop.mjs +49 -0
  590. package/src/standalone/daemon-session-control.mjs +88 -0
  591. package/src/standalone/daemon-stop-budget.d.mts +2 -0
  592. package/src/standalone/daemon-stop-budget.mjs +18 -0
  593. package/src/standalone/daemon-stored-session-views.mjs +25 -3
  594. package/src/standalone/daemon-telemetry.mjs +19 -0
  595. package/src/standalone/daemon.mjs +63 -1
  596. package/src/standalone/desktop-service-registry.mjs +5 -0
  597. package/src/standalone/local-session-runtime.mjs +80 -30
  598. package/src/standalone/session-client.mjs +42 -0
  599. package/src/standalone/session-protocol.mjs +14 -0
  600. package/src/standalone/session-proxy/projection.mjs +41 -4
  601. package/src/standalone/session-runtime-inline-host/boot-loaders.mjs +15 -1
  602. package/src/standalone/session-runtime-inline-host.mjs +3 -0
  603. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  604. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  605. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  606. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  607. package/src/standalone/session-service/agent-tree.mjs +28 -2
  608. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  609. package/src/standalone/session-service/entries.mjs +1 -0
  610. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  611. package/src/standalone/session-service/quota-resume.mjs +28 -13
  612. package/src/standalone/session-service/session-calls/session-action.mjs +18 -3
  613. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  614. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  615. package/src/standalone/session-service-api.mjs +6 -1
  616. package/src/standalone/session-state-patch.d.mts +2 -1
  617. package/src/standalone/session-state-patch.mjs +41 -1
  618. package/src/standalone/session-tool-output.mjs +82 -0
  619. package/src/tui/App.jsx +25 -0
  620. package/src/tui/app/app-view/floating-panel.jsx +26 -1
  621. package/src/tui/app/app-view.jsx +2 -1
  622. package/src/tui/app/btw-controller.mjs +118 -0
  623. package/src/tui/app/create-app-pickers.mjs +2 -0
  624. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  625. package/src/tui/app/provider-setup-picker.mjs +15 -3
  626. package/src/tui/app/shell-layout/row-budgets.mjs +11 -1
  627. package/src/tui/app/shell-layout/surfaces.mjs +3 -1
  628. package/src/tui/app/slash-commands.mjs +19 -3
  629. package/src/tui/app/slash-dispatch/session-commands.mjs +68 -6
  630. package/src/tui/app/use-global-key-input/approval-shortcut.mjs +12 -0
  631. package/src/tui/app/use-global-key-input.mjs +5 -12
  632. package/src/tui/app/use-prompt-handlers.mjs +11 -0
  633. package/src/tui/components/BtwPanel.jsx +53 -0
  634. package/src/tui/components/QuestionPanel.jsx +275 -0
  635. package/src/tui/components/Spinner.jsx +4 -0
  636. package/src/tui/components/SpinnerByline.jsx +21 -2
  637. package/src/tui/components/question-panel-model.mjs +156 -0
  638. package/src/tui/components/status-line/local-line.mjs +10 -2
  639. package/src/tui/dist/index.mjs +2897 -1722
  640. package/src/tui/session/active-tool-summary.mjs +1 -1
  641. package/src/tui/session/context-state.mjs +4 -5
  642. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  643. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  644. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  645. package/src/tui/session/quota-resume.mjs +140 -61
  646. package/src/tui/session/session-api/commands.mjs +14 -10
  647. package/src/tui/session/session-api/controls.mjs +14 -2
  648. package/src/tui/session/session-api/integrations.mjs +9 -2
  649. package/src/tui/session/session-api/lifecycle/dispose.mjs +3 -9
  650. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  651. package/src/tui/session/session-api/lifecycle.mjs +14 -11
  652. package/src/tui/session/session-api/media.mjs +2 -0
  653. package/src/tui/session/session-api/routes.mjs +81 -57
  654. package/src/tui/session/session-api/settings.mjs +9 -3
  655. package/src/tui/session/session-api/worktree.mjs +3 -2
  656. package/src/tui/session/session-api.mjs +1 -0
  657. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  658. package/src/tui/session/session-flow/drain.mjs +6 -4
  659. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  660. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  661. package/src/tui/session/session-flow/queue.mjs +1 -1
  662. package/src/tui/session/tool-approval.mjs +20 -8
  663. package/src/tui/session/transcript-spill.mjs +14 -7
  664. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  665. package/src/tui/session/tui-steering-persist.mjs +3 -5
  666. package/src/tui/session/turn-quiet.mjs +53 -0
  667. package/src/tui/session/turn-stream.mjs +179 -32
  668. package/src/tui/session/turn.mjs +50 -36
  669. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  670. package/src/tui/spinner-meta.mjs +10 -0
  671. package/src/tui/spinner-verbs.mjs +3 -0
  672. package/src/ui/statusline.mjs +13 -3
  673. package/src/workflows/default/WORKFLOW.md +3 -1
  674. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  675. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  676. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  677. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -246
  678. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  679. package/src/session-runtime/turn/share-ops.mjs +0 -77
  680. package/src/tui/session/cross-surface-share.mjs +0 -406
  681. package/src/tui/session/live-share/delta.mjs +0 -153
  682. package/src/tui/session/live-share/mirror.mjs +0 -106
  683. package/src/tui/session/live-share/owner.mjs +0 -254
  684. package/src/tui/session/live-share/retry.mjs +0 -54
  685. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  686. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  687. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  688. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  689. package/src/tui/session/live-share/viewer.mjs +0 -99
  690. package/src/tui/session/live-share/wire.mjs +0 -44
  691. package/src/tui/session/live-share.mjs +0 -154
  692. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -0,0 +1,533 @@
1
+ /**
2
+ * A project checkout's uncommitted changes and their move into a new managed
3
+ * worktree (manager.mjs createManagedWorktree runs it under the repository
4
+ * lock, before the include entries are copied).
5
+ *
6
+ * One scope everywhere: the WHOLE repository checkout (a Project in a
7
+ * subfolder included) — detection, snapshot, apply and clear all cover every
8
+ * tracked and untracked path of the checkout, because the worktree is a full
9
+ * checkout too. Only an untracked session identity file
10
+ * (<project>/.mixdog/project.id, Mixdog metadata) is left out. Ignored files
11
+ * (dependencies, build output, env files) are never carried: they stay in
12
+ * the project folder.
13
+ *
14
+ * A snapshot is two trees taken through a temporary copy of the index, so
15
+ * the checkout is never touched while it is read: `index` (what is staged)
16
+ * and `tree` (the working tree: tracked changes, deletions and untracked
17
+ * files). The carry, in order:
18
+ * 1. refuse what cannot move faithfully (assertCarryable: an operation in
19
+ * progress, unmerged entries, dirty submodules, nested repositories,
20
+ * filtered paths such as Git LFS, untracked files above the size limit);
21
+ * 2. snapshot the project checkout and keep it at a named recovery ref
22
+ * (refs/mixdog/carry/<id>-<time>: a commit of the working tree whose
23
+ * first parent is HEAD and whose second parent is the commit of the index);
24
+ * 3. write it into the fresh worktree and verify the worktree snapshots
25
+ * identically;
26
+ * 4. re-snapshot the project checkout (unchanged since 2, else refuse), then
27
+ * clear it: `reset --hard` and delete exactly its untracked files; verify
28
+ * it is clean.
29
+ * A failure restores the project checkout from the snapshot and reports
30
+ * WORKTREE_DIRTY (reason `carry-failed`) with the kept ref. A success
31
+ * returns the ref too: the caller drops it (dropRecoveryRef) only once the
32
+ * whole session move is committed, and restores from it
33
+ * (restoreFromRecoveryRef) if anything after the carry fails.
34
+ *
35
+ * Sessions are kept out by the caller (the project folder is held and must
36
+ * be quiet), but a process outside Mixdog — an editor, a file watcher — can
37
+ * still write between the final comparison and the clear; such a write is
38
+ * not captured.
39
+ */
40
+ import { randomUUID } from 'node:crypto';
41
+ import { existsSync } from 'node:fs';
42
+ import { copyFile, lstat, rm, rmdir } from 'node:fs/promises';
43
+ import { dirname, join } from 'node:path';
44
+ import { worktreeError } from './contract.mjs';
45
+ import { firstLine, gitStatusRunner, runGit } from './git.mjs';
46
+ import { operationInProgress } from './merge.mjs';
47
+
48
+ const IDENTITY_PATH = '.mixdog/project.id';
49
+ const RECOVERY_REF_PREFIX = 'refs/mixdog/carry/';
50
+ /** Untracked files beyond these limits are refused rather than snapshotted. */
51
+ const CARRY_UNTRACKED_MAX_BYTES = 256 * 1024 * 1024;
52
+ const CARRY_UNTRACKED_MAX_FILES = 10_000;
53
+ const CHECK_ATTR_CHUNK = 200;
54
+ // Recovery commits are Mixdog's own, independent of the user's identity config.
55
+ const COMMIT_ENV = Object.freeze({
56
+ GIT_AUTHOR_NAME: 'Mixdog',
57
+ GIT_AUTHOR_EMAIL: 'mixdog@localhost',
58
+ GIT_COMMITTER_NAME: 'Mixdog',
59
+ GIT_COMMITTER_EMAIL: 'mixdog@localhost',
60
+ });
61
+
62
+ function identityPath(projectSubdir) {
63
+ return projectSubdir ? `${projectSubdir}/${IDENTITY_PATH}` : IDENTITY_PATH;
64
+ }
65
+
66
+ /** CE_INTENT_TO_ADD in an index entry's flags (`git add -N`). */
67
+ const INTENT_TO_ADD_FLAG = 0x20000000;
68
+
69
+ const PORCELAIN_LEAD_FIELDS = { 1: 8, 2: 9, u: 10 };
70
+
71
+ /**
72
+ * `git status --porcelain=v2 -z` rows: path, untracked, unmerged, submodule.
73
+ * @returns {Array<{ path: string, untracked: boolean, unmerged: boolean, submodule: boolean }>}
74
+ */
75
+ function parseStatusV2(raw) {
76
+ const fields = String(raw || '').split('\0');
77
+ const rows = [];
78
+ for (let index = 0; index < fields.length; index++) {
79
+ const field = fields[index];
80
+ if (!field) continue;
81
+ const kind = field[0];
82
+ if (kind === '?') {
83
+ rows.push({ path: field.slice(2), untracked: true, unmerged: false, submodule: false });
84
+ continue;
85
+ }
86
+ // Leading fields before the path: 1 → 8, 2 → 9 (then the original path), u → 10.
87
+ const lead = PORCELAIN_LEAD_FIELDS[kind];
88
+ if (lead === undefined) continue;
89
+ const parts = field.split(' ');
90
+ rows.push({
91
+ path: parts.slice(lead).join(' '),
92
+ untracked: false,
93
+ unmerged: kind === 'u',
94
+ submodule: String(parts[2] || '').startsWith('S'),
95
+ });
96
+ if (kind === '2') index++;
97
+ }
98
+ return rows;
99
+ }
100
+
101
+ /**
102
+ * One `git status` read of the checkout (the single source for everything
103
+ * the carry asks of it): the changed rows, their paths, the untracked
104
+ * (not ignored) files — nested repositories end in "/" — and `identity`,
105
+ * the untracked-and-not-ignored session identity file's path (left out of
106
+ * every step; status lists exactly such a file), else null.
107
+ */
108
+ async function inspectCheckout(topLevel, projectSubdir) {
109
+ const status = await runGit(topLevel, ['status', '--porcelain=v2', '-z', '--untracked-files=all'], {
110
+ readOnly: true,
111
+ });
112
+ const identity = identityPath(projectSubdir);
113
+ const all = parseStatusV2(status.stdout);
114
+ const rows = all.filter((row) => !(row.untracked && row.path === identity));
115
+ return {
116
+ rows,
117
+ files: rows.map((row) => row.path),
118
+ untracked: rows.filter((row) => row.untracked).map((row) => row.path),
119
+ identity: rows.length < all.length ? identity : null,
120
+ };
121
+ }
122
+
123
+ /**
124
+ * Uncommitted paths of the whole checkout at `topLevel`: staged, unstaged
125
+ * and untracked (every file); ignored files and an untracked identity file
126
+ * excluded.
127
+ * @returns {Promise<string[]>}
128
+ */
129
+ export async function uncommittedChanges(topLevel, projectSubdir = '') {
130
+ return (await inspectCheckout(topLevel, projectSubdir)).files;
131
+ }
132
+
133
+ function carryRefusal(message, details) {
134
+ return worktreeError(
135
+ 'WORKTREE_DIRTY',
136
+ `${message} Leave the changes in the project folder instead, or fix this and retry.`,
137
+ details
138
+ );
139
+ }
140
+
141
+ /** Paths among `paths` with a clean/smudge filter (Git LFS and custom filters). */
142
+ async function filteredPaths(topLevel, paths) {
143
+ const filtered = [];
144
+ for (let start = 0; start < paths.length; start += CHECK_ATTR_CHUNK) {
145
+ const chunk = paths.slice(start, start + CHECK_ATTR_CHUNK);
146
+ const result = await runGit(topLevel, ['check-attr', '-z', 'filter', '--', ...chunk], { readOnly: true });
147
+ const fields = result.stdout.split('\0');
148
+ for (let index = 0; index + 2 < fields.length; index += 3) {
149
+ if (!['unspecified', 'unset'].includes(fields[index + 2])) filtered.push(fields[index]);
150
+ }
151
+ }
152
+ return filtered;
153
+ }
154
+
155
+ /**
156
+ * WORKTREE_DIRTY (with a `reason`) when the checkout's changes cannot be
157
+ * carried faithfully: a merge, rebase, cherry-pick or revert in progress;
158
+ * a bisect or a pending cherry-pick/revert sequence; a sparse checkout, or
159
+ * index entries marked skip-worktree or assume-unchanged (their edits are
160
+ * invisible to git); intent-to-add entries; unmerged index entries; a
161
+ * submodule with changes; an untracked nested repository; a changed path
162
+ * under a clean/smudge filter (Git LFS); untracked files above
163
+ * CARRY_UNTRACKED_MAX_FILES / CARRY_UNTRACKED_MAX_BYTES.
164
+ * Resolves the checkout's uncommitted paths.
165
+ * @returns {Promise<string[]>}
166
+ */
167
+ export async function assertCarryable(topLevel, projectSubdir = '') {
168
+ return (await checkCarryable(topLevel, projectSubdir)).files;
169
+ }
170
+
171
+ /**
172
+ * Index entries git does not look at (their edits are invisible to status,
173
+ * so a carry would silently drop or leave them): `assumeUnchanged` (`git
174
+ * update-index --assume-unchanged`, a lowercase `ls-files -v` tag),
175
+ * `skipWorktree` (tag S/s: sparse checkouts, `--skip-worktree`), and whether
176
+ * sparse checkout is enabled at all.
177
+ */
178
+ async function hiddenIndexEntries(topLevel) {
179
+ const [listed, debug, sparse] = await Promise.all([
180
+ runGit(topLevel, ['ls-files', '-v', '-z'], { readOnly: true }),
181
+ runGit(topLevel, ['ls-files', '--debug', '-z'], { readOnly: true }),
182
+ runGit(topLevel, ['config', '--type=bool', '--get', 'core.sparseCheckout'], { readOnly: true, allowFailure: true }),
183
+ ]);
184
+ const skipWorktree = [];
185
+ const assumeUnchanged = [];
186
+ for (const entry of listed.stdout.split('\0')) {
187
+ const tag = entry[0];
188
+ if (tag === 'S' || tag === 's') skipWorktree.push(entry.slice(2));
189
+ else if (tag && tag !== tag.toUpperCase()) assumeUnchanged.push(entry.slice(2));
190
+ }
191
+ // `--debug -z` prints "path NUL stat lines ending in `flags: <hex>` LF" per
192
+ // entry; the index's own intent-to-add flag holds whatever the working file
193
+ // does (a deleted one is ".D" in status, not ".A").
194
+ const intentToAdd = [];
195
+ const segments = debug.stdout.split('\0');
196
+ let path = segments[0];
197
+ for (let index = 1; index < segments.length; index++) {
198
+ const block = /^[\s\S]*?flags: ([0-9a-f]+)\n([\s\S]*)$/.exec(segments[index]);
199
+ if (!block) break;
200
+ if (parseInt(block[1], 16) & INTENT_TO_ADD_FLAG) intentToAdd.push(path);
201
+ path = block[2];
202
+ }
203
+ return { skipWorktree, assumeUnchanged, intentToAdd, sparse: sparse.stdout.trim() === 'true' };
204
+ }
205
+
206
+ /** assertCarryable's checks; resolves the status read they were made on. */
207
+ async function checkCarryable(topLevel, projectSubdir) {
208
+ const [operation, hidden, checkout] = await Promise.all([
209
+ operationInProgress(gitStatusRunner, topLevel),
210
+ hiddenIndexEntries(topLevel),
211
+ inspectCheckout(topLevel, projectSubdir),
212
+ ]);
213
+ if (operation) {
214
+ throw carryRefusal(`A ${operation} is in progress in the project folder; finish or abort it first.`, {
215
+ reason: 'operation-in-progress',
216
+ operation,
217
+ });
218
+ }
219
+ if (hidden.sparse) {
220
+ throw carryRefusal('The project folder uses a sparse checkout, which a carry cannot reproduce.', {
221
+ reason: 'sparse-checkout',
222
+ });
223
+ }
224
+ if (hidden.skipWorktree.length) {
225
+ throw carryRefusal('Files marked skip-worktree have changes git does not see, so they cannot be carried.', {
226
+ reason: 'skip-worktree',
227
+ paths: hidden.skipWorktree.slice(0, 50),
228
+ });
229
+ }
230
+ if (hidden.assumeUnchanged.length) {
231
+ throw carryRefusal('Files marked assume-unchanged have changes git does not see, so they cannot be carried.', {
232
+ reason: 'assume-unchanged',
233
+ paths: hidden.assumeUnchanged.slice(0, 50),
234
+ });
235
+ }
236
+ const { rows, untracked } = checkout;
237
+ if (rows.some((row) => row.unmerged)) {
238
+ throw carryRefusal('The project folder has unresolved merge conflicts; resolve them first.', {
239
+ reason: 'unmerged-paths',
240
+ });
241
+ }
242
+ const submodules = rows.filter((row) => row.submodule).map((row) => row.path);
243
+ if (submodules.length) {
244
+ throw carryRefusal('Submodules with changes cannot be carried; commit or reset them first.', {
245
+ reason: 'dirty-submodules',
246
+ paths: submodules.slice(0, 50),
247
+ });
248
+ }
249
+ const { intentToAdd } = hidden;
250
+ if (intentToAdd.length) {
251
+ throw carryRefusal(
252
+ 'Files added with "git add -N" (intent to add) cannot be carried; stage or unstage them first.',
253
+ {
254
+ reason: 'intent-to-add',
255
+ paths: intentToAdd.slice(0, 50),
256
+ }
257
+ );
258
+ }
259
+ const nested = untracked.filter((path) => path.endsWith('/'));
260
+ if (nested.length) {
261
+ throw carryRefusal('The project folder has untracked nested repositories, which cannot be carried.', {
262
+ reason: 'nested-repository',
263
+ paths: nested.slice(0, 50),
264
+ });
265
+ }
266
+ if (untracked.length > CARRY_UNTRACKED_MAX_FILES) {
267
+ throw carryRefusal(
268
+ `The project folder has ${untracked.length} untracked files (limit ${CARRY_UNTRACKED_MAX_FILES}); ` +
269
+ 'add generated output to .gitignore first.',
270
+ { reason: 'too-large', files: untracked.length, maxFiles: CARRY_UNTRACKED_MAX_FILES }
271
+ );
272
+ }
273
+ let bytes = 0;
274
+ for (const rel of untracked) {
275
+ bytes += (await lstat(join(topLevel, ...rel.split('/'))).catch(() => null))?.size ?? 0;
276
+ if (bytes > CARRY_UNTRACKED_MAX_BYTES) {
277
+ throw carryRefusal(
278
+ `The project folder's untracked files exceed ${Math.round(CARRY_UNTRACKED_MAX_BYTES / 1048576)} MB; ` +
279
+ 'add large or generated files to .gitignore first.',
280
+ { reason: 'too-large', maxBytes: CARRY_UNTRACKED_MAX_BYTES }
281
+ );
282
+ }
283
+ }
284
+ const filtered = await filteredPaths(
285
+ topLevel,
286
+ rows.map((row) => row.path)
287
+ );
288
+ if (filtered.length) {
289
+ throw carryRefusal('Changed files use a git filter (such as Git LFS), which a carry cannot reproduce exactly.', {
290
+ reason: 'filtered-paths',
291
+ paths: filtered.slice(0, 50),
292
+ });
293
+ }
294
+ return checkout;
295
+ }
296
+
297
+ /**
298
+ * { head, index, tree } of the checkout at `cwd`, read through temporary
299
+ * copies of its index: the live index is copied once, and two duplicates of
300
+ * that capture (one stays as staged, one takes `add --all`) are what git
301
+ * works on side by side. `identity` (inspectCheckout's: only ever an
302
+ * untracked, not ignored path — naming an ignored one, even as an exclude,
303
+ * makes `add` refuse) is left out.
304
+ */
305
+ async function snapshot(cwd, identity) {
306
+ const [head, indexFile] = (
307
+ await runGit(cwd, ['rev-parse', '--path-format=absolute', 'HEAD^{commit}', '--git-path', 'index'])
308
+ ).stdout
309
+ .split(/\r?\n/)
310
+ .map((line) => line.trim());
311
+ const temp = `${indexFile}.mixdog-carry-${randomUUID()}`;
312
+ const capturedIndex = `${temp}-captured`;
313
+ const stagedIndex = `${temp}-staged`;
314
+ const treeIndex = `${temp}-tree`;
315
+ const writeTree = async (file) =>
316
+ firstLine((await runGit(cwd, ['write-tree'], { env: { GIT_INDEX_FILE: file } })).stdout);
317
+ const pathspec = identity ? ['--', '.', `:(exclude,literal)${identity}`] : ['--', '.'];
318
+ try {
319
+ // The live index is read once; both working copies come from that capture.
320
+ if (existsSync(indexFile)) await copyFile(indexFile, capturedIndex);
321
+ else await runGit(cwd, ['read-tree', head], { env: { GIT_INDEX_FILE: capturedIndex } });
322
+ await Promise.all([copyFile(capturedIndex, stagedIndex), copyFile(capturedIndex, treeIndex)]);
323
+ const settled = await Promise.allSettled([
324
+ writeTree(stagedIndex),
325
+ runGit(cwd, ['add', '--all', ...pathspec], { env: { GIT_INDEX_FILE: treeIndex } }).then(() =>
326
+ writeTree(treeIndex)
327
+ ),
328
+ ]);
329
+ const failed = settled.find((result) => result.status === 'rejected');
330
+ if (failed) throw failed.reason;
331
+ return { head, index: settled[0].value, tree: settled[1].value };
332
+ } finally {
333
+ for (const file of [capturedIndex, stagedIndex, treeIndex]) {
334
+ await rm(file, { force: true });
335
+ await rm(`${file}.lock`, { force: true });
336
+ }
337
+ }
338
+ }
339
+
340
+ const sameSnapshot = (left, right) =>
341
+ !!left && !!right && left.head === right.head && left.index === right.index && left.tree === right.tree;
342
+
343
+ async function keepRecoveryRef(cwd, snap, label) {
344
+ const commit = async (tree, parents, message) =>
345
+ firstLine(
346
+ (
347
+ await runGit(cwd, ['commit-tree', tree, ...parents.flatMap((parent) => ['-p', parent]), '-m', message], {
348
+ env: COMMIT_ENV,
349
+ })
350
+ ).stdout
351
+ );
352
+ const indexCommit = await commit(snap.index, [snap.head], `mixdog: staged changes carried into worktree ${label}`);
353
+ const stateCommit = await commit(
354
+ snap.tree,
355
+ [snap.head, indexCommit],
356
+ `mixdog: uncommitted changes (untracked included) carried into worktree ${label}`
357
+ );
358
+ const ref = `${RECOVERY_REF_PREFIX}${label}-${Date.now()}`;
359
+ await runGit(cwd, ['update-ref', '-m', `mixdog: carry into worktree ${label}`, ref, stateCommit]);
360
+ return ref;
361
+ }
362
+
363
+ /** Make the checkout at `cwd` (index and files) exactly `snap`; HEAD stays. */
364
+ async function writeSnapshot(cwd, snap, { reset = false } = {}) {
365
+ await runGit(
366
+ cwd,
367
+ reset ? ['read-tree', '--reset', '-u', snap.tree] : ['read-tree', '-u', '-m', snap.head, snap.tree]
368
+ );
369
+ // The index now equals the working tree (`snap.tree`); moving it on to the
370
+ // staged state as a two-tree switch (-i: files are already as wanted) keeps
371
+ // the stat data of every entry that does not change. A plain
372
+ // `read-tree <index>` drops all of it, and the `update-index --refresh` it
373
+ // then needs re-reads every file of the checkout (seconds on a large one).
374
+ await runGit(cwd, ['read-tree', '-m', '-i', snap.tree, snap.index]);
375
+ }
376
+
377
+ /** Restore `snap` into the project checkout; true once it snapshots exactly as `snap`. */
378
+ async function restoreSnapshot(topLevel, identity, snap) {
379
+ let failure = null;
380
+ try {
381
+ await writeSnapshot(topLevel, snap, { reset: true });
382
+ } catch (error) {
383
+ failure = error;
384
+ }
385
+ // A clear that failed before changing anything needs no restore at all.
386
+ if (sameSnapshot(await snapshot(topLevel, identity).catch(() => null), snap)) return null;
387
+ return failure || new Error('the project folder does not match the snapshot after the restore');
388
+ }
389
+
390
+ /** Tracked files back to HEAD, then exactly `untracked` deleted (and the folders they leave empty). */
391
+ async function clearCheckout(topLevel, untracked) {
392
+ await runGit(topLevel, ['reset', '--hard', '--quiet', 'HEAD']);
393
+ const folders = new Set();
394
+ for (const rel of untracked) {
395
+ const path = join(topLevel, ...rel.split('/'));
396
+ await rm(path, { force: true });
397
+ for (let dir = dirname(path); dir.length > topLevel.length && dir.startsWith(topLevel); dir = dirname(dir)) {
398
+ folders.add(dir);
399
+ }
400
+ }
401
+ for (const dir of [...folders].sort((a, b) => b.length - a.length)) {
402
+ await rmdir(dir).catch(() => {});
403
+ }
404
+ }
405
+
406
+ /** Write `snap` into the fresh worktree and verify it snapshots identically. */
407
+ async function applyToWorktree(worktreePath, identity, snap) {
408
+ await writeSnapshot(worktreePath, snap);
409
+ if (!sameSnapshot(await snapshot(worktreePath, identity), snap)) {
410
+ throw new Error('the worktree does not match the project folder after the changes were applied');
411
+ }
412
+ }
413
+
414
+ function recoveryHint(ref) {
415
+ return `"git read-tree --reset -u ${ref}" then "git read-tree ${ref}^2" (the staged state)`;
416
+ }
417
+
418
+ /**
419
+ * Move the uncommitted changes of the project checkout at `topLevel` into
420
+ * the fresh worktree at `worktreePath` (a checkout of `baseCommit`).
421
+ * Nothing to move resolves `{ count: 0, recoveryRef: null }` without
422
+ * touching either. On success the recovery ref is kept for the caller.
423
+ * @param {{ topLevel: string, projectSubdir?: string, worktreePath: string, baseCommit: string, label: string }} input
424
+ * @returns {Promise<{ count: number, recoveryRef: string|null }>}
425
+ */
426
+ export async function carryUncommittedChanges({ topLevel, projectSubdir = '', worktreePath, baseCommit, label }) {
427
+ // Checked again right before acting (the worktree took a while to create);
428
+ // the same status read serves the file list, the untracked files to clear
429
+ // and the identity file to leave out.
430
+ const { files, untracked, identity } = await checkCarryable(topLevel, projectSubdir);
431
+ if (!files.length) return { count: 0, recoveryRef: null };
432
+ const snap = await snapshot(topLevel, identity);
433
+ if (snap.head !== baseCommit) {
434
+ throw carryRefusal('The project folder moved to another commit while the worktree was created.', {
435
+ reason: 'head-moved',
436
+ head: snap.head,
437
+ baseCommit,
438
+ });
439
+ }
440
+ // The recovery ref is written while the worktree takes the snapshot; it is
441
+ // awaited before anything in the project folder is touched.
442
+ const keeping = keepRecoveryRef(topLevel, snap, label);
443
+ const applying = applyToWorktree(worktreePath, identity, snap).then(
444
+ () => null,
445
+ (error) => error
446
+ );
447
+ let recoveryRef;
448
+ try {
449
+ recoveryRef = await keeping;
450
+ } catch (error) {
451
+ await applying;
452
+ throw error;
453
+ }
454
+ let touched = false;
455
+ try {
456
+ const applyError = await applying;
457
+ if (applyError) throw applyError;
458
+ if (!sameSnapshot(await snapshot(topLevel, identity), snap)) {
459
+ throw new Error('the project folder changed while its changes were being moved');
460
+ }
461
+ touched = true;
462
+ await clearCheckout(topLevel, untracked);
463
+ const left = (await inspectCheckout(topLevel, projectSubdir)).files;
464
+ if (left.length) throw new Error(`${left.length} changed path(s) could not be cleared from the project folder`);
465
+ } catch (cause) {
466
+ const restoreError = touched ? await restoreSnapshot(topLevel, identity, snap) : null;
467
+ const reason = String(cause?.message || cause);
468
+ throw worktreeError(
469
+ 'WORKTREE_DIRTY',
470
+ restoreError
471
+ ? `Moving the uncommitted changes into the worktree failed (${reason}) and the project folder could not be ` +
472
+ `restored; recover them in the project folder with ${recoveryHint(recoveryRef)}.`
473
+ : `Moving the uncommitted changes into the worktree failed (${reason}); the project folder still has them ` +
474
+ `(a copy is kept at ${recoveryRef}).`,
475
+ {
476
+ reason: 'carry-failed',
477
+ recoveryRef,
478
+ restored: !restoreError,
479
+ count: files.length,
480
+ cause: reason,
481
+ ...(restoreError ? { restoreError: String(restoreError.message || restoreError) } : {}),
482
+ }
483
+ );
484
+ }
485
+ return { count: files.length, recoveryRef };
486
+ }
487
+
488
+ function assertRecoveryRef(ref) {
489
+ if (typeof ref !== 'string' || !ref.startsWith(RECOVERY_REF_PREFIX) || ref.includes('..')) {
490
+ throw new TypeError('recoveryRef must be a mixdog carry ref.');
491
+ }
492
+ }
493
+
494
+ /**
495
+ * Put the changes kept at `recoveryRef` back into the project checkout
496
+ * after a carry whose session move failed later. Only a checkout still
497
+ * clean and on the ref's base commit is restored (nothing there can be
498
+ * overwritten); otherwise WORKTREE_DIRTY (reason `carry-restore-refused`)
499
+ * and the ref stays. A restored checkout keeps the ref (dropRecoveryRef).
500
+ */
501
+ export async function restoreFromRecoveryRef(topLevel, projectSubdir, recoveryRef) {
502
+ assertRecoveryRef(recoveryRef);
503
+ const rev = async (spec) => firstLine((await runGit(topLevel, ['rev-parse', '--verify', spec])).stdout);
504
+ const snap = {
505
+ head: await rev(`${recoveryRef}^1`),
506
+ index: await rev(`${recoveryRef}^2^{tree}`),
507
+ tree: await rev(`${recoveryRef}^{tree}`),
508
+ };
509
+ const head = await rev('HEAD^{commit}');
510
+ const { files: dirty, identity } = await inspectCheckout(topLevel, projectSubdir);
511
+ if (head !== snap.head || dirty.length) {
512
+ throw worktreeError(
513
+ 'WORKTREE_DIRTY',
514
+ `The carried changes could not be put back: the project folder changed meanwhile. Recover them with ${recoveryHint(recoveryRef)}.`,
515
+ { reason: 'carry-restore-refused', recoveryRef, count: dirty.length }
516
+ );
517
+ }
518
+ const failure = await restoreSnapshot(topLevel, identity, snap);
519
+ if (failure) {
520
+ throw worktreeError(
521
+ 'WORKTREE_DIRTY',
522
+ `The carried changes could not be put back (${failure.message || failure}). Recover them with ${recoveryHint(recoveryRef)}.`,
523
+ { reason: 'carry-restore-failed', recoveryRef }
524
+ );
525
+ }
526
+ return { restored: true, recoveryRef };
527
+ }
528
+
529
+ /** Forget a carry's recovery ref once the move it protected is committed. */
530
+ export async function dropRecoveryRef(topLevel, recoveryRef) {
531
+ assertRecoveryRef(recoveryRef);
532
+ await runGit(topLevel, ['update-ref', '-d', recoveryRef], { allowFailure: true });
533
+ }
@@ -30,6 +30,8 @@ export const WORKTREE_ERROR_CODES = Object.freeze({
30
30
  WORKTREE_DISK_INSUFFICIENT: 'WORKTREE_DISK_INSUFFICIENT',
31
31
  WORKTREE_LARGE_REPO_CONFIRM: 'WORKTREE_LARGE_REPO_CONFIRM',
32
32
  WORKTREE_DIRTY: 'WORKTREE_DIRTY',
33
+ WORKTREE_DIRTY_CHOICE: 'WORKTREE_DIRTY_CHOICE',
34
+ WORKTREE_FILE_RECOVERY: 'WORKTREE_FILE_RECOVERY',
33
35
  WORKTREE_UNMERGED: 'WORKTREE_UNMERGED',
34
36
  WORKTREE_MISSING: 'WORKTREE_MISSING',
35
37
  WORKTREE_UNSUPPORTED_REPO: 'WORKTREE_UNSUPPORTED_REPO',
@@ -55,6 +57,10 @@ const DEFAULT_ERROR_MESSAGES = Object.freeze({
55
57
  WORKTREE_DISK_INSUFFICIENT: 'Not enough free disk space to create the worktree.',
56
58
  WORKTREE_LARGE_REPO_CONFIRM: 'The repository is large; confirm before creating a worktree.',
57
59
  WORKTREE_DIRTY: 'The worktree has uncommitted changes.',
60
+ WORKTREE_DIRTY_CHOICE:
61
+ 'The project folder has uncommitted changes; choose whether to leave them there or carry them into the worktree.',
62
+ WORKTREE_FILE_RECOVERY:
63
+ 'Retained file recovery records block worktree removal, including force. Inspect and explicitly delete them first.',
58
64
  WORKTREE_UNMERGED: 'The worktree branch has commits that are not merged.',
59
65
  WORKTREE_MISSING: 'The managed worktree no longer exists.',
60
66
  WORKTREE_UNSUPPORTED_REPO: 'This repository does not support managed worktrees.',
@@ -100,6 +106,31 @@ export function worktreeError(code, message, details) {
100
106
  return new WorktreeError(code, message, details);
101
107
  }
102
108
 
109
+ /**
110
+ * Why a workspace management action (merge, folder cleanup, branch deletion,
111
+ * checkout) was refused because something still uses an affected folder. Every
112
+ * surface shows the same meaning: the runtime throws exactly this text (TUI,
113
+ * remote) and the desktop translates the same English source string.
114
+ * @readonly
115
+ */
116
+ export const WORKTREE_REFUSAL_REASONS = Object.freeze({
117
+ 'workspace-retained': 'Sessions still retain this folder. Close or remove those sessions before cleaning it up.',
118
+ 'workspace-lease': 'A runtime still holds this folder. Stop its work and release the workspace first.',
119
+ 'folder-in-use': 'Sessions or workers still use an affected folder. Finish or move them before continuing.',
120
+ 'folder-shell-jobs': 'Background commands still use an affected folder. Wait for them or stop them first.',
121
+ 'workspace-admission-unreadable': 'Workspace activity cannot be verified. Nothing was changed.',
122
+ });
123
+
124
+ /**
125
+ * WORKTREE_BUSY_JOBS with the shared text of `reason` (WORKTREE_REFUSAL_REASONS).
126
+ * @param {keyof typeof WORKTREE_REFUSAL_REASONS} reason
127
+ * @param {Record<string, unknown>} [details]
128
+ */
129
+ export function worktreeRefusal(reason, details = {}) {
130
+ if (!Object.hasOwn(WORKTREE_REFUSAL_REASONS, reason)) throw new TypeError(`Unknown refusal reason "${reason}".`);
131
+ return worktreeError('WORKTREE_BUSY_JOBS', WORKTREE_REFUSAL_REASONS[reason], { ...details, reason });
132
+ }
133
+
103
134
  /**
104
135
  * @param {unknown} error
105
136
  * @param {WorktreeErrorCode} [code] When given, also require this code.
@@ -508,6 +539,35 @@ export function runRootReleasers(root, ctx) {
508
539
  * `remove-worktree`: delete the retained worktree after dirty/unmerged checks.
509
540
  */
510
541
 
542
+ /**
543
+ * What happens to the project folder's uncommitted changes (tracked and
544
+ * untracked, ignored files excluded) when a session moves into a new
545
+ * worktree: `leave` keeps them in the project folder (the worktree starts at
546
+ * its HEAD); `carry` moves them into the worktree.
547
+ * @typedef {'leave'|'carry'} WorktreeCarryChoice
548
+ */
549
+ export const WORKTREE_CARRY_CHOICES = Object.freeze(['leave', 'carry']);
550
+
551
+ /**
552
+ * requestWorktree input.
553
+ * @typedef {object} RequestWorktreeInput
554
+ * @property {string|null} [name]
555
+ * @property {number} [expectedRevision]
556
+ * @property {boolean} [confirmLarge] Acknowledges WORKTREE_LARGE_REPO_CONFIRM.
557
+ * @property {boolean} [provision] Create and enter the worktree now (the session moves) instead of at the next send.
558
+ * @property {WorktreeCarryChoice} [carryChanges] Answers WORKTREE_DIRTY_CHOICE (`details: { count, paths }`,
559
+ * `paths` capped to a preview), which a provisioning request without it raises while the project checkout has
560
+ * uncommitted changes. Scope is the whole repository checkout; ignored files (dependencies, build output, env
561
+ * files) are never carried and stay in the project folder.
562
+ */
563
+
564
+ /**
565
+ * mergeWorkspace's `cleanup` after a successful merge: `removed: false` keeps
566
+ * the worktree and reports why (`code` of the refusal, e.g. WORKTREE_DIRTY,
567
+ * WORKTREE_UNMERGED, WORKTREE_REMOVAL_INCOMPLETE); the merge stands.
568
+ * @typedef {{ removed: boolean, code?: string, message?: string }} MergeCleanupResult
569
+ */
570
+
511
571
  /**
512
572
  * @typedef {object} WorkspaceTransitionOptions
513
573
  * @property {number} [expectedRevision] Reject with WORKTREE_REVISION_CONFLICT when the session moved on.