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,49 @@
1
+ import { spawn } from 'node:child_process';
2
+
3
+ /**
4
+ * Async child run with spawnSync's result shape ({ status, signal, stdout,
5
+ * stderr, error }) so the PG runtime's tar / initdb / pg_ctl steps never block
6
+ * the event loop of the process hosting them (the daemon serves every client
7
+ * from one loop). `timeoutMs` > 0 kills the child and reports ETIMEDOUT, like
8
+ * spawnSync's `timeout`. Never rejects.
9
+ */
10
+ export function runChild(file, args, { timeoutMs = 0, ...options } = {}) {
11
+ return new Promise((resolve) => {
12
+ const stdout = [];
13
+ const stderr = [];
14
+ let settled = false;
15
+ let timer = null;
16
+ let error = null;
17
+ const finish = (status, signal) => {
18
+ if (settled) return;
19
+ settled = true;
20
+ if (timer) clearTimeout(timer);
21
+ resolve({ status, signal, stdout: Buffer.concat(stdout), stderr: Buffer.concat(stderr), error });
22
+ };
23
+ let child;
24
+ try {
25
+ child = spawn(file, args, { windowsHide: true, ...options, stdio: ['ignore', 'pipe', 'pipe'] });
26
+ } catch (spawnError) {
27
+ error = spawnError;
28
+ finish(null, null);
29
+ return;
30
+ }
31
+ child.stdout?.on('data', (chunk) => stdout.push(chunk));
32
+ child.stderr?.on('data', (chunk) => stderr.push(chunk));
33
+ child.once('error', (childError) => {
34
+ error ??= childError;
35
+ finish(null, null);
36
+ });
37
+ // 'close' (not 'exit'): stdio is flushed, so captured output is complete.
38
+ child.once('close', (status, signal) => finish(status, signal));
39
+ if (timeoutMs > 0) {
40
+ timer = setTimeout(() => {
41
+ error = Object.assign(new Error(`${file} timed out after ${timeoutMs}ms`), { code: 'ETIMEDOUT' });
42
+ try {
43
+ child.kill();
44
+ } catch {}
45
+ finish(null, 'SIGTERM');
46
+ }, timeoutMs);
47
+ }
48
+ });
49
+ }
@@ -6,6 +6,9 @@ import { __mixdogMemoryLog } from '../memory-log.mjs';
6
6
  * Public API:
7
7
  * ensurePgInstance(dataDir) → Promise<{ host, port, runtimeDir, pgdataDir }>
8
8
  * stopPgForShutdown() → Promise<void> — call from server-main.mjs shutdown()
9
+ * stopOwnedPgForShutdown() → Promise<boolean> — a host stopping the cluster it ensured
10
+ * installOwnedPgExitStop() → void — last-chance sync stop on process exit
11
+ * pgClusterExists(dataDir) → boolean — initdb already ran for dataDir
9
12
  *
10
13
  * Uses process.mjs for startPg / stopPg / healthcheckPg (lazy-imported).
11
14
  *
@@ -17,7 +20,7 @@ import { __mixdogMemoryLog } from '../memory-log.mjs';
17
20
  */
18
21
 
19
22
  import { createServer } from 'node:net';
20
- import { unlinkSync, readFileSync, writeFileSync, renameSync, statSync, mkdirSync } from 'node:fs';
23
+ import { existsSync, unlinkSync, readFileSync, writeFileSync, renameSync, statSync, mkdirSync } from 'node:fs';
21
24
  import { join, resolve } from 'node:path';
22
25
  import {
23
26
  discoveryPath as _pgDiscoveryPath,
@@ -51,6 +54,10 @@ let _live = null;
51
54
  let _ensureInFlight = null;
52
55
  /** Per-process flag — postgresql.conf reconcile attempted once per supervisor. */
53
56
  let _v2ReconcileTried = false;
57
+ /** Set by stopOwnedPgForShutdown: this process is exiting, no further ensure. */
58
+ let _ownedShutdownStarted = false;
59
+ /** installOwnedPgExitStop registers its 'exit' hook once. */
60
+ let _ownedExitStopInstalled = false;
54
61
 
55
62
  // ── Constants ────────────────────────────────────────────────────────────────
56
63
  const PG_PORT_MIN = 55432;
@@ -537,9 +544,20 @@ async function tryReusePgInstance({ pgdata, runtimeDir, healthcheckPg, source =
537
544
 
538
545
  // ── Internal: full ensure logic (runs exclusively via _ensureInFlight) ────────
539
546
 
547
+ function _pgdataDir(dataDir) {
548
+ return join(dataDir, 'pgdata');
549
+ }
550
+
551
+ /** True once `dataDir` holds an initialized cluster (initdb ran). Before that
552
+ * no store in it can hold rows, so a read may answer empty without starting
553
+ * PostgreSQL — the first start downloads/extracts the runtime and runs initdb. */
554
+ export function pgClusterExists(dataDir) {
555
+ return existsSync(join(_pgdataDir(dataDir), 'PG_VERSION'));
556
+ }
557
+
540
558
  async function _doEnsure(dataDir) {
541
559
  const { healthcheckPg } = await _getPgProc();
542
- const pgdata = join(dataDir, 'pgdata');
560
+ const pgdata = _pgdataDir(dataDir);
543
561
 
544
562
  // Resolve runtimeDir via runtime-fetcher (cache-hits immediately when already downloaded).
545
563
  const { ensureRuntime } = await import('../runtime-fetcher.mjs');
@@ -552,7 +570,7 @@ async function _doEnsure(dataDir) {
552
570
  _v2ReconcileTried = true;
553
571
  try {
554
572
  const { reconcileConfV2 } = await _getPgProc();
555
- reconcileConfV2(runtimeDir, pgdata);
573
+ await reconcileConfV2(runtimeDir, pgdata);
556
574
  } catch (e) {
557
575
  __mixdogMemoryLog(`[supervisor-pg] reconcileConfV2 error (non-fatal): ${e?.message}\n`);
558
576
  }
@@ -699,6 +717,12 @@ async function _doEnsure(dataDir) {
699
717
  * @returns {Promise<{ host: string, port: number, runtimeDir: string, pgdataDir: string }>}
700
718
  */
701
719
  export function ensurePgInstance(dataDir) {
720
+ // Once this process began stopping the cluster it owns, nothing may start
721
+ // or re-attach one: a late connection-loss recovery would otherwise bring a
722
+ // postmaster back up right after the stop and leave it running past exit.
723
+ if (_ownedShutdownStarted) {
724
+ return Promise.reject(new Error('[supervisor-pg] process is shutting down; PG start refused'));
725
+ }
702
726
  // Every route to a live PG passes here — start, attach, reuse and the
703
727
  // in-process fast path — so this is the one place the sweep cannot be
704
728
  // skipped. Latched internally, so repeated calls schedule it only once.
@@ -804,5 +828,89 @@ export function stopPgForShutdownSync() {
804
828
  } catch {}
805
829
  // The postmaster is gone, so drop its advert too (same guarded clear as the
806
830
  // async path: never another live owner's); otherwise pg.json outlives the run.
807
- patchActiveInstance({ pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null }, { timeoutMs: 1000 });
831
+ patchActiveInstance(
832
+ { pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null },
833
+ { timeoutMs: 1000 }
834
+ );
835
+ }
836
+
837
+ // ── Owned-cluster shutdown (a host that starts PG through its stores) ────────
838
+ //
839
+ // `pg_ctl start` daemonizes the postmaster outside the starting process tree,
840
+ // so nothing ends it when that process exits: the process that brought a
841
+ // cluster up (or adopted one whose owner died) owns stopping it. Unlike
842
+ // stopPgForShutdown above, these never act on a cluster this process did not
843
+ // ensure, and never on one a different live process owns (pg_owner_pid) —
844
+ // that owner stops it on its own exit.
845
+
846
+ /** `_live` cluster's advert is held by another live process. */
847
+ function _liveClusterForeignOwned(snap) {
848
+ let advert = null;
849
+ try {
850
+ advert = _readPgAdvert();
851
+ } catch {}
852
+ if (!advert?.pg_pgdata || resolve(advert.pg_pgdata) !== resolve(snap.pgdata)) return false;
853
+ return _pgAdvertIsForeignLive(advert);
854
+ }
855
+
856
+ /**
857
+ * Graceful, bounded stop (`pg_ctl stop -m fast`) of the cluster this process
858
+ * ensured. Latches the process into shutdown first, so no later ensure can
859
+ * start another one. Resolves true when a cluster was stopped; rejects when
860
+ * pg_ctl failed (the 'exit' hook then retries synchronously).
861
+ */
862
+ export async function stopOwnedPgForShutdown({ timeoutMs = 6_000 } = {}) {
863
+ _ownedShutdownStarted = true;
864
+ // A start still under way (pg_ctl start already spawned, readiness pending)
865
+ // publishes its cluster only when it settles; stopping before that would
866
+ // miss the postmaster it is about to own. Bounded: a cold first start can
867
+ // be a download.
868
+ if (_ensureInFlight) {
869
+ let timer = null;
870
+ await Promise.race([
871
+ _ensureInFlight.catch(() => {}),
872
+ new Promise((resolveWait) => {
873
+ timer = setTimeout(resolveWait, timeoutMs);
874
+ timer.unref?.();
875
+ }),
876
+ ]);
877
+ clearTimeout(timer);
878
+ }
879
+ const snap = _live;
880
+ if (!snap) return false;
881
+ if (_liveClusterForeignOwned(snap)) {
882
+ __mixdogMemoryLog(`[supervisor-pg] PG on port ${snap.port} is owned by another live process; leaving it running\n`);
883
+ _live = null;
884
+ return false;
885
+ }
886
+ const { stopPg } = await _getPgProc();
887
+ await stopPg({ runtimeDir: snap.runtimeDir, pgdataDir: snap.pgdata, timeoutMs });
888
+ _live = null;
889
+ __mixdogMemoryLog(`[supervisor-pg] owned PG on port ${snap.port} stopped on shutdown\n`);
890
+ patchActiveInstance(
891
+ { pg_port: null, pg_started_at: null, pg_pgdata: null, pg_runtime_dir: null },
892
+ { timeoutMs: 2000, syncRetries: 1 }
893
+ );
894
+ return true;
895
+ }
896
+
897
+ /** Last-chance synchronous stop for a process 'exit' hook: acts only while the
898
+ * cluster this process ensured is still recorded as live (an exit the async
899
+ * stop never reached, or one where it failed). */
900
+ export function stopOwnedPgForShutdownSync() {
901
+ if (!_live) return;
902
+ stopPgForShutdownSync();
903
+ _live = null;
904
+ }
905
+
906
+ /** Register stopOwnedPgForShutdownSync on process 'exit', once. Cheap when this
907
+ * process never ensures a cluster: the hook is then a no-op. */
908
+ export function installOwnedPgExitStop() {
909
+ if (_ownedExitStopInstalled) return;
910
+ _ownedExitStopInstalled = true;
911
+ process.on('exit', () => {
912
+ try {
913
+ stopOwnedPgForShutdownSync();
914
+ } catch {}
915
+ });
808
916
  }
@@ -32,7 +32,7 @@ import {
32
32
  } from 'node:fs';
33
33
  import { basename, dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
34
34
  import { fileURLToPath } from 'node:url';
35
- import { spawnSync } from 'node:child_process';
35
+ import { runChild } from './pg/run-child.mjs';
36
36
  import { renameWithRetrySync, writeFileAtomicSync, writeJsonAtomicSync } from '../../shared/atomic-file.mjs';
37
37
  import { downloadToFileWithRetry } from '../../shared/bounded-download.mjs';
38
38
  import { platformKey, platformKeyCandidates, verifySha256File } from '../../shared/native-asset.mjs';
@@ -250,21 +250,23 @@ export function _validateRuntimeTarEntries(entries, details, stagingBase) {
250
250
  }
251
251
  }
252
252
 
253
- export function _extractRuntimeTarGz(tarPath, destDir, stagingBase) {
253
+ // Async throughout: the first boot extracts the whole PG runtime here, and a
254
+ // synchronous tar froze the daemon loop for seconds.
255
+ export async function _extractRuntimeTarGz(tarPath, destDir, stagingBase) {
254
256
  mkdirSync(destDir, { recursive: true });
255
257
  const resolvedTarPath = resolve(tarPath);
256
- const tarOptions = { cwd: dirname(resolvedTarPath), stdio: 'pipe', windowsHide: true };
258
+ const tarOptions = { cwd: dirname(resolvedTarPath) };
257
259
  const tarName = basename(resolvedTarPath);
258
260
 
259
261
  // List entries first and validate — reject traversal, external links, and
260
262
  // special files before the extractor can materialize archive content.
261
263
  // Keep the archive argument relative to cwd: GNU tar otherwise interprets
262
264
  // an absolute Windows path such as C:\runtime.tar.gz as a remote host path.
263
- const listResult = spawnSync('tar', ['-tzf', tarName], tarOptions);
265
+ const listResult = await runChild('tar', ['-tzf', tarName], tarOptions);
264
266
  if (listResult.status !== 0) {
265
267
  throw new Error(`[runtime-fetcher] tar list failed: ${listResult.stderr?.toString() || 'unknown'}`);
266
268
  }
267
- const detailResult = spawnSync('tar', ['-tvzf', tarName], tarOptions);
269
+ const detailResult = await runChild('tar', ['-tvzf', tarName], tarOptions);
268
270
  if (detailResult.status !== 0) {
269
271
  throw new Error(`[runtime-fetcher] tar detail list failed: ${detailResult.stderr?.toString() || 'unknown'}`);
270
272
  }
@@ -274,10 +276,7 @@ export function _extractRuntimeTarGz(tarPath, destDir, stagingBase) {
274
276
 
275
277
  const resolvedDestDir = resolve(destDir);
276
278
  const extractionArchive = relative(resolvedDestDir, resolvedTarPath);
277
- const r = spawnSync('tar', ['-xzf', extractionArchive], {
278
- ...tarOptions,
279
- cwd: resolvedDestDir,
280
- });
279
+ const r = await runChild('tar', ['-xzf', extractionArchive], { cwd: resolvedDestDir });
281
280
  if (r.status !== 0) {
282
281
  throw new Error(`[runtime-fetcher] tar extraction failed: ${r.stderr?.toString() || 'unknown error'}`);
283
282
  }
@@ -438,9 +437,9 @@ const runtimeCache = new Map();
438
437
 
439
438
  // One-shot tar availability probe; result cached after first call.
440
439
  let _tarProbed = false;
441
- function probeTar() {
440
+ async function probeTar() {
442
441
  if (_tarProbed) return;
443
- const r = spawnSync('tar', ['--version'], { stdio: 'pipe', windowsHide: true });
442
+ const r = await runChild('tar', ['--version']);
444
443
  if (r.status !== 0 || r.error) {
445
444
  throw new Error(
446
445
  '[runtime-fetcher] `tar` not found or not executable. ' +
@@ -532,7 +531,7 @@ async function installRuntimeVersion({ runtimeBaseDir, url, sha256, size, versio
532
531
  // tar is only required for the download/extract path. Probe here (not at
533
532
  // ensureRuntime entry) so a machine without tar can still reuse an
534
533
  // already-extracted, sha-matching cached runtime via the fast path.
535
- probeTar();
534
+ await probeTar();
536
535
 
537
536
  __mixdogMemoryLog(`[runtime-fetcher] downloading runtime ${version} for ${pkey} (~${size} bytes) …\n`);
538
537
 
@@ -574,7 +573,7 @@ async function installRuntimeVersion({ runtimeBaseDir, url, sha256, size, versio
574
573
  }
575
574
  }
576
575
  if (!downloadOk && lastDownloadErr) throw lastDownloadErr;
577
- _extractRuntimeTarGz(tarPath, stagingDir, stagingDir);
576
+ await _extractRuntimeTarGz(tarPath, stagingDir, stagingDir);
578
577
  } finally {
579
578
  try {
580
579
  rmSync(tarPath, { force: true });
@@ -53,7 +53,21 @@ export function createTranscriptIngest({
53
53
  return run;
54
54
  }
55
55
 
56
+ /** A deleted session's transcripts: wait out any ingest of each file, then
57
+ * drop its persisted read position. Returns the number of entries removed. */
58
+ async function forgetTranscripts(transcriptPaths) {
59
+ let removed = 0;
60
+ for (const transcriptPath of transcriptPaths) {
61
+ const resolved = path.resolve(transcriptPath);
62
+ await tails.get(resolved);
63
+ for (const key of new Set([transcriptPath, resolved])) if (offsets.forget(key)) removed += 1;
64
+ }
65
+ if (removed > 0) await offsets.persist();
66
+ return removed;
67
+ }
68
+
56
69
  return {
70
+ forgetTranscripts,
57
71
  loadTranscriptOffsets: offsets.load,
58
72
  persistTranscriptOffsets: offsets.persist,
59
73
  ingestTranscriptFile,
@@ -10,7 +10,7 @@ Microsoft Office installed.
10
10
  | --- | --- |
11
11
  | `(root)` | Public surface only: `index.mjs` (tool executor), `tool-defs.mjs`, `capabilities.mjs`. Test suites and `office-test-support.mjs` also live here. |
12
12
  | `authoring/` | The `author` action — the only way a deck is made: `pptx-script-contract.mjs` (what a script may require and how long it may run; the model-facing guide is the built-in `pptx` skill in `src/defaults/skills/pptx`), `pptx-script-runner.mjs` (runs a pptxgenjs script in-process with a scoped `require`, injecting `MEASURE` and `ICON`), `pptx-script-normalize.mjs` (paragraph properties, hanging punctuation, accent-series merge), `pptx-html-runner.mjs` + `pptx-html-measure.mjs` + `pptx-html-build.mjs` (the HTML input: a local Chrome/Edge lays the slides out, the measurement becomes native text boxes, shapes, tables, charts, and pictures, and the browser's pages are kept for the HTML-beside-PPTX comparison), `pptx-author-action.mjs` (writes the deck, opens an `authored` session, renders, returns the composition receipt and contact sheet), `pptx-receipt.mjs`, `pptx-contact-sheet.mjs`, `pptx-icons.mjs` + `pptx-icons.json` (offline Lucide subset built by `npm run office:icons`), `pptx-brief.mjs`. Sits beside the root and imports `core`. |
13
- | `core/` | Sessions, transactions, journal, snapshot pagination, tabular sessions, and the action pipeline (`office-actions-*.mjs`). |
13
+ | `core/` | Sessions, transactions, journal, snapshot pagination, tabular sessions, the action pipeline (`office-actions-*.mjs`), and the write ledger (`office-write-ledger.mjs`: every route that writes a workspace file reports it with `noteOfficeWrite` before writing; `index.mjs` records the reported files that changed in the session's turn review as its own changes). |
14
14
  | `com/` | Windows-native path: `com-adapter.mjs` plus the PowerShell hosts `office-com-host.ps1` and `office-com-session-host.ps1`. |
15
15
  | `portable/` | OOXML read/write without Office, package validation, XLSX contracts, the backend-neutral XLSX audits shared by portable `issues`, Excel's `issues` merge, and the quality review (`xlsx-audit-support.mjs` cell reading and the capped issue list, `xlsx-sheet-hygiene.mjs` every-profile hygiene and layout information, `xlsx-formula-audit.mjs` the financial-model discipline and the orchestration), the workbook conventions summary a snapshot carries (`xlsx-conventions.mjs`), LibreOffice recalculation with its error summary (`portable-soffice.mjs` holds one private user profile for the life of the process, runs conversions one at a time behind it, and drops the profile after a failed run; `xlsx-recalculate.mjs` asks LibreOffice first and falls back to `xlsx-formula-engine.mjs`, which evaluates the formulas in this process and refuses — never guesses — the ones it cannot read; `portable-shared-formulas.mjs` gives every cell a shared or array formula covers the formula it actually holds, so a filled-down column is never read as pasted numbers), the sheet content audits (`portable-sheet-audits.mjs`: percent scale, column fit, protected inputs, unreadable ink, a hardcode inside a row of formulas), the leftover scan (`placeholder-scan.mjs`: template wording and tools' citation markers, read on slides, a document's body, headers and footers, and PDF pages), `convertLegacyOffice` in `portable-soffice.mjs` (a `.doc`/`.xls`/`.ppt` saved once as its package beside the original when `open` meets one), text and image metrics. |
16
16
  | `pdf/` | `pdf-adapter.mjs` (session surface: snapshot, batch — including the marks `highlight`, `add_link`, `preview_fields` — validate, issues; re-exports `createPdf`), `pdf-writer.mjs` (blocks → pages, tables, page numbers), `pdf-forms.mjs` (field lint, add, describe, fill), `pdf-draw.mjs` (colors, page sizes, wrapping, images), `pdf-fonts.mjs` (system Unicode font fallback shared by create, marks, forms, and OCR), `pdf-analysis.mjs` (text layout with rules, boxes, and links, ruled and aligned tables, images, OCR), `pdf-search.mjs` (text search over a layout: lines from runs, glyph-width placement, whole words), `pdf-safety.mjs` (active-content report: JavaScript, Launch, non-web links), `pdf-render.mjs`, `pdf-security.mjs` (qpdf), `document-preview.mjs`. |
@@ -39,7 +39,7 @@ export async function runDocxHtmlAuthoring(html, output, { target, signal = null
39
39
  const scratch = await mkdtemp(join(tmpdir(), 'mixdog-docx-captures-'));
40
40
  let browser;
41
41
  try {
42
- browser = await launchHtmlBrowser('docx-html-browser');
42
+ browser = await launchHtmlBrowser('docx-html-browser', { signal });
43
43
  } catch (error) {
44
44
  await rm(scratch, { recursive: true, force: true }).catch(() => {});
45
45
  return htmlFailure(error, startedAt);
@@ -2,17 +2,25 @@
2
2
  // deadline and abort wiring around the layout work, the failure result, and the photographs of captured elements.
3
3
  import { join } from 'node:path';
4
4
 
5
- import { buildPuppeteerLaunchArgs, resolveBrowserLaunchOptions } from '../../shared/browser-launch.mjs';
5
+ import {
6
+ buildPuppeteerLaunchArgs,
7
+ launchPuppeteerBrowser,
8
+ resolveBrowserLaunchOptions,
9
+ } from '../../shared/browser-launch.mjs';
6
10
  import { startChildGuardian } from '../../shared/child-guardian.mjs';
7
11
 
8
12
  /** Launches the local browser the HTML is laid out in, under a guardian that reaps it with this process. */
9
- export async function launchHtmlBrowser(label) {
13
+ export async function launchHtmlBrowser(label, { signal = null } = {}) {
10
14
  const puppeteer = (await import('puppeteer-core')).default;
11
- const browser = await puppeteer.launch({
12
- headless: true,
13
- ...resolveBrowserLaunchOptions(),
14
- args: buildPuppeteerLaunchArgs(['--font-render-hinting=none']),
15
- });
15
+ const browser = await launchPuppeteerBrowser(
16
+ puppeteer,
17
+ {
18
+ headless: true,
19
+ ...resolveBrowserLaunchOptions(),
20
+ args: buildPuppeteerLaunchArgs(['--font-render-hinting=none']),
21
+ },
22
+ { signal }
23
+ );
16
24
  try {
17
25
  startChildGuardian({ childPid: browser.process?.()?.pid, label });
18
26
  } catch {}
@@ -22,7 +30,9 @@ export async function launchHtmlBrowser(label) {
22
30
  /** Runs `work` against the browser under a deadline and an abort signal, and closes the browser afterwards. */
23
31
  export async function runInBrowser(browser, { signal, timeoutMs }, work) {
24
32
  const abort = () => browser.close().catch(() => {});
25
- signal?.addEventListener('abort', abort, { once: true });
33
+ // A cancel that landed while the browser was launching closes it at once.
34
+ if (signal?.aborted) abort();
35
+ else signal?.addEventListener('abort', abort, { once: true });
26
36
  let timer = null;
27
37
  const running = work();
28
38
  const timeout = new Promise((_, reject) => {
@@ -8,6 +8,7 @@ import { render } from '../core/office-actions.mjs';
8
8
  import { documentFormat, officeSessionForDocument } from '../core/office-core.mjs';
9
9
  import { createAuthoredSession, exists, fullPath, validatePptxAuthorMode } from '../core/office-sessions.mjs';
10
10
  import { inlineOfficeAudit } from '../quality/inline-audit.mjs';
11
+ import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
11
12
  import { parseAuthoringBrief } from './pptx-brief.mjs';
12
13
  import { htmlBriefScript } from './pptx-html-runner.mjs';
13
14
  import {
@@ -68,6 +69,7 @@ async function writeComparisons(target, htmlPages, officePages) {
68
69
  (await scaled(officePages.find((image) => image.page === page && (image.pages?.length || 1) === 1))) ||
69
70
  (await blank(left.width));
70
71
  const path = `${target}.mixdog-compare-page-${page}.png`;
72
+ noteOfficeWrite(path);
71
73
  await sharp({ create: { width: left.width + right.width + gap, height, channels: 3, background: '#808080' } })
72
74
  .composite([
73
75
  { input: left.data, left: 0, top: 0 },
@@ -262,7 +262,7 @@ export async function printHtmlPdf(
262
262
  await rm(output, { force: true });
263
263
  let browser;
264
264
  try {
265
- browser = await launchHtmlBrowser('pdf-html-browser');
265
+ browser = await launchHtmlBrowser('pdf-html-browser', { signal });
266
266
  } catch (error) {
267
267
  return htmlFailure(error, startedAt);
268
268
  }
@@ -7,6 +7,7 @@
7
7
  import { writeFile } from 'node:fs/promises';
8
8
  import { basename, dirname, extname, join } from 'node:path';
9
9
  import { createCanvas, loadImage } from '@napi-rs/canvas';
10
+ import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
10
11
 
11
12
  function pageImages(images = []) {
12
13
  const pages = [];
@@ -51,6 +52,7 @@ export async function writeContactSheet(images, output, { width = 1600 } = {}) {
51
52
  const data = sheet.toBuffer('image/png');
52
53
  const stem = basename(output, extname(output));
53
54
  const path = join(dirname(output), `${stem}.mixdog-contact.png`);
55
+ noteOfficeWrite(path);
54
56
  await writeFile(path, data);
55
57
  return {
56
58
  path,
@@ -9,6 +9,7 @@ import { createRequire } from 'node:module';
9
9
  import { pathToFileURL } from 'node:url';
10
10
 
11
11
  import { launchHtmlBrowser, runInBrowser } from './html-browser.mjs';
12
+ import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
12
13
  import { iconGlobal } from './pptx-icons.mjs';
13
14
  import { GEOMETRY_TOLERANCE_PX, NEAR_MISS_PX, readGeometry } from './pptx-html-geometry.mjs';
14
15
 
@@ -558,7 +559,7 @@ export async function measureHtmlDeck(
558
559
  { sourcePath, shotPath, timeoutMs = 90_000, signal = null, prepare = null }
559
560
  ) {
560
561
  await writeFile(sourcePath, html, 'utf8');
561
- const browser = await launchHtmlBrowser('pptx-html-browser');
562
+ const browser = await launchHtmlBrowser('pptx-html-browser', { signal });
562
563
  const work = async () => {
563
564
  const page = await browser.newPage();
564
565
  await page.setViewport(viewport(HTML_CANVAS, 1));
@@ -598,6 +599,7 @@ export async function measureHtmlDeck(
598
599
  );
599
600
  }
600
601
  const shot = shotPath(index + 1);
602
+ noteOfficeWrite(shot);
601
603
  await element.screenshot({ path: shot });
602
604
  shots.push(shot);
603
605
  const measured = await page.evaluate(extractSlide, index);
@@ -6,6 +6,7 @@ import { dirname } from 'node:path';
6
6
  import { createRequire } from 'node:module';
7
7
 
8
8
  import { HTML_CANVAS, measureHtmlDeck } from './pptx-html-measure.mjs';
9
+ import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
9
10
  import { buildPptxFromMeasure } from './pptx-html-build.mjs';
10
11
  import { normalizeAuthoredPptx } from './pptx-script-normalize.mjs';
11
12
 
@@ -312,6 +313,7 @@ export async function writeHtmlComparisons(target, htmlShots, renderedImages) {
312
313
  const left = await sharp(html).resize(width, height, { fit: 'fill' }).png().toBuffer();
313
314
  const right = await sharp(image.path).resize(width, height, { fit: 'fill' }).png().toBuffer();
314
315
  const path = artifacts.compare(image.page);
316
+ noteOfficeWrite(path);
315
317
  await sharp({ create: { width: width * 2 + gap, height, channels: 3, background: '#808080' } })
316
318
  .composite([
317
319
  { input: left, left: 0, top: 0 },
@@ -6,6 +6,7 @@ import { dirname, join } from 'node:path';
6
6
  import { pathToFileURL } from 'node:url';
7
7
 
8
8
  import { htmlFailure, launchHtmlBrowser, photographCaptures, runInBrowser } from './html-browser.mjs';
9
+ import { noteOfficeWrite } from '../core/office-write-ledger.mjs';
9
10
  import { iconTable, injectIcons } from './pptx-html-measure.mjs';
10
11
  import { extractXlsxSheets } from './xlsx-html-measure.mjs';
11
12
  import { drawDataCharts } from './pdf-html-charts.mjs';
@@ -65,7 +66,7 @@ export async function runXlsxHtmlAuthoring(html, output, { target, signal = null
65
66
  const scratch = await mkdtemp(join(tmpdir(), 'mixdog-xlsx-captures-'));
66
67
  let browser;
67
68
  try {
68
- browser = await launchHtmlBrowser('xlsx-html-browser');
69
+ browser = await launchHtmlBrowser('xlsx-html-browser', { signal });
69
70
  } catch (error) {
70
71
  await rm(scratch, { recursive: true, force: true }).catch(() => {});
71
72
  return htmlFailure(error, startedAt);
@@ -106,6 +107,7 @@ export async function runXlsxHtmlAuthoring(html, output, { target, signal = null
106
107
  const htmlPages = [];
107
108
  for (const [index, clip] of extents.entries()) {
108
109
  const path = artifacts.shot(index + 1);
110
+ noteOfficeWrite(path);
109
111
  await page.screenshot({ path, clip, captureBeyondViewport: true });
110
112
  htmlPages.push({ page: index + 1, path });
111
113
  }
@@ -2,6 +2,7 @@ import { mkdir, writeFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
3
  import { extractPdfImages, extractPdfTextLayout, findPdfText, inferPdfTables } from '../pdf/pdf-analysis.mjs';
4
4
  import { findByDocumentPath, fullPath, queryObject, snapshot, snapshotSelectionForTarget } from './office-sessions.mjs';
5
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
5
6
 
6
7
  /** RFC 4180 quoting so a table cell that contains a comma or quote round-trips. */
7
8
  export function csvCell(text) {
@@ -94,6 +95,7 @@ async function queryPdfTables(session, args, cwd, signal) {
94
95
  const ordinal = (counters.get(table.page) || 0) + 1;
95
96
  counters.set(table.page, ordinal);
96
97
  const file = join(directory, `page-${table.page}-table-${ordinal}.csv`);
98
+ noteOfficeWrite(file);
97
99
  await writeFile(
98
100
  file,
99
101
  `${table.rows.map((row) => row.map((cell) => csvCell(String(cell ?? ''))).join(',')).join('\r\n')}\r\n`,
@@ -115,6 +117,7 @@ async function queryPdfImages(session, args, cwd, signal) {
115
117
  const written = [];
116
118
  for (const image of extracted._images) {
117
119
  const file = join(directory, `page-${image.page}-image-${image.index}.png`);
120
+ noteOfficeWrite(file);
118
121
  await writeFile(file, Buffer.from(image.data, 'base64'));
119
122
  written.push(file);
120
123
  }
@@ -8,6 +8,7 @@ import { resolveOfficeRenderOutput } from '../quality/quality-pipeline.mjs';
8
8
  import { defaultRenderOutput, exists, fullPath } from './office-sessions.mjs';
9
9
  import { completePageCoverage, pptxPageSignatures, reusablePptxPages } from './pptx-page-cache.mjs';
10
10
  import { numberDocxTableOfContents } from './docx-toc-pages.mjs';
11
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
11
12
 
12
13
  function request(session, args, cwd) {
13
14
  return {
@@ -136,6 +137,7 @@ export async function renderOfficePreview(
136
137
  const exportKey = `${session.target}\0${Number(session.snapshotVersion || 0)}\0${requested.output}`;
137
138
  if (!incremental || session.previewExportKey !== exportKey || !(await exists(requested.output))) {
138
139
  session.previewExportKey = null;
140
+ noteOfficeWrite(requested.output);
139
141
  await exportPreview(session, requested.output);
140
142
  if (incremental) session.previewExportKey = exportKey;
141
143
  }
@@ -9,6 +9,7 @@ import { describeOfficeCapabilities } from '../capabilities.mjs';
9
9
  import { applyBatch, closeSession, finalize, issues, qa, render, save, validate } from './office-actions.mjs';
10
10
  import { getOfficeElement, queryOfficeDocument } from './office-actions-read.mjs';
11
11
  import { snapshot } from './office-sessions.mjs';
12
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
12
13
  import { noteBatchAgainstHtml } from '../authoring/html-source-drift.mjs';
13
14
  import {
14
15
  assertTransactionUnchanged,
@@ -59,7 +60,13 @@ async function batchAction(session, args, cwd, signal) {
59
60
  };
60
61
  }
61
62
 
63
+ // Verbs that never write the session's document.
64
+ const READ_ACTIONS = new Set(['describe', 'begin', 'diff', 'snapshot', 'get', 'query', 'issues', 'validate']);
65
+
62
66
  export async function dispatchOfficeSessionAction({ action, session, args, cwd, signal }) {
67
+ // Write receipt: every other verb may write the session's document (an
68
+ // edit, save, rollback, autoFix, or a render that numbers its contents).
69
+ if (!READ_ACTIONS.has(action)) noteOfficeWrite(session.target);
63
70
  if (action === 'describe') {
64
71
  return describeOfficeCapabilities({
65
72
  format: session.format,
@@ -29,6 +29,7 @@ import {
29
29
  toolResult,
30
30
  } from './office-core.mjs';
31
31
  import { fullPath, selectMode } from './office-sessions.mjs';
32
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
32
33
  import { pendingOfficeTransactions, recoverOfficeTransaction } from './office-transactions.mjs';
33
34
 
34
35
  async function detectAction(args, cwd, dataDir) {
@@ -102,6 +103,13 @@ async function authorAction(args, cwd, dataDir, signal, startedAt) {
102
103
  const extension = extname(String(args.path || args.output || '')).toLowerCase();
103
104
  const author =
104
105
  { '.pdf': authorPdf, '.docx': authorHtmlDocument, '.xlsx': authorHtmlDocument }[extension] || authorPptx;
106
+ // Write receipts: the authored document and the sidecars kept beside it
107
+ // (its HTML source and print); per-page images report themselves.
108
+ for (const value of [args.path, args.output]) {
109
+ if (!String(value || '').trim()) continue;
110
+ const target = fullPath(String(value).trim(), cwd);
111
+ for (const path of [target, `${target}.mixdog-source.html`, `${target}.mixdog-html.pdf`]) noteOfficeWrite(path);
112
+ }
105
113
  const authored = await author(args, { cwd, dataDir, signal });
106
114
  const images = Array.isArray(authored?._images) ? authored._images : [];
107
115
  delete authored._images;
@@ -4,6 +4,7 @@ import { basename, dirname, extname, isAbsolute, join, resolve } from 'node:path
4
4
  import { tmpdir } from 'node:os';
5
5
  import { randomUUID } from 'node:crypto';
6
6
  import { assertOfficeOperationContracts } from '../capabilities.mjs';
7
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
7
8
  import {
8
9
  callMicrosoftOffice,
9
10
  detectMicrosoftOffice,
@@ -258,6 +259,7 @@ async function packageSource(path, signal) {
258
259
  `${path} is a legacy binary file and ${converted} already exists; open ${converted} directly, or move it aside to convert again.`
259
260
  );
260
261
  }
262
+ noteOfficeWrite(converted);
261
263
  await convertLegacyOffice(path, converted, { signal });
262
264
  return { source: converted, convertedFrom: path };
263
265
  }
@@ -280,6 +282,7 @@ export async function openSession(args, cwd, dataDir, { readOnly = false } = {})
280
282
  if (existing) return existing;
281
283
  if (copies) {
282
284
  await mkdir(dirname(target), { recursive: true });
285
+ noteOfficeWrite(target);
283
286
  await copyFile(source, target);
284
287
  }
285
288
  const designContext = await resolveOfficeDesignContext({
@@ -533,6 +536,7 @@ export async function createSession(args, cwd, dataDir) {
533
536
  // Whether this call brings the file into being decides what a failed create
534
537
  // may clean up afterwards: a file it wrote itself, never one already there.
535
538
  const targetExisted = await exists(target);
539
+ noteOfficeWrite(target);
536
540
  const designContext = await resolveOfficeDesignContext({ args, dataDir, target, format, created: true });
537
541
  const creation = { target, fileKind, format, dataDir, targetExisted, designContext };
538
542
  await releaseOverwrittenSession(target, args.overwrite);
@@ -645,6 +649,7 @@ export async function materializeWorkingCopy(session) {
645
649
  throw new Error('portable editing requires an output path different from the source');
646
650
  }
647
651
  await mkdir(dirname(target), { recursive: true });
652
+ noteOfficeWrite(target);
648
653
  await copyFile(session.source, target);
649
654
  if (documentSessions.get(documentSessionKey(session.target)) === session.id) {
650
655
  documentSessions.delete(documentSessionKey(session.target));
@@ -4,6 +4,7 @@ import { tmpdir } from 'node:os';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { callMicrosoftOffice, detectMicrosoftOffice, openMicrosoftOfficeSession } from '../com/com-adapter.mjs';
6
6
  import { listOfficeJournals, readOfficeJournal, removeOfficeJournal, writeOfficeJournal } from './journal.mjs';
7
+ import { noteOfficeWrite } from './office-write-ledger.mjs';
7
8
  import {
8
9
  OfficeConflictError,
9
10
  documentFingerprint,
@@ -299,6 +300,8 @@ export async function rollbackTransaction(session) {
299
300
  transaction.diff = transactionDocumentDiff(transaction, current.document);
300
301
  transaction.phase = 'rolling_back';
301
302
  await persistOfficeTransaction(session);
303
+ // A recovered rollback restores the document outside any session verb.
304
+ noteOfficeWrite(session.target);
302
305
  if (isMicrosoftOfficeSession(session)) {
303
306
  const result = await callMicrosoftOffice(
304
307
  {