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
@@ -1,13 +1,13 @@
1
- import { chmodSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs';
1
+ import { chmodSync, mkdirSync, readFileSync, rmSync } from 'node:fs';
2
2
  import { spawn } from 'node:child_process';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { debug } from './debug/log.js';
6
6
  import { effectiveInstanceRoot } from './instance/resolve.js';
7
- import { lastJsonObject, runGraph, type GraphRunState } from './graph-runner/runner.js';
7
+ import { runGraph } from './graph-runner/runner.js';
8
+ import { cardShape, detectCard, runCardFollowup, NotACardError, type CardDetectDecision } from './card-followup/core.js';
8
9
  import type { TgIncoming } from './telegram.js';
9
10
 
10
- const GRAPH = resolve(import.meta.dir, '../plugins/card-followup/graphs/card-followup.yaml');
11
11
  // Latin keywords are whole words only — «postcard» or «cardinal» is not a card request (review round 3).
12
12
  const KEYWORD = /명함|전략|메일\s*초안|초안|팔로업|\bcard\b|\bCRM\b/i;
13
13
  /** Untagged albums start one graph run, not one per photo (chatId:mediaGroupId → first seen at). */
@@ -22,30 +22,7 @@ function firstOfAlbum(chatId: number, mediaGroupId: string | undefined, now: num
22
22
  return true;
23
23
  }
24
24
 
25
- // Captionless photos start a run only when they look like a business card (OP 10-02 18:54 · cheap signals first, ambiguous → nothing).
26
- export type CardDetectDecision = 'card' | 'skip' | 'ambiguous';
27
- export function cardShape(width?: number, height?: number): { decision: 'skip' | 'pass'; signal: string } {
28
- if (!width || !height) return { decision: 'pass', signal: 'ratio:unknown' };
29
- const ratio = Math.max(width, height) / Math.min(width, height);
30
- if (ratio < 1.15) return { decision: 'skip', signal: 'ratio:square' };
31
- if (ratio > 2.0) return { decision: 'skip', signal: 'ratio:screenshot' };
32
- return { decision: 'pass', signal: `ratio:${ratio.toFixed(2)}` };
33
- }
34
- const EMAIL = /[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/i;
35
- const PHONE = /(?:\+?\d{1,3}[\s.-]?)?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{4}/;
36
- const WEB = /\b(?:https?:\/\/|www\.)\S+|\b[a-z0-9-]+\.(?:com|co\.kr|kr|io|ai|net|org)\b/i;
37
- const TITLE = /대표|이사|팀장|부장|과장|실장|매니저|주식회사|\(주\)|\b(?:CEO|CTO|COO|CFO|Director|Manager|Founder|Inc|Ltd|Corp)\b/i;
38
- /** Card text is short and carries contact patterns; long text is a document. Returns signal names only, never the text. */
39
- export function cardText(ocr: string | null): { decision: CardDetectDecision; signal: string } {
40
- if (ocr === null) return { decision: 'ambiguous', signal: 'ocr:unavailable' };
41
- const chars = ocr.replace(/\s+/g, '').length;
42
- if (chars < 15) return { decision: 'ambiguous', signal: `ocr:chars<15` };
43
- if (chars > 700) return { decision: 'skip', signal: 'ocr:document' };
44
- const hits = ([['email', EMAIL], ['phone', PHONE], ['web', WEB], ['title', TITLE]] as const).filter(([, re]) => re.test(ocr)).map(([name]) => name);
45
- const signal = `ocr:${hits.join(',') || 'none'}`;
46
- if (hits.length >= 2) return { decision: 'card', signal };
47
- return { decision: hits.length === 0 ? 'skip' : 'ambiguous', signal };
48
- }
25
+ export { cardShape, cardText } from './card-followup/core.js';
49
26
  const OCR_SCRIPT = resolve(import.meta.dir, '../scripts/ocr-text-regions.swift');
50
27
  /** Local macOS Vision OCR (no network · no new dependency); null where it cannot run. */
51
28
  export function localOcrText(path: string, timeoutMs = 30_000): Promise<string | null> {
@@ -78,36 +55,6 @@ export interface CardPhotoDeps {
78
55
  ocrText?: (path: string) => Promise<string | null>;
79
56
  }
80
57
 
81
- const obj = (value: unknown): Record<string, unknown> => value !== null && typeof value === 'object' && !Array.isArray(value) ? value as Record<string, unknown> : {};
82
- const text = (value: unknown): string => typeof value === 'string' ? value.trim() : '';
83
- const line = (value: unknown): string => (typeof value === 'number' && Number.isFinite(value) ? String(value) : text(value)).replace(/[\r\n]+/g, ' ') || '—';
84
-
85
- function isNotACard(state: GraphRunState): boolean {
86
- if (state.status !== 'failed') return false;
87
- const read = state.nodes.find((node) => node.nodeId === 'read-card');
88
- if (!read || !read.ok) return false;
89
- const output = lastJsonObject(read.output);
90
- if (output?.outcome === 'ok' && output.card && typeof output.card === 'object') {
91
- const card = obj(output.card);
92
- return !['name', 'company', 'title', 'email', 'phone', 'url', 'linkedin'].some((field) => text(card[field]));
93
- }
94
- return output?.outcome === 'fail' && typeof output.reason === 'string'
95
- && /\bnot a (?:business )?card\b|명함이?\s*아님|명함이?\s*아닙니다/i.test(output.reason);
96
- }
97
-
98
- function validateReport(report: Record<string, unknown>): void {
99
- const card = obj(report.card), fit = obj(report.fit), approach = obj(report.approach);
100
- const next = obj(report.nextAction), draft = obj(report.draft);
101
- const score = fit.score;
102
- if (report.sent !== false || !text(card.name) || !text(card.company)
103
- || !(score === null || (typeof score === 'number' && Number.isFinite(score)))
104
- || !text(fit.label) || !text(approach.problem) || !text(approach.proposal)
105
- || !text(approach.channel) || !text(next.what) || !text(next.due)
106
- || !text(draft.subject) || !text(draft.body)) {
107
- throw new Error('incomplete or sent card report');
108
- }
109
- }
110
-
111
58
  function pick(caption: string): Picked {
112
59
  if (/\bCRM\b/i.test(caption)) return 'crm';
113
60
  if (/메일\s*초안|초안/i.test(caption)) return 'draft';
@@ -149,7 +96,7 @@ export async function maybeHandleCardPhoto(ctx: TgIncoming, deps: CardPhotoDeps)
149
96
  const { localPath } = await deps.downloadFile(photo.fileId, dir);
150
97
  chmodSync(localPath, 0o600);
151
98
  if (!caption) {
152
- const verdict = cardText(await (deps.ocrText ?? localOcrText)(localPath));
99
+ const verdict = detectCard({ width: photo.width, height: photo.height, ocrText: await (deps.ocrText ?? localOcrText)(localPath) });
153
100
  detect(verdict.decision, verdict.signal);
154
101
  if (verdict.decision !== 'card') { rmSync(dir, { recursive: true, force: true }); return null; }
155
102
  }
@@ -166,38 +113,22 @@ export async function maybeHandleCardPhoto(ctx: TgIncoming, deps: CardPhotoDeps)
166
113
  try { localPath = await prepare(); } catch { log('failed'); return; }
167
114
  if (!localPath) return;
168
115
  }
169
- const state = await (deps.runGraph ?? runGraph)(GRAPH, { runId, deps: { root }, input: {
170
- image: localPath!, outDir: dir, ...(caption ? { context: caption } : {}),
171
- } });
172
- if (!deps.runGraph) {
173
- const statePath = join(base, `${runId}.json`);
174
- chmodSync(statePath, 0o600);
175
- const contexts = `${statePath}.contexts`;
176
- try {
177
- chmodSync(contexts, 0o700);
178
- for (const file of readdirSync(contexts)) chmodSync(join(contexts, file), 0o600);
179
- } catch (error) {
180
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
181
- }
182
- }
183
- if (isNotACard(state)) {
184
- log('not-a-card');
185
- return;
116
+ let result;
117
+ try {
118
+ result = await runCardFollowup({ imagePath: localPath!, ocrText: null,
119
+ runGraph: deps.runGraph ?? runGraph, rootDir: root, outDir: dir, runId,
120
+ ...(caption ? { context: caption } : {}),
121
+ });
122
+ } catch (error) {
123
+ if (error instanceof NotACardError) { log('not-a-card'); return; }
124
+ throw error;
186
125
  }
187
- if (state.status !== 'done') throw new Error('graph did not finish');
188
- const report = obj(JSON.parse(readFileSync(join(dir, 'followup.json'), 'utf8')));
189
- validateReport(report);
190
- const card = obj(report.card), fit = obj(report.fit), approach = obj(report.approach);
191
- const next = obj(report.nextAction), draft = obj(report.draft);
192
- const summary = `① 요약\n${line(card.name)} · ${line(card.company)} · ${line(card.title)}\n타겟 판정: ${line(fit.score)} · ${line(fit.label)}`;
193
- const strategy = `② 전략\n${[approach.problem, approach.proposal, approach.channel].map(line).join('\n')}\n다음 행동: ${line(next.what)} · ${line(next.due)}`;
194
- const mail = `③ 팔로업 초안 (보내지 않았습니다)\n제목: ${line(draft.subject)}\n${text(draft.body)}`;
195
- const markdown = readFileSync(join(dir, 'followup.md'));
126
+ const markdown = readFileSync(result.followupPath!);
196
127
  const crmLine = picked === 'crm'
197
128
  ? /^## ② CRM 한 줄\r?\n파일: [^\r\n]*\r?\n([^\r\n]+)/m.exec(markdown.toString('utf8'))?.[1]
198
129
  : undefined;
199
130
  if (picked === 'crm' && !crmLine) throw new Error('CRM row missing from report');
200
- const messages = picked === 'strategy' ? [strategy] : picked === 'draft' ? [mail] : picked === 'crm' ? [`CRM 한 줄\n${crmLine}`] : [summary, strategy, mail];
131
+ const messages = picked === 'strategy' ? [result.replies[1]] : picked === 'draft' ? [result.replies[2]] : picked === 'crm' ? [`CRM 한 줄\n${crmLine}`] : result.replies;
201
132
  for (const message of messages) await deps.sendMessage(ctx.chatId, message, reply);
202
133
  const sent = await deps.sendDocument(ctx.chatId, markdown, 'followup.md', { ...reply, mimeType: 'text/markdown' });
203
134
  if (sent === undefined) throw new Error('document delivery failed');
@@ -207,17 +138,6 @@ export async function maybeHandleCardPhoto(ctx: TgIncoming, deps: CardPhotoDeps)
207
138
  try {
208
139
  await deps.sendMessage(ctx.chatId, '명함 정리에 실패했습니다. 다시 시도해 주세요.', reply);
209
140
  } catch { /* Telegram delivery itself failed; the failure is already observed. */ }
210
- } finally {
211
- if (!deps.runGraph) {
212
- // The runner may throw before returning its state; seal any partial state and contexts too.
213
- const statePath = join(base, `${runId}.json`);
214
- try { chmodSync(statePath, 0o600); } catch { /* no state yet */ }
215
- const contexts = `${statePath}.contexts`;
216
- try {
217
- chmodSync(contexts, 0o700);
218
- for (const file of readdirSync(contexts)) chmodSync(join(contexts, file), 0o600);
219
- } catch { /* no contexts yet */ }
220
- }
221
141
  }
222
142
  })();
223
143
  } catch {
@@ -44,6 +44,12 @@ import { readDaemonSessionHistory } from './telegram/daemon-history-reader.js';
44
44
  import { renderTelegramReplayPreviewHtml } from './telegram/replay-preview.js';
45
45
  import { classifyIntake } from './ad-pipeline/intake.js';
46
46
  import { createAdPipelineDeps, runAdPipeline } from './ad-pipeline/run.js';
47
+ import { projectCommand } from './telegram-project-command.js';
48
+ import { telegramNowSlash } from './context-bus/context-now-surfaces.js';
49
+ import { createWishCard } from './intake-plane/wish-card.js';
50
+ import { telegramDecisionOwner } from './decisions/telegram-decision-cards.js';
51
+ import { CardStore } from './task-cards/card-store.js';
52
+ import type { ContextNowDeps } from './context-bus/context-now.js';
47
53
 
48
54
  /** How many prior user/assistant turns to pass into `executeSkill` as
49
55
  * the `## Recent conversation` block. Mirrors the dashboard default
@@ -152,6 +158,7 @@ export interface TgCommandContext {
152
158
  userConfig: UserConfig;
153
159
  /** All registered commands — used by `/help` to enumerate. */
154
160
  allCommands: TgSlashCommand[];
161
+ sendButtons?: (text: string, buttons: Array<Array<{ text: string; data: string }>>) => Promise<void>;
155
162
  /** When the caller posted a placeholder for a streaming command,
156
163
  * the streamer edits it in-place as the handler accumulates
157
164
  * output. Undefined for instant commands. Handlers must tolerate
@@ -291,7 +298,7 @@ function dropChatSessionReply(ctx: TgIncoming): string {
291
298
  /** Build the default command set for a Telegram bot wired via
292
299
  * botFromConfig. Returns the array rather than mutating globals so
293
300
  * tests can construct a variant set with stubbed handlers. */
294
- export function defaultTelegramCommands(): TgSlashCommand[] {
301
+ export function defaultTelegramCommands(nowDeps?: ContextNowDeps): TgSlashCommand[] {
295
302
  return [
296
303
  {
297
304
  name: 'help',
@@ -304,6 +311,11 @@ export function defaultTelegramCommands(): TgSlashCommand[] {
304
311
  return lines.join('\n');
305
312
  },
306
313
  },
314
+ {
315
+ name: 'now',
316
+ description: 'Show current release, decisions, seats and context: /now [topic]',
317
+ handler: async (args) => telegramNowSlash(args, nowDeps),
318
+ },
307
319
  {
308
320
  name: 'status',
309
321
  description: 'Show bot identity, current session, provider + model',
@@ -581,6 +593,24 @@ export function defaultTelegramCommands(): TgSlashCommand[] {
581
593
  return lines.join('\n');
582
594
  },
583
595
  },
596
+ projectCommand(),
597
+ {
598
+ name: 'wish',
599
+ description: '소원을 카드로 남기기',
600
+ handler: async (args, ctx, { userConfig }) => {
601
+ if (String(ctx.userId) !== telegramDecisionOwner(userConfig) || ctx.chatId !== ctx.userId || !ctx.isDm) {
602
+ return '대표만 쓸 수 있습니다';
603
+ }
604
+ if (args.length === 0) return '/wish <소원 한 줄>';
605
+ const store = new CardStore();
606
+ try {
607
+ const card = createWishCard({ text: args.join(' '), source: 'telegram', ref: `${ctx.chatId}:${ctx.messageId}` }, store);
608
+ return `소원 카드로 남겼습니다 — ${card.title} (카드 ${card.cardId.slice(0, 8)})`;
609
+ } finally {
610
+ store.close();
611
+ }
612
+ },
613
+ },
584
614
  {
585
615
  name: 'fork',
586
616
  description: 'Fork the current chat session (or /fork <prefix>) and continue HERE on the fork · time-travel: /fork before:N',
@@ -0,0 +1,145 @@
1
+ import { debug } from './debug/log.js';
2
+ import { getProject, listProjects, type Project } from './project/project-store.js';
3
+ import { findSessionByTelegramChat, updateSessionMeta } from './session/index.js';
4
+ import { telegramDecisionOwner } from './decisions/telegram-decision-cards.js';
5
+ import type { TelegramBot } from './telegram.js';
6
+ import type { TgSlashCommand } from './telegram-commands.js';
7
+ import type { UserConfig } from './user-config.js';
8
+
9
+ export interface TelegramProjectCommandDeps {
10
+ list: () => Project[];
11
+ find: (id: string) => Project | null;
12
+ update: typeof updateSessionMeta;
13
+ findSession: typeof findSessionByTelegramChat;
14
+ }
15
+
16
+ export function projectCommand(deps: TelegramProjectCommandDeps = {
17
+ list: listProjects,
18
+ find: getProject,
19
+ update: updateSessionMeta,
20
+ findSession: findSessionByTelegramChat,
21
+ }): TgSlashCommand {
22
+ return {
23
+ name: 'project',
24
+ description: '이 대화의 프로젝트 보기·옮기기',
25
+ handler: async (args, ctx, ctxOptions) => {
26
+ const session = deps.findSession(ctx.chatId, ctx.threadId, ctx.botId);
27
+ if (!session) {
28
+ debug.log('telegram.project', 'rejected', { projects: [], reason: 'no-session' });
29
+ return '아직 이 채팅에 대화가 없습니다 — 한 마디 보낸 뒤 다시';
30
+ }
31
+
32
+ if (args.length === 0) {
33
+ const current = session.projectId ? deps.find(session.projectId) : null;
34
+ const projects = deps.list().sort((a, b) => a.name.localeCompare(b.name));
35
+ debug.log('telegram.project', 'listed', { projects: projects.slice(0, 20).map(p => ({ id: p.id, name: p.name })) });
36
+ if (ctx.isDm && ctx.chatId === ctx.userId
37
+ && String(ctx.userId) === telegramDecisionOwner(ctxOptions.userConfig) && ctxOptions.sendButtons) {
38
+ const buttons = projects.filter(p => {
39
+ const bytes = Buffer.byteLength(`prj:${p.id}`, 'utf8');
40
+ if (bytes <= 64) return true;
41
+ debug.log('telegram.project', 'button-rejected', { reason: 'callback-data-too-long', projectId: p.id, bytes });
42
+ return false;
43
+ }).slice(0, 8).map(p => ({ text: p.name, data: `prj:${p.id}` }));
44
+ buttons.push({ text: '받은 대화로', data: 'prj:-' });
45
+ const rows: Array<Array<{ text: string; data: string }>> = [];
46
+ for (let i = 0; i < buttons.length; i += 2) rows.push(buttons.slice(i, i + 2));
47
+ try {
48
+ await ctxOptions.sendButtons('프로젝트를 선택하세요', rows);
49
+ } catch (err) {
50
+ debug.log('telegram.project', 'button-rejected', {
51
+ reason: 'send-failed', error: err instanceof Error ? err.message : String(err),
52
+ });
53
+ }
54
+ }
55
+ return [
56
+ `지금 대화의 프로젝트: ${current?.name ?? '받은 대화(프로젝트 없음)'}`,
57
+ '',
58
+ '프로젝트 목록:',
59
+ ...projects.slice(0, 20).map(p => `• ${p.name}`),
60
+ '',
61
+ '옮기려면 /project <이름>',
62
+ ].join('\n');
63
+ }
64
+
65
+ const name = args.join(' ').trim();
66
+ if (name === '-') {
67
+ deps.update(session.id, m => { delete m.projectId; });
68
+ debug.log('telegram.project', 'cleared', { projects: [] });
69
+ return '받은 대화로 옮겼습니다';
70
+ }
71
+
72
+ const projects = deps.list();
73
+ const normalized = name.toLocaleLowerCase();
74
+ const exact = projects.filter(p => p.name.toLocaleLowerCase() === normalized);
75
+ const matches = exact.length ? exact : projects.filter(p => p.name.toLocaleLowerCase().startsWith(normalized));
76
+ if (matches.length === 0) {
77
+ debug.log('telegram.project', 'rejected', { projects: [], reason: 'not-found' });
78
+ return '그 이름의 프로젝트가 없습니다 — /project 로 목록 보기';
79
+ }
80
+ if (matches.length > 1) {
81
+ debug.log('telegram.project', 'rejected', {
82
+ projects: matches.map(p => ({ id: p.id, name: p.name })), reason: 'ambiguous',
83
+ });
84
+ return `여러 프로젝트가 맞습니다 — 이름을 골라 주세요:\n${matches.map(p => `• ${p.name}`).join('\n')}`;
85
+ }
86
+
87
+ const project = matches[0]!;
88
+ deps.update(session.id, m => { m.projectId = project.id; });
89
+ debug.log('telegram.project', 'moved', { projects: [{ id: project.id, name: project.name }] });
90
+ return `이 대화를 ${project.name} 프로젝트로 옮겼습니다`;
91
+ },
92
+ };
93
+ }
94
+
95
+ export function attachTelegramProjectButtons(
96
+ bot: Pick<TelegramBot, 'botId' | 'onCallbackQuery' | 'sendMessage' | 'answerCallbackQuery'>,
97
+ cfg: UserConfig,
98
+ deps: TelegramProjectCommandDeps = {
99
+ list: listProjects,
100
+ find: getProject,
101
+ update: updateSessionMeta,
102
+ findSession: findSessionByTelegramChat,
103
+ },
104
+ ): () => void {
105
+ const owner = telegramDecisionOwner(cfg);
106
+ return bot.onCallbackQuery(async q => {
107
+ if (!q.data.startsWith('prj:')) return;
108
+ if (!owner || String(q.userId) !== owner) {
109
+ debug.log('telegram.project', 'button-rejected', { reason: 'not-owner', userId: q.userId, chatId: q.chatId });
110
+ await bot.answerCallbackQuery(q.id, { text: '권한이 없습니다' });
111
+ return;
112
+ }
113
+ if (q.chatId === undefined || q.chatId !== q.userId || q.threadId !== undefined) {
114
+ debug.log('telegram.project', 'button-rejected', { reason: 'not-private-chat', userId: q.userId, chatId: q.chatId });
115
+ await bot.answerCallbackQuery(q.id, { text: '개인 대화에서만 고를 수 있습니다' });
116
+ return;
117
+ }
118
+ const chatId = q.chatId;
119
+ const session = deps.findSession(chatId, q.threadId, bot.botId);
120
+ if (!session) {
121
+ debug.log('telegram.project', 'button-rejected', { reason: 'no-session', chatId });
122
+ await bot.answerCallbackQuery(q.id);
123
+ await bot.sendMessage(chatId, '아직 이 채팅에 대화가 없습니다 — 한 마디 보낸 뒤 다시');
124
+ return;
125
+ }
126
+ if (q.data === 'prj:-') {
127
+ deps.update(session.id, m => { delete m.projectId; });
128
+ debug.log('telegram.project', 'button-cleared', { chatId, sessionId: session.id });
129
+ await bot.answerCallbackQuery(q.id);
130
+ await bot.sendMessage(chatId, '받은 대화로 옮겼습니다');
131
+ return;
132
+ }
133
+ const project = deps.find(q.data.slice(4));
134
+ if (!project) {
135
+ debug.log('telegram.project', 'button-rejected', { reason: 'not-found', chatId, projectId: q.data.slice(4) });
136
+ await bot.answerCallbackQuery(q.id);
137
+ await bot.sendMessage(chatId, '그 이름의 프로젝트가 없습니다 — /project 로 목록 보기');
138
+ return;
139
+ }
140
+ deps.update(session.id, m => { m.projectId = project.id; });
141
+ debug.log('telegram.project', 'button-moved', { chatId, sessionId: session.id, projectId: project.id });
142
+ await bot.answerCallbackQuery(q.id);
143
+ await bot.sendMessage(chatId, `이 대화를 ${project.name} 프로젝트로 옮겼습니다`);
144
+ });
145
+ }
@@ -11,7 +11,7 @@
11
11
  // the report channel while interactive Q&A stays on the main bot.
12
12
 
13
13
  import { TelegramBot } from './telegram.js';
14
- import { isOperationalKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './domains/telegram-kind-route.js';
14
+ import { isTradingKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './domains/telegram-kind-route.js';
15
15
  export { DEFAULT_KIND_ROLES, roleForKind } from './domains/telegram-kind-route.js';
16
16
  import { resolveChannelBotToken } from './channel-bot-token.js';
17
17
  import type { UserConfig } from './user-config.js';
@@ -51,22 +51,27 @@ export interface ReportTarget {
51
51
  chatId: number;
52
52
  }
53
53
 
54
- /** Explicit role channels win. Without a role channel, operational kinds use the main bot's
55
- * home when the legacy report channel uses another bot; same-bot reports and trading kinds
56
- * keep the legacy report destination. Returns null when no safe target is configured. */
54
+ /** An exact role channel wins. A configured channels table cannot silently fall through
55
+ * to the legacy report bot or a channel with a different role. Without that table, only
56
+ * named finance kinds can use the legacy trading report channel. */
57
57
  export function resolveReportTarget(cfg: UserConfig, kind?: string): ReportTarget | null {
58
- const routed = kindRouteTarget(cfg, kind);
59
- if (routed) return routed;
58
+ if (cfg.telegram.channels?.length) {
59
+ const routed = kindRouteTarget(cfg, kind);
60
+ if (routed) return routed;
61
+ logKindRouteFallback(kind, 'none', false);
62
+ return null;
63
+ }
60
64
  const rc = cfg.telegram.reportChannel;
61
65
  const mainToken = resolveChannelBotToken('telegram', cfg).token;
62
66
  const sameBot = !!mainToken && !!rc && (rc.botToken ?? mainToken) === mainToken;
63
- if (isOperationalKind(kind) && !sameBot) {
67
+ if (!isTradingKind(kind)) {
64
68
  const home = mainHomeTarget(cfg);
65
69
  logKindRouteFallback(kind, home ? 'main-home' : 'none', sameBot);
66
70
  return home;
67
71
  }
68
- const botToken = rc?.botToken ?? mainToken;
69
- const target = rc && Number.isFinite(rc.chatId) && botToken ? { botToken, chatId: rc.chatId } : null;
72
+ const botToken = rc?.botToken;
73
+ const target = rc && Number.isFinite(rc.chatId) && botToken && botToken !== mainToken
74
+ ? { botToken, chatId: rc.chatId } : null;
70
75
  logKindRouteFallback(kind, target ? 'report-channel' : 'none', sameBot);
71
76
  return target;
72
77
  }
@@ -81,10 +86,9 @@ export interface SendReportOpts {
81
86
  kind?: string;
82
87
  }
83
88
 
84
- /** Send a report to the configured report channel. Returns false (a
85
- * no-op, never throws) when no report channel is configured, so callers
86
- * can fire-and-forget without gating on config. Throws only on an actual
87
- * Telegram API failure once a target exists. */
89
+ /** Send a report to its kind's exact-role channel (or the safe legacy target).
90
+ * Returns false when no safe target exists; throws only on an actual
91
+ * Telegram API failure after a target has been selected. */
88
92
  export async function sendTelegramReport(
89
93
  cfg: UserConfig,
90
94
  text: string,
@@ -110,9 +114,9 @@ export async function sendTelegramReport(
110
114
  export async function sendReportPhoto(
111
115
  cfg: UserConfig,
112
116
  photoUrl: string,
113
- opts: { caption?: string; fetchImpl?: typeof fetch } = {},
117
+ opts: { caption?: string; fetchImpl?: typeof fetch; kind?: string } = {},
114
118
  ): Promise<boolean> {
115
- const target = resolveReportTarget(cfg);
119
+ const target = resolveReportTarget(cfg, opts.kind);
116
120
  if (!target || !photoUrl) return false;
117
121
  const bot = new TelegramBot({
118
122
  token: target.botToken,
@@ -131,9 +135,9 @@ export async function sendReportPhoto(
131
135
  export async function sendReportPhotoBuffer(
132
136
  cfg: UserConfig,
133
137
  png: Buffer,
134
- opts: { caption?: string; fetchImpl?: typeof fetch } = {},
138
+ opts: { caption?: string; fetchImpl?: typeof fetch; kind?: string } = {},
135
139
  ): Promise<boolean> {
136
- const target = resolveReportTarget(cfg);
140
+ const target = resolveReportTarget(cfg, opts.kind);
137
141
  if (!target || !png) return false;
138
142
  const bot = new TelegramBot({
139
143
  token: target.botToken,