mixdog 1.3.5 → 1.4.1

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 (515) hide show
  1. package/NOTICE.md +2 -2
  2. package/README.md +2 -2
  3. package/package.json +2 -11
  4. package/scripts/check-voice-runtime-inputs.mjs +25 -0
  5. package/scripts/daemon-stub.mjs +1 -1
  6. package/scripts/gate-local.mjs +16 -5
  7. package/scripts/latest-guard.mjs +21 -9
  8. package/scripts/lib/isolated-test-env.mjs +10 -5
  9. package/scripts/lib/run-node-tests.mjs +37 -37
  10. package/scripts/lib/scoped-temp.d.mts +16 -0
  11. package/scripts/lib/scoped-temp.mjs +123 -0
  12. package/scripts/lib/stale-temp.mjs +44 -0
  13. package/scripts/native-release-status.mjs +2 -2
  14. package/scripts/post-release-check.mjs +1 -6
  15. package/scripts/pptx-kit-index.mjs +17 -4
  16. package/scripts/recall-history-cases.json +1 -1
  17. package/scripts/suite-health.mjs +20 -2
  18. package/scripts/test-data-dir.mjs +10 -7
  19. package/scripts/test.mjs +7 -11
  20. package/src/agents/reviewer/AGENT.md +7 -11
  21. package/src/agents/reviewer/agent.json +1 -1
  22. package/src/app.mjs +4 -0
  23. package/src/defaults/shipped-history.json +22 -1
  24. package/src/defaults/skills/browser-use/SKILL.md +62 -12
  25. package/src/defaults/skills/code-tidy/SKILL.md +142 -270
  26. package/src/defaults/skills/code-tidy/references/agent-cleanup.md +4 -4
  27. package/src/defaults/skills/code-tidy/references/cleanup-report.md +31 -76
  28. package/src/defaults/skills/code-tidy/references/dead-code.md +6 -12
  29. package/src/defaults/skills/computer-use/SKILL.md +35 -9
  30. package/src/defaults/skills/setup/SKILL.md +22 -0
  31. package/src/defaults/skills/setup/references/actions.md +39 -5
  32. package/src/defaults/skills/setup/references/surfaces.md +11 -2
  33. package/src/defaults/skills/visualize/SKILL.md +2 -1
  34. package/src/lib/rules-builder.cjs +39 -20
  35. package/src/output-styles/common.md +2 -2
  36. package/src/output-styles/simple.md +1 -1
  37. package/src/rules/agent/AGENT.md +11 -10
  38. package/src/rules/lead/LEAD.md +23 -20
  39. package/src/rules/shared/05-tool-calls.md +5 -6
  40. package/src/rules/shared/10-tool-workflow.md +4 -0
  41. package/src/rules/shared/45-destructive.md +0 -2
  42. package/src/rules/shared/50-execution.md +2 -0
  43. package/src/rules/shared/60-verification.md +7 -5
  44. package/src/rules/shared/70-delivery.md +7 -9
  45. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +29 -6
  46. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -3
  47. package/src/runtime/agent/orchestrator/agent-runtime/title-completion.mjs +3 -1
  48. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +3 -1
  49. package/src/runtime/agent/orchestrator/cache-break-trace.mjs +2 -0
  50. package/src/runtime/agent/orchestrator/config.mjs +17 -1
  51. package/src/runtime/agent/orchestrator/context/collect.mjs +3 -0
  52. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +73 -68
  53. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +57 -2
  54. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -3
  55. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +176 -72
  56. package/src/runtime/agent/orchestrator/mcp/client.mjs +19 -0
  57. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +5 -2
  58. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +192 -133
  59. package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +42 -6
  60. package/src/runtime/agent/orchestrator/providers/anthropic-api-recovery.mjs +1 -1
  61. package/src/runtime/agent/orchestrator/providers/anthropic-api-request.mjs +7 -3
  62. package/src/runtime/agent/orchestrator/providers/anthropic-api-transport.mjs +8 -2
  63. package/src/runtime/agent/orchestrator/providers/anthropic-cache-warm.mjs +165 -0
  64. package/src/runtime/agent/orchestrator/providers/anthropic-max-tokens-recovery.mjs +6 -2
  65. package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
  66. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  67. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +4 -1
  68. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/gzip-post.mjs +14 -14
  69. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-request/initial-status.mjs +3 -1
  70. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +159 -69
  71. package/src/runtime/agent/orchestrator/providers/anthropic-overload-fallback.mjs +25 -3
  72. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +70 -33
  73. package/src/runtime/agent/orchestrator/providers/anthropic-turn-result.mjs +2 -2
  74. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +135 -87
  75. package/src/runtime/agent/orchestrator/providers/antigravity-transport.mjs +123 -102
  76. package/src/runtime/agent/orchestrator/providers/cache-warm-admission.mjs +90 -0
  77. package/src/runtime/agent/orchestrator/providers/command-code-oauth-login.mjs +1 -10
  78. package/src/runtime/agent/orchestrator/providers/cursor-wire-transport.mjs +25 -9
  79. package/src/runtime/agent/orchestrator/providers/cursor.mjs +25 -13
  80. package/src/runtime/agent/orchestrator/providers/devin-wire-transport.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/providers/devin.mjs +3 -0
  82. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +210 -20
  83. package/src/runtime/agent/orchestrator/providers/lib/anthropic-prefix-trace.mjs +76 -11
  84. package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +45 -6
  85. package/src/runtime/agent/orchestrator/providers/lib/first-output-window.mjs +58 -0
  86. package/src/runtime/agent/orchestrator/providers/lib/listen-on.mjs +14 -0
  87. package/src/runtime/agent/orchestrator/providers/lib/oauth-token-utils.mjs +17 -3
  88. package/src/runtime/agent/orchestrator/providers/media-normalization.mjs +52 -10
  89. package/src/runtime/agent/orchestrator/providers/mimo-oauth-login.mjs +1 -10
  90. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.d.mts +8 -1
  91. package/src/runtime/agent/orchestrator/providers/model-proxy-channel.mjs +1 -0
  92. package/src/runtime/agent/orchestrator/providers/npm-cli-version.mjs +11 -3
  93. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +2 -1
  94. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  95. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +9 -2
  96. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +7 -0
  97. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +14 -3
  98. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +4 -2
  99. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  100. package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +5 -3
  101. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +16 -3
  102. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  103. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events/output-items.mjs +5 -2
  104. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +13 -3
  105. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +13 -2
  106. package/src/runtime/agent/orchestrator/providers/openai-http-sse-watchdogs.mjs +81 -42
  107. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +8 -0
  108. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +136 -5
  109. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -9
  110. package/src/runtime/agent/orchestrator/providers/openai-ws-send/send-context.mjs +22 -3
  111. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/attempt-context.mjs +43 -5
  112. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/handshake-failure.mjs +3 -1
  113. package/src/runtime/agent/orchestrator/providers/openai-ws-send-attempts/stream-failure.mjs +6 -3
  114. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +20 -12
  115. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +7 -1
  116. package/src/runtime/agent/orchestrator/providers/openai-ws-watchdogs.mjs +48 -22
  117. package/src/runtime/agent/orchestrator/providers/provider-error-kind.mjs +65 -1
  118. package/src/runtime/agent/orchestrator/providers/quota-reading.mjs +38 -12
  119. package/src/runtime/agent/orchestrator/providers/registry.mjs +38 -0
  120. package/src/runtime/agent/orchestrator/providers/retry-executor.mjs +39 -7
  121. package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +6 -0
  122. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +49 -16
  123. package/src/runtime/agent/orchestrator/runtime-core/compaction-config-keys.mjs +62 -0
  124. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +37 -16
  125. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +100 -23
  126. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +7 -6
  127. package/src/runtime/agent/orchestrator/runtime-core/session-text.mjs +9 -3
  128. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +19 -4
  129. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +125 -30
  130. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +3 -1
  131. package/src/runtime/agent/orchestrator/runtime-core/workflow.mjs +5 -2
  132. package/src/runtime/agent/orchestrator/session/cache/scoped-cache.mjs +1 -3
  133. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +798 -0
  134. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +4 -1
  135. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -8
  136. package/src/runtime/agent/orchestrator/session/compact/messages.mjs +23 -0
  137. package/src/runtime/agent/orchestrator/session/compact/observation-supersession.mjs +103 -23
  138. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +280 -45
  139. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +475 -58
  140. package/src/runtime/agent/orchestrator/session/compaction-archive.mjs +42 -17
  141. package/src/runtime/agent/orchestrator/session/context-utils.mjs +13 -1
  142. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +163 -22
  143. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +132 -17
  144. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +38 -7
  145. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +3 -0
  146. package/src/runtime/agent/orchestrator/session/conversation-import/turns.mjs +90 -22
  147. package/src/runtime/agent/orchestrator/session/eager-dispatch.mjs +4 -1
  148. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +11 -5
  149. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +0 -2
  150. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +36 -4
  151. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +1 -1
  152. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +51 -25
  153. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +37 -3
  154. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +3 -1
  155. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +1 -1
  156. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +1 -1
  157. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +13 -5
  158. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +31 -3
  159. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +20 -3
  160. package/src/runtime/agent/orchestrator/session/loop/tool-exec/after-hook.mjs +2 -1
  161. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +5 -4
  162. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  163. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +16 -1
  164. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +49 -21
  165. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +3 -10
  166. package/src/runtime/agent/orchestrator/session/loop/tool-phase.mjs +12 -3
  167. package/src/runtime/agent/orchestrator/session/manager/ask-queue.mjs +12 -2
  168. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +19 -1
  169. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +133 -90
  170. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +3 -3
  171. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +3 -1
  172. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +31 -4
  173. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +30 -18
  174. package/src/runtime/agent/orchestrator/session/manager/owner-route-fallbacks.mjs +24 -14
  175. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +11 -2
  176. package/src/runtime/agent/orchestrator/session/manager/provider-replay.mjs +13 -0
  177. package/src/runtime/agent/orchestrator/session/manager/route-fallback-chain.mjs +130 -0
  178. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +27 -9
  179. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +81 -8
  180. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +2 -0
  181. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -8
  182. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +12 -5
  183. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +9 -4
  185. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +4 -0
  186. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +102 -15
  187. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +21 -5
  188. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +9 -0
  189. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +24 -9
  190. package/src/runtime/agent/orchestrator/session/prompt-cache-idle-gaps.mjs +113 -0
  191. package/src/runtime/agent/orchestrator/session/prompt-cache-touch-store.mjs +150 -0
  192. package/src/runtime/agent/orchestrator/session/restart-prefix-parity.fixture.mjs +154 -0
  193. package/src/runtime/agent/orchestrator/session/routine-result.mjs +36 -9
  194. package/src/runtime/agent/orchestrator/session/sensitive-projection.mjs +100 -0
  195. package/src/runtime/agent/orchestrator/session/sensitive-values.mjs +257 -0
  196. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +20 -7
  197. package/src/runtime/agent/orchestrator/session/side-question.mjs +18 -0
  198. package/src/runtime/agent/orchestrator/session/skill-update-notice.mjs +60 -0
  199. package/src/runtime/agent/orchestrator/session/store/live-state.mjs +6 -3
  200. package/src/runtime/agent/orchestrator/session/store/save-delta.mjs +9 -5
  201. package/src/runtime/agent/orchestrator/session/store/serialize.mjs +9 -8
  202. package/src/runtime/agent/orchestrator/session/store/session-delete.mjs +7 -0
  203. package/src/runtime/agent/orchestrator/session/store/session-persistence-events.mjs +29 -0
  204. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +8 -6
  205. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +16 -2
  206. package/src/runtime/agent/orchestrator/session/store-summary-fields.mjs +15 -0
  207. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +81 -22
  208. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +92 -3
  209. package/src/runtime/agent/orchestrator/session/stored-attachment-history.mjs +32 -0
  210. package/src/runtime/agent/orchestrator/session/thinking-binding.mjs +108 -0
  211. package/src/runtime/agent/orchestrator/session/tool-batch/pre-dispatch.mjs +2 -2
  212. package/src/runtime/agent/orchestrator/session/tool-batch/record-outcome.mjs +2 -1
  213. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +79 -3
  214. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-status.mjs +9 -7
  215. package/src/runtime/agent/orchestrator/session/transcript-restore/tool-result-text.mjs +2 -2
  216. package/src/runtime/agent/orchestrator/session/turn-failure.mjs +5 -1
  217. package/src/runtime/agent/orchestrator/stall-policy.mjs +23 -0
  218. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +5 -2
  219. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +12 -1
  220. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/invocation-plan.mjs +2 -2
  221. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +240 -0
  222. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/shell-cwd.mjs +38 -0
  224. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +4 -2
  225. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +31 -6
  226. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +110 -26
  227. package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +7 -1
  228. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +31 -5
  229. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +22 -8
  230. package/src/runtime/agent/orchestrator/tools/builtin/lib/scoped-search-flight.mjs +9 -5
  231. package/src/runtime/agent/orchestrator/tools/builtin/read-single-stream.mjs +7 -1
  232. package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +6 -3
  233. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-dispatch.mjs +1 -1
  234. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-publication.mjs +23 -12
  235. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-snapshot.mjs +6 -1
  236. package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +1 -8
  237. package/src/runtime/agent/orchestrator/tools/builtin/shell-runtime.mjs +7 -1
  238. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch/federation.mjs +3 -1
  239. package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +1 -1
  240. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  241. package/src/runtime/agent/orchestrator/tools/lib/isolated-test-env.mjs +4 -3
  242. package/src/runtime/agent/orchestrator/tools/lib/shell-warm-standby.mjs +26 -4
  243. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +36 -13
  244. package/src/runtime/browser-bridge/action-schema.mjs +13 -2
  245. package/src/runtime/browser-bridge/client.mjs +73 -8
  246. package/src/runtime/browser-bridge/input-fields.mjs +8 -9
  247. package/src/runtime/browser-bridge/mail-links.mjs +107 -0
  248. package/src/runtime/browser-bridge/tool-defs.mjs +1 -1
  249. package/src/runtime/channels/lib/config.mjs +6 -61
  250. package/src/runtime/channels/lib/crash-log.mjs +1 -1
  251. package/src/runtime/channels/lib/event-pipeline.mjs +4 -19
  252. package/src/runtime/channels/lib/event-queue.mjs +3 -18
  253. package/src/runtime/channels/lib/owned-runtime/automation-services.mjs +2 -2
  254. package/src/runtime/channels/lib/owned-runtime/automation-start.mjs +3 -4
  255. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +4 -59
  256. package/src/runtime/channels/lib/owned-runtime/owned-start.mjs +33 -115
  257. package/src/runtime/channels/lib/owned-runtime/owned-stop.mjs +5 -22
  258. package/src/runtime/channels/lib/owned-runtime/state.mjs +2 -8
  259. package/src/runtime/channels/lib/owned-runtime.mjs +6 -23
  260. package/src/runtime/channels/lib/parent-bridge.mjs +2 -2
  261. package/src/runtime/channels/lib/runtime-paths.mjs +4 -43
  262. package/src/runtime/channels/lib/scheduler.mjs +33 -147
  263. package/src/runtime/channels/lib/schedules-db.mjs +34 -11
  264. package/src/runtime/channels/lib/status-snapshot.mjs +1 -1
  265. package/src/runtime/channels/lib/tool-dispatch.mjs +1 -2
  266. package/src/runtime/channels/lib/webhook/signature.mjs +4 -1
  267. package/src/runtime/channels/lib/webhooks-db.mjs +5 -9
  268. package/src/runtime/channels/lib/whisper-port.mjs +10 -1
  269. package/src/runtime/channels/lib/worker-bootstrap.mjs +0 -1
  270. package/src/runtime/channels/lib/worker-ipc.mjs +0 -1
  271. package/src/runtime/channels/lib/worker-main.mjs +10 -34
  272. package/src/runtime/computer-bridge/action-schema.mjs +42 -1
  273. package/src/runtime/computer-bridge/actions.mjs +9 -1
  274. package/src/runtime/computer-bridge/client.mjs +180 -8
  275. package/src/runtime/computer-bridge/core-actions.mjs +2 -2
  276. package/src/runtime/computer-bridge/error-recovery.mjs +28 -2
  277. package/src/runtime/computer-bridge/linux-action-capabilities.mjs +24 -5
  278. package/src/runtime/computer-bridge/linux-readiness.d.mts +7 -2
  279. package/src/runtime/computer-bridge/linux-readiness.mjs +65 -23
  280. package/src/runtime/computer-bridge/pending-continuation.mjs +1 -1
  281. package/src/runtime/connectors/connector-accounts-mcp.mjs +230 -0
  282. package/src/runtime/connectors/gmail-app-password.mjs +172 -39
  283. package/src/runtime/connectors/google-mcp.mjs +10 -12
  284. package/src/runtime/local-provider/server-process.mjs +2 -1
  285. package/src/runtime/media/adapters/gemini-video.mjs +4 -1
  286. package/src/runtime/media/adapters/xai-media.mjs +2 -1
  287. package/src/runtime/memory/lib/embedding-worker.mjs +6 -3
  288. package/src/runtime/memory/lib/memory-action-handlers/cycle-backfill-action.mjs +1 -7
  289. package/src/runtime/memory/lib/memory-chunk-quality.mjs +1 -6
  290. package/src/runtime/memory/lib/pg/adapter.mjs +77 -70
  291. package/src/runtime/memory/lib/pg/supervisor.mjs +24 -12
  292. package/src/runtime/memory/lib/runtime-fetcher.mjs +23 -5
  293. package/src/runtime/office/authoring/pdf-author-action.mjs +1 -6
  294. package/src/runtime/office/design/design-art-direction.mjs +29 -27
  295. package/src/runtime/office/pdf/pdf-writer.mjs +6 -1
  296. package/src/runtime/office/portable/portable-snapshot-docx.mjs +7 -1
  297. package/src/runtime/office/portable/portable-snapshot-shared.mjs +6 -1
  298. package/src/runtime/office/quality/assurance-rendered.mjs +9 -2
  299. package/src/runtime/office/quality/assurance-structure-docx.mjs +3 -1
  300. package/src/runtime/office/quality/design-review.mjs +1 -5
  301. package/src/runtime/office/quality/document-brief.mjs +1 -5
  302. package/src/runtime/office/quality/forced-break-pages.mjs +9 -6
  303. package/src/runtime/office/quality/quality-pipeline.mjs +1 -1
  304. package/src/runtime/session-search/session-search-db.mjs +73 -9
  305. package/src/runtime/session-search/session-search-ingest.mjs +110 -20
  306. package/src/runtime/shared/abort-race.mjs +89 -0
  307. package/src/runtime/shared/agent-avatar.d.mts +0 -1
  308. package/src/runtime/shared/agent-kind.mjs +4 -3
  309. package/src/runtime/shared/bot-owned-work.mjs +56 -0
  310. package/src/runtime/shared/bounded-download.mjs +8 -3
  311. package/src/runtime/shared/config.mjs +2 -13
  312. package/src/runtime/shared/connector-accounts.mjs +20 -10
  313. package/src/runtime/shared/connector-catalog.d.mts +29 -0
  314. package/src/runtime/shared/connector-catalog.mjs +150 -0
  315. package/src/runtime/shared/connector-triggers.mjs +22 -19
  316. package/src/runtime/shared/defaults-separation.mjs +2 -17
  317. package/src/runtime/shared/delegated-approval-host.mjs +103 -11
  318. package/src/runtime/shared/err-text.mjs +24 -15
  319. package/src/runtime/shared/fetch-safe-listen.d.mts +16 -0
  320. package/src/runtime/shared/fetch-safe-listen.mjs +50 -0
  321. package/src/runtime/shared/file-permissions.d.mts +0 -8
  322. package/src/runtime/shared/file-permissions.mjs +0 -11
  323. package/src/runtime/shared/git-direct.test-support.mjs +19 -0
  324. package/src/runtime/shared/handoff-artifacts.d.mts +2 -1
  325. package/src/runtime/shared/handoff-artifacts.mjs +18 -8
  326. package/src/runtime/shared/heartbeat-interval.d.mts +4 -0
  327. package/src/runtime/shared/heartbeat-interval.mjs +19 -0
  328. package/src/runtime/shared/idle-auto-clear-gate.mjs +126 -0
  329. package/src/runtime/shared/llm/cost.mjs +5 -1
  330. package/src/runtime/shared/llm/usage-rollup.mjs +1 -1
  331. package/src/runtime/shared/loopback-listener.mjs +25 -2
  332. package/src/runtime/shared/mixy-presence.mjs +22 -0
  333. package/src/runtime/shared/model-route-chain.d.mts +7 -2
  334. package/src/runtime/shared/model-route-chain.mjs +22 -4
  335. package/src/runtime/shared/open-url.mjs +10 -0
  336. package/src/runtime/shared/orchestration.d.mts +2 -0
  337. package/src/runtime/shared/pg-schema-db.mjs +27 -16
  338. package/src/runtime/shared/profile-config.cjs +1 -1
  339. package/src/runtime/shared/prompt-origin.d.mts +4 -0
  340. package/src/runtime/shared/prompt-origin.mjs +21 -0
  341. package/src/runtime/shared/provider-accounts.mjs +7 -1
  342. package/src/runtime/shared/provider-display-name.mjs +29 -0
  343. package/src/runtime/shared/provider-timeout-phase.mjs +2 -0
  344. package/src/runtime/shared/routine-trigger.d.mts +2 -0
  345. package/src/runtime/shared/routine-trigger.mjs +10 -0
  346. package/src/runtime/shared/sensitive-path-rules.d.mts +2 -10
  347. package/src/runtime/shared/sensitive-path-rules.mjs +19 -41
  348. package/src/runtime/shared/session-activity-signal.mjs +48 -1
  349. package/src/runtime/shared/session-scratch.mjs +37 -0
  350. package/src/runtime/shared/session-sidebar-host.d.mts +1 -1
  351. package/src/runtime/shared/session-sidebar-host.mjs +7 -3
  352. package/src/runtime/shared/session-turn-prelude.mjs +18 -0
  353. package/src/runtime/shared/tool-card-model/agent-surface.mjs +1 -3
  354. package/src/runtime/shared/tool-card-model/card-status.mjs +15 -2
  355. package/src/runtime/shared/tool-result-summary.mjs +2 -15
  356. package/src/runtime/shared/tool-surface.mjs +3 -0
  357. package/src/runtime/shared/tool-work-units.mjs +2 -0
  358. package/src/runtime/shared/turn-snapshot-store.mjs +13 -6
  359. package/src/runtime/shared/worktrees/contract.mjs +4 -1
  360. package/src/runtime/shared/worktrees/git.mjs +15 -0
  361. package/src/runtime/shared/worktrees/manager.mjs +87 -38
  362. package/src/runtime/shared/worktrees/test-support.mjs +14 -13
  363. package/src/runtime/terminal-bridge/client.mjs +10 -1
  364. package/src/runtime/visualize-bridge/client.mjs +29 -8
  365. package/src/runtime/visualize-bridge/tool-defs.mjs +22 -8
  366. package/src/runtime/visualize-bridge/tool-meta.d.mts +5 -0
  367. package/src/runtime/visualize-bridge/tool-meta.mjs +22 -0
  368. package/src/runtime/web-search/lib/fetch-pipeline.mjs +2 -2
  369. package/src/session-runtime/agent-home-delivery.mjs +489 -185
  370. package/src/session-runtime/agent-task-sessions.mjs +467 -229
  371. package/src/session-runtime/agent-tool-routing.mjs +8 -41
  372. package/src/session-runtime/boot/apis.mjs +9 -0
  373. package/src/session-runtime/boot/infrastructure.mjs +4 -0
  374. package/src/session-runtime/boot/providers.mjs +3 -0
  375. package/src/session-runtime/boot/tools.mjs +11 -2
  376. package/src/session-runtime/bot-status.mjs +12 -6
  377. package/src/session-runtime/bot-target.mjs +38 -0
  378. package/src/session-runtime/channel-config-api.mjs +6 -1
  379. package/src/session-runtime/config-lifecycle.mjs +6 -0
  380. package/src/session-runtime/connector-merge-migration.mjs +305 -0
  381. package/src/session-runtime/conversation-import/codex-titles.mjs +88 -0
  382. package/src/session-runtime/conversation-import/discovery.mjs +271 -25
  383. package/src/session-runtime/conversation-import/service.mjs +26 -5
  384. package/src/session-runtime/handoff-attachments.mjs +82 -31
  385. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +144 -41
  386. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -0
  387. package/src/session-runtime/internal-tool-executor/reaction-tool.mjs +4 -1
  388. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +8 -2
  389. package/src/session-runtime/lifecycle/cold-heir.mjs +11 -6
  390. package/src/session-runtime/lifecycle/inheritance.mjs +16 -3
  391. package/src/session-runtime/lifecycle/session-catalog.mjs +12 -2
  392. package/src/session-runtime/lifecycle/teardown/runtime-stops.mjs +50 -4
  393. package/src/session-runtime/lifecycle/teardown.mjs +23 -5
  394. package/src/session-runtime/mcp-glue/server-catalog.mjs +55 -3
  395. package/src/session-runtime/mcp-oauth-login.mjs +5 -3
  396. package/src/session-runtime/mixy-heartbeat.mjs +153 -33
  397. package/src/session-runtime/model-route/set-route.mjs +11 -2
  398. package/src/session-runtime/notification-bus.mjs +31 -117
  399. package/src/session-runtime/notification-completion-queue.mjs +54 -0
  400. package/src/session-runtime/notification-listeners.mjs +76 -0
  401. package/src/session-runtime/owned-automation-run.mjs +2 -1
  402. package/src/session-runtime/prewarm/channel-start.mjs +3 -7
  403. package/src/session-runtime/provider-catalog-cache.mjs +82 -36
  404. package/src/session-runtime/provider-models/catalog-load.mjs +23 -6
  405. package/src/session-runtime/provider-models.mjs +4 -2
  406. package/src/session-runtime/resource-mcp-api.mjs +143 -224
  407. package/src/session-runtime/resource-mcp-connector-accounts.mjs +163 -0
  408. package/src/session-runtime/resource-mcp-oauth-api.mjs +217 -0
  409. package/src/session-runtime/resource-mcp-secrets.mjs +82 -0
  410. package/src/session-runtime/resource-mcp-servers.mjs +78 -0
  411. package/src/session-runtime/resource-mcp-store.mjs +49 -0
  412. package/src/session-runtime/resource-recall-api.mjs +6 -1
  413. package/src/session-runtime/routine-owner.mjs +40 -9
  414. package/src/session-runtime/runtime-feature-gates.mjs +5 -1
  415. package/src/session-runtime/services/agent-tool/close-flow.mjs +3 -0
  416. package/src/session-runtime/services/agent-tool/execute.mjs +14 -9
  417. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +20 -4
  418. package/src/session-runtime/services/agent-tool/model-select.mjs +48 -7
  419. package/src/session-runtime/services/agent-tool/render.mjs +71 -17
  420. package/src/session-runtime/services/agent-tool/spawn-preset.mjs +13 -4
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +1 -0
  422. package/src/session-runtime/services/agent-tool/tool-def.mjs +9 -9
  423. package/src/session-runtime/services/agent-tool/worker-index/awaiting-user-sync.mjs +51 -0
  424. package/src/session-runtime/services/agent-tool/worker-index/disposal.mjs +55 -0
  425. package/src/session-runtime/services/agent-tool/worker-index/released-store.mjs +123 -0
  426. package/src/session-runtime/services/agent-tool/worker-index/restart-settlement.mjs +112 -0
  427. package/src/session-runtime/services/agent-tool/worker-index/row-reads.mjs +36 -0
  428. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +2 -0
  429. package/src/session-runtime/services/agent-tool/worker-index/row-store.mjs +17 -4
  430. package/src/session-runtime/services/agent-tool/worker-index/row-writes.mjs +58 -0
  431. package/src/session-runtime/services/agent-tool/worker-index.mjs +41 -241
  432. package/src/session-runtime/services/agent-tool/worker-rows.mjs +3 -0
  433. package/src/session-runtime/services/channel-admin.mjs +7 -13
  434. package/src/session-runtime/services/channel-session-router.mjs +4 -2
  435. package/src/session-runtime/services/provider-admin.mjs +2 -1
  436. package/src/session-runtime/services/seeds.mjs +0 -28
  437. package/src/session-runtime/session-compaction-sync.mjs +27 -0
  438. package/src/session-runtime/session-create/session-options.mjs +1 -0
  439. package/src/session-runtime/settings-builtin-tools-api.mjs +11 -0
  440. package/src/session-runtime/settings-compaction-api.mjs +2 -40
  441. package/src/session-runtime/settings-system-api.mjs +13 -1
  442. package/src/session-runtime/setup-tool/agent-settings-actions.mjs +65 -0
  443. package/src/session-runtime/setup-tool/executor.mjs +145 -22
  444. package/src/session-runtime/setup-tool/extended-actions.mjs +8 -3
  445. package/src/session-runtime/setup-tool/session-route-actions.mjs +152 -0
  446. package/src/session-runtime/setup-tool/settings-contract.mjs +4 -3
  447. package/src/session-runtime/setup-tool/tool-defs.mjs +64 -5
  448. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +17 -13
  449. package/src/session-runtime/tool-policy/policy-stage.mjs +17 -2
  450. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +1 -0
  451. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +5 -1
  452. package/src/session-runtime/workflow-agents-api/agent-route.mjs +15 -4
  453. package/src/session-runtime/workflow-agents-api/onboarding.mjs +3 -3
  454. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +4 -0
  455. package/src/session-runtime/workspace/transitions.mjs +25 -0
  456. package/src/session-runtime/workspace/worktree-tool.mjs +26 -9
  457. package/src/standalone/channel-transport/client-registry/client-records.mjs +65 -0
  458. package/src/standalone/channel-transport/client-registry/client-registration.mjs +118 -0
  459. package/src/standalone/channel-transport/client-registry.mjs +24 -163
  460. package/src/standalone/channel-transport.mjs +1 -1
  461. package/src/standalone/daemon-channels-loader.mjs +10 -8
  462. package/src/standalone/daemon-session-control/list-page.mjs +72 -0
  463. package/src/standalone/daemon-session-control/request.mjs +51 -0
  464. package/src/standalone/daemon-session-control/room-host.mjs +57 -0
  465. package/src/standalone/daemon-session-control/route-control.mjs +48 -0
  466. package/src/standalone/daemon-session-control/send-turn.mjs +60 -0
  467. package/src/standalone/daemon-session-control/worktree-control.mjs +40 -0
  468. package/src/standalone/daemon-session-control.mjs +142 -190
  469. package/src/standalone/daemon.mjs +26 -3
  470. package/src/standalone/session-client.mjs +7 -3
  471. package/src/standalone/session-protocol.mjs +5 -0
  472. package/src/standalone/session-service/agent-tree/agent-child.mjs +1 -0
  473. package/src/standalone/session-service/agent-tree/agent-turns.mjs +16 -12
  474. package/src/standalone/session-service/quota-resume.mjs +3 -0
  475. package/src/standalone/session-service/retention/busy.mjs +65 -1
  476. package/src/standalone/session-service/session-calls/session-action.mjs +30 -2
  477. package/src/standalone/session-service/session-calls/session-views.mjs +8 -0
  478. package/src/standalone/session-tool-output.mjs +15 -7
  479. package/src/standalone/session-transport/client-registry/client-grace.mjs +12 -9
  480. package/src/tui/app/app-view/floating-panel.jsx +7 -4
  481. package/src/tui/app/model-options.mjs +3 -28
  482. package/src/tui/app/model-picker/route-selection/fast-parameters.mjs +1 -3
  483. package/src/tui/app/model-picker/route-selection.mjs +5 -6
  484. package/src/tui/app/provider-setup/oauth-actions.mjs +38 -20
  485. package/src/tui/app/provider-setup/provider-items.mjs +8 -2
  486. package/src/tui/app/provider-setup-picker.mjs +9 -5
  487. package/src/tui/components/QuestionPanel.jsx +13 -4
  488. package/src/tui/dist/index.mjs +133 -92
  489. package/src/tui/session/agent-job-feed/model-notification.mjs +3 -0
  490. package/src/tui/session/initial-state.mjs +3 -0
  491. package/src/tui/session/notice-surface.mjs +7 -1
  492. package/src/tui/session/queue-helpers.mjs +3 -42
  493. package/src/tui/session/session-api/routes-agent-definitions.mjs +14 -2
  494. package/src/tui/session/session-api/routes-write.mjs +5 -1
  495. package/src/tui/session/session-api/routes.mjs +1 -0
  496. package/src/tui/session/session-api/settings.mjs +17 -1
  497. package/src/tui/session/session-flow/auto-clear.mjs +4 -33
  498. package/src/tui/session/session-flow/drain-closed-queue.mjs +38 -0
  499. package/src/tui/session/session-flow/drain-enqueue.mjs +86 -0
  500. package/src/tui/session/session-flow/drain-retry.mjs +35 -0
  501. package/src/tui/session/session-flow/drain.mjs +118 -149
  502. package/src/tui/session/session-flow/queue/entry-shape.mjs +5 -0
  503. package/src/tui/session/session-flow/queue/take-entries.mjs +16 -2
  504. package/src/tui/session/tool-approval-delegation.mjs +64 -0
  505. package/src/tui/session/tool-approval-request.mjs +39 -0
  506. package/src/tui/session/tool-approval-settle.mjs +62 -0
  507. package/src/tui/session/tool-approval.mjs +69 -115
  508. package/src/tui/session/tui-steering-persist.mjs +8 -1
  509. package/src/tui/session/turn-stream.mjs +19 -2
  510. package/src/tui/session/turn.mjs +1 -0
  511. package/src/ui/statusline-agents.mjs +1 -3
  512. package/src/vendor/statusline/bin/statusline-route.mjs +1 -219
  513. package/src/vendor/statusline/src/gateway/route-meta.mjs +35 -13
  514. package/scripts/code-graph-description-contract.mjs +0 -188
  515. package/src/runtime/channels/lib/owned-runtime/ownership-refresh.mjs +0 -36
@@ -1,9 +1,10 @@
1
1
  // One-off model selection for a spawn the user explicitly pinned to a model.
2
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.
3
+ // passed. The query is matched against the quick/cached provider catalog —
4
+ // literal id, then normalized id, then exact display name, then every query
5
+ // word — and a catalog miss retries once on a catalog refreshed for the
6
+ // requested provider (concurrent misses share that refresh). One match runs;
7
+ // several or none fail with candidates instead of substituting another model.
7
8
  import { clean } from './helpers.mjs';
8
9
 
9
10
  const CANDIDATE_LIMIT = 8;
@@ -20,7 +21,7 @@ const terms = (value) =>
20
21
  .filter(Boolean);
21
22
  const label = (row) => `${row.provider}/${row.id}`;
22
23
 
23
- function providerRows(rows, provider) {
24
+ export function providerRows(rows, provider) {
24
25
  if (!provider) return rows;
25
26
  const wanted = norm(provider);
26
27
  const exact = rows.filter((row) => norm(row.provider) === wanted);
@@ -33,6 +34,8 @@ function matchModels(rows, query) {
33
34
  // Nothing to match on ("???"): `[].every` would accept every row.
34
35
  if (!words.length) return [];
35
36
  const tiers = [
37
+ // A literal id wins over its normalized look-alikes (`foo-bar` vs `foo_bar`).
38
+ (row) => row.id === query,
36
39
  (row) => norm(row.id) === wanted,
37
40
  (row) => norm(row.display) === wanted,
38
41
  (row) => words.every((word) => `${norm(row.id)} ${norm(row.display)}`.includes(word)),
@@ -49,6 +52,34 @@ function candidates(rows) {
49
52
  return rows.length > CANDIDATE_LIMIT ? `${shown}, … (${rows.length} total)` : shown;
50
53
  }
51
54
 
55
+ // A quick read answered from the loaded catalog marks every row complete.
56
+ // Otherwise a hit stands only when it is a literal id of the exact provider the
57
+ // caller named, which the full catalog would pick the same way.
58
+ function quickAnswerHolds(rows, hits, model, provider) {
59
+ if (rows.length > 0 && rows.every((row) => row.catalogComplete === true)) return true;
60
+ const wanted = norm(provider);
61
+ return hits.length > 0 && wanted !== '' && hits.every((row) => row.id === model && norm(row.provider) === wanted);
62
+ }
63
+
64
+ // The forced refresh in flight per catalog reader and provider scope: concurrent
65
+ // misses share it instead of each refreshing.
66
+ const refreshing = new WeakMap();
67
+
68
+ // One forced refresh, scoped to the requested provider when one is given.
69
+ function refreshedRows(listProviderModels, provider) {
70
+ if (!refreshing.has(listProviderModels)) refreshing.set(listProviderModels, new Map());
71
+ const inflight = refreshing.get(listProviderModels);
72
+ const scope = norm(provider);
73
+ if (!inflight.has(scope)) {
74
+ const request = listProviderModels({ force: true, ...(provider ? { provider } : {}) });
75
+ inflight.set(
76
+ scope,
77
+ request.finally(() => inflight.delete(scope))
78
+ );
79
+ }
80
+ return inflight.get(scope);
81
+ }
82
+
52
83
  /** Resolve args.provider/args.model to one exact catalog route. Without a
53
84
  * catalog (headless/daemon surfaces) exact provider + model pass through. */
54
85
  export async function resolveRequestedModel(args, listProviderModels) {
@@ -64,10 +95,20 @@ export async function resolveRequestedModel(args, listProviderModels) {
64
95
  if (!terms(model).length) throw new Error(`agent spawn: model "${model}" for provider "${provider}" not found`);
65
96
  return args;
66
97
  }
67
- let pool = providerRows(await listProviderModels(), provider);
98
+ // The quick/cached catalog answers first. A loaded catalog is final, so its
99
+ // hit never waits on a provider; an incomplete one (cold or partial) may
100
+ // lack the model that wins, or one that makes the name ambiguous, so only a
101
+ // literal id inside an exact, explicitly named provider is taken from it.
102
+ // Anything else waits for the full catalog.
103
+ const quickRows = await listProviderModels({ quick: true });
104
+ let pool = providerRows(quickRows, provider);
68
105
  let hits = matchModels(pool, model);
106
+ if (!quickAnswerHolds(quickRows, hits, model, provider)) {
107
+ pool = providerRows(await listProviderModels(), provider);
108
+ hits = matchModels(pool, model);
109
+ }
69
110
  if (!hits.length) {
70
- pool = providerRows(await listProviderModels({ force: true }), provider);
111
+ pool = providerRows(await refreshedRows(listProviderModels, provider), provider);
71
112
  hits = matchModels(pool, model);
72
113
  }
73
114
  if (hits.length === 1) return { ...args, provider: hits[0].provider, model: hits[0].id };
@@ -1,5 +1,7 @@
1
1
  // Pure result-rendering and finish-classification helpers.
2
2
  import { oneLine } from '../../../runtime/shared/clean.mjs';
3
+ import { isActiveWorkerRow } from './job-views/agent-list.mjs';
4
+ import { isTerminalJobStatus } from './job-views/session-progress.mjs';
3
5
  import { compactIso, elapsedFromStamps, stripFinalAnswerWrapper } from './helpers.mjs';
4
6
 
5
7
  // A worker that gets truncated mid-synthesis or produces an empty terminal
@@ -32,25 +34,77 @@ export function abnormalEmptyFinishError(result, agent) {
32
34
  }
33
35
  }
34
36
 
35
- // The /agents roster: worker rows then task rows.
36
- function renderRoster(value) {
37
+ const ROSTER_TAIL = 5;
38
+ const ROSTER_MATCH_CAP = 25;
39
+
40
+ function workerLine(worker) {
41
+ const status = worker.status || 'idle';
42
+ const stage = worker.worker_stage || worker.stage;
43
+ const progress = oneLine(worker.last_progress);
44
+ const summary = progress.length > 60 ? `${progress.slice(0, 59)}…` : progress;
45
+ return `- ${worker.tag} ${status}${stage && stage !== status ? `/${stage}` : ''}${summary ? ` ${summary}` : ''}`;
46
+ }
47
+
48
+ function jobLine(job) {
49
+ const error = job.status === 'failed' ? oneLine(job.error) : '';
50
+ return `- ${job.task_id} ${job.type} ${job.status} ${job.tag || '-'}${error ? ` error=${error}` : ''}`;
51
+ }
52
+
53
+ const stamp = (row) => String(row.updatedAt || row.lastUsedAt || row.finishedAt || row.startedAt || '');
54
+
55
+ // Inactive rows: newest first (stable for rows without stamps).
56
+ const newestFirst = (rows) => rows.slice().sort((a, b) => stamp(b).localeCompare(stamp(a)));
57
+
58
+ // Inactive rows collapse to a count plus a short tail. A `tag` filter lists the
59
+ // matching inactive rows (bounded) so a specific one can still be found.
60
+ function inactiveSection(label, rows, render, tagOf, filter) {
61
+ if (rows.length === 0) return [];
62
+ const needle = filter.toLowerCase();
63
+ const matched = needle
64
+ ? rows.filter((row) =>
65
+ String(tagOf(row) ?? '')
66
+ .toLowerCase()
67
+ .includes(needle)
68
+ )
69
+ : null;
70
+ const shown = newestFirst(matched ?? rows).slice(0, matched ? ROSTER_MATCH_CAP : ROSTER_TAIL);
71
+ const shownFilter = filter.replace(/\s+/g, ' ');
72
+ const head = matched
73
+ ? `${label}: ${rows.length} (${matched.length} match tag "${shownFilter}", showing ${shown.length})`
74
+ : `${label}: ${rows.length} (latest ${shown.length}; pass tag=<text> to find one)`;
75
+ // Bulleted: a non-bullet line would make the list read as an authored response.
76
+ return [`- ${head}`, ...shown.map(render)];
77
+ }
78
+
79
+ // The /agents roster: active workers and running tasks in full; idle/closed
80
+ // workers and finished tasks as counts with a bounded tail.
81
+ function renderRoster(value, filter = '') {
37
82
  const workers = Array.isArray(value.workers) ? value.workers : [];
38
83
  const jobs = Array.isArray(value.jobs) ? value.jobs : [];
39
84
  const lines = [`agents: ${workers.length} · tasks: ${jobs.length}`];
85
+ const isActive = (worker) => Boolean(worker.awaitingUser) || isActiveWorkerRow(worker);
86
+ const isRunning = (job) => !isTerminalJobStatus(job.status);
40
87
  // Keep list bullets: agent cards distinguish these rows from authored responses.
41
- for (const worker of workers) {
42
- const status = worker.status || 'idle';
43
- const stage = worker.worker_stage || worker.stage;
44
- const progress = oneLine(worker.last_progress);
45
- const summary = progress.length > 60 ? `${progress.slice(0, 59)}…` : progress;
46
- lines.push(
47
- `- ${worker.tag} ${status}${stage && stage !== status ? `/${stage}` : ''}${summary ? ` ${summary}` : ''}`
48
- );
49
- }
50
- for (const job of jobs) {
51
- const error = job.status === 'failed' ? oneLine(job.error) : '';
52
- lines.push(`- ${job.task_id} ${job.type} ${job.status} ${job.tag || '-'}${error ? ` error=${error}` : ''}`);
53
- }
88
+ lines.push(...workers.filter(isActive).map(workerLine));
89
+ lines.push(...jobs.filter(isRunning).map(jobLine));
90
+ lines.push(
91
+ ...inactiveSection(
92
+ 'idle/closed agents',
93
+ workers.filter((w) => !isActive(w)),
94
+ workerLine,
95
+ (w) => w.tag,
96
+ filter
97
+ )
98
+ );
99
+ lines.push(
100
+ ...inactiveSection(
101
+ 'finished tasks',
102
+ jobs.filter((j) => !isRunning(j)),
103
+ jobLine,
104
+ (j) => j.tag || j.task_id,
105
+ filter
106
+ )
107
+ );
54
108
  if (workers.length === 0 && jobs.length === 0) lines.push('(no agents or tasks)');
55
109
  return lines.join('\n');
56
110
  }
@@ -151,11 +205,11 @@ function renderContent(value) {
151
205
  return `${header}\n${stripFinalAnswerWrapper(value.content)}`;
152
206
  }
153
207
 
154
- export function renderResult(value, { includeDiagnostics = false } = {}) {
208
+ export function renderResult(value, { includeDiagnostics = false, listFilter = '' } = {}) {
155
209
  if (value === undefined || value === null) return '';
156
210
  if (typeof value === 'string') return value;
157
211
  if (value && typeof value === 'object') {
158
- if (Array.isArray(value.workers) || Array.isArray(value.jobs)) return renderRoster(value);
212
+ if (Array.isArray(value.workers) || Array.isArray(value.jobs)) return renderRoster(value, listFilter);
159
213
  // close-flow results carry `closed` plus task_id: render the close outcome.
160
214
  // close-flow results carry `closed` plus task_id: render the close outcome.
161
215
  if (value.closed !== undefined) return renderClosed(value);
@@ -1,4 +1,5 @@
1
1
  import { configuredAgentRouteCandidates } from '../../../runtime/shared/agent-route-config.mjs';
2
+ import { executionFallbacks } from '../../../runtime/shared/model-route-chain.mjs';
2
3
  import {
3
4
  agentPresetName,
4
5
  clean,
@@ -29,8 +30,14 @@ export function resolveAgentSpawnPreset(config, args = {}) {
29
30
  const agentName = normalizeAgentName(args.agent);
30
31
  // The agent's stored route: config.agents, or a store bot's own folder.
31
32
  const candidates = !clean(args.preset) ? configuredAgentRouteCandidates(config, agentName) : [];
32
- const agentRoute = candidates.map((candidate) => normalizeAgentRoute(candidate)).find(Boolean) || null;
33
- const storedFallbacks = candidates.find((candidate) => normalizeAgentRoute(candidate))?.fallbacks;
33
+ const storedCandidate = candidates.find((candidate) => normalizeAgentRoute(candidate));
34
+ const agentRoute = storedCandidate ? normalizeAgentRoute(storedCandidate) : null;
35
+ const storedFallbacks = agentRoute
36
+ ? executionFallbacks({ ...agentRoute, fallbacks: storedCandidate.fallbacks })
37
+ : undefined;
38
+ // Where the spawned session reads its fallback chain at each hop
39
+ // (route-fallback-chain.mjs): this agent's route, else Main's.
40
+ const fallbackSource = { agent: agentName };
34
41
  if (agentRoute) {
35
42
  return {
36
43
  presetName: agentPresetName(agentName),
@@ -41,6 +48,7 @@ export function resolveAgentSpawnPreset(config, args = {}) {
41
48
  provider: agentRoute.provider,
42
49
  model: agentRoute.model,
43
50
  ...(storedFallbacks?.length ? { fallbacks: storedFallbacks.map(normalizeAgentRoute) } : {}),
51
+ fallbackSource,
44
52
  effort: agentRoute.effort,
45
53
  fast: agentRoute.fast === true,
46
54
  ...(agentRoute.autoEffort === false ? { autoEffort: false } : {}),
@@ -54,12 +62,13 @@ export function resolveAgentSpawnPreset(config, args = {}) {
54
62
 
55
63
  const mainPreset = !clean(args.preset) ? findPreset(config, config?.default) : null;
56
64
  if (normalizeAgentRoute(mainPreset)) {
57
- return { presetName: mainPreset.id || mainPreset.name, preset: mainPreset };
65
+ return { presetName: mainPreset.id || mainPreset.name, preset: { ...mainPreset, fallbackSource } };
58
66
  }
59
67
 
60
68
  const presetName = clean(args.preset);
61
69
  if (!presetName) throw new Error(`agent: agent "${agentName}" has no Main model assignment`);
62
- const preset = findPreset(config, presetName) || synthesizePreset(config, presetName);
70
+ const stored = findPreset(config, presetName);
71
+ const preset = stored ? { ...stored, fallbackSource: { preset: presetName } } : synthesizePreset(config, presetName);
63
72
  if (!preset) throw new Error(`agent: preset "${presetName}" not found`);
64
73
  if (!normalizeAgentRoute(preset)) throw new Error(`agent: preset "${presetName}" has no complete route`);
65
74
  return { presetName, preset };
@@ -121,6 +121,7 @@ export function createTagRegistry({ dataDir, cfgMod, mgr }) {
121
121
  updateLeadWorkerCwd,
122
122
  forgetLeadSession,
123
123
  removeWorkerRow,
124
+ markReleased: index.markReleased,
124
125
  refreshTagsFromIndex,
125
126
  tags,
126
127
  tagAgents: tagMaps.tagAgents,
@@ -28,7 +28,7 @@ export const AGENT_TOOL = {
28
28
  agentHidden: false,
29
29
  },
30
30
  description:
31
- 'Run scoped agent work as background tasks. spawn/send return task_id immediately. Reuse one tag for one scope for its whole lifetime; give distinct tags only to independent scopes. Never mint a new tag because a session expired or was lost — same-tag spawn respawns it with the full brief. Wait for completion notifications; use status/read only for manual recovery. Close finished agents yourself.',
31
+ 'Run scoped agent work as background tasks. spawn/send return task_id immediately. Reuse one tag for one scope for its whole lifetime; give distinct tags only to independent scopes. Never mint a new tag because a session expired or was lost — same-tag spawn respawns it with the full brief. Completion notifications arrive automatically; use status/read only for manual recovery. Close finished agents yourself.',
32
32
  inputSchema: {
33
33
  type: 'object',
34
34
  properties: {
@@ -36,21 +36,21 @@ export const AGENT_TOOL = {
36
36
  type: 'string',
37
37
  enum: ['spawn', 'send', 'list', 'close', 'cancel', 'status', 'read', 'cleanup'],
38
38
  description:
39
- 'Required action. New spawn requires agent and one of prompt/message/file; send requires tag and one of prompt/message/file; read/status/close/cancel require task_id or tag; list/cleanup require no target.',
39
+ 'Required action. New spawn requires agent and one of prompt/message/file; send requires tag and one of prompt/message/file; read/status/close/cancel require task_id or tag; list/cleanup require no target. list shows active agents and running tasks in full; idle/closed agents and finished tasks are counts plus a short latest tail (list with tag=<text> shows matching ones: case-insensitive substring, at most 25 per section).',
40
40
  },
41
- task_id: { type: 'string', description: 'Task ID returned by spawn/send; target for read/status/close/cancel.' },
41
+ task_id: { type: 'string', description: 'Task ID returned by spawn/send.' },
42
42
  agent: {
43
43
  type: 'string',
44
- description: 'Workflow agent id. Required for a new spawn; same-tag reuse can inherit it.',
44
+ description: 'Workflow agent id; same-tag reuse can inherit it.',
45
45
  },
46
46
  tag: {
47
47
  type: 'string',
48
48
  description:
49
- 'Stable scope handle. Reuse the same tag for the same scope; never derive a variant by appending a number or suffix. send requires it, while spawn can generate one when omitted.',
49
+ 'Stable scope handle; never derive a variant by appending a number or suffix. spawn generates one when omitted.',
50
50
  },
51
- prompt: { type: 'string', description: 'Spawn task brief; also accepted for send.' },
52
- message: { type: 'string', description: 'Send follow-up; also accepted for spawn.' },
53
- file: { type: 'string', description: 'Path to task text; alternative to prompt/message for spawn/send.' },
51
+ prompt: { type: 'string', description: 'Spawn task brief.' },
52
+ message: { type: 'string', description: 'Send follow-up.' },
53
+ file: { type: 'string', description: 'Path to task text; used instead of prompt/message.' },
54
54
  cwd: { type: 'string', description: 'Project directory for a new or respawned agent.' },
55
55
  context: { type: 'string', description: 'Extra context for the spawn/send turn.' },
56
56
  attachments: {
@@ -63,7 +63,7 @@ export const AGENT_TOOL = {
63
63
  model: {
64
64
  type: 'string',
65
65
  description:
66
- "Spawn only, and only when the user explicitly names a model: runs this one spawn on it instead of the agent's preset route, which stays the default (omit otherwise). Id, display name, or words of it; ambiguous or unknown names fail with candidates. The agent still supplies role and permissions.",
66
+ "Spawn only, and only when the user explicitly names a model: runs this one spawn on it instead of the agent's preset route. Id, display name, or words of it; ambiguous or unknown names fail with candidates. The agent still supplies role and permissions.",
67
67
  },
68
68
  provider: { type: 'string', description: 'Narrows model to one provider; requires model.' },
69
69
  effort: { type: 'string', description: 'Reasoning effort for an explicit model.' },
@@ -0,0 +1,51 @@
1
+ // worker-index/awaiting-user-sync.mjs
2
+ // A worker row's approval wait (`awaitingUser`), fenced to its session's turn
3
+ // and kept in step with the runtime this process runs for that session. Owns
4
+ // the sessions whose deferred rows still wait in the mutation batch.
5
+ import { clean } from '../helpers.mjs';
6
+ import { applyAwaitingUser, awaitingUserWouldChange } from './approval-wait.mjs';
7
+
8
+ export function createAwaitingUserSync(store, mgr) {
9
+ // Sessions whose deferred rows still wait in the batch; reconciled once the
10
+ // batch lands in the store.
11
+ const unreconciled = new Set();
12
+
13
+ /** Persist (or clear) a worker's approval wait as a field update fenced to
14
+ * the session and its turn (`turnAt`, ms): a row whose turn started later is
15
+ * left alone. Writes only when a row of that session exists and would
16
+ * change, so a Lead's own approvals cost nothing; a row that is not in the
17
+ * store yet takes the wait when it lands (reconcileAwaitingUser). */
18
+ function setAwaitingUser(sessionId, awaitingUser, { turnAt = 0 } = {}) {
19
+ const fence = {
20
+ sessionId: clean(sessionId),
21
+ awaitingUser: awaitingUser === 'approval' ? 'approval' : null,
22
+ turnAt: Number(turnAt) || 0,
23
+ };
24
+ if (!fence.sessionId || !awaitingUserWouldChange(store.readAll(), fence)) return false;
25
+ store.update((byKey) => applyAwaitingUser(byKey, fence));
26
+ return true;
27
+ }
28
+
29
+ /** The runtime of a session this process runs is the authority on its
30
+ * approval wait (the runtime-liveness counter behind `awaitingUser`): every
31
+ * row it creates or refreshes takes that state, so a missed or stale
32
+ * transition heals on the next write. No runtime here: not ours to judge. */
33
+ function reconcileAwaitingUser(sessionId) {
34
+ const runtime = mgr.getSessionRuntime?.(sessionId);
35
+ if (runtime) setAwaitingUser(sessionId, runtime.awaitingUser, { turnAt: runtime.askStartedAt });
36
+ }
37
+
38
+ /** A deferred row of `sessionId` is queued; reconciled when its batch lands. */
39
+ function deferReconcile(sessionId) {
40
+ unreconciled.add(sessionId);
41
+ }
42
+
43
+ /** The batch landed: reconcile every session whose deferred row it carried. */
44
+ function reconcileDeferred() {
45
+ const ids = [...unreconciled];
46
+ unreconciled.clear();
47
+ for (const id of ids) reconcileAwaitingUser(id);
48
+ }
49
+
50
+ return { setAwaitingUser, reconcileAwaitingUser, deferReconcile, reconcileDeferred };
51
+ }
@@ -0,0 +1,55 @@
1
+ // worker-index/disposal.mjs
2
+ // Disposing a worker index: queued rows persist first, then `teardown`
3
+ // releases its registrations; a failed drain leaves them for a retry. Owns
4
+ // the disposed / in-flight dispose state.
5
+ export function createIndexDisposal({ batch, store, teardown }) {
6
+ let disposed = false;
7
+ let disposePromise = null;
8
+
9
+ async function drainThenTeardown() {
10
+ batch.flush();
11
+ await store.flush();
12
+ teardown();
13
+ }
14
+
15
+ /** Persist queued rows, then release the exit registration. Active rows are
16
+ * NOT settled: they are keyed by process pid, which every inline runtime in
17
+ * the daemon shares, so settling here would idle other runtimes' workers.
18
+ * When the sync flush cannot run (this process's async writer holds the
19
+ * lock) the exit hook and store ref stay until the async drain settles. */
20
+ function dispose() {
21
+ if (disposed) return;
22
+ disposed = true;
23
+ let flushed = false;
24
+ try {
25
+ batch.flush();
26
+ flushed = store.flushSync();
27
+ } catch {}
28
+ if (flushed) {
29
+ teardown();
30
+ return;
31
+ }
32
+ disposePromise = drainThenTeardown().catch(reopenAfterFailedDrain);
33
+ }
34
+
35
+ /** A failed drain tears nothing down; allow a later dispose to retry it. */
36
+ function reopenAfterFailedDrain() {
37
+ disposed = false;
38
+ disposePromise = null;
39
+ }
40
+
41
+ /** Awaitable dispose: drain everything, then tear down. A failed drain
42
+ * rejects and leaves the registrations in place. */
43
+ function disposeAsync() {
44
+ if (!disposePromise) {
45
+ disposed = true;
46
+ disposePromise = drainThenTeardown().catch((error) => {
47
+ reopenAfterFailedDrain();
48
+ throw error;
49
+ });
50
+ }
51
+ return disposePromise;
52
+ }
53
+
54
+ return { dispose, disposeAsync };
55
+ }
@@ -0,0 +1,123 @@
1
+ // worker-index/released-store.mjs
2
+ // Which agent sessions their owner closed (the agent tool's `close`): session
3
+ // id → when. A closed session stays open and resumable, so a session scan
4
+ // (also after a daemon restart) would list it again; the marker is what keeps
5
+ // its rows `released` until a new turn starts on it. It lives in a file of its
6
+ // own beside the worker index, so a writer that rebuilds the index document
7
+ // (an older daemon) can never drop it. Entries expire after the tombstone TTL,
8
+ // judged on every read, not only when the file changes.
9
+ import { readFileSync, statSync } from 'node:fs';
10
+
11
+ import { parseJsonText } from '../../../../runtime/shared/atomic-file.mjs';
12
+ import { createIndexWriteQueue } from '../index-write-queue.mjs';
13
+ import { TAG_TOMBSTONE_TTL_MS } from '../worker-rows.mjs';
14
+
15
+ export const RELEASED_FILE = 'agent-workers-released.json';
16
+
17
+ /** id → epoch ms of every entry of a raw document (expired ones included). */
18
+ function parseReleased(doc) {
19
+ const raw = doc?.released && typeof doc.released === 'object' ? doc.released : {};
20
+ return new Map(
21
+ Object.entries(raw)
22
+ .map(([sessionId, at]) => [sessionId, Date.parse(String(at)) || 0])
23
+ .filter(([sessionId]) => sessionId)
24
+ );
25
+ }
26
+
27
+ /** The next document: unexpired entries plus the mutator's changes. */
28
+ function rewriteReleased(cur, mutator) {
29
+ const cutoff = Date.now() - TAG_TOMBSTONE_TTL_MS;
30
+ const entries = new Map();
31
+ for (const [sessionId, at] of parseReleased(cur)) {
32
+ if (at >= cutoff) entries.set(sessionId, new Date(at).toISOString());
33
+ }
34
+ mutator(entries);
35
+ return { version: 1, released: Object.fromEntries(entries) };
36
+ }
37
+
38
+ /** `file` may be null (no data dir): reads are empty and writes are no-ops. */
39
+ export function createReleasedStore(file) {
40
+ let cache = null; // { mtimeMs, size, doc, at }
41
+ let dirty = true;
42
+ // Unpersisted writes are read from the queue's projection.
43
+ let projected = null; // { version, at }
44
+
45
+ const queue = createIndexWriteQueue({
46
+ file,
47
+ rewrite: rewriteReleased,
48
+ readDoc: () => {
49
+ dirty = true;
50
+ entries();
51
+ return cache?.doc ?? null;
52
+ },
53
+ onPersisted: () => {
54
+ dirty = true;
55
+ },
56
+ });
57
+
58
+ /** id → epoch ms, as written (not yet filtered by expiry). */
59
+ function entries() {
60
+ const doc = queue.projection();
61
+ if (doc) {
62
+ if (!projected || projected.version !== queue.version()) {
63
+ projected = { version: queue.version(), at: parseReleased(doc) };
64
+ }
65
+ return projected.at;
66
+ }
67
+ if (!file) return new Map();
68
+ // Only a missing file is an empty store; any other read error propagates
69
+ // (queue.write reports it and queues nothing; a reader sees it thrown).
70
+ let st;
71
+ try {
72
+ st = statSync(file);
73
+ } catch (error) {
74
+ if (error?.code !== 'ENOENT') throw error;
75
+ cache = null;
76
+ return new Map();
77
+ }
78
+ if (!dirty && cache && cache.mtimeMs === st.mtimeMs && cache.size === st.size) return cache.at;
79
+ let parsed = null;
80
+ let text = null;
81
+ try {
82
+ text = readFileSync(file, 'utf8');
83
+ } catch (error) {
84
+ if (error?.code !== 'ENOENT') throw error;
85
+ }
86
+ if (text !== null) {
87
+ try {
88
+ parsed = parseJsonText(text);
89
+ } catch (error) {
90
+ // Left on disk and reported; the next save moves it aside to
91
+ // `<file>.corrupt-<ts>` (atomic-file's damaged-state recovery) instead
92
+ // of overwriting it.
93
+ process.stderr.write(`[released-store] ${file} is malformed (${error.message}); markers unreadable\n`);
94
+ }
95
+ }
96
+ cache = { mtimeMs: st.mtimeMs, size: st.size, doc: parsed, at: parseReleased(parsed) };
97
+ dirty = false;
98
+ return cache.at;
99
+ }
100
+
101
+ return {
102
+ /** Whether the owner closed this session less than the TTL ago. */
103
+ isReleased: (sessionId) => entries().get(sessionId) >= Date.now() - TAG_TOMBSTONE_TTL_MS,
104
+ mark(sessionIds) {
105
+ const at = new Date().toISOString();
106
+ queue.write((entriesById) => {
107
+ for (const sessionId of sessionIds) entriesById.set(sessionId, at);
108
+ });
109
+ },
110
+ clear(sessionId) {
111
+ if (!entries().has(sessionId)) return;
112
+ queue.write((entriesById) => {
113
+ entriesById.delete(sessionId);
114
+ });
115
+ },
116
+ flush: queue.flush,
117
+ flushSync: queue.flushSync,
118
+ hasUnpersisted: queue.hasUnpersisted,
119
+ invalidate: () => {
120
+ dirty = true;
121
+ },
122
+ };
123
+ }
@@ -0,0 +1,112 @@
1
+ // worker-index/restart-settlement.mjs
2
+ // Rows whose turn a process exit or restart ended: this process's active rows
3
+ // settled as failed on exit, stale active rows and approval waits settled at
4
+ // startup and on later sweeps, and the owner's restart-failure notification
5
+ // stamped once queued. Owns the keys of the rows this process marked active.
6
+ import { positiveInt } from '../helpers.mjs';
7
+ import { workerRowKey } from '../worker-rows.mjs';
8
+ import { clearAwaitingUser } from './approval-wait.mjs';
9
+ import {
10
+ createRowLiveness,
11
+ isActiveWorkerRow,
12
+ isStaleApprovalWait,
13
+ isUnnotifiedRestartFailure,
14
+ PROCESS_RESTART_FAILURE,
15
+ runtimeDied,
16
+ } from './row-liveness.mjs';
17
+
18
+ export function createRestartSettlement({ store, cfgMod, mgr }) {
19
+ const liveness = createRowLiveness({
20
+ cfgMod,
21
+ isSessionRunning: (sessionId) => mgr.isSessionTurnInFlight?.(sessionId) === true,
22
+ });
23
+ // Rows this process marked active; settled as failed on exit (their turn
24
+ // dies with the process) so a crash of the host never leaves them
25
+ // "running" forever.
26
+ const activeWorkerKeys = new Set();
27
+
28
+ /** Track whether the row written under `key` is one this process runs. */
29
+ function trackActiveRow(key, row) {
30
+ if (isActiveWorkerRow(row)) activeWorkerKeys.add(key);
31
+ else activeWorkerKeys.delete(key);
32
+ }
33
+
34
+ function flushActiveWorkerRows() {
35
+ if (!activeWorkerKeys.size) return;
36
+ const keys = [...activeWorkerKeys];
37
+ activeWorkerKeys.clear();
38
+ const now = Date.now();
39
+ store.write((byKey) => {
40
+ for (const key of keys) {
41
+ const current = byKey.get(key);
42
+ if (!current || positiveInt(current.runtimePid) !== process.pid) continue;
43
+ if (isActiveWorkerRow(current)) {
44
+ byKey.set(key, liveness.failedRow(current, now, PROCESS_RESTART_FAILURE, true));
45
+ }
46
+ }
47
+ });
48
+ }
49
+
50
+ // Startup recovery: stale active rows settle — as failed when another
51
+ // process ran their turn (it is gone, or is the predecessor still shutting
52
+ // down: either way the turn never finished), otherwise to idle — and an
53
+ // approval wait left behind by a runtime that is gone (daemon restart) is
54
+ // cleared. `deadOnly` (later sweeps) settles only rows whose runtime is
55
+ // provably dead, never a live process's quiet turn.
56
+ function recoverStaleWorkerRows({ deadOnly = false } = {}) {
57
+ const now = Date.now();
58
+ const rows = store.readAll();
59
+ const isStale = deadOnly
60
+ ? (row) => isActiveWorkerRow(row) && runtimeDied(row)
61
+ : (row) => liveness.isStaleActive(row, now);
62
+ const staleActive = rows.filter(isStale);
63
+ const staleWaits = rows.some(isStaleApprovalWait);
64
+ if (!staleActive.length && !staleWaits) return false;
65
+ if (staleActive.length) {
66
+ store.write((byKey) => {
67
+ for (const row of staleActive) {
68
+ const key = workerRowKey(row);
69
+ const current = byKey.get(key);
70
+ if (!current || !isStale(current)) continue;
71
+ const ranElsewhere = positiveInt(current.runtimePid) && positiveInt(current.runtimePid) !== process.pid;
72
+ byKey.set(
73
+ key,
74
+ ranElsewhere ? liveness.failedRow(current, now, PROCESS_RESTART_FAILURE) : liveness.idleRow(current, now)
75
+ );
76
+ }
77
+ });
78
+ }
79
+ if (staleWaits) store.update((byKey) => clearAwaitingUser(byKey, isStaleApprovalWait));
80
+ return true;
81
+ }
82
+
83
+ /** Rows whose turn a process restart ended and whose owner has not been told
84
+ * yet — whoever settled the row (this process at startup, or the
85
+ * predecessor's exit flush). Read-only: the caller enqueues the owner's
86
+ * notification durably, THEN calls `markRestartNotified`, so a crash in
87
+ * between retries (the notification's stable id makes the retry exactly-once). */
88
+ function pendingRestartFailures() {
89
+ return store.readAll().filter(isUnnotifiedRestartFailure);
90
+ }
91
+
92
+ /** Stamp these rows as told, once their notification is durably queued. */
93
+ function markRestartNotified(rows) {
94
+ const stamp = new Date().toISOString();
95
+ store.write((byKey) => {
96
+ for (const row of rows) {
97
+ const key = workerRowKey(row);
98
+ const current = byKey.get(key);
99
+ if (!current || !isUnnotifiedRestartFailure(current)) continue;
100
+ byKey.set(key, { ...current, restartNotified: stamp });
101
+ }
102
+ });
103
+ }
104
+
105
+ return {
106
+ trackActiveRow,
107
+ flushActiveWorkerRows,
108
+ recoverStaleWorkerRows,
109
+ pendingRestartFailures,
110
+ markRestartNotified,
111
+ };
112
+ }