mixdog 1.2.3 → 1.2.5

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 (469) 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/mobile-release.mjs +5 -4
  5. package/scripts/probe-provider-catalog.mjs +31 -0
  6. package/scripts/provider-pingpong-live.mjs +128 -38
  7. package/scripts/session-sweep.mjs +5 -25
  8. package/src/agents/maintainer/AGENT.md +1 -1
  9. package/src/agents/maintainer/agent.json +1 -1
  10. package/src/agents/reviewer/AGENT.md +2 -4
  11. package/src/agents/security/AGENT.md +1 -2
  12. package/src/agents/writer/AGENT.md +2 -3
  13. package/src/defaults/shipped-history.json +7 -1
  14. package/src/defaults/skills/image/SKILL.md +21 -23
  15. package/src/lib/keychain-cjs.cjs +13 -11
  16. package/src/output-styles/common.md +2 -1
  17. package/src/rules/agent/AGENT.md +7 -5
  18. package/src/rules/lead/LEAD.md +13 -13
  19. package/src/rules/shared/45-destructive.md +4 -0
  20. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/preset.mjs +1 -0
  21. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +28 -7
  22. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -11
  23. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +101 -23
  24. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +2 -0
  25. package/src/runtime/agent/orchestrator/providers/anthropic-api-key-fetch.mjs +36 -0
  26. package/src/runtime/agent/orchestrator/providers/anthropic-error-contract.mjs +1 -1
  27. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -3
  28. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  29. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +26 -44
  30. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +2 -5
  31. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +4 -0
  32. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-catalog.mjs +41 -12
  33. package/src/runtime/agent/orchestrator/providers/antigravity-oauth.mjs +13 -1
  34. package/src/runtime/agent/orchestrator/providers/antigravity-request.mjs +81 -10
  35. package/src/runtime/agent/orchestrator/providers/antigravity-retry-policy.mjs +109 -5
  36. package/src/runtime/agent/orchestrator/providers/antigravity-stream.mjs +2 -1
  37. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +107 -37
  38. package/src/runtime/agent/orchestrator/providers/command-code-oauth.mjs +4 -1
  39. package/src/runtime/agent/orchestrator/providers/cursor-auth.mjs +3 -1
  40. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  41. package/src/runtime/agent/orchestrator/providers/cursor-native-context.mjs +48 -0
  42. package/src/runtime/agent/orchestrator/providers/cursor-wire-exec.mjs +144 -34
  43. package/src/runtime/agent/orchestrator/providers/cursor-wire-protobuf.mjs +28 -4
  44. package/src/runtime/agent/orchestrator/providers/cursor-wire-request.mjs +8 -4
  45. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-bridge.mjs +41 -11
  46. package/src/runtime/agent/orchestrator/providers/cursor-wire-stream-messages.mjs +67 -5
  47. package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +14 -4
  48. package/src/runtime/agent/orchestrator/providers/cursor.mjs +14 -1
  49. package/src/runtime/agent/orchestrator/providers/custom-provider-config.mjs +9 -6
  50. package/src/runtime/agent/orchestrator/providers/custom-provider.mjs +7 -2
  51. package/src/runtime/agent/orchestrator/providers/effort-configuration.mjs +26 -4
  52. package/src/runtime/agent/orchestrator/providers/gemini-cache.mjs +12 -6
  53. package/src/runtime/agent/orchestrator/providers/gemini-error-contract.mjs +14 -6
  54. package/src/runtime/agent/orchestrator/providers/gemini-request-body.mjs +4 -4
  55. package/src/runtime/agent/orchestrator/providers/gemini-response.mjs +1 -0
  56. package/src/runtime/agent/orchestrator/providers/gemini-rest-stream/watchdogs.mjs +16 -5
  57. package/src/runtime/agent/orchestrator/providers/gemini-retry-policy.mjs +6 -1
  58. package/src/runtime/agent/orchestrator/providers/gemini-schema.mjs +22 -10
  59. package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +4 -2
  60. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +33 -7
  61. package/src/runtime/agent/orchestrator/providers/gemini.mjs +11 -2
  62. package/src/runtime/agent/orchestrator/providers/grok-client-version.mjs +1 -1
  63. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +9 -2
  64. package/src/runtime/agent/orchestrator/providers/lib/claude-bridge-schema.mjs +9 -5
  65. package/src/runtime/agent/orchestrator/providers/lib/request-compression.mjs +86 -0
  66. package/src/runtime/agent/orchestrator/providers/lib/wire-pairing.mjs +4 -2
  67. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +16 -5
  68. package/src/runtime/agent/orchestrator/providers/mimo-oauth.mjs +7 -1
  69. package/src/runtime/agent/orchestrator/providers/model-proxy-provider.mjs +60 -16
  70. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +11 -13
  71. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +30 -19
  72. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +5 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +42 -5
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-reasoning-field.mjs +13 -9
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +1 -3
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-parity.fixtures.mjs +18 -7
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +36 -3
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-retry-profile.mjs +51 -3
  80. package/src/runtime/agent/orchestrator/providers/openai-compat.mjs +11 -1
  81. package/src/runtime/agent/orchestrator/providers/openai-http-sse-request.mjs +64 -52
  82. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/terminal-frames.mjs +3 -1
  83. package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +2 -1
  84. package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +7 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-ws-delta.mjs +1 -4
  86. package/src/runtime/agent/orchestrator/providers/openai-ws-response-state/tool-calls.mjs +0 -1
  87. package/src/runtime/agent/orchestrator/providers/openai-ws.mjs +7 -6
  88. package/src/runtime/agent/orchestrator/providers/opencode-go-gemini.mjs +13 -2
  89. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +4 -1
  90. package/src/runtime/agent/orchestrator/providers/openrouter.mjs +1 -2
  91. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +5 -4
  92. package/src/runtime/agent/orchestrator/providers/registry.mjs +9 -1
  93. package/src/runtime/agent/orchestrator/providers/retry-classification.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/wire-gateway.mjs +11 -4
  95. package/src/runtime/agent/orchestrator/providers/wire-route.mjs +14 -4
  96. package/src/runtime/agent/orchestrator/providers/xai-tool-search.fixtures.mjs +15 -3
  97. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +6 -2
  98. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +8 -6
  99. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +4 -1
  100. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +30 -2
  101. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +3 -2
  102. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +8 -3
  103. package/src/runtime/agent/orchestrator/runtime-core/skill-tool-loading.mjs +3 -1
  104. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge-contract.mjs +6 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +10 -10
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +3 -1
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +46 -8
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +10 -8
  109. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +1 -2
  110. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +24 -0
  111. package/src/runtime/agent/orchestrator/session/cache/tool-result-reuse.mjs +10 -2
  112. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -3
  113. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +3 -11
  114. package/src/runtime/agent/orchestrator/session/catalog-db/worker.mjs +6 -22
  115. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +2 -0
  116. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +16 -2
  117. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +28 -0
  118. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +1 -1
  119. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +0 -6
  120. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +0 -6
  121. package/src/runtime/agent/orchestrator/session/eager-dispatch/admission.mjs +1 -1
  122. package/src/runtime/agent/orchestrator/session/historical-session-closure.mjs +10 -0
  123. package/src/runtime/agent/orchestrator/session/loop/assistant-commit.mjs +2 -5
  124. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +2 -4
  125. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +3 -3
  126. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +0 -27
  127. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/output-limit.mjs +1 -1
  128. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/segments.mjs +4 -10
  129. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +2 -9
  130. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +4 -1
  131. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +4 -1
  132. package/src/runtime/agent/orchestrator/session/loop/request-boundary.mjs +1 -4
  133. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +76 -0
  134. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +5 -1
  135. package/src/runtime/agent/orchestrator/session/loop/step-auto-effort.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/loop/tool-classify.mjs +6 -0
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +15 -2
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +1 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +3 -4
  140. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +33 -3
  141. package/src/runtime/agent/orchestrator/session/manager/ask-support.mjs +0 -4
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +21 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +38 -13
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +33 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +5 -0
  146. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +19 -1
  147. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +57 -13
  148. package/src/runtime/agent/orchestrator/session/manager/failed-turn-rewind.mjs +16 -0
  149. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +6 -120
  150. package/src/runtime/agent/orchestrator/session/manager/pending-persist-queue.mjs +1 -1
  151. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +29 -8
  152. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +42 -49
  153. package/src/runtime/agent/orchestrator/session/manager/runtime-state.mjs +50 -0
  154. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +32 -14
  155. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +17 -33
  157. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +29 -13
  158. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +3 -3
  159. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +45 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -9
  161. package/src/runtime/agent/orchestrator/session/send-instrumentation.mjs +2 -1
  162. package/src/runtime/agent/orchestrator/session/send-with-recovery.mjs +5 -2
  163. package/src/runtime/agent/orchestrator/session/side-question.mjs +12 -7
  164. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +5 -12
  165. package/src/runtime/agent/orchestrator/session/store/lifecycle-read.mjs +1 -1
  166. package/src/runtime/agent/orchestrator/session/store/listing.mjs +2 -6
  167. package/src/runtime/agent/orchestrator/session/store/live-cache-eviction.mjs +1 -1
  168. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +46 -37
  169. package/src/runtime/agent/orchestrator/session/store/load-cache.mjs +1 -1
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +5 -11
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +59 -0
  173. package/src/runtime/agent/orchestrator/session/store/summary-cache.mjs +1 -1
  174. package/src/runtime/agent/orchestrator/session/store/sweep/stale-sweep.mjs +1 -1
  175. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-finish.mjs +27 -32
  176. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-plan.mjs +0 -8
  177. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-record.mjs +6 -18
  178. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +33 -106
  179. package/src/runtime/agent/orchestrator/session/store/write-admission.mjs +1 -1
  180. package/src/runtime/agent/orchestrator/session/store-agent-worker-pool.mjs +5 -8
  181. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +160 -212
  182. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +50 -116
  183. package/src/runtime/agent/orchestrator/session/store-summary-locations.mjs +3 -29
  184. package/src/runtime/agent/orchestrator/session/store-summary-reader-catalog-fixture.mjs +1 -1
  185. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +7 -8
  186. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +9 -20
  187. package/src/runtime/agent/orchestrator/session/store.mjs +3 -16
  188. package/src/runtime/agent/orchestrator/session/tool-batch/execute-call.mjs +11 -12
  189. package/src/runtime/agent/orchestrator/session/tool-batch/finalize.mjs +37 -13
  190. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +15 -4
  191. package/src/runtime/agent/orchestrator/session/tool-schema-rejection.mjs +4 -2
  192. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +72 -15
  193. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +45 -0
  194. package/src/runtime/agent/orchestrator/stall-policy.mjs +50 -0
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +5 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +6 -2
  197. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +204 -10
  198. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +160 -39
  199. package/src/runtime/agent/orchestrator/tools/builtin/git-partial-stage.mjs +7 -5
  200. package/src/runtime/agent/orchestrator/tools/builtin/grep-formatting.mjs +14 -5
  201. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +4 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-output.mjs +2 -1
  203. package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +48 -44
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +10 -3
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +200 -17
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +5 -4
  207. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +6 -1
  208. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +20 -0
  209. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +101 -2
  210. package/src/runtime/agent/orchestrator/tools/code-graph-prewarm-worker.mjs +1 -1
  211. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +67 -0
  212. package/src/runtime/agent/orchestrator/tools/lib/process-table-fixture.mjs +50 -0
  213. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +303 -63
  214. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +2 -0
  215. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +108 -18
  216. package/src/runtime/agent/orchestrator/tools/lib/shell-run-spawn.mjs +77 -11
  217. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +81 -5
  218. package/src/runtime/attachments/store.d.mts +9 -0
  219. package/src/runtime/attachments/store.mjs +80 -9
  220. package/src/runtime/browser-bridge/browser-action-contract.d.mts +0 -1
  221. package/src/runtime/browser-bridge/browser-action-contract.mjs +0 -2
  222. package/src/runtime/computer-bridge/action-schema.mjs +178 -178
  223. package/src/runtime/computer-bridge/linux-readiness.d.mts +0 -2
  224. package/src/runtime/computer-bridge/linux-readiness.mjs +0 -16
  225. package/src/runtime/effort-judge/judge-client.mjs +21 -4
  226. package/src/runtime/effort-judge/judge-worker.mjs +4 -1
  227. package/src/runtime/github/tool.mjs +118 -17
  228. package/src/runtime/media/adapters/codex-image.mjs +140 -21
  229. package/src/runtime/media/catalog.mjs +4 -4
  230. package/src/runtime/media/chatgpt-usage.mjs +55 -0
  231. package/src/runtime/media/defaults.mjs +37 -0
  232. package/src/runtime/media/jobs.mjs +12 -2
  233. package/src/runtime/media/lanes.mjs +12 -0
  234. package/src/runtime/media/tool-defs.mjs +15 -4
  235. package/src/runtime/media/tool.mjs +41 -12
  236. package/src/runtime/media/upstream-error.mjs +72 -6
  237. package/src/runtime/memory/lib/ingest/transcript-rows.mjs +1 -0
  238. package/src/runtime/memory/lib/pg/process.mjs +5 -7
  239. package/src/runtime/memory/lib/pg/supervisor.mjs +4 -1
  240. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  241. package/src/runtime/office/authoring/html-browser.mjs +18 -8
  242. package/src/runtime/office/authoring/pdf-html-runner.mjs +1 -1
  243. package/src/runtime/office/authoring/pptx-html-measure.mjs +1 -1
  244. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  245. package/src/runtime/office/core/office-write-ledger.mjs +1 -5
  246. package/src/runtime/office/index.mjs +2 -3
  247. package/src/runtime/office/pdf/pdf-analysis.mjs +0 -68
  248. package/src/runtime/session-search/session-search-ingest.mjs +1 -1
  249. package/src/runtime/shared/agent-reap-state.mjs +13 -0
  250. package/src/runtime/shared/atomic-file.d.mts +1 -1
  251. package/src/runtime/shared/background-tasks.mjs +6 -1
  252. package/src/runtime/shared/bounded-line-diff.mjs +11 -2
  253. package/src/runtime/shared/browser-launch.mjs +15 -0
  254. package/src/runtime/shared/err-text.mjs +42 -4
  255. package/src/runtime/shared/file-lock.mjs +6 -5
  256. package/src/runtime/shared/llm/anthropic-betas.mjs +1 -2
  257. package/src/runtime/shared/llm/provider-model-identities.mjs +1 -0
  258. package/src/runtime/shared/model-route-chain.d.mts +8 -0
  259. package/src/runtime/shared/model-route-chain.mjs +48 -0
  260. package/src/runtime/shared/model-route-fallback.mjs +22 -0
  261. package/src/runtime/shared/pid-liveness.mjs +12 -0
  262. package/src/runtime/shared/provider-api-key.mjs +6 -0
  263. package/src/runtime/shared/provider-timeout-phase.mjs +17 -0
  264. package/src/runtime/shared/quiet-progress.d.mts +35 -0
  265. package/src/runtime/shared/quiet-progress.mjs +101 -0
  266. package/src/runtime/shared/quota-resume-store.mjs +32 -1
  267. package/src/runtime/shared/read-row-numbers.mjs +44 -0
  268. package/src/runtime/shared/remote-intent.mjs +2 -42
  269. package/src/runtime/shared/slow-span.mjs +8 -1
  270. package/src/runtime/shared/statusline/statusline-segments.mjs +24 -2
  271. package/src/runtime/shared/tool-card-model/card-header.mjs +2 -1
  272. package/src/runtime/shared/tool-output-budget.mjs +2 -0
  273. package/src/runtime/shared/tool-result-summary.mjs +16 -2
  274. package/src/runtime/shared/tool-status.mjs +3 -2
  275. package/src/runtime/shared/tool-surface.mjs +2 -3
  276. package/src/runtime/shared/tool-work-units.mjs +22 -15
  277. package/src/runtime/shared/transcript-metadata.mjs +49 -1
  278. package/src/runtime/shared/transcript-turn-keys.mjs +33 -0
  279. package/src/runtime/shared/transcript-writer/transcript-file.mjs +10 -1
  280. package/src/runtime/shared/transcript-writer.mjs +14 -4
  281. package/src/runtime/shared/turn-changes.mjs +2 -1
  282. package/src/runtime/shared/turn-snapshot.mjs +11 -3
  283. package/src/runtime/shared/worktrees/activity-gate.mjs +175 -0
  284. package/src/runtime/shared/worktrees/branch-management.mjs +179 -0
  285. package/src/runtime/shared/worktrees/carry.mjs +6 -4
  286. package/src/runtime/shared/worktrees/contract.mjs +27 -1
  287. package/src/runtime/shared/worktrees/dependencies.mjs +456 -0
  288. package/src/runtime/shared/worktrees/file-recovery.mjs +29 -13
  289. package/src/runtime/shared/worktrees/git-head.mjs +22 -13
  290. package/src/runtime/shared/worktrees/include.mjs +125 -7
  291. package/src/runtime/shared/worktrees/lock.mjs +1 -1
  292. package/src/runtime/shared/worktrees/maintenance.mjs +20 -2
  293. package/src/runtime/shared/worktrees/manager.mjs +378 -32
  294. package/src/runtime/shared/worktrees/owner.mjs +2 -1
  295. package/src/runtime/shared/worktrees/preparation-report.mjs +79 -0
  296. package/src/runtime/shared/worktrees/setup-commands.mjs +232 -0
  297. package/src/runtime/shared/worktrees/workspace-visibility.mjs +17 -0
  298. package/src/runtime/terminal-bridge/client.mjs +14 -10
  299. package/src/session-runtime/boot/apis.mjs +7 -1
  300. package/src/session-runtime/boot/config.mjs +2 -0
  301. package/src/session-runtime/boot/facade.mjs +3 -0
  302. package/src/session-runtime/boot/session.mjs +3 -2
  303. package/src/session-runtime/boot/tools.mjs +3 -0
  304. package/src/session-runtime/conversation-import/service.mjs +1 -1
  305. package/src/session-runtime/goal-facade-api.mjs +2 -2
  306. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +44 -18
  307. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +4 -1
  308. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +22 -8
  309. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  310. package/src/session-runtime/lifecycle/cold-heir.mjs +0 -1
  311. package/src/session-runtime/lifecycle/session-catalog.mjs +14 -16
  312. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +18 -53
  313. package/src/session-runtime/lifecycle/session-switch.mjs +1 -1
  314. package/src/session-runtime/lifecycle/shared.mjs +5 -6
  315. package/src/session-runtime/lifecycle/teardown/session-close.mjs +6 -5
  316. package/src/session-runtime/lifecycle/teardown.mjs +7 -16
  317. package/src/session-runtime/media-api.mjs +8 -0
  318. package/src/session-runtime/model-route/route-ownership.mjs +60 -0
  319. package/src/session-runtime/model-route/route-persist.mjs +3 -2
  320. package/src/session-runtime/model-route/set-route.mjs +114 -34
  321. package/src/session-runtime/model-route/web-search-route.mjs +7 -2
  322. package/src/session-runtime/model-route-api.mjs +4 -2
  323. package/src/session-runtime/native-web-search/routes.mjs +37 -24
  324. package/src/session-runtime/native-web-search.mjs +9 -8
  325. package/src/session-runtime/provider-auth/setup.mjs +4 -0
  326. package/src/session-runtime/provider-models/catalog-load.mjs +9 -0
  327. package/src/session-runtime/provider-models/web-search-catalog.mjs +23 -3
  328. package/src/session-runtime/provider-models.mjs +1 -0
  329. package/src/session-runtime/provider-usage.mjs +4 -0
  330. package/src/session-runtime/route-resolution.mjs +3 -1
  331. package/src/session-runtime/runtime-core.mjs +4 -0
  332. package/src/session-runtime/runtime-facade.mjs +6 -0
  333. package/src/session-runtime/services/agent-task-status.mjs +2 -0
  334. package/src/session-runtime/services/agent-tool/close-flow/close-all.mjs +0 -3
  335. package/src/session-runtime/services/agent-tool/close-flow.mjs +6 -7
  336. package/src/session-runtime/services/agent-tool/execute-send.mjs +2 -2
  337. package/src/session-runtime/services/agent-tool/execute-spawn.mjs +36 -3
  338. package/src/session-runtime/services/agent-tool/execute.mjs +9 -1
  339. package/src/session-runtime/services/agent-tool/helpers.mjs +30 -7
  340. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +7 -2
  341. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +6 -3
  342. package/src/session-runtime/services/agent-tool/lead-worker-index/lead-rows.mjs +3 -4
  343. package/src/session-runtime/services/agent-tool/lead-worker-index/reap-timers.mjs +9 -3
  344. package/src/session-runtime/services/agent-tool/lead-worker-index/row-settlement.mjs +7 -18
  345. package/src/session-runtime/services/agent-tool/lead-worker-index.mjs +9 -6
  346. package/src/session-runtime/services/agent-tool/model-select.mjs +82 -0
  347. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -6
  348. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +3 -1
  349. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +19 -5
  350. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -4
  351. package/src/session-runtime/services/agent-tool/spawn-flow.mjs +2 -1
  352. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +3 -0
  353. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +1 -3
  354. package/src/session-runtime/services/agent-tool/tag-registry/tag-refresh.mjs +1 -3
  355. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -9
  356. package/src/session-runtime/services/agent-tool/terminal-reaper/scan-admission.mjs +8 -41
  357. package/src/session-runtime/services/agent-tool/terminal-reaper/stale-rows.mjs +3 -3
  358. package/src/session-runtime/services/agent-tool/terminal-reaper.mjs +6 -23
  359. package/src/session-runtime/services/agent-tool/terminal-trace.mjs +0 -1
  360. package/src/session-runtime/services/agent-tool/tool-def.mjs +7 -0
  361. package/src/session-runtime/services/agent-tool/worker-index/row-liveness.mjs +32 -20
  362. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +5 -2
  363. package/src/session-runtime/services/agent-tool/worker-index.mjs +28 -8
  364. package/src/session-runtime/services/agent-tool.mjs +4 -0
  365. package/src/session-runtime/services/agent-watchdog-registry.mjs +2 -0
  366. package/src/session-runtime/services/provider-admin.mjs +77 -0
  367. package/src/session-runtime/session-create/session-options.mjs +7 -5
  368. package/src/session-runtime/session-create/session-start-hook.mjs +2 -1
  369. package/src/session-runtime/session-transcript.mjs +4 -5
  370. package/src/session-runtime/session-turn-api.mjs +3 -9
  371. package/src/session-runtime/settings-builtin-tools-api.mjs +9 -6
  372. package/src/session-runtime/setup-tool/executor.mjs +23 -5
  373. package/src/session-runtime/setup-tool/extended-actions.mjs +6 -6
  374. package/src/session-runtime/setup-tool/settings-contract.mjs +7 -2
  375. package/src/session-runtime/setup-tool/tool-defs.mjs +13 -1
  376. package/src/session-runtime/tool-policy-refresh.mjs +83 -32
  377. package/src/session-runtime/turn/agent-ops.mjs +49 -2
  378. package/src/session-runtime/turn/side-question-ops.mjs +1 -4
  379. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +2 -0
  380. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +4 -0
  381. package/src/session-runtime/turn/turn-run/turn-settle.mjs +8 -2
  382. package/src/session-runtime/turn/turn-run.mjs +1 -36
  383. package/src/session-runtime/usage-stats-api.mjs +4 -1
  384. package/src/session-runtime/workflow-agents-api/agent-route.mjs +16 -1
  385. package/src/session-runtime/workspace/admission-gate.mjs +44 -15
  386. package/src/session-runtime/workspace/project-branches.mjs +28 -36
  387. package/src/session-runtime/workspace/transition-steps.mjs +96 -95
  388. package/src/session-runtime/workspace/transitions.mjs +248 -68
  389. package/src/standalone/channel-transport.mjs +3 -4
  390. package/src/standalone/daemon-desktop-runtime.mjs +32 -1
  391. package/src/standalone/daemon-pg-stop.mjs +1 -3
  392. package/src/standalone/daemon-session-control.mjs +88 -0
  393. package/src/standalone/daemon-stored-session-views.mjs +23 -3
  394. package/src/standalone/daemon.mjs +2 -0
  395. package/src/standalone/desktop-service-registry.mjs +32 -5
  396. package/src/standalone/local-session-runtime.mjs +11 -12
  397. package/src/standalone/session-protocol.mjs +7 -0
  398. package/src/standalone/session-runtime-inline-host.mjs +2 -0
  399. package/src/standalone/session-service/agent-tree/agent-cancel.mjs +91 -49
  400. package/src/standalone/session-service/agent-tree/agent-child.mjs +19 -1
  401. package/src/standalone/session-service/agent-tree/agent-registry.mjs +25 -0
  402. package/src/standalone/session-service/agent-tree/agent-turns.mjs +9 -1
  403. package/src/standalone/session-service/agent-tree.mjs +28 -2
  404. package/src/standalone/session-service/entries/session-loads.mjs +16 -5
  405. package/src/standalone/session-service/entries.mjs +1 -0
  406. package/src/standalone/session-service/projection/transcript-window.mjs +39 -0
  407. package/src/standalone/session-service/quota-resume.mjs +28 -13
  408. package/src/standalone/session-service/session-calls/session-turns.mjs +17 -6
  409. package/src/standalone/session-service/session-calls/session-views.mjs +6 -0
  410. package/src/standalone/session-tool-output.mjs +82 -0
  411. package/src/tui/app/app-view.jsx +2 -1
  412. package/src/tui/app/provider-setup/provider-items.mjs +8 -0
  413. package/src/tui/app/provider-setup-picker.mjs +15 -3
  414. package/src/tui/app/slash-commands.mjs +6 -0
  415. package/src/tui/app/slash-dispatch/session-commands.mjs +49 -0
  416. package/src/tui/components/Spinner.jsx +4 -0
  417. package/src/tui/components/SpinnerByline.jsx +21 -2
  418. package/src/tui/components/status-line/local-line.mjs +10 -2
  419. package/src/tui/dist/index.mjs +529 -270
  420. package/src/tui/session/active-tool-summary.mjs +1 -1
  421. package/src/tui/session/context-state.mjs +0 -3
  422. package/src/tui/session/goal-continuation/continuation-queue.mjs +2 -3
  423. package/src/tui/session/goal-continuation/deadline-delivery.mjs +4 -2
  424. package/src/tui/session/goal-continuation/session-boundary.mjs +21 -0
  425. package/src/tui/session/quota-resume.mjs +140 -61
  426. package/src/tui/session/session-api/commands.mjs +4 -10
  427. package/src/tui/session/session-api/controls.mjs +12 -0
  428. package/src/tui/session/session-api/lifecycle/dispose.mjs +2 -9
  429. package/src/tui/session/session-api/lifecycle/resume.mjs +2 -7
  430. package/src/tui/session/session-api/lifecycle.mjs +13 -11
  431. package/src/tui/session/session-api/media.mjs +2 -0
  432. package/src/tui/session/session-api/routes.mjs +81 -57
  433. package/src/tui/session/session-api/settings.mjs +3 -3
  434. package/src/tui/session/session-api/worktree.mjs +3 -2
  435. package/src/tui/session/session-flow/auto-clear.mjs +1 -3
  436. package/src/tui/session/session-flow/drain.mjs +6 -4
  437. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -0
  438. package/src/tui/session/session-flow/queue/take-entries.mjs +2 -1
  439. package/src/tui/session/session-flow/queue.mjs +1 -1
  440. package/src/tui/session/tool-approval.mjs +17 -8
  441. package/src/tui/session/transcript-store/streaming-tail.mjs +1 -1
  442. package/src/tui/session/tui-steering-persist.mjs +3 -5
  443. package/src/tui/session/turn-quiet.mjs +53 -0
  444. package/src/tui/session/turn-stream.mjs +5 -1
  445. package/src/tui/session/turn.mjs +38 -31
  446. package/src/tui/session/worktree-setup-notice.mjs +38 -0
  447. package/src/tui/spinner-meta.mjs +10 -0
  448. package/src/tui/spinner-verbs.mjs +3 -0
  449. package/src/ui/statusline.mjs +13 -3
  450. package/src/ui/streaming-markdown-heal.mjs +70 -18
  451. package/src/runtime/agent/orchestrator/providers/model-pricing-rates.mjs +0 -1
  452. package/src/runtime/agent/orchestrator/session/manager/foreign-pending-messages.mjs +0 -346
  453. package/src/runtime/agent/orchestrator/session/manager/session-owner-liveness.mjs +0 -117
  454. package/src/runtime/agent/orchestrator/session/store/paths-heartbeat.mjs +0 -302
  455. package/src/session-runtime/services/agent-tool/terminal-reaper/reap-timers.mjs +0 -142
  456. package/src/session-runtime/turn/share-ops.mjs +0 -77
  457. package/src/tui/session/cross-surface-share.mjs +0 -406
  458. package/src/tui/session/live-share/delta.mjs +0 -153
  459. package/src/tui/session/live-share/mirror.mjs +0 -106
  460. package/src/tui/session/live-share/owner.mjs +0 -254
  461. package/src/tui/session/live-share/retry.mjs +0 -54
  462. package/src/tui/session/live-share/viewer/frame-router.mjs +0 -32
  463. package/src/tui/session/live-share/viewer/pipe-client.mjs +0 -108
  464. package/src/tui/session/live-share/viewer/submit-acks.mjs +0 -65
  465. package/src/tui/session/live-share/viewer/sync-waiters.mjs +0 -53
  466. package/src/tui/session/live-share/viewer.mjs +0 -99
  467. package/src/tui/session/live-share/wire.mjs +0 -44
  468. package/src/tui/session/live-share.mjs +0 -154
  469. package/src/tui/session/shared-dir-watch.mjs +0 -69
@@ -38,8 +38,8 @@ export function createSendDispatch({ registry, defaultCwd, sendFlow, spawnFlow,
38
38
  if (inherited.tombstone) registry.consumeTagTombstone(inherited.tombstone);
39
39
  }
40
40
 
41
- // Reaped/dead-tag fallback: with the 5m terminal-reap window a same-scope
42
- // follow-up often lands after the session is gone. Instead of bouncing an
41
+ // Dead-tag fallback: a same-scope follow-up can land after its session was
42
+ // closed or deleted. Instead of bouncing an
43
43
  // error back to Lead (who would just re-issue the same content as a spawn),
44
44
  // respawn a FRESH session under the same tag with the message as its brief.
45
45
  // `respawned: true` in the result tells Lead the worker has no prior session
@@ -1,4 +1,22 @@
1
1
  import { clean, rowMatchesContext } from './helpers.mjs';
2
+ import { resolveRequestedModel } from './model-select.mjs';
3
+
4
+ // What an explicit (already resolved) spawn asks a reused session to change:
5
+ // the model, the tuning, or both — only what it names, or null when it names
6
+ // nothing. It is a partial request: the session's route write completes it
7
+ // against the latest selected route (and drops it when nothing differs), so
8
+ // no descriptor snapshot decides what the other tuning becomes.
9
+ function requestedRoute(args) {
10
+ const model = clean(args.model);
11
+ const effort = clean(args.effort);
12
+ const route = {
13
+ ...(model ? { provider: clean(args.provider), model } : {}),
14
+ ...(effort ? { effort } : {}),
15
+ ...(args.fast === undefined ? {} : { fast: args.fast === true }),
16
+ ...(args.autoEffort === undefined ? {} : { autoEffort: args.autoEffort !== false }),
17
+ };
18
+ return Object.keys(route).length ? route : null;
19
+ }
2
20
 
3
21
  // Spawn request dispatch: deciding what an explicitly tagged spawn means before
4
22
  // a job is started, and starting it.
@@ -6,7 +24,14 @@ import { clean, rowMatchesContext } from './helpers.mjs';
6
24
  // Inputs: `registry` (tag resolution, live sessions, tombstones),
7
25
  // `terminalTrace`, `sendFlow` (reuse path), `spawnFlow`, and `spawnResult`
8
26
  // (job -> rendered tool output). Output: { spawn }.
9
- export function createSpawnDispatch({ registry, terminalTrace, sendFlow, spawnFlow, spawnResult }) {
27
+ export function createSpawnDispatch({
28
+ registry,
29
+ terminalTrace,
30
+ sendFlow,
31
+ spawnFlow,
32
+ spawnResult,
33
+ listProviderModels = null,
34
+ }) {
10
35
  // Explicit-tag spawn priority (auto nextTag always creates a fresh session):
11
36
  // 1) live + busy -> queue the prompt (reuse)
12
37
  // 2) live + idle -> continue existing session (reuse)
@@ -26,7 +51,12 @@ export function createSpawnDispatch({ registry, terminalTrace, sendFlow, spawnFl
26
51
  }
27
52
  if (liveSessionId && registry.getLiveSession(liveSessionId)) {
28
53
  const prepared = await sendFlow.prepareSend({ ...args, tag: explicitTag }, scopedContext);
29
- return { reused: sendFlow.dispatchToExistingSession(prepared, notifyContext, { reused: true }) };
54
+ const route = requestedRoute(args);
55
+ return {
56
+ reused: sendFlow.dispatchToExistingSession(route ? { ...prepared, route } : prepared, notifyContext, {
57
+ reused: true,
58
+ }),
59
+ };
30
60
  }
31
61
  if (terminalTrace.hasTerminalTrace(explicitTag, scopedContext)) {
32
62
  terminalTrace.reapTerminalTraceForTag(explicitTag, scopedContext);
@@ -52,7 +82,10 @@ export function createSpawnDispatch({ registry, terminalTrace, sendFlow, spawnFl
52
82
  return { spawnArgs: args, respawned: false };
53
83
  }
54
84
 
55
- async function spawn(args, request) {
85
+ async function spawn(requestArgs, request) {
86
+ // Resolve a user-pinned model before deferring, so a miss or an ambiguous
87
+ // name answers this call instead of failing the background job.
88
+ const args = await resolveRequestedModel(requestArgs, listProviderModels);
56
89
  const explicitTag = clean(args.tag);
57
90
  let spawnArgs = args;
58
91
  let respawned = false;
@@ -17,6 +17,7 @@ export function createAgentExecute({
17
17
  sendFlow,
18
18
  closeFlow,
19
19
  terminalTrace,
20
+ listProviderModels = null,
20
21
  }) {
21
22
  const spawnResult = (job) => renderResult(views.renderJob(job, false));
22
23
 
@@ -30,7 +31,14 @@ export function createAgentExecute({
30
31
  }
31
32
 
32
33
  const { send } = createSendDispatch({ registry, defaultCwd, sendFlow, spawnFlow, spawnResult });
33
- const { spawn } = createSpawnDispatch({ registry, terminalTrace, sendFlow, spawnFlow, spawnResult });
34
+ const { spawn } = createSpawnDispatch({
35
+ registry,
36
+ terminalTrace,
37
+ sendFlow,
38
+ spawnFlow,
39
+ spawnResult,
40
+ listProviderModels,
41
+ });
34
42
 
35
43
  return async function execute(args = {}, context = {}) {
36
44
  try {
@@ -316,29 +316,52 @@ function agentDefinitionFiles(cleanAgent, dataDir, standaloneSourceRoot) {
316
316
  return candidates;
317
317
  }
318
318
 
319
- export function readAgentFrontmatterPermission(agent, dataDir, standaloneSourceRoot) {
319
+ const _frontmatterFirstResponseCache = new Map(); // key -> { value, atMs }
320
+
321
+ // The first definition (in precedence order) whose frontmatter yields a value
322
+ // through `pick`, cached per agent for the frontmatter TTL.
323
+ function cachedFrontmatterValue(cache, agent, dataDir, standaloneSourceRoot, pick) {
320
324
  const cleanAgent = clean(agent);
321
325
  if (!cleanAgent) return null;
322
326
  if (agentDeleted(cleanAgent, dataDir)) return null;
323
327
  const cacheKey = `${dataDir || ''}\u0000${cleanAgent}`;
324
- const cached = _frontmatterPermCache.get(cacheKey);
328
+ const cached = cache.get(cacheKey);
325
329
  if (cached && Date.now() - cached.atMs < FRONTMATTER_PERM_CACHE_TTL_MS) {
326
330
  return cached.value;
327
331
  }
328
332
  let resolved = null;
329
333
  for (const file of agentDefinitionFiles(cleanAgent, dataDir, standaloneSourceRoot)) {
330
334
  if (!existsSync(file)) continue;
331
- const fm = parseMarkdownFrontmatter(readFileSync(file, 'utf8'));
332
- const permission = normalizeAgentPermissionOrNone(fm.permission);
333
- if (permission) {
334
- resolved = permission;
335
+ const value = pick(parseMarkdownFrontmatter(readFileSync(file, 'utf8')));
336
+ if (value != null) {
337
+ resolved = value;
335
338
  break;
336
339
  }
337
340
  }
338
- _frontmatterPermCache.set(cacheKey, { value: resolved, atMs: Date.now() });
341
+ cache.set(cacheKey, { value: resolved, atMs: Date.now() });
339
342
  return resolved;
340
343
  }
341
344
 
345
+ export function readAgentFrontmatterPermission(agent, dataDir, standaloneSourceRoot) {
346
+ return cachedFrontmatterValue(
347
+ _frontmatterPermCache,
348
+ agent,
349
+ dataDir,
350
+ standaloneSourceRoot,
351
+ (fm) => normalizeAgentPermissionOrNone(fm.permission) || null
352
+ );
353
+ }
354
+
355
+ /** The agent definition's `firstResponseTimeoutMs` (a non-negative integer;
356
+ * 0 disables the first-response deadline), or null when it sets none. */
357
+ export function readAgentFirstResponseTimeoutMs(agent, dataDir, standaloneSourceRoot) {
358
+ return cachedFrontmatterValue(_frontmatterFirstResponseCache, agent, dataDir, standaloneSourceRoot, (fm) => {
359
+ const raw = String(fm.firstResponseTimeoutMs ?? '').trim();
360
+ const ms = Number(raw);
361
+ return raw && Number.isFinite(ms) && ms >= 0 ? Math.floor(ms) : null;
362
+ });
363
+ }
364
+
342
365
  // Agents are global definitions (agents/<id>/ under the data dir or the
343
366
  // shipped source root). A spawn of an id with no definition anywhere —
344
367
  // typically a custom agent deleted in settings — must fail loudly instead of
@@ -57,13 +57,18 @@ export function terminalResultHook({ job, turnReview, completionValue, notifyEar
57
57
  };
58
58
  }
59
59
 
60
- /** Watchdog stall that still recovered a partial handoff: a completion. */
60
+ /** Watchdog stall that still recovered a partial handoff: the turn never
61
+ * finished, so the owner is told it FAILED (stalled), with the partial
62
+ * output it produced before the stop. */
63
+ export const WATCHDOG_STOP_ERROR = 'stalled: the progress watchdog stopped the turn';
64
+
61
65
  export function reconcileJobWatchdogPartial(job, value) {
62
66
  if (!job?.taskId) return;
63
67
  try {
64
68
  reconcileBackgroundTask(job.taskId, {
65
- status: 'completed',
69
+ status: 'failed',
66
70
  result: value,
71
+ error: WATCHDOG_STOP_ERROR,
67
72
  terminalReason: 'agent-watchdog-partial',
68
73
  });
69
74
  } catch {}
@@ -38,7 +38,7 @@ export function terminalJobFrozenFields(status, now = Date.now()) {
38
38
  }
39
39
 
40
40
  /** A worker/job row as the published session state carries it. That state
41
- * (statusline, desktop, phone, live-share viewers) re-reads agent status on a
41
+ * (statusline, desktop, phone) re-reads agent status on a
42
42
  * 2s pulse, and these fields are the agent card's diagnostics, derived from
43
43
  * the read time or the last stream delta: they changed on every read while
44
44
  * nothing a surface shows did, so each pulse re-sent both row lists. Surfaces
@@ -64,7 +64,10 @@ export function createSessionProgress({ mgr }) {
64
64
  const runtime = mgr.getSessionRuntime?.(sessionId) || null;
65
65
  const snapshot =
66
66
  typeof mgr.getSessionProgressSnapshot === 'function' ? mgr.getSessionProgressSnapshot(sessionId) : null;
67
- const policy = role ? resolveAgentWatchdogPolicy(role) : null;
67
+ // The armed watchdog's own policy (agent definition and provider budget
68
+ // applied); the role default only when none is registered.
69
+ const watchdogState = getProgressWatchdogState(mgr, sessionId);
70
+ const policy = watchdogState.policy || (role ? resolveAgentWatchdogPolicy(role) : null);
68
71
  const queuedFollowups =
69
72
  typeof mgr.getSessionPendingMessageDepth === 'function' ? mgr.getSessionPendingMessageDepth(sessionId) : null;
70
73
  return buildAgentTaskProgressFields({
@@ -74,7 +77,7 @@ export function createSessionProgress({ mgr }) {
74
77
  snapshot,
75
78
  runtime,
76
79
  policy,
77
- watchdogState: getProgressWatchdogState(mgr, sessionId),
80
+ watchdogState,
78
81
  queuedFollowups,
79
82
  taskStatus,
80
83
  lastToolCall: runtime?.lastToolCall || null,
@@ -6,10 +6,9 @@ import { leadPoolTag } from '../worker-rows.mjs';
6
6
  export const ACTIVE_LEAD_STATUS =
7
7
  /^(?:connecting|requesting|streaming|tool[-_\s]?running|running|queued|pending|starting|cancelling)$/i;
8
8
 
9
- // Mirror of the pool reader's freshness window (store-summary-reader): a row is
10
- // only believed to be working while its heartbeat sidecar or its own stamp sits
11
- // inside this window. Recovery applies the SAME rule, so the index can never
12
- // keep claiming work the panel has already stopped believing.
9
+ // Recovery's freshness window: an active row whose turn is not in flight in
10
+ // this daemon is only believed while its own stamp sits inside this window
11
+ // (it covers a row written just before its turn's runtime entry exists).
13
12
  export const LEAD_POOL_FRESH_MS = 2 * 60 * 1000;
14
13
 
15
14
  export const isActiveLeadRow = (row) => ACTIVE_LEAD_STATUS.test(clean(row?.status || row?.stage));
@@ -1,5 +1,7 @@
1
- // One reap timer per settled Lead row: fires at the row's reapAt and removes
2
- // the row only when it still carries that same deadline.
1
+ // One reap timer per settled Lead row: fires at the row's reapAt — never
2
+ // before the row has been listed in the Agents pane for its full visibility
3
+ // window — and removes the row only when it still carries that same deadline.
4
+ import { AGENT_PANE_VISIBLE_MS } from '../../../../runtime/shared/agent-reap-state.mjs';
3
5
  import { clean } from '../helpers.mjs';
4
6
 
5
7
  export function createLeadReapTimers({ removeRow }) {
@@ -18,7 +20,11 @@ export function createLeadReapTimers({ removeRow }) {
18
20
  const reapAt = clean(row?.reapAt);
19
21
  if (!sessionId || !reapAt) return;
20
22
  cancel(sessionId);
21
- const delay = Math.max(0, (Date.parse(reapAt) || 0) - Date.now());
23
+ // The provider reap window may be shorter than the pane window; deleting
24
+ // the row then would drop a finished Lead the pane still has to show.
25
+ const finishedMs = Date.parse(clean(row.finishedAt)) || Date.parse(clean(row.updatedAt)) || 0;
26
+ const deadline = Math.max(Date.parse(reapAt) || 0, finishedMs + AGENT_PANE_VISIBLE_MS);
27
+ const delay = Math.max(0, deadline - Date.now());
22
28
  const handle = setTimeout(() => {
23
29
  reapTimers.delete(sessionId);
24
30
  removeRow(sessionId, reapAt);
@@ -1,34 +1,23 @@
1
1
  // When an active Lead row is no longer believable, and how it settles to idle:
2
- // runtime liveness, the heartbeat sidecar, the stamp freshness window, and the
3
- // terminal reap deadline from config.
4
- import { statSync } from 'node:fs';
5
- import { resolve } from 'node:path';
2
+ // runtime liveness, the daemon's in-memory turn state, the stamp freshness
3
+ // window, and the terminal reap deadline from config.
6
4
  import { clean, runtimeAlive } from '../helpers.mjs';
7
5
  import { createRowLiveness } from '../worker-index/row-liveness.mjs';
8
6
  import { isActiveLeadRow, LEAD_POOL_FRESH_MS } from './lead-rows.mjs';
9
7
 
10
- export function createRowSettlement({ dataDir, cfgMod }) {
8
+ /** `isSessionRunning(sessionId)`: whether that Lead's turn is in flight in
9
+ * this process (every session runs inline in the daemon). */
10
+ export function createRowSettlement({ cfgMod, isSessionRunning = () => false }) {
11
11
  // Lead rows settle to idle exactly like worker rows: same stamps, same
12
12
  // config-driven terminal reap deadline.
13
- const { idleRow } = createRowLiveness({ dataDir, cfgMod });
14
-
15
- function leadHeartbeatFresh(sessionId, now) {
16
- const id = clean(sessionId);
17
- if (!dataDir || !id) return false;
18
- try {
19
- const mtimeMs = statSync(resolve(dataDir, 'sessions', `${id}.hb`)).mtimeMs || 0;
20
- return mtimeMs > 0 && now - mtimeMs <= LEAD_POOL_FRESH_MS;
21
- } catch {
22
- return false;
23
- }
24
- }
13
+ const { idleRow } = createRowLiveness({ cfgMod });
25
14
 
26
15
  function staleActiveLeadRow(row, now) {
27
16
  if (!isActiveLeadRow(row)) return false;
28
17
  // The runtime that stamped the turn is gone: no wall-clock window can make
29
18
  // that row true again, so the panel must not wait one out.
30
19
  if (!runtimeAlive(row.runtimePid)) return true;
31
- if (leadHeartbeatFresh(row.sessionId, now)) return false;
20
+ if (isSessionRunning(clean(row.sessionId))) return false;
32
21
  const updated = Date.parse(clean(row.updatedAt)) || 0;
33
22
  return !(updated > 0 && now - updated <= LEAD_POOL_FRESH_MS);
34
23
  }
@@ -17,11 +17,11 @@ import { createRowSettlement } from './lead-worker-index/row-settlement.mjs';
17
17
  import { createLeadReapTimers } from './lead-worker-index/reap-timers.mjs';
18
18
  import { createForgottenLeads } from './lead-worker-index/forgotten-leads.mjs';
19
19
 
20
- export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession }) {
20
+ export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession, isSessionRunning }) {
21
21
  const activeLeadSessions = new Set();
22
22
  const index = createLeadIndexFile({ dataDir });
23
23
  const forgotten = createForgottenLeads(dataDir);
24
- const settlement = createRowSettlement({ dataDir, cfgMod });
24
+ const settlement = createRowSettlement({ cfgMod, isSessionRunning });
25
25
  const reaps = createLeadReapTimers({ removeRow: (sessionId, reapAt) => removeLeadWorkerRow(sessionId, reapAt) });
26
26
 
27
27
  function removeLeadWorkerRow(sessionId, expectedReapAt = '') {
@@ -73,8 +73,11 @@ export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession })
73
73
  updatedAt: new Date(now).toISOString(),
74
74
  });
75
75
  if (!row) return false;
76
+ const settled = !ACTIVE_LEAD_STATUS.test(status) && !ACTIVE_LEAD_STATUS.test(stage);
76
77
  const normalized = {
77
78
  ...row,
79
+ // The pane lists a settled Lead for a fixed window from this stamp.
80
+ finishedAt: settled ? row.finishedAt || new Date(now).toISOString() : row.finishedAt,
78
81
  // Liveness identity of the runtime that owns this status. A row stamped
79
82
  // by a process that no longer exists is recovered on the next boot.
80
83
  runtimePid: process.pid,
@@ -89,12 +92,12 @@ export function createLeadWorkerIndex({ dataDir, cfgMod, workerRowFromSession })
89
92
  },
90
93
  { rerun: true }
91
94
  );
92
- if (ACTIVE_LEAD_STATUS.test(status) || ACTIVE_LEAD_STATUS.test(stage)) {
93
- activeLeadSessions.add(session.id);
94
- reaps.cancel(session.id);
95
- } else {
95
+ if (settled) {
96
96
  activeLeadSessions.delete(session.id);
97
97
  reaps.schedule(normalized);
98
+ } else {
99
+ activeLeadSessions.add(session.id);
100
+ reaps.cancel(session.id);
98
101
  }
99
102
  return true;
100
103
  }
@@ -0,0 +1,82 @@
1
+ // One-off model selection for a spawn the user explicitly pinned to a model.
2
+ // The agent's preset route stays the default; this only runs when `model` is
3
+ // passed. The query is matched against the live provider catalog — exact id,
4
+ // then exact display name, then every query word — and a catalog miss retries
5
+ // once on a refreshed catalog. One match runs; several or none fail with
6
+ // candidates instead of substituting another model.
7
+ import { clean } from './helpers.mjs';
8
+
9
+ const CANDIDATE_LIMIT = 8;
10
+
11
+ const norm = (value) =>
12
+ String(value || '')
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9]+/g, '');
15
+ const terms = (value) =>
16
+ String(value || '')
17
+ .toLowerCase()
18
+ .split(/\s+/)
19
+ .map(norm)
20
+ .filter(Boolean);
21
+ const label = (row) => `${row.provider}/${row.id}`;
22
+
23
+ function providerRows(rows, provider) {
24
+ if (!provider) return rows;
25
+ const wanted = norm(provider);
26
+ const exact = rows.filter((row) => norm(row.provider) === wanted);
27
+ return exact.length ? exact : rows.filter((row) => norm(row.provider).includes(wanted));
28
+ }
29
+
30
+ function matchModels(rows, query) {
31
+ const wanted = norm(query);
32
+ const words = terms(query);
33
+ // Nothing to match on ("???"): `[].every` would accept every row.
34
+ if (!words.length) return [];
35
+ const tiers = [
36
+ (row) => norm(row.id) === wanted,
37
+ (row) => norm(row.display) === wanted,
38
+ (row) => words.every((word) => `${norm(row.id)} ${norm(row.display)}`.includes(word)),
39
+ ];
40
+ for (const test of tiers) {
41
+ const hits = rows.filter(test);
42
+ if (hits.length) return hits;
43
+ }
44
+ return [];
45
+ }
46
+
47
+ function candidates(rows) {
48
+ const shown = rows.slice(0, CANDIDATE_LIMIT).map(label).join(', ');
49
+ return rows.length > CANDIDATE_LIMIT ? `${shown}, … (${rows.length} total)` : shown;
50
+ }
51
+
52
+ /** Resolve args.provider/args.model to one exact catalog route. Without a
53
+ * catalog (headless/daemon surfaces) exact provider + model pass through. */
54
+ export async function resolveRequestedModel(args, listProviderModels) {
55
+ const model = clean(args.model);
56
+ const provider = clean(args.provider);
57
+ if (!model) {
58
+ if (provider) throw new Error('agent spawn: provider requires model');
59
+ return args;
60
+ }
61
+ if (typeof listProviderModels !== 'function') {
62
+ if (!provider) throw new Error('agent spawn: model requires provider on this surface');
63
+ // Punctuation alone names no model; the catalog path rejects it the same way.
64
+ if (!terms(model).length) throw new Error(`agent spawn: model "${model}" for provider "${provider}" not found`);
65
+ return args;
66
+ }
67
+ let pool = providerRows(await listProviderModels(), provider);
68
+ let hits = matchModels(pool, model);
69
+ if (!hits.length) {
70
+ pool = providerRows(await listProviderModels({ force: true }), provider);
71
+ hits = matchModels(pool, model);
72
+ }
73
+ if (hits.length === 1) return { ...args, provider: hits[0].provider, model: hits[0].id };
74
+ const scope = provider ? ` for provider "${provider}"` : '';
75
+ if (hits.length > 1) {
76
+ throw new Error(`agent spawn: model "${model}"${scope} is ambiguous: ${candidates(hits)}; pass the exact one`);
77
+ }
78
+ if (!pool.length) {
79
+ throw new Error(`agent spawn: provider "${provider}" has no available models`);
80
+ }
81
+ throw new Error(`agent spawn: model "${model}"${scope} not found; available: ${candidates(pool)}`);
82
+ }
@@ -1,4 +1,5 @@
1
1
  import {
2
+ isAgentProgressWatchdogAbortError,
2
3
  resolveAgentWatchdogPolicy,
3
4
  resolveHandoffMessageStartIndex,
4
5
  turnPartialHandoff,
@@ -15,6 +16,7 @@ import {
15
16
  terminalResultHook,
16
17
  } from './job-task-reconcile.mjs';
17
18
  import { createTurnReviewCollector } from './turn-review.mjs';
19
+ import { settledWorkerStatus } from './worker-index/row-liveness.mjs';
18
20
 
19
21
  // `send` to an existing worker: resolve the live session, run one follow-up
20
22
  // turn under the progress watchdog, and reconcile the job/worker row. Busy
@@ -28,7 +30,6 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
28
30
  if (!sessionId) throw new Error(`agent send: target "${target}" not found`);
29
31
  const session = mgr.getSession(sessionId);
30
32
  if (!session || session.closed) throw new Error(`agent send: session "${sessionId}" is closed`);
31
- registry.cancelReap(sessionId);
32
33
  const prompt = await resolvePrompt(args, session.cwd || defaultCwd);
33
34
  return { args, session, sessionId, prompt };
34
35
  }
@@ -82,13 +83,18 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
82
83
  const tag = registry.tagForSession(sessionId);
83
84
  // Queued sends run as follow-up turns inside this same askSession call;
84
85
  // the watchdog sweep re-stamps turnStartedAt at each turn boundary.
85
- const watchdog = spawnFlow.startProgressIdleWatchdog(sessionId, resolveAgentWatchdogPolicy(sendAgent), sendAgent, {
86
+ const watchdogPolicy = resolveAgentWatchdogPolicy(
87
+ sendAgent,
88
+ spawnFlow.agentWatchdogOverrides({ agent: sendAgent, provider: session.provider, model: session.model })
89
+ );
90
+ const watchdog = spawnFlow.startProgressIdleWatchdog(sessionId, watchdogPolicy, sendAgent, {
86
91
  onTurnStart: spawnFlow.turnStartStamper(session, tag),
87
92
  onProgress: spawnFlow.progressStamper(session, tag),
88
93
  });
89
94
  const turnReview = createTurnReviewCollector(session, tag, sendAgent, notifyContext || {});
90
95
  const completionValue = completionValueFor({ session, tag, sessionId, sendAgent });
91
96
  let finalStatus = 'idle';
97
+ let stalled = false;
92
98
  registry.upsertWorkerSessionDeferred(session, tag, {
93
99
  status: 'running',
94
100
  stage: 'running',
@@ -129,6 +135,7 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
129
135
  settleCancelled(error);
130
136
  throw error;
131
137
  }
138
+ stalled = isAgentProgressWatchdogAbortError(error);
132
139
  const partial = watchdogPartialHandoffFromError(error, mgr.getSession(sessionId), handoffMsgStart);
133
140
  if (partial) {
134
141
  finalStatus = 'idle';
@@ -152,12 +159,10 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
152
159
  turnReview.complete();
153
160
  watchdog?.stop?.();
154
161
  registry.upsertWorkerSessionDeferred(session, tag, {
155
- status: finalStatus,
156
- stage: finalStatus,
162
+ ...settledWorkerStatus(finalStatus, stalled),
157
163
  finishedAt: new Date().toISOString(),
158
164
  });
159
165
  reconcileJobFinally(job, finalStatus);
160
- registry.scheduleReap(sessionId);
161
166
  // Same lifecycle as a fresh spawn: the transcript/tag stays resumable,
162
167
  // while heavy process-local runtime state is reclaimed immediately.
163
168
  try {
@@ -166,11 +171,24 @@ export function createSendFlow({ mgr, defaultCwd, sessionSurface, canUseSessionS
166
171
  }
167
172
  }
168
173
 
174
+ // An explicit model on a reused tag goes through the session's own route
175
+ // API, which saves it and applies it from the session's next turn: the
176
+ // dispatched prompt when the session is idle, a later one while it is busy.
177
+ async function applyRequestedRoute(prepared) {
178
+ const { session, sessionId, route } = prepared;
179
+ if (!canUseSessionSurface(session) || typeof sessionSurface.setRoute !== 'function') {
180
+ throw new Error(`agent spawn: this surface cannot change the model of reused session "${sessionId}"`);
181
+ }
182
+ await sessionSurface.setRoute({ session, route });
183
+ return { ...prepared, session: mgr.getSession(sessionId) || session };
184
+ }
185
+
169
186
  // Shared send dispatch for an already-resolved live session. Used by the
170
187
  // `send` branch AND by the `spawn` branch when an explicit tag maps to a
171
188
  // live session (reuse path). Busy sessions queue the prompt; idle ones run a
172
189
  // background send job that continues the existing session (context kept).
173
- async function dispatchToExistingSession(prepared, notifyContext, extras = {}) {
190
+ async function dispatchToExistingSession(requested, notifyContext, extras = {}) {
191
+ const prepared = requested.route ? await applyRequestedRoute(requested) : requested;
174
192
  const { session, sessionId, prompt } = prepared;
175
193
  const tag = registry.tagForSession(sessionId);
176
194
  if (canUseSessionSurface(session) && views.isSessionBusy(sessionId)) {
@@ -53,7 +53,9 @@ export function createJobStarter({ mgr, notifyStatusChange, notifySessionComplet
53
53
  const currentMeta = task?.meta || jobMeta;
54
54
  if (currentMeta?.sessionId) {
55
55
  try {
56
- Promise.resolve(mgr.closeSession(currentMeta.sessionId, 'agent-task-cancel')).catch(() => {});
56
+ Promise.resolve(mgr.closeSession(currentMeta.sessionId, 'agent-task-cancel', { tombstone: false })).catch(
57
+ () => {}
58
+ );
57
59
  } catch {}
58
60
  }
59
61
  setImmediate(notifyStatusChange);
@@ -4,7 +4,7 @@
4
4
  * route, and the transport prewarm. The validated spawn plan and the session
5
5
  * spec it produces live in ./spawn-plan.mjs.
6
6
  */
7
- import { presetKey, writeAgentStatuslineRoute } from '../helpers.mjs';
7
+ import { presetKey, readAgentFirstResponseTimeoutMs, writeAgentStatuslineRoute } from '../helpers.mjs';
8
8
  import { resolveAgentWatchdogPolicy } from '../../../../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
9
9
  import { prepareAgentSession } from '../../../../runtime/agent/orchestrator/agent-runtime/session-builder.mjs';
10
10
  import { getProvider } from '../../../../runtime/agent/orchestrator/providers/registry.mjs';
@@ -55,7 +55,6 @@ export function createSpawnPreparer({
55
55
  resolveTag,
56
56
  wantsSessionScan,
57
57
  bindTag,
58
- cancelReap,
59
58
  ensureProvider,
60
59
  sessionSurface = null,
61
60
  refreshTagsFromSessions,
@@ -71,6 +70,19 @@ export function createSpawnPreparer({
71
70
  wantsSessionScan,
72
71
  });
73
72
 
73
+ /** Watchdog inputs for one agent run: the agent definition's own
74
+ * first-response deadline and the provider's first-response budget for the
75
+ * routed model (headers, and first output), handed to
76
+ * resolveAgentWatchdogPolicy. A provider on the shared window names none. */
77
+ function agentWatchdogOverrides({ agent, provider, model }) {
78
+ const budget = getProvider(provider)?.firstResponseBudget?.(model);
79
+ return {
80
+ firstResponseTimeoutMs: readAgentFirstResponseTimeoutMs(agent, dataDir, STANDALONE_SOURCE_ROOT) ?? undefined,
81
+ providerFirstByteTimeoutMs: budget?.transportMs,
82
+ providerFirstOutputTimeoutMs: budget?.semanticMs,
83
+ };
84
+ }
85
+
74
86
  /** Shared post-create wiring. Lead sessions write a gateway-session route on
75
87
  * create; agent sessions are built through prepareAgentSession()/the remote
76
88
  * runtime, so mirror that registration here or the vendored L1/L2
@@ -79,7 +91,6 @@ export function createSpawnPreparer({
79
91
  const { agent, presetName, preset, tag } = plan;
80
92
  writeAgentStatuslineRoute(session.id, preset);
81
93
  bindTag(tag, session, { ...presetDescriptor(agent, preset, presetName), status: 'idle', stage: 'idle' });
82
- cancelReap(session.id);
83
94
  }
84
95
 
85
96
  async function createSpawnedSession(plan, spec, prepState) {
@@ -127,7 +138,10 @@ export function createSpawnPreparer({
127
138
  presetName: plan.presetName,
128
139
  workerCwd: effectiveCwd || plan.workerCwd,
129
140
  prompt: plan.prompt,
130
- watchdogPolicy: resolveAgentWatchdogPolicy(plan.agent),
141
+ watchdogPolicy: resolveAgentWatchdogPolicy(
142
+ plan.agent,
143
+ agentWatchdogOverrides({ agent: plan.agent, provider: plan.preset.provider, model: plan.preset.model })
144
+ ),
131
145
  };
132
146
  }
133
147
 
@@ -136,5 +150,5 @@ export function createSpawnPreparer({
136
150
  return prepareSpawnInProcess(args, callerCwd, context, prepState);
137
151
  }
138
152
 
139
- return { prepareSpawn, prepareSpawnInProcess, createSpawnedSession };
153
+ return { prepareSpawn, prepareSpawnInProcess, createSpawnedSession, agentWatchdogOverrides };
140
154
  }
@@ -15,11 +15,13 @@ import {
15
15
  import { abnormalEmptyFinishError } from '../render.mjs';
16
16
  import { createProgressWatchdogRegistry } from '../../agent-watchdog-registry.mjs';
17
17
  import {
18
+ isAgentProgressWatchdogAbortError,
18
19
  resolveHandoffMessageStartIndex,
19
20
  turnPartialHandoff,
20
21
  watchdogPartialHandoffFromError,
21
22
  watchdogStoppedHandoff,
22
23
  } from '../../../../runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs';
24
+ import { settledWorkerStatus } from '../worker-index/row-liveness.mjs';
23
25
  import { saveSessionAsync } from '../../../../runtime/agent/orchestrator/session/store.mjs';
24
26
  import { presetDescriptor } from './spawn-prep.mjs';
25
27
  import { workerStageForStatus } from '../worker-rows.mjs';
@@ -35,7 +37,6 @@ export function createSpawnRunner({
35
37
  notifyStatusChange = () => {},
36
38
  notifySessionCompletion,
37
39
  emitSubagentEvent,
38
- scheduleReap,
39
40
  sessionSurface = null,
40
41
  createTurnReviewCollector,
41
42
  }) {
@@ -128,6 +129,7 @@ export function createSpawnRunner({
128
129
  const turnReview = createTurnReviewCollector(session, tag, agent, notifyContext || {});
129
130
  const completionValue = completionValueFor(prepared);
130
131
  let finalStatus = 'idle';
132
+ let stalled = false;
131
133
  // SubagentStart: a worker session is about to run its first turn.
132
134
  emitSubagentEvent('start', agent, { session_id: session.id, tag });
133
135
  upsertWorkerSessionDeferred(session, tag, {
@@ -189,6 +191,7 @@ export function createSpawnRunner({
189
191
  settleCancelled(error);
190
192
  throw error;
191
193
  }
194
+ stalled = isAgentProgressWatchdogAbortError(error);
192
195
  const partial = watchdogPartialHandoffFromError(error, mgr.getSession(session.id), handoffMsgStart);
193
196
  if (partial) {
194
197
  finalStatus = 'idle';
@@ -211,13 +214,11 @@ export function createSpawnRunner({
211
214
  watchdog?.stop?.();
212
215
  upsertWorkerSessionDeferred(session, tag, {
213
216
  ...presetDescriptor(agent, preset, presetName),
214
- status: finalStatus,
215
- stage: finalStatus,
217
+ ...settledWorkerStatus(finalStatus, stalled),
216
218
  finishedAt: new Date().toISOString(),
217
219
  });
218
220
  setImmediate(notifyStatusChange);
219
221
  reconcileJobFinally(job, finalStatus);
220
- scheduleReap(session.id);
221
222
  // SubagentStop: worker finished (terminal), regardless of outcome.
222
223
  emitSubagentEvent('stop', agent, { session_id: session.id, tag, status: finalStatus });
223
224
  // Keep the durable session/tag for same-tag follow-ups, but release every
@@ -14,7 +14,7 @@ export function createSpawnFlow(deps) {
14
14
  notifyStatusChange: deps.notifyStatusChange ?? (() => {}),
15
15
  notifySessionCompletion: deps.notifySessionCompletion,
16
16
  });
17
- const { prepareSpawn, prepareSpawnInProcess } = createSpawnPreparer(deps);
17
+ const { prepareSpawn, prepareSpawnInProcess, agentWatchdogOverrides } = createSpawnPreparer(deps);
18
18
  const runner = createSpawnRunner(deps);
19
19
  const { closePreparedSpawn, startDeferredSpawnJob } = createDeferredSpawn({
20
20
  ...deps,
@@ -35,6 +35,7 @@ export function createSpawnFlow(deps) {
35
35
  progressStamper: runner.progressStamper,
36
36
  prepareSpawn,
37
37
  prepareSpawnInProcess,
38
+ agentWatchdogOverrides,
38
39
  runSpawn: runner.runSpawn,
39
40
  };
40
41
  }