mixdog 1.2.3 → 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 (465) hide show
  1. package/package.json +1 -1
  2. package/scripts/gate-local.mjs +6 -4
  3. package/scripts/lib/gate-scheduler.mjs +29 -11
  4. package/scripts/probe-provider-catalog.mjs +31 -0
  5. package/scripts/provider-pingpong-live.mjs +128 -38
  6. package/scripts/session-sweep.mjs +5 -25
  7. package/src/agents/maintainer/AGENT.md +1 -1
  8. package/src/agents/maintainer/agent.json +1 -1
  9. package/src/agents/reviewer/AGENT.md +2 -4
  10. package/src/agents/security/AGENT.md +1 -2
  11. package/src/agents/writer/AGENT.md +2 -3
  12. package/src/defaults/shipped-history.json +6 -1
  13. package/src/defaults/skills/image/SKILL.md +21 -23
  14. package/src/lib/keychain-cjs.cjs +13 -11
  15. package/src/rules/agent/AGENT.md +7 -5
  16. package/src/rules/lead/LEAD.md +7 -9
  17. package/src/rules/shared/45-destructive.md +4 -0
  18. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  19. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  20. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  21. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  22. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  23. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  24. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  25. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  26. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  29. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  30. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  31. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  32. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  33. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  34. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  35. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  36. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  37. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  38. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  40. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  41. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  46. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  47. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  48. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  49. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  50. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  51. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  52. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  53. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  54. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  55. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  56. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  57. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  58. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  59. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  60. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  62. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  63. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  64. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  65. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  66. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  67. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  68. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  69. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  70. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  78. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  79. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  80. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  82. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  84. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  86. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  87. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  88. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  89. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  90. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  91. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  92. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  93. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  94. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  95. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  96. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  97. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  98. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  99. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  100. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  101. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  102. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  103. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  106. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  107. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  108. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  109. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  110. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  111. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  112. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  113. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  114. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  115. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  116. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  117. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  118. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  119. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  120. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  121. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  122. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  123. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  124. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  125. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  126. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  128. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  129. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  130. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  131. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  132. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  133. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  134. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  135. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  137. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  138. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  139. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  146. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  147. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  148. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  150. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  157. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  158. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  159. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  160. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  161. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  162. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  163. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  164. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  165. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  167. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  169. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  170. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  171. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  172. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  174. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  177. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  179. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +136 -196
  180. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  181. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  182. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  183. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  184. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  185. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  186. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  187. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  188. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  189. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  190. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  191. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  192. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  195. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  196. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  197. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  198. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  199. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  200. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  205. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  206. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  208. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  209. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  210. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  211. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  212. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  216. package/src/runtime/attachments/store.d.mts +9 -0
  217. package/src/runtime/attachments/store.mjs +80 -9
  218. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  219. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  220. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  221. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  222. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  223. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  224. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  225. package/src/runtime/github/tool.mjs +118 -17
  226. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  227. package/src/runtime/media/catalog.mjs +4 -4
  228. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  229. package/src/runtime/media/defaults.mjs +37 -0
  230. package/src/runtime/media/jobs.mjs +12 -2
  231. package/src/runtime/media/lanes.mjs +12 -0
  232. package/src/runtime/media/tool-defs.mjs +15 -4
  233. package/src/runtime/media/tool.mjs +41 -12
  234. package/src/runtime/media/upstream-error.mjs +72 -6
  235. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  236. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  237. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  238. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  239. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  240. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  242. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  243. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  244. package/src/runtime/office/index.mjs +2 -3
  245. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  246. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  247. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  248. package/src/runtime/shared/atomic-file.d.mts +1 -1
  249. package/src/runtime/shared/background-tasks.mjs +6 -1
  250. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  251. package/src/runtime/shared/browser-launch.mjs +15 -0
  252. package/src/runtime/shared/err-text.mjs +42 -4
  253. package/src/runtime/shared/file-lock.mjs +6 -5
  254. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  255. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  256. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  257. package/src/runtime/shared/model-route-chain.mjs +48 -0
  258. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  259. package/src/runtime/shared/pid-liveness.mjs +12 -0
  260. package/src/runtime/shared/provider-api-key.mjs +6 -0
  261. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  262. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  263. package/src/runtime/shared/quiet-progress.mjs +101 -0
  264. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  265. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  266. package/src/runtime/shared/remote-intent.mjs +2 -42
  267. package/src/runtime/shared/slow-span.mjs +8 -1
  268. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  269. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  270. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  271. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  272. package/src/runtime/shared/tool-status.mjs +3 -2
  273. package/src/runtime/shared/tool-surface.mjs +2 -3
  274. package/src/runtime/shared/tool-work-units.mjs +22 -15
  275. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  276. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  277. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  278. package/src/runtime/shared/transcript-writer.mjs +14 -4
  279. package/src/runtime/shared/turn-changes.mjs +2 -1
  280. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  281. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  282. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  283. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  284. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  285. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  286. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  287. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  288. package/src/runtime/shared/worktrees/include.mjs +125 -7
  289. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  290. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  291. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  292. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  293. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  294. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  295. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  296. package/src/runtime/terminal-bridge/client.mjs +14 -10
  297. package/src/session-runtime/boot/apis.mjs +7 -1
  298. package/src/session-runtime/boot/config.mjs +2 -0
  299. package/src/session-runtime/boot/facade.mjs +3 -0
  300. package/src/session-runtime/boot/session.mjs +3 -2
  301. package/src/session-runtime/boot/tools.mjs +3 -0
  302. package/src/session-runtime/conversation-import/service.mjs +1 -1
  303. package/src/session-runtime/goal-facade-api.mjs +2 -2
  304. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  305. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  306. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  307. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  308. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  309. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  310. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  311. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  312. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  313. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  314. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  315. package/src/session-runtime/media-api.mjs +8 -0
  316. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  317. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  318. package/src/session-runtime/model-route/set-route.mjs +114 -34
  319. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  320. package/src/session-runtime/model-route-api.mjs +4 -2
  321. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  322. package/src/session-runtime/native-web-search.mjs +9 -8
  323. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  324. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  325. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  326. package/src/session-runtime/provider-models.mjs +1 -0
  327. package/src/session-runtime/provider-usage.mjs +4 -0
  328. package/src/session-runtime/route-resolution.mjs +3 -1
  329. package/src/session-runtime/runtime-core.mjs +4 -0
  330. package/src/session-runtime/runtime-facade.mjs +6 -0
  331. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  332. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  333. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  334. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  335. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  336. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  337. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  338. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  339. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  340. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  341. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  343. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  344. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  345. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  346. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  347. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  348. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  349. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  350. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  351. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  352. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  353. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  354. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  355. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  356. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  357. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  358. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  359. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  360. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  361. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  362. package/src/session-runtime/services/agent-tool.mjs +4 -0
  363. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  364. package/src/session-runtime/services/provider-admin.mjs +77 -0
  365. package/src/session-runtime/session-create/session-options.mjs +7 -5
  366. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  367. package/src/session-runtime/session-transcript.mjs +4 -5
  368. package/src/session-runtime/session-turn-api.mjs +3 -9
  369. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  370. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  371. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  372. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  373. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  374. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  375. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  376. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  377. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  378. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  379. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  380. package/src/session-runtime/turn/turn-run.mjs +1 -36
  381. package/src/session-runtime/usage-stats-api.mjs +4 -1
  382. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  383. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  384. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  385. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  386. package/src/session-runtime/workspace/transitions.mjs +248 -68
  387. package/src/standalone/channel-transport.mjs +3 -4
  388. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  389. package/src/standalone/daemon-pg-stop.mjs +1 -3
  390. package/src/standalone/daemon-session-control.mjs +88 -0
  391. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  392. package/src/standalone/daemon.mjs +2 -0
  393. package/src/standalone/local-session-runtime.mjs +11 -12
  394. package/src/standalone/session-protocol.mjs +7 -0
  395. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  396. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  397. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  398. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  399. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  400. package/src/standalone/session-service/agent-tree.mjs +28 -2
  401. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  402. package/src/standalone/session-service/entries.mjs +1 -0
  403. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  404. package/src/standalone/session-service/quota-resume.mjs +28 -13
  405. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  406. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  407. package/src/standalone/session-tool-output.mjs +82 -0
  408. package/src/tui/app/app-view.jsx +2 -1
  409. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  410. package/src/tui/app/provider-setup-picker.mjs +15 -3
  411. package/src/tui/app/slash-commands.mjs +6 -0
  412. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  413. package/src/tui/components/Spinner.jsx +4 -0
  414. package/src/tui/components/SpinnerByline.jsx +21 -2
  415. package/src/tui/components/status-line/local-line.mjs +10 -2
  416. package/src/tui/dist/index.mjs +478 -251
  417. package/src/tui/session/active-tool-summary.mjs +1 -1
  418. package/src/tui/session/context-state.mjs +0 -3
  419. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  420. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  421. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  422. package/src/tui/session/quota-resume.mjs +140 -61
  423. package/src/tui/session/session-api/commands.mjs +4 -10
  424. package/src/tui/session/session-api/controls.mjs +12 -0
  425. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  426. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  427. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  428. package/src/tui/session/session-api/media.mjs +2 -0
  429. package/src/tui/session/session-api/routes.mjs +81 -57
  430. package/src/tui/session/session-api/settings.mjs +3 -3
  431. package/src/tui/session/session-api/worktree.mjs +3 -2
  432. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  433. package/src/tui/session/session-flow/drain.mjs +6 -4
  434. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  435. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  436. package/src/tui/session/session-flow/queue.mjs +1 -1
  437. package/src/tui/session/tool-approval.mjs +17 -8
  438. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  439. package/src/tui/session/tui-steering-persist.mjs +3 -5
  440. package/src/tui/session/turn-quiet.mjs +53 -0
  441. package/src/tui/session/turn-stream.mjs +5 -1
  442. package/src/tui/session/turn.mjs +38 -31
  443. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  444. package/src/tui/spinner-meta.mjs +10 -0
  445. package/src/tui/spinner-verbs.mjs +3 -0
  446. package/src/ui/statusline.mjs +13 -3
  447. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  448. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  449. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  450. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  451. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  452. package/src/session-runtime/turn/share-ops.mjs +0 -77
  453. package/src/tui/session/cross-surface-share.mjs +0 -406
  454. package/src/tui/session/live-share/delta.mjs +0 -153
  455. package/src/tui/session/live-share/mirror.mjs +0 -106
  456. package/src/tui/session/live-share/owner.mjs +0 -254
  457. package/src/tui/session/live-share/retry.mjs +0 -54
  458. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  459. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  460. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  461. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  462. package/src/tui/session/live-share/viewer.mjs +0 -99
  463. package/src/tui/session/live-share/wire.mjs +0 -44
  464. package/src/tui/session/live-share.mjs +0 -154
  465. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -3,6 +3,7 @@
3
3
  // throttled crash-recovery checkpoint sidecar, and the close snapshot that
4
4
  // canonicalizes the transcript when the turn is interrupted.
5
5
  import { captureTurnCheckpointContextState, createTurnCheckpointRecorder } from './turn-checkpoint.mjs';
6
+ import { finalizeTurnInterruptionSnapshot } from './turn-interruption.mjs';
6
7
 
7
8
  // Coalescing window for streaming-delta flushes. Each flush journals only the
8
9
  // delta since the previous one, so the window just bounds syscalls.
@@ -83,28 +84,46 @@ export function createTurnCheckpointScheduler({ sessionId, generation, turnToken
83
84
  };
84
85
  }
85
86
 
87
+ // The live-session fields a staged close snapshot rewrites.
88
+ const CLOSE_SNAPSHOT_SESSION_FIELDS = [
89
+ 'liveTurnMessages',
90
+ '_providerPrefixGuardState',
91
+ 'messages',
92
+ 'activeTurnCheckpoint',
93
+ 'providerState',
94
+ 'updatedAt',
95
+ 'lastUsedAt',
96
+ ];
97
+
86
98
  /** closeSession is synchronous and generation-first by design. This turn-local
87
99
  * hook lets it canonicalize the in-flight transcript before
88
100
  * bumpSessionGeneration()/markSessionClosed() writes the disk snapshot and
89
101
  * invalidates the ordinary cancellation cleanup save. Finalizes at most once;
90
- * later calls return the same snapshot. */
102
+ * later calls return the same snapshot.
103
+ *
104
+ * `prepare.tentative(reason)` stages the snapshot on the live session only:
105
+ * the checkpoint scheduler and the interruption tracker keep running until
106
+ * `commit()`, and `rollback()` restores the live session exactly, so a close
107
+ * whose lifecycle barrier is refused leaves the turn as it was. */
91
108
  export function createCloseSnapshot({ turn, interruption, checkpoint, runtime }) {
92
109
  let snapshot = null;
93
- return (abortReason) => {
94
- if (snapshot) return snapshot;
110
+ const stage = (abortReason) => {
95
111
  const session = turn.session;
96
112
  if (!session) return null;
97
- checkpoint.stop();
98
- session.liveTurnMessages = null;
99
- // Interruption finalize below rewrites the transcript; the
100
- // mid-turn provider prefix snapshot must not survive it.
101
- delete session._providerPrefixGuardState;
102
- interruption.restoreTombstonedText();
103
- const finalized = interruption.finalize({
113
+ const saved = CLOSE_SNAPSHOT_SESSION_FIELDS.map((key) => [key, Object.hasOwn(session, key), session[key]]);
114
+ const savedRuntimeSession = runtime.session;
115
+ // The tracker snapshot already counts retracted (tombstoned) text as
116
+ // output, so finalizing from it leaves the tracker itself untouched.
117
+ const finalized = finalizeTurnInterruptionSnapshot({
104
118
  turnOutgoing: turn.outgoing || session.messages,
105
119
  currentUserContent: turn.userTurnContent,
106
- abortReason,
120
+ snapshot: interruption.snapshot(),
121
+ abortReason: abortReason ?? null,
107
122
  });
123
+ session.liveTurnMessages = null;
124
+ // The finalized transcript is a rewrite; the mid-turn provider prefix
125
+ // snapshot must not survive it.
126
+ delete session._providerPrefixGuardState;
108
127
  session.messages = finalized.messages;
109
128
  delete session.activeTurnCheckpoint;
110
129
  // A preserved response — or a non-user detach that keeps the provisional
@@ -113,7 +132,32 @@ export function createCloseSnapshot({ turn, interruption, checkpoint, runtime })
113
132
  session.updatedAt = Date.now();
114
133
  session.lastUsedAt = Date.now();
115
134
  runtime.session = session;
116
- snapshot = finalized;
117
- return finalized;
135
+ return {
136
+ snapshot: finalized,
137
+ commit() {
138
+ checkpoint.stop();
139
+ interruption.restoreTombstonedText();
140
+ snapshot = finalized;
141
+ },
142
+ rollback() {
143
+ for (const [key, had, value] of saved) {
144
+ if (had) session[key] = value;
145
+ else delete session[key];
146
+ }
147
+ runtime.session = savedRuntimeSession;
148
+ },
149
+ };
150
+ };
151
+ const prepare = (abortReason) => {
152
+ if (snapshot) return snapshot;
153
+ const staged = stage(abortReason);
154
+ if (!staged) return null;
155
+ staged.commit();
156
+ return staged.snapshot;
157
+ };
158
+ prepare.tentative = (abortReason) => {
159
+ if (snapshot) return { snapshot, commit() {}, rollback() {} };
160
+ return stage(abortReason);
118
161
  };
162
+ return prepare;
119
163
  }
@@ -45,3 +45,19 @@ export function rewindUnansweredPrompt(session, prompt) {
45
45
  delete session._providerPrefixGuardState;
46
46
  return true;
47
47
  }
48
+
49
+ /** Resolve a retry against the durable failure, never against display text. */
50
+ export function retainedFailedTurnInput(messages, messageId, attemptId) {
51
+ const list = Array.isArray(messages) ? messages : [];
52
+ const index = list.findLastIndex((message) => message?.role !== 'system');
53
+ const message = list[index];
54
+ const transcript = message?.meta?.transcript;
55
+ if (!messageId || transcript?.messageId !== messageId || transcript?.completion?.status !== 'failed') {
56
+ throw new Error('The failed turn is no longer the latest execution.');
57
+ }
58
+ if (transcript.completion.attemptId && transcript.completion.attemptId !== attemptId) {
59
+ throw new Error('The failed attempt is no longer the latest execution.');
60
+ }
61
+ if (message.role !== 'user') return null;
62
+ return { index, message };
63
+ }
@@ -1,7 +1,6 @@
1
1
  // Steering / pending-message queue with sync buffering and atomic persistence.
2
- import { mkdirSync, readFileSync } from 'node:fs';
2
+ import { readFileSync } from 'node:fs';
3
3
  import { loadSession } from '../store.mjs';
4
- import { ForeignPendingMessageController } from './foreign-pending-messages.mjs';
5
4
  import {
6
5
  currentPendingLifecycleToken,
7
6
  lifecycleTokenStale,
@@ -29,7 +28,7 @@ import {
29
28
  pendingIdSet,
30
29
  pruneEmptyPendingIdSet,
31
30
  } from './pending-claim-ledger.mjs';
32
- import { legacyPendingMessagesPath, pendingShardDir } from './pending-spool-path.mjs';
31
+ import { legacyPendingMessagesPath } from './pending-spool-path.mjs';
33
32
  import { pruneCleanupConfirmedLedger } from './pending-delivered-ledger.mjs';
34
33
  // The durable delivered-id ledger on the session record lives in
35
34
  // pending-delivered-ledger.mjs; re-exported so prior importers stay unchanged.
@@ -83,16 +82,6 @@ const _sessionPendingMessages = new Map();
83
82
  const _hydratedPendingMessages = new Map();
84
83
  const _pendingHydrations = new Map();
85
84
 
86
- // Exposed for live-share owners: they fs.watch this session's shard file for
87
- // instant pickup of cross-surface submits (the 3s drain tick remains the
88
- // safety net). Without a valid session id there is nothing to watch.
89
- export function pendingMessagesSpoolPath(sessionId) {
90
- if (!isValidPendingSessionId(sessionId)) return '';
91
- // fs.watch needs the directory to exist before the first shard is written.
92
- mkdirSync(pendingShardDir(), { recursive: true });
93
- return pendingMessagesPath(sessionId);
94
- }
95
-
96
85
  function pendingIdStillQueued(sessionId, id) {
97
86
  for (const map of [_sessionPendingMessages, _pendingPersistBuffers, _hydratedPendingMessages]) {
98
87
  const q = map.get(sessionId);
@@ -196,11 +185,10 @@ function acknowledgePendingMessages(sessionId, deliveredEntries, options = {}) {
196
185
  return false;
197
186
  }
198
187
  // Keep the acked ids when the spool cleanup FAILED: the durable entry
199
- // still exists, and forgetting that it was already delivered let the
200
- // foreign-injection drain re-take it as a "new" cross-surface submit
201
- // (user saw the same message duplicated in the transcript). Retained
202
- // ids also keep suppressing the hydrate path; they are dropped only
203
- // once a later ack round actually lands.
188
+ // still exists, and forgetting that it was already delivered would let
189
+ // the hydrate path re-take it as a new message (the same message
190
+ // duplicated in the transcript). They are dropped only once a later ack
191
+ // round actually lands.
204
192
  if (!ok) return false;
205
193
  forgetAckedIds();
206
194
  return true;
@@ -462,99 +450,6 @@ export function enqueuePendingMessage(sessionId, message) {
462
450
  return Math.max(q.length, bufferedDepth || 0);
463
451
  }
464
452
 
465
- /**
466
- * Remote-attach injection enqueue: persist a user message into the shared
467
- * cross-process spool WITHOUT touching this process's in-memory queues. Used
468
- * by a viewer surface attached to a session another live process owns — the
469
- * owner's injection poller (drainForeignUserInjections) picks it up there.
470
- * Skipping the local queues is deliberate: if this process later takes real
471
- * ownership of the session, a lingering local copy would double-inject.
472
- */
473
- export function enqueueRemotePendingMessage(sessionId, message) {
474
- if (!isValidPendingSessionId(sessionId)) return 0;
475
- // Same durable-lifecycle authority as the local enqueue: a cross-surface
476
- // submit that lands after the session was closed is refused before any
477
- // spool mutation.
478
- const token = currentPendingLifecycleToken(sessionId);
479
- if (pendingLifecycleInvalidated(sessionId)) return 0;
480
- const normalized = pendingMessageQueueEntry(message);
481
- if (!normalized) return 0;
482
- // The token rides into the async spool commit, which revalidates under the
483
- // spool lock (see persistPendingMessages).
484
- if (pendingLifecycleInvalidated(sessionId, token)) return 0;
485
- // Preserve the viewer's submission id end-to-end. Minting a fresh id here
486
- // made pipe-delivered + spool-fallback twins look like two messages
487
- // (owner accepted the pipe copy, then re-took the spool copy).
488
- const preservedId = pendingMessageId(normalized) || pendingMessageId(message);
489
- const remoteEntry = stampLifecycleToken(
490
- {
491
- ...normalized,
492
- id: typeof preservedId === 'string' && preservedId.trim() ? preservedId.trim() : newPendingMessageId(),
493
- },
494
- token
495
- );
496
- return persistPendingMessages(sessionId, [remoteEntry]);
497
- }
498
-
499
- function _foreignLocalIds(sessionId) {
500
- const localIds = new Set();
501
- for (const map of [_sessionPendingMessages, _pendingPersistBuffers, _hydratedPendingMessages]) {
502
- for (const entry of map.get(sessionId) || []) {
503
- const id = pendingMessageId(entry);
504
- if (id) localIds.add(id);
505
- }
506
- }
507
- for (const set of [_inDeliveryPendingIds.get(sessionId), _ackedPendingIds.get(sessionId)]) {
508
- for (const id of set || []) localIds.add(id);
509
- }
510
- // The durable delivered-ledger mirrors the hydrate path's suppression: an
511
- // entry whose id was recorded as delivered (but whose spool cleanup has
512
- // not landed yet — crash, lock contention, another process) is NOT a
513
- // foreign submit and must never re-inject.
514
- try {
515
- for (const id of loadSession(sessionId)?.deliveredPendingMessageIds || []) {
516
- if (typeof id === 'string' && id) localIds.add(id);
517
- }
518
- } catch {
519
- /* ledger unavailable — in-memory sets still guard */
520
- }
521
- return localIds;
522
- }
523
-
524
- function addForeignInDeliveryIds(sessionId, ids) {
525
- const inDelivery = pendingIdSet(_inDeliveryPendingIds, sessionId);
526
- for (const id of ids) inDelivery.add(id);
527
- }
528
-
529
- function removeForeignInDeliveryIds(sessionId, ids) {
530
- const inDelivery = _inDeliveryPendingIds.get(sessionId);
531
- for (const id of ids) inDelivery?.delete(id);
532
- pruneEmptyPendingIdSet(_inDeliveryPendingIds, sessionId);
533
- }
534
-
535
- const foreignPendingMessages = new ForeignPendingMessageController({
536
- addInDeliveryIds: addForeignInDeliveryIds,
537
- chainSpoolTail,
538
- currentLifecycleToken: currentPendingLifecycleToken,
539
- getSpoolTail: (sessionId) => _pendingPersistTails.get(sessionId),
540
- isValidSessionId: isValidPendingSessionId,
541
- lifecycleEpochMoved: pendingLifecycleEpochMoved,
542
- lifecycleInvalidated: pendingLifecycleInvalidated,
543
- localIds: _foreignLocalIds,
544
- normalizeStore: normalizePendingStore,
545
- removeInDeliveryIds: removeForeignInDeliveryIds,
546
- setSpoolQueue,
547
- spoolPath: pendingMessagesPath,
548
- updateSpool,
549
- warn: pendingWarn,
550
- });
551
-
552
- // The controller parks rows before delivery so an owner crash can redeliver
553
- // accepted input instead of losing it.
554
- export async function drainForeignUserInjections(sessionId) {
555
- return foreignPendingMessages.drainUserInjections(sessionId);
556
- }
557
-
558
453
  // Drain order: enqueue time, then source (hydrated < buffered < memory), then
559
454
  // position within the source.
560
455
  function byEnqueueOrder(a, b) {
@@ -710,15 +605,6 @@ export function _dropPendingMessageState(id, { clearPersisted = true } = {}) {
710
605
  } catch {
711
606
  /* ignore */
712
607
  }
713
- // A tombstoned close removes the whole durable queue anyway; a DETACH keeps
714
- // its scheduled release so this process still cleans up rows it parked.
715
- if (clearPersisted) {
716
- try {
717
- foreignPendingMessages.cancelHandoffRelease(id);
718
- } catch {
719
- /* ignore */
720
- }
721
- }
722
608
  _inDeliveryPendingIds.delete(id);
723
609
  _ackedPendingIds.delete(id);
724
610
  _pendingHydrations.delete(id);
@@ -93,7 +93,7 @@ export function persistPendingMessages(sessionId, messages) {
93
93
  // schedule the retry HERE. Waiting for "the next scheduled flush or
94
94
  // session takeover" meant a FINAL submit (nothing else ever
95
95
  // enqueues afterwards) stayed process-local forever, while
96
- // enqueue/enqueueRemotePendingMessage had already reported success.
96
+ // enqueuePendingMessage had already reported success.
97
97
  try {
98
98
  // Closed/detached since this write started: its buffer and its
99
99
  // retry timer were torn down on purpose — do not rebuild them.
@@ -229,7 +229,7 @@ export function replaceWorkspaceCoreSlices(core, { coreMemoryContext, instructio
229
229
  if (memory) parts.push(`${CORE_MEMORY_HEADING}${memory}`);
230
230
  }
231
231
  if (instructionFilesContext !== undefined) instructionFiles = trimmedSlice(instructionFilesContext);
232
- return [...parts, instructionFiles].filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
232
+ return [...parts, instructionFiles].filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
233
233
  }
234
234
 
235
235
  /**
@@ -243,7 +243,7 @@ export function replaceWorkspaceEnvironment(environmentTail, environmentContext)
243
243
  const facts = trimmedSlice(environmentContext);
244
244
  if (parts[0]?.startsWith('- ')) parts[0] = facts;
245
245
  else parts.unshift(facts);
246
- return parts.filter((part) => part && part.trim()).join(BP3_PART_SEPARATOR);
246
+ return parts.filter((part) => part?.trim()).join(BP3_PART_SEPARATOR);
247
247
  }
248
248
 
249
249
  /**
@@ -399,12 +399,13 @@ function sameWorkspacePath(left, right) {
399
399
  /** The lines a move from the project folder into a worktree adds to the notice. */
400
400
  function workspaceMoveLines(move, cwd) {
401
401
  if (!move?.from || sameWorkspacePath(move.from, cwd)) return [];
402
- const changes =
403
- move.changes === 'carried'
404
- ? 'Uncommitted changes were moved into the worktree; ignored files (dependencies, build output, env files) were not.'
405
- : move.changes === 'left'
406
- ? `Uncommitted changes were left in ${move.from}; they are not present here.`
407
- : '';
402
+ let changes = '';
403
+ if (move.changes === 'carried') {
404
+ changes =
405
+ 'Uncommitted changes were moved into the worktree; ignored files (dependencies, build output, env files) were not.';
406
+ } else if (move.changes === 'left') {
407
+ changes = `Uncommitted changes were left in ${move.from}; they are not present here.`;
408
+ }
408
409
  return [
409
410
  changes,
410
411
  `For continued work, substitute ${cwd} for the project root ${move.from} in paths mentioned earlier; absolute paths are not remapped automatically. Re-read affected files before editing them.`,
@@ -418,6 +419,7 @@ function workspaceNoticeContent({ seen, target }) {
418
419
  [
419
420
  '# Working Folder Changed',
420
421
  `The working folder is now ${target.cwd}. This replaces the Cwd and environment facts given earlier in this conversation; relative paths and shell commands resolve here.`,
422
+ 'Keep the conversation goals and decisions, but treat earlier file contents, search results and Git observations as historical, not as evidence of this checkout. Check the target project instructions and Git status, and re-read the files needed for the next edit before continuing. A folder change alone does not transfer uncommitted changes.',
421
423
  ...workspaceMoveLines(target.move, target.cwd),
422
424
  changed('environmentContext') ? target.environmentContext : '',
423
425
  ]
@@ -455,6 +457,25 @@ export function snapshotPendingWorkspaceNotice(session) {
455
457
  return content ? { revision: pending.revision, content } : null;
456
458
  }
457
459
 
460
+ /**
461
+ * The report of a new worktree's preparation (session.pendingSetupNotice, set
462
+ * by the first turn's workspace gate) for this turn, or null. Acknowledged
463
+ * when the provider accepted the turn, like the workspace notice.
464
+ * @returns {{ revision: number, content: string } | null}
465
+ */
466
+ export function snapshotPendingSetupNotice(session) {
467
+ const pending = session?.pendingSetupNotice;
468
+ return typeof pending?.content === 'string' && pending.content
469
+ ? { revision: pending.revision, content: pending.content }
470
+ : null;
471
+ }
472
+
473
+ export function acknowledgePendingSetupNotice(session, revision) {
474
+ if (session?.pendingSetupNotice?.revision !== revision) return false;
475
+ delete session.pendingSetupNotice;
476
+ return true;
477
+ }
478
+
458
479
  /** The notice of `revision` reached the model: what it was told is now the target. */
459
480
  export function acknowledgePendingWorkspaceNotice(session, revision) {
460
481
  const pending = session?.workspaceReroot;
@@ -1,7 +1,10 @@
1
1
  // ── Runtime liveness map ──────────────────────────────────────────────
2
- // In-memory only. Tracks per-session stage + stream heartbeat so agent type=list
2
+ // In-memory only. Tracks per-session stage + stream progress so agent type=list
3
3
  // can surface whether a session is actually alive vs stuck. Never persisted —
4
- // heartbeats would otherwise churn the session JSON on every SSE delta.
4
+ // progress would otherwise churn the session JSON on every SSE delta. The Map
5
+ // itself lives in the import-free leaf runtime-state.mjs so cold readers can
6
+ // ask which turns are in flight (isSessionTurnInFlight /
7
+ // inFlightSessionActivity); every mutation stays here.
5
8
  //
6
9
  // The Map and its timer companion are
7
10
  // module-level singletons — preserving the baseline's single-process shape.
@@ -20,14 +23,16 @@
20
23
  // }
21
24
  import { createAbortController } from '../../../../shared/abort-controller.mjs';
22
25
  import { emitSessionApprovalWaitChange } from '../../../../shared/session-approval-wait.mjs';
23
- import { publishHeartbeat, deleteHeartbeat } from '../store.mjs';
24
26
  import { DEFAULT_ACTIVITY_HEARTBEAT_MS } from '../../stall-policy.mjs';
27
+ import { MODEL_THINKING_STAGE, awaitingModelResponse, lastProgressAt } from '../../../../shared/quiet-progress.mjs';
25
28
  import { finishBrowserTurn } from '../../../../browser-bridge/client.mjs';
26
29
  import { configureUsageMetricsRuntime, dropMetricSeenState, bumpUsageMetricsTurnId } from './usage-metrics.mjs';
30
+ import { runtimeState as _runtimeState, isTurnInFlight } from './runtime-state.mjs';
31
+
32
+ export { IN_FLIGHT_STAGES, inFlightSessionActivity } from './runtime-state.mjs';
27
33
 
28
34
  const HEARTBEAT_THROTTLE_MS = 60_000; // 60s
29
35
 
30
- const _runtimeState = new Map();
31
36
  const _toolActivityHeartbeats = new Map();
32
37
  const VALID_STAGES = new Set([
33
38
  'connecting',
@@ -99,7 +104,6 @@ function _touchSessionActivityProgress(id) {
99
104
  const now = Date.now();
100
105
  entry.lastProgressAt = now;
101
106
  entry.updatedAt = now;
102
- publishHeartbeat(id, now);
103
107
  }
104
108
 
105
109
  function _startToolActivityHeartbeat(id) {
@@ -111,11 +115,17 @@ function _startToolActivityHeartbeat(id) {
111
115
  }
112
116
 
113
117
  export function updateSessionStage(id, stage) {
118
+ if (stage === MODEL_THINKING_STAGE) {
119
+ markSessionRequestSent(id);
120
+ return;
121
+ }
114
122
  if (!id || !VALID_STAGES.has(stage)) return;
115
123
  const entry = _touchRuntime(id);
116
124
  const now = Date.now();
117
125
  const priorStage = entry.stage;
118
126
  entry.stage = stage;
127
+ // A new stage (a retried attempt included) has not sent its request yet.
128
+ entry.requestSentAt = null;
119
129
  if (stage === 'requesting' && priorStage !== 'requesting') {
120
130
  entry.modelRequestStartedAt = now;
121
131
  }
@@ -148,6 +158,7 @@ export function markSessionAskStart(id) {
148
158
  entry.lastToolProtocolAt = null;
149
159
  entry.lastSemanticKind = null;
150
160
  entry.transportTrackingEnabled = false;
161
+ entry.requestSentAt = null;
151
162
  entry.lastToolCall = null;
152
163
  entry.toolStartedAt = null;
153
164
  entry.toolOutputTail = null;
@@ -166,14 +177,18 @@ export function markSessionAskStart(id) {
166
177
  entry.modelRequestStartedAt = null;
167
178
  entry.lastProgressAt = now;
168
179
  entry.updatedAt = now;
169
- // Publish heartbeat immediately so the status aggregator picks the
170
- // session up in the connecting / requesting window. Without this the
171
- // .hb file only landed on the first stream chunk — producing a 3–10s
172
- // (xhigh: 30s+) invisible gap where agent sessions ran but the
173
- // statusline showed no maintenance/agent badge. STREAM_FRESH_MS (5 min)
174
- // still drops a session whose provider truly never returns a chunk;
175
- // markSessionStreamDelta keeps refreshing once chunks arrive.
176
- return publishHeartbeat(id, now);
180
+ }
181
+ /** The provider put the request on the wire; its response headers are
182
+ * pending. The turn stays `requesting` (every stage set and the watchdog
183
+ * clock are unchanged); the send time lets surfaces show "model thinking"
184
+ * until transport arrives. Not progress: lastProgressAt is untouched. */
185
+ export function markSessionRequestSent(id) {
186
+ if (!id) return;
187
+ const entry = _runtimeState.get(id);
188
+ if (!entry || entry.closed || entry.controller?.signal?.aborted || entry.stage !== 'requesting') return;
189
+ const now = Date.now();
190
+ entry.requestSentAt = now;
191
+ entry.updatedAt = now;
177
192
  }
178
193
  export function enableSessionTransportTracking(id) {
179
194
  if (!id) return;
@@ -194,7 +209,6 @@ export function markSessionTransportActivity(id) {
194
209
  const now = Date.now();
195
210
  entry.lastTransportAt = now;
196
211
  entry.updatedAt = now;
197
- publishHeartbeat(id, now);
198
212
  }
199
213
  function _normalizeModelProgressKind(kind) {
200
214
  let value = 'semantic';
@@ -211,10 +225,10 @@ export async function markSessionStreamDelta(id, kind = 'semantic') {
211
225
  // creates it before streaming begins). _touchRuntime would instead resurrect a
212
226
  // blank entry — and closeSession()/idle-sweep clear _runtimeState on a deferred
213
227
  // tick while a detached provider stream may still be trickling deltas. A delta
214
- // arriving after that clear must NOT re-create an entry or it would republish the
215
- // .hb heartbeat that markSessionClosed deleted, orphaning a dead session's
216
- // heartbeat indefinitely (the disk tombstone blocks ask resumption but not this
217
- // path). Skip a missing, tombstoned, or aborted entry — never refresh liveness.
228
+ // arriving after that clear must NOT re-create an entry or it would revive a
229
+ // dead session's runtime state (the disk tombstone blocks ask resumption but
230
+ // not this path). Skip a missing, tombstoned, or aborted entry — never refresh
231
+ // liveness.
218
232
  const entry = _runtimeState.get(id);
219
233
  if (!entry || entry.closed || entry.controller?.signal?.aborted || TERMINAL_STAGES.has(entry.stage)) return;
220
234
  const progressKind = _normalizeModelProgressKind(kind);
@@ -240,11 +254,7 @@ export async function markSessionStreamDelta(id, kind = 'semantic') {
240
254
  if (entry.stage === 'connecting' || entry.stage === 'requesting') {
241
255
  entry.stage = 'streaming';
242
256
  }
243
- // Lightweight heartbeat (≤5s self-throttled) for the status aggregator.
244
- // Disk-side session.lastHeartbeatAt below is the heavy 60s zombie-reaper
245
- // signal; the .hb file is the fast fresh-session signal consumed by the
246
- // status line.
247
- publishHeartbeat(id, now);
257
+ // Disk-side session.lastHeartbeatAt is the 60s zombie-reaper signal.
248
258
  const session = entry.session;
249
259
  if (session && now - (session.lastHeartbeatAt || 0) > HEARTBEAT_THROTTLE_MS) {
250
260
  session.lastHeartbeatAt = now;
@@ -271,9 +281,7 @@ export function markSessionToolCall(id, toolName) {
271
281
  entry.lastToolProtocolAt = entry.toolStartedAt;
272
282
  entry.lastProgressAt = entry.toolStartedAt;
273
283
  entry.updatedAt = entry.toolStartedAt;
274
- const heartbeat = publishHeartbeat(id, entry.toolStartedAt);
275
284
  _startToolActivityHeartbeat(id);
276
- return heartbeat;
277
285
  }
278
286
  // Live shell-output tail for the CURRENT running tool. Written by the shell
279
287
  // tool's onOutputTail timer (~1 s cadence), read by engine transcript
@@ -341,10 +349,6 @@ export function markSessionDone(id, { empty = false } = {}) {
341
349
  entry.emptyFinal = false;
342
350
  entry.emptyFinalAt = null;
343
351
  }
344
- // Terminal stage — drop the heartbeat so the status badge releases
345
- // immediately. A subsequent ask on the same session re-publishes via
346
- // markSessionStreamDelta on the first chunk.
347
- return deleteHeartbeat(id);
348
352
  }
349
353
  // Tag a session as having completed with empty final synthesis (no
350
354
  // content/reasoning). Distinct from `markSessionDone`: still a success
@@ -368,9 +372,7 @@ export function markSessionError(id, msg) {
368
372
  // silent empty final). Clear the flag so the next ask starts clean.
369
373
  entry.emptyFinal = false;
370
374
  entry.emptyFinalAt = null;
371
- const heartbeatDeleted = deleteHeartbeat(id);
372
375
  _unlinkParentAbortListener(entry);
373
- return heartbeatDeleted;
374
376
  }
375
377
  export function markSessionCancelled(id) {
376
378
  if (!id) return;
@@ -380,24 +382,19 @@ export function markSessionCancelled(id) {
380
382
  _settleTerminalEntry(entry, 'done', null);
381
383
  entry.emptyFinal = false;
382
384
  entry.emptyFinalAt = null;
383
- const heartbeatDeleted = deleteHeartbeat(id);
384
385
  _unlinkParentAbortListener(entry);
385
- return heartbeatDeleted;
386
386
  }
387
387
  export function getSessionRuntime(id) {
388
388
  return id ? _runtimeState.get(id) || null : null;
389
389
  }
390
390
 
391
- // Stages that still own live provider/tool work: compaction, idle cleanup and
392
- // runtime unload all stay away from an entry in one of them.
393
- export const IN_FLIGHT_STAGES = new Set(['connecting', 'requesting', 'streaming', 'tool_running', 'cancelling']);
391
+ /** Whether this session's turn (provider or tool work) is running now. */
392
+ export function isSessionTurnInFlight(sessionId) {
393
+ return Boolean(sessionId) && isTurnInFlight(_runtimeState.get(sessionId));
394
+ }
394
395
 
395
396
  export function isSessionCompactionBlocked(sessionId) {
396
- if (!sessionId) return false;
397
- const entry = _runtimeState.get(sessionId);
398
- if (!entry || entry.closed === true) return false;
399
- if (entry.controller && !entry.controller.signal?.aborted) return true;
400
- return IN_FLIGHT_STAGES.has(entry.stage);
397
+ return isSessionTurnInFlight(sessionId);
401
398
  }
402
399
 
403
400
  export function getSessionProgressSnapshot(sessionId) {
@@ -418,6 +415,9 @@ export function getSessionProgressSnapshot(sessionId) {
418
415
  firstActivityAt,
419
416
  firstSemanticAt,
420
417
  lastTransportAt: entry.lastTransportAt || 0,
418
+ requestSentAt: entry.requestSentAt || 0,
419
+ // Request on the wire, headers pending: the model is queued or thinking.
420
+ awaitingModelResponse: awaitingModelResponse(entry),
421
421
  lastSemanticAt: entry.lastSemanticAt || 0,
422
422
  lastSemanticKind: entry.lastSemanticKind || null,
423
423
  lastReasoningAt: entry.lastReasoningAt || 0,
@@ -482,14 +482,7 @@ export function getSessionAbortSignal(sessionId) {
482
482
  * either skip the watchdog or treat 0 as "no progress yet".
483
483
  */
484
484
  export function getSessionLastProgressAt(sessionId) {
485
- const entry = _runtimeState.get(sessionId);
486
- if (!entry) return 0;
487
- return Math.max(
488
- entry.lastProgressAt || 0,
489
- entry.lastStreamDeltaAt || 0,
490
- entry.toolStartedAt || 0,
491
- entry.askStartedAt || 0
492
- );
485
+ return lastProgressAt(_runtimeState.get(sessionId));
493
486
  }
494
487
 
495
488
  /**
@@ -0,0 +1,50 @@
1
+ // manager/runtime-state.mjs
2
+ // The process-global runtime liveness map and its turn-activity reads.
3
+ // runtime-liveness.mjs owns every entry and mutation; this leaf has no
4
+ // imports, so a cold reader (the daemon's session catalog) can ask which turns
5
+ // are in flight without loading the session manager graph. Every session runs
6
+ // inline in the daemon, so this map is the whole answer to "is this session's
7
+ // turn running".
8
+
9
+ export const runtimeState = new Map();
10
+
11
+ // Stages that still own live provider/tool work: compaction, idle cleanup and
12
+ // runtime unload all stay away from an entry in one of them.
13
+ export const IN_FLIGHT_STAGES = new Set(['connecting', 'requesting', 'streaming', 'tool_running', 'cancelling']);
14
+
15
+ /** Whether a runtime entry's turn (provider or tool work) is running now. */
16
+ export function isTurnInFlight(entry) {
17
+ if (!entry || entry.closed === true) return false;
18
+ if (entry.controller && !entry.controller.signal?.aborted) return true;
19
+ return IN_FLIGHT_STAGES.has(entry.stage);
20
+ }
21
+
22
+ const cleanLower = (value) =>
23
+ String(value || '')
24
+ .trim()
25
+ .toLowerCase();
26
+
27
+ /**
28
+ * Turn activity of every session this process runs: `working` holds the ids
29
+ * whose turn is in flight, `agentWorking` the root owner (Lead) ids of every
30
+ * in-flight Agent session.
31
+ */
32
+ export function inFlightSessionActivity() {
33
+ const working = new Set();
34
+ const agentWorking = new Set();
35
+ for (const [id, entry] of runtimeState) {
36
+ if (!isTurnInFlight(entry)) continue;
37
+ working.add(id);
38
+ const session = entry.session;
39
+ const ownerSessionId = String(session?.ownerSessionId || session?.parentSessionId || '').trim();
40
+ const agent = cleanLower(session?.agent);
41
+ if (
42
+ ownerSessionId &&
43
+ ownerSessionId !== id &&
44
+ (cleanLower(session?.owner) === 'agent' || (agent && agent !== 'lead'))
45
+ ) {
46
+ agentWorking.add(ownerSessionId);
47
+ }
48
+ }
49
+ return { working, agentWorking };
50
+ }