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
@@ -8,6 +8,8 @@
8
8
  // `powershell -Command "<inline>"` / `pwsh -Command "<inline>"` to
9
9
  // `-EncodedCommand <utf16le-base64>` so the payload stays opaque to the
10
10
  // outer shell. Other shells / non-Windows are no-op pass-through.
11
+ import { codeMask } from './builtin/bash-tool/bashism-rewrite.mjs';
12
+
11
13
  // Match -Command "<body>" where the body may contain escaped quotes
12
14
  // (`\"` or `""`). Stops at the first unescaped closing quote so nested
13
15
  // patterns like `"... \"inner\" ..."` survive intact. Common PowerShell
@@ -16,49 +18,63 @@
16
18
  // Single-quoted -Command '<body>' is also covered.
17
19
  const _POWERSHELL_FLAGS_RE =
18
20
  /\s+-(?:NoProfile|NonInteractive|WindowStyle\s+\S+|ExecutionPolicy\s+\S+|Sta|Mta|NoLogo|NoExit)/.source;
21
+ // What may follow the closing quote: the end of the command or a shell
22
+ // operator. Anything else (`pwsh -Command "a" "b"`) is more arguments, which
23
+ // PowerShell appends to the command text and an -EncodedCommand would drop.
24
+ const _POWERSHELL_TRAILING = '(?=[ \\t]*(?:$|[\\r\\n;|&<>]|\\d+>))';
19
25
  const _POWERSHELL_DOUBLE_RE = new RegExp(
20
26
  '\\b(powershell(?:\\.exe)?|pwsh(?:\\.exe)?)((?:' +
21
27
  _POWERSHELL_FLAGS_RE +
22
- ')*)\\s+(?:-Command|-c)\\s+"((?:[^"\\\\]|\\\\.|"")+?)"(?=\\s|$|;|&&|\\|\\|)',
28
+ ')*)\\s+(?:-Command|-c)\\s+"((?:[^"\\\\]|\\\\.|"")+?)"' +
29
+ _POWERSHELL_TRAILING,
23
30
  'gi'
24
31
  );
25
32
  const _POWERSHELL_SINGLE_RE = new RegExp(
26
33
  '\\b(powershell(?:\\.exe)?|pwsh(?:\\.exe)?)((?:' +
27
34
  _POWERSHELL_FLAGS_RE +
28
- ")*)\\s+(?:-Command|-c)\\s+'((?:[^'\\\\]|\\\\.|'')+?)'(?=\\s|$|;|&&|\\|\\|)",
35
+ ")*)\\s+(?:-Command|-c)\\s+'((?:[^'\\\\]|\\\\.|'')+?)'" +
36
+ _POWERSHELL_TRAILING,
29
37
  'gi'
30
38
  );
31
39
 
32
- export function _maybeEncodePowerShellCommand(command) {
33
- if (process.platform !== 'win32') return command;
34
- if (typeof command !== 'string' || command.length === 0) return command;
35
- const replaceFn = (match, exe, flags, body) => {
40
+ // One pass of one quote style. Only an invocation the outer shell itself runs
41
+ // is rewritten: the same text inside a string, here-string or comment (a
42
+ // README being written, a commit message) is data, not a command.
43
+ function _encodePass(command, pattern, quote) {
44
+ const live = codeMask(command);
45
+ if (!live) return command;
46
+ return command.replace(pattern, (match, exe, flags, body, offset) => {
47
+ if (!live[offset]) return match;
36
48
  try {
37
- // Unescape doubled-up quotes the caller used to embed " / ' inside
38
- // the -Command literal. We're handing the body to powershell as
39
- // base64 so the outer-shell escaping is no longer needed.
40
- // Unescape both PowerShell-style doubled quotes (`""` / `''`) AND
41
- // bash-style backslash-escaped quotes (`\"` / `\'`) since POSIX
42
- // outer-shell wrappers commonly use backslash form. Without
43
- // backslash unescape, `pwsh -Command "Get-Process \"foo\""` would
44
- // base64-encode the literal backslash, breaking inside PowerShell.
45
- const unescaped = _unescapePowerShellCommandBody(body);
49
+ // Unescape the quote the caller used to embed the body: the PowerShell
50
+ // doubled form (`""` / `''`) AND the bash-style backslash form (`\"` /
51
+ // `\'`) POSIX outer-shell wrappers commonly use. Without the backslash
52
+ // unescape, `pwsh -Command "Get-Process \"foo\""` would base64-encode the
53
+ // literal backslash. The OTHER quote is the body's own syntax and stays.
54
+ const unescaped = _unescapePowerShellCommandBody(body, quote);
46
55
  const encoded = Buffer.from(unescaped, 'utf16le').toString('base64');
47
56
  const trimmedFlags = (flags || '').replace(/\s+/g, ' ').trim();
48
57
  return `${exe}${trimmedFlags ? ` ${trimmedFlags}` : ''} -EncodedCommand ${encoded}`;
49
58
  } catch {
50
59
  return match;
51
60
  }
52
- };
53
- return command.replace(_POWERSHELL_DOUBLE_RE, replaceFn).replace(_POWERSHELL_SINGLE_RE, replaceFn);
61
+ });
62
+ }
63
+
64
+ export function _maybeEncodePowerShellCommand(command) {
65
+ if (process.platform !== 'win32') return command;
66
+ if (typeof command !== 'string' || command.length === 0) return command;
67
+ return _encodePass(_encodePass(command, _POWERSHELL_DOUBLE_RE, '"'), _POWERSHELL_SINGLE_RE, "'");
54
68
  }
55
69
 
56
- function _unescapePowerShellCommandBody(body) {
57
- return String(body || '')
58
- .replace(/""/g, '"')
59
- .replace(/''/g, "'")
60
- .replace(/\\"/g, '"')
61
- .replace(/\\'/g, "'");
70
+ // `quote` names the delimiter that wrapped the body, so only its own escapes
71
+ // are removed. Without it (policy scanning, which wants every reading of the
72
+ // body) both styles are removed.
73
+ function _unescapePowerShellCommandBody(body, quote) {
74
+ let text = String(body || '');
75
+ if (quote !== "'") text = text.replace(/""/g, '"').replace(/\\"/g, '"');
76
+ if (quote !== '"') text = text.replace(/''/g, "'").replace(/\\'/g, "'");
77
+ return text;
62
78
  }
63
79
 
64
80
  // Extract inline `powershell -Command "…"` bodies for policy scan parity
@@ -126,11 +126,15 @@ function getSnapshotScript(shellPath, snapshotFilePath, configFileExists) {
126
126
  declare -f "$func" >> "$SNAPSHOT_FILE" 2>/dev/null
127
127
  done
128
128
  `;
129
+ // Options are matched on their state column: a name containing "on"
130
+ // (monitor, onecmd) must not turn an OFF option on. Job control (monitor)
131
+ // is never restored: it gives every pipeline its own process group, which
132
+ // the runner's group kill then cannot reach.
129
133
  const optDump = isZsh
130
- ? `setopt 2>/dev/null | sed 's/^/setopt /' | head -n 1000 >> "$SNAPSHOT_FILE"`
134
+ ? `setopt 2>/dev/null | grep -vxE '(no)?monitor' | sed 's/^/setopt /' | head -n 1000 >> "$SNAPSHOT_FILE"`
131
135
  : `
132
136
  shopt -p 2>/dev/null | head -n 1000 >> "$SNAPSHOT_FILE"
133
- set -o 2>/dev/null | grep "on" | awk '{print "set -o " $1}' | head -n 1000 >> "$SNAPSHOT_FILE"
137
+ set -o 2>/dev/null | awk '$2 == "on" && $1 != "monitor" {print "set -o " $1}' | head -n 1000 >> "$SNAPSHOT_FILE"
134
138
  echo "shopt -s expand_aliases" >> "$SNAPSHOT_FILE"
135
139
  `;
136
140
  const aliasDump = `
@@ -1,30 +1,30 @@
1
1
  {
2
- "version": "0.1.10",
2
+ "version": "0.1.11",
3
3
  "_comment": "Synced from immutable spawn-v release assets.",
4
4
  "assets": {
5
5
  "darwin-arm64": {
6
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-darwin-arm64",
7
- "sha256": "8c20f4096c08a5159c62b28ada8aaed9d49d18ab21faaa1631e3eadadaadc91c"
6
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-darwin-arm64",
7
+ "sha256": "6f1a7e6597dcc8785d12c5a13c42388110d9f4a2e5378ca2773172adbf50de60"
8
8
  },
9
9
  "darwin-x64": {
10
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-darwin-x64",
11
- "sha256": "a35c66bf5c0121f1202ea69cc22a5804b0587a37929ebf3b66e08ec2b18b6b86"
10
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-darwin-x64",
11
+ "sha256": "ff755eb9848cb3ce90f3df24136bb84a52f9ff6c67002c4d764beb6d7906e6b2"
12
12
  },
13
13
  "linux-arm64": {
14
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-linux-arm64",
15
- "sha256": "463bc8bdb6abe49fd99fee53397365417cb6613ebabbfcd332e6b62fa3e79c37"
14
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-linux-arm64",
15
+ "sha256": "3bc0fccde46537046d498a2b953aa92ef4afacc95c96c563ce1d6fb92d74e593"
16
16
  },
17
17
  "linux-x64": {
18
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-linux-x64",
19
- "sha256": "329a940ae3affc9fa5bcc9e6a48b2f7c8a783900f2de3a952de2422aa60413f5"
18
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-linux-x64",
19
+ "sha256": "16dc4e3bf6636335c1325c88bbb5245529731525953466e761d363897b59aac7"
20
20
  },
21
21
  "win32-arm64": {
22
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-win32-arm64.exe",
23
- "sha256": "a1719d2ea8f87707cb515a5e8e055ed56d6b48f3b717346b0a4ee63d40e562df"
22
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-win32-arm64.exe",
23
+ "sha256": "c2fb5499d3d44405e27c79f8db65694299d61ddf029f466df68549fc2f2c5f5c"
24
24
  },
25
25
  "win32-x64": {
26
- "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.10/mixdog-spawn-win32-x64.exe",
27
- "sha256": "ac65bd416d03f9c4e9e65eb04c6455a948551e726f74bd73bb32acbf4bc66d93"
26
+ "url": "https://github.com/tribgames/mixdog/releases/download/spawn-v0.1.11/mixdog-spawn-win32-x64.exe",
27
+ "sha256": "50531a9f033dc22eff3ec5b2a9d10441ffdfac0e50d9acf9047e4f302e5d70d9"
28
28
  }
29
29
  }
30
30
  }
@@ -13,7 +13,10 @@ const ATTR_BOUND = 4096;
13
13
  const ATTRS = `[^<>]{0,${ATTR_BOUND}}`;
14
14
  const A = ATTRS;
15
15
  // A regex built from a raw template; the interpolations are pattern text.
16
- const re = (flags = '') => (strings, ...values) => new RegExp(String.raw(strings, ...values), flags);
16
+ const re =
17
+ (flags = '') =>
18
+ (strings, ...values) =>
19
+ new RegExp(String.raw(strings, ...values), flags);
17
20
  // The patterns the per-cell / per-run loops use are built once.
18
21
  const V_RE = re()`<v(?:\s${A})?>([^<]*)</v>`;
19
22
  const T_RUN_RE = re('g')`<t(?:\s${A})?>([^<]*)</t>`;
@@ -384,16 +387,12 @@ function relationshipList(xml) {
384
387
 
385
388
  function sharedStringTable(xml) {
386
389
  if (!xml) return [];
387
- return [...scanElements(xml, 'si')]
388
- .filter((item) => item.inner !== null)
389
- .map((item) => textRuns(item.inner));
390
+ return [...scanElements(xml, 'si')].filter((item) => item.inner !== null).map((item) => textRuns(item.inner));
390
391
  }
391
392
 
392
393
  // Concatenated <t> character data of one fragment.
393
394
  function textRuns(fragment) {
394
- return [...fragment.matchAll(T_RUN_RE)]
395
- .map(([, text]) => decodeXmlEntities(text))
396
- .join('');
395
+ return [...fragment.matchAll(T_RUN_RE)].map(([, text]) => decodeXmlEntities(text)).join('');
397
396
  }
398
397
 
399
398
  // Excel stores a date as a day count and a percentage as a fraction, so a
@@ -421,7 +420,10 @@ function cellNumberFormats(xml) {
421
420
  if (!xml) return [];
422
421
  const custom = new Map(
423
422
  [...xml.matchAll(re('g')`<numFmt\b(${A}?)/?>`)]
424
- .map(([, attributes]) => [/\bnumFmtId="(\d+)"/.exec(attributes)?.[1], /\bformatCode="([^"<>]*)"/.exec(attributes)?.[1]])
423
+ .map(([, attributes]) => [
424
+ /\bnumFmtId="(\d+)"/.exec(attributes)?.[1],
425
+ /\bformatCode="([^"<>]*)"/.exec(attributes)?.[1],
426
+ ])
425
427
  .filter(([id, code]) => id !== undefined && code !== undefined)
426
428
  .map(([id, code]) => [Number(id), decodeXmlEntities(code)])
427
429
  );
@@ -629,7 +631,7 @@ function relatedPart(target, ownerPart) {
629
631
  const base = String(ownerPart).replace(/\/[^/]+$/, '');
630
632
  const value = String(target).replace(/^\//, '');
631
633
  if (!value.startsWith('..')) return value.startsWith('xl/') ? value : `${base}/${value}`;
632
- return value.replace(/^\.\.\//, `${base.replace(/\/[^/]+$/, '')}/`);
634
+ return value.replace(/^\.\.\//, () => `${base.replace(/\/[^/]+$/, '')}/`);
633
635
  }
634
636
 
635
637
  // A sheet's chart is its message, and it lives outside the cell grid: read as
@@ -53,6 +53,30 @@ export async function finishBrowserTurn(sessionId, turnId, { aborted = false } =
53
53
  }
54
54
  }
55
55
 
56
+ /** Runtime-only: fill the secret the user handed the desktop for `requestId`
57
+ * into the page's focused password field through the host's isolated-world
58
+ * fill. The value never passes through here; the host answers only
59
+ * 'provided' or 'skipped'. */
60
+ export async function browserSecretInput(sessionId, requestId, { signal } = {}) {
61
+ const discovery = readBridgeDiscovery(DISCOVERY_FILE);
62
+ if (!discovery || !sessionId || !requestId) return 'skipped';
63
+ try {
64
+ const result = await requestBridge(
65
+ discovery,
66
+ JSON.stringify({ action: 'secret_input', session_id: String(sessionId), request_id: String(requestId) }),
67
+ signal
68
+ ? AbortSignal.any([AbortSignal.timeout(REQUEST_TIMEOUT_MS), signal])
69
+ : AbortSignal.timeout(REQUEST_TIMEOUT_MS),
70
+ { sessionId, action: 'secret_input' }
71
+ );
72
+ return result.status === 200 && result.body?.ok !== false && result.body?.value?.text === 'provided'
73
+ ? 'provided'
74
+ : 'skipped';
75
+ } catch {
76
+ return 'skipped';
77
+ }
78
+ }
79
+
56
80
  const BRIDGE_UNAVAILABLE_MESSAGE = 'browser use is unavailable; open the Mixdog desktop app and enable Browser Use';
57
81
 
58
82
  /** Sync gate for the session tool surface (featureDisallowedTools). */
@@ -12,6 +12,7 @@ import {
12
12
  markFired,
13
13
  markDone,
14
14
  markScheduleFailure,
15
+ markScheduleSkipped,
15
16
  markScheduleSuccess,
16
17
  setDeferred,
17
18
  setNextFire,
@@ -61,6 +62,11 @@ function isCronExpression(time) {
61
62
  return false;
62
63
  }
63
64
  }
65
+ // True when this fire (started at `since`) ended in an overlap skip.
66
+ function skippedSince(schedule, since) {
67
+ const at = schedule.lastSkippedAt ? new Date(schedule.lastSkippedAt).getTime() : 0;
68
+ return at >= since.getTime();
69
+ }
64
70
  // Records a one-shot fire that did not complete; the entry stays pending.
65
71
  async function markOneShotFailed(schedule) {
66
72
  schedule.lastFailedAt = new Date().toISOString();
@@ -407,6 +413,8 @@ ${prompt}`;
407
413
  schedule.lastFiredAt = startedAt.toISOString();
408
414
  this.lastFired.set(schedule.name, startedAt.toISOString());
409
415
  const fired = await this.fireTimed(schedule, type, { awaitDispatch: true });
416
+ // An overlap skip is recorded as a skip, never as a failed run.
417
+ if (!fired && skippedSince(schedule, startedAt)) return;
410
418
  if (!fired) {
411
419
  schedule.lastFailedAt = new Date().toISOString();
412
420
  await markScheduleFailure(schedule.name, new Date());
@@ -498,7 +506,7 @@ ${prompt}`;
498
506
  return;
499
507
  }
500
508
  // Mark done ONLY after a successful fire. A false/throwing fireTimed
501
- // (missing prompt/model, running guard, dispatch error) must leave the
509
+ // (missing prompt, running guard, paused owner, dispatch error) must leave the
502
510
  // one-shot pending so a reload/restart can retry it — never retire it.
503
511
  try {
504
512
  // awaitDispatch: for a one-shot we must wait for the actual LLM/relay
@@ -506,6 +514,10 @@ ${prompt}`;
506
514
  // contract) would retire the entry even when schedulerLlm() later
507
515
  // rejects. Only a resolved dispatch counts as a real fire here.
508
516
  const fired = await this.fireTimed(schedule, type, { awaitDispatch: true });
517
+ if (!fired && skippedSince(schedule, now)) {
518
+ logSchedule(`one-shot "${schedule.name}" skipped (previous run in progress) — leaving pending for retry\n`);
519
+ return;
520
+ }
509
521
  if (!fired) {
510
522
  await markOneShotFailed(schedule);
511
523
  logSchedule(`one-shot "${schedule.name}" did not fire (skipped/guarded) — leaving pending for retry\n`);
@@ -625,20 +637,11 @@ ${prompt}`;
625
637
  async fireTimedPrompt(schedule, type, prompt, { awaitDispatch = false } = {}) {
626
638
  logSchedule(`firing ${schedule.name} (${type})\n`);
627
639
  if (this.running.has(schedule.name)) {
628
- // Not silent anymore: overlap skips were invisible and read as the
629
- // scheduler "ignoring" a fire.
630
- logSchedule(`${schedule.name}: skipped — previous run still in progress\n`);
640
+ this.recordSkip(schedule);
631
641
  return false;
632
642
  }
633
643
  if (type === 'interactive' && this.injectIntoLead(schedule, type, prompt)) return true;
634
644
  this.running.add(schedule.name);
635
- const presetId = schedule.model;
636
- if (!presetId) {
637
- this.running.delete(schedule.name);
638
- logSchedule(`${schedule.name}: missing required "model" in schedule config — dispatch rejected\n`);
639
- this.notifyFailure(schedule, 'missing "model" in schedule config — dispatch rejected');
640
- return false;
641
- }
642
645
  // Cron (recurring) keeps the fire-and-forget contract: return truthy now
643
646
  // and swallow async failures. A one-shot (awaitDispatch) instead awaits
644
647
  // the dispatch so a rejected run propagates and the caller leaves the
@@ -676,8 +679,22 @@ ${prompt}`;
676
679
  ? this.scheduleRunner(schedule, { prompt })
677
680
  : Promise.reject(new Error('schedule runner not configured'));
678
681
  return run
679
- .then(({ result }) => {
682
+ .then((outcome) => {
680
683
  this.running.delete(schedule.name);
684
+ // The run path found this schedule's previous run still going (a
685
+ // "run now" from the app) or backing off after failures in a row; it
686
+ // already persisted the skip.
687
+ if (outcome?.skipped) {
688
+ schedule.lastSkippedAt = new Date().toISOString();
689
+ const why =
690
+ outcome.reason === 'backoff'
691
+ ? 'backing off after failures'
692
+ : outcome.reason === 'owner'
693
+ ? `paused: ${outcome.message}`
694
+ : 'previous run still in progress';
695
+ logSchedule(`${schedule.name}: skipped — ${why}\n`);
696
+ return false;
697
+ }
681
698
  logSchedule(`${schedule.name} done\n`);
682
699
  return true;
683
700
  })
@@ -693,6 +710,17 @@ ${prompt}`;
693
710
  });
694
711
  }
695
712
  // ── Helpers ─────────────────────────────────────────────────────────
713
+ /** Overlap skip: the previous run of this schedule is still in progress.
714
+ * Logged and persisted (last_skipped_at) so a skipped fire is never
715
+ * invisible; `persistSkip` is injectable for tests. */
716
+ recordSkip(schedule) {
717
+ const at = new Date();
718
+ schedule.lastSkippedAt = at.toISOString();
719
+ logSchedule(`${schedule.name}: skipped — previous run still in progress\n`);
720
+ void Promise.resolve()
721
+ .then(() => (this.persistSkip ?? markScheduleSkipped)(schedule.name, at))
722
+ .catch((err) => process.stderr.write(`mixdog scheduler: ${schedule.name} skip record failed: ${err}\n`));
723
+ }
696
724
  /** Failure notice: logged so scheduled fires never fail silently. */
697
725
  notifyFailure(schedule, reason) {
698
726
  logSchedule(`schedule "${schedule?.name}": ${reason}\n`);
@@ -14,6 +14,7 @@
14
14
  import { ensurePgInstance, withSchemaBootstrapLock } from '../../memory/lib/pg/adapter.mjs';
15
15
  import { createPgSchemaDb } from '../../shared/pg-schema-db.mjs';
16
16
  import { resolvePluginData } from '../../shared/plugin-paths.mjs';
17
+ import { ROUTINE_LIMITS } from '../../shared/routine-limits.mjs';
17
18
 
18
19
  const SCHEMA = 'scheduler';
19
20
 
@@ -54,6 +55,18 @@ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_scheduled_at times
54
55
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_started_at timestamptz;
55
56
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_success_at timestamptz;
56
57
  ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_failed_at timestamptz;
58
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS last_skipped_at timestamptz;
59
+ -- The agent a schedule belongs to (its results land in that agent's home
60
+ -- thread). Rows from before ownership existed fill with Mixy ('lead').
61
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS owner_agent_id text NOT NULL DEFAULT 'lead';
62
+ -- Legacy columns model, workflow and run_as_agent are no longer read or written (kept, not dropped, to avoid a risky migration).
63
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS run_as_agent boolean NOT NULL DEFAULT false;
64
+ -- Failed runs in a row: a success or switching it back on resets it; at
65
+ -- ROUTINE_LIMITS.pauseAfterFailures the schedule switches itself off.
66
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS consecutive_failures integer NOT NULL DEFAULT 0;
67
+ -- Results of finished runs not yet queued for the owner's home thread, by run
68
+ -- id ({ state: 'pending' | 'failed', error?, at, result }).
69
+ ALTER TABLE scheduler.schedules ADD COLUMN IF NOT EXISTS result_delivery jsonb;
57
70
  `;
58
71
 
59
72
  const getDb = createPgSchemaDb({
@@ -78,9 +91,7 @@ function rowToDef(row) {
78
91
  timezone: row.timezone,
79
92
  target: row.target,
80
93
  channelId: row.channel_id,
81
- model: row.model,
82
94
  cwd: row.cwd,
83
- workflow: row.workflow,
84
95
  attachments: row.attachments || null,
85
96
  delivery: row.delivery || null,
86
97
  prompt: row.prompt,
@@ -91,6 +102,10 @@ function rowToDef(row) {
91
102
  lastStartedAt: row.last_started_at,
92
103
  lastSuccessAt: row.last_success_at,
93
104
  lastFailedAt: row.last_failed_at,
105
+ lastSkippedAt: row.last_skipped_at,
106
+ ownerAgentId: row.owner_agent_id || 'lead',
107
+ consecutiveFailures: Number(row.consecutive_failures) || 0,
108
+ resultDelivery: row.result_delivery || null,
94
109
  nextFireAt: row.next_fire_at,
95
110
  deferredUntil: row.deferred_until,
96
111
  skippedUntil: row.skipped_until,
@@ -100,7 +115,7 @@ function rowToDef(row) {
100
115
  }
101
116
 
102
117
  const COLS =
103
- 'name, description, when_at, when_cron, timezone, target, channel_id, model, cwd, workflow, attachments, delivery, prompt, enabled, status, last_fired_at, last_scheduled_at, last_started_at, last_success_at, last_failed_at, next_fire_at, deferred_until, skipped_until, created_at, updated_at';
118
+ 'name, description, when_at, when_cron, timezone, target, channel_id, 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';
104
119
 
105
120
  // ---------------------------------------------------------------------------
106
121
  // Public API
@@ -134,30 +149,28 @@ export async function upsertSchedule(def, { dataDir } = {}) {
134
149
  def.timezone ?? null,
135
150
  def.target,
136
151
  def.channelId ?? null,
137
- def.model ?? null,
138
152
  def.cwd ?? null,
139
- def.workflow ?? null,
140
153
  def.attachments ? JSON.stringify(def.attachments) : null,
141
154
  def.delivery ?? null,
142
155
  def.prompt,
143
156
  def.enabled ?? true,
144
157
  def.status ?? 'active',
145
158
  def.nextFireAt ?? null,
159
+ def.ownerAgentId || 'lead',
146
160
  ];
147
161
  const { rows } = await db.query(
148
162
  `INSERT INTO scheduler.schedules
149
- (name, description, when_at, when_cron, timezone, target, channel_id, model, cwd, workflow, attachments, delivery, prompt, enabled, status, next_fire_at)
150
- VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16)
163
+ (name, description, when_at, when_cron, timezone, target, channel_id, cwd, attachments, delivery, prompt, enabled, status, next_fire_at, owner_agent_id)
164
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15)
151
165
  ON CONFLICT (name) DO UPDATE SET
166
+ owner_agent_id = EXCLUDED.owner_agent_id,
152
167
  description = EXCLUDED.description,
153
168
  when_at = EXCLUDED.when_at,
154
169
  when_cron = EXCLUDED.when_cron,
155
170
  timezone = EXCLUDED.timezone,
156
171
  target = EXCLUDED.target,
157
172
  channel_id = EXCLUDED.channel_id,
158
- model = EXCLUDED.model,
159
173
  cwd = EXCLUDED.cwd,
160
- workflow = EXCLUDED.workflow,
161
174
  attachments = EXCLUDED.attachments,
162
175
  delivery = EXCLUDED.delivery,
163
176
  prompt = EXCLUDED.prompt,
@@ -173,6 +186,8 @@ export async function upsertSchedule(def, { dataDir } = {}) {
173
186
  last_started_at = NULL,
174
187
  last_success_at = NULL,
175
188
  last_failed_at = NULL,
189
+ last_skipped_at = NULL,
190
+ consecutive_failures = 0,
176
191
  updated_at = now()
177
192
  RETURNING ${COLS}`,
178
193
  params
@@ -198,8 +213,16 @@ async function updateScheduleColumn(name, column, value, dataDir) {
198
213
  return rowToDef(rows[0]);
199
214
  }
200
215
 
216
+ /** Switching a schedule back on clears its failure streak. */
201
217
  export async function setEnabled(name, enabled, { dataDir } = {}) {
202
- return updateScheduleColumn(name, 'enabled', !!enabled, dataDir);
218
+ const db = await getDb(dataDir);
219
+ const { rows } = await db.query(
220
+ `UPDATE scheduler.schedules
221
+ SET enabled = $2, consecutive_failures = CASE WHEN $2 THEN 0 ELSE consecutive_failures END, updated_at = now()
222
+ WHERE name = $1 RETURNING ${COLS}`,
223
+ [name, !!enabled]
224
+ );
225
+ return rowToDef(rows[0]);
203
226
  }
204
227
 
205
228
  export async function markFired(name, ts = new Date(), { dataDir } = {}) {
@@ -240,11 +263,47 @@ export async function claimScheduleRun(name, scheduledAt, startedAt, nextFireAt,
240
263
  }
241
264
 
242
265
  export async function markScheduleSuccess(name, ts = new Date(), { dataDir } = {}) {
243
- return updateScheduleColumn(name, 'last_success_at', ts, dataDir);
266
+ const db = await getDb(dataDir);
267
+ const { rows } = await db.query(
268
+ `UPDATE scheduler.schedules SET last_success_at = $2, consecutive_failures = 0, updated_at = now()
269
+ WHERE name = $1 RETURNING ${COLS}`,
270
+ [name, ts]
271
+ );
272
+ return rowToDef(rows[0]);
244
273
  }
245
274
 
275
+ /** One more failure in a row; the one reaching the pause limit switches the
276
+ * schedule off (claimScheduleRun admits only enabled rows). */
246
277
  export async function markScheduleFailure(name, ts = new Date(), { dataDir } = {}) {
247
- return updateScheduleColumn(name, 'last_failed_at', ts, dataDir);
278
+ const db = await getDb(dataDir);
279
+ const { rows } = await db.query(
280
+ `UPDATE scheduler.schedules
281
+ SET last_failed_at = $2, consecutive_failures = consecutive_failures + 1,
282
+ enabled = enabled AND consecutive_failures + 1 < $3, updated_at = now()
283
+ WHERE name = $1 RETURNING ${COLS}`,
284
+ [name, ts, ROUTINE_LIMITS.pauseAfterFailures]
285
+ );
286
+ return rowToDef(rows[0]);
287
+ }
288
+
289
+ /** Set (`entry`) or clear (null) the undelivered result of run `runId`. */
290
+ export async function recordScheduleDelivery(name, runId, entry, { dataDir } = {}) {
291
+ const db = await getDb(dataDir);
292
+ await db.query(
293
+ entry
294
+ ? `UPDATE scheduler.schedules
295
+ SET result_delivery = COALESCE(result_delivery, '{}'::jsonb) || jsonb_build_object($2::text, $3::jsonb)
296
+ WHERE name = $1`
297
+ : `UPDATE scheduler.schedules
298
+ SET result_delivery = NULLIF(COALESCE(result_delivery, '{}'::jsonb) - $2::text, '{}'::jsonb)
299
+ WHERE name = $1`,
300
+ entry ? [name, runId, JSON.stringify(entry)] : [name, runId]
301
+ );
302
+ }
303
+
304
+ /** A fire skipped because the previous run of the same schedule still runs. */
305
+ export async function markScheduleSkipped(name, ts = new Date(), { dataDir } = {}) {
306
+ return updateScheduleColumn(name, 'last_skipped_at', ts, dataDir);
248
307
  }
249
308
 
250
309
  export async function markDone(name, { dataDir } = {}) {
@@ -1,9 +1,14 @@
1
1
  import { logWebhook } from './log.mjs';
2
- import { updateDeliveryStatus } from '../webhooks-db.mjs';
2
+ import { recordEndpointEvent, updateDeliveryStatus } from '../webhooks-db.mjs';
3
3
  import { UNTRUSTED_MARKER, scrubFenceMarker } from './untrusted-fence.mjs';
4
4
 
5
5
  const DISPATCH_TIMEOUT_MS = 10 * 60 * 1000;
6
6
 
7
+ /** The headers a run's prompt shows (and a stored event keeps). */
8
+ function promptHeaders(headers) {
9
+ return Object.fromEntries(Object.entries(headers || {}).filter(([k]) => k.startsWith('x-') || k === 'content-type'));
10
+ }
11
+
7
12
  function buildFencedPayload(body, headers) {
8
13
  // Trust boundary: webhook body + headers are external input and must be
9
14
  // treated as DATA, never instructions. The directive line gives the
@@ -11,8 +16,7 @@ function buildFencedPayload(body, headers) {
11
16
  const _UNTRUSTED = UNTRUSTED_MARKER;
12
17
  const payload = scrubFenceMarker(JSON.stringify(body, null, 2));
13
18
  const headersSummary = scrubFenceMarker(
14
- Object.entries(headers)
15
- .filter(([k]) => k.startsWith('x-') || k === 'content-type')
19
+ Object.entries(promptHeaders(headers))
16
20
  .map(([k, v]) => `${k}: ${v}`)
17
21
  .join('\n')
18
22
  );
@@ -35,6 +39,14 @@ function webhookRunPrompt(instructions, body, headers) {
35
39
  return { prompt: `${trusted}\n\n${buildFencedPayload(body, headers)}`, skillSelection: trusted };
36
40
  }
37
41
 
42
+ /** A short label of the event for the routine marker in the bot's chat
43
+ * (display only, never model-visible): the sender's event type and action. */
44
+ function webhookEventSummary(headers, body) {
45
+ const field = (value) => (typeof value === 'string' ? value.trim() : '');
46
+ const type = field(headers?.['x-github-event']) || field(headers?.['sentry-hook-resource']) || field(body?.type);
47
+ return [type, field(body?.action)].filter(Boolean).join(' · ').slice(0, 120);
48
+ }
49
+
38
50
  function trackDispatch({ name, deliveryId, dispatchP, controller }) {
39
51
  let settled = false;
40
52
  const timeoutHandle = setTimeout(
@@ -63,7 +75,10 @@ function trackDispatch({ name, deliveryId, dispatchP, controller }) {
63
75
  { once: true }
64
76
  );
65
77
  dispatchP.then(
66
- () => finish('done', {}, `${name}: webhook session run dispatched (id=${deliveryId})`),
78
+ (run) =>
79
+ run?.skipped
80
+ ? finish('skipped', {}, `${name}: previous run still in progress; skipped (id=${deliveryId})`)
81
+ : finish('done', {}, `${name}: webhook session run dispatched (id=${deliveryId})`),
67
82
  (err) =>
68
83
  finish(
69
84
  'failed',
@@ -73,8 +88,8 @@ function trackDispatch({ name, deliveryId, dispatchP, controller }) {
73
88
  );
74
89
  }
75
90
 
76
- function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
77
- async function dispatchSessionRun(name, model, fullPrompt, headers, deliveryId, res, extra = {}) {
91
+ function createWebhookSessionDispatcher({ getBridgeDispatch }) {
92
+ async function dispatchSessionRun(name, fullPrompt, deliveryId, res, extra = {}) {
78
93
  await updateDeliveryStatus(name, deliveryId, 'processing');
79
94
  // Session dispatch must not be allowed to hang forever — without a
80
95
  // ceiling a stuck LLM call leaves the delivery in `processing`
@@ -88,13 +103,10 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
88
103
  const controller = new AbortController();
89
104
  const dispatchP = Promise.resolve(
90
105
  getBridgeDispatch()({
91
- model: model || null,
92
106
  prompt: fullPrompt,
93
107
  signal: controller.signal,
94
- // Endpoint-scoped project/workflow (New-task parity): the webhook row's
95
- // cwd/workflow define the created session, not the worker's global cwd.
96
- cwd: extra.cwd || getConfig()?.cwd || null,
97
- workflow: extra.workflow || null,
108
+ // The endpoint's project folder, named to the bot in the turn.
109
+ cwd: extra.cwd || null,
98
110
  attachments: extra.attachments || null,
99
111
  delivery: extra.delivery || null,
100
112
  // Trusted skill-selection input: only the endpoint-authored
@@ -104,8 +116,10 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
104
116
  context: {
105
117
  source: 'webhook',
106
118
  endpoint: name,
119
+ // The bot whose chat the run is a turn in.
120
+ owner: extra.owner || null,
107
121
  deliveryId,
108
- event: headers['x-github-event'] || null,
122
+ event: extra.event || null,
109
123
  },
110
124
  })
111
125
  );
@@ -116,12 +130,18 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
116
130
 
117
131
  async function dispatchEndpoint({ name, body, headers, res, deliveryId, endpoint }) {
118
132
  try {
119
- const { model, instructions, cwd, workflow, attachments, delivery } = endpoint;
133
+ const { instructions, cwd, attachments, delivery, ownerAgentId } = endpoint;
120
134
  const run = webhookRunPrompt(instructions, body, headers);
121
135
  if (!getBridgeDispatch()) throw new Error(`[webhook] session dispatch requires bridgeDispatch`);
122
- await dispatchSessionRun(name, model || null, run.prompt, headers, deliveryId, res, {
136
+ // Kept for the app's "Run now", which re-runs the latest event. Losing
137
+ // the record never blocks the delivery itself.
138
+ await recordEndpointEvent(name, { body, headers: promptHeaders(headers) }).catch((e) =>
139
+ logWebhook(`${name}: event record failed: ${e?.message || e}`)
140
+ );
141
+ await dispatchSessionRun(name, run.prompt, deliveryId, res, {
142
+ owner: ownerAgentId || null,
123
143
  cwd: cwd || null,
124
- workflow: workflow || null,
144
+ event: webhookEventSummary(headers, body),
125
145
  attachments: attachments || null,
126
146
  delivery: delivery || null,
127
147
  skillSelection: run.skillSelection,
@@ -137,4 +157,4 @@ function createWebhookSessionDispatcher({ getConfig, getBridgeDispatch }) {
137
157
  return { dispatchEndpoint };
138
158
  }
139
159
 
140
- export { buildFencedPayload, createWebhookSessionDispatcher, webhookRunPrompt };
160
+ export { buildFencedPayload, createWebhookSessionDispatcher, promptHeaders, webhookEventSummary, webhookRunPrompt };
@@ -23,7 +23,6 @@ class WebhookServer {
23
23
  },
24
24
  });
25
25
  this.sessionDispatcher = createWebhookSessionDispatcher({
26
- getConfig: () => this.config,
27
26
  getBridgeDispatch: () => this.bridgeDispatch,
28
27
  });
29
28
  this.requestHandler = createWebhookRequestHandler({