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,197 @@
1
+ // side-question.mjs — /btw: a quick question answered while the main turn
2
+ // keeps working. The answer comes from a cache-sharing fork of the session's
3
+ // latest provider request (side-question-store.mjs): the same model, system
4
+ // prompt, tools and cache-relevant options and the same message prefix, so
5
+ // the provider's prompt cache serves the prefix. Only the reply that request
6
+ // produced and one question are appended. One provider call, no tool loop;
7
+ // the answer is returned to the caller and never enters the transcript.
8
+ import { collectAssistantToolCallIds } from './context-tool-pairs.mjs';
9
+ import { buildIntermediateAssistantMessage, buildToolCallAssistantMessage } from './loop/assistant-commit.mjs';
10
+ import { logSessionLlmUsage } from './manager/ask-turn-commit.mjs';
11
+ import { disposeTransportScope, forkTransportScope } from '../providers/transport-scope.mjs';
12
+ import { isAgentOwner } from '../agent-owner.mjs';
13
+ import { getProvider } from '../providers/registry.mjs';
14
+ import { prepareTurnEffortConfiguration } from '../providers/effort-configuration.mjs';
15
+ import { providerNativeToolPrefixCount } from '../runtime-core/provider-request-tools.mjs';
16
+ import { snapshotProviderRequestTools } from '../runtime-core/tool-catalog.mjs';
17
+ import { repairTranscriptBeforeProviderSend } from './loop/transcript-repair.mjs';
18
+ import { buildRequestSendOptions } from './manager/ask-turn-loop-options.mjs';
19
+ import { filterModelVisibleSessionMessages } from './manager/message-sanitize.mjs';
20
+ import { hasUserConversationMessage } from './manager/prompt-utils.mjs';
21
+ import { loadSession } from './store.mjs';
22
+ import { projectSyntheticUserEnvelopes } from './synthetic-user-envelope.mjs';
23
+ import { beginSideQuestion, sideQuestionRequest, sideQuestionRequestSnapshot } from './side-question-store.mjs';
24
+
25
+ export { cancelSideQuestion } from './side-question-store.mjs';
26
+
27
+ export const SIDE_QUESTION_NO_REQUEST = 'Send a message first, then try /btw.';
28
+ export const SIDE_QUESTION_TOOL_ATTEMPT =
29
+ 'The model tried to use a tool instead of answering. A side question cannot run tools; try asking it differently.';
30
+ const SIDE_QUESTION_EMPTY_ANSWER = 'The model returned no answer.';
31
+
32
+ // The reply's tool calls run in the main session; the fork only needs each
33
+ // call answered so the request stays well-formed.
34
+ const SIDE_QUESTION_TOOL_STUB =
35
+ 'This tool call belongs to the main task and is handled there; its result is not part of this side question.';
36
+
37
+ function sideQuestionPrompt(question) {
38
+ return [
39
+ '<side-question>',
40
+ 'The user is asking you a quick side question. The main agent keeps working on the task on its own;',
41
+ 'this exchange does not reach it and does not change that work.',
42
+ 'Answer directly, in this one response, from what this conversation already contains.',
43
+ 'No tools are available here: do not call any, and do not offer or promise to do anything.',
44
+ 'If the conversation does not tell you the answer, say that you do not know.',
45
+ '',
46
+ question,
47
+ '</side-question>',
48
+ ].join('\n');
49
+ }
50
+
51
+ // The assistant turn the saved request produced, built the way the loop
52
+ // commits it (without the turn's one-shot transcript metadata), plus a stub
53
+ // result per tool call.
54
+ function replyMessages(response) {
55
+ if (!response) return [];
56
+ const reply = response.toolCalls?.length
57
+ ? buildToolCallAssistantMessage(response, { calls: response.toolCalls, opts: {} })
58
+ : buildIntermediateAssistantMessage(response, {});
59
+ if (!reply) return [];
60
+ const stubs = collectAssistantToolCallIds(reply).map((id) => ({
61
+ role: 'tool',
62
+ toolCallId: id,
63
+ content: SIDE_QUESTION_TOOL_STUB,
64
+ }));
65
+ return [reply, ...stubs];
66
+ }
67
+
68
+ /**
69
+ * The fork of a saved request: its messages unchanged, then the reply and
70
+ * the question. The send options are the saved ones; the provider
71
+ * continuation state follows the reply when it is appended, exactly as the
72
+ * main loop's next request would carry it. `sharedCachePrefixCount` (the
73
+ * shared prefix's message count, which survives model-proxy serialization)
74
+ * keeps an Anthropic message breakpoint on the shared prefix, so the fork reads the
75
+ * saved request's cache entry and writes none for its suffix.
76
+ */
77
+ export function buildSideQuestionRequest(request, question) {
78
+ const reply = replyMessages(request.response);
79
+ const opts = { ...request.opts };
80
+ if (reply.length && Object.hasOwn(request.response, 'providerState')) {
81
+ opts.providerState = request.response.providerState;
82
+ }
83
+ if (request.messages.length) opts.sharedCachePrefixCount = request.messages.length;
84
+ const messages = [
85
+ ...request.messages,
86
+ ...reply,
87
+ // A user interjection keeps its own turn (never folded into a preceding
88
+ // tool result), so the prefix's last block reaches the provider unchanged.
89
+ { role: 'user', content: sideQuestionPrompt(question), meta: { source: 'steering' } },
90
+ ];
91
+ return { messages, opts };
92
+ }
93
+
94
+ /**
95
+ * The request the main loop would send next for a session that has none saved
96
+ * (the runtime restarted, or the saved one was forgotten): the session's own
97
+ * provider, model, tools, send options and transcript, assembled by the same
98
+ * pieces the loop uses, without the loop's side effects (no compaction, no
99
+ * transcript repair in place, nothing written to the session). Null when the
100
+ * session has no conversation, no resolvable provider, or is an agent's.
101
+ */
102
+ function nextMainRequest(sessionId) {
103
+ const session = sessionId ? loadSession(sessionId) : null;
104
+ if (!session || session.closed === true || isAgentOwner(session)) return null;
105
+ if (!hasUserConversationMessage(session.messages)) return null;
106
+ const provider = getProvider(session.provider);
107
+ if (!provider) return null;
108
+ const transcript = filterModelVisibleSessionMessages(session.messages);
109
+ repairTranscriptBeforeProviderSend(transcript, sessionId);
110
+ const opts = buildRequestSendOptions({
111
+ sessionId,
112
+ session,
113
+ turnEffort: session.effort || null,
114
+ effortConfiguration: prepareTurnEffortConfiguration(session, provider),
115
+ stepAutoEffort: null,
116
+ codexTurnId: null,
117
+ startedAtMs: Date.now(),
118
+ signal: undefined,
119
+ });
120
+ const sendTools = snapshotProviderRequestTools({
121
+ provider: session.provider || provider.name,
122
+ tools: session.tools,
123
+ nativeTools: opts.nativeTools,
124
+ messages: transcript,
125
+ session,
126
+ });
127
+ opts.iteration = 1;
128
+ opts.providerToolSnapshotAuthoritative = true;
129
+ opts.providerNativeToolPrefixCount = providerNativeToolPrefixCount(sendTools);
130
+ return sideQuestionRequestSnapshot({
131
+ provider,
132
+ model: session.model,
133
+ messages: projectSyntheticUserEnvelopes(transcript).messages,
134
+ tools: sendTools,
135
+ opts,
136
+ });
137
+ }
138
+
139
+ function answerText(response) {
140
+ if (response?.toolCalls?.length) return SIDE_QUESTION_TOOL_ATTEMPT;
141
+ const text = typeof response?.content === 'string' ? response.content.trim() : '';
142
+ return text || SIDE_QUESTION_EMPTY_ANSWER;
143
+ }
144
+
145
+ /**
146
+ * Answer `question` from the session's latest provider request. One side
147
+ * question runs per session: a new one aborts the previous, and
148
+ * cancelSideQuestion aborts it. A session without a saved request forks the
149
+ * request its next turn would send. Rejects when the session has no
150
+ * conversation or provider, with the provider's error, or with the cancel
151
+ * reason.
152
+ */
153
+ export async function askSideQuestion(sessionId, question) {
154
+ const text = typeof question === 'string' ? question.trim() : '';
155
+ if (!text) throw new TypeError('Side question is empty.');
156
+ const request = sideQuestionRequest(sessionId) || nextMainRequest(sessionId);
157
+ if (!request) throw new Error(SIDE_QUESTION_NO_REQUEST);
158
+ const run = beginSideQuestion(sessionId);
159
+ const startedAt = Date.now();
160
+ // The fork keeps the session id (prompt cache routing) but runs on its own
161
+ // transport scope: no pooled socket, response-chain anchor, turn-state
162
+ // token or provider run of the main session is waited on, read or replaced.
163
+ const transportScope = forkTransportScope(sessionId, 'side');
164
+ try {
165
+ const { messages, opts } = buildSideQuestionRequest(request, text);
166
+ let response;
167
+ try {
168
+ response = await request.provider.send(
169
+ messages,
170
+ request.model,
171
+ request.tools.length ? request.tools : undefined,
172
+ {
173
+ ...opts,
174
+ transportScope,
175
+ admissionOwner: sessionId,
176
+ signal: run.signal,
177
+ }
178
+ );
179
+ } catch (error) {
180
+ throw run.signal.aborted ? run.signal.reason : error;
181
+ }
182
+ if (run.signal.aborted) throw run.signal.reason;
183
+ const session = request.opts.session;
184
+ if (response?.usage && session?.id) {
185
+ void logSessionLlmUsage({
186
+ session: { ...session, model: request.model },
187
+ usage: response.usage,
188
+ startedAt,
189
+ mode: 'side-question',
190
+ }).catch(() => {});
191
+ }
192
+ return { text: answerText(response) };
193
+ } finally {
194
+ run.end();
195
+ disposeTransportScope(transportScope, request.provider);
196
+ }
197
+ }
@@ -11,7 +11,7 @@ import { join } from 'node:path';
11
11
  import { getPluginData } from '../../config.mjs';
12
12
  import { PLUGIN_LOG_MAX_BYTES, PLUGIN_LOG_KEEP_BYTES } from '../../../../../lib/mixdog-debug.cjs';
13
13
  import { createAsyncLogAppender } from '../../../../shared/async-log-appender.mjs';
14
- import { sessionPath, deleteHeartbeat } from './paths-heartbeat.mjs';
14
+ import { sessionPath } from './session-paths.mjs';
15
15
  import {
16
16
  readCanonicalSessionRecord as _readCanonicalRecord,
17
17
  CANONICAL_RECORD_UNREADABLE as LIFECYCLE_AMBIGUOUS,
@@ -36,7 +36,7 @@ import {
36
36
  } from './live-state.mjs';
37
37
  import { _uncacheSessionSummary, _queueSessionSummaryUpsert } from './summary-cache.mjs';
38
38
  import { _savePending, _clearDebounce } from './pending-saves.mjs';
39
- import { _runtimeLivenessVeto, _heartbeatLivenessVeto, _deleteHeartbeatUnlessNewer } from './liveness-veto.mjs';
39
+ import { _runtimeLivenessVeto } from './liveness-veto.mjs';
40
40
  import { loadSession } from './load-session.mjs';
41
41
 
42
42
  // Cancellation truth, single rule: an UNCONFIRMED stop outranks a confirmed
@@ -158,9 +158,23 @@ function _logSessionCloseMetric(id, existing, closedAt, reason) {
158
158
  }
159
159
  }
160
160
 
161
+ /**
162
+ * A barrier that found no record at all — nothing on disk and no live or
163
+ * pending copy in memory — fenced nothing because there is nothing to fence:
164
+ * its pending writes were already cancelled under the commit lock. It still
165
+ * returns null (no generation exists), but a caller passing `options.outcome`
166
+ * learns that the record was legitimately gone rather than vetoed, contended
167
+ * or failed.
168
+ */
169
+ function _recordAbsent(options) {
170
+ if (options?.outcome && typeof options.outcome === 'object') options.outcome.absent = true;
171
+ return null;
172
+ }
173
+
161
174
  /**
162
175
  * Atomically mark a session closed on disk with a bumped generation.
163
176
  * Returns the new generation, or null if the session file doesn't exist.
177
+ * `options.outcome` (an object) gets `absent: true` when no record existed.
164
178
  * Used by closeSession() to plant a tombstone that races against in-flight
165
179
  * saveSession() calls.
166
180
  */
@@ -171,15 +185,11 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
171
185
  // Caller-provided probes may re-enter the store, so evaluate them before
172
186
  // taking the non-reentrant Atomics commit lock. A veto must also precede
173
187
  // pending-save cancellation so debounce durability remains intact.
174
- if (_runtimeLivenessVeto(id, options) || _heartbeatLivenessVeto(id, options)) return null;
188
+ if (_runtimeLivenessVeto(id, options)) return null;
175
189
  const closeGuard = _guardedSaveOptions(id);
176
190
  const commitControl = _acquireWriteCommit(closeGuard);
177
191
  if (commitControl === false) return null;
178
192
  try {
179
- // Cross-process heartbeat revival after full-TTL silence is accepted as a
180
- // best-effort race: the tombstone resurrection guard is the authoritative
181
- // post-race arbiter. Re-stat here, but never invoke caller code under lock.
182
- if (_heartbeatLivenessVeto(id, options)) return null;
183
193
  const authority = _readLifecycleAuthority(id, reason, 'close');
184
194
  if (authority === LIFECYCLE_AMBIGUOUS) return null;
185
195
  // Only a committed close may disrupt pending persistence.
@@ -187,7 +197,7 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
187
197
  _cancelSessionWrites(id);
188
198
  _uncacheSessionSummary(id);
189
199
  const existing = loadSession(id);
190
- if (!existing) return null;
200
+ if (!existing) return _recordAbsent(options);
191
201
  // Re-close idempotence: a session that is ALREADY tombstoned keeps its
192
202
  // ORIGINAL close time (updatedAt) and generation. The old code refreshed
193
203
  // updatedAt=Date.now() on every call, so the 5-min idle sweep re-closing a
@@ -216,7 +226,7 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
216
226
  const tombstone = {
217
227
  ...base,
218
228
  closed: true,
219
- closedReason: alreadyClosed ? base.closedReason || reason : reason,
229
+ closedReason: alreadyClosed && reason !== 'desktop-session-delete' ? base.closedReason || reason : reason,
220
230
  status: 'closed',
221
231
  generation: newGen,
222
232
  updatedAt: closeTime,
@@ -226,8 +236,6 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
226
236
  _savePending.delete(id);
227
237
  clearSessionSaveError(id);
228
238
  _clearLiveSession(id);
229
- // Preserve a sidecar published strictly after the sweep's scan snapshot.
230
- _deleteHeartbeatUnlessNewer(id, options);
231
239
  _queueSessionSummaryUpsert(tombstone);
232
240
  _droppedSaveIds.delete(id);
233
241
  // Emit the close metric only on the FIRST close — a re-close of an
@@ -243,7 +251,7 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
243
251
  /**
244
252
  * Bump a session's generation WITHOUT planting a closed:true tombstone.
245
253
  * Used by closeSession(id, reason, { tombstone: false }) — the runtime side
246
- * (heartbeat, bash shells, controller, in-memory entry) is detached, but the
254
+ * (bash shells, controller, in-memory entry) is detached, but the
247
255
  * session file itself stays valid/resumable. The generation bump alone is
248
256
  * what protects it from a late save race: any saveSession() still in flight
249
257
  * from the detached turn was issued with the OLD generation as its
@@ -253,7 +261,7 @@ export function markSessionClosed(id, reason = 'manual', options = {}) {
253
261
  * of clobbering whatever the resumed session writes next.
254
262
  * Returns the new generation, or null if the session file doesn't exist.
255
263
  */
256
- export function bumpSessionGeneration(id, reason = 'detach') {
264
+ export function bumpSessionGeneration(id, reason = 'detach', options = {}) {
257
265
  clearSessionLifecycleCommitError(id);
258
266
  // The detach barrier is a canonical write and MUST take the same commit
259
267
  // lock as markSessionClosed. Cancellation alone is not a barrier: a writer
@@ -278,14 +286,13 @@ export function bumpSessionGeneration(id, reason = 'detach') {
278
286
  _cancelSessionWrites(id);
279
287
  _uncacheSessionSummary(id);
280
288
  const existing = loadSession(id);
281
- if (!existing) return null;
289
+ if (!existing) return _recordAbsent(options);
282
290
  const newGen = (typeof existing.generation === 'number' ? existing.generation : 0) + 1;
283
291
  const detached = { ...existing, generation: newGen, updatedAt: Date.now(), detachedReason: reason };
284
292
  if (!_writeLifecycleRecord(id, detached, existing, reason)) return null;
285
293
  _savePending.delete(id);
286
294
  clearSessionSaveError(id);
287
295
  _clearLiveSession(id);
288
- deleteHeartbeat(id);
289
296
  _queueSessionSummaryUpsert(detached);
290
297
  _droppedSaveIds.delete(id);
291
298
  return newGen;
@@ -6,7 +6,7 @@
6
6
  * path and of the in-memory snapshot caches.
7
7
  */
8
8
  import { readCanonicalSessionRecord, CANONICAL_RECORD_UNREADABLE } from './canonical-reader.mjs';
9
- import { sessionPath } from './paths-heartbeat.mjs';
9
+ import { sessionPath } from './session-paths.mjs';
10
10
 
11
11
  /**
12
12
  * Cheap authoritative lifecycle read straight from disk (no live/pending
@@ -5,7 +5,7 @@
5
5
  import { readFileSync, readdirSync } from 'node:fs';
6
6
  import { Worker } from 'node:worker_threads';
7
7
  import { getPluginData } from '../../config.mjs';
8
- import { getStoreDir } from './paths-heartbeat.mjs';
8
+ import { getStoreDir } from './session-paths.mjs';
9
9
  import { probePath, PROBE_PRESENT, PROBE_ABSENT } from './fs-probe.mjs';
10
10
  import { isCancelledWrite as _isCancelledWrite } from './write-guards.mjs';
11
11
  import {
@@ -29,7 +29,6 @@ import {
29
29
  import { _saveAsyncQueued, _saveWorkerPending } from './save-worker.mjs';
30
30
  import { STORED_SESSION_UNREADABLE, _ensureLifecycleFields, _storedSessionFromFile } from './serialize.mjs';
31
31
  import { _savePending } from './pending-saves.mjs';
32
- import { isOrdinarySession } from '../store-summary-visibility.mjs';
33
32
  import { closeSessionCatalogDb } from '../catalog-db/index.mjs';
34
33
 
35
34
  // Disk mtime of the summary index when the in-memory cache was last refreshed
@@ -63,10 +62,7 @@ export function listStoredSessions(options = {}) {
63
62
  options.includeLive === true
64
63
  ? _withUnpersistedSessions(stored, invalidStorageIds)
65
64
  : stored.sort((a, b) => b.updatedAt - a.updatedAt);
66
- // This full-record API is the ordinary catalog boundary. Agent discovery
67
- // uses the durable summary/worker indexes instead; callers that explicitly
68
- // own an Agent surface can request the unfiltered internal rows.
69
- return options.includeAgentOnly === true ? listed : listed.filter(isOrdinarySession);
65
+ return listed;
70
66
  }
71
67
 
72
68
  function _withUnpersistedSessions(stored, invalidStorageIds = new Set()) {
@@ -13,7 +13,7 @@ import {
13
13
  } from './live-state.mjs';
14
14
  import { _summaryCacheVersions } from './summary-cache.mjs';
15
15
  import { probePath, PROBE_PRESENT } from './fs-probe.mjs';
16
- import { sessionPath } from './paths-heartbeat.mjs';
16
+ import { sessionPath } from './session-paths.mjs';
17
17
  import { _hasPendingPersistence } from './pending-saves.mjs';
18
18
  import { forgetSessionLoadCache } from './load-cache.mjs';
19
19
 
@@ -124,6 +124,29 @@ export function _retireSessionIncarnation(id) {
124
124
  if (id) _sessionIncarnations.delete(id);
125
125
  }
126
126
 
127
+ // ── Delete fence ────────────────────────────────────────────────────────────
128
+ // An explicitly deleted session may still own a running turn whose later saves
129
+ // carry a brand-new write guard and incarnation, so neither the cancellation
130
+ // counter nor the retired incarnation stops them: an absent file reads as
131
+ // "creatable". The fence is the id-level refusal every write-admission check
132
+ // consults. Only explicit user deletes fence (ids are never reused); the cap
133
+ // only ever forgets the oldest, long-settled entries.
134
+ const SESSION_DELETE_FENCE_MAX = 4096;
135
+ const _deleteFence = new Set();
136
+
137
+ export function fenceSessionWrites(id) {
138
+ if (!id) return;
139
+ _deleteFence.delete(id);
140
+ _deleteFence.add(id);
141
+ if (_deleteFence.size > SESSION_DELETE_FENCE_MAX) {
142
+ _deleteFence.delete(_deleteFence.values().next().value);
143
+ }
144
+ }
145
+
146
+ export function isSessionWriteFenced(id) {
147
+ return _deleteFence.has(id);
148
+ }
149
+
127
150
  /** Clear every save marker for an id (successful hard delete). */
128
151
  export function _clearSessionSaveState(id) {
129
152
  if (!id) return;
@@ -1,53 +1,62 @@
1
1
  /**
2
- * Runtime-liveness evidence for the destructive paths (close, detach,
3
- * delete): the freshness of a session's `.hb` sidecar, the veto rules derived
4
- * from it and the caller-supplied `isSessionLive` probe.
2
+ * Runtime-liveness veto for the destructive paths (close, delete): the
3
+ * caller-supplied `isSessionLive` probe over the daemon's in-memory runtime
4
+ * state, plus — for the sweep (`legacyOwnerVeto`) — the legacy `.hb` / `.own`
5
+ * markers an OLDER Mixdog process still running beside this one (mid-upgrade)
6
+ * keeps publishing. No current build writes those markers; they are only ever
7
+ * READ here, never refreshed.
5
8
  *
6
9
  * Separate from the write pipeline on purpose — a veto is a pure READ taken
7
10
  * before any commit lock is held, and markSessionClosed / deleteSession must
8
- * apply exactly the same rules.
11
+ * apply exactly the same rule.
9
12
  */
10
- import { existsSync, statSync } from 'node:fs';
11
13
  import { join } from 'node:path';
12
- import { getStoreDir, deleteHeartbeat } from './paths-heartbeat.mjs';
14
+ import { getStoreDir } from './session-paths.mjs';
15
+ import { probePath, readTextFile, PROBE_PRESENT } from './fs-probe.mjs';
13
16
 
14
- function _heartbeatMtime(id) {
17
+ export const LEGACY_MARKER_SUFFIXES = ['.hb', '.own'];
18
+ // An older live owner rewrote `.hb` every ≤5s mid-turn and `.own` every ~20s
19
+ // while holding the session open; one untouched this long holds nothing.
20
+ export const LEGACY_MARKER_FRESH_MS = 2 * 60 * 1000;
21
+
22
+ function processAlive(pid) {
15
23
  try {
16
- const path = join(getStoreDir(), `${id}.hb`);
17
- return existsSync(path) ? statSync(path).mtimeMs || 0 : 0;
18
- } catch {
19
- return 0;
24
+ process.kill(pid, 0);
25
+ return true;
26
+ } catch (error) {
27
+ // ESRCH: no such process. EPERM means the pid exists.
28
+ return error?.code === 'EPERM';
20
29
  }
21
30
  }
22
31
 
23
- /**
24
- * Freshness of a session's `.hb` heartbeat sidecar (0 when absent). Used by
25
- * the fork-on-resume guard: a fresh heartbeat published by another process
26
- * means the session is actively being driven there RIGHT NOW.
27
- */
28
- export function readSessionHeartbeatMtime(id) {
29
- if (!id) return 0;
30
- return _heartbeatMtime(id);
31
- }
32
-
33
- export function _runtimeLivenessVeto(id, options = {}) {
34
- return typeof options.isSessionLive === 'function' && options.isSessionLive(id);
32
+ /** A legacy marker's evidence, or null when it is not there to judge. The
33
+ * marker holds `<msTimestamp>\n<pid>\n`; a pid-less (pre-pid) marker names
34
+ * no owner, so it is neither live nor dead. */
35
+ export function readLegacyMarker(path, now = Date.now()) {
36
+ const probe = probePath(path);
37
+ if (probe.state !== PROBE_PRESENT) return null;
38
+ const read = readTextFile(path);
39
+ const pid = read.state === PROBE_PRESENT ? Number(read.text.split('\n')[1]) || 0 : 0;
40
+ const alive = pid > 0 && processAlive(pid);
41
+ return {
42
+ fresh: now - (probe.mtimeMs || 0) <= LEGACY_MARKER_FRESH_MS,
43
+ ownerAlive: alive,
44
+ ownerDead: pid > 0 && !alive,
45
+ };
35
46
  }
36
47
 
37
- export function _heartbeatLivenessVeto(id, options = {}) {
38
- const heartbeatMtime = _heartbeatMtime(id);
39
- if (!(heartbeatMtime > 0)) return false;
40
- const hasHeartbeatSnapshot = Object.hasOwn(options, 'heartbeatSnapshotMtime');
41
- const snapshotMtime = Number(options.heartbeatSnapshotMtime) || 0;
42
- if (hasHeartbeatSnapshot && heartbeatMtime > snapshotMtime) return true;
43
- const freshMs = Number(options.heartbeatFreshMs);
44
- return Number.isFinite(freshMs) && freshMs > 0 && Date.now() - heartbeatMtime <= freshMs;
48
+ /** Whether a legacy marker of the session names a live owner pid with a
49
+ * fresh heartbeat: that session is driven by another live process. */
50
+ export function _legacyOwnerLivenessVeto(id, now = Date.now()) {
51
+ if (typeof id !== 'string' || !/^[A-Za-z0-9_-]+$/.test(id)) return false;
52
+ const dir = getStoreDir();
53
+ return LEGACY_MARKER_SUFFIXES.some((suffix) => {
54
+ const marker = readLegacyMarker(join(dir, `${id}${suffix}`), now);
55
+ return Boolean(marker?.fresh && marker.ownerAlive);
56
+ });
45
57
  }
46
58
 
47
- export function _deleteHeartbeatUnlessNewer(id, options = {}) {
48
- const hasHeartbeatSnapshot = Object.hasOwn(options, 'heartbeatSnapshotMtime');
49
- const snapshotMtime = Number(options.heartbeatSnapshotMtime) || 0;
50
- if (!hasHeartbeatSnapshot || _heartbeatMtime(id) <= snapshotMtime) {
51
- deleteHeartbeat(id);
52
- }
59
+ export function _runtimeLivenessVeto(id, options = {}) {
60
+ if (typeof options.isSessionLive === 'function' && options.isSessionLive(id)) return true;
61
+ return options.legacyOwnerVeto === true && _legacyOwnerLivenessVeto(id);
53
62
  }
@@ -8,7 +8,7 @@ import {
8
8
  readCanonicalSessionRecord,
9
9
  acceptStrictSessionRecords,
10
10
  } from './canonical-reader.mjs';
11
- import { sessionPath } from './paths-heartbeat.mjs';
11
+ import { sessionPath } from './session-paths.mjs';
12
12
  import { noteMemoryRelease } from '../../../../shared/idle-gc.mjs';
13
13
 
14
14
  // Recent full-session reads are much hotter than writes while a user hops
@@ -4,7 +4,7 @@
4
4
  * snapshot, or the evidence-backed failed-save snapshot. Nothing here writes;
5
5
  * the precedence rules are the whole responsibility.
6
6
  */
7
- import { sessionPath } from './paths-heartbeat.mjs';
7
+ import { sessionPath } from './session-paths.mjs';
8
8
  import { _savePending } from './pending-saves.mjs';
9
9
  import { _liveSessions, _droppedSaveIds, getFailedSaveSnapshot } from './live-state.mjs';
10
10
  import { _readStoredSessionCached } from './load-cache.mjs';
@@ -4,11 +4,12 @@
4
4
  * edge plus the full local-state purge that must happen under the same lock
5
5
  * (incarnation retirement, pending slots, purge hooks, markers, summary row).
6
6
  */
7
- import { readFileSync, readdirSync, unlinkSync } from 'node:fs';
8
- import { join } from 'node:path';
7
+ import { unlinkSync } from 'node:fs';
9
8
  import { isAgentOwner } from '../../agent-owner.mjs';
10
- import { readTopLevelLifecycleRecord, isLifecycleUnreadable } from '../lifecycle-scan.mjs';
11
- import { getStoreDir, sessionPath, deleteSessionPresence } from './paths-heartbeat.mjs';
9
+ import { listStoredAgentWorkerLinks } from '../store-agent-worker-rows.mjs';
10
+ import { listStoredSessionSummaries } from './listing.mjs';
11
+ import { STORED_SESSION_UNREADABLE, _storedSessionFromFile } from './serialize.mjs';
12
+ import { getStoreDir, sessionPath } from './session-paths.mjs';
12
13
  import {
13
14
  readCanonicalSessionRecord as _readCanonicalRecord,
14
15
  CANONICAL_RECORD_UNREADABLE as LIFECYCLE_AMBIGUOUS,
@@ -22,60 +23,66 @@ import {
22
23
  } from './write-guards.mjs';
23
24
  import {
24
25
  _clearLiveSession,
26
+ _liveSessions,
25
27
  _retireSessionIncarnation,
26
28
  _clearSessionSaveState,
27
29
  clearSessionLifecycleCommitError,
30
+ fenceSessionWrites,
28
31
  } from './live-state.mjs';
29
32
  import { purgeSessionSaveBookkeeping as _purgeSessionSaveBookkeeping } from './save-worker.mjs';
30
- import { _uncacheSessionSummary, _queueSessionSummaryRemoval } from './summary-cache.mjs';
33
+ import { _uncacheSessionSummary, _queueSessionSummaryRemoval, _cachedSummaryRows } from './summary-cache.mjs';
31
34
  import { _savePending, _clearDebounce, _releasePendingSlot } from './pending-saves.mjs';
32
- import { _runtimeLivenessVeto, _heartbeatLivenessVeto, _deleteHeartbeatUnlessNewer } from './liveness-veto.mjs';
35
+ import { _runtimeLivenessVeto } from './liveness-veto.mjs';
33
36
  import { _runSessionPurgeHooks } from './session-observers.mjs';
34
37
  import { deleteCompactionArchive } from '../compaction-archive.mjs';
35
38
 
36
- /** Strictly enumerate child-agent session files linked to one visible parent.
37
- * Used only by explicit parent deletion; ordinary close/context switches keep
38
- * the relationship intact. */
39
+ /** Child-agent session ids linked to one visible parent. Candidates come from
40
+ * metadata only (the summary rows and the agent-worker links — no folder
41
+ * scan); each candidate's own file is then read strictly and must still be an
42
+ * agent-owned record linked to this parent, so a stale/foreign metadata row
43
+ * can never name a deletion target. Parent deletion no longer removes these
44
+ * children; they outlive the parent as independent sessions. */
39
45
  export function listOwnedAgentSessionIds(ownerSessionId) {
40
46
  const ownerId = String(ownerSessionId || '').trim();
41
47
  if (!/^[A-Za-z0-9_-]+$/.test(ownerId)) return [];
42
- const dir = getStoreDir();
43
- if (probePath(dir).state !== PROBE_PRESENT) return [];
44
- let files;
45
- try {
46
- files = readdirSync(dir).filter((file) => file.endsWith('.json'));
47
- } catch {
48
- return [];
48
+ const linksTo = (row) => row?.ownerSessionId === ownerId || row?.parentSessionId === ownerId;
49
+ const candidates = new Set();
50
+ for (const row of [...listStoredSessionSummaries(), ...listStoredAgentWorkerLinks()]) {
51
+ const candidateId = String(row?.id || row?.sessionId || '').trim();
52
+ if (linksTo(row) && candidateId !== ownerId && /^[A-Za-z0-9_-]+$/.test(candidateId)) {
53
+ candidates.add(candidateId);
54
+ }
49
55
  }
56
+ const dir = getStoreDir();
50
57
  const ids = [];
51
- for (const file of files) {
52
- const candidateId = file.slice(0, -5);
53
- if (!candidateId || candidateId === ownerId || !/^[A-Za-z0-9_-]+$/.test(candidateId)) continue;
54
- try {
55
- const record = readTopLevelLifecycleRecord(readFileSync(join(dir, file), 'utf8'));
56
- if (isLifecycleUnreadable(record) || record.id !== candidateId) continue;
57
- const session = record.doc;
58
- if (!isAgentOwner(session)) continue;
59
- const linkedOwner = String(session.ownerSessionId || session.parentSessionId || '').trim();
60
- if (linkedOwner === ownerId) ids.push(candidateId);
61
- } catch {
62
- /* unreadable/vanished records are never deletion targets */
63
- }
58
+ for (const candidateId of candidates) {
59
+ const session = _storedSessionFromFile(dir, `${candidateId}.json`, false);
60
+ // Unreadable, vanished, corrupt or foreign records are never deletion targets.
61
+ if (!session || session === STORED_SESSION_UNREADABLE || !isAgentOwner(session)) continue;
62
+ const linkedOwner = String(session.ownerSessionId || session.parentSessionId || '').trim();
63
+ if (linkedOwner === ownerId) ids.push(candidateId);
64
64
  }
65
65
  return ids;
66
66
  }
67
67
 
68
+ /** Working folders the session is known to have used (its cached summary row
69
+ * and live snapshot — no file read, no scan); the purge hooks derive
70
+ * per-folder artifacts such as the transcript path from them. */
71
+ function knownSessionCwds(id) {
72
+ const row = _cachedSummaryRows()?.find((candidate) => candidate.id === id);
73
+ const live = _liveSessions.get(id);
74
+ const cwds = [row?.cwd, row?.workspace?.cwd, row?.workspace?.projectPath, live?.cwd];
75
+ return [...new Set(cwds.filter((cwd) => typeof cwd === 'string' && cwd))];
76
+ }
77
+
68
78
  export function deleteSession(id, options = {}) {
69
79
  // Keep caller probes and all vetoes ahead of the non-reentrant lock and
70
80
  // ahead of pending-save disruption, matching markSessionClosed().
71
- if (_runtimeLivenessVeto(id, options) || _heartbeatLivenessVeto(id, options)) return false;
81
+ if (_runtimeLivenessVeto(id, options)) return false;
72
82
  const deleteGuard = _guardedSaveOptions(id);
73
83
  const commitControl = _acquireWriteCommit(deleteGuard);
74
84
  if (commitControl === false) return false;
75
85
  try {
76
- // Cross-process revival after full-TTL silence remains best-effort; the
77
- // tombstone resurrection guard is authoritative. Only re-stat .hb here.
78
- if (_heartbeatLivenessVeto(id, options)) return false;
79
86
  // ── 1. unlink FIRST, still holding the commit lock ─────────────────────
80
87
  // A failed unlink is NOT a delete: the canonical bytes are still there, so
81
88
  // nothing may be purged, cancelled or closed — every pending write, parked
@@ -122,15 +129,15 @@ export function deleteSession(id, options = {}) {
122
129
  // its settlement moves no marker and touches no bookkeeping — and a
123
130
  // re-created id gets a brand new incarnation of its own.
124
131
  _retireSessionIncarnation(id);
132
+ // An explicit user delete also fences the id: a still-running turn's later
133
+ // saves get fresh guards/incarnations and would otherwise recreate the file.
134
+ if (options.fenceWrites === true) fenceSessionWrites(id);
125
135
  _cancelSessionWrites(id);
126
136
  _clearDebounce(id);
127
137
  _releasePendingSlot(_savePending.get(id));
128
138
  _savePending.delete(id);
129
139
  _purgeSessionSaveBookkeeping(id);
130
- _runSessionPurgeHooks(id);
131
- // Preserve a sidecar published strictly after the sweep's scan snapshot.
132
- _deleteHeartbeatUnlessNewer(id, options);
133
- deleteSessionPresence(id);
140
+ _runSessionPurgeHooks(id, { cwds: knownSessionCwds(id) });
134
141
  // The display-only compaction archive has no meaning without its session.
135
142
  deleteCompactionArchive(getStoreDir(), id);
136
143
  _clearLiveSession(id);