elanous 0.2.21 → 0.2.22

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 (315) 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/chunks/1307-08b716476f1d50af.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-d781be3e3a9cfd74.js → page-d85c43f46f58716b.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/board/{page-b0a80997903852d8.js → page-4e645600579ae952.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-2b1263165630d9c8.js → page-87edbfd19117823b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/chat/{page-e2d6bed48ac20120.js → page-cbaf66b1e1804adf.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-80ed511e02c81412.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/control/{page-544dd4bd4847eeb7.js → page-9f5610caaa06178c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-3ee191319bf97d87.js → page-ec8f3575e339de74.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/exec/{page-a449f1ed472b81c2.js → page-2a19c5cd3b562580.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/field/{page-9045094d6d3b5cd0.js → page-b6a09cde2e07521a.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/inside/{page-c06673596849ef45.js → page-f7eae1518ca0c353.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/{layout-9f81233f29dcd409.js → layout-8462d6a543cbb2d9.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/morning/{page-7a12f61be4a579d1.js → page-786fb4504d0f2fa9.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/{not-found-c85b0589591a99c9.js → not-found-75209f14597dcb8f.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-725a4da02dd129eb.js → page-bf30affe3a85af7e.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/{page-21bec4afeb2f0917.js → page-76cf838050a8d681.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-dc39ee7a36d26385.js → page-7ec8e289b4c14df6.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-84ae759f76053672.js → page-8c8ac17d52be2a79.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-3db58ddb61fe2be9.js → page-d359ab048d63a9d9.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-c5315e30f3528dc4.js → page-b8b345c880bf78d1.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/settings/{page-2fe368efb6de3b6a.js → page-f151cc10ad026940.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/share/{page-c69762987b751174.js → page-bf495a1e284f4e00.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-7f8d7fcdf058a845.js → page-69cb93d1a2e57e0e.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-22163556cd38bc88.js → page-72ec543928d67c3e.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
  58. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
  59. package/apps/pwa/out/approvals/index.html +1 -1
  60. package/apps/pwa/out/approvals/index.txt +14 -14
  61. package/apps/pwa/out/autopilot/index.html +1 -1
  62. package/apps/pwa/out/autopilot/index.txt +14 -14
  63. package/apps/pwa/out/board/index.html +1 -1
  64. package/apps/pwa/out/board/index.txt +14 -14
  65. package/apps/pwa/out/botlab/index.html +1 -1
  66. package/apps/pwa/out/botlab/index.txt +14 -14
  67. package/apps/pwa/out/bots/index.html +1 -1
  68. package/apps/pwa/out/bots/index.txt +14 -14
  69. package/apps/pwa/out/ceo/index.html +1 -1
  70. package/apps/pwa/out/ceo/index.txt +14 -14
  71. package/apps/pwa/out/chat/index.html +1 -1
  72. package/apps/pwa/out/chat/index.txt +14 -14
  73. package/apps/pwa/out/consult/index.html +1 -1
  74. package/apps/pwa/out/consult/index.txt +14 -14
  75. package/apps/pwa/out/control/index.html +1 -1
  76. package/apps/pwa/out/control/index.txt +14 -14
  77. package/apps/pwa/out/decisions/index.html +1 -1
  78. package/apps/pwa/out/decisions/index.txt +14 -14
  79. package/apps/pwa/out/design-check/index.html +1 -1
  80. package/apps/pwa/out/design-check/index.txt +14 -14
  81. package/apps/pwa/out/editor/index.html +1 -1
  82. package/apps/pwa/out/editor/index.txt +14 -14
  83. package/apps/pwa/out/exec/index.html +1 -1
  84. package/apps/pwa/out/exec/index.txt +14 -14
  85. package/apps/pwa/out/field/index.html +1 -1
  86. package/apps/pwa/out/field/index.txt +14 -14
  87. package/apps/pwa/out/healthz +1 -1
  88. package/apps/pwa/out/index.html +1 -1
  89. package/apps/pwa/out/index.txt +14 -14
  90. package/apps/pwa/out/inside/index.html +1 -1
  91. package/apps/pwa/out/inside/index.txt +14 -14
  92. package/apps/pwa/out/intake/index.html +1 -1
  93. package/apps/pwa/out/intake/index.txt +14 -14
  94. package/apps/pwa/out/live/index.html +1 -1
  95. package/apps/pwa/out/live/index.txt +14 -14
  96. package/apps/pwa/out/live-run/index.html +1 -1
  97. package/apps/pwa/out/live-run/index.txt +14 -14
  98. package/apps/pwa/out/loops/index.html +1 -1
  99. package/apps/pwa/out/loops/index.txt +14 -14
  100. package/apps/pwa/out/market/index.html +1 -1
  101. package/apps/pwa/out/market/index.txt +14 -14
  102. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  103. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  104. package/apps/pwa/out/morning/index.html +1 -1
  105. package/apps/pwa/out/morning/index.txt +14 -14
  106. package/apps/pwa/out/observatory/index.html +1 -1
  107. package/apps/pwa/out/observatory/index.txt +14 -14
  108. package/apps/pwa/out/ops/checklist/index.html +1 -1
  109. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  110. package/apps/pwa/out/ops/release/index.html +1 -1
  111. package/apps/pwa/out/ops/release/index.txt +14 -14
  112. package/apps/pwa/out/ops/seats/index.html +1 -1
  113. package/apps/pwa/out/ops/seats/index.txt +14 -14
  114. package/apps/pwa/out/outputs/index.html +1 -1
  115. package/apps/pwa/out/outputs/index.txt +14 -14
  116. package/apps/pwa/out/reflection/index.html +1 -1
  117. package/apps/pwa/out/reflection/index.txt +15 -15
  118. package/apps/pwa/out/scheduler/index.html +1 -1
  119. package/apps/pwa/out/scheduler/index.txt +14 -14
  120. package/apps/pwa/out/sessions/index.html +1 -1
  121. package/apps/pwa/out/sessions/index.txt +14 -14
  122. package/apps/pwa/out/settings/devices/index.html +1 -1
  123. package/apps/pwa/out/settings/devices/index.txt +14 -14
  124. package/apps/pwa/out/settings/index.html +1 -1
  125. package/apps/pwa/out/settings/index.txt +14 -14
  126. package/apps/pwa/out/setup/done/index.html +1 -1
  127. package/apps/pwa/out/setup/done/index.txt +14 -14
  128. package/apps/pwa/out/setup/index.html +1 -1
  129. package/apps/pwa/out/setup/index.txt +14 -14
  130. package/apps/pwa/out/share/index.html +1 -1
  131. package/apps/pwa/out/share/index.txt +14 -14
  132. package/apps/pwa/out/showroom/index.html +1 -1
  133. package/apps/pwa/out/showroom/index.txt +14 -14
  134. package/apps/pwa/out/tasks/index.html +1 -1
  135. package/apps/pwa/out/tasks/index.txt +14 -14
  136. package/apps/pwa/out/term/index.html +1 -1
  137. package/apps/pwa/out/term/index.txt +14 -14
  138. package/apps/pwa/out/today/index.html +1 -1
  139. package/apps/pwa/out/today/index.txt +14 -14
  140. package/apps/pwa/out/trace/index.html +1 -1
  141. package/apps/pwa/out/trace/index.txt +14 -14
  142. package/apps/pwa/out/vault/index.html +1 -1
  143. package/apps/pwa/out/vault/index.txt +14 -14
  144. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  145. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  146. package/apps/pwa/out/workflows/index.html +1 -1
  147. package/apps/pwa/out/workflows/index.txt +14 -14
  148. package/apps/pwa/out/workspace/index.html +1 -1
  149. package/apps/pwa/out/workspace/index.txt +14 -14
  150. package/apps/pwa/out/worktrees/index.html +1 -1
  151. package/apps/pwa/out/worktrees/index.txt +14 -14
  152. package/graphs/catalog/catalog.yaml +1 -1
  153. package/graphs/drafts/recipes.yaml +2 -2
  154. package/graphs/test-diet/recipes.yaml +3 -0
  155. package/graphs/test-diet/test-diet.yaml +3 -1
  156. package/package.json +1 -1
  157. package/scripts/f-session-monitor.ts +69 -0
  158. package/scripts/hq/standby-snapshot.ts +7 -3
  159. package/scripts/knowledge-compress.ts +11 -0
  160. package/scripts/nightly-docops.ts +90 -3
  161. package/scripts/research-coo-admin-dates.ts +39 -0
  162. package/scripts/skills/public-check.ts +7 -2
  163. package/scripts/smart-merge/resync-shadow.ts +5 -3
  164. package/scripts/test-diet/lib.ts +81 -1
  165. package/scripts/test-diet/node.ts +48 -2
  166. package/scripts/verify-coo-admin-evidence.ts +83 -0
  167. package/src/acp/client.ts +4 -2
  168. package/src/agent/run-context.ts +4 -0
  169. package/src/agent-env/profile.ts +25 -5
  170. package/src/agent-mission/driver.ts +26 -12
  171. package/src/agent-mission/supervisor-metrics.ts +17 -2
  172. package/src/agent-substrate/acp-reviewer.ts +12 -5
  173. package/src/agent-substrate/pr-reviewer.ts +11 -4
  174. package/src/agent-substrate/review-intent.ts +102 -3
  175. package/src/autopilot/build/llm-conflict-merge.ts +22 -8
  176. package/src/autopilot/pr-manager.ts +18 -8
  177. package/src/away/release-watch.ts +47 -8
  178. package/src/briefing/brief-items.ts +104 -11
  179. package/src/chat/index.ts +1 -0
  180. package/src/cli/brief-cli.ts +20 -2
  181. package/src/cli/config-classify.ts +83 -0
  182. package/src/cli/control-cli.ts +14 -0
  183. package/src/cli/decisions-cli.ts +26 -16
  184. package/src/cli/logs-model-input.ts +1 -1
  185. package/src/cli/pr-cli.ts +65 -8
  186. package/src/cli/release-cli.ts +21 -4
  187. package/src/cli/tasks-cli.ts +108 -1
  188. package/src/connectors/types.ts +6 -6
  189. package/src/context-bus/context-now-surfaces.ts +18 -0
  190. package/src/context-bus/coord-events.ts +32 -4
  191. package/src/context-bus/long-term-memory.ts +23 -6
  192. package/src/context-bus/nightly-condense.ts +169 -21
  193. package/src/control/control-status.ts +87 -0
  194. package/src/control-plane/light-member.ts +5 -3
  195. package/src/control-plane/member.ts +172 -7
  196. package/src/core-turn/run-goal-loop.ts +22 -1
  197. package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
  198. package/src/decisions/decision-cards.ts +20 -1
  199. package/src/decisions/decision-ledger.ts +11 -8
  200. package/src/decisions/web-push-decision-cards.ts +19 -3
  201. package/src/discord-self-message.ts +2 -2
  202. package/src/domains/coo-admin-dates.evidence.json +876 -0
  203. package/src/domains/coo-admin-tool.ts +48 -14
  204. package/src/domains/ops-status.ts +53 -0
  205. package/src/domains/outbound-alert.ts +24 -1
  206. package/src/exec-requests/planner.ts +6 -0
  207. package/src/graph-kinds/registry.ts +6 -6
  208. package/src/graph-runner/graph-cli.ts +34 -2
  209. package/src/graph-runner/graph-run-api.ts +7 -0
  210. package/src/graph-runner/graph-tick.ts +73 -12
  211. package/src/graph-runner/runner.ts +43 -11
  212. package/src/graph-unify/wf-to-graph.ts +79 -0
  213. package/src/graph-wizard/generate.ts +34 -2
  214. package/src/graph-wizard/steps.ts +13 -0
  215. package/src/harness/harness-cli-command.ts +121 -23
  216. package/src/harness/harness-queue.ts +58 -14
  217. package/src/harness/launch-advice.ts +23 -0
  218. package/src/index.ts +36 -7
  219. package/src/knowledge/conclusion-compress.ts +93 -0
  220. package/src/knowledge/know-find.ts +9 -2
  221. package/src/knowledge/query.ts +20 -0
  222. package/src/llm.ts +26 -16
  223. package/src/loops/neighbor-runtime.ts +88 -0
  224. package/src/loops/orchestrator/author-depth.ts +29 -11
  225. package/src/loops/orchestrator/overlap.ts +7 -1
  226. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  227. package/src/loops/orchestrator/tick.ts +35 -2
  228. package/src/loops/orchestrator/traffic.ts +2 -0
  229. package/src/loops/registry.ts +5 -0
  230. package/src/market/publish.ts +17 -1
  231. package/src/maturity/feature-maturity.ts +2 -1
  232. package/src/mss/logging/redaction.ts +17 -0
  233. package/src/nexus/api/draft-metrics.ts +22 -8
  234. package/src/nexus/api/graph-peer-edit.ts +77 -0
  235. package/src/nexus/api/graph-wizard-api.ts +9 -1
  236. package/src/nexus/api/graphs-api.ts +145 -7
  237. package/src/nexus/api/http-server.ts +93 -8
  238. package/src/nexus/api/plugins-market.ts +24 -0
  239. package/src/nexus/index.ts +3 -2
  240. package/src/oauth/claude-pty-maturity.ts +24 -0
  241. package/src/oauth/fallback-chain.ts +27 -14
  242. package/src/plugins/maker/market-bundle.ts +9 -0
  243. package/src/plugins/maker/plugin-maker.ts +9 -3
  244. package/src/plugins/maker/wizard-generate.ts +73 -9
  245. package/src/policy/llm-policy.ts +11 -6
  246. package/src/release-loop/checklist-board.ts +27 -0
  247. package/src/release-loop/checklist.ts +38 -0
  248. package/src/seat-address/seat-address.ts +1 -0
  249. package/src/seat-loop/seat-loop.ts +174 -20
  250. package/src/seat-loop/seat-report.ts +29 -1
  251. package/src/self-dev/dev-cli.ts +90 -8
  252. package/src/self-dev/dev-pipeline.ts +20 -1
  253. package/src/self-dev/draft-sweep.ts +45 -8
  254. package/src/self-dev/orchestrate.ts +3 -0
  255. package/src/self-dev/review-provider-fallback.ts +83 -0
  256. package/src/self-dev/run-supervisor.ts +5 -1
  257. package/src/self-implement/harness-policy-gates.ts +7 -5
  258. package/src/self-implement/host-regate.ts +28 -14
  259. package/src/self-implement/l8-merge-queue.ts +80 -8
  260. package/src/self-implement/orchestrator.ts +68 -27
  261. package/src/self-implement/seams.ts +5 -3
  262. package/src/self-implement/self-implement-cli.ts +4 -0
  263. package/src/shell-env-bootstrap.ts +19 -4
  264. package/src/steward/triage.ts +25 -8
  265. package/src/storage/file-lock.ts +33 -4
  266. package/src/task-agent/completion-evidence.ts +31 -3
  267. package/src/task-agent/cover.ts +61 -0
  268. package/src/task-agent/live-moves.ts +126 -3
  269. package/src/task-agent/shadow.ts +19 -4
  270. package/src/task-agent/task-hand.ts +2 -0
  271. package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
  272. package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
  273. package/src/telegram-commands.ts +8 -0
  274. package/src/telegram-project-command.ts +6 -0
  275. package/src/telegram.ts +2 -2
  276. package/src/typecheck-ratchet.ts +2 -1
  277. package/src/user-config.ts +35 -4
  278. package/src/version/packed-revision.json +1 -1
  279. package/src/workflow-runtime/executor.ts +24 -2
  280. package/src/workflow-runtime/triggers/registry.ts +27 -0
  281. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  282. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
  291. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
  292. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
  293. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
  294. package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
  295. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
  296. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
  297. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
  298. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
  301. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
  303. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
  304. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
  305. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
  306. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
  307. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
  308. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
  309. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
  310. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
  311. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
  313. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
  314. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_buildManifest.js +0 -0
  315. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
@@ -55,6 +55,8 @@ export interface SelfImplementDisposition {
55
55
  prNumber?: number;
56
56
  /** Head SHA checked by the Pod before host regate. */
57
57
  checkedHeadCommit?: string;
58
+ /** Review verdict only when the child bound a real pass to a clean, unchanged worktree head. */
59
+ selfReview?: { verdict: 'pass'; head: string };
58
60
  hostRegate?: { passed: boolean; failures: Array<{ step: string; detail: string }>; os: string };
59
61
  merged?: boolean;
60
62
  detail?: string;
@@ -469,6 +471,11 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
469
471
  ...(typeof o.prUrl === 'string' ? { prUrl: o.prUrl } : {}),
470
472
  ...(typeof o.prNumber === 'number' ? { prNumber: o.prNumber } : {}),
471
473
  ...(typeof o.checkedHeadCommit === 'string' ? { checkedHeadCommit: o.checkedHeadCommit } : {}),
474
+ ...(o.reviewedHeadCommit && typeof o.reviewedHeadCommit === 'string' && /^[0-9a-f]{40}$/i.test(o.reviewedHeadCommit)
475
+ && o.review && typeof o.review === 'object' && !Array.isArray(o.review)
476
+ && (o.review as { reviewed?: unknown; verdict?: unknown }).reviewed === true
477
+ && (o.review as { verdict?: unknown }).verdict === 'pass'
478
+ ? { selfReview: { verdict: 'pass' as const, head: o.reviewedHeadCommit } } : {}),
472
479
  ...(typeof o.hostRegate === 'object' && o.hostRegate !== null ? { hostRegate: o.hostRegate as SelfImplementDisposition['hostRegate'] } : {}),
473
480
  ...(typeof o.merged === 'boolean' ? { merged: o.merged } : {}),
474
481
  ...(typeof o.detail === 'string' ? { detail: o.detail } : {}),
@@ -56,6 +56,8 @@ import { telegramDecisionOwner } from './decisions/telegram-decision-cards.js';
56
56
  import { CardStore } from './task-cards/card-store.js';
57
57
  import type { ContextNowDeps } from './context-bus/context-now.js';
58
58
  import type { RunningRunsResult } from './self-implement/running-runs.js';
59
+ import { checklistBoardSlash } from './release-loop/checklist-board.js';
60
+ import type { Checklist } from './release-loop/checklist.js';
59
61
 
60
62
  /** How many prior user/assistant turns to pass into `executeSkill` as
61
63
  * the `## Recent conversation` block. Mirrors the dashboard default
@@ -348,6 +350,7 @@ export function defaultTelegramCommands(
348
350
  coreCommands: readonly SlashCommand[] = SLASH_COMMANDS,
349
351
  maturity: { tuiSlash: Readonly<Record<string, Maturity>>; telegramCommand: Readonly<Record<string, Maturity>>; discordCommand: Readonly<Record<string, Maturity>> } = FEATURE_MATURITY,
350
352
  runningRunsQuery?: () => RunningRunsResult,
353
+ checklistRead?: (version: string) => Checklist,
351
354
  ): TgSlashCommand[] {
352
355
  const handled: TgSlashCommand[] = [
353
356
  {
@@ -376,6 +379,11 @@ export function defaultTelegramCommands(
376
379
  description: 'Running harness runs — same output as TUI /harness runs',
377
380
  handler: async (args) => telegramRunsSlash(args, runningRunsQuery),
378
381
  },
382
+ {
383
+ name: 'board',
384
+ description: '판 체크리스트 — /board [판]',
385
+ handler: async (args) => checklistBoardSlash(args, checklistRead),
386
+ },
379
387
  {
380
388
  name: 'release',
381
389
  description: '발행 현황 — 지금 런·노드·막힘을 한 통으로',
@@ -121,6 +121,12 @@ export function attachTelegramProjectButtons(
121
121
  await bot.answerCallbackQuery(q.id, { text: '개인 대화에서만 고를 수 있습니다' });
122
122
  return;
123
123
  }
124
+ // Reject callbacks outside Telegram's 64-byte limit and empty project ids.
125
+ if (Buffer.byteLength(q.data, 'utf8') > 64 || q.data.length === 4) {
126
+ debug.log('telegram.project', 'button-rejected', { reason: 'invalid-callback-data', chatId: q.chatId });
127
+ await bot.answerCallbackQuery(q.id, { text: '프로젝트를 다시 골라 주세요' });
128
+ return;
129
+ }
124
130
  const chatId = q.chatId;
125
131
  const session = deps.findSession(chatId, q.threadId, bot.botId);
126
132
  if (!session) {
package/src/telegram.ts CHANGED
@@ -81,7 +81,7 @@ import { maybeHandleCardPhoto, type CardPhotoDeps } from './telegram-card-follow
81
81
  import { attachTelegramProjectButtons } from './telegram-project-command.js';
82
82
  import { attachTelegramFabricPlan } from './telegram-fabric-plan.js';
83
83
  import { contextNow as readContextNow, type ContextNowDeps } from './context-bus/context-now.js';
84
- import { renderTelegramNow } from './context-bus/context-now-surfaces.js';
84
+ import { renderContextFirstNow } from './context-bus/context-now-surfaces.js';
85
85
  import { createContextFirstGate, renderContextFirst } from './context-bus/context-first.js';
86
86
 
87
87
  export type TgAttachmentKind = 'photo' | 'voice' | 'audio' | 'document';
@@ -437,7 +437,7 @@ export class TelegramBot {
437
437
  const cfg = this.slashContext?.userConfig.telegram;
438
438
  if (cfg?.contextFirst === false) return;
439
439
  try {
440
- await this.sendMessage(ctx.chatId, renderContextFirst(this.readContextNow, this.contextNowDeps, renderTelegramNow), { replyTo: ctx.messageId, threadId: ctx.threadId });
440
+ await this.sendMessage(ctx.chatId, renderContextFirst(this.readContextNow, this.contextNowDeps, renderContextFirstNow), { replyTo: ctx.messageId, threadId: ctx.threadId });
441
441
  } catch (err) {
442
442
  this.log(`telegram context-first failed: ${err instanceof Error ? err.message : String(err)}`);
443
443
  }
@@ -8,9 +8,10 @@ export const TYPECHECK_GATE_CONFIG = 'tsconfig.gate.json';
8
8
  * 🩸 2026-09-23: 🅢·🅕·🅣 세 창이 같은 날 `heap out of memory` 로 착지가 막혔다. 📏 통과한 판의 최대 RSS **5.0GB**
9
9
  * (128GB 기계 · 부하와 무관하게 재현 — 🅕 「로드 ~7 로 내려간 뒤에도 났다」). 우회 12288 로 🅢 착지 19건 PASS.
10
10
  * ⇒ 부하 탓이 아니라 «자란 프로젝트»다. 값을 사람이 기억해 붙이는 우회를 도구가 스스로 갖는다.
11
+ * 🩸 2026-10-08: 12288 로도 호스트 재게이트·`pr land` 가 OOM 으로 «못 잼»(#25177 · #25248 · TC 수확 4건 — 전부 20480 에서 PASS) → 20480.
11
12
  * ⚠️ 호출자가 이미 `--max-old-space-size` 를 줬으면 «그것을» 존중한다(덮지 않는다).
12
13
  */
13
- export const TSC_HEAP_MB = 12288;
14
+ export const TSC_HEAP_MB = 20480;
14
15
  export function tscEnv(base: NodeJS.ProcessEnv = process.env): NodeJS.ProcessEnv {
15
16
  const cur = base.NODE_OPTIONS ?? '';
16
17
  if (/--max-old-space-size[= ]/.test(cur)) return base;
@@ -214,6 +214,8 @@ export interface LLMConfig {
214
214
  * `within-quota`(한도 안에서만 · 전부 차면 멈춤) · `fallback`(한도 안 ⊕ 폴백 체인 · 기본) · `credits`(크레딧까지).
215
215
  * 회전·Pod 배분·폴백·매시 한도 알림이 «이 한 값»을 같이 읽는다. 없으면 옛 `codexCreditsAllowed` 를 읽는다. */
216
216
  codexQuotaPolicy?: 'within-quota' | 'fallback' | 'credits';
217
+ /** Grok weekly launch usage ceiling (percent); defaults to 80. */
218
+ grokWeeklyCapPct?: number;
217
219
  /** ⭐⭐ codex 가 «소진된 뒤» 갈 곳을 «순서»로 정한다 (대표 2026-08-13).
218
220
  *
219
221
  * 값: `['codex-rotate', 'grok']` — 아는 칸은 그 둘뿐이고, 모르는 이름은 버린다(관측에 남는다).
@@ -431,7 +433,7 @@ export interface RotationEntry {
431
433
  label?: string;
432
434
  }
433
435
 
434
- const LLM_DEFAULTS: LLMConfig = { provider: 'auto' };
436
+ const LLM_DEFAULTS: LLMConfig = { provider: 'auto', grokWeeklyCapPct: 80 };
435
437
 
436
438
  // ── Skills ───────────────────────────────────────────────────────────
437
439
 
@@ -1149,6 +1151,8 @@ export interface SelfImplementToolConfig {
1149
1151
  clarificationEscalation: SelfImplementClarificationEscalationConfig;
1150
1152
  /** RUN-TTL: hours an open self-impl PR may go without a commit before the draft sweep asks for a rebase. Absent or invalid = 24. */
1151
1153
  runTtlHours?: number;
1154
+ /** DRAFT-CLEANUP-FIX: per-tick close cap of `harness drafts sweep`. Raw value — `resolveDraftSweepCloseCap` validates (1–100, default 10, warns). */
1155
+ draftSweepCloseCap?: unknown;
1152
1156
  /** RUN-TTL sweep step: `shadow` (default) only logs; `live` sends the resync memo / adds `elanous:needs-rebase`. Cap absent or invalid = 30. */
1153
1157
  runTtl?: { mode: 'shadow' | 'live'; cap?: number };
1154
1158
  /** STOP-AUTOHEAL: `shadow` (default) only records what the stop dispatcher would do (`event='autoheal'`); `live` runs the injected heal seams once per run per stop class. */
@@ -2684,6 +2688,8 @@ function parseToolsConfig(raw: unknown): ToolsConfig {
2684
2688
  const childLlm = parseChildLlmPreference(selfImplRaw.childLlm);
2685
2689
  const runTtlHoursRaw = selfImplRaw.runTtlHours;
2686
2690
  const runTtlHours = typeof runTtlHoursRaw === 'number' && Number.isFinite(runTtlHoursRaw) && runTtlHoursRaw > 0 ? runTtlHoursRaw : undefined;
2691
+ // Kept raw: the sweep validates and warns, so an out-of-range value is visible rather than silently dropped.
2692
+ const draftSweepCloseCap = selfImplRaw.draftSweepCloseCap;
2687
2693
  const runTtlRaw = selfImplRaw.runTtl && typeof selfImplRaw.runTtl === 'object' && !Array.isArray(selfImplRaw.runTtl)
2688
2694
  ? selfImplRaw.runTtl as Record<string, unknown> : undefined;
2689
2695
  const runTtl = runTtlRaw ? {
@@ -2729,7 +2735,7 @@ function parseToolsConfig(raw: unknown): ToolsConfig {
2729
2735
  runDevHarness,
2730
2736
  selfOrchestrate,
2731
2737
  nativeStructure,
2732
- selfImplement: { worktreeRoot, childInstanceMode, prApprovalDelivery, observeOnly, goalRecordInDoc, goalAuthorPersistentGrounding, fabricDecompose, fabricDecomposeAutoPathThreshold, autoOpenPr, draftOnStop, salvageRetention, autoStop, autoAssist, screenStallTermination, reworkBudget, decompositionShadow, clarificationEscalation, ...(childLlm ? { childLlm } : {}), ...(runTtlHours !== undefined ? { runTtlHours } : {}), ...(runTtl ? { runTtl } : {}), ...(autoheal ? { autoheal } : {}) },
2738
+ selfImplement: { worktreeRoot, childInstanceMode, prApprovalDelivery, observeOnly, goalRecordInDoc, goalAuthorPersistentGrounding, fabricDecompose, fabricDecomposeAutoPathThreshold, autoOpenPr, draftOnStop, salvageRetention, autoStop, autoAssist, screenStallTermination, reworkBudget, decompositionShadow, clarificationEscalation, ...(childLlm ? { childLlm } : {}), ...(runTtlHours !== undefined ? { runTtlHours } : {}), ...(draftSweepCloseCap !== undefined ? { draftSweepCloseCap } : {}), ...(runTtl ? { runTtl } : {}), ...(autoheal ? { autoheal } : {}) },
2733
2739
  };
2734
2740
  }
2735
2741
 
@@ -3311,8 +3317,22 @@ export interface RoleLlmSpec {
3311
3317
  model?: string;
3312
3318
  }
3313
3319
 
3314
- /** 역할별 LLM 선택(sparse) — provider 를 «역할마다» 정할 수 있는 유일한 자리. */
3315
- export type RoleLlmConfig = Partial<Record<ModelRole, RoleLlmSpec>>;
3320
+ /** Reviewer-only ACP subscription selection; it is not a streaming LLM provider. */
3321
+ export interface SubscriptionReviewerSpec {
3322
+ provider: 'claude-acp';
3323
+ executor?: 'cct' | 'cc';
3324
+ }
3325
+
3326
+ export function parseSubscriptionReviewerSpec(raw: unknown): SubscriptionReviewerSpec | undefined {
3327
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined;
3328
+ const v = raw as Record<string, unknown>;
3329
+ if (v.provider !== 'claude-acp' || (v.executor !== undefined && v.executor !== 'cct' && v.executor !== 'cc')
3330
+ || Object.keys(v).some(k => k !== 'provider' && k !== 'executor')) return undefined;
3331
+ return { provider: 'claude-acp', ...(v.executor ? { executor: v.executor as 'cct' | 'cc' } : {}) };
3332
+ }
3333
+
3334
+ /** 역할별 LLM 선택(sparse). `reviewer` is the opt-in harness ACP lane. */
3335
+ export type RoleLlmConfig = Partial<Record<ModelRole, RoleLlmSpec>> & { reviewer?: SubscriptionReviewerSpec };
3316
3336
 
3317
3337
  export type ModelResolutionSource = 'config' | 'tier' | 'environment' | 'default';
3318
3338
 
@@ -3603,6 +3623,8 @@ export interface SeatLoopConfig {
3603
3623
  repeatStop?: number;
3604
3624
  /** OP-LOOP-DECIDE: OP seat filters open decision cards (repeat-stop → b · harness scope → b unless money/security/secret/public paths). shadow records only; live decides/routes. Absent = shadow. */
3605
3625
  opDecide?: { mode: 'shadow' | 'live' };
3626
+ /** IDLE-LADDER: when a seat's own queue is empty the loop picks idle work (landed-yellow rebuttal → next-release authoring → standing work → borrow an unlaunched neighbor cell). Absent = off (only the empty-queue minutes metric is recorded); 'shadow' records the pick only; 'live' lets live-safe/on launch it. */
3627
+ idleLadder?: 'shadow' | 'live';
3606
3628
  }
3607
3629
 
3608
3630
  export type EventSeat = 'OP' | 'TC' | 'MK' | 'UX';
@@ -4134,6 +4156,10 @@ function parseRoleLlmConfig(raw: unknown): RoleLlmConfig | undefined {
4134
4156
  if (entry.ok) parsed[role] = entry.spec;
4135
4157
  else dropped.push({ role, reason: entry.reason });
4136
4158
  }
4159
+ if (values.reviewer !== undefined) {
4160
+ parsed.reviewer = parseSubscriptionReviewerSpec(values.reviewer);
4161
+ if (!parsed.reviewer) warnUserConfigDrop('roleLlm.reviewer', 'claude-acp reviewer 설정이 유효하지 않아 무시');
4162
+ }
4137
4163
  // ⛔ config 로딩은 «나머지를 살리려고» 잘못된 칸을 버리는데, 그 사실이 어디에도 안 남으면
4138
4164
  // 사용자는 「왜 내 설정이 안 먹지」를 영영 못 푼다(CLI 는 이름을 대며 거부하는데 여기만 조용했다).
4139
4165
  // ⇒ 버린 것을 «이름과 이유»로 남긴다. 로딩 자체는 계속한다.
@@ -4747,6 +4773,7 @@ function parseSeatLoopsConfig(input: unknown): SeatLoopConfig {
4747
4773
  const mode = (raw as Record<string, unknown>).mode;
4748
4774
  return mode === 'shadow' || mode === 'live' ? { opDecide: { mode } } : {};
4749
4775
  })(),
4776
+ ...(values.idleLadder === 'shadow' || values.idleLadder === 'live' ? { idleLadder: values.idleLadder } : {}),
4750
4777
  };
4751
4778
  }
4752
4779
 
@@ -5114,6 +5141,8 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
5114
5141
  // ⛔ 같은 모양(문자열 배열)이라 아래 fallbackChain 과 «같은 자»를 쓴다.
5115
5142
  codexCreditsAllowed: llm.codexCreditsAllowed === true ? true : undefined,
5116
5143
  codexQuotaPolicy: isCodexQuotaPolicy(llm.codexQuotaPolicy) ? llm.codexQuotaPolicy : undefined,
5144
+ grokWeeklyCapPct: typeof llm.grokWeeklyCapPct === 'number' && Number.isFinite(llm.grokWeeklyCapPct)
5145
+ && llm.grokWeeklyCapPct >= 0 && llm.grokWeeklyCapPct <= 100 ? llm.grokWeeklyCapPct : LLM_DEFAULTS.grokWeeklyCapPct,
5117
5146
  codexAccountOrder: Array.isArray(llm.codexAccountOrder)
5118
5147
  ? llm.codexAccountOrder.filter((v: unknown): v is string => typeof v === 'string' && v.length > 0)
5119
5148
  : undefined,
@@ -6328,6 +6357,7 @@ export function saveUserConfig(
6328
6357
  ...(cfg.loops.seat.stall ? { stall: cfg.loops.seat.stall } : {}),
6329
6358
  ...(cfg.loops.seat.repeatStop === undefined ? {} : { repeatStop: cfg.loops.seat.repeatStop }),
6330
6359
  ...(cfg.loops.seat.opDecide ? { opDecide: cfg.loops.seat.opDecide } : {}),
6360
+ ...(cfg.loops.seat.idleLadder ? { idleLadder: cfg.loops.seat.idleLadder } : {}),
6331
6361
  } } : {}),
6332
6362
  } } : {}),
6333
6363
  ...(cfg.autopilot ? { autopilot: {
@@ -6383,6 +6413,7 @@ export function saveUserConfig(
6383
6413
  // ★ 2026-09-28: 타입·파서에만 넣고 이 whitelist 를 빠뜨려 `config set llm.codexCreditsAllowed true` 가 «직렬화 드롭» 됐다(#21375 직후 운영 실측).
6384
6414
  codexCreditsAllowed: cfg.llm.codexCreditsAllowed,
6385
6415
  codexQuotaPolicy: cfg.llm.codexQuotaPolicy,
6416
+ grokWeeklyCapPct: cfg.llm.grokWeeklyCapPct,
6386
6417
  fallbackChain: cfg.llm.fallbackChain,
6387
6418
  reviewFallbackModels: cfg.llm.reviewFallbackModels,
6388
6419
  codexStore: cfg.llm.codexStore,
@@ -1 +1 @@
1
- {"commit":"7d04a88523648e6883e3c1888f1df2ac59944696"}
1
+ {"commit":"7f281a6aa9a90d9da2b5f3e9a22e2347b0c19bff"}
@@ -41,6 +41,9 @@ import {
41
41
  import { evaluateWhen } from './variables.js';
42
42
  import { readWorkflowPin } from './pin-data.js';
43
43
  import { WORKFLOW_CORE_KINDS } from '../graph-kinds/registry.js';
44
+ import { workflowToGraph, walkWorkflowGraph } from '../graph-unify/wf-to-graph.js';
45
+ import { debug } from '../debug/log.js';
46
+ import { getUserConfig } from '../user-config.js';
44
47
  import { checkModelRequires } from '../registry/resolver.js';
45
48
  import { validateJudgmentContract, validateJudgmentVerdict } from './judgment-contract.js';
46
49
  import { signalBus } from '../signal-bus/index.js';
@@ -107,8 +110,25 @@ export async function* runWorkflow(
107
110
  yield { type: 'workflow_start', workflow: opts.workflow.name, runId, mode };
108
111
 
109
112
  let order: string[];
113
+ let graphRecipes: Readonly<Record<string, DagNode>> | undefined;
110
114
  try {
111
- order = topoSort(opts.workflow.nodes);
115
+ // WF2G supplies the graph cursor. The compatibility adapter owns workflow
116
+ // events, substitution, pins and persistence; graph-runner's file/command
117
+ // recipes do not execute workflow nodes yet.
118
+ const graphEnabled = getUserConfig().raw.workflowGraphEnabled !== false;
119
+ const conversion = graphEnabled ? workflowToGraph(opts.workflow) : null;
120
+ if (conversion?.ok) {
121
+ try {
122
+ order = walkWorkflowGraph(conversion.graph);
123
+ graphRecipes = conversion.graph.recipes;
124
+ } catch (error) {
125
+ debug.log('graph.unify', 'wf-fallback', { file: opts.workflow.name, reason: String(error) });
126
+ order = topoSort(opts.workflow.nodes);
127
+ }
128
+ } else {
129
+ if (conversion) debug.log('graph.unify', 'wf-fallback', { file: opts.workflow.name, reason: conversion.reason });
130
+ order = topoSort(opts.workflow.nodes);
131
+ }
112
132
  } catch (err) {
113
133
  yield {
114
134
  type: 'workflow_failed',
@@ -119,7 +139,9 @@ export async function* runWorkflow(
119
139
  return {};
120
140
  }
121
141
 
122
- const nodeById = new Map(opts.workflow.nodes.map(n => [n.id, n] as const));
142
+ const nodeById = graphRecipes
143
+ ? new Map(Object.entries(graphRecipes))
144
+ : new Map(opts.workflow.nodes.map(n => [n.id, n] as const));
123
145
  const startIndex = opts.fromNode ? order.indexOf(opts.fromNode) : 0;
124
146
  const outputs: Record<string, NodeOutput> = {};
125
147
  if (startIndex < 0 || (opts.onlyNode && !nodeById.has(opts.onlyNode))) {
@@ -20,6 +20,33 @@ export interface ScheduleEntry {
20
20
  trigger: ScheduleTriggerNode['scheduleTrigger'];
21
21
  }
22
22
 
23
+ // Graph schedules use the same ScheduleEntry shape as workflow nodes; the
24
+ // graph identity is namespaced so a workflow of the same name cannot collide.
25
+ export function graphScheduleEntries(document: unknown): ScheduleEntry[] {
26
+ if (!document || typeof document !== 'object' || Array.isArray(document)) throw new Error('invalid graph YAML');
27
+ const graph = document as Record<string, unknown>;
28
+ const triggers = graph.triggers;
29
+ if (triggers === undefined) return [];
30
+ if (!triggers || typeof triggers !== 'object' || Array.isArray(triggers)) throw new Error('graph triggers must be an object');
31
+ const schedule = (triggers as Record<string, unknown>).schedule;
32
+ if (schedule === undefined) return [];
33
+ if (typeof graph.graph_id !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(graph.graph_id) || graph.graph_id === '.' || graph.graph_id === '..') {
34
+ throw new Error('graph schedule requires a valid graph_id');
35
+ }
36
+ const schedules = Array.isArray(schedule) ? schedule : [schedule];
37
+ return schedules.map((raw, index) => {
38
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('graph triggers.schedule must contain schedule objects');
39
+ const value = raw as Record<string, unknown>;
40
+ if (value.type === 'cron' && typeof value.cron === 'string' && value.cron.trim()) {
41
+ return { workflowName: `graph:${graph.graph_id}`, nodeId: `schedule:${index}`, trigger: { type: 'cron', cron: value.cron } };
42
+ }
43
+ if (value.type === 'interval' && typeof value.interval === 'number' && Number.isFinite(value.interval) && value.interval > 0) {
44
+ return { workflowName: `graph:${graph.graph_id}`, nodeId: `schedule:${index}`, trigger: { type: 'interval', interval: value.interval } };
45
+ }
46
+ throw new Error(`invalid graph triggers.schedule[${index}]: expected cron or interval`);
47
+ });
48
+ }
49
+
23
50
  export interface WebhookEntry {
24
51
  workflowName: string;
25
52
  nodeId: string;
@@ -5,9 +5,12 @@
5
5
  // daemon can drive schedule + webhook + (future) discord/telegram
6
6
  // uniformly.
7
7
 
8
+ import { readFileSync } from 'node:fs';
9
+ import { resolve } from 'node:path';
10
+ import { parse as parseYaml } from 'yaml';
8
11
  import { isScheduleTriggerNode } from '../schema.js';
9
12
  import type { ScheduleTriggerNode, WorkflowEntry } from '../types.js';
10
- import type { ScheduleEntry } from './registry.js';
13
+ import { graphScheduleEntries, type ScheduleEntry } from './registry.js';
11
14
  import { startScheduler, type SchedulerHandle, type SchedulerOpts } from './scheduler.js';
12
15
  import type { TriggerEmit, TriggerSource, TriggerSubscription } from './source.js';
13
16
 
@@ -25,6 +28,8 @@ export interface ScheduleSource extends TriggerSource {
25
28
  handle(): SchedulerHandle | null;
26
29
  /** Subscription snapshot for `daemon.status()`. */
27
30
  subscriptions(): TriggerSubscription[];
31
+ /** Subscribe a graph YAML's schedule declarations to the same scheduler. */
32
+ subscribeGraph(file: string, onFire: () => void | Promise<void>): void;
28
33
  }
29
34
 
30
35
  interface Binding {
@@ -34,14 +39,30 @@ interface Binding {
34
39
  onEmit: TriggerEmit;
35
40
  }
36
41
 
42
+ interface GraphBinding {
43
+ file: string;
44
+ scheduleEntry: ScheduleEntry;
45
+ onFire: () => void | Promise<void>;
46
+ }
47
+
37
48
  export function createScheduleSource(opts: ScheduleSourceOpts = {}): ScheduleSource {
38
49
  const bindings: Binding[] = [];
50
+ const graphBindings: GraphBinding[] = [];
39
51
  let handle: SchedulerHandle | null = null;
40
52
  let started = false;
41
53
 
42
54
  return {
43
55
  kind: 'schedule',
44
56
  handle: () => handle,
57
+ subscribeGraph(file, onFire) {
58
+ file = resolve(file);
59
+ const schedules = graphScheduleEntries(parseYaml(readFileSync(file, 'utf8')) as unknown);
60
+ for (const scheduleEntry of schedules) {
61
+ if (graphBindings.some(b => b.file === file && b.scheduleEntry.nodeId === scheduleEntry.nodeId)) continue;
62
+ graphBindings.push({ file, scheduleEntry, onFire });
63
+ if (started && handle) handle.add(scheduleEntry);
64
+ }
65
+ },
45
66
  subscribe(entry, onEmit) {
46
67
  for (const node of entry.definition.nodes ?? []) {
47
68
  if (!isScheduleTriggerNode(node)) continue;
@@ -68,8 +89,13 @@ export function createScheduleSource(opts: ScheduleSourceOpts = {}): ScheduleSou
68
89
  if (started) return;
69
90
  started = true;
70
91
  handle = startScheduler({
71
- registry: bindings.map(b => b.scheduleEntry),
92
+ registry: [...bindings.map(b => b.scheduleEntry), ...graphBindings.map(b => b.scheduleEntry)],
72
93
  runWorkflow: async (scheduleEntry) => {
94
+ const graphBinding = graphBindings.find(b => b.scheduleEntry === scheduleEntry);
95
+ if (graphBinding) {
96
+ await graphBinding.onFire();
97
+ return;
98
+ }
73
99
  const binding = bindings.find(b =>
74
100
  b.scheduleEntry.workflowName === scheduleEntry.workflowName
75
101
  && b.scheduleEntry.nodeId === scheduleEntry.nodeId);
@@ -89,9 +115,10 @@ export function createScheduleSource(opts: ScheduleSourceOpts = {}): ScheduleSou
89
115
  handle?.stop();
90
116
  handle = null;
91
117
  bindings.length = 0;
118
+ graphBindings.length = 0;
92
119
  started = false;
93
120
  },
94
- subscriptions: () => bindings.map(b => ({
121
+ subscriptions: () => [...bindings, ...graphBindings].map(b => ({
95
122
  kind: 'schedule' as const,
96
123
  workflowName: b.scheduleEntry.workflowName,
97
124
  nodeId: b.scheduleEntry.nodeId,