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
@@ -0,0 +1,118 @@
1
+ import { loopCronVerdict, type LoopCronVerdict } from './verdict';
2
+
3
+ const VERDICT_LABEL: Record<LoopCronVerdict, LoopRow['verdict']> = {
4
+ alive: '살아 있음', late: '늦음', failed: '실패', off: '꺼짐',
5
+ };
6
+
7
+ export interface LoopSchedule {
8
+ id: string;
9
+ name: string;
10
+ source: string;
11
+ category: string | null;
12
+ domain: string | null;
13
+ runVia: string | null;
14
+ cron: string | null;
15
+ intervalMs: number | null;
16
+ state: 'live' | 'firing' | 'stale' | 'off';
17
+ next: string[];
18
+ lastRun: { at: string; status: string | null; exit: number | null } | null;
19
+ }
20
+
21
+ export interface LoopRow {
22
+ id: string;
23
+ name: string;
24
+ layer: string;
25
+ owner: string;
26
+ mode: string;
27
+ lastRun: string | null;
28
+ verdict: '살아 있음' | '늦음' | '실패' | '꺼짐' | '판정 불가';
29
+ }
30
+
31
+ export const LOOP_SCHEDULES_PATH = '/v1/schedules?includeOff=1';
32
+ export const LOOPS_PATH = '/v1/dashboard/loops';
33
+
34
+ interface RecordedLoop {
35
+ name: string;
36
+ label: string;
37
+ category?: string;
38
+ armed: boolean | null;
39
+ last: { at: string; status: string } | null;
40
+ }
41
+
42
+ export async function loadLoopRows(fetchJson: (path: string) => Promise<unknown>, now: number): Promise<LoopRow[]> {
43
+ const [schedulesResult, loopsResult] = await Promise.allSettled([
44
+ fetchJson(LOOP_SCHEDULES_PATH), fetchJson(LOOPS_PATH),
45
+ ]);
46
+ const schedules = schedulesResult.status === 'fulfilled' ? (schedulesResult.value as { schedules?: unknown })?.schedules : null;
47
+ const loops = loopsResult.status === 'fulfilled' ? (loopsResult.value as { loops?: { loops?: unknown } | null })?.loops?.loops : null;
48
+ if (!Array.isArray(schedules) && !Array.isArray(loops)) throw new Error('loop registries unavailable');
49
+ if (!Array.isArray(schedules)) throw new Error('스케줄 레지스트리 조회 실패');
50
+ if (!Array.isArray(loops)) throw new Error('루프 레지스트리 조회 실패');
51
+ // These registries have different ID namespaces even when labels match.
52
+ const rows = loopRows(schedules as LoopSchedule[], now).map((row) => ({ ...row, id: `schedule:${row.id}` }));
53
+ for (const loop of loops as RecordedLoop[]) {
54
+ const last = loop.last ? Date.parse(loop.last.at) : NaN;
55
+ const verdict = loopCronVerdict({
56
+ enabled: loop.armed !== false,
57
+ lastRunAt: loop.last?.at ?? null,
58
+ lastStatus: loop.last?.status.toLowerCase() ?? null,
59
+ intervalMs: null,
60
+ }, now);
61
+ rows.push({
62
+ id: `loop:${loop.name}`, name: loop.label, layer: loop.category ?? '루프', owner: '미지정',
63
+ mode: loop.armed === null ? '관측' : loop.armed ? 'armed' : 'off',
64
+ lastRun: Number.isFinite(last) ? loop.last!.at : null,
65
+ verdict: verdict === 'alive' ? '판정 불가' : VERDICT_LABEL[verdict],
66
+ });
67
+ }
68
+ return rows;
69
+ }
70
+
71
+ export function createLoopRowsRefresh(
72
+ fetchJson: (path: string) => Promise<unknown>,
73
+ onResult: (result: { rows: LoopRow[]; state: 'ready' | 'error' }) => void,
74
+ clock: () => number = Date.now,
75
+ ) {
76
+ let sequence = 0;
77
+ let active = true;
78
+ return {
79
+ async refresh() {
80
+ const request = ++sequence;
81
+ try {
82
+ const rows = await loadLoopRows(fetchJson, clock());
83
+ if (active && request === sequence) onResult({ rows, state: 'ready' });
84
+ } catch {
85
+ if (active && request === sequence) onResult({ rows: [], state: 'error' });
86
+ }
87
+ },
88
+ dispose() { active = false; },
89
+ };
90
+ }
91
+
92
+ export function loopRows(schedules: readonly LoopSchedule[], now: number): LoopRow[] {
93
+ return schedules.map((schedule) => {
94
+ const last = schedule.lastRun ? Date.parse(schedule.lastRun.at) : NaN;
95
+ const sharedVerdict = loopCronVerdict({
96
+ enabled: schedule.state !== 'off',
97
+ lastRunAt: schedule.lastRun?.at ?? null,
98
+ lastStatus: schedule.lastRun?.exit != null && schedule.lastRun.exit !== 0
99
+ ? 'failed' : schedule.lastRun?.status != null && !['ok', 'success', 'completed'].includes(schedule.lastRun.status.toLowerCase())
100
+ ? 'failed' : schedule.lastRun?.status?.toLowerCase() ?? null,
101
+ intervalMs: schedule.cron ? null : schedule.intervalMs,
102
+ // A cron uses daemon calendar state; fixed intervals use the 2× interval rule.
103
+ ...(schedule.cron ? { scheduleState: schedule.state } : {}),
104
+ }, now);
105
+ const noCadence = !schedule.cron && !(schedule.intervalMs && schedule.intervalMs > 0);
106
+ const verdict = sharedVerdict === 'alive' && (!Number.isFinite(last) || noCadence)
107
+ ? '판정 불가' : VERDICT_LABEL[sharedVerdict];
108
+ return {
109
+ id: schedule.id,
110
+ name: schedule.name,
111
+ layer: schedule.domain || schedule.category || schedule.source,
112
+ owner: '미지정',
113
+ mode: schedule.runVia || schedule.source,
114
+ lastRun: Number.isFinite(last) ? schedule.lastRun!.at : null,
115
+ verdict,
116
+ };
117
+ });
118
+ }
@@ -1,20 +1,32 @@
1
1
  export type LoopCronVerdict = 'alive' | 'late' | 'failed' | 'off';
2
2
 
3
- /** Shared display inputs for a graph loop or a cron job. The caller supplies
4
- * the cadence in milliseconds (or null when it is not known). */
3
+ /** Shared display inputs for a graph loop or cron job. Fixed intervals use
4
+ * milliseconds; calendar cron schedules supply the second actual due instant. */
5
5
  export interface LoopCronVerdictInput {
6
6
  enabled: boolean;
7
7
  lastRunAt: string | null;
8
8
  lastStatus: string | null;
9
9
  intervalMs: number | null;
10
+ /** Second scheduled execution after the last run, for calendar-based cron schedules. */
11
+ secondDueAt?: string | null;
12
+ /** Cron-aware daemon state (src/domains/schedule-state.ts). Callers pass it for cron schedules only —
13
+ * a fixed interval keeps its own «2× interval» rule (review must-fix: stale must not override an interval). */
14
+ scheduleState?: 'live' | 'firing' | 'stale' | 'off';
10
15
  }
11
16
 
12
17
  /** Pure health classification; a failed last execution takes precedence over age.
13
- * With no valid timestamp or positive cadence, lateness cannot be established. */
18
+ * Without a valid last run and a cadence/due instant, lateness needs the daemon stale signal. */
14
19
  export function loopCronVerdict(entry: LoopCronVerdictInput, nowMs: number): LoopCronVerdict {
15
- if (!entry.enabled) return 'off';
16
- if (entry.lastStatus === 'failed' || entry.lastStatus === 'error' || entry.lastStatus === 'failure') return 'failed';
20
+ if (!entry.enabled || entry.scheduleState === 'off') return 'off';
21
+ if (entry.lastStatus === 'failed' || entry.lastStatus === 'error' || entry.lastStatus === 'failure' ||
22
+ entry.lastStatus === 'abandoned' || entry.lastStatus === 'expired') return 'failed';
23
+ if (entry.scheduleState === 'stale') return 'late';
17
24
  const lastRunMs = entry.lastRunAt === null ? NaN : Date.parse(entry.lastRunAt);
25
+ if (entry.secondDueAt !== undefined) {
26
+ const secondDueMs = entry.secondDueAt === null ? NaN : Date.parse(entry.secondDueAt);
27
+ return Number.isFinite(lastRunMs) && Number.isFinite(nowMs) &&
28
+ Number.isFinite(secondDueMs) && secondDueMs > lastRunMs && nowMs > secondDueMs ? 'late' : 'alive';
29
+ }
18
30
  if (Number.isFinite(nowMs) && Number.isFinite(lastRunMs) &&
19
31
  entry.intervalMs !== null && Number.isFinite(entry.intervalMs) && entry.intervalMs > 0 &&
20
32
  nowMs - lastRunMs > 2 * entry.intervalMs) return 'late';
@@ -0,0 +1,66 @@
1
+ import type { SlashCommand } from '../chat/index.js';
2
+ import type { Maturity } from './feature-maturity.js';
3
+ import type { BotSurface } from './bot-command-maturity.js';
4
+
5
+ export interface BotSlashCatalogEntry {
6
+ name: string;
7
+ description: string;
8
+ supported: boolean;
9
+ }
10
+
11
+ export interface BotSlashCatalog {
12
+ commands: BotSlashCatalogEntry[];
13
+ /** Null means this name is already handled, or is not in the catalog. */
14
+ unsupportedReply(name: string): string | null;
15
+ }
16
+
17
+ /** The menu is a projection of registered core commands, not a second list of invented names.
18
+ * A bot's actual handlers take precedence over maturity metadata: a grade describes
19
+ * visibility, never whether an existing command can be dispatched. */
20
+ export function buildBotSlashCatalog(options: {
21
+ surface: BotSurface;
22
+ coreCommands: readonly Pick<SlashCommand, 'name' | 'description'>[];
23
+ maturity: {
24
+ tuiSlash: Readonly<Record<string, Maturity>>;
25
+ telegramCommand: Readonly<Record<string, Maturity>>;
26
+ discordCommand: Readonly<Record<string, Maturity>>;
27
+ };
28
+ handledCommands: readonly { name: string; description: string }[];
29
+ }): BotSlashCatalog {
30
+ const { surface, coreCommands, maturity, handledCommands } = options;
31
+ const grades = surface === 'telegram' ? maturity.telegramCommand : maturity.discordCommand;
32
+ const validName = surface === 'telegram' ? /^[a-z0-9_]{1,32}$/ : /^[a-z0-9_-]{1,32}$/;
33
+ const commands: BotSlashCatalogEntry[] = [];
34
+ const seen = new Set<string>();
35
+ for (const command of handledCommands) {
36
+ if (seen.has(command.name)) continue;
37
+ commands.push({ name: command.name, description: command.description, supported: true });
38
+ seen.add(command.name);
39
+ }
40
+ const limit = surface === 'telegram' ? 100 : 25;
41
+ const candidateNames = new Set<string>();
42
+ const candidates = coreCommands.filter((command) => {
43
+ const name = command.name;
44
+ const grade = Object.hasOwn(grades, name) ? grades[name] : undefined;
45
+ if (seen.has(name) || candidateNames.has(name) || !validName.test(name) || grade === undefined || grade === 'broken' || grade === 'system') return false;
46
+ candidateNames.add(name);
47
+ return true;
48
+ });
49
+ if (commands.length + candidates.length > limit) {
50
+ throw new Error(`${surface} command catalog exceeds ${limit} slots`);
51
+ }
52
+ for (const command of candidates) {
53
+ const name = command.name;
54
+ const description = command.description.replace(/\s+/g, ' ').trim();
55
+ const suffix = ` (${surface === 'telegram' ? '텔레그램' : '디스코드'} 미지원)`;
56
+ commands.push({ name, description: `${description.slice(0, 100 - suffix.length)}${suffix}`, supported: false });
57
+ seen.add(name);
58
+ }
59
+ const unsupported = new Set(commands.filter((command) => !command.supported).map((command) => command.name));
60
+ return {
61
+ commands,
62
+ unsupportedReply(name) {
63
+ return unsupported.has(name) ? `/${name}은(는) ${surface === 'telegram' ? '텔레그램' : '디스코드'}에서 아직 지원되지 않습니다. TUI에서 /${name}을(를) 사용하세요.` : null;
64
+ },
65
+ };
66
+ }
@@ -41,9 +41,11 @@ export const FEATURE_MATURITY = {
41
41
  '/design-check': { pwa: 'tool' },
42
42
  '/control': { pwa: 'tool' },
43
43
  '/scheduler': { pwa: 'ops' },
44
+ '/loops': { pwa: 'beta' },
44
45
  '/ops/release': { pwa: 'ops' },
45
46
  '/ops/seats': { pwa: 'ops' },
46
47
  '/ops/checklist': { pwa: 'ops' },
48
+ '/ceo': { pwa: 'ops' },
47
49
  '/dashboard': { pwa: 'ops' },
48
50
  '/bots': { pwa: 'ops' },
49
51
  '/botlab': { pwa: 'ops' },
@@ -54,10 +56,10 @@ export const FEATURE_MATURITY = {
54
56
  },
55
57
  // Canonical CLI command names only; aliases inherit their command's grade.
56
58
  cliRoot: {
57
- pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta',
59
+ pty: 'beta', pr: 'beta', repo: 'beta', role: 'beta', machine: 'beta', 'agent-env': 'beta',
58
60
  where: 'stable', shadow: 'beta', llm: 'beta', storage: 'beta', leader: 'beta',
59
61
  pod: 'beta', browser: 'beta', questions: 'beta', usage: 'stable', live: 'beta',
60
- research: 'beta', release: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
62
+ research: 'beta', release: 'beta', freeze: 'beta', flow: 'beta', seat: 'beta', hq: 'beta', steward: 'beta', coord: 'beta', context: 'beta', 'model-watch': 'beta', doctor: 'stable',
61
63
  control: 'beta', resources: 'beta', hooks: 'beta', setup: 'stable', start: 'stable',
62
64
  grounding: 'beta', graph: 'beta', loop: 'stable', card: 'stable', 'launch-head': 'beta',
63
65
  a2a: 'beta', plugin: 'stable', make: 'beta', skills: 'beta', connect: 'beta', import: 'beta',
@@ -71,7 +73,7 @@ export const FEATURE_MATURITY = {
71
73
  provider: 'stable', 'provider:set': 'beta', 'provider:restore': 'beta',
72
74
  'provider:rotate': 'beta', 'provider:use': 'beta', 'status-bar': 'beta',
73
75
  schedule: 'beta', intake: 'beta', logs: 'stable', ad: 'beta', docs: 'beta',
74
- decisions: 'beta', directives: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
76
+ decisions: 'beta', directives: 'beta', claims: 'beta', lesson: 'beta', fleet: 'beta', ops: 'stable', outputs: 'beta', publish: 'beta',
75
77
  env: 'beta', 'agent-mission': 'beta', dev: 'beta', keys: 'beta', finance: 'beta',
76
78
  config: 'stable', tasks: 'stable', labels: 'beta', msg: 'beta', ask: 'stable',
77
79
  onboarding: 'beta', login: 'stable', session: 'beta', chat: 'stable', repl: 'stable',
@@ -110,6 +112,8 @@ export const FEATURE_MATURITY = {
110
112
  plugin: 'tool',
111
113
  widget: 'tool',
112
114
  log: 'tool',
115
+ loops: 'beta',
116
+ persona: 'beta', // G3 b — 대화별 페르소나(GOAL-BENCH B7 #23836)
113
117
  memory: 'beta',
114
118
  export: 'stable',
115
119
  delta: 'tool',
@@ -153,7 +157,7 @@ export const FEATURE_MATURITY = {
153
157
  ping: 'stable', provider: 'stable', sessions: 'stable', fork: 'stable', resume: 'stable',
154
158
  decisions: 'stable', work: 'stable', cancel: 'stable',
155
159
  skills: 'beta', skill: 'beta', digest: 'beta', intake: 'beta', ad: 'beta',
156
- taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', project: 'beta', wish: 'beta',
160
+ taste: 'beta', missions: 'beta', attach: 'beta', detach: 'beta', now: 'beta', loops: 'beta', project: 'beta', wish: 'beta',
157
161
  brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool', local: 'tool',
158
162
  harness: 'ops',
159
163
  // Existing commands outside the CXO four-grade menu list are owner-only.
@@ -162,7 +166,7 @@ export const FEATURE_MATURITY = {
162
166
  },
163
167
  discordCommand: {
164
168
  status: 'stable', sessions: 'stable', new: 'stable', fork: 'stable',
165
- persona: 'beta', poll: 'beta', attach: 'beta',
169
+ persona: 'beta', poll: 'beta', attach: 'beta', now: 'beta',
166
170
  brain: 'tool', cc: 'tool', cdx: 'tool', gem: 'tool',
167
171
  relay: 'ops', showroom: 'ops', bots: 'ops', bot: 'ops', screen: 'ops', chart: 'ops',
168
172
  routines: 'ops', botsay: 'ops', 'voice-join': 'ops', 'voice-leave': 'ops', 'voice-status': 'ops',
@@ -0,0 +1,31 @@
1
+ import { SLASH_COMMANDS, type SlashCommand } from '../chat/index.js';
2
+ import { FEATURE_MATURITY, type Maturity } from './feature-maturity.js';
3
+
4
+ export interface TuiSlashAvailability {
5
+ name: string;
6
+ maturity: Maturity;
7
+ telegram: boolean;
8
+ discord: boolean;
9
+ }
10
+
11
+ /** Project canonical TUI commands onto the maturity map and bot-surface grade maps.
12
+ * An absent bot grade means unavailable; a missing TUI grade is a registry error.
13
+ */
14
+ export function deriveTuiSlashAvailability(
15
+ commands: readonly Pick<SlashCommand, 'name'>[] = SLASH_COMMANDS,
16
+ grades: {
17
+ tuiSlash: Readonly<Record<string, Maturity>>;
18
+ telegramCommand: Readonly<Record<string, Maturity>>;
19
+ discordCommand: Readonly<Record<string, Maturity>>;
20
+ } = FEATURE_MATURITY,
21
+ ): TuiSlashAvailability[] {
22
+ return commands.map(({ name }) => {
23
+ if (!Object.hasOwn(grades.tuiSlash, name)) throw new Error(`TUI slash command /${name} has no maturity grade`);
24
+ return {
25
+ name,
26
+ maturity: grades.tuiSlash[name]!,
27
+ telegram: Object.hasOwn(grades.telegramCommand, name),
28
+ discord: Object.hasOwn(grades.discordCommand, name),
29
+ };
30
+ });
31
+ }
@@ -270,23 +270,54 @@ export class LogStore {
270
270
  this.db.run('CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level, ts_ms)');
271
271
  this.db.run('CREATE INDEX IF NOT EXISTS idx_logs_surface ON logs(surface, ts_ms)');
272
272
  this.db.run('CREATE INDEX IF NOT EXISTS idx_logs_category ON logs(category, ts_ms)');
273
+ this.db.run(`CREATE TABLE IF NOT EXISTS pod_log_imports (
274
+ job TEXT NOT NULL,
275
+ line INTEGER NOT NULL,
276
+ PRIMARY KEY (job, line)
277
+ )`);
278
+ }
279
+
280
+ /** Insert each returned Pod line at most once per job, atomically with its receipt. */
281
+ insertPodBatch(job: string, rows: Array<{ rec: LogRecord; surface: string; line: number }>): number {
282
+ if (rows.length === 0) return 0;
283
+ const receipt = this.db.prepare('INSERT OR IGNORE INTO pod_log_imports (job, line) VALUES (?, ?)');
284
+ return this.db.transaction(() => {
285
+ const unseen = rows.filter(({ line }) => receipt.run(job, line).changes > 0);
286
+ this.insertBatch(unseen, { preserveRows: true });
287
+ return unseen.length;
288
+ })();
273
289
  }
274
290
 
275
291
  /** 배치 insert(단일 트랜잭션). 실패는 throw — fail-soft 는 호출측(StoreSink)
276
292
  * 책임(ops-log 의 recordOpsEvent/Safe 분업 동형). */
277
- insertBatch(rows: Array<{ rec: LogRecord; surface: string }>): void {
293
+ insertBatch(rows: Array<{ rec: LogRecord; surface: string }>, opts: { preserveRows?: boolean } = {}): void {
278
294
  if (rows.length === 0) return;
279
295
  const stmt = this.db.prepare(
280
296
  `INSERT INTO logs (ts, ts_ms, level, instance, host_id, surface, category, event, session_id, trace_id, data)
281
297
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
282
298
  );
299
+ const decisionRow = this.db.prepare(`SELECT id FROM logs WHERE instance = ? AND category = 'loop.steward' AND event = 'decision'
300
+ AND json_valid(data) AND json_extract(data, '$.runId') = ? ORDER BY id DESC LIMIT 1`);
301
+ const updateDecision = this.db.prepare(`UPDATE logs SET ts = ?, ts_ms = ?, level = ?, surface = ?, data = ? WHERE id = ?`);
283
302
  const insertAll = this.db.transaction((batch: Array<{ rec: LogRecord; surface: string }>) => {
284
303
  for (const { rec, surface } of batch) {
285
304
  const tsMs = Date.parse(rec.ts);
305
+ const timestamp = Number.isFinite(tsMs) ? tsMs : Date.now();
306
+ const level = deriveLogLevel(rec);
307
+ const data = rec.data !== undefined ? JSON.stringify(rec.data) : null;
308
+ const runId = rec.category === 'loop.steward' && rec.event === 'decision' &&
309
+ rec.data && typeof rec.data === 'object' && !Array.isArray(rec.data)
310
+ ? (rec.data as Record<string, unknown>).runId : undefined;
311
+ const existing = !opts.preserveRows && typeof runId === 'string' && runId
312
+ ? decisionRow.get(this.instance, runId) as { id: number } | undefined : undefined;
313
+ if (existing) {
314
+ updateDecision.run(rec.ts, timestamp, level, surface, data, existing.id);
315
+ continue;
316
+ }
286
317
  stmt.run(
287
318
  rec.ts,
288
- Number.isFinite(tsMs) ? tsMs : Date.now(),
289
- deriveLogLevel(rec),
319
+ timestamp,
320
+ level,
290
321
  this.instance,
291
322
  rec.data && typeof rec.data === 'object' && !Array.isArray(rec.data)
292
323
  && typeof (rec.data as Record<string, unknown>).hostId === 'string'
@@ -297,7 +328,7 @@ export class LogStore {
297
328
  rec.event,
298
329
  (rec as { session_id?: string }).session_id ?? null,
299
330
  rec.trace_id ?? null,
300
- rec.data !== undefined ? JSON.stringify(rec.data) : null,
331
+ data,
301
332
  );
302
333
  }
303
334
  });
@@ -4,6 +4,7 @@
4
4
  // GET /v1/dashboard/timeline — 신호 타임라인 (?hours=48&floor=6)
5
5
  // GET /v1/dashboard/heatmap — 자산×국가×섹터 매력도 + US 섹터 + 뉴스 분포
6
6
  // GET /v1/dashboard/digs — 디깅 리포트 피드 (?limit=20)
7
+ // GET /v1/dashboard/loop-activity — 루프 활동 그래프 (?since=24h)
7
8
  //
8
9
  // 전부 read-only · 캐시/로컬 SQLite 만 (dashboard-data.ts) — 라이브 fetch 없음.
9
10
  // PWA 폴링(60s) 대상. reflection.ts 패턴(CORS·checkAuth seam) 상속.
@@ -16,12 +17,15 @@ import {
16
17
  } from '../../domains/dashboard-data.js';
17
18
  import { readLiveSnapshot } from '../../domains/market-live.js';
18
19
  import { marketSessions } from '../../domains/finance.js';
20
+ import { loopActivity } from '../../loops/activity.js';
19
21
 
20
22
  export interface DashboardRouteOpts {
21
23
  /** Auth check seam. */
22
24
  checkAuth?: (req: Request) => boolean;
23
25
  /** Test seam — 실제 detached 수집 spawn 대체. */
24
26
  spawnCollector?: () => void;
27
+ /** Read-only activity provider for isolated route tests. */
28
+ loopActivity?: typeof loopActivity;
25
29
  }
26
30
 
27
31
  const CORS_HEADERS: Record<string, string> = {
@@ -43,7 +47,7 @@ export function parseDashboardPath(pathname: string): string | null {
43
47
  const m = /^\/v1\/dashboard\/([^/]+)$/.exec(pathname);
44
48
  if (!m) return null;
45
49
  const seg = decodeURIComponent(m[1]!);
46
- return ['summary', 'timeline', 'heatmap', 'digs', 'schedules', 'ontology', 'backtest', 'loops', 'ops'].includes(seg) ? seg : null;
50
+ return ['summary', 'timeline', 'heatmap', 'digs', 'schedules', 'ontology', 'backtest', 'loops', 'loop-activity', 'ops'].includes(seg) ? seg : null;
47
51
  }
48
52
 
49
53
  /** POST /v1/dashboard/refresh-live — 온디맨드 라이브 재수집 트리거 (refresh 버튼 설계 ·
@@ -99,6 +103,7 @@ export function handleDashboard(req: Request, section: string, opts: DashboardRo
99
103
  case 'ontology': return jsonResponse({ ok: true, ontology: dashboardOntology() }, 200);
100
104
  case 'backtest': return jsonResponse({ ok: true, backtest: dashboardBacktest() }, 200);
101
105
  case 'loops': return jsonResponse({ ok: true, loops: dashboardLoops() }, 200);
106
+ case 'loop-activity': return jsonResponse({ ok: true, activity: (opts.loopActivity ?? loopActivity)({ since: q.get('since') ?? '24h' }) }, 200);
102
107
  case 'ops': {
103
108
  const mid = q.get('mission');
104
109
  if (mid) return jsonResponse({ ok: true, missionDetail: dashboardOpsMission(mid) }, 200);
@@ -162,6 +162,7 @@ import {
162
162
  } from './vault-api.js';
163
163
  import { handleBuildsGet, parseBuildsPath } from './builds-api.js';
164
164
  import { handleHarnessAskPost, handleHarnessAskStatusGet, handleHarnessRunEventsGet, handleHarnessRunScreenGet, handleHarnessRunsGet, handleHarnessStopPost } from './harness-api.js';
165
+ import { handleLoopEdgesGet } from './loop-edges.js';
165
166
  import { handleFabricPlans, type FabricPlansRouteOpts } from './fabric-plans.js';
166
167
  import { dispatchPersonaRoute } from './personas.js';
167
168
  import { handleMe } from './operator.js';
@@ -184,7 +185,9 @@ import { handleChannelBotsGet, handleChannelBotSet } from './setup-channel-bot.j
184
185
  import { handleLlmRoutePredict } from './llm-route-predict.js';
185
186
  import type { MissionRouter } from '../../llm/mission-router.js';
186
187
  import { handleContextFetchUrl } from './context-url.js';
187
- import { contextNow } from '../../context-bus/context-now.js';
188
+ import { contextNow, type ContextNowDeps } from '../../context-bus/context-now.js';
189
+ import type { ContextNowAudience } from '../../context-bus/context-now-surfaces.js';
190
+ import { renderVoiceNow } from '../../context-bus/context-now-surfaces.js';
188
191
  import type { IntentPredictionService } from '../../intent-prediction/index.js';
189
192
  import type { NotificationActionLoopback } from '../../web-push/notification-action-loopback.js';
190
193
  import { handlePlatforms } from './platforms.js';
@@ -486,6 +489,8 @@ export interface NexusHttpServerOpts {
486
489
  eventBus: NexusEventBus;
487
490
  /** Test seam for the read-only usage collector; production uses the cached handler. */
488
491
  usageHandler?: () => Promise<Response>;
492
+ /** Test seam for the context-now ledgers; production reads the live ledgers. */
493
+ contextNowDeps?: ContextNowDeps;
489
494
  /** Supervisor handle — required for the write API (PR ι). When omitted
490
495
  * (skipSupervisor mode), mutation routes return 503. */
491
496
  supervisor?: Supervisor;
@@ -1291,6 +1296,15 @@ export async function routeRequest(
1291
1296
  return handleDevices(req, opts.devices);
1292
1297
  }
1293
1298
 
1299
+ if (pathname === '/v1/loops/edges' && method === 'GET') {
1300
+ if (!opts.metaApi?.bearerToken) return jsonResponse({ error: 'unauthorized' }, 401);
1301
+ const offered = req.headers.get('authorization');
1302
+ if (!offered?.startsWith('Bearer ') || !compareTokenConstTime(offered.slice('Bearer '.length).trim(), opts.metaApi.bearerToken)) {
1303
+ return jsonResponse({ error: 'unauthorized' }, 401);
1304
+ }
1305
+ return handleLoopEdgesGet(req);
1306
+ }
1307
+
1294
1308
  if (pathname === '/v1/harness/runs' && method === 'GET') {
1295
1309
  if (!opts.metaApi) return jsonResponse({ error: 'meta-api-runtime-not-wired' }, 503);
1296
1310
  if (!checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
@@ -2878,8 +2892,24 @@ export async function routeRequest(
2878
2892
  if (result) return result;
2879
2893
  }
2880
2894
  if (pathname === '/v1/context/now') {
2881
- if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
2882
- return jsonResponse(contextNow({ ...(url.searchParams.has('topic') ? { topic: url.searchParams.get('topic') ?? '' } : {}) }));
2895
+ try {
2896
+ if (!opts.metaApi || !checkAuth(req, opts.metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
2897
+ const audiences = url.searchParams.getAll('audience');
2898
+ if (audiences.length > 1) return jsonResponse({ error: 'invalid_audience' }, 400);
2899
+ const requested = audiences[0];
2900
+ if (requested !== undefined && requested !== 'operator' && requested !== 'user' && requested !== 'public-demo') {
2901
+ return jsonResponse({ error: 'invalid_audience' }, 400);
2902
+ }
2903
+ const forced = getUserConfig().nexus?.demoMode === true;
2904
+ const audience: ContextNowAudience = forced ? 'public-demo' : requested ?? 'operator';
2905
+ const answer = contextNow({ audience, ...(url.searchParams.has('topic') ? { topic: url.searchParams.get('topic') ?? '' } : {}) }, opts.contextNowDeps);
2906
+ debug.log('context.now', 'served', { audience, hidden: answer.hiddenCount ?? 0, forced });
2907
+ return jsonResponse(url.searchParams.get('format') === 'voice'
2908
+ ? { text: renderVoiceNow(answer, audience), audience, hiddenCount: answer.hiddenCount ?? 0 }
2909
+ : { ...answer, audience, hiddenCount: answer.hiddenCount ?? 0 });
2910
+ } catch {
2911
+ return jsonResponse({ error: 'context-now-failed' }, 500);
2912
+ }
2883
2913
  }
2884
2914
  // §6.3 (2026-05-09) — context URL fetch (showroom).
2885
2915
  // POST /v1/context/fetch-url