mixdog 1.4.2 → 1.4.3

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 (417) hide show
  1. package/package.json +2 -2
  2. package/scripts/build-shipped-history.mjs +15 -5
  3. package/scripts/gate-local.mjs +65 -19
  4. package/scripts/graph-parity.mjs +8 -3
  5. package/scripts/lib/gate-scheduler.mjs +2 -1
  6. package/scripts/lib/run-node-tests.mjs +17 -5
  7. package/scripts/lib/test-failure-summary.mjs +1 -8
  8. package/scripts/lib/test-spawn-ledger.mjs +33 -2
  9. package/scripts/post-release-check.mjs +26 -16
  10. package/scripts/provider-pingpong-live.mjs +1 -1
  11. package/scripts/prune-embedding-runtime.mjs +3 -1
  12. package/scripts/release-paths.mjs +7 -1
  13. package/src/defaults/shipped-history.json +20 -0
  14. package/src/defaults/skills/setup/SKILL.md +2 -2
  15. package/src/defaults/skills/setup/references/actions.md +16 -3
  16. package/src/defaults/skills/setup/references/surfaces.md +3 -0
  17. package/src/defaults/skills/setup-guide/SKILL.md +6 -1
  18. package/src/lib/instruction-text.cjs +36 -0
  19. package/src/lib/rules-builder.cjs +34 -20
  20. package/src/orchestration/balanced.md +23 -0
  21. package/src/orchestration/focused.md +22 -0
  22. package/src/orchestration/none.md +5 -0
  23. package/src/orchestration/swarm.md +24 -0
  24. package/src/rules/agent-mode/AGENT_MODE.md +3 -1
  25. package/src/rules/agent-mode/MIXY.md +2 -2
  26. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch/session-prep.mjs +3 -3
  27. package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
  28. package/src/runtime/agent/orchestrator/agent-runtime/agent-progress-watchdog.mjs +4 -2
  29. package/src/runtime/agent/orchestrator/agent-runtime/cache-strategy.mjs +0 -9
  30. package/src/runtime/agent/orchestrator/agent-runtime/heartbeat-completion.mjs +2 -2
  31. package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +67 -0
  32. package/src/runtime/agent/orchestrator/agent-trace-format.mjs +1 -0
  33. package/src/runtime/agent/orchestrator/context/role-instructions.mjs +4 -4
  34. package/src/runtime/agent/orchestrator/mcp/agent-connectors.mjs +3 -1
  35. package/src/runtime/agent/orchestrator/mcp/child-tree.mjs +199 -46
  36. package/src/runtime/agent/orchestrator/mcp/client-config.mjs +3 -5
  37. package/src/runtime/agent/orchestrator/mcp/client-transport.mjs +9 -3
  38. package/src/runtime/agent/orchestrator/mcp/client.mjs +3 -5
  39. package/src/runtime/agent/orchestrator/mcp/oauth-client.mjs +1 -1
  40. package/src/runtime/agent/orchestrator/mcp/oauth-credentials.mjs +8 -2
  41. package/src/runtime/agent/orchestrator/mcp/oauth-probe.mjs +1 -1
  42. package/src/runtime/agent/orchestrator/mcp/oauth-provider.mjs +14 -0
  43. package/src/runtime/agent/orchestrator/mcp/oauth.mjs +1 -1
  44. package/src/runtime/agent/orchestrator/providers/anthropic-oauth-client-version.mjs +1 -1
  45. package/src/runtime/agent/orchestrator/providers/anthropic-sse-turn/turn-events/content-block-stop.mjs +3 -3
  46. package/src/runtime/agent/orchestrator/providers/codex-client-meta.mjs +1 -1
  47. package/src/runtime/agent/orchestrator/providers/gemini-thinking.mjs +2 -2
  48. package/src/runtime/agent/orchestrator/providers/openai-compat-response-normalization.mjs +4 -6
  49. package/src/runtime/agent/orchestrator/providers/openai-compat-responses-head-parity.json +2784 -2872
  50. package/src/runtime/agent/orchestrator/providers/openai-compat-xai.mjs +5 -8
  51. package/src/runtime/agent/orchestrator/providers/openai-responses-payload-parity.fixture.json +288 -300
  52. package/src/runtime/agent/orchestrator/providers/vertex-ai.mjs +10 -1
  53. package/src/runtime/agent/orchestrator/providers/xai-tool-search-head-parity.json +10 -30
  54. package/src/runtime/agent/orchestrator/runtime-core/builtin-features.mjs +30 -0
  55. package/src/runtime/agent/orchestrator/runtime-core/config-helpers.mjs +0 -11
  56. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-availability.mjs +8 -0
  57. package/src/runtime/agent/orchestrator/runtime-core/deferred-tool-delta.mjs +17 -3
  58. package/src/runtime/agent/orchestrator/runtime-core/goal-control.mjs +47 -27
  59. package/src/runtime/agent/orchestrator/runtime-core/goal-deadlines.mjs +141 -10
  60. package/src/runtime/agent/orchestrator/runtime-core/goal-mutations.mjs +26 -63
  61. package/src/runtime/agent/orchestrator/runtime-core/goal-reminder.mjs +28 -3
  62. package/src/runtime/agent/orchestrator/runtime-core/goal-runtime.mjs +36 -11
  63. package/src/runtime/agent/orchestrator/runtime-core/goal-state.mjs +88 -56
  64. package/src/runtime/agent/orchestrator/runtime-core/goal-storage.mjs +4 -1
  65. package/src/runtime/agent/orchestrator/runtime-core/goal-store.mjs +51 -32
  66. package/src/runtime/agent/orchestrator/runtime-core/goal-text.mjs +45 -145
  67. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-defs.mjs +15 -98
  68. package/src/runtime/agent/orchestrator/runtime-core/goal-tool-exec.mjs +32 -99
  69. package/src/runtime/agent/orchestrator/runtime-core/goal-turns.mjs +23 -78
  70. package/src/runtime/agent/orchestrator/runtime-core/orchestration.mjs +69 -48
  71. package/src/runtime/agent/orchestrator/runtime-core/todo-list.mjs +367 -0
  72. package/src/runtime/agent/orchestrator/runtime-core/todo-tool-defs.mjs +47 -0
  73. package/src/runtime/agent/orchestrator/runtime-core/tool-availability.mjs +155 -0
  74. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog-data.mjs +9 -0
  75. package/src/runtime/agent/orchestrator/runtime-core/tool-catalog.mjs +119 -35
  76. package/src/runtime/agent/orchestrator/runtime-core/tool-defs.mjs +1 -1
  77. package/src/runtime/agent/orchestrator/runtime-core/workflow/context.mjs +1 -1
  78. package/src/runtime/agent/orchestrator/session/agent-loop.mjs +28 -22
  79. package/src/runtime/agent/orchestrator/session/agent-pool-progress.mjs +30 -0
  80. package/src/runtime/agent/orchestrator/session/attachment-preflight.mjs +1 -1
  81. package/src/runtime/agent/orchestrator/session/cache-warm.mjs +2 -2
  82. package/src/runtime/agent/orchestrator/session/catalog-db/index.mjs +2 -2
  83. package/src/runtime/agent/orchestrator/session/catalog-db/schema.mjs +1 -1
  84. package/src/runtime/agent/orchestrator/session/compact/execution-tail.mjs +1 -1
  85. package/src/runtime/agent/orchestrator/session/compact/runner.mjs +3 -0
  86. package/src/runtime/agent/orchestrator/session/compact/summary-route.mjs +1 -1
  87. package/src/runtime/agent/orchestrator/session/context-utils.mjs +8 -12
  88. package/src/runtime/agent/orchestrator/session/conversation-import/claude.mjs +1 -1
  89. package/src/runtime/agent/orchestrator/session/conversation-import/codex.mjs +1 -1
  90. package/src/runtime/agent/orchestrator/session/conversation-import/convert.mjs +1 -1
  91. package/src/runtime/agent/orchestrator/session/conversation-import/imported-session.mjs +7 -3
  92. package/src/runtime/agent/orchestrator/session/human-gate/held-message.mjs +1 -1
  93. package/src/runtime/agent/orchestrator/session/human-gate/requests.mjs +1 -2
  94. package/src/runtime/agent/orchestrator/session/human-gate/run-policy.mjs +1 -1
  95. package/src/runtime/agent/orchestrator/session/image-strip-recovery.mjs +1 -1
  96. package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +11 -1
  97. package/src/runtime/agent/orchestrator/session/loop/context-overflow.mjs +1 -1
  98. package/src/runtime/agent/orchestrator/session/loop/loop-state.mjs +8 -1
  99. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn/todo-check.mjs +196 -0
  100. package/src/runtime/agent/orchestrator/session/loop/no-tool-turn.mjs +116 -50
  101. package/src/runtime/agent/orchestrator/session/loop/pre-dispatch-deny.mjs +8 -1
  102. package/src/runtime/agent/orchestrator/session/loop/provider-tool-exclusions.mjs +1 -1
  103. package/src/runtime/agent/orchestrator/session/loop/route-fallback.mjs +1 -1
  104. package/src/runtime/agent/orchestrator/session/loop/steering.mjs +3 -1
  105. package/src/runtime/agent/orchestrator/session/loop/stored-tool-args.mjs +5 -6
  106. package/src/runtime/agent/orchestrator/session/loop/tool-exec/before-hook.mjs +6 -1
  107. package/src/runtime/agent/orchestrator/session/loop/tool-exec/dispatch.mjs +16 -2
  108. package/src/runtime/agent/orchestrator/session/loop/turn-report.mjs +35 -0
  109. package/src/runtime/agent/orchestrator/session/loop/usage.mjs +1 -1
  110. package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +57 -26
  111. package/src/runtime/agent/orchestrator/session/manager/ask-turn-auto-effort.mjs +9 -7
  112. package/src/runtime/agent/orchestrator/session/manager/ask-turn-execute.mjs +21 -3
  113. package/src/runtime/agent/orchestrator/session/manager/ask-turn-failure.mjs +27 -11
  114. package/src/runtime/agent/orchestrator/session/manager/ask-turn-open.mjs +37 -34
  115. package/src/runtime/agent/orchestrator/session/manager/ask-turn-prepare.mjs +15 -0
  116. package/src/runtime/agent/orchestrator/session/manager/ask-turn-settle.mjs +25 -15
  117. package/src/runtime/agent/orchestrator/session/manager/ask-turn-state.mjs +79 -24
  118. package/src/runtime/agent/orchestrator/session/manager/ask-turn-steering-drain.mjs +3 -0
  119. package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +12 -1
  120. package/src/runtime/agent/orchestrator/session/manager/context-meta.mjs +1 -1
  121. package/src/runtime/agent/orchestrator/session/manager/idle-cleanup.mjs +6 -1
  122. package/src/runtime/agent/orchestrator/session/manager/pending-messages.mjs +9 -1
  123. package/src/runtime/agent/orchestrator/session/manager/pending-spool-path.mjs +2 -2
  124. package/src/runtime/agent/orchestrator/session/manager/pending-spool-shard.mjs +1 -1
  125. package/src/runtime/agent/orchestrator/session/manager/prompt-settings.mjs +598 -0
  126. package/src/runtime/agent/orchestrator/session/manager/prompt-utils.mjs +19 -0
  127. package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +19 -10
  128. package/src/runtime/agent/orchestrator/session/manager/session-close.mjs +5 -0
  129. package/src/runtime/agent/orchestrator/session/manager/session-fallback-title.mjs +1 -1
  130. package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +31 -0
  131. package/src/runtime/agent/orchestrator/session/manager/session-lock.mjs +31 -0
  132. package/src/runtime/agent/orchestrator/session/manager/session-prompt-composition.mjs +42 -6
  133. package/src/runtime/agent/orchestrator/session/manager/session-record.mjs +2 -0
  134. package/src/runtime/agent/orchestrator/session/manager/tool-resolution.mjs +11 -1
  135. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-context.mjs +8 -9
  136. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-journal.mjs +339 -88
  137. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint-recovery.mjs +135 -11
  138. package/src/runtime/agent/orchestrator/session/manager/turn-checkpoint.mjs +377 -104
  139. package/src/runtime/agent/orchestrator/session/manager/turn-durability.mjs +144 -0
  140. package/src/runtime/agent/orchestrator/session/manager/turn-interruption.mjs +1 -1
  141. package/src/runtime/agent/orchestrator/session/manager/usage-metrics.mjs +4 -0
  142. package/src/runtime/agent/orchestrator/session/manager.mjs +6 -0
  143. package/src/runtime/agent/orchestrator/session/mixy-name.mjs +1 -1
  144. package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +8 -1
  145. package/src/runtime/agent/orchestrator/session/save-session-worker.mjs +5 -1
  146. package/src/runtime/agent/orchestrator/session/store/canonical-reader.mjs +1 -1
  147. package/src/runtime/agent/orchestrator/session/store/liveness-veto.mjs +2 -2
  148. package/src/runtime/agent/orchestrator/session/store/save-worker.mjs +23 -2
  149. package/src/runtime/agent/orchestrator/session/store/sweep/sub-agent-retention.mjs +3 -3
  150. package/src/runtime/agent/orchestrator/session/store/transcript-durability.mjs +88 -0
  151. package/src/runtime/agent/orchestrator/session/store-agent-worker-rows.mjs +5 -0
  152. package/src/runtime/agent/orchestrator/session/store-summary-log.mjs +1 -1
  153. package/src/runtime/agent/orchestrator/session/store-transcript-projection.mjs +14 -5
  154. package/src/runtime/agent/orchestrator/session/store-transcript-worker.mjs +2 -2
  155. package/src/runtime/agent/orchestrator/session/store.mjs +5 -0
  156. package/src/runtime/agent/orchestrator/session/synthetic-user-envelope.mjs +7 -1
  157. package/src/runtime/agent/orchestrator/session/tail-reminder-rules.mjs +9 -1
  158. package/src/runtime/agent/orchestrator/session/transcript-restore/agent-envelope.mjs +1 -1
  159. package/src/runtime/agent/orchestrator/session/transcript-restore/restore.mjs +3 -1
  160. package/src/runtime/agent/orchestrator/session/transcript-restore/user-text.mjs +2 -2
  161. package/src/runtime/agent/orchestrator/stall-policy.mjs +3 -5
  162. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/backgrounded-result.mjs +4 -1
  163. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/misuse-guard.mjs +21 -3
  164. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/result-format.mjs +1 -1
  165. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool/worktree-add-guard.mjs +77 -0
  166. package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +0 -1
  167. package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +7 -3
  168. package/src/runtime/agent/orchestrator/tools/builtin/git-worktree-tool.mjs +27 -9
  169. package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +2 -2
  170. package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +1 -1
  171. package/src/runtime/agent/orchestrator/tools/builtin/read-single-fast-paths.mjs +0 -1
  172. package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -10
  173. package/src/runtime/agent/orchestrator/tools/builtin/shell-jobs.mjs +1 -2
  174. package/src/runtime/agent/orchestrator/tools/builtin.mjs +1 -24
  175. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary/child-kill.mjs +4 -3
  176. package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +1 -2
  177. package/src/runtime/agent/orchestrator/tools/code-graph/modes/calls.mjs +1 -1
  178. package/src/runtime/agent/orchestrator/tools/code-graph/modes/symbols.mjs +2 -2
  179. package/src/runtime/agent/orchestrator/tools/code-graph/search.mjs +4 -5
  180. package/src/runtime/agent/orchestrator/tools/code-graph/symbol-index.mjs +3 -4
  181. package/src/runtime/agent/orchestrator/tools/code-graph.mjs +4 -22
  182. package/src/runtime/agent/orchestrator/tools/lib/shell-descendants.mjs +2 -2
  183. package/src/runtime/agent/orchestrator/tools/lib/shell-run-state.mjs +1 -1
  184. package/src/runtime/agent/orchestrator/tools/shell-command.mjs +0 -4
  185. package/src/runtime/agent/orchestrator/tools/shell-policy.mjs +0 -1
  186. package/src/runtime/attachments/attachment-reference.mjs +1 -1
  187. package/src/runtime/attachments/pdf-extract.mjs +1 -1
  188. package/src/runtime/attachments/store.d.mts +1 -0
  189. package/src/runtime/attachments/store.mjs +2 -2
  190. package/src/runtime/channels/lib/config-reload-watch.mjs +42 -0
  191. package/src/runtime/channels/lib/owned-runtime/config-reload.mjs +29 -9
  192. package/src/runtime/channels/lib/scheduler.mjs +33 -0
  193. package/src/runtime/channels/lib/schedules-db.mjs +11 -4
  194. package/src/runtime/channels/lib/webhook/session-dispatch.mjs +4 -1
  195. package/src/runtime/channels/lib/webhooks-db.mjs +9 -3
  196. package/src/runtime/channels/lib/whisper-server.mjs +12 -12
  197. package/src/runtime/channels/lib/worker-main.mjs +9 -31
  198. package/src/runtime/computer-bridge/linux-readiness.mjs +10 -14
  199. package/src/runtime/connectors/connector-accounts-mcp.mjs +169 -33
  200. package/src/runtime/connectors/gmail-app-password.mjs +2 -2
  201. package/src/runtime/connectors/google-mcp.mjs +1 -1
  202. package/src/runtime/effort-judge/judge-client.mjs +2 -2
  203. package/src/runtime/effort-judge/judge-input.mjs +4 -4
  204. package/src/runtime/explore/explorer.mjs +0 -5
  205. package/src/runtime/explore/tool.mjs +3 -3
  206. package/src/runtime/local-provider/hardware.mjs +1 -1
  207. package/src/runtime/media/adapters/codex-image.mjs +3 -3
  208. package/src/runtime/media/adapters/gemini-video.mjs +1 -1
  209. package/src/runtime/media/media-usage.mjs +1 -1
  210. package/src/runtime/media/store.mjs +1 -1
  211. package/src/runtime/memory/index.mjs +11 -1
  212. package/src/runtime/memory/lib/embedding-reindex.mjs +27 -0
  213. package/src/runtime/memory/lib/memory-embed.mjs +83 -14
  214. package/src/runtime/memory/lib/memory-process-lock.mjs +159 -36
  215. package/src/runtime/memory/lib/memory-text-utils.mjs +6 -1
  216. package/src/runtime/memory/lib/memory.mjs +17 -0
  217. package/src/runtime/memory/lib/pg/process.mjs +1 -1
  218. package/src/runtime/memory/lib/pg/supervisor.mjs +16 -96
  219. package/src/runtime/memory/lib/recall-hybrid/hybrid-sql.mjs +12 -3
  220. package/src/runtime/office/authoring/docx-html-runner.mjs +1 -1
  221. package/src/runtime/office/authoring/pptx-author-action.mjs +1 -1
  222. package/src/runtime/office/authoring/xlsx-html-build.mjs +1 -1
  223. package/src/runtime/office/authoring/xlsx-html-runner.mjs +1 -1
  224. package/src/runtime/office/capabilities.mjs +1 -1
  225. package/src/runtime/office/core/office-core.mjs +1 -1
  226. package/src/runtime/office/design/content-model.mjs +3 -3
  227. package/src/runtime/office/design/docx/design-docx/section.mjs +6 -6
  228. package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +1 -1
  229. package/src/runtime/office/design/xlsx/design-xlsx.mjs +1 -1
  230. package/src/runtime/office/pdf/ocr-engine.mjs +1 -1
  231. package/src/runtime/office/pdf/pdf-writer.mjs +4 -4
  232. package/src/runtime/office/portable/portable-docx-operations.mjs +2 -2
  233. package/src/runtime/office/portable/portable-soffice.mjs +8 -6
  234. package/src/runtime/office/portable/xlsx-contract.mjs +1 -1
  235. package/src/runtime/shared/atomic-file.d.mts +8 -0
  236. package/src/runtime/shared/background-tasks.mjs +22 -4
  237. package/src/runtime/shared/background-wait.mjs +66 -0
  238. package/src/runtime/shared/child-guardian.mjs +17 -7
  239. package/src/runtime/shared/config.mjs +313 -16
  240. package/src/runtime/shared/connector-catalog.d.mts +4 -5
  241. package/src/runtime/shared/connector-catalog.mjs +11 -1
  242. package/src/runtime/shared/file-lock.mjs +8 -3
  243. package/src/runtime/shared/git-runner.mjs +7 -2
  244. package/src/runtime/shared/idle-gc.mjs +30 -7
  245. package/src/runtime/shared/llm/http-agent.mjs +8 -5
  246. package/src/runtime/shared/llm/system-proxy.mjs +2 -1
  247. package/src/runtime/shared/llm/usage-ledger-quota.mjs +31 -12
  248. package/src/runtime/shared/llm/usage-ledger-rollup.mjs +84 -8
  249. package/src/runtime/shared/llm/usage-ledger.mjs +3 -0
  250. package/src/runtime/shared/llm/usage-rollup.mjs +48 -0
  251. package/src/runtime/shared/maintenance-schedule.mjs +109 -0
  252. package/src/runtime/shared/process-runner.mjs +8 -4
  253. package/src/runtime/shared/routine-trigger.d.mts +13 -0
  254. package/src/runtime/shared/routine-trigger.mjs +34 -0
  255. package/src/runtime/shared/run-child.d.mts +2 -1
  256. package/src/runtime/shared/run-child.mjs +70 -18
  257. package/src/runtime/shared/session-scratch.mjs +14 -2
  258. package/src/runtime/shared/shipped-definitions.mjs +7 -1
  259. package/src/runtime/shared/tool-card-model.golden.json +3 -12
  260. package/src/runtime/shared/tool-execution-contract.mjs +2 -0
  261. package/src/runtime/shared/tool-work-units.mjs +31 -6
  262. package/src/runtime/shared/turn-snapshot.mjs +26 -4
  263. package/src/runtime/shared/turn-worktree-snapshot.mjs +6 -11
  264. package/src/runtime/shared/worker-requests.mjs +17 -3
  265. package/src/runtime/shared/worktrees/branch-management.mjs +1 -5
  266. package/src/runtime/shared/worktrees/contract.mjs +16 -6
  267. package/src/runtime/shared/worktrees/dependencies.mjs +127 -89
  268. package/src/runtime/shared/worktrees/disk.mjs +79 -2
  269. package/src/runtime/shared/worktrees/git.mjs +44 -11
  270. package/src/runtime/shared/worktrees/include.mjs +6 -4
  271. package/src/runtime/shared/worktrees/maintenance.mjs +422 -30
  272. package/src/runtime/shared/worktrees/manager.mjs +772 -75
  273. package/src/runtime/shared/worktrees/settings.mjs +33 -7
  274. package/src/runtime/shared/worktrees/test-support.mjs +5 -1
  275. package/src/runtime/shared/worktrees/worker-snapshots.mjs +82 -0
  276. package/src/runtime/tidy/process.mjs +2 -1
  277. package/src/runtime/web-search/index.mjs +3 -22
  278. package/src/runtime/web-search/lib/web-tools.mjs +2 -2
  279. package/src/runtime/web-search/tool-defs.mjs +17 -26
  280. package/src/session-runtime/agent-profile-changes.mjs +55 -4
  281. package/src/session-runtime/boot/apis.mjs +27 -4
  282. package/src/session-runtime/boot/begin.mjs +2 -0
  283. package/src/session-runtime/boot/config.mjs +10 -1
  284. package/src/session-runtime/boot/facade.mjs +1 -0
  285. package/src/session-runtime/boot/providers.mjs +70 -3
  286. package/src/session-runtime/boot/session.mjs +2 -0
  287. package/src/session-runtime/boot/tools.mjs +69 -25
  288. package/src/session-runtime/channel-config-api.mjs +1 -0
  289. package/src/session-runtime/config-change-effects.mjs +179 -0
  290. package/src/session-runtime/config-lifecycle/config-writers.mjs +1 -0
  291. package/src/session-runtime/config-lifecycle.mjs +160 -24
  292. package/src/session-runtime/conversation-import/discovery.mjs +86 -7
  293. package/src/session-runtime/conversation-import/service.mjs +3 -2
  294. package/src/session-runtime/goal-facade-api.mjs +49 -3
  295. package/src/session-runtime/internal-tool-executor/ask-user-tool.mjs +3 -0
  296. package/src/session-runtime/internal-tool-executor/bridge-tools.mjs +3 -2
  297. package/src/session-runtime/internal-tool-executor/feature-tools.mjs +3 -11
  298. package/src/session-runtime/internal-tool-executor/workspace-tools.mjs +18 -4
  299. package/src/session-runtime/internal-tool-executor.mjs +12 -19
  300. package/src/session-runtime/lifecycle/cold-heir.mjs +7 -0
  301. package/src/session-runtime/lifecycle/memory-ingest.mjs +3 -3
  302. package/src/session-runtime/lifecycle/session-switch/session-delete.mjs +53 -5
  303. package/src/session-runtime/lifecycle/session-switch.mjs +13 -2
  304. package/src/session-runtime/lifecycle/teardown/session-close.mjs +4 -4
  305. package/src/session-runtime/lifecycle-api.mjs +1 -0
  306. package/src/session-runtime/mcp-glue/server-catalog.mjs +14 -3
  307. package/src/session-runtime/mcp-glue/server-connection.mjs +34 -8
  308. package/src/session-runtime/model-route/set-route.mjs +40 -7
  309. package/src/session-runtime/native-web-search/routes.mjs +3 -3
  310. package/src/session-runtime/provider-auth/refresh.mjs +6 -3
  311. package/src/session-runtime/provider-auth/setup.mjs +1 -1
  312. package/src/session-runtime/provider-readiness.mjs +53 -4
  313. package/src/session-runtime/resource-api.mjs +1 -1
  314. package/src/session-runtime/resource-extension-sync.mjs +4 -8
  315. package/src/session-runtime/resource-mcp-connector-accounts.mjs +5 -1
  316. package/src/session-runtime/resource-mcp-secrets.mjs +3 -3
  317. package/src/session-runtime/resource-plugins-api.mjs +4 -0
  318. package/src/session-runtime/routine-executor.mjs +152 -0
  319. package/src/session-runtime/routine-home-turn.mjs +46 -5
  320. package/src/session-runtime/runtime-facade.mjs +5 -0
  321. package/src/session-runtime/runtime-feature-gates.mjs +2 -11
  322. package/src/session-runtime/schedule-session-run.mjs +1 -0
  323. package/src/session-runtime/services/agent-tool/job-task-reconcile.mjs +6 -1
  324. package/src/session-runtime/services/agent-tool/job-views/session-progress.mjs +7 -0
  325. package/src/session-runtime/services/agent-tool/send-flow.mjs +110 -6
  326. package/src/session-runtime/services/agent-tool/spawn-flow/job-start.mjs +12 -0
  327. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-plan.mjs +5 -0
  328. package/src/session-runtime/services/agent-tool/spawn-flow/spawn-run.mjs +24 -6
  329. package/src/session-runtime/services/agent-tool.mjs +51 -1
  330. package/src/session-runtime/services/channel-admin.mjs +18 -0
  331. package/src/session-runtime/services/hook-bus/command-handler.mjs +7 -11
  332. package/src/session-runtime/services/plugin-admin.mjs +4 -0
  333. package/src/session-runtime/services/seeds.mjs +7 -0
  334. package/src/session-runtime/services/usage-stats-model.mjs +32 -11
  335. package/src/session-runtime/session-create/materialize.mjs +4 -2
  336. package/src/session-runtime/session-create/session-options.mjs +2 -0
  337. package/src/session-runtime/settings-builtin-tools-api.mjs +1 -9
  338. package/src/session-runtime/setup-tool/connector-widen-approval.mjs +1 -1
  339. package/src/session-runtime/setup-tool/executor.mjs +8 -1
  340. package/src/session-runtime/setup-tool/extended-actions.mjs +2 -0
  341. package/src/session-runtime/setup-tool/settings-contract.mjs +28 -0
  342. package/src/session-runtime/setup-tool/tool-defs.mjs +26 -1
  343. package/src/session-runtime/tool-policy/agent-profile-fold.mjs +40 -43
  344. package/src/session-runtime/tool-policy/mode-switch.mjs +1 -1
  345. package/src/session-runtime/tool-policy/policy-stage.mjs +106 -8
  346. package/src/session-runtime/tool-policy/workflow-overlay.mjs +2 -1
  347. package/src/session-runtime/tool-policy-refresh.mjs +182 -5
  348. package/src/session-runtime/tool-surface.mjs +38 -1
  349. package/src/session-runtime/turn/agent-ops.mjs +10 -2
  350. package/src/session-runtime/turn/session-ops.mjs +2 -0
  351. package/src/session-runtime/turn/turn-run/turn-prepare.mjs +5 -0
  352. package/src/session-runtime/turn/turn-run.mjs +1 -1
  353. package/src/session-runtime/webhook-session-run.mjs +2 -0
  354. package/src/session-runtime/workflow-agents-api/agent-connectors.mjs +1 -4
  355. package/src/session-runtime/workflow-agents-api/agent-route.mjs +3 -10
  356. package/src/session-runtime/workflow-agents-api/instruction-files.mjs +165 -0
  357. package/src/session-runtime/workflow-agents-api/style-and-mode.mjs +5 -4
  358. package/src/session-runtime/workflow-agents-api/workflow-packs.mjs +2 -3
  359. package/src/session-runtime/workflow-agents-api.mjs +2 -0
  360. package/src/session-runtime/workspace/project-branches.mjs +1 -1
  361. package/src/session-runtime/workspace/session-view.mjs +1 -1
  362. package/src/session-runtime/workspace/transitions.mjs +20 -4
  363. package/src/session-runtime/workspace/worktree-maintenance.mjs +126 -6
  364. package/src/session-runtime/workspace/worktree-setup.mjs +13 -1
  365. package/src/session-runtime/workspace/worktree-tool.mjs +16 -0
  366. package/src/standalone/daemon-agent-control.mjs +25 -8
  367. package/src/standalone/daemon-desktop-runtime.mjs +6 -2
  368. package/src/standalone/daemon-lag-profiler.mjs +7 -7
  369. package/src/standalone/daemon.mjs +54 -2
  370. package/src/standalone/local-session-runtime.mjs +16 -0
  371. package/src/standalone/session-client/daemon-unavailable.mjs +1 -1
  372. package/src/standalone/session-protocol.mjs +11 -0
  373. package/src/standalone/session-service/agent-route-follow.mjs +23 -20
  374. package/src/standalone/session-service/agent-tree/agent-turns.mjs +18 -6
  375. package/src/standalone/session-service/quota-resume.mjs +9 -0
  376. package/src/standalone/session-service/session-calls/session-action.mjs +51 -1
  377. package/src/standalone/session-service/session-calls/session-turns.mjs +37 -18
  378. package/src/standalone/session-service/session-calls/turn-gate.mjs +34 -0
  379. package/src/standalone/session-service/session-calls.mjs +2 -1
  380. package/src/tui/App.jsx +8 -0
  381. package/src/tui/app/clipboard.mjs +1 -1
  382. package/src/tui/app/create-app-pickers.mjs +2 -0
  383. package/src/tui/app/doctor.mjs +2 -2
  384. package/src/tui/app/panel-signature.mjs +1 -1
  385. package/src/tui/app/settings-picker.mjs +53 -8
  386. package/src/tui/app/shell-layout/row-budgets.mjs +2 -2
  387. package/src/tui/app/slash-commands.mjs +1 -1
  388. package/src/tui/app/slash-dispatch/route-commands.mjs +1 -1
  389. package/src/tui/app/slash-dispatch/session-commands.mjs +1 -1
  390. package/src/tui/app/use-prompt-queue-history.mjs +2 -2
  391. package/src/tui/app/use-transcript-activity.mjs +1 -1
  392. package/src/tui/app/use-transcript-window/geometry.mjs +1 -1
  393. package/src/tui/components/prompt-input/escape-policy.d.mts +0 -1
  394. package/src/tui/components/prompt-input/escape-policy.mjs +1 -1
  395. package/src/tui/components/question-panel-model.mjs +4 -4
  396. package/src/tui/components/tool-execution/standard-card.jsx +3 -4
  397. package/src/tui/components/user-message-skills.mjs +1 -1
  398. package/src/tui/dist/index.mjs +309 -30
  399. package/src/tui/markdown/render-ansi.mjs +1 -1
  400. package/src/tui/session/agent-job-feed/notification-router.mjs +10 -0
  401. package/src/tui/session/agent-job-feed.mjs +2 -0
  402. package/src/tui/session/context-state.mjs +3 -0
  403. package/src/tui/session/quota-resume.mjs +1 -1
  404. package/src/tui/session/session-api/lifecycle/dispose.mjs +1 -0
  405. package/src/tui/session/session-api/lifecycle/session-reset.mjs +7 -0
  406. package/src/tui/session/session-api/routes-agent-definitions.mjs +5 -0
  407. package/src/tui/session/session-api/worktree.mjs +13 -1
  408. package/src/tui/session/session-flow/drain.mjs +11 -14
  409. package/src/tui/session/tool-card-results/grouped-result.mjs +2 -2
  410. package/src/tui/session/turn-quiet.mjs +1 -1
  411. package/src/tui/session/turn.mjs +3 -0
  412. package/src/tui/session/worktree-setup-notice.mjs +1 -1
  413. package/src/tui/spinner-meta.mjs +3 -3
  414. package/src/tui/spinner-verbs.mjs +2 -2
  415. package/src/defaults/skills/goal-management/SKILL.md +0 -169
  416. package/src/runtime/agent/orchestrator/runtime-core/goal-tasks.mjs +0 -156
  417. package/src/session-runtime/goal-tasks.mjs +0 -1
@@ -33,7 +33,7 @@ function quotedName(name, fallback) {
33
33
  }
34
34
 
35
35
  /** How much of a PDF reads as text, in words the model can act on. */
36
- export function textLayerLabel(pageCount, textPages) {
36
+ function textLayerLabel(pageCount, textPages) {
37
37
  const pages = Number(pageCount);
38
38
  const withText = Number(textPages);
39
39
  if (!(pages > 0) || !Number.isFinite(withText) || withText < 0) return 'text layer not checked';
@@ -81,7 +81,7 @@ export const PDF_INTAKE_TIMEOUT_MS = 60_000;
81
81
  // least this many non-whitespace characters. A scan often keeps a stamped page
82
82
  // number or a few stray glyphs; those alone do not make its content readable
83
83
  // as text, so such a page still needs its image (or OCR).
84
- export const PDF_TEXT_LAYER_MIN_CHARS = 16;
84
+ const PDF_TEXT_LAYER_MIN_CHARS = 16;
85
85
 
86
86
  /** Whether one page's extracted text is a real text layer (see above). */
87
87
  export function pageHasTextLayer(text) {
@@ -1,4 +1,5 @@
1
1
  export function readAttachmentBuffer(value: string | { attachmentRef?: string }): Buffer;
2
+ export function attachmentBlobSize(value: string | { attachmentRef?: string }, claimed?: number): number;
2
3
  export function isPermanentAttachmentError(error: unknown): boolean;
3
4
  export function isAttachmentReference(value: unknown): boolean;
4
5
  export interface MessageAttachment {
@@ -298,7 +298,7 @@ const ATTACHMENT_INTEGRITY = 'ATTACHMENT_INTEGRITY';
298
298
 
299
299
  // A referenced blob that is gone from disk (retention, manual cleanup, a
300
300
  // copied session) surfaces from readAttachmentBuffer as the stat ENOENT.
301
- export function isMissingAttachmentError(error) {
301
+ function isMissingAttachmentError(error) {
302
302
  return error?.code === 'ENOENT';
303
303
  }
304
304
 
@@ -316,7 +316,7 @@ export function readAttachmentBase64(value) {
316
316
  // Real on-disk size of a referenced blob. Caps must never trust the client's
317
317
  // claimed sizeBytes; a blob that is not on disk cannot be read later either, so
318
318
  // only then does the claimed size stand.
319
- function attachmentBlobSize(value, claimed = 0) {
319
+ export function attachmentBlobSize(value, claimed = 0) {
320
320
  try {
321
321
  return statSync(attachmentPath(typeof value === 'string' ? value : value?.attachmentRef)).size;
322
322
  } catch (error) {
@@ -0,0 +1,42 @@
1
+ // The channels worker's config hot reload (schedules, webhooks, events, voice).
2
+ // It rides the shared config-change subscription: that watches the data
3
+ // directory, so the atomic rename of every write never orphans it, it recovers
4
+ // from watcher errors, and it carries in-process writes and keychain changes.
5
+ // A reload restarts the webhook server and hook tunnel, so only a change of
6
+ // the sections the worker reads (channels: webhook, events, …; voice) reloads;
7
+ // schedules arrive through the reload_config IPC, never through this watch.
8
+ import { readSection, subscribeConfigChange } from '../../shared/config.mjs';
9
+
10
+ function channelsConfigSnapshot() {
11
+ return JSON.stringify([readSection('channels') ?? null, readSection('voice') ?? null]);
12
+ }
13
+
14
+ export function createConfigReloadWatch(reload, { snapshot = channelsConfigSnapshot } = {}) {
15
+ let unsubscribe = null;
16
+ let applied = null;
17
+ const read = () => {
18
+ try {
19
+ return snapshot();
20
+ } catch {
21
+ return null;
22
+ }
23
+ };
24
+ return {
25
+ start() {
26
+ if (unsubscribe) return;
27
+ applied = read();
28
+ unsubscribe = subscribeConfigChange(() => {
29
+ const next = read();
30
+ if (next !== null && next === applied) return;
31
+ applied = next;
32
+ Promise.resolve()
33
+ .then(reload)
34
+ .catch(() => {});
35
+ });
36
+ },
37
+ stop() {
38
+ unsubscribe?.();
39
+ unsubscribe = null;
40
+ },
41
+ };
42
+ }
@@ -1,6 +1,19 @@
1
1
  import { loadConfig } from '../config.mjs';
2
2
 
3
- export function createConfigReloader({ state, getConfig, setConfig, getBridgeRuntimeConnected, scheduler, services }) {
3
+ export function createConfigReloader({
4
+ state,
5
+ getConfig,
6
+ setConfig,
7
+ getBridgeRuntimeConnected,
8
+ scheduler,
9
+ services,
10
+ load = loadConfig,
11
+ }) {
12
+ // Reloads overlap (no debounce), so a slower load of an older config must not
13
+ // be applied over a newer one: only the newest started reload applies, and an
14
+ // older caller waits for it instead.
15
+ let latestStarted = 0;
16
+ let latestReload = null;
4
17
  function reloadScheduler() {
5
18
  // The scheduler must be RE-ARMED by the reload whenever it is supposed to
6
19
  // be running: `restart: false` only destroys the cron/one-shot bindings and
@@ -12,13 +25,20 @@ export function createConfigReloader({ state, getConfig, setConfig, getBridgeRun
12
25
  restart: state.automationRunning || getBridgeRuntimeConnected(),
13
26
  });
14
27
  }
15
- return async function reloadRuntimeConfig() {
16
- setConfig(await loadConfig());
17
- reloadScheduler();
18
- if (getBridgeRuntimeConnected() || state.automationRunning) {
19
- services.syncWebhookAndEventRuntime({ reload: true });
20
- } else {
21
- await services.stopWebhookAndEventRuntime();
22
- }
28
+ return function reloadRuntimeConfig() {
29
+ const ticket = ++latestStarted;
30
+ const run = (async () => {
31
+ const loaded = await load();
32
+ if (ticket !== latestStarted) return latestReload;
33
+ setConfig(loaded);
34
+ reloadScheduler();
35
+ if (getBridgeRuntimeConnected() || state.automationRunning) {
36
+ services.syncWebhookAndEventRuntime({ reload: true });
37
+ } else {
38
+ await services.stopWebhookAndEventRuntime();
39
+ }
40
+ })();
41
+ latestReload = run;
42
+ return run;
23
43
  };
24
44
  }
@@ -9,6 +9,7 @@ import { isPidAlive } from '../../shared/pid-liveness.mjs';
9
9
  import {
10
10
  advanceScheduleCursor,
11
11
  claimScheduleRun,
12
+ getSchedule,
12
13
  markFired,
13
14
  markDone,
14
15
  markScheduleFailure,
@@ -76,6 +77,14 @@ function isCronExpression(time) {
76
77
  return false;
77
78
  }
78
79
  }
80
+ // Two one-shot times name the same instant. The database hands back a new
81
+ // Date on every read, so identity would call an unchanged schedule changed.
82
+ function sameInstant(left, right) {
83
+ if (left == null || right == null) return (left ?? null) === (right ?? null);
84
+ const at = (value) => (value instanceof Date ? value.getTime() : Date.parse(value));
85
+ const [a, b] = [at(left), at(right)];
86
+ return Number.isNaN(a) || Number.isNaN(b) ? String(left) === String(right) : a === b;
87
+ }
79
88
  // True when this fire (started at `since`) ended in an overlap skip.
80
89
  function skippedSince(schedule, since) {
81
90
  const at = schedule.lastSkippedAt ? new Date(schedule.lastSkippedAt).getTime() : 0;
@@ -616,6 +625,30 @@ class Scheduler {
616
625
  }
617
626
  // ── Fire timed schedule ─────────────────────────────────────────────
618
627
  async fireTimed(schedule, opts = {}) {
628
+ // The armed copy may be stale (a reload that failed or has not arrived):
629
+ // the stored row decides whether, and with what prompt, it still fires.
630
+ let row;
631
+ try {
632
+ row = await getSchedule(schedule.name);
633
+ } catch (err) {
634
+ logSchedule(`${schedule.name}: not fired — schedule unreadable: ${err?.message || err}\n`);
635
+ this.notifyFailure(schedule, 'schedule unreadable — fire skipped');
636
+ return false;
637
+ }
638
+ const sameCron = (row?.whenCron ?? null) === (schedule.whenCron ?? null);
639
+ if (
640
+ !row ||
641
+ row.enabled === false ||
642
+ row.status === 'done' ||
643
+ !sameInstant(row.whenAt, schedule.whenAt) ||
644
+ !sameCron
645
+ ) {
646
+ logSchedule(`${schedule.name}: not fired — deleted or changed since it was armed\n`);
647
+ // A skip, not a failed run: the callers record no failure on its row.
648
+ schedule.lastSkippedAt = new Date().toISOString();
649
+ return false;
650
+ }
651
+ schedule.prompt = row.prompt;
619
652
  const prompt = this.resolvePrompt(schedule);
620
653
  if (!prompt) {
621
654
  process.stderr.write(`mixdog scheduler: prompt not found for "${schedule.name}"
@@ -60,6 +60,9 @@ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_skipped_at timesta
60
60
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS owner_agent_id text NOT NULL DEFAULT 'lead';
61
61
  -- Legacy columns model, workflow and run_as_agent are no longer read or written (kept, not dropped, to avoid a risky migration).
62
62
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS run_as_agent boolean NOT NULL DEFAULT false;
63
+ -- The workflow sub-agent that carries out each fire first (its final report is
64
+ -- what the owner bot relays); NULL = the owner bot does the work itself.
65
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS executor_agent_id text;
63
66
  -- Failed runs in a row: a success or switching it back on resets it; at
64
67
  -- ROUTINE_LIMITS.pauseAfterFailures the schedule switches itself off.
65
68
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS consecutive_failures integer NOT NULL DEFAULT 0;
@@ -101,6 +104,7 @@ function rowToDef(row) {
101
104
  lastFailedAt: row.last_failed_at,
102
105
  lastSkippedAt: row.last_skipped_at,
103
106
  ownerAgentId: row.owner_agent_id || 'lead',
107
+ executorAgentId: row.executor_agent_id || null,
104
108
  consecutiveFailures: Number(row.consecutive_failures) || 0,
105
109
  resultDelivery: row.result_delivery || null,
106
110
  nextFireAt: row.next_fire_at,
@@ -136,7 +140,7 @@ function hasRequiredTargetColumn(db) {
136
140
  }
137
141
 
138
142
  const COLS =
139
- 'name, description, when_at, when_cron, timezone, cwd, attachments, delivery, prompt, enabled, status, last_fired_at, last_scheduled_at, last_started_at, last_success_at, last_failed_at, last_skipped_at, owner_agent_id, consecutive_failures, result_delivery, next_fire_at, deferred_until, skipped_until, created_at, updated_at';
143
+ 'name, description, when_at, when_cron, timezone, cwd, attachments, delivery, prompt, enabled, status, last_fired_at, last_scheduled_at, last_started_at, last_success_at, last_failed_at, last_skipped_at, owner_agent_id, executor_agent_id, consecutive_failures, result_delivery, next_fire_at, deferred_until, skipped_until, created_at, updated_at';
140
144
 
141
145
  // ---------------------------------------------------------------------------
142
146
  // Public API
@@ -176,6 +180,7 @@ export async function upsertSchedule(def, { dataDir } = {}) {
176
180
  def.status ?? 'active',
177
181
  def.nextFireAt ?? null,
178
182
  def.ownerAgentId || 'lead',
183
+ def.executorAgentId || null,
179
184
  ];
180
185
  let legacyTarget = '';
181
186
  let legacyTargetValue = '';
@@ -186,10 +191,11 @@ export async function upsertSchedule(def, { dataDir } = {}) {
186
191
  }
187
192
  const { rows } = await db.query(
188
193
  `INSERT INTO scheduler.schedules
189
- (name, description, when_at, when_cron, timezone, cwd, attachments, delivery, prompt, enabled, status, next_fire_at, owner_agent_id${legacyTarget})
190
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13${legacyTargetValue})
194
+ (name, description, when_at, when_cron, timezone, cwd, attachments, delivery, prompt, enabled, status, next_fire_at, owner_agent_id, executor_agent_id${legacyTarget})
195
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14${legacyTargetValue})
191
196
  ON CONFLICT (name) DO UPDATE SET
192
197
  owner_agent_id = EXCLUDED.owner_agent_id,
198
+ executor_agent_id = EXCLUDED.executor_agent_id,
193
199
  description = EXCLUDED.description,
194
200
  when_at = EXCLUDED.when_at,
195
201
  when_cron = EXCLUDED.when_cron,
@@ -235,7 +241,7 @@ export async function renameSchedule(oldName, def, { timingChanged = false, data
235
241
  const { rows } = await db.query(
236
242
  `UPDATE scheduler.schedules SET
237
243
  name = $2, description = $3, when_at = $4, when_cron = $5, timezone = $6, cwd = $7,
238
- attachments = $8, prompt = $9, enabled = $10, owner_agent_id = $11,
244
+ attachments = $8, prompt = $9, enabled = $10, owner_agent_id = $11, executor_agent_id = $13,
239
245
  next_fire_at = CASE WHEN $12::boolean THEN $4::timestamptz ELSE next_fire_at END,
240
246
  status = CASE WHEN $12::boolean THEN 'active' ELSE status END,
241
247
  deferred_until = CASE WHEN $12::boolean THEN NULL ELSE deferred_until END,
@@ -258,6 +264,7 @@ export async function renameSchedule(oldName, def, { timingChanged = false, data
258
264
  def.enabled ?? true,
259
265
  def.ownerAgentId || 'lead',
260
266
  timingChanged === true,
267
+ def.executorAgentId || null,
261
268
  ]
262
269
  );
263
270
  return rowToDef(rows[0]);
@@ -96,6 +96,8 @@ function createWebhookSessionDispatcher({ getBridgeDispatch, store = { recordEnd
96
96
  endpoint: name,
97
97
  // The bot whose chat the run is a turn in.
98
98
  owner: extra.owner || null,
99
+ // The sub-agent that carries the run out first, if the endpoint names one.
100
+ executor: extra.executor || null,
99
101
  deliveryId,
100
102
  event: extra.event || null,
101
103
  },
@@ -108,7 +110,7 @@ function createWebhookSessionDispatcher({ getBridgeDispatch, store = { recordEnd
108
110
 
109
111
  async function dispatchEndpoint({ name, body, headers, res, deliveryId, endpoint }) {
110
112
  try {
111
- const { instructions, cwd, attachments, delivery, ownerAgentId } = endpoint;
113
+ const { instructions, cwd, attachments, delivery, ownerAgentId, executorAgentId } = endpoint;
112
114
  const run = webhookRunPrompt(instructions, body, headers);
113
115
  if (!getBridgeDispatch()) throw new Error(`[webhook] session dispatch requires bridgeDispatch`);
114
116
  // Kept for the app's "Run now", which re-runs the latest event. Losing
@@ -118,6 +120,7 @@ function createWebhookSessionDispatcher({ getBridgeDispatch, store = { recordEnd
118
120
  );
119
121
  await dispatchSessionRun(name, run.prompt, deliveryId, res, {
120
122
  owner: ownerAgentId || null,
123
+ executor: executorAgentId || null,
121
124
  cwd: cwd || null,
122
125
  event: webhookEventSummary(headers, body),
123
126
  attachments: attachments || null,
@@ -45,6 +45,9 @@ ALTER TABLE webhooks.endpoints ALTER COLUMN role SET DEFAULT '';
45
45
  ALTER TABLE webhooks.endpoints ADD COLUMN IF NOT EXISTS owner_agent_id text NOT NULL DEFAULT 'lead';
46
46
  -- Legacy columns model, workflow and run_as_agent are no longer read or written (kept, not dropped, to avoid a risky migration).
47
47
  ALTER TABLE webhooks.endpoints ADD COLUMN IF NOT EXISTS run_as_agent boolean NOT NULL DEFAULT false;
48
+ -- The workflow sub-agent that carries out each run first (its final report is
49
+ -- what the owner bot relays); NULL = the owner bot does the work itself.
50
+ ALTER TABLE webhooks.endpoints ADD COLUMN IF NOT EXISTS executor_agent_id text;
48
51
  -- The latest received event (its payload kept for a manual re-run) and the
49
52
  -- outcome stamps of the runs it started.
50
53
  ALTER TABLE webhooks.endpoints ADD COLUMN IF NOT EXISTS last_event_at timestamptz;
@@ -86,7 +89,7 @@ const getDb = createPgSchemaDb({
86
89
  // The stored payload itself is never projected here (readEndpointLastPayload
87
90
  // is its only read path); rows carry a presence flag.
88
91
  const ENDPOINT_COLS =
89
- 'name, description, parser, secret, cwd, attachments, delivery, instructions, enabled, owner_agent_id, last_event_at, last_success_at, last_failed_at, last_skipped_at, consecutive_failures, result_delivery, (last_payload IS NOT NULL) AS has_last_payload, created_at, updated_at';
92
+ 'name, description, parser, secret, cwd, attachments, delivery, instructions, enabled, owner_agent_id, executor_agent_id, last_event_at, last_success_at, last_failed_at, last_skipped_at, consecutive_failures, result_delivery, (last_payload IS NOT NULL) AS has_last_payload, created_at, updated_at';
90
93
 
91
94
  function rowToEndpoint(row) {
92
95
  if (!row) return null;
@@ -104,6 +107,7 @@ function rowToEndpoint(row) {
104
107
  instructions: row.instructions,
105
108
  enabled: row.enabled,
106
109
  ownerAgentId: row.owner_agent_id || 'lead',
110
+ executorAgentId: row.executor_agent_id || null,
107
111
  lastEventAt: row.last_event_at || null,
108
112
  lastSuccessAt: row.last_success_at || null,
109
113
  lastFailedAt: row.last_failed_at || null,
@@ -187,15 +191,17 @@ export async function upsertEndpoint(def, { dataDir } = {}) {
187
191
  def.instructions ?? '',
188
192
  def.enabled ?? true,
189
193
  def.ownerAgentId || 'lead',
194
+ def.executorAgentId || null,
190
195
  ];
191
196
  // The last event and run stamps survive an edit: they describe deliveries,
192
197
  // not the definition.
193
198
  const { rows } = await db.query(
194
199
  `INSERT INTO webhooks.endpoints
195
- (name, description, role, parser, secret, cwd, attachments, delivery, instructions, enabled, owner_agent_id)
196
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11)
200
+ (name, description, role, parser, secret, cwd, attachments, delivery, instructions, enabled, owner_agent_id, executor_agent_id)
201
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)
197
202
  ON CONFLICT (name) DO UPDATE SET
198
203
  owner_agent_id = EXCLUDED.owner_agent_id,
204
+ executor_agent_id = EXCLUDED.executor_agent_id,
199
205
  description = EXCLUDED.description,
200
206
  role = EXCLUDED.role,
201
207
  parser = EXCLUDED.parser,
@@ -40,6 +40,7 @@ import fs from 'node:fs';
40
40
  import { spawn, spawnSync } from 'node:child_process';
41
41
  import { setTimeout as delay } from 'node:timers/promises';
42
42
  import { windowsSystemRoot } from '../../agent/orchestrator/tools/builtin/windows-roots.mjs';
43
+ import { killProcessTrees } from '../../agent/orchestrator/mcp/child-tree.mjs';
43
44
  import { selectWhisperPort, whisperListenerOwned } from './whisper-port.mjs';
44
45
 
45
46
  // ── Tunables (deterministic; no ranges) ──────────────────────────────────────
@@ -217,16 +218,13 @@ function isOwnedWhisperServer(pid, { modelPath, port } = {}) {
217
218
  return modelMatch || portMatch;
218
219
  }
219
220
 
220
- function killPid(pid, force) {
221
+ // `aliveAt`: when `pid` was last known to be the process meant (a live child
222
+ // handle: now; a proven stale child: before its proof). Windows has no graceful
223
+ // stop for a console server, so both steps run the shared tree kill.
224
+ async function killPid(pid, force, aliveAt = Date.now()) {
221
225
  if (!pid) return;
222
226
  if (IS_WIN) {
223
- const args = ['/PID', String(pid), '/T'];
224
- if (force) args.push('/F');
225
- try {
226
- spawnSync('taskkill', args, { windowsHide: true });
227
- } catch {
228
- /* best-effort */
229
- }
227
+ await killProcessTrees([pid], { aliveAt });
230
228
  return;
231
229
  }
232
230
  try {
@@ -325,10 +323,10 @@ async function teardown(reason) {
325
323
  resolve();
326
324
  }
327
325
  });
328
- killPid(pid, false);
326
+ void killPid(pid, false);
329
327
  const raced = await Promise.race([exited, delay(STOP_GRACE_MS, 'timeout')]);
330
328
  if (raced === 'timeout') {
331
- killPid(pid, true); // escalate
329
+ void killPid(pid, true); // escalate
332
330
  await Promise.race([exited, delay(STOP_GRACE_MS, 'timeout')]);
333
331
  }
334
332
  }
@@ -384,11 +382,13 @@ async function startServer(contract) {
384
382
  // Prefer the recorded meta's own contract fields; fall back to the contract we
385
383
  // are about to start. A non-positive match → never kill (fail closed).
386
384
  const ownArgs = { modelPath: meta?.modelPath ?? modelPath, port: meta?.port ?? FIXED_PORT };
385
+ const provenAt = Date.now();
387
386
  if (meta && isOwnedWhisperServer(meta.pid, ownArgs)) {
388
- killPid(meta.pid, false);
387
+ await killPid(meta.pid, false, provenAt);
389
388
  await delay(500);
389
+ const reprovenAt = Date.now();
390
390
  if (isOwnedWhisperServer(meta.pid, ownArgs)) {
391
- killPid(meta.pid, true);
391
+ await killPid(meta.pid, true, reprovenAt);
392
392
  await delay(500);
393
393
  }
394
394
  clearPidMeta();
@@ -18,7 +18,7 @@ import {
18
18
  cleanupInstanceRuntimeFiles,
19
19
  clearServerPid,
20
20
  } from './runtime-paths.mjs';
21
- import { invalidateConfigReadCache } from '../../shared/config.mjs';
21
+ import { createConfigReloadWatch } from './config-reload-watch.mjs';
22
22
  import { bootProfile, utcTimestamp } from './boot-profile.mjs';
23
23
  import {
24
24
  isChannelsDegraded,
@@ -121,9 +121,6 @@ function writeBridgeState(active) {
121
121
  function isChannelBridgeActive() {
122
122
  return channelBridgeActive;
123
123
  }
124
- // Config hot-reload watcher (installed by start(); torn down by stop()).
125
- let _configWatcher = null;
126
- let _reloadDebounce = null;
127
124
  const STATUS_FILE = getStatusPath(INSTANCE_ID);
128
125
  const statusState = new JsonStateFile(STATUS_FILE, {});
129
126
  statusState.ensure();
@@ -201,6 +198,7 @@ function wireWebhookHandlers() {
201
198
  const run = await runWebhookSession({
202
199
  name: context?.endpoint || 'webhook',
203
200
  ownerAgentId: context?.owner || null,
201
+ executorAgentId: context?.executor || null,
204
202
  cwd: cwd || null,
205
203
  attachments: attachments || null,
206
204
  event: context?.event || '',
@@ -252,24 +250,14 @@ const { handleToolCall } = createToolDispatch({
252
250
  }),
253
251
  },
254
252
  });
255
- function ensureConfigWatcher() {
256
- if (_configWatcher) return;
257
- try {
258
- _configWatcher = fs.watch(path.join(DATA_DIR, 'mixdog-config.json'), () => {
259
- invalidateConfigReadCache();
260
- if (_reloadDebounce) clearTimeout(_reloadDebounce);
261
- _reloadDebounce = setTimeout(() => {
262
- reloadRuntimeConfig().catch(() => {});
263
- }, 500);
264
- });
265
- } catch {}
266
- }
253
+ // Config hot reload (started by start(); stopped by stop()).
254
+ const configReloadWatch = createConfigReloadWatch(reloadRuntimeConfig);
267
255
  async function start(options = {}) {
268
256
  if (options?.bridge === false) {
269
257
  channelBridgeActive = false;
270
258
  writeBridgeState(false);
271
259
  await startAutomationRuntime();
272
- ensureConfigWatcher();
260
+ configReloadWatch.start();
273
261
  return;
274
262
  }
275
263
  channelBridgeActive = true;
@@ -283,10 +271,9 @@ async function start(options = {}) {
283
271
  } catch {
284
272
  // Non-fatal: owned-runtime start errors degrade to automation-only mode.
285
273
  }
286
- // Hot-reload config on file change (schedules/webhooks/events).
287
- // Cross-process edits invalidate the raw config cache before the debounced
288
- // reload so both bridge and automation-only daemon modes stay current.
289
- ensureConfigWatcher();
274
+ // Hot-reload config on change (schedules/webhooks/events), in both bridge
275
+ // and automation-only daemon modes.
276
+ configReloadWatch.start();
290
277
  // Pre-warm the whisper-server manager once at owner startup so the first
291
278
  // voice transcription does not pay cold-start cost. Non-blocking: failures
292
279
  // (e.g. runtime not installed) are swallowed; per-request ensureReady retries.
@@ -314,16 +301,7 @@ async function stop() {
314
301
  } catch {}
315
302
  await stopOwnedRuntime('unified server stop');
316
303
  cleanupInstanceRuntimeFiles(INSTANCE_ID);
317
- if (_reloadDebounce) {
318
- clearTimeout(_reloadDebounce);
319
- _reloadDebounce = null;
320
- }
321
- if (_configWatcher) {
322
- try {
323
- _configWatcher.close();
324
- } catch {}
325
- _configWatcher = null;
326
- }
304
+ configReloadWatch.stop();
327
305
  }
328
306
  // ── IPC worker mode ──────────────────────────────────────────────
329
307
  // Skipped under the machine-global host (MIXDOG_DAEMON_HOST=1): the daemon
@@ -10,25 +10,25 @@ import fs from 'node:fs';
10
10
  import path from 'node:path';
11
11
  import { isForegroundWindowCapture, requiredCapabilities } from './linux-action-capabilities.mjs';
12
12
 
13
- export const UINPUT_HINT =
13
+ const UINPUT_HINT =
14
14
  'input_unavailable: Wayland input needs write access to /dev/uinput; add the udev rule `KERNEL=="uinput", GROUP="input", MODE="0660", OPTIONS+="static_node=uinput"`, add your user to the input group, then sign in again';
15
15
  export const INPUT_GROUP_SECURITY_NOTE =
16
16
  'Security note: membership of the input group lets every program you run read and inject keyboard and mouse input system-wide. Grant it only on a machine and account you trust.';
17
17
  export const GNOME_EXTENSION_UUID = 'mixdog-windows@mixdog.io';
18
18
  /** The oldest helper interface this app can use (2 added Screenshot and StopRequested, 3 the in-shell indicator). */
19
19
  export const MIN_WINDOW_HELPER_VERSION = 3;
20
- export const WINDOW_HELPER_RELOGIN_HINT =
20
+ const WINDOW_HELPER_RELOGIN_HINT =
21
21
  'wayland_window_control_unavailable: the Mixdog Windows GNOME extension is installed and enabled but GNOME Shell has not loaded it yet; the user must sign out and sign in again once';
22
- export const WINDOW_HELPER_ENABLE_HINT =
22
+ const WINDOW_HELPER_ENABLE_HINT =
23
23
  'wayland_window_control_unavailable: the Mixdog Windows GNOME extension is not enabled; it could not be enabled automatically (run gnome-extensions enable mixdog-windows@mixdog.io)';
24
- export const WINDOW_CONTROL_UNSUPPORTED_HINT =
24
+ const WINDOW_CONTROL_UNSUPPORTED_HINT =
25
25
  'wayland_window_control_unavailable: this desktop offers no window control to other programs (supported: GNOME with the Mixdog Windows extension, sway, Hyprland, or an X11 session); screen, pointer, keyboard and clipboard still work';
26
- export const ACCESSIBILITY_HINT = 'accessibility_unavailable: the AT-SPI bus is not running; install at-spi2-core';
26
+ const ACCESSIBILITY_HINT = 'accessibility_unavailable: the AT-SPI bus is not running; install at-spi2-core';
27
27
  export const TESSERACT_HINT =
28
28
  'ocr_unavailable: install Tesseract (for example `sudo apt install tesseract-ocr tesseract-ocr-kor`) to read text from pixels';
29
29
  export const CLIPBOARD_HINT =
30
30
  'clipboard_unavailable: install wl-clipboard (`wl-copy`/`wl-paste`) for clipboard access on Wayland';
31
- export const DISPLAY_HINT =
31
+ const DISPLAY_HINT =
32
32
  'display_unavailable: no graphical session to drive; run inside a desktop session with DISPLAY (X11) or WAYLAND_DISPLAY set';
33
33
 
34
34
  /** Stable code of the whole refusal; the per-capability codes are the native ones. */
@@ -301,14 +301,10 @@ function capabilityIssue(name, readiness) {
301
301
  return { code: 'display_unavailable', message: DISPLAY_HINT };
302
302
  // Semantic actions fail on whichever of their two prerequisites is missing.
303
303
  const gnomeWayland = readiness.session === 'wayland' && readiness.desktop === 'gnome';
304
- const target =
305
- name === 'semanticActions'
306
- ? readiness.capabilities.windows.state === 'ready'
307
- ? 'accessibility'
308
- : 'windows'
309
- : name === 'capture' && gnomeWayland
310
- ? 'windows'
311
- : name;
304
+ let target = name;
305
+ if (name === 'semanticActions')
306
+ target = readiness.capabilities.windows.state === 'ready' ? 'accessibility' : 'windows';
307
+ else if (name === 'capture' && gnomeWayland) target = 'windows';
312
308
  const capability = readiness.capabilities[target];
313
309
  switch (target) {
314
310
  case 'windows':