elanous 0.2.13 → 0.2.15

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 (524) 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/LZcstaO9_NXypPFmujolv/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/1307-33ffcc49ab45123b.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
  10. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
  31. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
  42. package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/approvals/page-0e2c57fe6add4b2a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e05a77df7e005b04.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/board/page-f544338193a9a762.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5491c115b1f9a923.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/bots/page-b8c49f6b4fa2df91.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/ceo/page-96398e364fbd4548.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/chat/page-976813c91d7394ea.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/consult/page-28b9448091776c06.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/control/page-1d4901eb7c2d8822.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/design-check/page-72d3e7ff35a8c954.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/editor/page-60c7a8e1a4fe62ce.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/exec/page-2a52b316cb143d9c.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/field/page-c3ce483c8d5db370.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/inside/page-13c7b53e0c2519a9.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/intake/page-96f074f6538e8a2c.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/layout-e97be5334d83a9f5.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/live/page-65e484302921a932.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/loops/page-34d08facaf08f556.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-955ec2da3be4c560.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-9f88fbc15d9058d1.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-c1343f9499d86f3f.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-8739a2e0ae793130.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-c9fc5706b8abaa11.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-d23b12bb92242558.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-8d47f45a0dc044ac.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c7fea475488201a8.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-e2ee2896811e98d0.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-83b19dae8619de05.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-5efbf81124c474ae.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f1a1fc0a82738c.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-da6c08aae0f92768.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-06ee266953d9c879.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-153c2b3bcc89e8de.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-87a11e6a05f15199.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-7d4182964407c09c.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-0d6d23b40263a7ea.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-7e8c39241f8eb6d0.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-008c1a96618a4887.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-f5597e894c02cd33.js} +1 -1
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-0337fdf7fb5e37b6.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-73735b64bfe1ae67.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-8fcec4c9ea2e7f70.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f7e631e9b4daf66e.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-4881b8f9f63694b4.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9817c7f9007466cc.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-97f9f43172a3a90e.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  100. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
  106. package/apps/pwa/out/_next/static/css/1f7c958c41ffa3b9.css +1 -0
  107. package/apps/pwa/out/approvals/index.html +1 -1
  108. package/apps/pwa/out/approvals/index.txt +23 -23
  109. package/apps/pwa/out/autopilot/index.html +1 -1
  110. package/apps/pwa/out/autopilot/index.txt +23 -23
  111. package/apps/pwa/out/board/index.html +1 -1
  112. package/apps/pwa/out/board/index.txt +23 -23
  113. package/apps/pwa/out/botlab/index.html +1 -1
  114. package/apps/pwa/out/botlab/index.txt +22 -22
  115. package/apps/pwa/out/bots/index.html +1 -1
  116. package/apps/pwa/out/bots/index.txt +22 -22
  117. package/apps/pwa/out/ceo/index.html +1 -0
  118. package/apps/pwa/out/ceo/index.txt +33 -0
  119. package/apps/pwa/out/chat/index.html +1 -1
  120. package/apps/pwa/out/chat/index.txt +22 -22
  121. package/apps/pwa/out/consult/index.html +1 -1
  122. package/apps/pwa/out/consult/index.txt +23 -23
  123. package/apps/pwa/out/control/index.html +1 -1
  124. package/apps/pwa/out/control/index.txt +23 -23
  125. package/apps/pwa/out/design-check/index.html +1 -1
  126. package/apps/pwa/out/design-check/index.txt +23 -23
  127. package/apps/pwa/out/editor/index.html +1 -1
  128. package/apps/pwa/out/editor/index.txt +23 -23
  129. package/apps/pwa/out/exec/index.html +1 -1
  130. package/apps/pwa/out/exec/index.txt +23 -23
  131. package/apps/pwa/out/field/index.html +1 -1
  132. package/apps/pwa/out/field/index.txt +23 -23
  133. package/apps/pwa/out/healthz +1 -1
  134. package/apps/pwa/out/index.html +1 -1
  135. package/apps/pwa/out/index.txt +22 -22
  136. package/apps/pwa/out/inside/index.html +1 -1
  137. package/apps/pwa/out/inside/index.txt +22 -22
  138. package/apps/pwa/out/intake/index.html +1 -1
  139. package/apps/pwa/out/intake/index.txt +23 -23
  140. package/apps/pwa/out/live/index.html +1 -1
  141. package/apps/pwa/out/live/index.txt +23 -23
  142. package/apps/pwa/out/loops/index.html +1 -0
  143. package/apps/pwa/out/loops/index.txt +30 -0
  144. package/apps/pwa/out/market/index.html +1 -1
  145. package/apps/pwa/out/market/index.txt +22 -22
  146. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  147. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  148. package/apps/pwa/out/morning/index.html +1 -1
  149. package/apps/pwa/out/morning/index.txt +23 -23
  150. package/apps/pwa/out/observatory/index.html +1 -1
  151. package/apps/pwa/out/observatory/index.txt +23 -23
  152. package/apps/pwa/out/ops/checklist/index.html +1 -1
  153. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  154. package/apps/pwa/out/ops/release/index.html +1 -1
  155. package/apps/pwa/out/ops/release/index.txt +22 -22
  156. package/apps/pwa/out/ops/seats/index.html +1 -1
  157. package/apps/pwa/out/ops/seats/index.txt +22 -22
  158. package/apps/pwa/out/outputs/index.html +1 -1
  159. package/apps/pwa/out/outputs/index.txt +22 -22
  160. package/apps/pwa/out/reflection/index.html +1 -1
  161. package/apps/pwa/out/reflection/index.txt +23 -23
  162. package/apps/pwa/out/scheduler/index.html +1 -1
  163. package/apps/pwa/out/scheduler/index.txt +23 -23
  164. package/apps/pwa/out/sessions/index.html +1 -1
  165. package/apps/pwa/out/sessions/index.txt +22 -22
  166. package/apps/pwa/out/settings/devices/index.html +1 -1
  167. package/apps/pwa/out/settings/devices/index.txt +23 -23
  168. package/apps/pwa/out/settings/index.html +1 -1
  169. package/apps/pwa/out/settings/index.txt +23 -23
  170. package/apps/pwa/out/setup/done/index.html +1 -1
  171. package/apps/pwa/out/setup/done/index.txt +23 -23
  172. package/apps/pwa/out/setup/index.html +1 -1
  173. package/apps/pwa/out/setup/index.txt +23 -23
  174. package/apps/pwa/out/share/index.html +1 -1
  175. package/apps/pwa/out/share/index.txt +23 -23
  176. package/apps/pwa/out/showroom/index.html +1 -1
  177. package/apps/pwa/out/showroom/index.txt +22 -22
  178. package/apps/pwa/out/tasks/index.html +1 -1
  179. package/apps/pwa/out/tasks/index.txt +23 -23
  180. package/apps/pwa/out/term/index.html +1 -1
  181. package/apps/pwa/out/term/index.txt +23 -23
  182. package/apps/pwa/out/today/index.html +1 -1
  183. package/apps/pwa/out/today/index.txt +23 -23
  184. package/apps/pwa/out/trace/index.html +1 -1
  185. package/apps/pwa/out/trace/index.txt +23 -23
  186. package/apps/pwa/out/vault/index.html +1 -1
  187. package/apps/pwa/out/vault/index.txt +23 -23
  188. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  189. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  190. package/apps/pwa/out/workflows/index.html +1 -1
  191. package/apps/pwa/out/workflows/index.txt +23 -23
  192. package/apps/pwa/out/workspace/index.html +1 -1
  193. package/apps/pwa/out/workspace/index.txt +23 -23
  194. package/apps/pwa/out/worktrees/index.html +1 -1
  195. package/apps/pwa/out/worktrees/index.txt +23 -23
  196. package/graphs/heal/heal-loop.yaml +1 -1
  197. package/graphs/implement-loop.yaml +1 -1
  198. package/graphs/orchestrator/orchestrator.yaml +59 -0
  199. package/graphs/orchestrator/recipes.yaml +18 -0
  200. package/graphs/release/recipes.yaml +6 -0
  201. package/graphs/release/release-loop.yaml +6 -1
  202. package/graphs/rhythm/daily.yaml +38 -0
  203. package/graphs/rhythm/recipes.yaml +18 -0
  204. package/graphs/rhythm/weekly.yaml +39 -0
  205. package/graphs/video/recipes.yaml +3 -0
  206. package/graphs/video/youtube-summary.yaml +17 -0
  207. package/package.json +1 -1
  208. package/scripts/audit-test-state-writes.ts +72 -17
  209. package/scripts/brand/check.ts +188 -0
  210. package/scripts/ci-typecheck-changed.ts +50 -18
  211. package/scripts/coord-post.sh +11 -1
  212. package/scripts/docs/generate-graph-anatomy.ts +315 -0
  213. package/scripts/docs-cli-check.ts +23 -6
  214. package/scripts/expose-rubric-check.ts +118 -0
  215. package/scripts/expose-rubric-suggest.ts +169 -0
  216. package/scripts/heal/run-role.ts +14 -0
  217. package/scripts/hq/standby-verify.ts +11 -7
  218. package/scripts/lecture-note/node.ts +31 -11
  219. package/scripts/lessons/handbook-page.ts +112 -0
  220. package/scripts/marketing/generate-ax-brief.ts +140 -0
  221. package/scripts/marketing/generate-feature-map.ts +118 -0
  222. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  223. package/scripts/release-loop/gate-node.ts +89 -14
  224. package/scripts/release-loop/publish-node.ts +14 -1
  225. package/scripts/release-loop/unattended-release.ts +23 -1
  226. package/scripts/release-loop/vault-note-node.ts +428 -0
  227. package/scripts/release-story/draft.ts +220 -0
  228. package/scripts/rhythm/daily.ts +570 -0
  229. package/scripts/rhythm/weekly.ts +315 -0
  230. package/scripts/skills/public-check.ts +169 -0
  231. package/scripts/smart-merge/resync-shadow.ts +147 -0
  232. package/scripts/test-deterministic.ts +4 -8
  233. package/scripts/test-diet/effectiveness.ts +195 -0
  234. package/scripts/test-diet/lib.ts +16 -8
  235. package/scripts/test-diet/node.ts +77 -9
  236. package/scripts/ux/demo-rehearsal.ts +2 -2
  237. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  238. package/scripts/video/youtube-summary.ts +133 -0
  239. package/skills/diagram-master/SKILL.md +42 -49
  240. package/skills/diagram-master/references/pyproject.toml +4 -1
  241. package/skills/diagram-master/references/routing-guide.md +4 -2
  242. package/skills/grill-me/SKILL.md +1 -0
  243. package/skills/youtube-master/SKILL.md +13 -13
  244. package/skills/youtube-master/package-lock.json +0 -545
  245. package/skills/youtube-master/package.json +2 -5
  246. package/skills/youtube-master/scripts/main.ts +2 -2
  247. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  248. package/skills/youtube-master/src/util.ts +30 -7
  249. package/src/agent-env/profile.ts +161 -0
  250. package/src/agent-mission/driver.ts +3 -3
  251. package/src/agent-substrate/pr-reviewer.ts +2 -0
  252. package/src/ask-user-question/pending-questions.ts +5 -0
  253. package/src/auth/github-app-token.ts +112 -13
  254. package/src/autopilot/build/llm-conflict-merge.ts +73 -10
  255. package/src/autopilot/build/merge-intent.ts +53 -0
  256. package/src/briefing/brief-items.ts +121 -0
  257. package/src/chat/index.ts +2 -0
  258. package/src/claims/claims-ledger.ts +232 -0
  259. package/src/claims/claims-render.ts +166 -0
  260. package/src/cli/agent-env-cli.ts +21 -0
  261. package/src/cli/claims-cli.ts +94 -0
  262. package/src/cli/decisions-cli.ts +44 -3
  263. package/src/cli/docs-cli.ts +2 -0
  264. package/src/cli/doctor-cli.ts +23 -1
  265. package/src/cli/doctor-node-modules.ts +147 -0
  266. package/src/cli/flow-cli.ts +11 -0
  267. package/src/cli/freeze-cli.ts +45 -0
  268. package/src/cli/intake-cli.ts +46 -17
  269. package/src/cli/lesson-cli.ts +108 -0
  270. package/src/cli/logs-cli.ts +104 -2
  271. package/src/cli/logs-model-input.ts +131 -0
  272. package/src/cli/pod-cli.ts +2 -0
  273. package/src/cli/pr-cli.ts +29 -1
  274. package/src/cli/private-ledger-commands.ts +54 -4
  275. package/src/cli/pty-drive-verdict.ts +11 -2
  276. package/src/cli/release-cli.ts +190 -33
  277. package/src/cli/rfc-status.ts +91 -0
  278. package/src/cli/seat-requests-cli.ts +52 -0
  279. package/src/connectors/linear.ts +5 -3
  280. package/src/context-bus/condense.ts +15 -20
  281. package/src/context-bus/context-now-public.ts +96 -0
  282. package/src/context-bus/context-now-surfaces.ts +55 -2
  283. package/src/context-bus/context-now.ts +6 -3
  284. package/src/context-bus/coord-events.ts +6 -4
  285. package/src/context-bus/long-term-memory.ts +92 -11
  286. package/src/dashboard/index.ts +75 -22
  287. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  288. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  289. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  290. package/src/decisions/decision-cards.ts +33 -7
  291. package/src/decisions/decision-ledger.ts +40 -1
  292. package/src/decisions/proact-meter.ts +286 -0
  293. package/src/decisions/proact1-lite.ts +86 -0
  294. package/src/decisions/web-push-decision-cards.ts +1 -1
  295. package/src/discord/sprint21-runtime.ts +16 -0
  296. package/src/discord-self-message.ts +8 -0
  297. package/src/discord-slash-wire.ts +31 -2
  298. package/src/domains/alert-audit.ts +271 -0
  299. package/src/domains/core-tools.ts +4 -3
  300. package/src/domains/ops-facts-tool.ts +31 -1
  301. package/src/domains/outbound-alert.ts +1 -1
  302. package/src/elanous-config-dir.ts +29 -4
  303. package/src/execution-loop/launch-gate.ts +29 -14
  304. package/src/flow/cards.ts +239 -0
  305. package/src/graph-runner/graph-tick.ts +4 -0
  306. package/src/graph-runner/runner.ts +30 -5
  307. package/src/harness/harness-cli-command.ts +259 -38
  308. package/src/harness/harness-incidents.ts +45 -12
  309. package/src/harness/harness-pod-dispatch.ts +102 -11
  310. package/src/harness/harness-queue-child.ts +3 -3
  311. package/src/harness/harness-queue.ts +583 -65
  312. package/src/harness/helper-repair.ts +87 -0
  313. package/src/harness/helper-scan.ts +151 -0
  314. package/src/harness/launch-stamp.ts +236 -0
  315. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  316. package/src/harness/ledger-write-guard.ts +35 -0
  317. package/src/harness/model-input-observation.ts +37 -0
  318. package/src/harness/pod-memory-policy.ts +15 -0
  319. package/src/harness/repository-name.ts +55 -0
  320. package/src/hq/fence-audit.ts +90 -0
  321. package/src/hq/hq.ts +63 -5
  322. package/src/hq/lease.ts +4 -4
  323. package/src/hq/move-plan.ts +126 -0
  324. package/src/hq/promote.ts +332 -0
  325. package/src/index.ts +159 -27
  326. package/src/intake-plane/check.ts +63 -0
  327. package/src/intake-plane/digest.ts +37 -15
  328. package/src/intake-plane/front-classifier.ts +6 -0
  329. package/src/intake-plane/items.ts +3 -2
  330. package/src/intake-plane/lens.ts +177 -0
  331. package/src/intake-plane/linear-wish.ts +39 -11
  332. package/src/intake-plane/route.ts +145 -1
  333. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  334. package/src/intake-plane/wish-card.ts +53 -2
  335. package/src/intake-plane/wish-folder.ts +5 -2
  336. package/src/intake-plane/wish-reply.ts +72 -0
  337. package/src/launch-head/seat-goal-queue-lock.fixture.ts +15 -0
  338. package/src/launch-head/seat-goal-queue.ts +198 -0
  339. package/src/lessons/lesson-import.ts +106 -0
  340. package/src/lessons/lesson-ledger.ts +206 -0
  341. package/src/loops/activity.ts +203 -0
  342. package/src/loops/budget.ts +63 -0
  343. package/src/loops/checker.ts +5 -3
  344. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  345. package/src/loops/loop-cli.ts +36 -4
  346. package/src/loops/neighbors.ts +40 -0
  347. package/src/loops/observe.ts +10 -0
  348. package/src/loops/orchestrator/author-depth.ts +220 -0
  349. package/src/loops/orchestrator/author-input-check.ts +82 -0
  350. package/src/loops/orchestrator/author-ledger.ts +232 -0
  351. package/src/loops/orchestrator/finish-rate.ts +175 -0
  352. package/src/loops/orchestrator/overlap.ts +51 -12
  353. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  354. package/src/loops/orchestrator/seat-share.ts +56 -0
  355. package/src/loops/orchestrator/tick.ts +309 -0
  356. package/src/loops/orchestrator/traffic.ts +134 -13
  357. package/src/loops/registry.ts +535 -9
  358. package/src/loops/status-rows.ts +118 -0
  359. package/src/loops/verdict.ts +17 -5
  360. package/src/maturity/bot-slash-catalog.ts +66 -0
  361. package/src/maturity/feature-maturity.ts +9 -5
  362. package/src/maturity/tui-slash-availability.ts +31 -0
  363. package/src/mss/logging/log-store.ts +35 -4
  364. package/src/nexus/api/dashboard.ts +6 -1
  365. package/src/nexus/api/http-server.ts +33 -3
  366. package/src/nexus/api/loop-edges.ts +173 -0
  367. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  368. package/src/nexus/api/schedules-read.ts +18 -1
  369. package/src/nexus/api/seat-requests.ts +79 -56
  370. package/src/nexus/api/task-cards-api.ts +44 -5
  371. package/src/nexus/api/workflows.ts +2 -1
  372. package/src/prompt-enhance/enhance.ts +6 -1
  373. package/src/release-loop/auto-start.ts +51 -0
  374. package/src/release-loop/checklist.ts +22 -4
  375. package/src/release-loop/feature-store.ts +71 -32
  376. package/src/release-loop/landing-freeze.ts +121 -0
  377. package/src/release-loop/merged-pr-checklist.ts +125 -0
  378. package/src/release-loop/placement.ts +302 -0
  379. package/src/release-loop/release-schedule.ts +3 -1
  380. package/src/seat-dispatch/ceo-commands.ts +2 -2
  381. package/src/seat-dispatch/seat-ask.ts +16 -3
  382. package/src/seat-dispatch/seat-questions.ts +58 -0
  383. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  384. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  385. package/src/seat-loop/seat-loop.ts +531 -40
  386. package/src/self-dev/dev-pipeline.ts +14 -6
  387. package/src/self-dev/draft-sweep.ts +162 -6
  388. package/src/self-dev/draft-triage-rules.ts +25 -8
  389. package/src/self-dev/orchestrate.ts +15 -1
  390. package/src/self-dev/review-provider-fallback.ts +6 -1
  391. package/src/self-dev/run-store.ts +73 -1
  392. package/src/self-dev/run-supervisor.ts +57 -2
  393. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  394. package/src/self-implement/budget-gate.ts +29 -0
  395. package/src/self-implement/expose-gate.ts +47 -0
  396. package/src/self-implement/frozen-merges.ts +295 -0
  397. package/src/self-implement/gate-cli.ts +10 -4
  398. package/src/self-implement/gate-scope.ts +46 -9
  399. package/src/self-implement/goal-archive.ts +222 -0
  400. package/src/self-implement/goal-author.ts +43 -5
  401. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  402. package/src/self-implement/goal-lookup.ts +147 -0
  403. package/src/self-implement/heal-intake.ts +197 -0
  404. package/src/self-implement/host-regate.ts +32 -2
  405. package/src/self-implement/l8-merge-queue.ts +132 -0
  406. package/src/self-implement/orchestrator.ts +299 -48
  407. package/src/self-implement/pipeline-shape.ts +1 -1
  408. package/src/self-implement/revert-guard.ts +97 -0
  409. package/src/self-implement/run-ledger.ts +3 -1
  410. package/src/self-implement/run-status-mapping.ts +2 -0
  411. package/src/self-implement/seams.ts +29 -9
  412. package/src/self-implement/self-implement-cli.ts +1 -0
  413. package/src/self-implement/speed-claims.ts +338 -0
  414. package/src/session/index.ts +2 -0
  415. package/src/steward/triage.ts +74 -3
  416. package/src/steward/working-backwards.ts +4 -1
  417. package/src/task-cards/card-cli.ts +10 -2
  418. package/src/task-cards/card-store.ts +10 -6
  419. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  420. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  421. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  422. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  423. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  424. package/src/task-orchestrator/surfaces/pod-pool.ts +60 -8
  425. package/src/task-orchestrator/surfaces/self-implement-pod.ts +224 -49
  426. package/src/task-orchestrator/surfaces/self-implement.ts +5 -1
  427. package/src/telegram-commands.ts +27 -3
  428. package/src/telegram-loops-command.ts +44 -0
  429. package/src/telegram.ts +25 -4
  430. package/src/user-config.ts +146 -18
  431. package/src/version/packed-revision.json +1 -1
  432. package/src/workflow-runtime/nodes/bash.ts +9 -1
  433. package/src/workflow-runtime/storage.ts +31 -13
  434. package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
  445. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  449. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-78b40a344ed22f24.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  523. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  524. /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → LZcstaO9_NXypPFmujolv}/_ssgManifest.js +0 -0
@@ -49,6 +49,7 @@ export interface SelfImplementDisposition {
49
49
  ok?: boolean;
50
50
  worktreePath?: string;
51
51
  branch?: string;
52
+ harvestable?: true;
52
53
  prUrl?: string;
53
54
  prNumber?: number;
54
55
  /** Head SHA checked by the Pod before host regate. */
@@ -84,6 +85,7 @@ export interface SelfImplementDisposition {
84
85
  reviewVerdict?: 'pass' | 'warn' | 'fail';
85
86
  /** 마지막 리뷰의 must-fix 수(`review.mustFix.length`). 같은 조건. */
86
87
  reviewMustFixCount?: number;
88
+ mustFix?: string[];
87
89
  }
88
90
 
89
91
  /**
@@ -265,7 +267,7 @@ export function createSelfImplementAdapter(opts: SelfImplementAdapterOptions) {
265
267
  : r.output.length === 0
266
268
  ? `${exitMessage} (child produced no output)`
267
269
  : r.disposition
268
- ? `${exitMessage} (child terminal result contained no error diagnostic)`
270
+ ? `${exitMessage} (child stage=${r.disposition.stage ?? 'unknown'}; ${r.disposition.detail ?? `mustFix=${JSON.stringify(r.disposition.mustFix ?? 'unknown')}`})`
269
271
  : `${exitMessage} (child output did not contain a parseable terminal JSON diagnostic)`;
270
272
  return {
271
273
  ...exec,
@@ -461,6 +463,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
461
463
  ...(typeof o.ok === 'boolean' ? { ok: o.ok } : {}),
462
464
  ...(typeof o.worktreePath === 'string' ? { worktreePath: o.worktreePath } : {}),
463
465
  ...(typeof o.branch === 'string' ? { branch: o.branch } : {}),
466
+ ...(o.harvestable === true && typeof o.branch === 'string' && o.branch.trim() ? { harvestable: true } : {}),
464
467
  ...(typeof o.prUrl === 'string' ? { prUrl: o.prUrl } : {}),
465
468
  ...(typeof o.prNumber === 'number' ? { prNumber: o.prNumber } : {}),
466
469
  ...(typeof o.checkedHeadCommit === 'string' ? { checkedHeadCommit: o.checkedHeadCommit } : {}),
@@ -483,6 +486,7 @@ export function parseSelfImplementJson(stdout: string): SelfImplementDisposition
483
486
  ? { providerErrors: o.providerErrors as SelfImplementDisposition['providerErrors'] } : {}),
484
487
  ...(typeof o.runId === 'string' && o.runId.trim() ? { runId: o.runId, childRunId: o.runId } : {}),
485
488
  ...reviewGateFields(o),
489
+ ...(Array.isArray(o.mustFix) && o.mustFix.every((item) => typeof item === 'string') ? { mustFix: o.mustFix as string[] } : {}),
486
490
  };
487
491
  reportUnmappedDispositionFields(o, mapped);
488
492
  return mapped;
@@ -14,6 +14,9 @@
14
14
  import type { TgIncoming, TgMessageStreamer } from './telegram.js';
15
15
  import type { UserConfig } from './user-config.js';
16
16
  import { botCommandsToTelegram } from './bots/command-surface.js';
17
+ import { SLASH_COMMANDS, type SlashCommand } from './chat/index.js';
18
+ import { FEATURE_MATURITY, type Maturity } from './maturity/feature-maturity.js';
19
+ import { buildBotSlashCatalog } from './maturity/bot-slash-catalog.js';
17
20
  import {
18
21
  appendMessage,
19
22
  attachTelegramBinding,
@@ -46,6 +49,7 @@ import { classifyIntake } from './ad-pipeline/intake.js';
46
49
  import { createAdPipelineDeps, runAdPipeline } from './ad-pipeline/run.js';
47
50
  import { projectCommand } from './telegram-project-command.js';
48
51
  import { telegramNowSlash } from './context-bus/context-now-surfaces.js';
52
+ import { telegramLoopsStatus } from './telegram-loops-command.js';
49
53
  import { createWishCard } from './intake-plane/wish-card.js';
50
54
  import { telegramDecisionOwner } from './decisions/telegram-decision-cards.js';
51
55
  import { CardStore } from './task-cards/card-store.js';
@@ -298,8 +302,12 @@ function dropChatSessionReply(ctx: TgIncoming): string {
298
302
  /** Build the default command set for a Telegram bot wired via
299
303
  * botFromConfig. Returns the array rather than mutating globals so
300
304
  * tests can construct a variant set with stubbed handlers. */
301
- export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashCommand[] {
302
- return [
305
+ export function defaultTelegramCommands(
306
+ nowDeps?: ContextNowDeps,
307
+ coreCommands: readonly SlashCommand[] = SLASH_COMMANDS,
308
+ maturity: { tuiSlash: Readonly<Record<string, Maturity>>; telegramCommand: Readonly<Record<string, Maturity>>; discordCommand: Readonly<Record<string, Maturity>> } = FEATURE_MATURITY,
309
+ ): TgSlashCommand[] {
310
+ const handled: TgSlashCommand[] = [
303
311
  {
304
312
  name: 'help',
305
313
  description: 'List available commands',
@@ -316,6 +324,11 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
316
324
  description: 'Show current release, decisions, seats and context: /now [topic]',
317
325
  handler: async (args) => telegramNowSlash(args, nowDeps),
318
326
  },
327
+ {
328
+ name: 'loops',
329
+ description: '루프·크론 현황 — 늦음·실패를 먼저 보여줍니다',
330
+ handler: async (args) => telegramLoopsStatus(args),
331
+ },
319
332
  {
320
333
  name: 'status',
321
334
  description: 'Show bot identity, current session, provider + model',
@@ -604,7 +617,8 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
604
617
  if (args.length === 0) return '/wish <소원 한 줄>';
605
618
  const store = new CardStore();
606
619
  try {
607
- const card = createWishCard({ text: args.join(' '), source: 'telegram', ref: `${ctx.chatId}:${ctx.messageId}` }, store);
620
+ const card = createWishCard({ text: args.join(' '), source: 'telegram', ref: `${ctx.chatId}:${ctx.messageId}`,
621
+ replyTo: { surface: 'telegram', chatId: String(ctx.chatId), ...(ctx.threadId != null ? { threadId: String(ctx.threadId) } : {}), ...(ctx.botId ? { botId: ctx.botId } : {}) } }, store);
608
622
  return `소원 카드로 남겼습니다 — ${card.title} (카드 ${card.cardId.slice(0, 8)})`;
609
623
  } finally {
610
624
  store.close();
@@ -951,6 +965,16 @@ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashComman
951
965
  },
952
966
  ...botCommandsToTelegram(),
953
967
  ];
968
+ const catalog = buildBotSlashCatalog({
969
+ surface: 'telegram', coreCommands, maturity,
970
+ handledCommands: handled,
971
+ });
972
+ const handlers = new Map(handled.map((command) => [command.name, command]));
973
+ return catalog.commands.map((entry) => handlers.get(entry.name) ?? {
974
+ name: entry.name,
975
+ description: entry.description,
976
+ handler: async () => catalog.unsupportedReply(entry.name)!,
977
+ });
954
978
  }
955
979
 
956
980
  /** Telegram-side adapter over the messenger-agnostic ACP turn runner.
@@ -0,0 +1,44 @@
1
+ import { dashboardLoops } from './domains/dashboard-data.js';
2
+ import { readSchedulesInventory } from './nexus/api/schedules-read.js';
3
+ import { loadLoopRows, LOOP_SCHEDULES_PATH, LOOPS_PATH, type LoopRow } from './loops/status-rows.js';
4
+
5
+ interface LoopsSources {
6
+ schedules: () => unknown;
7
+ loops: () => unknown;
8
+ now: () => number;
9
+ }
10
+
11
+ const defaultSources: LoopsSources = {
12
+ schedules: () => ({ schedules: readSchedulesInventory() }),
13
+ loops: () => ({ loops: dashboardLoops() }),
14
+ now: Date.now,
15
+ };
16
+
17
+ const clean = (text: string) => text.replace(/[\r\n\t]+/g, ' ').replace(/\s+/g, ' ').trim();
18
+
19
+ /** Read the same two inventories and apply the same row verdicts as PWA /loops. */
20
+ export async function telegramLoopsStatus(args: string[], sources: LoopsSources = defaultSources): Promise<string> {
21
+ if (args.length) return '사용법: /loops';
22
+ try {
23
+ const rows = await loadLoopRows(async (path) => {
24
+ if (path === LOOP_SCHEDULES_PATH) return sources.schedules();
25
+ if (path === LOOPS_PATH) return sources.loops();
26
+ throw new Error('unknown inventory');
27
+ }, sources.now());
28
+ if (!rows.length) return '등록된 루프·크론이 없습니다.';
29
+ const counts = (['늦음', '실패', '살아 있음', '꺼짐', '판정 불가'] as const)
30
+ .map(verdict => `${verdict} ${rows.filter(row => row.verdict === verdict).length}`);
31
+ const priority: Record<LoopRow['verdict'], number> = { '늦음': 0, '실패': 1, '살아 있음': 2, '꺼짐': 3, '판정 불가': 4 };
32
+ const lines = rows.slice().sort((a, b) => priority[a.verdict] - priority[b.verdict]).map(row =>
33
+ `• ${row.verdict === '늦음' ? '🔴' : row.verdict === '실패' ? '❌' : row.verdict === '살아 있음' ? '🟢' : '⚪'} ${row.verdict} · ${clean(row.name)} (${clean(row.layer)}) · ${clean(row.mode)} · 마지막 ${row.lastRun ?? '기록 없음'} · ${clean(row.id)}`);
34
+ let reply = ['루프·크론 현황', counts.join(' · ')].join('\n');
35
+ for (const [index, line] of lines.entries()) {
36
+ // Telegram caps a message at 4096 characters; leave space for an explicit omission count.
37
+ if (reply.length + line.length + 35 > 4000) return `${reply}\n… ${lines.length - index}개 더 (메시지 길이 제한)`;
38
+ reply += `\n${line}`;
39
+ }
40
+ return reply;
41
+ } catch {
42
+ return '루프 현황을 읽지 못했습니다. 일부 레지스트리 조회가 실패했습니다. 데몬 연결을 확인하세요.';
43
+ }
44
+ }
package/src/telegram.ts CHANGED
@@ -66,7 +66,9 @@ import { detectUrlRoute, type UrlRouteDecision } from './skills/url-router.js';
66
66
  import { observeDevRequestRouteFailSoft } from './skills/dev-request-router.js';
67
67
  import { runUrlRoute } from './skills/url-route-exec.js';
68
68
  import { recordRoutedLinkAbsorbed } from './intake-plane/link-ledger.js';
69
- import { classifyFrontInput } from './intake-plane/front-classifier.js';
69
+ import { classifyFrontInput, telegramAbsorbUrls } from './intake-plane/front-classifier.js';
70
+ import { scheduleIntakeEvent } from './intake-plane/route.js';
71
+ import { dispatchTaskCreate } from './task-orchestrator/runtimes/create.js';
70
72
  import { handleTelegramSeatWork, type TelegramSeatWorkDeps } from './intake-plane/telegram-seat-work.js';
71
73
  import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
72
74
  import { effectiveInstanceRoot } from './instance/resolve.js';
@@ -1919,11 +1921,30 @@ export class TelegramBot {
1919
1921
  ? getActiveDelegation(delegationChatKey(this.botId, ctx.chatId, ctx.threadId))
1920
1922
  : null;
1921
1923
  if (!activeDeleg) {
1922
- try {
1923
- classifyFrontInput({ text: ctx.text, surface: 'telegram' }, { urlRouting: getUserConfig().skills.urlRouting });
1924
- } catch { /* observation must not interrupt URL routing */ }
1925
1924
  const urlDec = detectUrlRoute(ctx.text, getUserConfig().skills.urlRouting);
1926
1925
  if (urlDec) {
1926
+ try {
1927
+ const frontInput = { text: ctx.text, surface: 'telegram' as const };
1928
+ const decision = classifyFrontInput(frontInput, { urlRouting: getUserConfig().skills.urlRouting });
1929
+ const absorbUrls = telegramAbsorbUrls(frontInput, decision);
1930
+ for (const url of absorbUrls) {
1931
+ const result = await scheduleIntakeEvent(effectiveInstanceRoot(), url, {
1932
+ eventMax: getUserConfig().intake.eventMax,
1933
+ post: async ({ url: link }) => {
1934
+ const posted = await dispatchTaskCreate({
1935
+ title: `흡수: ${link}`.slice(0, 80),
1936
+ surface: { kind: 'llm-direct', prompt: `이 링크를 흡수하고 노트로 저장하세요: ${link}` },
1937
+ });
1938
+ if (!posted.taskId) throw new Error('intake event task not created');
1939
+ return { taskId: posted.taskId };
1940
+ },
1941
+ });
1942
+ if (result.reply) await this.sendMessage(ctx.chatId, result.reply, { threadId: ctx.threadId, markdown: true });
1943
+ }
1944
+ if (absorbUrls.length) return;
1945
+ } catch (error) {
1946
+ debug.log('intake.event', 'failed', { reason: error instanceof Error ? error.name : 'unknown' });
1947
+ }
1927
1948
  const handled = await this.runUrlRouteReply(ctx, urlDec);
1928
1949
  if (handled) return;
1929
1950
  }
@@ -3097,6 +3097,8 @@ export interface IntakeSurfaceConfig {
3097
3097
  }
3098
3098
 
3099
3099
  export interface IntakeConfig {
3100
+ /** Maximum immediately scheduled Telegram absorption links per KST day. */
3101
+ eventMax?: number;
3100
3102
  telegram: IntakeSurfaceConfig;
3101
3103
  discord: IntakeSurfaceConfig;
3102
3104
  approvals?: { repo?: string };
@@ -3477,13 +3479,20 @@ export function resolveRoleModel(role: ModelRole, config: UserConfig = getUserCo
3477
3479
 
3478
3480
  export type OrchestratorSeat = 'OP' | 'TC' | 'MK' | 'UX';
3479
3481
  export interface OrchestratorLoopConfig {
3482
+ /** ORCH1 graph mode — shadow writes observations only; live appends delegations (default shadow). */
3483
+ mode: 'off' | 'shadow' | 'live';
3480
3484
  seatTrees: Partial<Record<OrchestratorSeat, string[]>>;
3481
3485
  seatCaps: Record<OrchestratorSeat, number>;
3486
+ /** Per-seat release gate; absent seats retain the spawn-budget default. */
3487
+ releaseGate?: Partial<Record<OrchestratorSeat, number>>;
3482
3488
  trafficMode: 'shadow' | 'live';
3489
+ idleRequest?: 'off' | 'shadow' | 'live';
3490
+ /** FINISH-RATE — the queue tick reads finish metrics before launching; shadow logs the hold only, on holds (default shadow). */
3491
+ finishGate?: 'off' | 'shadow' | 'on';
3483
3492
  }
3484
3493
 
3485
3494
  export const ORCHESTRATOR_DEFAULTS: OrchestratorLoopConfig = {
3486
- seatTrees: {}, seatCaps: { TC: 8, UX: 6, MK: 6, OP: 4 }, trafficMode: 'shadow',
3495
+ mode: 'shadow', seatTrees: {}, seatCaps: { TC: 8, UX: 6, MK: 6, OP: 4 }, trafficMode: 'shadow', idleRequest: 'shadow', finishGate: 'shadow',
3487
3496
  };
3488
3497
 
3489
3498
  export function parseOrchestratorLoopConfig(raw: unknown): OrchestratorLoopConfig {
@@ -3492,25 +3501,36 @@ export function parseOrchestratorLoopConfig(raw: unknown): OrchestratorLoopConfi
3492
3501
  ? value.seatTrees as Record<string, unknown> : {};
3493
3502
  const rawCaps = value.seatCaps && typeof value.seatCaps === 'object' && !Array.isArray(value.seatCaps)
3494
3503
  ? value.seatCaps as Record<string, unknown> : {};
3504
+ const rawGate = value.releaseGate && typeof value.releaseGate === 'object' && !Array.isArray(value.releaseGate)
3505
+ ? value.releaseGate as Record<string, unknown> : {};
3495
3506
  const seatTrees: OrchestratorLoopConfig['seatTrees'] = {};
3496
3507
  const seatCaps = { ...ORCHESTRATOR_DEFAULTS.seatCaps };
3508
+ const releaseGate: NonNullable<OrchestratorLoopConfig['releaseGate']> = {};
3497
3509
  for (const seat of ['OP', 'TC', 'MK', 'UX'] as const) {
3498
3510
  if (Array.isArray(rawTrees[seat])) seatTrees[seat] = rawTrees[seat].filter((path): path is string => typeof path === 'string' && isAbsolute(path));
3499
3511
  const cap = rawCaps[seat];
3500
3512
  if (typeof cap === 'number' && Number.isSafeInteger(cap) && cap >= 0) seatCaps[seat] = cap;
3513
+ const gate = rawGate[seat];
3514
+ if (typeof gate === 'number' && Number.isSafeInteger(gate) && gate >= 0) releaseGate[seat] = gate;
3501
3515
  }
3502
- return { seatTrees, seatCaps, trafficMode: value.trafficMode === 'live' ? 'live' : 'shadow' };
3516
+ const mode = value.mode === 'off' || value.mode === 'live' ? value.mode : 'shadow';
3517
+ return { mode, seatTrees, seatCaps, ...(Object.keys(releaseGate).length ? { releaseGate } : {}),
3518
+ trafficMode: value.trafficMode === 'live' ? 'live' : 'shadow',
3519
+ idleRequest: value.idleRequest === 'off' || value.idleRequest === 'live' ? value.idleRequest : 'shadow',
3520
+ finishGate: value.finishGate === 'off' || value.finishGate === 'on' ? value.finishGate : 'shadow' };
3503
3521
  }
3504
3522
 
3505
3523
  export type StewardLoopMode = 'off' | 'shadow' | 'live' | 'rescue';
3506
3524
 
3507
- export type SeatLoopMode = 'off' | 'shadow' | 'on';
3525
+ export type SeatLoopMode = 'off' | 'shadow' | 'on' | 'live-safe';
3508
3526
 
3509
3527
  export interface PersonaLoopConfig { enabled: boolean }
3510
3528
 
3511
3529
  export interface SeatLoopConfig {
3512
- /** No seat actions by default; shadow observes without execution, on permits the seat loop to act. */
3530
+ /** Shadow observes by default; live-safe acts only through the shared rescue policy, while legacy on retains its behavior. */
3513
3531
  mode: SeatLoopMode;
3532
+ /** Validated loop-agent neighbor entries keyed by the observing seat. */
3533
+ neighbors?: Partial<Record<'OP' | 'TC' | 'MK' | 'UX', import('./loops/neighbors.js').Neighbor[]>>;
3514
3534
  /** Seat-to-seat and human questions default to shadow even when the checklist loop is on. */
3515
3535
  questions?: 'shadow' | 'on';
3516
3536
  seats?: string[];
@@ -3553,6 +3573,8 @@ export interface HqConfig {
3553
3573
  standby?: string;
3554
3574
  /** Lease TTL in seconds (default 1500 = 25 min). */
3555
3575
  ttlSeconds?: number;
3576
+ /** Arbiter silence threshold (default 3); applying promotion requires explicit opt-in. */
3577
+ autoPromote?: { enabled?: boolean; streak?: number };
3556
3578
  /** Roles that may still run when this host is fenced only for lack of quorum (never when a newer generation exists). Absent = none. */
3557
3579
  failOpenRoles?: HqFenceRole[];
3558
3580
  /** Per-host nexus health URL over the tailnet (default https://<host>.<tailnetDomain>:31415/v1/health). */
@@ -3575,19 +3597,22 @@ export interface UserConfig {
3575
3597
  hq?: HqConfig;
3576
3598
  /** Root CLI help audience; absent or invalid means owner. */
3577
3599
  cli?: { helpRole?: 'owner' | 'contributor' | 'general' };
3600
+ /** Daemon public demonstration mode; absent means off. */
3601
+ nexus?: { demoMode?: boolean };
3578
3602
  /** Execution-phase HITL answer deadline; absent means 30 minutes. */
3579
3603
  hitl?: { executionDeadlineMinutes?: number };
3580
3604
  coo?: { linearProject?: string };
3581
- decisions?: { linearProjection: { enabled: boolean }; requireCrossCheck: boolean };
3605
+ decisions?: { linearProjection: { enabled: boolean }; requireCrossCheck: boolean;
3606
+ crossCheckNeighbor: Record<EventSeat, EventSeat>; crossCheckWaitMinutes: number };
3582
3607
  /** Per-seat goal and Pod limits; missing or invalid limits use seat-budget defaults. */
3583
3608
  org?: { budget?: Record<string, { dailyGoals?: number; concurrentPods?: number }> };
3584
- /** Steward and seat loops are parsed independently. An absent seat remains off. */
3585
- loops?: { orchestrator?: OrchestratorLoopConfig; steward?: { mode?: StewardLoopMode; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string>; alertAfterFailures?: number; launch?: 'off' | 'shadow' | 'live'; maxParallel?: number; podPool?: string }; seat?: SeatLoopConfig; persona?: PersonaLoopConfig };
3609
+ /** Steward and seat loops are parsed independently. An absent seat defaults to shadow. */
3610
+ loops?: { owners?: Record<string, EventSeat>; defaultOwner?: EventSeat; orchestrator?: OrchestratorLoopConfig; steward?: { mode?: StewardLoopMode; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string>; alertAfterFailures?: number; launch?: 'off' | 'shadow' | 'live'; maxParallel?: number; podPool?: string }; seat?: SeatLoopConfig; persona?: PersonaLoopConfig };
3586
3611
  /** 🔐 Pod 의 Grok API 키 과금은 명시 opt-in 만. 생략·잘못된 값은 false.
3587
3612
  * budgetGate 와 같은 `harness` 객체다 — 중복 선언이면 뒤 타입이 앞을 지운다. */
3588
- harness?: { pod?: { grokApiKeyOptIn: boolean }; budgetGate?: HarnessBudgetGateConfig; difficultyPlacement?: boolean; defaultRepo?: string; substrate?: 'local' | 'pod'; podPool?: string; worktreeAddTimeoutSec?: number; queue?: { seatCap?: Partial<Record<'OP' | 'TC' | 'MK' | 'UX', number>> } };
3613
+ harness?: { revertGuard?: { depth: number }; pod?: { grokApiKeyOptIn: boolean }; budgetGate?: HarnessBudgetGateConfig; exposeGate?: 'warn' | 'strict'; difficultyPlacement?: boolean; defaultRepo?: string; /** Optional GitHub repository name (owner/repo), distinct from the local defaultRepo path. */ repo?: string; substrate?: 'local' | 'pod'; podPool?: string; worktreeAddTimeoutSec?: number; queue?: { seatCap?: Partial<Record<'OP' | 'TC' | 'MK' | 'UX', number>> }; /** `harness say` 문장을 Pod 안에서 저작부터 돌릴지. 생략·false = 끔(호스트가 저작). */ authorOnPod?: boolean };
3589
3614
  /** ☸️ Pod 실행 칸 — `pool` = 기존 기본 풀(`컨텍스트[@ssh호스트][:상한][#k3d-레지스트리:포트]` 쉼표 · 앞이 우선). harness 실행은 인자·ELANOUS_POD_POOL·harness.podPool 다음으로 읽는다. */
3590
- pod?: { pool?: string; /** 호스트 Git 미러 디렉터리 — Pod Job 에 읽기 전용으로 마운트한다. */ hostMirror?: string; /** 원격 그라운딩 엔드포인트(호스트 nexus · tailnet 주소) — Pod 가 토큰으로 «질의→인용»만 묻는다(P13). */ groundingUrl?: string; /** 읽기 전용 Pod lease 권장에 쓰는 계정별 동시 수. */ lease?: { perAccount?: number }; /** 실측 권고를 Pod 발사 기본값으로 쓸지 (기본 off). */ memory?: { adviseDefaults?: boolean } };
3615
+ pod?: { pool?: string; /** 호스트 Git 미러 디렉터리 — Pod Job 에 읽기 전용으로 마운트한다. */ hostMirror?: string; /** 원격 그라운딩 엔드포인트(호스트 nexus · tailnet 주소) — Pod 가 토큰으로 «질의→인용»만 묻는다(P13). */ groundingUrl?: string; /** 읽기 전용 Pod lease 권장에 쓰는 계정별 동시 수. */ lease?: { perAccount?: number }; /** 실측 권고를 Pod 발사 기본값으로 쓸지 (기본 off). */ memory?: { adviseDefaults?: boolean }; /** 명령 Job 자식 로그(child.log) 상한 바이트. 양의 정수만. 넘으면 끝부분 우선. */ childLogMaxBytes?: number };
3591
3616
  skillRouter: SkillRouterConfig;
3592
3617
  llm: LLMConfig;
3593
3618
  skills: SkillsConfig;
@@ -3913,8 +3938,9 @@ function defaultConfig(): UserConfig {
3913
3938
  return {
3914
3939
  cli: { helpRole: 'owner' },
3915
3940
  coo: { linearProject: '외부 행정·큰 일 (COO)' },
3916
- decisions: { linearProjection: { enabled: false }, requireCrossCheck: false },
3917
- loops: { orchestrator: parseOrchestratorLoopConfig(undefined), steward: { mode: 'shadow' }, seat: { mode: 'off', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' }, persona: { enabled: false } },
3941
+ decisions: { linearProjection: { enabled: false }, requireCrossCheck: false,
3942
+ crossCheckNeighbor: { OP: 'TC', TC: 'OP', MK: 'OP', UX: 'OP' }, crossCheckWaitMinutes: 120 },
3943
+ loops: { owners: {}, defaultOwner: 'OP', orchestrator: parseOrchestratorLoopConfig(undefined), steward: { mode: 'shadow' }, seat: { mode: 'shadow', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' }, persona: { enabled: false } },
3918
3944
  skillRouter: { ...SR_DEFAULTS },
3919
3945
  llm: { ...LLM_DEFAULTS },
3920
3946
  skills: skillsDefaults(),
@@ -3945,7 +3971,7 @@ function defaultConfig(): UserConfig {
3945
3971
  },
3946
3972
  },
3947
3973
  voice: { stt: {}, tts: {}, vad: {}, chat: {}, discord: {}, telegram: {}, pwa: {} },
3948
- intake: { telegram: {}, discord: {} },
3974
+ intake: { eventMax: 10, telegram: {}, discord: {} },
3949
3975
  dashboard: {
3950
3976
  promptBank: { ...DASHBOARD_PROMPT_BANK_DEFAULTS },
3951
3977
  foldMode: 'task-unit',
@@ -3983,7 +4009,7 @@ function defaultConfig(): UserConfig {
3983
4009
  nativeStructure: { ...TOOLS_DEFAULTS.nativeStructure },
3984
4010
  selfImplement: { ...TOOLS_DEFAULTS.selfImplement },
3985
4011
  },
3986
- harness: { budgetGate: { ...DEFAULT_BUDGET_GATE }, difficultyPlacement: false },
4012
+ harness: { revertGuard: { depth: 50 }, budgetGate: { ...DEFAULT_BUDGET_GATE }, exposeGate: 'warn', difficultyPlacement: false },
3987
4013
  grounding: { sources: [] },
3988
4014
  raw: {},
3989
4015
  };
@@ -4394,20 +4420,95 @@ function parseStewardTracks(input: unknown): Record<string, string> | undefined
4394
4420
  return Object.keys(out).length ? out : undefined;
4395
4421
  }
4396
4422
 
4423
+ function parseDecisionsConfig(input: unknown): NonNullable<UserConfig['decisions']> {
4424
+ const defaults: Record<EventSeat, EventSeat> = { OP: 'TC', TC: 'OP', MK: 'OP', UX: 'OP' };
4425
+ const raw = input && typeof input === 'object' && !Array.isArray(input) ? input as Record<string, unknown> : {};
4426
+ const neighbors = { ...defaults };
4427
+ if (raw.crossCheckNeighbor !== undefined) {
4428
+ if (!raw.crossCheckNeighbor || typeof raw.crossCheckNeighbor !== 'object' || Array.isArray(raw.crossCheckNeighbor)) {
4429
+ warnUserConfigDrop('decisions.crossCheckNeighbor', '자리별 이웃 자리 객체가 아닙니다 — 기본값으로 진행합니다.');
4430
+ } else {
4431
+ const values = raw.crossCheckNeighbor as Record<string, unknown>;
4432
+ for (const seat of Object.keys(defaults) as EventSeat[]) {
4433
+ if (values[seat] === undefined) continue;
4434
+ const to = values[seat];
4435
+ if ((to === 'OP' || to === 'TC' || to === 'MK' || to === 'UX') && to !== seat) neighbors[seat] = to;
4436
+ else warnUserConfigDrop(`decisions.crossCheckNeighbor.${seat}`, '다른 자리가 아닙니다 — 기본값으로 진행합니다.');
4437
+ }
4438
+ for (const seat of Object.keys(values)) {
4439
+ if (!(seat in defaults)) warnUserConfigDrop(`decisions.crossCheckNeighbor.${seat}`, '알 수 없는 자리입니다 — 기본값으로 진행합니다.');
4440
+ }
4441
+ }
4442
+ }
4443
+ const wait = raw.crossCheckWaitMinutes;
4444
+ if (wait !== undefined && !(typeof wait === 'number' && Number.isSafeInteger(wait) && wait > 0)) {
4445
+ warnUserConfigDrop('decisions.crossCheckWaitMinutes', '양의 정수가 아닙니다 — 기본값 120으로 진행합니다.');
4446
+ }
4447
+ return { linearProjection: { enabled: (raw.linearProjection as { enabled?: unknown } | undefined)?.enabled === true },
4448
+ requireCrossCheck: raw.requireCrossCheck === true, crossCheckNeighbor: neighbors,
4449
+ crossCheckWaitMinutes: typeof wait === 'number' && Number.isSafeInteger(wait) && wait > 0 ? wait : 120 };
4450
+ }
4451
+
4452
+ function parseLoopOwners(input: unknown): { owners: Record<string, EventSeat>; defaultOwner: EventSeat } {
4453
+ const raw = input && typeof input === 'object' && !Array.isArray(input) ? input as Record<string, unknown> : {};
4454
+ const valid = (value: unknown): value is EventSeat => value === 'OP' || value === 'TC' || value === 'MK' || value === 'UX';
4455
+ const defaultOwner = valid(raw.defaultOwner) ? raw.defaultOwner : 'OP';
4456
+ if (raw.defaultOwner !== undefined && !valid(raw.defaultOwner)) warnUserConfigDrop('loops.defaultOwner', '잘못된 자리 — 기본 OP 로 진행');
4457
+ // Prototype-less: a loop titled «__proto__» (or «constructor») must keep its owner (review round 3).
4458
+ const owners: Record<string, EventSeat> = Object.create(null) as Record<string, EventSeat>;
4459
+ if (raw.owners !== undefined) {
4460
+ if (!raw.owners || typeof raw.owners !== 'object' || Array.isArray(raw.owners)) {
4461
+ warnUserConfigDrop('loops.owners', '객체가 아니다 — 기본 자리로 진행');
4462
+ } else {
4463
+ for (const [key, value] of Object.entries(raw.owners)) {
4464
+ if (key.trim() && valid(value)) owners[key] = value;
4465
+ else warnUserConfigDrop(`loops.owners.${key}`, `잘못된 자리 — 기본 ${defaultOwner} 로 진행`);
4466
+ }
4467
+ }
4468
+ }
4469
+ return { owners, defaultOwner };
4470
+ }
4471
+
4397
4472
  function parseSeatLoopsConfig(input: unknown): SeatLoopConfig {
4398
- const defaults: SeatLoopConfig = { mode: 'off', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' };
4473
+ const defaults: SeatLoopConfig = { mode: 'shadow', questions: 'shadow', seats: ['MK'], podPool: 'pool-node-b@node-b:8' };
4399
4474
  if (!input || typeof input !== 'object' || Array.isArray(input)) return defaults;
4400
4475
  const seat = (input as Record<string, unknown>).seat;
4401
4476
  if (!seat || typeof seat !== 'object' || Array.isArray(seat)) return defaults;
4402
4477
  const values = seat as Record<string, unknown>;
4403
4478
  const mode = values.mode;
4404
4479
  return {
4405
- mode: mode === 'on' || mode === 'shadow' ? mode : 'off',
4480
+ mode: mode === 'on' || mode === 'shadow' || mode === 'live-safe' || mode === 'off' ? mode : 'shadow',
4406
4481
  questions: values.questions === 'on' ? 'on' : 'shadow',
4407
4482
  seats: Array.isArray(values.seats) && values.seats.length > 0
4408
4483
  && values.seats.every((value) => typeof value === 'string' && /^(?:MK|OP|TC|UX)$/.test(value))
4409
4484
  ? [...new Set(values.seats as string[])] : ['MK'],
4410
4485
  podPool: typeof values.podPool === 'string' && values.podPool.trim() ? values.podPool.trim() : defaults.podPool,
4486
+ ...(() => {
4487
+ const raw = values.neighbors;
4488
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return {};
4489
+ const neighbors: NonNullable<SeatLoopConfig['neighbors']> = {};
4490
+ for (const [owner, entries] of Object.entries(raw)) {
4491
+ if (!/^(?:OP|TC|MK|UX)$/.test(owner) || !Array.isArray(entries)) continue;
4492
+ const valid = entries.filter((entry): entry is import('./loops/neighbors.js').Neighbor => {
4493
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;
4494
+ const value = entry as Record<string, unknown>;
4495
+ const heartbeat = value.heartbeat && typeof value.heartbeat === 'object' && !Array.isArray(value.heartbeat)
4496
+ ? value.heartbeat as Record<string, unknown> : undefined;
4497
+ const onAbsent = value.onAbsent && typeof value.onAbsent === 'object' && !Array.isArray(value.onAbsent)
4498
+ ? value.onAbsent as Record<string, unknown> : undefined;
4499
+ return typeof value.id === 'string' && /^(?:op|tc|mk|ux)-seat$/.test(value.id)
4500
+ && value.id !== `${owner.toLowerCase()}-seat`
4501
+ && Array.isArray(value.exchange) && value.exchange.length > 0
4502
+ && value.exchange.every((item) => typeof item === 'string' && ['status', 'request', 'decision', 'blocked'].includes(item))
4503
+ && !!heartbeat && Number.isSafeInteger(heartbeat.everyMinutes) && (heartbeat.everyMinutes as number) > 0
4504
+ && Number.isSafeInteger(heartbeat.missedTicks) && (heartbeat.missedTicks as number) > 0
4505
+ && !!onAbsent && typeof onAbsent.action === 'string' && ['delegate', 'defer', 'escalate'].includes(onAbsent.action)
4506
+ && typeof onAbsent.delegateTo === 'string' && onAbsent.delegateTo.trim().length > 0;
4507
+ });
4508
+ neighbors[owner as keyof typeof neighbors] = valid;
4509
+ }
4510
+ return { neighbors };
4511
+ })(),
4411
4512
  ...(typeof values.reportPr === 'number' && Number.isSafeInteger(values.reportPr) && values.reportPr > 0
4412
4513
  ? { reportPr: values.reportPr } : {}),
4413
4514
  ...(() => {
@@ -4472,6 +4573,13 @@ export function parseHqConfig(raw: unknown): HqConfig | undefined {
4472
4573
  }
4473
4574
  if (typeof r.ttlSeconds === 'number' && Number.isFinite(r.ttlSeconds) && r.ttlSeconds > 0) out.ttlSeconds = r.ttlSeconds;
4474
4575
  if (typeof r.probeTimeoutSeconds === 'number' && Number.isFinite(r.probeTimeoutSeconds) && r.probeTimeoutSeconds > 0) out.probeTimeoutSeconds = r.probeTimeoutSeconds;
4576
+ if (r.autoPromote && typeof r.autoPromote === 'object' && !Array.isArray(r.autoPromote)) {
4577
+ const auto = r.autoPromote as Record<string, unknown>;
4578
+ const setting: NonNullable<HqConfig['autoPromote']> = {};
4579
+ if (typeof auto.enabled === 'boolean') setting.enabled = auto.enabled;
4580
+ if (typeof auto.streak === 'number' && Number.isSafeInteger(auto.streak) && auto.streak > 0) setting.streak = auto.streak;
4581
+ if (Object.keys(setting).length) out.autoPromote = setting;
4582
+ }
4475
4583
  if (Array.isArray(r.failOpenRoles)) out.failOpenRoles = r.failOpenRoles.filter((v): v is HqFenceRole => HQ_FENCE_ROLES.includes(v as HqFenceRole));
4476
4584
  if (r.healthUrls && typeof r.healthUrls === 'object' && !Array.isArray(r.healthUrls)) {
4477
4585
  out.healthUrls = Object.fromEntries(Object.entries(r.healthUrls as Record<string, unknown>).filter((e): e is [string, string] => typeof e[1] === 'string'));
@@ -4598,11 +4706,15 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4598
4706
  };
4599
4707
  }
4600
4708
 
4709
+ const nexus = rawObj.nexus && typeof rawObj.nexus === 'object' && !Array.isArray(rawObj.nexus)
4710
+ ? rawObj.nexus as Record<string, unknown> : {};
4601
4711
  return {
4712
+ nexus: { demoMode: nexus.demoMode === true },
4602
4713
  ...(Object.keys(seatBudgets).length ? { org: { budget: seatBudgets } } : {}),
4603
4714
  pod: {
4604
4715
  ...(typeof legacyPod.pool === 'string' && legacyPod.pool.trim() ? { pool: legacyPod.pool.trim() } : {}),
4605
4716
  ...(typeof legacyPod.hostMirror === 'string' && legacyPod.hostMirror.trim() ? { hostMirror: legacyPod.hostMirror.trim() } : {}),
4717
+ ...(typeof legacyPod.childLogMaxBytes === 'number' && Number.isSafeInteger(legacyPod.childLogMaxBytes) && legacyPod.childLogMaxBytes > 0 ? { childLogMaxBytes: legacyPod.childLogMaxBytes } : {}),
4606
4718
  ...(typeof legacyPod.groundingUrl === 'string' ? { groundingUrl: legacyPod.groundingUrl } : {}),
4607
4719
  memory: { adviseDefaults: Boolean(legacyPod.memory && typeof legacyPod.memory === 'object' && !Array.isArray(legacyPod.memory)
4608
4720
  && (legacyPod.memory as Record<string, unknown>).adviseDefaults === true) },
@@ -4614,8 +4726,14 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4614
4726
  })() },
4615
4727
  },
4616
4728
  harness: {
4729
+ revertGuard: { depth: (() => {
4730
+ const value = harness.revertGuard && typeof harness.revertGuard === 'object' && !Array.isArray(harness.revertGuard)
4731
+ ? (harness.revertGuard as Record<string, unknown>).depth : undefined;
4732
+ return typeof value === 'number' && Number.isSafeInteger(value) && value > 0 ? value : 50;
4733
+ })() },
4617
4734
  pod: { grokApiKeyOptIn: harnessPod.grokApiKeyOptIn === true },
4618
4735
  difficultyPlacement: harness.difficultyPlacement === true,
4736
+ exposeGate: harness.exposeGate === 'strict' ? 'strict' : 'warn',
4619
4737
  queue: { seatCap: Object.fromEntries(Object.entries(
4620
4738
  harness.queue && typeof harness.queue === 'object' && !Array.isArray(harness.queue)
4621
4739
  && (harness.queue as Record<string, unknown>).seatCap && typeof (harness.queue as Record<string, unknown>).seatCap === 'object'
@@ -4629,6 +4747,9 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4629
4747
  ? { worktreeAddTimeoutSec: harness.worktreeAddTimeoutSec } : {}),
4630
4748
  ...(typeof harness.defaultRepo === 'string' && isAbsolute(harness.defaultRepo)
4631
4749
  ? { defaultRepo: harness.defaultRepo } : {}),
4750
+ ...(typeof harness.repo === 'string' && harness.repo.trim()
4751
+ ? { repo: harness.repo.trim() } : {}),
4752
+ ...(harness.authorOnPod === true ? { authorOnPod: true } : {}),
4632
4753
  budgetGate: parseHarnessBudgetGate(
4633
4754
  rawObj.harness && typeof rawObj.harness === 'object' && !Array.isArray(rawObj.harness)
4634
4755
  ? (rawObj.harness as Record<string, unknown>).budgetGate
@@ -5165,6 +5286,7 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
5165
5286
  },
5166
5287
  },
5167
5288
  intake: {
5289
+ eventMax: typeof intake.eventMax === 'number' && Number.isSafeInteger(intake.eventMax) && intake.eventMax >= 0 ? intake.eventMax : 10,
5168
5290
  telegram: {
5169
5291
  ...(normalizeIntakeAmbientCaptureMode(intakeTelegram.ambientCapture) !== undefined
5170
5292
  ? { ambientCapture: normalizeIntakeAmbientCaptureMode(intakeTelegram.ambientCapture)! }
@@ -5270,9 +5392,8 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
5270
5392
  cli: { helpRole: parseCliHelpRole(rawObj.cli) },
5271
5393
  coo: { linearProject: typeof (rawObj.coo as { linearProject?: unknown } | undefined)?.linearProject === 'string' && (rawObj.coo as { linearProject: string }).linearProject.trim()
5272
5394
  ? (rawObj.coo as { linearProject: string }).linearProject.trim() : '외부 행정·큰 일 (COO)' },
5273
- decisions: { linearProjection: { enabled: ((rawObj.decisions as { linearProjection?: { enabled?: unknown } } | undefined)?.linearProjection?.enabled === true) },
5274
- requireCrossCheck: (rawObj.decisions as { requireCrossCheck?: unknown } | undefined)?.requireCrossCheck === true },
5275
- loops: { ...(stewardLoops ?? {}), orchestrator: parseOrchestratorLoopConfig((rawObj.loops as { orchestrator?: unknown } | undefined)?.orchestrator), seat: parseSeatLoopsConfig(rawObj.loops),
5395
+ decisions: parseDecisionsConfig(rawObj.decisions),
5396
+ loops: { ...(stewardLoops ?? {}), ...parseLoopOwners(rawObj.loops), orchestrator: parseOrchestratorLoopConfig((rawObj.loops as { orchestrator?: unknown } | undefined)?.orchestrator), seat: parseSeatLoopsConfig(rawObj.loops),
5276
5397
  persona: { enabled: (rawObj.loops as { persona?: { enabled?: unknown } } | undefined)?.persona?.enabled === true } },
5277
5398
  events: parseEventsConfig(rawObj.events),
5278
5399
  // M1-1: sparse — undefined when the user hasn't set anything, so
@@ -5868,15 +5989,20 @@ export function saveUserConfig(
5868
5989
  ...rawHarness,
5869
5990
  pod: cfg.harness?.pod ?? rawHarness.pod,
5870
5991
  budgetGate: cfg.harness?.budgetGate ?? rawHarness.budgetGate,
5992
+ exposeGate: cfg.harness?.exposeGate ?? rawHarness.exposeGate,
5993
+ revertGuard: cfg.harness?.revertGuard ?? rawHarness.revertGuard,
5871
5994
  difficultyPlacement: cfg.harness?.difficultyPlacement ?? rawHarness.difficultyPlacement,
5872
5995
  defaultRepo: cfg.harness?.defaultRepo ?? rawHarness.defaultRepo,
5996
+ repo: cfg.harness?.repo,
5873
5997
  substrate: cfg.harness?.substrate,
5874
5998
  podPool: cfg.harness?.podPool,
5999
+ authorOnPod: cfg.harness?.authorOnPod === true ? true : undefined,
5875
6000
  queue: cfg.harness?.queue ?? rawHarness.queue,
5876
6001
  }),
5877
6002
  ...rawRest,
5878
6003
  ...(cfg.loops ? { loops: {
5879
6004
  ...rawLoops,
6005
+ owners: cfg.loops.owners ?? {}, defaultOwner: cfg.loops.defaultOwner ?? 'OP',
5880
6006
  ...(cfg.loops.steward ? { steward: { ...rawSteward, mode: cfg.loops.steward.mode ?? cfg.loops.steward.launch ?? 'shadow' } } : {}),
5881
6007
  ...(cfg.loops.orchestrator ? { orchestrator: cfg.loops.orchestrator } : {}),
5882
6008
  ...(cfg.loops.seat ? { seat: {
@@ -5884,6 +6010,7 @@ export function saveUserConfig(
5884
6010
  questions: cfg.loops.seat.questions ?? 'shadow',
5885
6011
  seats: cfg.loops.seat.seats ?? ['MK'],
5886
6012
  podPool: cfg.loops.seat.podPool ?? 'pool-node-b@node-b:8',
6013
+ ...(cfg.loops.seat.neighbors ? { neighbors: cfg.loops.seat.neighbors } : {}),
5887
6014
  ...(cfg.loops.seat.reportPr === undefined ? {} : { reportPr: cfg.loops.seat.reportPr }),
5888
6015
  } } : {}),
5889
6016
  } } : {}),
@@ -6104,6 +6231,7 @@ export function saveUserConfig(
6104
6231
  }),
6105
6232
  }),
6106
6233
  }),
6234
+ intake: { ...(rawRest.intake && typeof rawRest.intake === 'object' ? rawRest.intake as Record<string, unknown> : {}), ...cfg.intake },
6107
6235
  dashboard: stripUndef({
6108
6236
  views: cfg.dashboard.views,
6109
6237
  theme: cfg.dashboard.theme,
@@ -1 +1 @@
1
- {"commit":"5116219907580581e7bb40b93018d155deafa5c2"}
1
+ {"commit":"f89d24931e428797960330ae109e8919ee784a15"}
@@ -8,6 +8,14 @@
8
8
  import type { BashNode, NodeExecContext, NodeOutput, WorkflowDeps } from '../types.js';
9
9
  import { interpolate } from '../variables.js';
10
10
 
11
+ /** Keep the start of stderr and its real error line. Bun prints the source frame first. */
12
+ export function formatBashStderr(stderr: string): string {
13
+ const head = 200;
14
+ const tail = 800;
15
+ if (stderr.length <= head + tail) return stderr;
16
+ return `${stderr.slice(0, head)}…${stderr.slice(-tail)}`;
17
+ }
18
+
11
19
  export async function executeBashNode(
12
20
  node: BashNode,
13
21
  ctx: NodeExecContext,
@@ -30,7 +38,7 @@ export async function executeBashNode(
30
38
  return {
31
39
  ok: false,
32
40
  output: r.stdout, // surface partial output for $node.output access
33
- error: `bash exit ${r.exitCode}${r.stderr ? `: ${r.stderr.slice(0, 500)}` : ''}`,
41
+ error: `bash exit ${r.exitCode}${r.stderr ? `: ${formatBashStderr(r.stderr)}` : ''}`,
34
42
  durationMs: Date.now() - startedAt,
35
43
  };
36
44
  }