elanous 0.2.10 → 0.2.12

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 (615) 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/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  8. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  18. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  19. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  24. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-5c05b0da4bc1392c.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-a67e11ba962337b5.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-793fa536b3a6ef37.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-e297866051595d81.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/{2ea96958-e8c7e112de50b067.js → f48170cc-d05d28bf87a43bf4.js} +1 -1
  99. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +1 -0
  105. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
  106. package/apps/pwa/out/approvals/index.html +1 -1
  107. package/apps/pwa/out/approvals/index.txt +23 -23
  108. package/apps/pwa/out/autopilot/index.html +1 -1
  109. package/apps/pwa/out/autopilot/index.txt +23 -23
  110. package/apps/pwa/out/board/index.html +1 -1
  111. package/apps/pwa/out/board/index.txt +23 -23
  112. package/apps/pwa/out/botlab/index.html +1 -1
  113. package/apps/pwa/out/botlab/index.txt +22 -22
  114. package/apps/pwa/out/bots/index.html +1 -1
  115. package/apps/pwa/out/bots/index.txt +22 -22
  116. package/apps/pwa/out/chat/index.html +1 -1
  117. package/apps/pwa/out/chat/index.txt +22 -22
  118. package/apps/pwa/out/consult/index.html +1 -1
  119. package/apps/pwa/out/consult/index.txt +23 -23
  120. package/apps/pwa/out/control/index.html +1 -1
  121. package/apps/pwa/out/control/index.txt +23 -23
  122. package/apps/pwa/out/design-check/index.html +1 -1
  123. package/apps/pwa/out/design-check/index.txt +23 -23
  124. package/apps/pwa/out/editor/index.html +1 -1
  125. package/apps/pwa/out/editor/index.txt +23 -23
  126. package/apps/pwa/out/exec/index.html +1 -1
  127. package/apps/pwa/out/exec/index.txt +23 -23
  128. package/apps/pwa/out/field/index.html +1 -1
  129. package/apps/pwa/out/field/index.txt +23 -23
  130. package/apps/pwa/out/healthz +1 -1
  131. package/apps/pwa/out/index.html +1 -1
  132. package/apps/pwa/out/index.txt +22 -22
  133. package/apps/pwa/out/inside/index.html +1 -0
  134. package/apps/pwa/out/inside/index.txt +31 -0
  135. package/apps/pwa/out/intake/index.html +1 -1
  136. package/apps/pwa/out/intake/index.txt +23 -23
  137. package/apps/pwa/out/live/index.html +1 -1
  138. package/apps/pwa/out/live/index.txt +23 -23
  139. package/apps/pwa/out/market/index.html +1 -1
  140. package/apps/pwa/out/market/index.txt +22 -22
  141. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  142. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  143. package/apps/pwa/out/morning/index.html +1 -1
  144. package/apps/pwa/out/morning/index.txt +23 -23
  145. package/apps/pwa/out/observatory/index.html +1 -1
  146. package/apps/pwa/out/observatory/index.txt +23 -23
  147. package/apps/pwa/out/ops/checklist/index.html +1 -1
  148. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  149. package/apps/pwa/out/ops/release/index.html +1 -1
  150. package/apps/pwa/out/ops/release/index.txt +22 -22
  151. package/apps/pwa/out/ops/seats/index.html +1 -1
  152. package/apps/pwa/out/ops/seats/index.txt +22 -22
  153. package/apps/pwa/out/outputs/index.html +1 -0
  154. package/apps/pwa/out/outputs/index.txt +30 -0
  155. package/apps/pwa/out/reflection/index.html +1 -1
  156. package/apps/pwa/out/reflection/index.txt +23 -23
  157. package/apps/pwa/out/scheduler/index.html +1 -1
  158. package/apps/pwa/out/scheduler/index.txt +23 -23
  159. package/apps/pwa/out/sessions/index.html +1 -1
  160. package/apps/pwa/out/sessions/index.txt +22 -22
  161. package/apps/pwa/out/settings/devices/index.html +1 -1
  162. package/apps/pwa/out/settings/devices/index.txt +23 -23
  163. package/apps/pwa/out/settings/index.html +1 -1
  164. package/apps/pwa/out/settings/index.txt +23 -23
  165. package/apps/pwa/out/setup/done/index.html +1 -1
  166. package/apps/pwa/out/setup/done/index.txt +23 -23
  167. package/apps/pwa/out/setup/index.html +1 -1
  168. package/apps/pwa/out/setup/index.txt +23 -23
  169. package/apps/pwa/out/share/index.html +1 -1
  170. package/apps/pwa/out/share/index.txt +23 -23
  171. package/apps/pwa/out/showroom/index.html +1 -1
  172. package/apps/pwa/out/showroom/index.txt +22 -22
  173. package/apps/pwa/out/tasks/index.html +1 -1
  174. package/apps/pwa/out/tasks/index.txt +23 -23
  175. package/apps/pwa/out/term/index.html +1 -1
  176. package/apps/pwa/out/term/index.txt +23 -24
  177. package/apps/pwa/out/today/index.html +1 -1
  178. package/apps/pwa/out/today/index.txt +23 -23
  179. package/apps/pwa/out/trace/index.html +1 -1
  180. package/apps/pwa/out/trace/index.txt +23 -23
  181. package/apps/pwa/out/vault/index.html +1 -1
  182. package/apps/pwa/out/vault/index.txt +23 -23
  183. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  184. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  185. package/apps/pwa/out/workflows/index.html +1 -1
  186. package/apps/pwa/out/workflows/index.txt +23 -23
  187. package/apps/pwa/out/workspace/index.html +1 -1
  188. package/apps/pwa/out/workspace/index.txt +23 -24
  189. package/apps/pwa/out/worktrees/index.html +1 -1
  190. package/apps/pwa/out/worktrees/index.txt +23 -23
  191. package/graphs/context/nightly-condense.yaml +21 -0
  192. package/graphs/context/recipes.yaml +12 -0
  193. package/graphs/demo/inside-seed.yaml +20 -0
  194. package/graphs/demo/recipes.yaml +12 -0
  195. package/graphs/drafts/draft-cleanup.yaml +19 -0
  196. package/graphs/drafts/recipes.yaml +23 -0
  197. package/graphs/intake/intake-daily.yaml +2 -1
  198. package/graphs/ops/recipes.yaml +3 -0
  199. package/graphs/ops/secret-exposure.yaml +24 -0
  200. package/graphs/release/recipes.yaml +3 -0
  201. package/graphs/release/release-loop.yaml +4 -1
  202. package/graphs/video/explainer-line.yaml +2 -2
  203. package/package.json +2 -1
  204. package/plugins/card-followup/examples/sample-card.png +0 -0
  205. package/plugins/elanous-hwp/README.md +25 -0
  206. package/plugins/elanous-hwp/cli.ts +22 -0
  207. package/plugins/elanous-hwp/converter.ts +237 -0
  208. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  209. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  210. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  211. package/plugins/elanous-hwp/plugin.json +16 -0
  212. package/plugins/elanous-hwp/plugin.ts +40 -0
  213. package/scripts/ci-public-export-leak-gate.ts +75 -14
  214. package/scripts/ci-public-leak-gate.ts +44 -9
  215. package/scripts/context-hooks/claude-stop.sh +4 -0
  216. package/scripts/context-hooks/emit.ts +24 -0
  217. package/scripts/context-hooks/post-tool-use.sh +4 -0
  218. package/scripts/coord-channel-watch.sh +12 -0
  219. package/scripts/coord-requests-bridge.ts +143 -0
  220. package/scripts/field-feed/lib.ts +4 -0
  221. package/scripts/fleet/node-setup.sh +20 -3
  222. package/scripts/homebrew-formula-bump.ts +69 -0
  223. package/scripts/lib/telegram-inject-args.ts +31 -0
  224. package/scripts/marketing/measure-site-numbers.ts +169 -0
  225. package/scripts/measure-gate-shards.ts +309 -0
  226. package/scripts/measure-pod-memory-by-goal.ts +130 -0
  227. package/scripts/mission-request-judge.ts +31 -1
  228. package/scripts/morning-report.ts +2 -2
  229. package/scripts/opportunity-scan.ts +1 -1
  230. package/scripts/release-loop/auto-approve-node.ts +21 -4
  231. package/scripts/release-loop/checklist-gate-node.ts +2 -1
  232. package/scripts/release-loop/cut-branch.ts +84 -0
  233. package/scripts/release-loop/gate-node.ts +22 -15
  234. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  235. package/scripts/release-loop/node-verdict.ts +3 -3
  236. package/scripts/release-loop/ops-upgrade-node.ts +47 -6
  237. package/scripts/release-loop/publish-node.ts +4 -1
  238. package/scripts/release-loop/tui-node-checks.ts +42 -0
  239. package/scripts/release-loop/tui-sim-node.ts +32 -8
  240. package/scripts/release-loop/unattended-release.ts +5 -3
  241. package/scripts/release-loop/version-node.ts +74 -13
  242. package/scripts/signal-digest-cycle.ts +2 -7
  243. package/scripts/signal-digest-photo.ts +11 -0
  244. package/scripts/tech1-public-measure.ts +94 -0
  245. package/scripts/telegram-inject.ts +14 -6
  246. package/scripts/test-diet/node.ts +1 -1
  247. package/scripts/test-diet/remeasure-list.ts +112 -0
  248. package/scripts/tui-regress.ts +2 -2
  249. package/scripts/ux/demo-rehearsal.ts +247 -0
  250. package/scripts/ux/demo-seed.ts +108 -0
  251. package/scripts/ux/lib/rehearsal-verdict.ts +80 -0
  252. package/skills/diagram-master/SKILL.md +366 -0
  253. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  254. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  255. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  256. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  257. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  258. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  259. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  260. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  261. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  262. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  263. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  264. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  265. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  266. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  267. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  268. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  269. package/skills/diagram-master/references/examples/README.md +69 -0
  270. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  271. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  272. package/skills/diagram-master/references/pyproject.toml +16 -0
  273. package/skills/diagram-master/references/quality-checklist.md +33 -0
  274. package/skills/diagram-master/references/routing-guide.md +164 -0
  275. package/skills/diagram-master/references/uv.lock +1356 -0
  276. package/skills/google-workspace/SKILL.md +33 -0
  277. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  278. package/skills/grill-me/SKILL.md +1 -1
  279. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  280. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  281. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  282. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  283. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  284. package/skills/omni-crawl/SKILL.md +21 -11
  285. package/skills/omni-crawl/scripts/main.ts +6 -6
  286. package/skills/omni-crawl/src/router.ts +3 -3
  287. package/skills/omni-digest/SKILL.md +17 -9
  288. package/skills/omni-digest/digest.md +2 -2
  289. package/skills/omni-digest/src/fetch.ts +30 -3
  290. package/skills/pitch-deck/SKILL.md +2 -2
  291. package/skills/pitch-deck/template.html +2 -2
  292. package/skills/youtube-master/SKILL.md +163 -0
  293. package/skills/youtube-master/package-lock.json +558 -0
  294. package/skills/youtube-master/package.json +13 -0
  295. package/skills/youtube-master/references/study-note-template.md +124 -0
  296. package/skills/youtube-master/scripts/main.ts +435 -0
  297. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  298. package/skills/youtube-master/src/audio.ts +63 -0
  299. package/skills/youtube-master/src/domain-staging.ts +50 -0
  300. package/skills/youtube-master/src/env.ts +36 -0
  301. package/skills/youtube-master/src/obsidian.ts +155 -0
  302. package/skills/youtube-master/src/router.ts +110 -0
  303. package/skills/youtube-master/src/stt.ts +199 -0
  304. package/skills/youtube-master/src/study-note.ts +175 -0
  305. package/skills/youtube-master/src/summarize.ts +358 -0
  306. package/skills/youtube-master/src/transcript.ts +76 -0
  307. package/skills/youtube-master/src/types.ts +91 -0
  308. package/skills/youtube-master/src/util.ts +93 -0
  309. package/skills/youtube-master/src/youtube.ts +70 -0
  310. package/skills/youtube-master/tsconfig.json +13 -0
  311. package/src/acp/server.ts +5 -2
  312. package/src/agent-mission/driver.ts +215 -23
  313. package/src/agent-mission/pty-decision.ts +71 -0
  314. package/src/agent-mission/review-loop.ts +30 -0
  315. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  316. package/src/autopilot/pty-control-loop.ts +4 -1
  317. package/src/autopilot/tool-registry.ts +2 -0
  318. package/src/boot/daemon-prompt-turn.ts +5 -0
  319. package/src/boot/daemon-runtime.ts +9 -3
  320. package/src/boot/daemon-tools/web-search.ts +3 -0
  321. package/src/card-followup/core.ts +109 -0
  322. package/src/chat/index.ts +2 -0
  323. package/src/cli/drive-input-jail.ts +101 -0
  324. package/src/cli/hooks-cli.ts +17 -3
  325. package/src/cli/make-cli.ts +43 -0
  326. package/src/cli/persona-cli.ts +98 -9
  327. package/src/cli/plugin-cli.ts +31 -22
  328. package/src/cli/pod-cli.ts +36 -9
  329. package/src/cli/pod-memory-advice.ts +35 -0
  330. package/src/cli/pr-cli.ts +51 -15
  331. package/src/cli/pty-drive-cli.ts +41 -7
  332. package/src/cli/pty-drive-verdict.ts +40 -0
  333. package/src/cli/release-cli.ts +63 -10
  334. package/src/cli/setup-cli.ts +43 -1
  335. package/src/cli/skills-cli.ts +17 -1
  336. package/src/context-bus/claude-hooks-install.ts +10 -0
  337. package/src/context-bus/context-now-surfaces.ts +65 -0
  338. package/src/context-bus/context-now.ts +104 -0
  339. package/src/context-bus/coord-events.ts +2 -2
  340. package/src/context-bus/external-events.ts +52 -0
  341. package/src/context-bus/long-term-memory.ts +108 -0
  342. package/src/context-bus/nightly-condense.ts +153 -0
  343. package/src/context-bus/session-events.ts +35 -0
  344. package/src/dashboard/dashboard-status-lines.ts +2 -0
  345. package/src/dashboard/index.ts +17 -4
  346. package/src/dashboard/slash-runtime/dashboard-handlers.ts +31 -1
  347. package/src/decisions/decision-cards.ts +10 -3
  348. package/src/decisions/decision-ledger.ts +13 -5
  349. package/src/discord/sprint21-runtime.ts +5 -1
  350. package/src/discord-self-message.ts +32 -0
  351. package/src/discord-slash-wire.ts +5 -1
  352. package/src/discord.ts +17 -1
  353. package/src/domains/core-tools.ts +3 -2
  354. package/src/domains/outbound-alert.ts +105 -32
  355. package/src/domains/outbound-log-probe.ts +8 -0
  356. package/src/domains/telegram-kind-route.ts +51 -13
  357. package/src/exec-requests/default-outputs.ts +25 -0
  358. package/src/exec-requests/planner.ts +10 -2
  359. package/src/exec-requests/runner.ts +20 -6
  360. package/src/exec-requests/store.ts +3 -0
  361. package/src/execution-loop/launch-gate.ts +109 -0
  362. package/src/field/field-reel.ts +5 -1
  363. package/src/field-note/index.ts +201 -0
  364. package/src/field-note/telegram.ts +142 -0
  365. package/src/field-note/transcribe.ts +248 -0
  366. package/src/graph-kinds/registry.ts +3 -3
  367. package/src/graph-runner/graph-cli.ts +61 -1
  368. package/src/graph-runner/graph-grow-llm.ts +47 -0
  369. package/src/graph-runner/graph-grow.ts +50 -0
  370. package/src/graph-runner/runner.ts +329 -33
  371. package/src/grok/agent-search.ts +8 -2
  372. package/src/guardian/secret-exposure-scan.ts +101 -0
  373. package/src/harness/harness-cli-command.ts +375 -13
  374. package/src/harness/harness-pod-dispatch.ts +43 -23
  375. package/src/harness/harness-seams.ts +38 -4
  376. package/src/harness/harness-stop.ts +23 -4
  377. package/src/harness/launch-registry.ts +91 -0
  378. package/src/harness/process-garbage.ts +5 -3
  379. package/src/hooks/consult-intake.ts +153 -0
  380. package/src/hooks/dispatch.ts +82 -0
  381. package/src/hooks/expose.ts +254 -0
  382. package/src/hooks/github-shadow.ts +108 -0
  383. package/src/hooks/providers.ts +71 -5
  384. package/src/hooks/queue.ts +1 -1
  385. package/src/hooks/receiver.ts +94 -16
  386. package/src/index.ts +76 -14
  387. package/src/intake-plane/categorize.ts +18 -46
  388. package/src/intake-plane/discord-seat-work.ts +38 -2
  389. package/src/intake-plane/front-route-classifier.ts +33 -39
  390. package/src/intake-plane/persona-answer.ts +30 -0
  391. package/src/intake-plane/runtime-callables.ts +8 -1
  392. package/src/intake-plane/telegram-seat-work.ts +36 -2
  393. package/src/intake-plane/wish-card.ts +33 -0
  394. package/src/intake-plane/wish-folder.ts +67 -0
  395. package/src/intent-gate/gate.ts +30 -10
  396. package/src/live/detail-switch.ts +5 -0
  397. package/src/llm/difficulty.ts +57 -0
  398. package/src/llm/judge-layer.ts +101 -0
  399. package/src/llm/mission-router.ts +33 -19
  400. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  401. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  402. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  403. package/src/loops/contract/loop-agent.schema.json +138 -0
  404. package/src/loops/contract/validate.ts +64 -0
  405. package/src/loops/registry.ts +20 -6
  406. package/src/market/signed-index.ts +4 -0
  407. package/src/maturity/bot-command-maturity.ts +45 -0
  408. package/src/maturity/feature-maturity.ts +27 -4
  409. package/src/model-tier/index.ts +2 -2
  410. package/src/model-tier/tier-resolver.ts +20 -20
  411. package/src/model-tier/types.ts +6 -6
  412. package/src/mss/logging/log-store.ts +15 -0
  413. package/src/nexus/api/config-model-tier.ts +5 -4
  414. package/src/nexus/api/fabric-plans.ts +53 -0
  415. package/src/nexus/api/harness-api.ts +5 -1
  416. package/src/nexus/api/health.ts +2 -0
  417. package/src/nexus/api/http-server.ts +67 -8
  418. package/src/nexus/api/inside-events.ts +226 -0
  419. package/src/nexus/api/meta-api.ts +21 -8
  420. package/src/nexus/api/missions.ts +6 -1
  421. package/src/nexus/api/ops-seats.ts +33 -0
  422. package/src/nexus/api/outbound-report.ts +40 -7
  423. package/src/nexus/api/outputs-route.ts +44 -0
  424. package/src/nexus/api/personas.ts +95 -53
  425. package/src/nexus/api/seat-requests.ts +44 -2
  426. package/src/nexus/api/sessions-store.ts +17 -38
  427. package/src/nexus/api/sub-seat-charters.ts +34 -0
  428. package/src/nexus/api/tabs-mutations.ts +3 -1
  429. package/src/nexus/api/task-cards-api.ts +19 -0
  430. package/src/nexus/api/trace.ts +18 -6
  431. package/src/nexus/api/workflow-history.ts +88 -0
  432. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  433. package/src/nexus/api/workflows.ts +132 -6
  434. package/src/nexus/index.ts +2 -0
  435. package/src/nexus/kinds/webterm.ts +2 -1
  436. package/src/nexus/outbound/delivery-ledger.ts +17 -8
  437. package/src/nexus/outbound/router.ts +8 -3
  438. package/src/nexus/webterm/pty.ts +6 -2
  439. package/src/onboarding/cli-auth-status.ts +80 -0
  440. package/src/onboarding/voice-ai.ts +3 -3
  441. package/src/org/seat-budget.ts +48 -0
  442. package/src/org/stall-escalation.ts +63 -0
  443. package/src/outputs/ledger.ts +120 -0
  444. package/src/persona/persona-todo.ts +79 -0
  445. package/src/persona/presets.ts +123 -2
  446. package/src/persona/team-set.ts +155 -0
  447. package/src/plugins/install/plugin-install.ts +14 -0
  448. package/src/plugins/maker/market-bundle.ts +110 -0
  449. package/src/plugins/maker/plugin-maker.ts +254 -11
  450. package/src/plugins/maker/wizard-generate.ts +103 -0
  451. package/src/pod-lease/admission.ts +150 -0
  452. package/src/pod-lease/dependency-state.ts +85 -0
  453. package/src/pod-lease/host-lease.ts +127 -0
  454. package/src/project/project-suggestion.ts +16 -0
  455. package/src/project/session-context.ts +24 -0
  456. package/src/release-loop/checklist.ts +17 -5
  457. package/src/release-loop/feature-store.ts +29 -13
  458. package/src/release-loop/landed-but-yellow.ts +48 -0
  459. package/src/release-loop/next-md-merge.ts +58 -0
  460. package/src/release-loop/release-note.ts +59 -4
  461. package/src/seat-dispatch/seat-ask.ts +130 -0
  462. package/src/seat-dispatch/seat-questions.ts +100 -0
  463. package/src/seat-loop/seat-loop.ts +709 -29
  464. package/src/seat-loop/seat-report.ts +37 -2
  465. package/src/self-dev/dev-cli.ts +4 -1
  466. package/src/self-dev/dev-pipeline.ts +37 -1
  467. package/src/self-dev/draft-sweep.ts +79 -40
  468. package/src/self-dev/draft-triage-rules.ts +62 -7
  469. package/src/self-dev/fabric-plan-core.ts +297 -0
  470. package/src/self-dev/orchestrate.ts +36 -4
  471. package/src/self-dev/release-path-guard.ts +62 -0
  472. package/src/self-dev/shard-aggregate.ts +230 -0
  473. package/src/self-dev/shard-placement.ts +60 -0
  474. package/src/self-dev/shard-plan.ts +48 -0
  475. package/src/self-dev/shard-pod-run.ts +77 -0
  476. package/src/self-dev/shard-run.ts +105 -0
  477. package/src/self-implement/budget-gate.ts +29 -12
  478. package/src/self-implement/gate-baseline.ts +1 -1
  479. package/src/self-implement/gate-candidate-ranking.ts +60 -0
  480. package/src/self-implement/gate-cli.ts +40 -1
  481. package/src/self-implement/gate-scope.ts +44 -13
  482. package/src/self-implement/graph-variant.ts +171 -0
  483. package/src/self-implement/harness-policy-gates.ts +1 -1
  484. package/src/self-implement/headless-elanous-driver.ts +27 -3
  485. package/src/self-implement/host-regate.ts +22 -1
  486. package/src/self-implement/importer-test-index.ts +2 -1
  487. package/src/self-implement/l8-merge-queue.ts +130 -0
  488. package/src/self-implement/orchestrator.ts +257 -9
  489. package/src/self-implement/route-consumer-test-index.ts +122 -0
  490. package/src/self-implement/seams.ts +72 -11
  491. package/src/self-implement/test-seams.ts +2 -0
  492. package/src/self-implement/uncommitted-work.ts +59 -0
  493. package/src/session/index.ts +36 -13
  494. package/src/skills/gift-install.ts +215 -0
  495. package/src/skills/tools/web-search.ts +4 -1
  496. package/src/steward/launch.ts +42 -26
  497. package/src/steward/steward-cards.ts +17 -9
  498. package/src/steward/triage.ts +46 -14
  499. package/src/task-cards/card-cli.ts +25 -1
  500. package/src/task-orchestrator/mission.ts +5 -0
  501. package/src/task-orchestrator/store.ts +12 -3
  502. package/src/task-orchestrator/surfaces/pod-command-job.ts +43 -17
  503. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  504. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  505. package/src/task-orchestrator/surfaces/self-implement-pod.ts +222 -40
  506. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  507. package/src/task-orchestrator/types.ts +2 -0
  508. package/src/telegram-card-followup.ts +16 -96
  509. package/src/telegram-commands.ts +31 -1
  510. package/src/telegram-project-command.ts +145 -0
  511. package/src/telegram-report.ts +21 -17
  512. package/src/telegram.ts +61 -8
  513. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  514. package/src/user-config.ts +110 -24
  515. package/src/version/packed-revision.json +1 -1
  516. package/src/web-search/grok.ts +7 -1
  517. package/src/web-search/index.ts +28 -1
  518. package/src/web-search/provider.ts +4 -0
  519. package/src/web-search/x-intent.ts +5 -0
  520. package/src/workflow-runtime/executor.ts +277 -47
  521. package/src/workflow-runtime/index.ts +1 -0
  522. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  523. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  524. package/src/workflow-runtime/schema.ts +79 -4
  525. package/src/workflow-runtime/types.ts +35 -4
  526. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  536. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  537. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  556. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  583. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  584. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  585. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  586. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  587. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  588. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  589. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  590. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  591. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  592. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  593. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  594. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  595. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  596. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  597. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  598. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  599. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  600. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  601. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  602. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  603. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  604. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  605. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  606. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  614. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  615. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → -6XrAoD3eU7NyMHAE4xPS}/_ssgManifest.js +0 -0
@@ -61,6 +61,9 @@ import { executionFooter } from './telegram-exec-footer.js';
61
61
  import { debug } from './debug/log.js';
62
62
  import { makeChunkProducer } from './session/streaming/chunk-producer.js';
63
63
  import { getUserConfig } from './user-config.js';
64
+ import { detectCard, runCardFollowup, NotACardError } from './card-followup/core.js';
65
+ import { localOcrText } from './telegram-card-followup.js';
66
+ import type { runGraph } from './graph-runner/runner.js';
64
67
 
65
68
  const SLASH_FOCUS_TURNS_DEFAULT = 8;
66
69
 
@@ -135,6 +138,7 @@ export interface DiscordSelfMessageDeps {
135
138
  * (legacy). */
136
139
  questionChannelFor?: (channelId: string) => import('./hitl/question.js').QuestionChannel;
137
140
  log?: (msg: string) => void;
141
+ cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
138
142
  }
139
143
 
140
144
  /** Compose the full discord self+interweave onMessage handler. */
@@ -352,6 +356,34 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
352
356
  // Voice text commands pass the guild gate for the voice adapter's
353
357
  // sake — never route them into the LLM turn (legacy behavior).
354
358
  if (/^\/voice-(join|leave|status)\b/.test(ctx.text.trim())) return undefined;
359
+ if (!ctx.text.trim() && ctx.attachments.length === 1 && ctx.attachments[0]!.contentType?.startsWith('image/')) {
360
+ const photo = ctx.attachments[0]!;
361
+ if (detectCard({ width: photo.width, height: photo.height, ocrText: null }).decision !== 'skip') {
362
+ const bot = deps.getBot();
363
+ if (bot) {
364
+ let imagePath: string;
365
+ let ocrText: string | null;
366
+ try {
367
+ imagePath = (await bot.downloadAttachment(photo)).localPath;
368
+ ocrText = await (deps.cardFollowupDeps?.ocrText ?? localOcrText)(imagePath);
369
+ } catch {
370
+ return '명함을 읽지 못했습니다';
371
+ }
372
+ if (detectCard({ width: photo.width, height: photo.height, ocrText }).decision === 'card') {
373
+ try {
374
+ const result = await runCardFollowup({ imagePath, ocrText,
375
+ ...(deps.cardFollowupDeps?.runGraph ? { runGraph: deps.cardFollowupDeps.runGraph } : {}),
376
+ ...(deps.cardFollowupDeps?.rootDir ? { rootDir: deps.cardFollowupDeps.rootDir() } : {}),
377
+ });
378
+ for (const reply of result.replies) await bot.sendMessage(ctx.channelId, reply);
379
+ return streamer ? '명함 정리를 완료했습니다.' : undefined;
380
+ } catch (error) {
381
+ if (!(error instanceof NotACardError)) return '명함을 읽지 못했습니다';
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
355
387
  // S1 session commands (!sessions·!new·!attach) — before delegation
356
388
  // and self-turn routing, mirroring telegram's command precedence.
357
389
  const sessionReply = handleSessionCommand(ctx);
@@ -21,6 +21,7 @@ import { makeCommandRest } from './discord/slash-registry.js';
21
21
  import { normalizeInteractionPayload } from './discord/slash-router.js';
22
22
  import type { SlashCommandSchema } from './discord/slash-types.js';
23
23
  import { debug } from './debug/log.js';
24
+ import { gateDiscordSchemas, readBotAudience } from './maturity/bot-command-maturity.js';
24
25
  import { resolveChannelBotToken } from './channel-bot-token.js';
25
26
 
26
27
  const REST_BASE = 'https://discord.com/api/v10';
@@ -121,8 +122,11 @@ export function buildDiscordSlashWire(deps: DiscordSlashWireDeps): DiscordSlashW
121
122
  if (!app.id) { log('[slash] application id unresolved — skip'); return; }
122
123
  const guilds = await (await fetchImpl(`${REST_BASE}/users/@me/guilds`, auth)).json() as Array<{ id: string }>;
123
124
  const rest = makeCommandRest({ token, ...(deps.__fetchImpl ? { fetchImpl: deps.__fetchImpl } : {}) });
125
+ const audience = readBotAudience((deps.userConfig.raw?.discord as { commandAudience?: unknown } | undefined)?.commandAudience);
126
+ const { schemas, hidden } = gateDiscordSchemas(ELANOUS_SLASH_COMMANDS, audience);
127
+ debug.log('discord.command', 'menu-filtered', { total: ELANOUS_SLASH_COMMANDS.length, shown: schemas.length - hidden, ...audience });
124
128
  for (const g of Array.isArray(guilds) ? guilds : []) {
125
- const registered = await rest.bulkOverwriteGuild(app.id, g.id, [...ELANOUS_SLASH_COMMANDS]);
129
+ const registered = await rest.bulkOverwriteGuild(app.id, g.id, schemas);
126
130
  log(`[slash] registered ${registered.length} commands in guild ${g.id}`);
127
131
  }
128
132
  }
package/src/discord.ts CHANGED
@@ -23,6 +23,7 @@
23
23
  import { markdownToTelegramHtml as _unusedMarkdownFn } from './telegram-format.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
24
24
  import { debug } from './debug/log.js';
25
25
  import { getUserConfig } from './user-config.js';
26
+ import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
26
27
  import { handleDiscordSeatWork, type DiscordSeatWorkDeps } from './intake-plane/discord-seat-work.js';
27
28
  import { parseSeatAddress } from './seat-address/seat-address.js';
28
29
 
@@ -226,6 +227,7 @@ export class DiscordBot {
226
227
  private ws: WebSocket | null = null;
227
228
  private running = false;
228
229
  private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
230
+ private seatAskTimer: ReturnType<typeof setInterval> | null = null;
229
231
  private lastHeartbeatAck = true;
230
232
  private lastSeq: number | null = null;
231
233
 
@@ -246,7 +248,15 @@ export class DiscordBot {
246
248
  this.onInteraction = opts.onInteraction ?? null;
247
249
  this.onReaction = opts.onReaction ?? null;
248
250
  this.onTriggerTap = opts.onTriggerTap ?? null;
249
- this.seatWorkDeps = opts.seatWorkDeps ?? {};
251
+ this.seatWorkDeps = { ...opts.seatWorkDeps, askDeps: {
252
+ ...opts.seatWorkDeps?.askDeps,
253
+ channel: 'discord',
254
+ send: opts.seatWorkDeps?.askDeps?.send ?? (async (origin, text) => {
255
+ if (origin.channel !== 'discord') throw new Error('seat ask belongs to another surface');
256
+ const posted = await this.sendMessage(origin.threadId ?? origin.channelId, text);
257
+ if (!posted) throw new Error('Discord seat ask reply was not posted');
258
+ }),
259
+ } };
250
260
  }
251
261
 
252
262
  /** Send a raw payload on the gateway WebSocket. Voice adapter uses
@@ -523,8 +533,12 @@ export class DiscordBot {
523
533
  async start(): Promise<void> {
524
534
  this.running = true;
525
535
  this.log(`discord bot starting (allowlist size ${this.allowedUsers.size})`);
536
+ this.seatAskTimer = setInterval(() => {
537
+ void deliverSeatAnswers(this.seatWorkDeps.askDeps!).catch((error) => this.log(`seat ask delivery failed: ${String(error)}`));
538
+ }, 10_000);
526
539
  while (this.running) {
527
540
  try {
541
+ await deliverSeatAnswers(this.seatWorkDeps.askDeps!);
528
542
  await this.runConnection();
529
543
  } catch (err: any) {
530
544
  this.log(`gateway error: ${err?.message ?? String(err)}`);
@@ -538,6 +552,8 @@ export class DiscordBot {
538
552
 
539
553
  stop(): void {
540
554
  this.running = false;
555
+ if (this.seatAskTimer) clearInterval(this.seatAskTimer);
556
+ this.seatAskTimer = null;
541
557
  if (this.ws) {
542
558
  try { this.ws.close(1000, 'client stop'); } catch { /* ignore */ }
543
559
  this.ws = null;
@@ -15,7 +15,7 @@ import { existsSync } from 'node:fs';
15
15
  import { Database } from 'bun:sqlite';
16
16
  import type { LLMToolSpec } from '../llm.js';
17
17
  import { debug } from '../debug/log.js';
18
- import { runRecall, type RecallSearch } from '../tool-runtime/self-cognition-runtimes.js';
18
+ import { runRecall, CONTEXT_NOW_SPEC, type RecallSearch } from '../tool-runtime/self-cognition-runtimes.js';
19
19
  import type { ToolRuntimeContext } from '../tool-runtime/types.js';
20
20
  import { SCHEDULE_MANAGE_SPEC, dispatchScheduleManage } from './schedule-manage-tool.js';
21
21
  import { SESSION_MANAGE_SPEC, dispatchSessionQuery } from './session-query-tool.js';
@@ -114,6 +114,7 @@ const CORE_TOOL_HANDLERS: Record<string, (args: Record<string, unknown>, onParti
114
114
  schedule_manage: dispatchScheduleManage,
115
115
  session_manage: (args) => dispatchSessionQuery(args), // 대화 세션 검색·열람·목록·삭제(내용/ID/텔레그램)
116
116
  memory_recall: dispatchMemoryRecall,
117
+ context_now: async (args) => (await import('../context-bus/context-now.js')).contextNow({ ...(typeof args.topic === 'string' ? { topic: args.topic } : {}) }),
117
118
  fact_check: dispatchFactCheck, // 팩트체크 캐스케이드(내부 발송원장→외부 X/레딧/웹)·도메인 무관 코어
118
119
  self_recall: dispatchSelfRecall, // self-awareness(elanous 구현 이력)·도메인 무관 코어
119
120
  autopilot_missions: dispatchAutopilotMissions, // 오토파일럿 계보 조회(AL3)·도메인 무관 코어
@@ -141,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
141
142
  },
142
143
  },
143
144
  };
144
- export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
145
+ export const CORE_TOOL_SPECS: LLMToolSpec[] = [SCHEDULE_MANAGE_SPEC, SESSION_MANAGE_SPEC, MEMORY_RECALL_SPEC, FACT_CHECK_SPEC, SELF_RECALL_BUDGET_SPEC, CONTEXT_NOW_SPEC, AUTOPILOT_MISSION_SPEC, OPS_STATUS_SPEC, SE_BUILD_SPEC, LOGS_QUERY_SPEC, MISSION_DECIDE_SPEC, COO_ADMIN_SPEC, RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC];
145
146
 
146
147
  export interface CoreTools {
147
148
  specs: LLMToolSpec[];
@@ -17,6 +17,8 @@ import { getUserConfig, type UserConfig } from '../user-config.js';
17
17
  import { resolveChannelBotToken } from '../channel-bot-token.js';
18
18
  import { isOperationalKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './telegram-kind-route.js';
19
19
  import { debug } from '../debug/log.js';
20
+ import { registerLogStoreSink, setLogInstanceName } from '../mss/logging/log-store.js';
21
+ import type { LogSink } from '../mss/logging/sink.js';
20
22
  import { resolveDaemonEndpoint } from '../nexus/daemon-endpoint.js';
21
23
  import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
22
24
  // ★ origin 되돌림(대표 2026-07-12) — 미션 알림을 발신 채널(메인 Q&A 봇)로 되돌린다. type-only
@@ -36,6 +38,29 @@ function nexusUrl(): string | null {
36
38
  const ACP_TOKEN_PATH = join(getElanousConfigDir(), 'acp-token');
37
39
  const DEFERRED_PATH = conatusPath('outbound_deferred.jsonl');
38
40
 
41
+ // CLI/cron do not inherit the daemon's StoreSink. Register once before the first
42
+ // send, but never attach a second sink to a daemon which already owns one.
43
+ let outboundLogSink: (() => void) | null = null;
44
+ function ensureOutboundLogSink(): void {
45
+ if (outboundLogSink || inProcessOutbound || process.env.NODE_ENV === 'test') return;
46
+ try {
47
+ const logs = getUserConfig().logs;
48
+ setLogInstanceName(logs.instanceName);
49
+ outboundLogSink = registerLogStoreSink((s: LogSink) => debug.registerSink(s), 'outbound', logs.retention);
50
+ if (outboundLogSink) process.once('exit', outboundLogSink);
51
+ } catch { /* observations must not prevent delivery */ }
52
+ }
53
+
54
+ function botLabel(token: string): string {
55
+ // Telegram bot IDs are public numeric IDs; never log the token's secret suffix.
56
+ const id = token.split(':', 1)[0];
57
+ return /^\d+$/.test(id ?? '') ? `telegram:${id}` : 'telegram:configured';
58
+ }
59
+
60
+ function safeObservationLabel(value: string): string {
61
+ return /^[a-zA-Z0-9_./-]{1,160}$/.test(value) ? value : 'unknown';
62
+ }
63
+
39
64
  // ── 발송 관측 (대표 지시 2026-07-15): 발송 시각·mode·밀림(burst/lag) 를 logs.db 에 남겨
40
65
  // "실시간인지 밀린 것인지" 를 `elanous logs --category outbound.send` 로 판단 가능하게. ──
41
66
  /** 밀림(버스트) 판정 — 최근 창 내 이 수 이상 발송이 몰리면 밀려 나가는 중으로 본다. */
@@ -61,7 +86,7 @@ function logSend(
61
86
  extra: Record<string, unknown> = {},
62
87
  opts?: { level?: 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'critical' },
63
88
  ): void {
64
- try { debug.log('outbound.send', mode, { kind, ...extra }, opts); } catch { /* fail-open */ }
89
+ try { debug.log('outbound.send', mode, { kind: safeObservationLabel(kind), ...extra }, opts); } catch { /* fail-open */ }
65
90
  }
66
91
 
67
92
  /** 밀림 경고 임계 env 이름 — 비교 값은 여기서만 읽는다(호출부에 리터럴을 박지 않는다). */
@@ -143,12 +168,13 @@ function resolveBotToken(botId?: string): string | null {
143
168
  }
144
169
 
145
170
  /** origin(발신 채널)으로 직접 발송. 실패하면 호출자가 report 폴백/재시도한다. */
146
- function deliverToOrigin(origin: MissionOrigin, text: string): boolean {
171
+ function deliverToOrigin(origin: MissionOrigin, text: string, onBot?: (bot: string) => void): boolean {
147
172
  if (origin.channel === 'discord') {
148
173
  if (!origin.channelId) return false;
149
174
  try {
150
175
  const token = resolveChannelBotToken('discord', getUserConfig())?.token;
151
176
  if (!token) return false;
177
+ onBot?.('discord:configured');
152
178
  const channelId = origin.discordThreadId || origin.channelId;
153
179
  const out = spillLongContent(text).text;
154
180
  for (let start = 0; start < out.length;) {
@@ -168,19 +194,21 @@ function deliverToOrigin(origin: MissionOrigin, text: string): boolean {
168
194
  if (origin.channel !== 'telegram' || origin.chatId == null) return false;
169
195
  const token = resolveBotToken(origin.botId);
170
196
  if (!token) return false;
197
+ onBot?.(botLabel(token));
171
198
  try { return sendTelegramRaw(token, origin.chatId, text, origin.threadId); } catch { return false; }
172
199
  }
173
200
 
174
201
  /** 보류분 일괄 발송 — 묶음 1건(아침 폭주 방지). 발송 건수 반환.
175
202
  * 무음 창 밖 첫 sendOutbound 가 자동 호출 + 06:31 플러시 크론이 보장. */
176
203
  export function flushDeferred(path = DEFERRED_PATH): number {
204
+ ensureOutboundLogSink();
177
205
  const observeFlush = (count: number, lagMin: number, kinds: string[]): void => {
178
206
  const lagWarnMin = flushLagWarnMin();
179
207
  const over = count > 0 && lagMin > lagWarnMin;
180
208
  logSend(
181
209
  'flush',
182
210
  'deferred-batch',
183
- { count, lagMin, kinds, path, lagWarnMin },
211
+ { count, lagMin, kinds: kinds.map(safeObservationLabel), path, lagWarnMin },
184
212
  over ? { level: 'warn' } : undefined,
185
213
  );
186
214
  };
@@ -209,14 +237,28 @@ export function flushDeferred(path = DEFERRED_PATH): number {
209
237
  ...list.map(i => `\n── [${kst(i.ts)} · ${i.kind}] ──\n${i.text}`),
210
238
  ].join('\n');
211
239
  observeFlush(items.length, lagMin, [...new Set(items.map(i => i.kind))]);
240
+ const sendBatch = (text: string, kind: string, origin?: MissionOrigin): boolean => {
241
+ let bot = 'unknown';
242
+ const path = origin
243
+ ? (deliverToOrigin(origin, text, label => { bot = label; }) ? 'origin' : false)
244
+ : deliver(text, kind, label => { bot = label; }, 'deferred-flush');
245
+ if (path === 'daemon') return true;
246
+ try {
247
+ debug.log('outbound.send', path ? 'sent' : 'failed', {
248
+ kind: safeObservationLabel(kind), source: 'deferred-flush', bot,
249
+ chars: text.length, path: path || 'undeliverable',
250
+ });
251
+ } catch { /* observation must not change delivery */ }
252
+ return path !== false;
253
+ };
212
254
  let delivered = 0;
213
- // origin 없는 finance 는 기존 report 묶음. 운영 보류분은 별도로 묶어 운영 봇 경계를 유지한다.
255
+ // origin 없는 매매 알림만 report 묶음. 그 외는 kind 별로 운영 봇 경계를 유지한다.
214
256
  const noOrigin = items.filter(i => !i.origin);
215
257
  const tradingBatch = noOrigin.filter(i => !isOperationalKind(i.kind));
216
- if (tradingBatch.length && deliver(fmt(tradingBatch), 'report')) delivered += tradingBatch.length;
258
+ if (tradingBatch.length && sendBatch(fmt(tradingBatch), 'report')) delivered += tradingBatch.length;
217
259
  for (const kind of new Set(noOrigin.filter(i => isOperationalKind(i.kind)).map(i => i.kind))) {
218
260
  const batch = noOrigin.filter(i => i.kind === kind);
219
- if (deliver(fmt(batch), kind)) delivered += batch.length;
261
+ if (sendBatch(fmt(batch), kind)) delivered += batch.length;
220
262
  }
221
263
  // origin 있는 것 = 발신 채널·스레드별로 묶어 서로 다른 수신자에게 섞이지 않게 발송.
222
264
  const groups = new Map<string, { origin: MissionOrigin; list: Item[] }>();
@@ -228,7 +270,10 @@ export function flushDeferred(path = DEFERRED_PATH): number {
228
270
  if (!groups.has(key)) groups.set(key, { origin: it.origin, list: [] });
229
271
  groups.get(key)!.list.push(it);
230
272
  }
231
- for (const g of groups.values()) { if (deliverToOrigin(g.origin, fmt(g.list))) delivered += g.list.length; }
273
+ for (const g of groups.values()) {
274
+ const kinds = new Set(g.list.map(i => i.kind));
275
+ if (sendBatch(fmt(g.list), kinds.size === 1 ? g.list[0]!.kind : 'mixed', g.origin)) delivered += g.list.length;
276
+ }
232
277
  // 전량 배달 성공 시에만 파일 제거(부분 실패는 다음 flush 재시도 — 성공분 중복은 드문 엣지 수용).
233
278
  if (delivered >= items.length) { try { unlinkSync(path); } catch { /* */ } }
234
279
  return delivered;
@@ -268,17 +313,19 @@ export function sendOutbound(text: string, kind = 'alert', origin?: (MissionOrig
268
313
  const source = origin?.surface || (callerPath
269
314
  ? relative(repoRoot, callerPath.startsWith('file://') ? fileURLToPath(callerPath) : callerPath).replace(/\\/g, '/')
270
315
  : 'unknown');
271
- const logOutcome = (event: 'sent' | 'deferred' | 'failed'): void => {
272
- try { debug.log('outbound.send', event, { kind, source, chars: text.length }); } catch { /* fail-open */ }
316
+ ensureOutboundLogSink();
317
+ let bot = origin?.channel === 'discord' ? 'discord:configured' : 'unknown';
318
+ const logOutcome = (event: 'sent' | 'deferred' | 'failed', path?: string): void => {
319
+ try { debug.log('outbound.send', event, { kind: safeObservationLabel(kind), source: safeObservationLabel(source), chars: text.length, bot, ...(path ? { path } : {}) }); } catch { /* fail-open */ }
273
320
  };
274
321
  // ★ 무음 우회(대표 2026-07-14) — 사용자가 최근(기본 30분) genuine 인텐트(타이핑/버튼탭)를 냈으면
275
322
  // 깨어있으므로 야간 무음이어도 즉시 발송(사용자 발원 흐름의 결과물이 아침까지 묶이지 않게).
276
323
  // 우회 시 flushDeferred 로 그간 보류분도 함께 전달(사용자가 지금 볼 수 있음).
277
324
  if (inQuietHours() && !userRecentlyActive()) {
278
325
  const queued = deferOutbound(text, kind, origin);
279
- recordOutbound(text, kind); // 보류도 논리적 발송 — 회상 대상(데몬 미경유라 클라 기록)
280
- logOutcome(queued ? 'deferred' : 'failed');
281
- return true; // 보류 시 수락 — 적재 오류에서도 기존 호출측 재시도 계약 유지
326
+ if (queued) recordOutbound(text, kind); // 성공한 보류만 회상 대상
327
+ logOutcome(queued ? 'deferred' : 'failed', queued ? 'deferred' : 'queue-failed');
328
+ return queued; // 적재가 성공했을 때만 보류 수락
282
329
  }
283
330
  const bypass = inQuietHours();
284
331
  if (bypass) console.error('[outbound] 야간 무음 우회 — 최근 사용자 활동(깨어있음) → 즉시 발송 + 보류분 flush');
@@ -288,16 +335,15 @@ export function sendOutbound(text: string, kind = 'alert', origin?: (MissionOrig
288
335
  const { recentCount, burst } = recentSendBurst();
289
336
  logSend(bypass ? 'quiet-bypass' : 'realtime', kind, { burst, recentCount, ...(burst ? { backlog: true } : {}) });
290
337
  // ★ origin 되돌림(무음 밖) — 발신 채널로 직접 발송. 성공 시 종료, 실패면 report 폴백.
291
- if (origin && deliverToOrigin(origin, text)) {
338
+ if (origin && deliverToOrigin(origin, text, (label) => { bot = label; })) {
292
339
  recordOutbound(text, kind);
293
- logOutcome('sent');
340
+ logOutcome('sent', 'origin');
294
341
  return true;
295
342
  }
296
- const path = deliver(text, kind);
297
- // 원장 기록은 정확히 1회. 데몬 경유(daemon)면 /v1/outbound 핸들러(outbound-report.ts)
298
- // 가 기록하므로 클라는 중복 금지 — 직접 폴백(direct·데몬 다운)만 클라가 기록.
343
+ const path = deliver(text, kind, (label) => { bot = label; }, source);
344
+ // 데몬 경유는 실제 라우터의 최종 결과만 기록한다. 직접 폴백과 미전달만 여기서 기록.
299
345
  if (path === 'direct') recordOutbound(text, kind);
300
- logOutcome(path === false ? 'failed' : 'sent');
346
+ if (path !== 'daemon') logOutcome(path === false ? 'failed' : 'sent', path === false ? 'undeliverable' : path);
301
347
  return path !== false;
302
348
  }
303
349
 
@@ -318,7 +364,14 @@ export function classifyDaemonResponse(j: unknown): DaemonPathClass {
318
364
  /** 데몬 경로를 못 쓴 이유를 남긴다. 관측 실패가 발송을 막지 않음(recordOutbound 과 같은 fail-soft).
319
365
  * 비-ok 분류는 크론 운영자가 읽는 표준 출력에도 한 줄 — 싱크 미등록 스크립트에서도 즉시 보이게. */
320
366
  function logDaemonPath(classification: DaemonPathClass, kind: string, extra: Record<string, unknown> = {}): void {
321
- try { debug.log('outbound.send', 'daemon-path', { classification, kind, ...extra }); } catch { /* fail-soft */ }
367
+ ensureOutboundLogSink();
368
+ try {
369
+ debug.log('outbound.send', 'daemon-path', {
370
+ classification, kind: safeObservationLabel(kind),
371
+ ...('hasToken' in extra ? { hasToken: extra.hasToken === true } : {}),
372
+ ...('inProcess' in extra ? { inProcess: extra.inProcess === true } : {}),
373
+ });
374
+ } catch { /* fail-soft */ }
322
375
  if (classification === 'ok') return;
323
376
  try { console.error(`[outbound] daemon-path ${classification}`); } catch { /* fail-soft */ }
324
377
  }
@@ -326,23 +379,33 @@ function logDaemonPath(classification: DaemonPathClass, kind: string, extra: Rec
326
379
  /** OB8 — the daemon registers its own in-process sender at startup. Inside the daemon, deliver() must never curl
327
380
  * its own `/v1/outbound`: the curl is synchronous, the event loop that would answer it is the one it blocks, and the
328
381
  * whole daemon freezes until the curl times out (~25 s · `unreachable`) — the message is lost (CS1 · 10-01). */
329
- export type InProcessOutbound = (text: string, kind: string) => Promise<boolean>;
382
+ export type InProcessOutbound = (text: string, kind: string, source?: string, deferFailureObservation?: boolean) => Promise<boolean>;
330
383
  let inProcessOutbound: InProcessOutbound | null = null;
331
384
  export function setInProcessOutbound(send: InProcessOutbound | null): void { inProcessOutbound = send; }
332
385
 
333
386
  /** elanous `/v1/outbound` 우선 → 실패 시 텔레그램 직접. 성공 경로 반환(원장 중복방지용). */
334
- export function deliver(text: string, kind = 'alert'): 'daemon' | 'direct' | false {
387
+ export function deliver(text: string, kind = 'alert', onBot?: (bot: string) => void, source?: string): 'daemon' | 'direct' | false {
335
388
  // 0) inside the daemon — route in-process, asynchronously; the caller's synchronous answer is «accepted».
336
389
  if (inProcessOutbound && process.env.SEND_VIA_ELANOUS !== '0') {
390
+ onBot?.('daemon:configured');
337
391
  const send = inProcessOutbound;
338
- void send(text, kind)
392
+ const fallback = (): void => {
393
+ let bot = 'unknown';
394
+ const ok = sendTelegramDirect(text, kind, {}, label => { bot = label; });
395
+ if (ok) recordOutbound(text, kind);
396
+ try { debug.log('outbound.send', ok ? 'sent' : 'failed', {
397
+ kind: safeObservationLabel(kind), source: safeObservationLabel(source ?? 'daemon-fallback'),
398
+ bot, path: ok ? 'direct' : 'undeliverable', chars: text.length,
399
+ }); } catch { /* fail-open */ }
400
+ };
401
+ void send(text, kind, source, true)
339
402
  .then((ok) => {
340
403
  logDaemonPath(ok ? 'ok' : 'rejected', kind, { inProcess: true });
341
- if (!ok && sendTelegramDirect(text, kind)) recordOutbound(text, kind);
404
+ if (!ok) fallback();
342
405
  })
343
- .catch((error: unknown) => {
344
- logDaemonPath('rejected', kind, { inProcess: true, error: error instanceof Error ? error.message : String(error) });
345
- if (sendTelegramDirect(text, kind)) recordOutbound(text, kind);
406
+ .catch(() => {
407
+ logDaemonPath('rejected', kind, { inProcess: true });
408
+ fallback();
346
409
  });
347
410
  return 'daemon';
348
411
  }
@@ -351,20 +414,22 @@ export function deliver(text: string, kind = 'alert'): 'daemon' | 'direct' | fal
351
414
  if (process.env.SEND_VIA_ELANOUS !== '0') {
352
415
  let token = '';
353
416
  try { if (existsSync(ACP_TOKEN_PATH)) token = readFileSync(ACP_TOKEN_PATH, 'utf-8').trim(); } catch { /* no token */ }
354
- const headers = ['Content-Type: application/json', ...(token ? [`Authorization: Bearer ${token}`] : [])];
417
+ const headers = ['Content-Type: application/json', 'X-Elanous-Client-Fallback: direct',
418
+ ...(source ? [`X-Elanous-Outbound-Source: ${safeObservationLabel(source)}`] : []),
419
+ ...(token ? [`Authorization: Bearer ${token}`] : [])];
355
420
  const nexus = nexusUrl();
356
421
  const j = nexus
357
422
  ? curlPost(`${nexus}/v1/outbound`, JSON.stringify({ text, markdown: false, kind }), headers)
358
423
  : null;
359
424
  const classification = classifyDaemonResponse(j);
360
- if (classification === 'ok') return 'daemon';
425
+ if (classification === 'ok') { onBot?.('daemon:configured'); return 'daemon'; }
361
426
  daemonPath = nexus ? classification : 'not-found';
362
427
  const extra: Record<string, unknown> = { hasToken: token.length > 0 };
363
428
  if (j && typeof j === 'object' && 'error' in (j as object)) extra.error = (j as { error?: unknown }).error;
364
429
  logDaemonPath(classification, kind, extra);
365
430
  }
366
431
  // 2) fallback: 텔레그램 sendMessage 직접(3900자 분할) — 데몬 미경유라 클라가 원장 기록.
367
- if (sendTelegramDirect(text, kind)) return 'direct';
432
+ if (sendTelegramDirect(text, kind, {}, onBot)) return 'direct';
368
433
  reportUndeliverable(kind, daemonPath);
369
434
  return false;
370
435
  }
@@ -379,7 +444,8 @@ function reportUndeliverable(kind: string, daemonPath: DaemonPathClass | 'not-fo
379
444
  root = effectiveInstanceRoot();
380
445
  universe = root === prodInstanceRoot() ? 'prod' : 'test';
381
446
  } catch { /* the report still goes out */ }
382
- try { debug.log('outbound.send', 'undeliverable', { kind, daemonPath, universe, root }); } catch { /* fail-soft */ }
447
+ ensureOutboundLogSink();
448
+ try { debug.log('outbound.send', 'undeliverable', { kind: safeObservationLabel(kind), daemonPath, universe, root }); } catch { /* fail-soft */ }
383
449
  const hint = universe === 'prod'
384
450
  ? '운영 데몬이 떠 있는지 확인: elanous nexus show'
385
451
  : '운영으로 보내려면 설치본 elanous 로 실행하거나 ELANOUS_STATE_DIR=~/.elanous ELANOUS_CONFIG_DIR=~/.elanous 를 준다';
@@ -411,6 +477,7 @@ function sendTelegramRaw(token: string, chatId: string | number, text: string, t
411
477
  export function sendTelegramDirect(
412
478
  text: string, kind?: string,
413
479
  deps: { config?: UserConfig; sendRaw?: typeof sendTelegramRaw; legacyEnv?: typeof conatusEnv } = {},
480
+ onBot?: (bot: string) => void,
414
481
  ): boolean {
415
482
  const sendRaw = deps.sendRaw ?? sendTelegramRaw;
416
483
  let cfg: UserConfig | undefined;
@@ -418,8 +485,13 @@ export function sendTelegramDirect(
418
485
  if (cfg) {
419
486
  try {
420
487
  const target = kindRouteTarget(cfg, kind);
421
- if (target) return sendRaw(target.botToken, String(target.chatId), text);
488
+ if (target) { onBot?.(botLabel(target.botToken)); return sendRaw(target.botToken, String(target.chatId), text); }
422
489
  } catch { /* unavailable channel routing: fall through */ }
490
+ // A declared table is authoritative: missing roles cannot escape to an env bot.
491
+ if (cfg.telegram.channels?.length) {
492
+ logKindRouteFallback(kind, 'none', false);
493
+ return false;
494
+ }
423
495
  }
424
496
  if (isOperationalKind(kind)) {
425
497
  let home: ReturnType<typeof mainHomeTarget> = null;
@@ -432,7 +504,7 @@ export function sendTelegramDirect(
432
504
  } catch { /* unavailable main bot */ }
433
505
  const rcToken = cfg?.telegram.reportChannel?.botToken;
434
506
  logKindRouteFallback(kind, home ? 'main-home' : 'none', !!mainToken && !!cfg?.telegram.reportChannel && (!rcToken || rcToken === mainToken));
435
- if (home) return sendRaw(home.botToken, String(home.chatId), text);
507
+ if (home) { onBot?.(botLabel(home.botToken)); return sendRaw(home.botToken, String(home.chatId), text); }
436
508
  console.error('[outbound] 운영 kind 메인 봇/홈 미설정 — 콘솔 출력\n' + text);
437
509
  return false;
438
510
  }
@@ -445,5 +517,6 @@ export function sendTelegramDirect(
445
517
  }
446
518
  logKindRouteFallback(kind, tok && chat ? 'conatus-env' : 'none', false);
447
519
  if (!tok || !chat) { console.error('[outbound] 토큰/chat 미설정 — 콘솔 출력\n' + text); return false; }
520
+ onBot?.(botLabel(tok));
448
521
  return sendRaw(tok, chat, text);
449
522
  }
@@ -0,0 +1,8 @@
1
+ import { sendOutbound, setInProcessOutbound } from './outbound-alert.js';
2
+ import { setUserConfigOverlay } from '../user-config.js';
3
+
4
+ // Focused subprocess fixture: isolated state/config roots and a fake curl on PATH.
5
+ setUserConfigOverlay(cfg => ({ ...cfg, telegram: { ...cfg.telegram, botToken: '123456:private-secret', homeChannel: 98765 } }));
6
+ process.env.SEND_VIA_ELANOUS = '0';
7
+ setInProcessOutbound(null);
8
+ if (!sendOutbound('SECRET-BODY-DO-NOT-LOG', 'ops-alert', { channel: 'cli', surface: process.env.OUTBOUND_PROBE_SOURCE ?? 'outbound-log-probe' })) process.exitCode = 1;
@@ -1,22 +1,27 @@
1
1
  // 발송 목적(kind) → 텔레그램 채널 역할 · 대상. 가벼운 모듈(telegram.ts 를 import 하지 않는다 — outbound-alert 순환 방지).
2
- // 표에 없는 kind 는 undefined → 부르는 쪽이 폴백을 판정한다(투자 알림은 옛 reportChannel 유지).
2
+ // 명시적으로 매매 kind 인 경우만 trading; 나머지는 system. 역할 채널이 없으면 다른 역할로 폴백하지 않는다.
3
3
  import type { UserConfig } from '../user-config.js';
4
4
  import { resolveChannelBotToken } from '../channel-bot-token.js';
5
5
  import { debug } from '../debug/log.js';
6
- import { channelForRole, resolveTelegramChannels } from './telegram-channels.js';
6
+ import { resolveTelegramChannels } from './telegram-channels.js';
7
7
 
8
8
  export const DEFAULT_KIND_ROLES: Readonly<Record<string, string>> = {
9
+ report: 'report', alert: 'report', digest: 'report',
9
10
  intake: 'system', 'ops-report': 'system', 'ops-alert': 'system', 'ops-health': 'system',
10
- regression: 'system', 'agent-mission': 'system',
11
+ regression: 'system', 'agent-mission': 'system', brief: 'system', 'op-report': 'system',
11
12
  };
12
13
 
13
- /** No channel mapping for an operational kind may cross into a different bot. */
14
+ /** Only named finance delivery kinds can use the trading/report bot. */
15
+ export function isTradingKind(kind?: string): boolean {
16
+ return kind === 'report' || kind === 'alert' || kind === 'digest';
17
+ }
18
+
14
19
  export function isOperationalKind(kind?: string): boolean {
15
- return kind?.startsWith('ops-') ?? false;
20
+ return !isTradingKind(kind);
16
21
  }
17
22
 
18
23
  export function mainHomeTarget(cfg: UserConfig): { botToken: string; chatId: number } | null {
19
- const botToken = resolveChannelBotToken('telegram', cfg).token;
24
+ const botToken = operationsBotToken(cfg);
20
25
  const chatId = cfg.telegram.homeChannel ?? cfg.telegram.allowedUsers[0];
21
26
  return botToken && chatId != null && Number.isFinite(chatId) ? { botToken, chatId } : null;
22
27
  }
@@ -25,15 +30,48 @@ export function logKindRouteFallback(kind: string | undefined, to: 'report-chann
25
30
  try { debug.log('telegram.kind-route', 'fallback', { kind, to, sameBot }); } catch { /* logging must not prevent delivery */ }
26
31
  }
27
32
 
28
- export function roleForKind(cfg: UserConfig, kind?: string): string | undefined {
29
- if (!kind) return undefined;
30
- return cfg.telegram.kindRoles?.[kind] ?? DEFAULT_KIND_ROLES[kind];
33
+ function operationsBotToken(cfg: UserConfig): string | undefined {
34
+ const main = resolveChannelBotToken('telegram', cfg);
35
+ const channels = cfg.telegram.channels?.length ? resolveTelegramChannels(cfg.telegram) : [];
36
+ const tradingTokens = new Set(channels.filter(c => c.roles.includes('report')).map(c => c.botToken));
37
+ // The report (trading) bot is never the operations bot — with or without a channels table (review must-fix · OUT1).
38
+ if (cfg.telegram.reportChannel?.botToken) tradingTokens.add(cfg.telegram.reportChannel.botToken);
39
+ if (main.source !== 'channels.main') return main.token && !tradingTokens.has(main.token) ? main.token : undefined;
40
+ // channels.main may mean "first channel", which can be the trading bot.
41
+ // Only a named operations channel (or the named main channel) with an
42
+ // operational role establishes identity; array order and role alone do not.
43
+ const tokens = new Set(channels
44
+ .filter(c => (c.name === 'ops' && c.roles.includes('system'))
45
+ || (c.name === 'main' && c.roles.some(r => r === 'system' || r === 'qa' || r === 'default')))
46
+ .filter(c => !tradingTokens.has(c.botToken))
47
+ .map(c => c.botToken));
48
+ return tokens.size === 1 ? tokens.values().next().value : undefined;
49
+ }
50
+
51
+ function isAllowedBot(cfg: UserConfig, kind: string | undefined, token: string): boolean {
52
+ const mainToken = operationsBotToken(cfg);
53
+ if (!mainToken) return false;
54
+ if (!isTradingKind(kind)) return token === mainToken;
55
+ const reportToken = cfg.telegram.reportChannel?.botToken;
56
+ return token !== mainToken && (!reportToken || token === reportToken);
57
+ }
58
+
59
+ export function roleForKind(cfg: UserConfig, kind?: string): string {
60
+ const fallback = isTradingKind(kind) ? 'report' : 'system';
61
+ if (!kind || !cfg.telegram.channels?.length) return fallback;
62
+ const override = cfg.telegram.kindRoles?.[kind];
63
+ if (!override) return fallback;
64
+ const channel = resolveTelegramChannels(cfg.telegram)
65
+ .find(c => c.roles.includes(override) && isAllowedBot(cfg, kind, c.botToken));
66
+ return channel ? override : fallback;
31
67
  }
32
68
 
33
- /** The channel for a purpose kind — only with explicit `telegram.channels` and a mapped role. */
69
+ /** Exact role only: channelForRole's default/first fallback can cross bot boundaries. */
34
70
  export function kindRouteTarget(cfg: UserConfig, kind?: string): { botToken: string; chatId: number } | null {
71
+ if (!cfg.telegram.channels?.length) return null;
35
72
  const role = roleForKind(cfg, kind);
36
- if (!role || !cfg.telegram.channels?.length) return null;
37
- const ch = channelForRole(resolveTelegramChannels(cfg.telegram), role);
38
- return ch?.botToken && Number.isFinite(ch.chatId) ? { botToken: ch.botToken, chatId: ch.chatId } : null;
73
+ const ch = resolveTelegramChannels(cfg.telegram)
74
+ .find(c => c.roles.includes(role) && isAllowedBot(cfg, kind, c.botToken));
75
+ if (!ch || !Number.isFinite(ch.chatId)) return null;
76
+ return { botToken: ch.botToken, chatId: ch.chatId };
39
77
  }
@@ -0,0 +1,25 @@
1
+ export const OUTPUT_KINDS = {
2
+ report: { fileName: 'report.md', cardTitle: '한 장 보고서', guidance: '보고·정리·요약·회고 → 한 장 보고서', words: ['보고', '정리', '요약', '회고', 'report', 'summary', 'summarize', 'recap', 'brief'] },
3
+ slides: { fileName: 'slides.md', cardTitle: '슬라이드', guidance: '발표·자료·덱·피치 → 슬라이드(장마다 --- 구분)', words: ['발표', '자료', '덱', '피치', 'presentation', 'slides', 'slide', 'deck', 'pitch'] },
4
+ research: { fileName: 'research.md', cardTitle: '조사 문서', guidance: '조사·비교·경쟁사·시장 → 조사 문서(출처 링크 칸 필수)', words: ['조사', '비교', '경쟁사', '시장', 'research', 'compare', 'comparison', 'competitor', 'competitors', 'market'] },
5
+ post: { fileName: 'post.md', cardTitle: '게시글 초안', guidance: '게시글·캡션·공지·SNS → 게시글 초안(게시는 승인 노드 뒤)', words: ['게시글', '캡션', '공지', 'SNS', 'post', 'caption', 'announcement', 'social media'] },
6
+ video: { fileName: '', cardTitle: '영상', guidance: '영상·릴스·쇼츠 → 영상(그래프가 있을 때만)', words: ['영상', '릴스', '쇼츠', 'video', 'reels', 'shorts'] },
7
+ answer: { fileName: '', cardTitle: '답', guidance: '단순한 질문 → 답 그대로(산출물 없음)', words: [] },
8
+ } as const;
9
+
10
+ export type OutputKind = keyof typeof OUTPUT_KINDS;
11
+
12
+ export function pickDefaultOutput(text: string): { kind: OutputKind; fileName: string; cardTitle: string } {
13
+ const normalized = text.toLowerCase();
14
+ // Explicit artifact formats outrank activity words such as 정리, 요약 and 비교.
15
+ for (const kind of ['slides', 'post', 'video', 'research', 'report'] as const) {
16
+ const entry = OUTPUT_KINDS[kind];
17
+ if (entry.words.some(word => /[a-z]/i.test(word)
18
+ ? new RegExp(`(^|[^a-z])${word.toLowerCase()}(?=$|[^a-z])`).test(normalized)
19
+ : normalized.includes(word))) {
20
+ return { kind, fileName: entry.fileName, cardTitle: entry.cardTitle };
21
+ }
22
+ }
23
+ const kind = /(?:[??]|뭐|어떻게)\s*$/.test(text) ? 'answer' : 'report';
24
+ return { kind, fileName: OUTPUT_KINDS[kind].fileName, cardTitle: OUTPUT_KINDS[kind].cardTitle };
25
+ }