elanous 0.2.10 → 0.2.11

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 (584) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -1,14 +1,12 @@
1
1
  // Archon-port T2.1 (2026-05-08) — DAG executor.
2
2
  //
3
- // Sequential topological execution. Each ready node runs to completion
4
- // before the next is dispatched. Parallelism is intentionally deferred
5
- // (Archon's executor.ts is 800+ LOC partly because of parallel
6
- // dispatch + fan-out — elanous's MVP runs strictly sequential).
3
+ // Ready nodes overlap up to workflow.concurrency (default 1 = one at a time); lifecycle events retain
4
+ // their wire shape and node_done follows actual completion order.
5
+ // Failed nodes may route their output to an on_error handler. Subworkflow nodes invoke the same executor
6
+ // recursively, with an independent run id and full child execution.
7
7
  //
8
- // Yields events as it goes — caller (CLI / SSE / tests) decides
9
- // rendering. Stops on the first node that fails AND has no
10
- // `trigger_rule: all_done` consumer (Archon parity: `all_done`
11
- // downstream nodes always run regardless of upstream ok).
8
+ // Yields events as it goes — caller (CLI / SSE / tests) decides rendering. A failure with no on_error
9
+ // route and no `trigger_rule: all_done` consumer ends the run (Archon parity).
12
10
 
13
11
  import { mkdirSync, writeFileSync } from 'fs';
14
12
  import { join } from 'path';
@@ -32,6 +30,7 @@ import {
32
30
  isPromptNode,
33
31
  isSetNode,
34
32
  isShowroomNode,
33
+ isSubworkflowNode,
35
34
  isTaskNode,
36
35
  isSkillNode,
37
36
  isSwitchNode,
@@ -61,6 +60,7 @@ import { executeFilterNode } from './nodes/filter.js';
61
60
  import { executeTemplateNode } from './nodes/template.js';
62
61
  import { executeHttpRequestNode } from './nodes/http.js';
63
62
  import { executeShowroomNode } from './nodes/showroom.js';
63
+ import { executeSubworkflowNode } from './nodes/subworkflow.js';
64
64
  import { executeScheduleTriggerNode, executeWebhookTriggerNode, executeDiscordTriggerNode, executeTelegramTriggerNode, executeManualTriggerNode, executeChatTriggerNode } from './nodes/triggers.js';
65
65
  import { executePluginKindNode, isPluginKindNode } from './plugin-kind-node.js';
66
66
  import type {
@@ -80,6 +80,7 @@ export async function* runWorkflow(
80
80
  deps: WorkflowDeps,
81
81
  ): AsyncGenerator<WorkflowEvent, Record<string, NodeOutput>, unknown> {
82
82
  const runId = opts.runId ?? generateRunId();
83
+ const mode = opts.mode ?? (opts.onlyNode ? 'only' : opts.fromNode ? 'from' : 'full');
83
84
  // Persistence (Caveat #4 follow-up): when `runDir` is set OR neither
84
85
  // override is supplied, we own the run dir and persist node outputs +
85
86
  // a final run.json. When only `artifactsDir` is overridden (the
@@ -97,10 +98,11 @@ export async function* runWorkflow(
97
98
  workflowName: opts.workflow.name,
98
99
  arguments: opts.arguments,
99
100
  startedAt,
101
+ mode,
100
102
  });
101
103
  }
102
104
 
103
- yield { type: 'workflow_start', workflow: opts.workflow.name, runId };
105
+ yield { type: 'workflow_start', workflow: opts.workflow.name, runId, mode };
104
106
 
105
107
  let order: string[];
106
108
  try {
@@ -110,14 +112,151 @@ export async function* runWorkflow(
110
112
  type: 'workflow_failed',
111
113
  error: err instanceof Error ? err.message : String(err),
112
114
  partial: {},
115
+ mode,
113
116
  };
114
117
  return {};
115
118
  }
116
119
 
117
- const outputs: Record<string, NodeOutput> = {};
118
120
  const nodeById = new Map(opts.workflow.nodes.map(n => [n.id, n] as const));
121
+ const startIndex = opts.fromNode ? order.indexOf(opts.fromNode) : 0;
122
+ const outputs: Record<string, NodeOutput> = {};
123
+ if (startIndex < 0 || (opts.onlyNode && !nodeById.has(opts.onlyNode))) {
124
+ yield { type: 'workflow_failed', error: 'unknown selected node', partial: outputs, mode };
125
+ return outputs;
126
+ }
127
+ if (opts.fromNode) {
128
+ const upstream = order.slice(0, startIndex);
129
+ if (!opts.previousOutputs || upstream.some(id => opts.previousOutputs?.[id] === undefined)) {
130
+ const error = 'source run missing upstream outputs';
131
+ if (shouldPersist && runDir) persistRunFinal(runDir, {
132
+ runId, workflowName: opts.workflow.name, arguments: opts.arguments,
133
+ startedAt, mode, ok: false, error, outputs, completedAt: Date.now(),
134
+ });
135
+ yield { type: 'workflow_failed', error, partial: outputs, mode };
136
+ return outputs;
137
+ }
138
+ for (const id of upstream) outputs[id] = opts.previousOutputs[id]!;
139
+ }
140
+ const selectedOrder = opts.onlyNode ? [opts.onlyNode] : order.slice(startIndex);
141
+
142
+ // Keep the original serial path for workflows that do not opt in. Its
143
+ // start/done/skip sequence and gate failures are part of the CLI/SSE contract.
144
+ if ((opts.workflow.concurrency ?? 1) > 1 || selectedOrder.some(id => nodeById.get(id)?.on_error || opts.workflow.nodes.some(n => n.on_error === id))) {
145
+ const concurrency = Math.max(1, Math.min(8, opts.workflow.concurrency ?? 1));
146
+ const pending = new Set(selectedOrder);
147
+ const settled = new Set(order.slice(0, startIndex));
148
+ const active = new Set<string>();
149
+ const completed: Array<{ id: string; result: NodeOutput }> = [];
150
+ let wakeCompletion: (() => void) | undefined;
151
+ let failure: string | undefined;
152
+
153
+ while (pending.size || active.size || completed.length) {
154
+ if (completed.length) {
155
+ const { id, result } = completed.shift()!;
156
+ active.delete(id);
157
+ settled.add(id);
158
+ outputs[id] = result;
159
+ if (shouldPersist && runDir) persistNodeOutput(runDir, id, result);
160
+ yield { type: 'node_done', nodeId: id, result, mode, ...(result.childRunId ? { childRunId: result.childRunId } : {}) };
161
+ if (!result.ok && !failure) {
162
+ const node = nodeById.get(id)!;
163
+ const handler = !opts.onlyNode && node.on_error && selectedOrder.includes(node.on_error) ? node.on_error : undefined;
164
+ const hasAllDone = selectedOrder.some(rid => pending.has(rid) && nodeById.get(rid)?.trigger_rule === 'all_done'
165
+ && dependsOnFailure(rid, id, nodeById));
166
+ if (!handler && !hasAllDone) {
167
+ failure = `node '${id}' failed: ${result.error ?? '(no error message)'}`;
168
+ }
169
+ }
170
+ continue;
171
+ }
172
+ for (const id of selectedOrder) {
173
+ if (completed.length || active.size >= concurrency || failure) break;
174
+ if (!pending.has(id)) continue;
175
+ const node = nodeById.get(id)!;
176
+ const sources = opts.onlyNode ? [] : opts.workflow.nodes.filter(n => n.on_error === id).map(n => n.id);
177
+ const prerequisites = [...(node.depends_on ?? []), ...sources];
178
+ if (!opts.onlyNode && !prerequisites.every(dep => settled.has(dep))) continue;
179
+ pending.delete(id);
180
+ const failedSources = sources.filter(source => outputs[source] && !outputs[source]!.ok);
181
+ if (sources.length && !failedSources.length) {
182
+ settled.add(id);
183
+ yield { type: 'node_skipped', nodeId: id, reason: 'on_error source did not fail', mode };
184
+ continue;
185
+ }
186
+ if (opts.dryRun && isTriggerVariant(node)) {
187
+ settled.add(id);
188
+ yield { type: 'node_skipped', nodeId: id, reason: 'dry-run', mode };
189
+ continue;
190
+ }
191
+ const skipReason = opts.onlyNode ? null : shouldSkip(node, outputs, failedSources);
192
+ if (skipReason) {
193
+ settled.add(id);
194
+ yield { type: 'node_skipped', nodeId: id, reason: skipReason, mode };
195
+ continue;
196
+ }
197
+ const toolPolicy: ToolPolicy = {
198
+ ...(node.allowed_tools ? { allow: node.allowed_tools } : {}),
199
+ ...(node.denied_tools ? { deny: node.denied_tools } : {}),
200
+ };
201
+ const ctx: NodeExecContext = {
202
+ arguments: opts.arguments, artifactsDir, outputs: { ...outputs },
203
+ resolvedProvider: node.provider ?? opts.workflow.provider,
204
+ resolvedModel: node.model ?? opts.workflow.model, toolPolicy,
205
+ ...(opts.signal !== undefined ? { signal: opts.signal } : {}),
206
+ ...(opts.screen !== undefined ? { screen: opts.screen } : {}),
207
+ ...(opts.onTokenChunk !== undefined
208
+ ? { onTokenChunk: (chunk: string) => opts.onTokenChunk!(id, chunk) } : {}),
209
+ };
210
+ let childRunId: string | undefined;
211
+ try { childRunId = isSubworkflowNode(node) && !(!opts.ignorePins && readWorkflowPin(opts.workflow.name, id) !== null) ? generateRunId() : undefined; }
212
+ catch { childRunId = undefined; }
213
+ yield { type: 'node_start', nodeId: id, nodeType: variantOf(node), mode, ...(childRunId ? { childRunId } : {}) };
214
+ active.add(id);
215
+ const finish = (result: NodeOutput) => {
216
+ completed.push({ id, result });
217
+ wakeCompletion?.();
218
+ wakeCompletion = undefined;
219
+ };
220
+ // W8 must-fix: a rejected node is still a completed (failed) node — otherwise it stays active and the run never ends.
221
+ void executeWorkflowNode(node, ctx, opts, deps, childRunId).then(finish,
222
+ (err: unknown) => finish({ ok: false, output: '', error: err instanceof Error ? err.message : String(err), durationMs: 0 }));
223
+ }
224
+ if (!active.size) {
225
+ if (!pending.size || failure) break;
226
+ failure = `workflow has unresolved nodes: ${[...pending].join(', ')}`;
227
+ break;
228
+ }
229
+ if (!completed.length) await new Promise<void>(resolve => { wakeCompletion = resolve; });
230
+ }
231
+ if (!failure) {
232
+ const unhandled = selectedOrder.find(id => {
233
+ const node = nodeById.get(id)!;
234
+ if (!outputs[id] || outputs[id]!.ok) return false;
235
+ const handledByError = !opts.onlyNode && node.on_error && selectedOrder.includes(node.on_error)
236
+ && outputs[node.on_error]?.ok === true;
237
+ const handledByAllDone = selectedOrder.some(next => nodeById.get(next)?.trigger_rule === 'all_done'
238
+ && dependsOnFailure(next, id, nodeById) && outputs[next]?.ok === true);
239
+ return !handledByError && !handledByAllDone;
240
+ });
241
+ if (unhandled) failure = `node '${unhandled}' failed: ${outputs[unhandled]!.error ?? '(no error message)'}`;
242
+ }
243
+ if (failure) {
244
+ if (shouldPersist && runDir) persistRunFinal(runDir, {
245
+ runId, workflowName: opts.workflow.name, arguments: opts.arguments,
246
+ startedAt, mode, ok: false, error: failure, outputs, completedAt: Date.now(),
247
+ });
248
+ yield { type: 'workflow_failed', error: failure, partial: outputs, mode };
249
+ return outputs;
250
+ }
251
+ if (shouldPersist && runDir) persistRunFinal(runDir, {
252
+ runId, workflowName: opts.workflow.name, arguments: opts.arguments,
253
+ startedAt, mode, ok: true, outputs, completedAt: Date.now(),
254
+ });
255
+ yield { type: 'workflow_done', outputs, mode };
256
+ return outputs;
257
+ }
119
258
 
120
- for (const id of order) {
259
+ for (const id of selectedOrder) {
121
260
  const node = nodeById.get(id);
122
261
  if (!node) continue;
123
262
 
@@ -128,13 +267,13 @@ export async function* runWorkflow(
128
267
  // usual; manualTrigger / chatTrigger are also covered by the
129
268
  // variant family check.
130
269
  if (opts.dryRun && isTriggerVariant(node)) {
131
- yield { type: 'node_skipped', nodeId: id, reason: 'dry-run' };
270
+ yield { type: 'node_skipped', nodeId: id, reason: 'dry-run', mode };
132
271
  continue;
133
272
  }
134
273
 
135
- const skipReason = shouldSkip(node, outputs);
274
+ const skipReason = opts.onlyNode ? null : shouldSkip(node, outputs);
136
275
  if (skipReason) {
137
- yield { type: 'node_skipped', nodeId: id, reason: skipReason };
276
+ yield { type: 'node_skipped', nodeId: id, reason: skipReason, mode };
138
277
  continue;
139
278
  }
140
279
 
@@ -163,18 +302,35 @@ export async function* runWorkflow(
163
302
  : {}),
164
303
  };
165
304
 
305
+ const hasPin = !opts.ignorePins && readWorkflowPin(opts.workflow.name, id) !== null;
306
+ if (mode === 'test' && (isExternalSideEffectNode(node) || Boolean(node.judgment))
307
+ && !hasPin) {
308
+ const blocked: NodeOutput = { ok: false, output: '', error: 'pin required', durationMs: 0 };
309
+ outputs[id] = blocked;
310
+ if (shouldPersist && runDir) persistNodeOutput(runDir, id, blocked);
311
+ yield { type: 'node_start', nodeId: id, nodeType: variantOf(node), mode };
312
+ yield { type: 'node_done', nodeId: id, result: blocked, mode };
313
+ if (shouldPersist && runDir) persistRunFinal(runDir, {
314
+ runId, workflowName: opts.workflow.name, arguments: opts.arguments,
315
+ startedAt, mode, ok: false, error: `node '${id}' failed: pin required`,
316
+ outputs, completedAt: Date.now(),
317
+ });
318
+ yield { type: 'workflow_failed', error: `node '${id}' failed: pin required`, partial: outputs, mode };
319
+ return outputs;
320
+ }
321
+
166
322
  // RFC #2161 Phase 3 — capability requirements gate. When a node
167
323
  // declares `requires`, verify the resolved (provider, model) pair
168
324
  // satisfies every clause before we burn an LLM/bash call. Phase 5
169
325
  // tightens this by layering Live Registry (apiKey/health) on top.
170
- if (node.requires) {
326
+ if (node.requires && !hasPin) {
171
327
  const reason = checkModelRequires(
172
328
  ctx.resolvedProvider,
173
329
  ctx.resolvedModel,
174
330
  node.requires,
175
331
  );
176
332
  if (reason) {
177
- yield { type: 'node_start', nodeId: id, nodeType: variantOf(node) };
333
+ yield { type: 'node_start', nodeId: id, nodeType: variantOf(node), mode };
178
334
  const startedAt = Date.now();
179
335
  const blocked: NodeOutput = {
180
336
  ok: false,
@@ -184,11 +340,11 @@ export async function* runWorkflow(
184
340
  };
185
341
  outputs[id] = blocked;
186
342
  if (shouldPersist && runDir) persistNodeOutput(runDir, id, blocked);
187
- yield { type: 'node_done', nodeId: id, result: blocked };
188
- const remaining = order.slice(order.indexOf(id) + 1);
343
+ yield { type: 'node_done', nodeId: id, result: blocked, mode };
344
+ const remaining = selectedOrder.slice(selectedOrder.indexOf(id) + 1);
189
345
  const hasAllDone = remaining.some((rid) => {
190
346
  const rn = nodeById.get(rid);
191
- return rn?.trigger_rule === 'all_done';
347
+ return rn?.trigger_rule === 'all_done' && dependsOnFailure(rid, id, nodeById);
192
348
  });
193
349
  if (!hasAllDone) {
194
350
  if (shouldPersist && runDir) {
@@ -197,6 +353,7 @@ export async function* runWorkflow(
197
353
  workflowName: opts.workflow.name,
198
354
  arguments: opts.arguments,
199
355
  startedAt,
356
+ mode,
200
357
  ok: false,
201
358
  error: `node '${id}' blocked: ${reason}`,
202
359
  outputs,
@@ -207,6 +364,7 @@ export async function* runWorkflow(
207
364
  type: 'workflow_failed',
208
365
  error: `node '${id}' blocked: ${reason}`,
209
366
  partial: outputs,
367
+ mode,
210
368
  };
211
369
  return outputs;
212
370
  }
@@ -214,14 +372,14 @@ export async function* runWorkflow(
214
372
  }
215
373
  }
216
374
 
217
- if (node.judgment) {
375
+ if (node.judgment && !hasPin) {
218
376
  const reason = validateJudgmentContract(node)
219
377
  ?? (node.observes?.includes('screen') && ctx.screen === undefined
220
378
  ? 'screen observation is not wired in this run'
221
379
  : null)
222
380
  ?? (!deps.runJudgment ? 'deps.runJudgment is not wired in this runtime' : null);
223
381
  if (reason) {
224
- yield { type: 'node_start', nodeId: id, nodeType: variantOf(node) };
382
+ yield { type: 'node_start', nodeId: id, nodeType: variantOf(node), mode };
225
383
  const nodeStartedAt = Date.now();
226
384
  const blocked: NodeOutput = {
227
385
  ok: false,
@@ -231,9 +389,10 @@ export async function* runWorkflow(
231
389
  };
232
390
  outputs[id] = blocked;
233
391
  if (shouldPersist && runDir) persistNodeOutput(runDir, id, blocked);
234
- yield { type: 'node_done', nodeId: id, result: blocked };
235
- const remaining = order.slice(order.indexOf(id) + 1);
236
- const hasAllDone = remaining.some((rid) => nodeById.get(rid)?.trigger_rule === 'all_done');
392
+ yield { type: 'node_done', nodeId: id, result: blocked, mode };
393
+ const remaining = selectedOrder.slice(selectedOrder.indexOf(id) + 1);
394
+ const hasAllDone = remaining.some((rid) => nodeById.get(rid)?.trigger_rule === 'all_done'
395
+ && dependsOnFailure(rid, id, nodeById));
237
396
  if (!hasAllDone) {
238
397
  if (shouldPersist && runDir) {
239
398
  persistRunFinal(runDir, {
@@ -241,6 +400,7 @@ export async function* runWorkflow(
241
400
  workflowName: opts.workflow.name,
242
401
  arguments: opts.arguments,
243
402
  startedAt,
403
+ mode,
244
404
  ok: false,
245
405
  error: `node '${id}' blocked: ${reason}`,
246
406
  outputs,
@@ -251,6 +411,7 @@ export async function* runWorkflow(
251
411
  type: 'workflow_failed',
252
412
  error: `node '${id}' blocked: ${reason}`,
253
413
  partial: outputs,
414
+ mode,
254
415
  };
255
416
  return outputs;
256
417
  }
@@ -258,23 +419,25 @@ export async function* runWorkflow(
258
419
  }
259
420
  }
260
421
 
261
- yield { type: 'node_start', nodeId: id, nodeType: variantOf(node) };
262
- const result = node.judgment
263
- ? await executeJudgmentNode(node, ctx, deps, opts.workflow.name, opts.judgmentContext)
264
- : await dispatchNode(node, ctx, deps, opts.workflow.name);
422
+ const childRunId = isSubworkflowNode(node) && !hasPin ? generateRunId() : undefined;
423
+ yield { type: 'node_start', nodeId: id, nodeType: variantOf(node), mode,
424
+ ...(childRunId ? { childRunId } : {}) };
425
+ const result = isSubworkflowNode(node) && childRunId
426
+ ? await executeSubworkflowNode(node, ctx, deps, opts, childRunId)
427
+ : node.judgment && !hasPin
428
+ ? await executeJudgmentNode(node, ctx, deps, opts.workflow.name, opts.judgmentContext)
429
+ : await dispatchNode(node, ctx, deps, opts.workflow.name, opts.ignorePins);
265
430
  outputs[id] = result;
266
431
  if (shouldPersist && runDir) persistNodeOutput(runDir, id, result);
267
- yield { type: 'node_done', nodeId: id, result };
432
+ yield { type: 'node_done', nodeId: id, result, mode,
433
+ ...(result.childRunId ? { childRunId: result.childRunId } : {}) };
268
434
 
269
- // Stop on the first failed node UNLESS some downstream node has
270
- // `trigger_rule: all_done` (which means it explicitly wants to
271
- // run after failures). Cheaper than a full DAG re-walk:
272
- // any-downstream-all_done in the remaining order.
435
+ // A failed node can continue only if a dependent all_done node will consume it.
273
436
  if (!result.ok) {
274
- const remaining = order.slice(order.indexOf(id) + 1);
437
+ const remaining = selectedOrder.slice(selectedOrder.indexOf(id) + 1);
275
438
  const hasAllDone = remaining.some(rid => {
276
439
  const rn = nodeById.get(rid);
277
- return rn?.trigger_rule === 'all_done';
440
+ return rn?.trigger_rule === 'all_done' && dependsOnFailure(rid, id, nodeById);
278
441
  });
279
442
  if (!hasAllDone) {
280
443
  if (shouldPersist && runDir) {
@@ -283,6 +446,7 @@ export async function* runWorkflow(
283
446
  workflowName: opts.workflow.name,
284
447
  arguments: opts.arguments,
285
448
  startedAt,
449
+ mode,
286
450
  ok: false,
287
451
  error: `node '${id}' failed: ${result.error ?? '(no error message)'}`,
288
452
  outputs,
@@ -293,27 +457,83 @@ export async function* runWorkflow(
293
457
  type: 'workflow_failed',
294
458
  error: `node '${id}' failed: ${result.error ?? '(no error message)'}`,
295
459
  partial: outputs,
460
+ mode,
296
461
  };
297
462
  return outputs;
298
463
  }
299
464
  }
300
465
  }
301
466
 
467
+ // W8 must-fix: the serial path re-checks failures at the end like the parallel path — a failure whose all_done
468
+ // consumer was skipped (e.g. `when: false`) must not end as workflow_done.
469
+ const unhandled = selectedOrder.find(id => {
470
+ const node = nodeById.get(id);
471
+ if (!node || !outputs[id] || outputs[id]!.ok) return false;
472
+ const handledByError = !opts.onlyNode && node.on_error && selectedOrder.includes(node.on_error)
473
+ && outputs[node.on_error]?.ok === true;
474
+ const handledByAllDone = selectedOrder.some(next => nodeById.get(next)?.trigger_rule === 'all_done'
475
+ && dependsOnFailure(next, id, nodeById) && outputs[next]?.ok === true);
476
+ return !handledByError && !handledByAllDone;
477
+ });
478
+ if (unhandled) {
479
+ const error = `node '${unhandled}' failed: ${outputs[unhandled]!.error ?? '(no error message)'}`;
480
+ if (shouldPersist && runDir) persistRunFinal(runDir, {
481
+ runId, workflowName: opts.workflow.name, arguments: opts.arguments,
482
+ startedAt, mode, ok: false, error, outputs, completedAt: Date.now(),
483
+ });
484
+ yield { type: 'workflow_failed', error, partial: outputs, mode };
485
+ return outputs;
486
+ }
487
+
302
488
  if (shouldPersist && runDir) {
303
489
  persistRunFinal(runDir, {
304
490
  runId,
305
491
  workflowName: opts.workflow.name,
306
492
  arguments: opts.arguments,
307
493
  startedAt,
494
+ mode,
308
495
  ok: true,
309
496
  outputs,
310
497
  completedAt: Date.now(),
311
498
  });
312
499
  }
313
- yield { type: 'workflow_done', outputs };
500
+ yield { type: 'workflow_done', outputs, mode };
314
501
  return outputs;
315
502
  }
316
503
 
504
+ async function executeWorkflowNode(
505
+ node: DagNode,
506
+ ctx: NodeExecContext,
507
+ opts: RunWorkflowOpts,
508
+ deps: WorkflowDeps,
509
+ childRunId?: string,
510
+ ): Promise<NodeOutput> {
511
+ const startedAt = Date.now();
512
+ // W8 must-fix: every step (pin read included) is inside the try — a throw becomes a failed result, never a stuck active node.
513
+ try {
514
+ const hasPin = !opts.ignorePins && readWorkflowPin(opts.workflow.name, node.id) !== null;
515
+ if (opts.mode === 'test' && (isExternalSideEffectNode(node) || Boolean(node.judgment)) && !hasPin) {
516
+ return { ok: false, output: '', error: 'pin required', durationMs: 0 };
517
+ }
518
+ if (node.requires && !hasPin) {
519
+ const reason = checkModelRequires(ctx.resolvedProvider, ctx.resolvedModel, node.requires);
520
+ if (reason) return { ok: false, output: '', error: `requires unmet — ${reason}`, durationMs: Date.now() - startedAt };
521
+ }
522
+ if (node.judgment && !hasPin) {
523
+ const reason = validateJudgmentContract(node)
524
+ ?? (node.observes?.includes('screen') && ctx.screen === undefined ? 'screen observation is not wired in this run' : null)
525
+ ?? (!deps.runJudgment ? 'deps.runJudgment is not wired in this runtime' : null);
526
+ if (reason) return { ok: false, output: '', error: `judgment contract unmet — ${reason}`, durationMs: Date.now() - startedAt };
527
+ }
528
+ if (isSubworkflowNode(node) && childRunId) return await executeSubworkflowNode(node, ctx, deps, opts, childRunId);
529
+ return node.judgment && !hasPin
530
+ ? await executeJudgmentNode(node, ctx, deps, opts.workflow.name, opts.judgmentContext)
531
+ : await dispatchNode(node, ctx, deps, opts.workflow.name, opts.ignorePins);
532
+ } catch (err) {
533
+ return { ok: false, output: '', error: err instanceof Error ? err.message : String(err), durationMs: Date.now() - startedAt };
534
+ }
535
+ }
536
+
317
537
  /** Simpler convenience: run-to-completion + collect events. */
318
538
  export async function runWorkflowToCompletion(
319
539
  opts: RunWorkflowOpts,
@@ -335,7 +555,13 @@ export async function runWorkflowToCompletion(
335
555
  return { outputs, events, ok };
336
556
  }
337
557
 
338
- function shouldSkip(node: DagNode, outputs: Record<string, NodeOutput>): string | null {
558
+ function dependsOnFailure(id: string, failedId: string, nodeById: Map<string, DagNode>, visited = new Set<string>()): boolean {
559
+ if (visited.has(id)) return false;
560
+ visited.add(id);
561
+ return (nodeById.get(id)?.depends_on ?? []).some(dep => dep === failedId || dependsOnFailure(dep, failedId, nodeById, visited));
562
+ }
563
+
564
+ function shouldSkip(node: DagNode, outputs: Record<string, NodeOutput>, errorSources: readonly string[] = []): string | null {
339
565
  // trigger_rule check on depends_on
340
566
  const deps = node.depends_on ?? [];
341
567
  if (deps.length > 0) {
@@ -343,9 +569,9 @@ function shouldSkip(node: DagNode, outputs: Record<string, NodeOutput>): string
343
569
  const depResults = deps.map(d => outputs[d]).filter((o): o is NodeOutput => !!o);
344
570
  if (rule === 'all_success') {
345
571
  if (depResults.length !== deps.length) return `dep missing (rule=all_success)`;
346
- if (depResults.some(r => !r.ok)) return `dep failed (rule=all_success)`;
572
+ if (deps.some(d => !outputs[d]?.ok && !errorSources.includes(d))) return `dep failed (rule=all_success)`;
347
573
  } else if (rule === 'one_success') {
348
- if (!depResults.some(r => r.ok)) return `no successful dep (rule=one_success)`;
574
+ if (!depResults.some(r => r.ok) && !errorSources.length) return `no successful dep (rule=one_success)`;
349
575
  } else if (rule === 'all_done') {
350
576
  if (depResults.length !== deps.length) return `dep not finished (rule=all_done)`;
351
577
  }
@@ -430,13 +656,14 @@ async function dispatchNode(
430
656
  ctx: NodeExecContext,
431
657
  deps: WorkflowDeps,
432
658
  workflowName: string,
659
+ ignorePins = false,
433
660
  ): Promise<NodeOutput> {
434
661
  // M4-4.2 (FU8 PR #1 · 2026-05-12) — pin executor seam. When a pin
435
662
  // exists for `<workflowName>/<node.id>` in the `.pins.json` side-
436
663
  // file, return the pinned value as the node output and skip the
437
664
  // real LLM / HTTP / bash call. Lets workflow authors freeze a
438
665
  // specific node's response for fast iteration on downstream nodes.
439
- const pinned = readWorkflowPin(workflowName, node.id);
666
+ const pinned = ignorePins ? null : readWorkflowPin(workflowName, node.id);
440
667
  if (pinned !== null) {
441
668
  const output = pinned.value;
442
669
  // Fire the same 3-sink emit fan-out as the dispatch reference
@@ -464,7 +691,7 @@ async function dispatchNode(
464
691
  } catch { /* best-effort */ }
465
692
  return { ok: true, output, durationMs: 0 };
466
693
  }
467
- // Plugin kinds are dispatched before the core 22. A node without `kind`
694
+ // Plugin kinds are dispatched before body-key core variants. A node without `kind`
468
695
  // never enters this branch, so the core chain below is unchanged.
469
696
  if (isPluginKindNode(node)) return executePluginKindNode(node, ctx, deps);
470
697
  if (isPromptNode(node)) return executePromptNode(node, ctx, deps);
@@ -501,11 +728,13 @@ async function dispatchNode(
501
728
  };
502
729
  }
503
730
 
504
- /** Surface-unification §D3 (2026-05-11) — `dryRun` covers every trigger
505
- * variant (Schedule · Webhook · HTTP · Discord · Telegram · Manual ·
506
- * Chat). HTTP is not strictly a trigger, but in dry-run we typically
507
- * want to skip external IO too — callers that need real HTTP results
508
- * during dry-run can leave dryRun=false and run normally. */
731
+ /** Calls that may mutate state outside the workflow during test runs. */
732
+ function isExternalSideEffectNode(node: DagNode): boolean {
733
+ return isPluginKindNode(node) || isSubworkflowNode(node) || isBashNode(node) || isSkillNode(node)
734
+ || isCftNode(node) || isApprovalNode(node) || isTaskNode(node)
735
+ || isHttpRequestNode(node) || isIterationNode(node);
736
+ }
737
+
509
738
  function isTriggerVariant(node: DagNode): boolean {
510
739
  return (
511
740
  isScheduleTriggerNode(node)
@@ -524,7 +753,7 @@ function variantOf(node: DagNode): string {
524
753
  isSwitchNode, isIterationNode, isClassifyNode, isExtractNode, isSetNode,
525
754
  isFilterNode, isTemplateNode, isHttpRequestNode, isShowroomNode, isTaskNode,
526
755
  isScheduleTriggerNode, isWebhookTriggerNode, isDiscordTriggerNode,
527
- isTelegramTriggerNode, isManualTriggerNode, isChatTriggerNode,
756
+ isTelegramTriggerNode, isManualTriggerNode, isChatTriggerNode, isSubworkflowNode,
528
757
  ];
529
758
  const index = guards.findIndex((guard) => guard(node));
530
759
  return index < 0 ? 'unknown' : WORKFLOW_CORE_KINDS[index]!;
@@ -574,6 +803,7 @@ interface RunHeader {
574
803
  workflowName: string;
575
804
  arguments: string;
576
805
  startedAt: number;
806
+ mode?: RunWorkflowOpts['mode'];
577
807
  }
578
808
 
579
809
  interface RunFinal extends RunHeader {
@@ -16,6 +16,7 @@ export type {
16
16
  WorkflowDeps,
17
17
  WorkflowEntry,
18
18
  WorkflowEvent,
19
+ WorkflowRunMode,
19
20
  WorkflowSource,
20
21
  } from './types.js';
21
22
 
@@ -0,0 +1,60 @@
1
+ import { join } from 'node:path';
2
+ import { findWorkflow } from '../discovery.js';
3
+ import { elanousStateRoot } from '../../autopilot/state-paths.js';
4
+ import { interpolate } from '../variables.js';
5
+ import type { NodeExecContext, NodeOutput, RunWorkflowOpts, SubworkflowNode, WorkflowDeps } from '../types.js';
6
+
7
+ /** Execute a named workflow as one node, retaining the full child result map. */
8
+ export async function executeSubworkflowNode(
9
+ node: SubworkflowNode,
10
+ ctx: NodeExecContext,
11
+ deps: WorkflowDeps,
12
+ parent: RunWorkflowOpts,
13
+ childRunId: string,
14
+ ): Promise<NodeOutput> {
15
+ const startedAt = Date.now();
16
+ const stack = [...(parent.workflowStack ?? []), parent.workflow.name];
17
+ const failure = (error: string): NodeOutput => ({
18
+ ok: false, output: '', error, durationMs: Date.now() - startedAt, childRunId,
19
+ });
20
+ if (stack.includes(node.workflow)) return failure(`subworkflow cycle: ${[...stack, node.workflow].join(' -> ')}`);
21
+ const maxDepth = parent.maxSubworkflowDepth ?? 5;
22
+ if (stack.length > maxDepth) return failure(`subworkflow depth exceeds ${maxDepth}`);
23
+
24
+ try {
25
+ const child = findWorkflow(node.workflow)?.definition;
26
+ if (!child) return failure(`subworkflow '${node.workflow}' not found`);
27
+ const inputs = Object.fromEntries(Object.entries(node.inputs).map(([name, expression]) => [
28
+ name, interpolate(expression, ctx).text,
29
+ ]));
30
+ const { runWorkflowToCompletion } = await import('../executor.js');
31
+ const result = await runWorkflowToCompletion({
32
+ workflow: child,
33
+ arguments: JSON.stringify(inputs),
34
+ runId: childRunId,
35
+ workflowStack: stack,
36
+ maxSubworkflowDepth: parent.maxSubworkflowDepth,
37
+ mode: 'full',
38
+ ignorePins: true,
39
+ ...(parent.artifactsDir !== undefined ? { artifactsDir: `${parent.artifactsDir}/${childRunId}` } : {}),
40
+ ...(parent.runDir !== undefined ? { runDir: `${parent.runDir}/children/${childRunId}` } : {}),
41
+ ...(parent.runDir === undefined && parent.artifactsDir === undefined
42
+ ? { runDir: join(process.env.ELANOUS_WORKFLOWS_RUNS_DIR?.trim() || join(elanousStateRoot(), 'workflows-runs'), childRunId) } : {}),
43
+ ...(parent.persistRun !== undefined ? { persistRun: parent.persistRun } : {}),
44
+ ...(parent.signal !== undefined ? { signal: parent.signal } : {}),
45
+ ...(parent.screen !== undefined ? { screen: parent.screen } : {}),
46
+ ...(parent.onTokenChunk !== undefined ? { onTokenChunk: parent.onTokenChunk } : {}),
47
+ }, deps);
48
+ const failedNode = Object.entries(result.outputs).find(([, output]) => !output.ok);
49
+ if (!result.ok || failedNode) {
50
+ const error = result.events.find(event => event.type === 'workflow_failed');
51
+ const message = error?.type === 'workflow_failed' ? error.error
52
+ : failedNode ? `node '${failedNode[0]}' failed: ${failedNode[1].error ?? '(no error message)'}`
53
+ : 'subworkflow failed';
54
+ return { ...failure(message), output: result.outputs };
55
+ }
56
+ return { ok: true, output: result.outputs, durationMs: Date.now() - startedAt, childRunId };
57
+ } catch (err) {
58
+ return failure(err instanceof Error ? err.message : String(err));
59
+ }
60
+ }
@@ -20,7 +20,7 @@ const PLACEHOLDER = /\{\{([^{}]*)\}\}/g;
20
20
  const INPUT_REF = /^inputs\.([A-Za-z_][A-Za-z0-9_]*)$/;
21
21
 
22
22
  export function isPluginKindNode(node: DagNode): node is PluginKindNode {
23
- return typeof (node as PluginKindNode).kind === 'string';
23
+ return typeof (node as PluginKindNode).kind === 'string' && node.kind !== 'subworkflow';
24
24
  }
25
25
 
26
26
  export function pluginKindRunName(run: NodeKindRun): 'bash' | 'http' | 'skill' | 'mcp' {