mixdog 1.2.5 → 1.3.0

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 (493) hide show
  1. package/LICENSES/simple-icons-CC0-1.0.txt +30 -0
  2. package/NOTICE.md +19 -0
  3. package/package.json +4 -1
  4. package/scripts/check-model-recommendations.mjs +89 -0
  5. package/scripts/gate-local.mjs +7 -0
  6. package/scripts/lib/gate-scheduler.mjs +2 -2
  7. package/scripts/lib/isolated-test-env.mjs +16 -0
  8. package/scripts/lib/run-node-tests.mjs +5 -14
  9. package/scripts/provider-pingpong-live.mjs +9 -9
  10. package/src/agents/advisor/AGENT.md +4 -4
  11. package/src/agents/advisor/agent.json +1 -1
  12. package/src/agents/reviewer/AGENT.md +5 -4
  13. package/src/agents/reviewer/agent.json +1 -1
  14. package/src/defaults/shipped-history.json +17 -0
  15. package/src/defaults/skills/browser-use/SKILL.md +7 -1
  16. package/src/defaults/skills/computer-use/SKILL.md +3 -1
  17. package/src/defaults/skills/memory-management/SKILL.md +4 -1
  18. package/src/defaults/skills/setup/references/actions.md +11 -4
  19. package/src/defaults/skills/setup-guide/SKILL.md +175 -0
  20. package/src/defaults/skills/setup-guide/model-recommendations.json +333 -0
  21. package/src/defaults/skills/visualize/SKILL.md +3 -2
  22. package/src/lib/rules-builder.cjs +56 -7
  23. package/src/output-styles/common.md +22 -6
  24. package/src/output-styles/detailed.md +7 -5
  25. package/src/output-styles/minimal.md +7 -3
  26. package/src/output-styles/simple.md +2 -3
  27. package/src/rules/agent/AGENT.md +7 -1
  28. package/src/rules/agent-mode/AGENT_MODE.md +24 -0
  29. package/src/rules/agent-mode/MIXY.md +21 -0
  30. package/src/rules/agent-mode/ROUTINE.md +6 -0
  31. package/src/rules/lead/LEAD.md +9 -4
  32. package/src/rules/shared/10-tool-workflow.md +2 -1
  33. package/src/rules/shared/60-verification.md +4 -5
  34. package/src/rules/shared/70-delivery.md +3 -0
  35. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +2 -1
  36. package/src/runtime/agent/orchestrator/agent-runtime/room-supervisor-completion.mjs +86 -0
  37. package/src/runtime/agent/orchestrator/config.mjs +6 -0
  38. package/src/runtime/agent/orchestrator/context/deferred-tools.mjs +77 -14
  39. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +182 -13
  40. package/src/runtime/agent/orchestrator/context/skill-catalog.mjs +5 -3
  41. package/src/runtime/agent/orchestrator/internal-agents.mjs +10 -0
  42. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +215 -0
  43. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +58 -64
  44. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +10 -1
  45. package/src/runtime/agent/orchestrator/mcp/client.mjs +122 -15
  46. package/src/runtime/agent/orchestrator/mcp/oauth-account.mjs +97 -0
  47. package/src/runtime/agent/orchestrator/mcp/oauth-callback.mjs +2 -3
  48. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +49 -0
  49. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +59 -9
  50. package/src/runtime/agent/orchestrator/mcp/oauth-login.mjs +32 -7
  51. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +45 -6
  52. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +195 -6
  53. package/src/runtime/agent/orchestrator/mcp/server-connectors.mjs +17 -0
  54. package/src/runtime/agent/orchestrator/providers/account-pool.mjs +9 -2
  55. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  56. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +41 -19
  57. package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +3 -2
  58. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-start.mjs +4 -1
  59. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events.mjs +2 -0
  60. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn.mjs +3 -0
  61. package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +2 -1
  62. package/src/runtime/agent/orchestrator/providers/anthropic.mjs +3 -1
  63. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-login.mjs +11 -5
  64. package/src/runtime/agent/orchestrator/providers/antigravity-oauth-tokens.mjs +4 -0
  65. package/src/runtime/agent/orchestrator/providers/catalog-deadline.mjs +163 -0
  66. package/src/runtime/agent/orchestrator/providers/catalog-failures.mjs +34 -0
  67. package/src/runtime/agent/orchestrator/providers/cursor-client-version.mjs +1 -1
  68. package/src/runtime/agent/orchestrator/providers/grok-oauth-login.mjs +38 -14
  69. package/src/runtime/agent/orchestrator/providers/lib/oauth-page.mjs +100 -26
  70. package/src/runtime/agent/orchestrator/providers/lib/send-callbacks.mjs +24 -0
  71. package/src/runtime/agent/orchestrator/providers/oauth-credential-probes.mjs +13 -0
  72. package/src/runtime/agent/orchestrator/providers/oauth-usage.mjs +2 -1
  73. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-send.mjs +1 -0
  74. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/chunk-events.mjs +4 -0
  75. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream/stream-state.mjs +3 -1
  76. package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +2 -0
  77. package/src/runtime/agent/orchestrator/providers/openai-compat-models.mjs +49 -4
  78. package/src/runtime/agent/orchestrator/providers/openai-compat-presets.mjs +0 -7
  79. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-events.mjs +7 -1
  80. package/src/runtime/agent/orchestrator/providers/openai-compat-responses.mjs +1 -0
  81. package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +2 -0
  82. package/src/runtime/agent/orchestrator/providers/openai-compat-xai-send.mjs +1 -0
  83. package/src/runtime/agent/orchestrator/providers/openai-error-contract.mjs +17 -12
  84. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state/response-events.mjs +5 -1
  85. package/src/runtime/agent/orchestrator/providers/openai-http-sse-response-state.mjs +11 -2
  86. package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +1 -0
  87. package/src/runtime/agent/orchestrator/providers/openai-oauth-login.mjs +45 -16
  88. package/src/runtime/agent/orchestrator/providers/openai-oauth-tokens.mjs +10 -0
  89. package/src/runtime/agent/orchestrator/providers/openai-oauth-ws.mjs +1 -0
  90. package/src/runtime/agent/orchestrator/providers/openai-ws-connect.mjs +196 -0
  91. package/src/runtime/agent/orchestrator/providers/openai-ws-pool.mjs +74 -9
  92. package/src/runtime/agent/orchestrator/providers/openai-ws-stream/event-switch.mjs +14 -1
  93. package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -0
  94. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-login.mjs +194 -0
  95. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth-tokens.mjs +353 -0
  96. package/src/runtime/agent/orchestrator/providers/opencode-go-oauth.mjs +293 -0
  97. package/src/runtime/agent/orchestrator/providers/opencode-go-usage.mjs +33 -100
  98. package/src/runtime/agent/orchestrator/providers/opencode-go.mjs +23 -14
  99. package/src/runtime/agent/orchestrator/providers/registry.mjs +41 -27
  100. package/src/runtime/agent/orchestrator/runtime-core/ask-user-tool-defs.mjs +98 -4
  101. package/src/runtime/agent/orchestrator/runtime-core/context-status-shape.mjs +4 -0
  102. package/src/runtime/agent/orchestrator/runtime-core/extension-scopes.mjs +6 -4
  103. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +15 -8
  104. package/src/runtime/agent/orchestrator/runtime-core/plugin-mcp.mjs +1 -0
  105. package/src/runtime/agent/orchestrator/runtime-core/tool-bridge.mjs +12 -6
  106. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +32 -5
  107. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +10 -4
  108. package/src/runtime/agent/orchestrator/runtime-core/workflow/agents.mjs +4 -1
  109. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +50 -20
  110. package/src/runtime/agent/orchestrator/runtime-core/workflow-ids.mjs +2 -0
  111. package/src/runtime/agent/orchestrator/session/agent-home.d.mts +4 -0
  112. package/src/runtime/agent/orchestrator/session/agent-home.mjs +33 -0
  113. package/src/runtime/agent/orchestrator/session/agent-mode.mjs +62 -0
  114. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +15 -3
  115. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.d.mts +31 -0
  116. package/src/runtime/agent/orchestrator/session/agent-room-supervisor.mjs +241 -0
  117. package/src/runtime/agent/orchestrator/session/agent-room.d.mts +162 -0
  118. package/src/runtime/agent/orchestrator/session/agent-room.mjs +641 -0
  119. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +7 -1
  120. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +74 -13
  121. package/src/runtime/agent/orchestrator/session/compact/forked-summary.mjs +102 -0
  122. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +8 -2
  123. package/src/runtime/agent/orchestrator/session/compact/summary.mjs +16 -0
  124. package/src/runtime/agent/orchestrator/session/compact/user-message-kind.mjs +2 -0
  125. package/src/runtime/agent/orchestrator/session/compaction-archive-capture.mjs +6 -2
  126. package/src/runtime/agent/orchestrator/session/human-gate/gate.mjs +165 -0
  127. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +95 -0
  128. package/src/runtime/agent/orchestrator/session/human-gate/outbound-policy.mjs +221 -0
  129. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +195 -0
  130. package/src/runtime/agent/orchestrator/session/human-gate/store.mjs +167 -0
  131. package/src/runtime/agent/orchestrator/session/lifecycle-scan.mjs +9 -0
  132. package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +3 -1
  133. package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +39 -2
  134. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +10 -0
  135. package/src/runtime/agent/orchestrator/session/loop/send-phase.mjs +16 -1
  136. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +82 -33
  137. package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +11 -4
  138. package/src/runtime/agent/orchestrator/session/loop/tool-exposure-migration.mjs +5 -4
  139. package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +13 -3
  140. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +7 -0
  141. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +1 -0
  142. package/src/runtime/agent/orchestrator/session/manager/ask-turn-loop-options.mjs +6 -1
  143. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +6 -0
  144. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +2 -0
  145. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +13 -5
  146. package/src/runtime/agent/orchestrator/session/manager/pending-lifecycle-epoch.mjs +0 -8
  147. package/src/runtime/agent/orchestrator/session/manager/pending-message-entry.mjs +10 -7
  148. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +62 -22
  149. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +44 -16
  150. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +22 -5
  151. package/src/runtime/agent/orchestrator/session/manager/runtime-liveness.mjs +23 -1
  152. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +81 -34
  153. package/src/runtime/agent/orchestrator/session/manager/session-crud.mjs +39 -4
  154. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +27 -5
  155. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +55 -8
  156. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +16 -0
  157. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +3 -2
  158. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +6 -0
  159. package/src/runtime/agent/orchestrator/session/manager/workflow-switch-notice.mjs +58 -0
  160. package/src/runtime/agent/orchestrator/session/manager.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +24 -0
  162. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +32 -5
  163. package/src/runtime/agent/orchestrator/session/request-fork.mjs +47 -0
  164. package/src/runtime/agent/orchestrator/session/routine-result.d.mts +70 -0
  165. package/src/runtime/agent/orchestrator/session/routine-result.mjs +277 -0
  166. package/src/runtime/agent/orchestrator/session/side-question-store.mjs +16 -1
  167. package/src/runtime/agent/orchestrator/session/side-question.mjs +5 -43
  168. package/src/runtime/agent/orchestrator/session/store/fs-probe.mjs +10 -0
  169. package/src/runtime/agent/orchestrator/session/store/lifecycle-barriers.mjs +73 -6
  170. package/src/runtime/agent/orchestrator/session/store/load-session.mjs +40 -0
  171. package/src/runtime/agent/orchestrator/session/store/save-fault.mjs +15 -0
  172. package/src/runtime/agent/orchestrator/session/store/session-paths.mjs +12 -0
  173. package/src/runtime/agent/orchestrator/session/store/sweep/sweep-row.mjs +8 -0
  174. package/src/runtime/agent/orchestrator/session/store-agent-worker-header.mjs +36 -0
  175. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +258 -43
  176. package/src/runtime/agent/orchestrator/session/store-record-strict-scan.mjs +325 -0
  177. package/src/runtime/agent/orchestrator/session/store-summary-catalog.mjs +84 -21
  178. package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +44 -12
  179. package/src/runtime/agent/orchestrator/session/store-summary-last-message.mjs +46 -0
  180. package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +46 -30
  181. package/src/runtime/agent/orchestrator/session/store-summary-visibility.mjs +0 -4
  182. package/src/runtime/agent/orchestrator/session/store-transcript-cache.mjs +144 -45
  183. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +32 -13
  184. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +364 -38
  185. package/src/runtime/agent/orchestrator/session/store.mjs +9 -2
  186. package/src/runtime/agent/orchestrator/session/task-wait-control.mjs +43 -12
  187. package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +23 -6
  188. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +44 -1
  189. package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +12 -6
  190. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +13 -7
  191. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/bashism-rewrite.mjs +1 -1
  192. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/benign-exit.mjs +7 -1
  193. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/completed-result.mjs +40 -7
  194. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/deadline-plan.mjs +37 -34
  195. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/inline-hoist.mjs +1 -1
  196. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/spawn-env.mjs +0 -4
  197. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +6 -0
  198. package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +29 -27
  199. package/src/runtime/agent/orchestrator/tools/builtin/external-tool-adapters.mjs +1 -1
  200. package/src/runtime/agent/orchestrator/tools/builtin/find-files-tool.mjs +28 -11
  201. package/src/runtime/agent/orchestrator/tools/builtin/git-command-policy.mjs +12 -3
  202. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +203 -101
  203. package/src/runtime/agent/orchestrator/tools/builtin/git-repo-rw-lock.mjs +90 -41
  204. package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +17 -1
  205. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-delivery.mjs +199 -0
  206. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-job-records.mjs +12 -0
  207. package/src/runtime/agent/orchestrator/tools/builtin/lib/shell-task-output.mjs +165 -39
  208. package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +25 -13
  209. package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +113 -5
  210. package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +7 -6
  211. package/src/runtime/agent/orchestrator/tools/builtin/read-formatting.mjs +19 -4
  212. package/src/runtime/agent/orchestrator/tools/builtin/read-line-collector.mjs +5 -3
  213. package/src/runtime/agent/orchestrator/tools/builtin/read-single-render.mjs +25 -7
  214. package/src/runtime/agent/orchestrator/tools/builtin/read-special-files.mjs +2 -1
  215. package/src/runtime/agent/orchestrator/tools/builtin/read-tool/batch-render.mjs +3 -1
  216. package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +12 -5
  217. package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +61 -22
  218. package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +32 -8
  219. package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +193 -19
  220. package/src/runtime/agent/orchestrator/tools/builtin/shell-direct-exe.mjs +57 -7
  221. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +123 -32
  222. package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +26 -1
  223. package/src/runtime/agent/orchestrator/tools/builtin/shell-output.mjs +13 -28
  224. package/src/runtime/agent/orchestrator/tools/builtin/task-tool.mjs +90 -11
  225. package/src/runtime/agent/orchestrator/tools/builtin/wait-budgets.mjs +23 -0
  226. package/src/runtime/agent/orchestrator/tools/builtin.mjs +54 -10
  227. package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +22 -28
  228. package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +11 -7
  229. package/src/runtime/agent/orchestrator/tools/destructive-warning.mjs +126 -9
  230. package/src/runtime/agent/orchestrator/tools/graph-manifest.json +13 -13
  231. package/src/runtime/agent/orchestrator/tools/lib/owned-shell-trees.mjs +15 -6
  232. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +64 -2
  233. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promote-job.mjs +10 -2
  234. package/src/runtime/agent/orchestrator/tools/lib/shell-run-background/promotion-results.mjs +13 -3
  235. package/src/runtime/agent/orchestrator/tools/lib/shell-run-settle.mjs +8 -0
  236. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +6 -1
  237. package/src/runtime/agent/orchestrator/tools/patch/v4a-convert.mjs +1 -1
  238. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +104 -55
  239. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +7 -2
  240. package/src/runtime/agent/orchestrator/tools/shell-powershell.mjs +39 -23
  241. package/src/runtime/agent/orchestrator/tools/shell-snapshot.mjs +6 -2
  242. package/src/runtime/agent/orchestrator/tools/spawn-manifest.json +13 -13
  243. package/src/runtime/attachments/office-extract.mjs +11 -9
  244. package/src/runtime/browser-bridge/client.mjs +24 -0
  245. package/src/runtime/channels/lib/scheduler.mjs +40 -12
  246. package/src/runtime/channels/lib/schedules-db.mjs +71 -12
  247. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +36 -16
  248. package/src/runtime/channels/lib/webhook.mjs +0 -1
  249. package/src/runtime/channels/lib/webhooks-db.mjs +100 -10
  250. package/src/runtime/channels/lib/worker-main.mjs +17 -20
  251. package/src/runtime/computer-bridge/client.mjs +28 -1
  252. package/src/runtime/connectors/gmail-app-password.mjs +690 -0
  253. package/src/runtime/connectors/google-mcp.mjs +446 -0
  254. package/src/runtime/memory/lib/core-memory-access.mjs +69 -0
  255. package/src/runtime/memory/lib/core-memory-file.mjs +16 -8
  256. package/src/runtime/memory/lib/core-memory-index.mjs +15 -9
  257. package/src/runtime/memory/lib/core-memory-management.mjs +10 -4
  258. package/src/runtime/memory/lib/core-memory-store.mjs +53 -30
  259. package/src/runtime/memory/lib/core-memory-uniqueness.mjs +10 -6
  260. package/src/runtime/memory/lib/embedding-tokenizer.mjs +32 -18
  261. package/src/runtime/memory/lib/http-router/session-routes.mjs +3 -2
  262. package/src/runtime/memory/lib/memory-action-handlers/core-actions.mjs +44 -14
  263. package/src/runtime/memory/lib/memory-schema/core-and-meta.mjs +11 -0
  264. package/src/runtime/memory/lib/memory.mjs +7 -1
  265. package/src/runtime/memory/lib/query-core-recall.mjs +9 -1
  266. package/src/runtime/memory/lib/query-hybrid-search.mjs +2 -0
  267. package/src/runtime/memory/lib/runtime-fetcher.mjs +1 -1
  268. package/src/runtime/memory/lib/tool-call-handler.mjs +9 -5
  269. package/src/runtime/memory/tool-defs.mjs +5 -0
  270. package/src/runtime/office/authoring/pptx-script-normalize.mjs +11 -8
  271. package/src/runtime/office/core/tabular.mjs +1 -1
  272. package/src/runtime/office/design/library/design-template-inspect.mjs +7 -4
  273. package/src/runtime/office/portable/docx-formatting.mjs +1 -1
  274. package/src/runtime/office/portable/docx-runs.mjs +1 -1
  275. package/src/runtime/office/portable/docx-tracked-edits.mjs +2 -2
  276. package/src/runtime/office/portable/portable-docx-edits.mjs +11 -10
  277. package/src/runtime/office/portable/portable-docx-operations.mjs +21 -17
  278. package/src/runtime/office/portable/portable-docx-parts.mjs +15 -9
  279. package/src/runtime/office/portable/portable-docx-styles.mjs +4 -1
  280. package/src/runtime/office/portable/portable-docx-xml.mjs +9 -9
  281. package/src/runtime/office/portable/portable-opc.mjs +27 -13
  282. package/src/runtime/office/portable/portable-pivot.mjs +3 -3
  283. package/src/runtime/office/portable/portable-pptx-chart.mjs +3 -3
  284. package/src/runtime/office/portable/portable-pptx-charts.mjs +24 -18
  285. package/src/runtime/office/portable/portable-pptx-core.mjs +5 -5
  286. package/src/runtime/office/portable/portable-pptx-deck.mjs +8 -4
  287. package/src/runtime/office/portable/portable-pptx-package.mjs +7 -4
  288. package/src/runtime/office/portable/portable-pptx-shapes.mjs +18 -9
  289. package/src/runtime/office/portable/portable-sheet-parts.mjs +9 -3
  290. package/src/runtime/office/portable/portable-sheet-xml.mjs +5 -5
  291. package/src/runtime/office/portable/portable-xlsx-charts.mjs +4 -1
  292. package/src/runtime/office/portable/portable-xlsx-drawings.mjs +10 -6
  293. package/src/runtime/office/portable/portable-xlsx-operations.mjs +8 -5
  294. package/src/runtime/office/portable/portable-xlsx-pivot-ops.mjs +1 -1
  295. package/src/runtime/office/portable/portable-xlsx-reference-shift.mjs +11 -7
  296. package/src/runtime/office/portable/portable-xlsx-sheet-edits.mjs +6 -3
  297. package/src/runtime/office/portable/portable-xml.mjs +1 -1
  298. package/src/runtime/shared/agent-avatar-sprites.mjs +445 -0
  299. package/src/runtime/shared/agent-avatar.d.mts +64 -0
  300. package/src/runtime/shared/agent-avatar.mjs +320 -0
  301. package/src/runtime/shared/agent-route-config.d.mts +3 -0
  302. package/src/runtime/shared/agent-route-config.mjs +16 -0
  303. package/src/runtime/shared/atomic-file.mjs +36 -14
  304. package/src/runtime/shared/background-tasks.mjs +60 -20
  305. package/src/runtime/shared/child-spawn-gate.mjs +26 -16
  306. package/src/runtime/shared/config.mjs +8 -11
  307. package/src/runtime/shared/connector-accounts.mjs +15 -0
  308. package/src/runtime/shared/connector-catalog.d.mts +52 -0
  309. package/src/runtime/shared/connector-catalog.mjs +230 -0
  310. package/src/runtime/shared/connector-triggers.d.mts +3 -0
  311. package/src/runtime/shared/connector-triggers.mjs +43 -0
  312. package/src/runtime/shared/file-lock.mjs +7 -1
  313. package/src/runtime/shared/file-permissions-worker.mjs +38 -0
  314. package/src/runtime/shared/file-permissions.d.mts +19 -4
  315. package/src/runtime/shared/file-permissions.mjs +310 -4
  316. package/src/runtime/shared/git-runner-worker.mjs +6 -78
  317. package/src/runtime/shared/google-oauth-client.mjs +24 -0
  318. package/src/runtime/shared/held-request-host.d.mts +12 -0
  319. package/src/runtime/shared/held-request-host.mjs +71 -0
  320. package/src/runtime/shared/llm/http-agent.mjs +7 -3
  321. package/src/runtime/shared/llm/provider-catalog-registry.mjs +10 -0
  322. package/src/runtime/shared/llm/usage-accounting.mjs +2 -0
  323. package/src/runtime/shared/llm/usage-ledger-import.mjs +32 -3
  324. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +12 -5
  325. package/src/runtime/shared/llm/usage-ledger-worker.mjs +1 -1
  326. package/src/runtime/shared/llm/usage-ledger.mjs +20 -5
  327. package/src/runtime/shared/memory-snapshot.mjs +5 -1
  328. package/src/runtime/shared/notify-trace.mjs +8 -1
  329. package/src/runtime/shared/plain-preview.d.mts +1 -0
  330. package/src/runtime/shared/plain-preview.mjs +28 -0
  331. package/src/runtime/shared/pristine-execution-contract.json +5 -1
  332. package/src/runtime/shared/process-runner-worker.mjs +7 -0
  333. package/src/runtime/shared/process-runner.mjs +38 -0
  334. package/src/runtime/shared/provider-accounts.mjs +2 -1
  335. package/src/runtime/shared/provider-api-key.mjs +0 -1
  336. package/src/runtime/shared/read-row-numbers.mjs +1 -1
  337. package/src/runtime/shared/resource-admission.mjs +60 -18
  338. package/src/runtime/shared/routine-limits.d.mts +11 -0
  339. package/src/runtime/shared/routine-limits.mjs +21 -0
  340. package/src/runtime/shared/routine-trigger.d.mts +10 -0
  341. package/src/runtime/shared/routine-trigger.mjs +44 -0
  342. package/src/runtime/shared/run-child.mjs +84 -0
  343. package/src/runtime/shared/schedule-model-ref.mjs +9 -10
  344. package/src/runtime/shared/session-activity-signal.mjs +23 -0
  345. package/src/runtime/shared/shell-output-summary.mjs +23 -0
  346. package/src/runtime/shared/singleton-owner.mjs +126 -42
  347. package/src/runtime/shared/slow-span.mjs +4 -12
  348. package/src/runtime/shared/statusline/statusline-segments.mjs +13 -1
  349. package/src/runtime/shared/tool-output-limit.mjs +4 -2
  350. package/src/runtime/shared/tool-output-marker.mjs +58 -0
  351. package/src/runtime/shared/transcript-metadata.mjs +10 -0
  352. package/src/runtime/shared/turn-changes.mjs +0 -8
  353. package/src/runtime/shared/usage-readout-totals.mjs +56 -0
  354. package/src/runtime/shared/user-data-guard.mjs +131 -34
  355. package/src/runtime/shared/worktrees/activity-gate.mjs +33 -8
  356. package/src/runtime/shared/worktrees/git-head.mjs +20 -2
  357. package/src/runtime/shared/worktrees/lock.mjs +3 -2
  358. package/src/runtime/terminal-bridge/tool-defs.mjs +1 -1
  359. package/src/runtime/tidy/runners/biome.mjs +3 -1
  360. package/src/runtime/tidy/shadow.mjs +2 -1
  361. package/src/runtime/web-search/index.mjs +5 -7
  362. package/src/runtime/web-search/lib/formatter.mjs +42 -6
  363. package/src/runtime/web-search/tool-defs.mjs +6 -1
  364. package/src/session-runtime/agent-home-delivery.mjs +459 -0
  365. package/src/session-runtime/agent-profile-changes.mjs +21 -0
  366. package/src/session-runtime/agent-room-delivery.mjs +92 -0
  367. package/src/session-runtime/agent-task-sessions.mjs +200 -0
  368. package/src/session-runtime/boot/apis.mjs +50 -18
  369. package/src/session-runtime/boot/facade.mjs +5 -8
  370. package/src/session-runtime/boot/infrastructure.mjs +28 -17
  371. package/src/session-runtime/boot/tools.mjs +3 -0
  372. package/src/session-runtime/channel-config-api.mjs +53 -5
  373. package/src/session-runtime/cwd-plugins/core-memory-context.mjs +36 -20
  374. package/src/session-runtime/cwd-plugins/plugin-status.mjs +6 -1
  375. package/src/session-runtime/held-requests.mjs +228 -0
  376. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +121 -0
  377. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +5 -1
  378. package/src/session-runtime/internal-tool-executor/knowledge-tools.mjs +31 -2
  379. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +31 -2
  380. package/src/session-runtime/internal-tool-executor.mjs +2 -0
  381. package/src/session-runtime/lifecycle/session-catalog.mjs +9 -0
  382. package/src/session-runtime/lifecycle/session-switch/context-switch.mjs +4 -3
  383. package/src/session-runtime/lifecycle/session-switch/resume.mjs +11 -4
  384. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +3 -3
  385. package/src/session-runtime/lifecycle/shared.mjs +25 -2
  386. package/src/session-runtime/lifecycle/teardown/session-close.mjs +2 -0
  387. package/src/session-runtime/lifecycle/teardown.mjs +5 -2
  388. package/src/session-runtime/lifecycle-api.mjs +3 -1
  389. package/src/session-runtime/mcp-glue/server-catalog.mjs +8 -0
  390. package/src/session-runtime/mcp-glue/server-input.mjs +3 -0
  391. package/src/session-runtime/mcp-oauth-login.mjs +112 -15
  392. package/src/session-runtime/model-route/set-route.mjs +14 -3
  393. package/src/session-runtime/owned-automation-run.mjs +229 -0
  394. package/src/session-runtime/provider-auth/login.mjs +10 -6
  395. package/src/session-runtime/provider-auth/setup.mjs +19 -2
  396. package/src/session-runtime/provider-catalog-cache.mjs +101 -25
  397. package/src/session-runtime/provider-models/catalog-load.mjs +14 -3
  398. package/src/session-runtime/provider-models/row-hydration.mjs +4 -0
  399. package/src/session-runtime/provider-models.mjs +76 -10
  400. package/src/session-runtime/provider-readiness.mjs +4 -0
  401. package/src/session-runtime/resource-api.mjs +5 -0
  402. package/src/session-runtime/resource-mcp-api.mjs +107 -13
  403. package/src/session-runtime/resource-skills-api.mjs +12 -0
  404. package/src/session-runtime/room-member-gate.mjs +48 -0
  405. package/src/session-runtime/routine-home-turn.mjs +156 -0
  406. package/src/session-runtime/routine-owner.mjs +48 -0
  407. package/src/session-runtime/schedule-session-run.mjs +30 -64
  408. package/src/session-runtime/services/agent-tool/close-flow.mjs +2 -1
  409. package/src/session-runtime/services/agent-tool/helpers.mjs +10 -0
  410. package/src/session-runtime/services/agent-tool/job-views/agent-list.mjs +10 -2
  411. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +13 -4
  412. package/src/session-runtime/services/agent-tool/notify.mjs +15 -4
  413. package/src/session-runtime/services/agent-tool/restart-notify.mjs +134 -0
  414. package/src/session-runtime/services/agent-tool/send-flow.mjs +24 -1
  415. package/src/session-runtime/services/agent-tool/spawn-flow/deferred-spawn.mjs +2 -1
  416. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +4 -3
  417. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +19 -4
  418. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-prep.mjs +2 -0
  419. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +5 -1
  420. package/src/session-runtime/services/agent-tool/tag-registry/tag-lookup.mjs +8 -3
  421. package/src/session-runtime/services/agent-tool/tag-registry.mjs +3 -0
  422. package/src/session-runtime/services/agent-tool/worker-index/row-shape.mjs +4 -0
  423. package/src/session-runtime/services/agent-tool/worker-index.mjs +44 -10
  424. package/src/session-runtime/services/agent-tool.mjs +20 -0
  425. package/src/session-runtime/services/channel-admin.mjs +93 -24
  426. package/src/session-runtime/services/memory-access.mjs +37 -0
  427. package/src/session-runtime/services/provider-admin.mjs +166 -59
  428. package/src/session-runtime/services/schedule-access.mjs +91 -0
  429. package/src/session-runtime/services/usage-dashboard-model.mjs +2 -2
  430. package/src/session-runtime/services/usage-dashboard.mjs +27 -69
  431. package/src/session-runtime/session-create/session-options.mjs +34 -7
  432. package/src/session-runtime/session-hooks.mjs +16 -8
  433. package/src/session-runtime/setup-tool/executor.mjs +68 -6
  434. package/src/session-runtime/setup-tool/extended-actions.mjs +72 -14
  435. package/src/session-runtime/setup-tool/settings-contract.mjs +8 -6
  436. package/src/session-runtime/setup-tool/tool-defs.mjs +8 -0
  437. package/src/session-runtime/skills-api.mjs +16 -1
  438. package/src/session-runtime/tool-policy-refresh.mjs +238 -29
  439. package/src/session-runtime/turn/session-ops.mjs +10 -3
  440. package/src/session-runtime/turn/turn-run/ask-callbacks.mjs +11 -2
  441. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +3 -0
  442. package/src/session-runtime/usage-stats-api.mjs +6 -1
  443. package/src/session-runtime/webhook-session-run.mjs +49 -67
  444. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +239 -0
  445. package/src/session-runtime/workflow-agents-api/agent-editor.mjs +556 -22
  446. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +11 -1
  447. package/src/session-runtime/workflow-agents-api.mjs +4 -1
  448. package/src/standalone/channel-transport/http-routes.mjs +10 -9
  449. package/src/standalone/daemon-agent-control.mjs +1 -8
  450. package/src/standalone/daemon-desktop-runtime.mjs +20 -7
  451. package/src/standalone/daemon-owner.mjs +117 -25
  452. package/src/standalone/daemon-session-control.mjs +11 -2
  453. package/src/standalone/daemon.mjs +61 -17
  454. package/src/standalone/session-call-cache.mjs +6 -0
  455. package/src/standalone/session-protocol.mjs +28 -1
  456. package/src/standalone/session-service/retention/busy.mjs +7 -2
  457. package/src/standalone/session-service/session-calls/session-turns.mjs +9 -0
  458. package/src/standalone/session-service.mjs +19 -0
  459. package/src/standalone/session-transport/routes.mjs +7 -6
  460. package/src/tui/app/model-options.mjs +39 -2
  461. package/src/tui/app/model-picker.mjs +16 -2
  462. package/src/tui/app/provider-setup-picker.mjs +1 -2
  463. package/src/tui/app/slash-commands.mjs +20 -0
  464. package/src/tui/dist/index.mjs +487 -128
  465. package/src/tui/session/context-state.mjs +6 -1
  466. package/src/tui/session/notice-surface.mjs +35 -4
  467. package/src/tui/session/notice-text.mjs +0 -1
  468. package/src/tui/session/queue-helpers.mjs +2 -1
  469. package/src/tui/session/session-api/extensions.mjs +4 -0
  470. package/src/tui/session/session-api/intake/submission.mjs +12 -13
  471. package/src/tui/session/session-api/integrations.mjs +9 -5
  472. package/src/tui/session/session-api/lifecycle/session-reset.mjs +13 -3
  473. package/src/tui/session/session-api/lifecycle.mjs +17 -1
  474. package/src/tui/session/session-api/routes.mjs +48 -3
  475. package/src/tui/session/session-flow/drain.mjs +20 -3
  476. package/src/tui/session/session-flow/queue/entry-shape.mjs +2 -1
  477. package/src/tui/session/session-flow/queue/take-entries.mjs +3 -2
  478. package/src/tui/session/session-flow/steering.mjs +104 -24
  479. package/src/tui/session/session-flow.mjs +2 -0
  480. package/src/tui/session/tool-approval.mjs +26 -5
  481. package/src/tui/session/transcript-intake.mjs +6 -2
  482. package/src/tui/session/transcript-spill.mjs +5 -1
  483. package/src/tui/session/tui-steering-persist.mjs +65 -42
  484. package/src/tui/session/turn-live-call.mjs +56 -0
  485. package/src/tui/session/turn-stream.mjs +33 -0
  486. package/src/tui/session/turn.mjs +50 -7
  487. package/src/ui/statusline.mjs +1 -6
  488. package/src/ui/streaming-markdown-heal.mjs +24 -7
  489. package/src/vendor/statusline/bin/statusline-route.mjs +0 -1
  490. package/src/vendor/statusline/src/gateway/route-meta.mjs +0 -1
  491. package/src/workflows/default/WORKFLOW.md +9 -13
  492. package/src/session-runtime/automation-agents.mjs +0 -94
  493. package/src/session-runtime/automation-workflow.mjs +0 -59
@@ -0,0 +1,48 @@
1
+ // A group-room member's tool calls (session/agent-room.mjs). Reads run freely;
2
+ // anything that can change files or state asks the user through the ordinary
3
+ // approval flow, whatever the agent's own permission or hook rules say — even
4
+ // an agent that normally runs auto-approved. A hook's deny still wins.
5
+ import { analyzeShellCommandEffects } from '../runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs';
6
+
7
+ const READ_ONLY_TOOLS = new Set([
8
+ 'read',
9
+ 'grep',
10
+ 'glob',
11
+ 'find',
12
+ 'list',
13
+ 'code_graph',
14
+ 'load_tool',
15
+ 'skill',
16
+ 'Skill',
17
+ 'recall',
18
+ 'search_memories',
19
+ 'web_search',
20
+ 'web_fetch',
21
+ 'local_fetch',
22
+ 'image_fetch',
23
+ 'ask_user',
24
+ ]);
25
+
26
+ export const ROOM_APPROVAL_REASON = 'Group-room members need your approval before changing anything.';
27
+
28
+ /** Whether `name(args)` only reads: a read-only tool, or a shell command whose
29
+ * every stage the shell analysis classifies as non-mutating. */
30
+ export async function isReadOnlyRoomCall({ name, args, cwd }, analyze = analyzeShellCommandEffects) {
31
+ if (READ_ONLY_TOOLS.has(name)) return true;
32
+ if (name !== 'shell' || typeof args?.command !== 'string') return false;
33
+ try {
34
+ return (await analyze(args.command, cwd))?.mutationMode === 'none';
35
+ } catch {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ /** The PreToolUse decision for a room member's call: the hook's own `decision`
41
+ * when it denies or the call only reads; otherwise an approval ask that keeps
42
+ * any rewrite the hook made. */
43
+ export async function roomMemberToolDecision(call, decision, analyze) {
44
+ const action = String(decision?.action || decision?.decision || '').toLowerCase();
45
+ if (action === 'deny' || action === 'block') return decision;
46
+ if (await isReadOnlyRoomCall(call, analyze)) return decision;
47
+ return { ...(decision && typeof decision === 'object' ? decision : {}), action: 'ask', reason: ROOM_APPROVAL_REASON };
48
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * A routine fire (schedule or webhook) is a TURN in its owner bot's home
3
+ * thread (`sess_home_<agentId>`): the routine's instructions are delivered
4
+ * there as a queued turn the bot handles itself — doing it or delegating as
5
+ * in any turn — and its reply in the chat is the result. Nothing else is
6
+ * created: no run session, no result card.
7
+ *
8
+ * The turn waits behind the thread's own work (a user turn, another routine):
9
+ * it rides the thread's queue as its own `routine` entry (one per turn, never
10
+ * merged or steered into another), shows as a routine marker row, and selects
11
+ * skills only from the routine's instructions (`skillSelection`), never from a
12
+ * webhook payload. While it runs the thread counts as unattended, so its
13
+ * approvals are held for the owner (held-requests.mjs). Resolves with the
14
+ * reply once the turn ends; rejects when the turn fails, is cancelled, or the
15
+ * caller's `signal` aborts (which also aborts the turn once it runs).
16
+ */
17
+ import { randomUUID } from 'node:crypto';
18
+ import { agentHomeSessionId } from '../runtime/agent/orchestrator/session/agent-home.mjs';
19
+ import { routineHumanNeededText } from '../runtime/agent/orchestrator/session/routine-result.mjs';
20
+ import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
21
+ import { ROUTINE_LIMITS } from '../runtime/shared/routine-limits.mjs';
22
+ import { normalizeRoutineTrigger, routineTriggerHeader } from '../runtime/shared/routine-trigger.mjs';
23
+ import { agentHomeLiveSurface, scheduleOwnerAgentId } from './agent-home-delivery.mjs';
24
+ import { markUnattendedRun, unattendedRunOf } from './held-requests.mjs';
25
+
26
+ // Routine turns waiting or running per home thread.
27
+ const waiting = new Map();
28
+
29
+ const abortError = (signal) =>
30
+ signal?.reason instanceof Error ? signal.reason : new Error(String(signal?.reason || 'routine run aborted'));
31
+
32
+ async function defaultLoadSession(sessionId) {
33
+ const { loadSession } = await import('../runtime/agent/orchestrator/session/store.mjs');
34
+ return loadSession(sessionId);
35
+ }
36
+
37
+ function settledError(detail) {
38
+ if (detail?.status === 'cancelled') return new Error('the routine turn was cancelled');
39
+ if (detail?.status === 'rejected') return new Error("the bot's chat closed before the routine ran");
40
+ const error = detail?.error;
41
+ return new Error(String(error?.message || error || 'the routine turn failed'));
42
+ }
43
+
44
+ /**
45
+ * Deliver one fire of routine `name` (`kind` schedule | webhook) to its
46
+ * owner's home thread. `prompt` is the model-visible body (instructions, plus
47
+ * a webhook's fenced payload); `skillSelection` its trusted part (default:
48
+ * the whole body); `event` a short label for the marker (webhook event).
49
+ */
50
+ export async function runRoutineTurn(
51
+ {
52
+ kind,
53
+ name,
54
+ ownerAgentId = null,
55
+ prompt,
56
+ skillSelection = null,
57
+ attachments = null,
58
+ cwd = null,
59
+ event = '',
60
+ signal = null,
61
+ },
62
+ {
63
+ surface = agentHomeLiveSurface(),
64
+ loadSession = defaultLoadSession,
65
+ limit = ROUTINE_LIMITS.homeQueueLimit,
66
+ log = (line) => process.stderr.write(`mixdog routines: ${line}\n`),
67
+ } = {}
68
+ ) {
69
+ const body = String(prompt || '').trim();
70
+ if (!body) throw new Error(`${kind} "${name}" has no instructions`);
71
+ if (signal?.aborted) throw abortError(signal);
72
+ if (typeof surface?.submit !== 'function') throw new Error('routine run: the session service is not running');
73
+ const agentId = scheduleOwnerAgentId({ ownerAgentId });
74
+ const homeId = agentHomeSessionId(agentId);
75
+ const home = await loadSession(homeId);
76
+ if (!home || home.closed === true) {
77
+ throw new Error(`the bot "${agentId}" has no chat yet: open it once in the app`);
78
+ }
79
+ const queued = waiting.get(homeId) || 0;
80
+ if (queued >= limit) throw new Error(`${limit} routine runs already wait in this bot's chat`);
81
+ waiting.set(homeId, queued + 1);
82
+ const trigger = normalizeRoutineTrigger({ kind, name, event });
83
+ const header = routineTriggerHeader({ kind, name, project: cwd ? String(cwd) : '' });
84
+ const content = automationPromptContent(`${header}\n\n${body}`, attachments);
85
+ return new Promise((resolve, reject) => {
86
+ let started = false;
87
+ let aborted = false;
88
+ let done = false;
89
+ let unmark = () => {};
90
+ let released = false;
91
+ const release = () => {
92
+ if (released) return;
93
+ released = true;
94
+ const left = (waiting.get(homeId) || 1) - 1;
95
+ if (left > 0) waiting.set(homeId, left);
96
+ else waiting.delete(homeId);
97
+ signal?.removeEventListener?.('abort', onAbort);
98
+ };
99
+ const finish = (fn, value) => {
100
+ if (done) return;
101
+ done = true;
102
+ fn(value);
103
+ };
104
+ const abortTurn = () =>
105
+ Promise.resolve()
106
+ .then(() => surface.abort?.(homeId))
107
+ .catch((error) => log(`aborting ${kind} "${name}" in ${homeId} failed: ${error?.message || error}`));
108
+ function onAbort() {
109
+ aborted = true;
110
+ if (started) void abortTurn();
111
+ finish(reject, abortError(signal));
112
+ }
113
+ signal?.addEventListener?.('abort', onAbort, { once: true });
114
+ const options = {
115
+ id: `routine_${randomUUID()}`,
116
+ mode: 'routine',
117
+ // Runs after the thread's current turn, never steered into it.
118
+ priority: 'later',
119
+ isMeta: true,
120
+ skipSlashCommands: true,
121
+ restorable: false,
122
+ // Esc ends this fire; it is never put back in the queue.
123
+ abortDiscardOnAbort: true,
124
+ skillSelection: String(skillSelection ?? body),
125
+ transcriptMeta: { routineTrigger: trigger },
126
+ onCommitted: () => {
127
+ if (started) return;
128
+ started = true;
129
+ // Nobody watches this turn: its approvals are held for the owner.
130
+ unmark = markUnattendedRun(homeId, { kind, agentId, name });
131
+ if (aborted) void abortTurn();
132
+ },
133
+ onSettled: (detail) => {
134
+ // Read before unmarking: a step only a person can do ended the turn.
135
+ const human = unattendedRunOf(homeId)?.humanNeeded;
136
+ unmark();
137
+ release();
138
+ if (human) finish(reject, new Error(routineHumanNeededText(human.outcome, human.site)));
139
+ else if (detail?.status === 'done') {
140
+ finish(resolve, { sessionId: homeId, result: String(detail?.result?.content || '') });
141
+ } else finish(reject, settledError(detail));
142
+ },
143
+ };
144
+ Promise.resolve()
145
+ .then(() => surface.submit(homeId, content, options))
146
+ .then((accepted) => {
147
+ if (accepted === true) return;
148
+ release();
149
+ finish(reject, new Error(`the bot's chat did not accept the ${kind} "${name}"`));
150
+ })
151
+ .catch((error) => {
152
+ release();
153
+ finish(reject, error);
154
+ });
155
+ });
156
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Routines are bot-based: a schedule or webhook runs in its owner bot's home
3
+ * thread (routine-home-turn.mjs) and nowhere else. An owner that does not
4
+ * exist — Mixy before the representative bot was created, a bot archived or
5
+ * deleted — makes the routine paused: it does not fire, the lists show why,
6
+ * and a new routine cannot name it.
7
+ */
8
+ import { MIXY_AGENT_ID } from '../runtime/agent/orchestrator/session/agent-home.mjs';
9
+ import { scheduleOwnerAgentId } from './agent-home-delivery.mjs';
10
+
11
+ /** `ownerProblem` codes and the error each one raises. */
12
+ export const ROUTINE_OWNER_PROBLEMS = Object.freeze({
13
+ 'no-mixy': 'Create the representative bot first.',
14
+ 'owner-deleted': 'The bot that owns this routine no longer exists. Pick another bot.',
15
+ });
16
+
17
+ let editor = null;
18
+ async function defaultProbe() {
19
+ if (!editor) {
20
+ const [{ getPluginData }, { createAgentEditorApi }] = await Promise.all([
21
+ import('../runtime/agent/orchestrator/config.mjs'),
22
+ import('./workflow-agents-api/agent-editor.mjs'),
23
+ ]);
24
+ editor = createAgentEditorApi({ cfgMod: { getPluginData } });
25
+ }
26
+ return {
27
+ // An undecided profile (null) counts as having Mixy: nobody is blocked by
28
+ // a flag the host has not written yet.
29
+ mixyExists: () => editor.getLeadStatus().created !== false,
30
+ presence: (agentId) => editor.agentDefinitionPresence([agentId])[agentId],
31
+ };
32
+ }
33
+
34
+ /** Why `ownerAgentId` (none = Mixy) cannot own or run a routine: a
35
+ * ROUTINE_OWNER_PROBLEMS code, '' when it can. Only a proven deletion
36
+ * counts for a bot; a read fault never pauses anything. */
37
+ export async function routineOwnerProblem(ownerAgentId, probe = null) {
38
+ const owner = scheduleOwnerAgentId({ ownerAgentId });
39
+ const check = probe || (await defaultProbe());
40
+ if (owner === MIXY_AGENT_ID) return check.mixyExists() ? '' : 'no-mixy';
41
+ return check.presence(owner) === 'deleted' ? 'owner-deleted' : '';
42
+ }
43
+
44
+ /** Rejects with the owner's problem; resolves when it can own a routine. */
45
+ export async function assertRoutineOwner(ownerAgentId, probe = null) {
46
+ const problem = await routineOwnerProblem(ownerAgentId, probe);
47
+ if (problem) throw new Error(ROUTINE_OWNER_PROBLEMS[problem]);
48
+ }
@@ -1,71 +1,37 @@
1
1
  /**
2
- * Run a schedule as a VISIBLE Mixdog session instead of a hidden agent
3
- * dispatch. Shared by the engine run-now capability and the channels-worker
4
- * scheduler so both paths leave the same trail: a resumable session with
5
- * owner 'user' + sourceType 'schedule' (admitted by the lead-session list
6
- * filter as its own type, so it shows in desktop Recent / TUI resume), the
7
- * schedule's provider/model route, and the schedule's project cwd.
2
+ * Run a schedule fire: its instructions become one turn in its owner bot's
3
+ * home thread (routine-home-turn.mjs), under the owned-routine guards
4
+ * (owned-automation-run.mjs). Shared by the engine's "Run now" and the
5
+ * channels-worker scheduler.
8
6
  */
9
- import { loadConfig } from '../runtime/agent/orchestrator/config.mjs';
10
- import { createSession } from '../runtime/agent/orchestrator/session/manager/session-lifecycle.mjs';
11
- import { askSession } from '../runtime/agent/orchestrator/session/manager/ask-session.mjs';
12
- import { modelRouteFields, parseScheduleModelRef } from '../runtime/shared/schedule-model-ref.mjs';
13
- import { migrateCodexRoute } from '../runtime/agent/orchestrator/providers/openai-oauth-catalog-store.mjs';
14
- import { automationWorkflowOpts } from './automation-workflow.mjs';
15
- import { runAutomationTurns } from './automation-agents.mjs';
16
- import { automationPromptContent } from '../runtime/shared/automation-attachments.mjs';
17
-
18
- /** Resolve schedule.model into a {provider,model,effort?,fast?} route. */
19
- function scheduleRouteFromModelRef(modelRef, config = null) {
20
- const ref = parseScheduleModelRef(modelRef);
21
- // A model the provider catalog superseded runs on its successor.
22
- if (ref && typeof ref === 'object') return migrateCodexRoute(ref);
23
- const cfg = config || loadConfig({ secrets: false });
24
- const preset = cfg.presets?.find((p) => p.id === ref || p.name === ref);
25
- if (!preset) {
26
- throw new Error(`schedule model "${ref}" is neither a provider/model route nor a known preset name`);
27
- }
28
- return modelRouteFields(preset);
29
- }
7
+ import { markScheduleSkipped } from '../runtime/channels/lib/schedules-db.mjs';
8
+ import { runOwnedAutomation } from './owned-automation-run.mjs';
9
+ import { runRoutineTurn } from './routine-home-turn.mjs';
30
10
 
31
11
  /**
32
- * Create the visible schedule session and run the prompt in it.
33
- * `prompt` overrides schedule.prompt when the caller (channels worker) has
34
- * already resolved/wrapped the prompt body; the override is used verbatim
35
- * while the fallback gets a small origin header.
12
+ * One fire of `schedule`. `prompt` overrides schedule.prompt when the caller
13
+ * (channels worker) already resolved the body; `attended` marks a person's
14
+ * "Run now" (no failure backoff).
36
15
  */
37
- export async function runScheduleSession(schedule, { config = null, prompt: promptOverride = null } = {}) {
38
- if (!schedule?.name) throw new Error('runScheduleSession: schedule row required');
39
- const prompt = String(promptOverride ?? schedule.prompt ?? '').trim();
40
- if (!prompt) throw new Error(`schedule "${schedule.name}" has no instructions`);
41
- if (!schedule.model) {
42
- throw new Error(`schedule "${schedule.name}" has no model configured — edit it and choose a model first`);
43
- }
44
- const route = scheduleRouteFromModelRef(schedule.model, config);
45
- const cwd = schedule.cwd ? String(schedule.cwd) : null;
46
- // Every fire is a fresh New task (user decision): one prompt, the
47
- // schedule's model/workflow/project, a brand-new session in the sidebar
48
- // Automations section (newest per name wins there).
49
- const session = createSession({
50
- ...modelRouteFields(route),
51
- owner: 'user',
52
- sourceType: 'schedule',
53
- sourceName: schedule.name,
54
- ...(cwd ? { cwd } : {}),
55
- desktopSession: cwd
56
- ? { classification: 'project', projectPath: cwd }
57
- : { classification: 'task', projectPath: null },
58
- ...automationWorkflowOpts(schedule.workflow),
59
- });
60
- // The user message is the schedule's instructions verbatim: the desktop
61
- // title/preview derive from it, so no "[Scheduled task: …]" header noise.
62
- // Stored attachments ride along as composer-style content parts.
63
- const content = automationPromptContent(prompt, schedule.attachments);
64
- // Agents the run delegates to report back; each result is the next turn.
65
- const result = await runAutomationTurns(
66
- session.id,
67
- () => askSession(session.id, content, null, null, cwd || undefined),
68
- (completions, askOpts) => askSession(session.id, completions, null, null, cwd || undefined, undefined, askOpts)
16
+ export async function runScheduleSession(schedule, { attended = false, prompt = null, signal = null } = {}) {
17
+ const body = String(prompt ?? schedule.prompt ?? '').trim();
18
+ return runOwnedSchedule(
19
+ schedule,
20
+ ({ signal: runSignal }) =>
21
+ runRoutineTurn({
22
+ kind: 'schedule',
23
+ name: schedule.name,
24
+ ownerAgentId: schedule.ownerAgentId,
25
+ prompt: body,
26
+ attachments: schedule.attachments,
27
+ cwd: schedule.cwd || null,
28
+ signal: runSignal,
29
+ }),
30
+ { attended, signal }
69
31
  );
70
- return { sessionId: session.id, result: String(result?.content || '') };
32
+ }
33
+
34
+ /** One owned run of `schedule` (owned-automation-run.mjs). */
35
+ export function runOwnedSchedule(schedule, run, { recordSkip = markScheduleSkipped, ...deps } = {}) {
36
+ return runOwnedAutomation('schedule', schedule, run, { recordSkip, ...deps });
71
37
  }
@@ -8,6 +8,7 @@ import {
8
8
  import { agentScope, clean, clearAgentStatuslineRoute } from './helpers.mjs';
9
9
  import { isLeadPoolAgent } from './worker-rows.mjs';
10
10
  import { createCloseAll } from './close-flow/close-all.mjs';
11
+ import { closeManagedSession } from '../../lifecycle/shared.mjs';
11
12
 
12
13
  const CLOSE_REASON = 'cancelled by agent close';
13
14
 
@@ -68,7 +69,7 @@ export function createCloseFlow({ mgr, registry, views }) {
68
69
  // below is an idempotent no-op fallback once terminal.
69
70
  // Closing a runtime cancels its work; only explicit session deletion
70
71
  // may tombstone the conversation.
71
- const ok = await mgr.closeSession(sessionId, 'cli-agent-close', { tombstone: false });
72
+ const ok = await closeManagedSession(mgr, sessionId, 'cli-agent-close', { tombstone: false });
72
73
  if (task?.taskId) cancelBackgroundTask(task.taskId, CLOSE_REASON);
73
74
  // Drop the row only once the cancellation is DURABLY recorded: a failed
74
75
  // close that still forgot the row would leave no cancelled state behind.
@@ -352,6 +352,16 @@ export function readAgentFrontmatterPermission(agent, dataDir, standaloneSourceR
352
352
  );
353
353
  }
354
354
 
355
+ const _frontmatterKindCache = new Map(); // key -> { value, atMs }
356
+
357
+ /** The agent definition's frontmatter `kind` (isContactAgentId reads it), or
358
+ * null when it sets none. */
359
+ export function readAgentFrontmatterKind(agent, dataDir, standaloneSourceRoot) {
360
+ return cachedFrontmatterValue(_frontmatterKindCache, agent, dataDir, standaloneSourceRoot, (fm) =>
361
+ typeof fm.kind === 'string' && fm.kind.trim() ? fm.kind.trim() : null
362
+ );
363
+ }
364
+
355
365
  /** The agent definition's `firstResponseTimeoutMs` (a non-negative integer;
356
366
  * 0 disables the first-response deadline), or null when it sets none. */
357
367
  export function readAgentFirstResponseTimeoutMs(agent, dataDir, standaloneSourceRoot) {
@@ -76,13 +76,21 @@ export function createAgentList({ mgr, refreshTagsFromSessions, agentSessionEntr
76
76
  return function list({ scanSessions = false, context = {}, includeTerminal = false } = {}) {
77
77
  refreshTagsFromSessions({ scanSessions, context });
78
78
  const now = Date.now();
79
- const rows = agentSessionEntries({ scanSessions, context }).map(({ tag, session, indexRow }) =>
79
+ const rows = agentSessionEntries({ scanSessions, context }).map(({ tag, session, indexRow, loaded }) =>
80
80
  agentRow({
81
81
  tag,
82
82
  session,
83
83
  indexRow,
84
84
  runtime: mgr.getSessionRuntime?.(session.id),
85
- progress: progress.sessionProgressExtras(session.id, session.agent || null, now),
85
+ // A tag-bound entry is the getSession answer itself; an index row or
86
+ // a scanned summary is only a projection and still reads the session.
87
+ progress: progress.sessionProgressExtras(
88
+ session.id,
89
+ session.agent || null,
90
+ now,
91
+ null,
92
+ loaded ? { loaded: session } : {}
93
+ ),
86
94
  now,
87
95
  })
88
96
  );
@@ -58,9 +58,16 @@ export function liveAgentStatusRow(row) {
58
58
  }
59
59
 
60
60
  export function createSessionProgress({ mgr }) {
61
- function sessionProgressExtras(sessionId, role, now = Date.now(), taskStatus = null) {
61
+ /** `loaded`: the caller's own getSession(sessionId) answer from this same
62
+ * read, so the session is not loaded a second time. */
63
+ function sessionProgressExtras(sessionId, role, now = Date.now(), taskStatus = null, { loaded } = {}) {
62
64
  if (!sessionId) return {};
63
- const session = mgr.getSession(sessionId);
65
+ // Only the session's status is read here: the manager's stamp-validated
66
+ // status lookup spares a whole-transcript read per idle worker per pulse.
67
+ let sessionStatus;
68
+ if (loaded !== undefined) sessionStatus = loaded?.status || null;
69
+ else if (typeof mgr.loadSessionStatus === 'function') sessionStatus = mgr.loadSessionStatus(sessionId) || null;
70
+ else sessionStatus = mgr.getSession(sessionId)?.status || null;
64
71
  const runtime = mgr.getSessionRuntime?.(sessionId) || null;
65
72
  const snapshot =
66
73
  typeof mgr.getSessionProgressSnapshot === 'function' ? mgr.getSessionProgressSnapshot(sessionId) : null;
@@ -72,8 +79,8 @@ export function createSessionProgress({ mgr }) {
72
79
  typeof mgr.getSessionPendingMessageDepth === 'function' ? mgr.getSessionPendingMessageDepth(sessionId) : null;
73
80
  return buildAgentTaskProgressFields({
74
81
  now,
75
- sessionStatus: session?.status || null,
76
- runtimeStage: runtime?.stage || snapshot?.stage || session?.status || null,
82
+ sessionStatus,
83
+ runtimeStage: runtime?.stage || snapshot?.stage || sessionStatus,
77
84
  snapshot,
78
85
  runtime,
79
86
  policy,
@@ -96,6 +103,8 @@ export function createSessionProgress({ mgr }) {
96
103
  stage: progress.worker_stage || runtime?.stage || status,
97
104
  // Additive: present only while the worker's turn waits on a user approval.
98
105
  ...(runtime?.awaitingUser ? { awaitingUser: runtime.awaitingUser } : {}),
106
+ // Additive: present only while the worker's turn sits in `task wait`.
107
+ ...(runtime?.taskWaits > 0 ? { awaitingTask: 'wait' } : {}),
99
108
  clientHostPid: session.clientHostPid || null,
100
109
  lastStreamDeltaAt: runtime?.lastStreamDeltaAt ? new Date(runtime.lastStreamDeltaAt).toISOString() : null,
101
110
  ...progress,
@@ -9,7 +9,7 @@ import {
9
9
  import { clean } from './helpers.mjs';
10
10
  import { createEarlyCompletionNotice } from './notify/early-completion.mjs';
11
11
 
12
- export function createNotify(mgr, { notifySessionCompletion } = {}) {
12
+ export function createNotify(mgr, { notifySessionCompletion, onWorkerCompletionQueued = null } = {}) {
13
13
  function enqueueCompletionMessage(sessionId, text, meta = {}) {
14
14
  const target = clean(sessionId);
15
15
  if (!target || typeof mgr.enqueuePendingMessage !== 'function') return false;
@@ -28,8 +28,8 @@ export function createNotify(mgr, { notifySessionCompletion } = {}) {
28
28
  logDuplicateSkip('notify-enqueue', { executionId: meta?.execution_id, text: visible });
29
29
  return true;
30
30
  }
31
- // Mark this as a deferred completion/task notification so a later session
32
- // resume drops it rather than replaying it out-of-order (owner decision).
31
+ // Mark this as a completion/task notification: delivery dedupes it by
32
+ // execution id, and it stays queued (durably) until the model gets it.
33
33
  return Boolean(
34
34
  mgr.enqueuePendingMessage(
35
35
  target,
@@ -67,7 +67,18 @@ export function createNotify(mgr, { notifySessionCompletion } = {}) {
67
67
  // appear as a second Lead-level completion card. Agent task completion
68
68
  // has its own owner delivery path below; only that terminal handoff
69
69
  // crosses the parent boundary.
70
- return workerId ? enqueueCompletionMessage(workerId, text, meta) : false;
70
+ if (!workerId) return false;
71
+ const queued = enqueueCompletionMessage(workerId, text, meta);
72
+ // A busy worker takes the completion at its next tool boundary; an idle
73
+ // one is woken so the result reaches it now, not at its next message.
74
+ if (queued && typeof onWorkerCompletionQueued === 'function') {
75
+ setImmediate(() => {
76
+ try {
77
+ onWorkerCompletionQueued(workerId);
78
+ } catch {}
79
+ });
80
+ }
81
+ return queued;
71
82
  };
72
83
  }
73
84
 
@@ -0,0 +1,134 @@
1
+ // agent-tool/restart-notify.mjs
2
+ // Agent tasks live in process memory, so a daemon restart ends every in-flight
3
+ // one without a word: the successor only sees worker rows left `failed`
4
+ // (process-restart). This tells each task's owner, exactly once.
5
+ //
6
+ // Order is what makes it safe: the failure notification is enqueued in the
7
+ // owner's persisted pending queue under a stable id (`restart:<taskId>`) and
8
+ // flushed to the spool FIRST; only then is the row stamped `restartNotified`.
9
+ // A crash or a throw before the stamp leaves the row retryable, and the retry
10
+ // re-enqueues the same id — the queue is id-idempotent, so the owner still
11
+ // sees it once.
12
+ import {
13
+ acknowledgeBackgroundTaskCompletion,
14
+ completeBackgroundTask,
15
+ getBackgroundTask,
16
+ registerBackgroundTask,
17
+ } from '../../../runtime/shared/background-tasks.mjs';
18
+ import { renderAgentCompletionEnvelope } from '../../../runtime/shared/task-notification-envelope.mjs';
19
+ import { modelVisibleToolCompletionMessage } from '../../../runtime/shared/tool-execution-contract.mjs';
20
+ import { clean } from './helpers.mjs';
21
+
22
+ const RESTART_FAILURE_ERROR = 'process restart: the daemon restarted while this agent task was running';
23
+ const SETTLE_TIMEOUT_MS = 5000;
24
+
25
+ /** Stable dedup id of one lost task's notification. A row that never recorded
26
+ * a task id falls back to its session and settle time. */
27
+ export function restartNotificationId(row) {
28
+ return `restart:${clean(row.task_id) || `${clean(row.sessionId)}:${clean(row.finishedAt)}`}`;
29
+ }
30
+
31
+ /** `pendingRestartFailures()` lists rows whose owner is not yet told;
32
+ * `markRestartNotified(rows)` stamps them. `mgr` supplies the persisted
33
+ * pending queue (enqueuePendingMessage / markCompletionEntry /
34
+ * settlePendingMessageWrites). `notifySessionCompletion` is only the live,
35
+ * best-effort emit to attached surfaces after the durable enqueue. */
36
+ export function createRestartFailureNotifier({
37
+ pendingRestartFailures,
38
+ markRestartNotified,
39
+ mgr,
40
+ notifySessionCompletion,
41
+ }) {
42
+ function enqueueDurably(row) {
43
+ const owner = clean(row.ownerSessionId);
44
+ const executionId = restartNotificationId(row);
45
+ const meta = {
46
+ type: 'agent_task_result',
47
+ execution_surface: 'agent',
48
+ execution_id: executionId,
49
+ status: 'failed',
50
+ caller_session_id: owner,
51
+ };
52
+ const text = renderAgentCompletionEnvelope({
53
+ id: clean(row.task_id) || executionId,
54
+ tag: row.tag,
55
+ status: 'failed',
56
+ error: RESTART_FAILURE_ERROR,
57
+ });
58
+ const entry = mgr.markCompletionEntry(modelVisibleToolCompletionMessage(text, meta), { executionId, meta });
59
+ return { owner, text, meta, entry };
60
+ }
61
+
62
+ // Lets `agent status <id>` resolve after the restart; best-effort.
63
+ function registerFailedTask(row) {
64
+ const taskId = clean(row.task_id);
65
+ if (!taskId || getBackgroundTask(taskId)) return;
66
+ const owner = clean(row.ownerSessionId);
67
+ registerBackgroundTask({
68
+ taskId,
69
+ surface: 'agent',
70
+ operation: 'restart',
71
+ label: row.tag,
72
+ input: { type: 'restart', tag: row.tag, sessionId: row.sessionId, agent: row.agent || null },
73
+ context: { callerSessionId: owner, ownerSessionId: owner, clientHostPid: row.clientHostPid || null },
74
+ meta: {
75
+ tag: row.tag,
76
+ sessionId: row.sessionId,
77
+ agent: row.agent || null,
78
+ provider: row.provider || null,
79
+ model: row.model || null,
80
+ preset: row.preset || null,
81
+ effort: row.effort || null,
82
+ },
83
+ resultType: 'agent_task_result',
84
+ });
85
+ completeBackgroundTask(taskId, {
86
+ status: 'failed',
87
+ error: RESTART_FAILURE_ERROR,
88
+ terminalReason: 'process-restart',
89
+ notify: false,
90
+ });
91
+ acknowledgeBackgroundTaskCompletion(taskId);
92
+ }
93
+
94
+ async function notifyRow(row) {
95
+ const { owner, text, meta, entry } = enqueueDurably(row);
96
+ if (!(mgr.enqueuePendingMessage(owner, entry) > 0)) throw new Error(`restart notification refused for ${owner}`);
97
+ if (!(await mgr.settlePendingMessageWrites({ timeoutMs: SETTLE_TIMEOUT_MS }))) {
98
+ throw new Error(`restart notification for ${owner} did not reach the spool`);
99
+ }
100
+ markRestartNotified([row]);
101
+ try {
102
+ registerFailedTask(row);
103
+ } catch {
104
+ /* status lookup only */
105
+ }
106
+ try {
107
+ // Same execution id as the queued entry: the queue dedups the mirror.
108
+ notifySessionCompletion?.(owner, text, meta);
109
+ } catch {
110
+ /* the durable queue is authoritative */
111
+ }
112
+ }
113
+
114
+ let inFlight = null;
115
+ async function run() {
116
+ for (const row of pendingRestartFailures()) {
117
+ try {
118
+ await notifyRow(row);
119
+ } catch (err) {
120
+ // Not stamped: the next pass or restart retries this row.
121
+ process.stderr.write(`[agent-tool] restart notification retry pending: ${err?.message || err}\n`);
122
+ }
123
+ }
124
+ }
125
+
126
+ return function notifyRestartFailures() {
127
+ inFlight ||= run()
128
+ .catch((err) => process.stderr.write(`[agent-tool] restart notification pass failed: ${err?.message || err}\n`))
129
+ .finally(() => {
130
+ inFlight = null;
131
+ });
132
+ return inFlight;
133
+ };
134
+ }