elanous 0.2.10 → 0.2.11

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 (584) 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/Y4zKzL62sakG5momz3SoQ/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1394-556de6d71bbdb0fb.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1452.93700483b9d4fc61.js +9 -0
  7. package/apps/pwa/out/_next/static/chunks/{6062-73e68343a9814f6a.js → 1829-b453a1b22d3999bb.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/390.c373e31b3b9d0c41.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +25 -0
  20. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4685-c3c1c625b71accd1.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +2 -0
  25. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/{8067-87ce253ec2f23697.js → 5845-e6c003ce8d5fd36a.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/{8046cadf-211ca3c0e00f2b72.js → 5cd7e3a9-404fd6b8d0ecd44c.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/{4323.c5dfb6f503304775.js → 7334.a6679d9168f37aa8.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/79f8be9e-957d68913b590b08.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8039-df92e08fede5de8f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8421-1c4acfb1395a0edf.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/8605-ea5bee9dfae512c7.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{2415.ed6428ff334c7ce2.js → 9269.d2a4795bc6812717.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/9656-c1540088f5859c80.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.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-1dcdc58fc2de2454.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-6a061bd97f0b9398.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-54b32bd27726588d.js → page-b12b6d2f8d2d1aea.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/chat/page-7c97dcdfe2e37a60.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/control/page-5721537ca8a3598c.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/design-check/page-b654240a572e4c48.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/editor/page-b9750abc897508c3.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.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-50cb2a0eb15e6c83.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/intake/page-c8b1ccda6ba7ae98.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/layout-d6b1d156fb95e74b.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/market/page-9688f5b4f1ff950f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/morning/page-35414e0a007b4082.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e001823c35c9dd67.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-371423d1a573d64b.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-4b764b125339ac17.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cdf8e4b01067bcf1.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a24072b35470157.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d37cb0d52f9b6bd5.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/sessions/page-686da0df9bf23e5c.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4dc614f3ce043e63.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d02c782fc792e2af.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-239c17b911b84e72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/share/page-dc6a79fb003f746c.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/showroom/page-0fffb84cafdc9da6.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/tasks/page-1218753c82d585f6.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/term/page-4b5935e67531efb5.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/today/page-3285bc87caf1eaa5.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/trace/page-ad553b0e06505ecd.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/vault/page-fca096e3e860306f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-9ebfa78f0d9885c3.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/workflows/page-3dc5f21f7e39622d.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.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/543f56bcb1f79fca.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/drafts/draft-cleanup.yaml +19 -0
  192. package/graphs/drafts/recipes.yaml +23 -0
  193. package/graphs/intake/intake-daily.yaml +2 -1
  194. package/graphs/ops/recipes.yaml +3 -0
  195. package/graphs/ops/secret-exposure.yaml +24 -0
  196. package/graphs/release/recipes.yaml +3 -0
  197. package/graphs/release/release-loop.yaml +4 -1
  198. package/graphs/video/explainer-line.yaml +2 -2
  199. package/package.json +2 -1
  200. package/plugins/elanous-hwp/README.md +25 -0
  201. package/plugins/elanous-hwp/cli.ts +22 -0
  202. package/plugins/elanous-hwp/converter.ts +237 -0
  203. package/plugins/elanous-hwp/fixtures/fixed-controls.hwpx +0 -0
  204. package/plugins/elanous-hwp/fixtures/fixed-table-controls.hwpx +0 -0
  205. package/plugins/elanous-hwp/fixtures/fixed.hwpx +0 -0
  206. package/plugins/elanous-hwp/plugin.json +16 -0
  207. package/plugins/elanous-hwp/plugin.ts +40 -0
  208. package/scripts/ci-public-export-leak-gate.ts +75 -14
  209. package/scripts/ci-public-leak-gate.ts +44 -9
  210. package/scripts/context-hooks/claude-stop.sh +4 -0
  211. package/scripts/context-hooks/emit.ts +24 -0
  212. package/scripts/context-hooks/post-tool-use.sh +4 -0
  213. package/scripts/coord-channel-watch.sh +12 -0
  214. package/scripts/coord-requests-bridge.ts +143 -0
  215. package/scripts/field-feed/lib.ts +4 -0
  216. package/scripts/fleet/node-setup.sh +20 -3
  217. package/scripts/homebrew-formula-bump.ts +69 -0
  218. package/scripts/marketing/measure-site-numbers.ts +169 -0
  219. package/scripts/measure-gate-shards.ts +309 -0
  220. package/scripts/mission-request-judge.ts +31 -1
  221. package/scripts/morning-report.ts +2 -2
  222. package/scripts/opportunity-scan.ts +1 -1
  223. package/scripts/release-loop/auto-approve-node.ts +21 -4
  224. package/scripts/release-loop/cut-branch.ts +84 -0
  225. package/scripts/release-loop/gate-node.ts +14 -13
  226. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  227. package/scripts/release-loop/node-verdict.ts +3 -3
  228. package/scripts/release-loop/tui-node-checks.ts +42 -0
  229. package/scripts/release-loop/tui-sim-node.ts +32 -8
  230. package/scripts/release-loop/unattended-release.ts +5 -3
  231. package/scripts/release-loop/version-node.ts +61 -10
  232. package/scripts/signal-digest-cycle.ts +2 -7
  233. package/scripts/signal-digest-photo.ts +11 -0
  234. package/scripts/tech1-public-measure.ts +94 -0
  235. package/scripts/test-diet/node.ts +1 -1
  236. package/scripts/tui-regress.ts +2 -2
  237. package/scripts/ux/demo-rehearsal.ts +243 -0
  238. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  239. package/skills/diagram-master/SKILL.md +366 -0
  240. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  241. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  242. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  243. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  244. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  245. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  246. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  247. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  248. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  249. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  250. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  251. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  252. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  253. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  254. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  255. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  256. package/skills/diagram-master/references/examples/README.md +69 -0
  257. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  258. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  259. package/skills/diagram-master/references/pyproject.toml +16 -0
  260. package/skills/diagram-master/references/quality-checklist.md +33 -0
  261. package/skills/diagram-master/references/routing-guide.md +164 -0
  262. package/skills/diagram-master/references/uv.lock +1356 -0
  263. package/skills/google-workspace/SKILL.md +33 -0
  264. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  265. package/skills/grill-me/SKILL.md +1 -1
  266. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  267. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  268. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  269. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  270. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  271. package/skills/omni-crawl/SKILL.md +21 -11
  272. package/skills/omni-crawl/scripts/main.ts +6 -6
  273. package/skills/omni-crawl/src/router.ts +3 -3
  274. package/skills/omni-digest/SKILL.md +17 -9
  275. package/skills/omni-digest/digest.md +2 -2
  276. package/skills/omni-digest/src/fetch.ts +30 -3
  277. package/skills/pitch-deck/SKILL.md +2 -2
  278. package/skills/pitch-deck/template.html +2 -2
  279. package/skills/youtube-master/SKILL.md +163 -0
  280. package/skills/youtube-master/package-lock.json +558 -0
  281. package/skills/youtube-master/package.json +13 -0
  282. package/skills/youtube-master/references/study-note-template.md +124 -0
  283. package/skills/youtube-master/scripts/main.ts +435 -0
  284. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  285. package/skills/youtube-master/src/audio.ts +63 -0
  286. package/skills/youtube-master/src/domain-staging.ts +50 -0
  287. package/skills/youtube-master/src/env.ts +36 -0
  288. package/skills/youtube-master/src/obsidian.ts +155 -0
  289. package/skills/youtube-master/src/router.ts +110 -0
  290. package/skills/youtube-master/src/stt.ts +199 -0
  291. package/skills/youtube-master/src/study-note.ts +175 -0
  292. package/skills/youtube-master/src/summarize.ts +358 -0
  293. package/skills/youtube-master/src/transcript.ts +76 -0
  294. package/skills/youtube-master/src/types.ts +91 -0
  295. package/skills/youtube-master/src/util.ts +93 -0
  296. package/skills/youtube-master/src/youtube.ts +70 -0
  297. package/skills/youtube-master/tsconfig.json +13 -0
  298. package/src/acp/server.ts +5 -2
  299. package/src/agent-mission/driver.ts +80 -10
  300. package/src/agent-mission/pty-decision.ts +71 -0
  301. package/src/agent-mission/review-loop.ts +30 -0
  302. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  303. package/src/autopilot/tool-registry.ts +2 -0
  304. package/src/boot/daemon-prompt-turn.ts +5 -0
  305. package/src/boot/daemon-runtime.ts +9 -3
  306. package/src/boot/daemon-tools/web-search.ts +3 -0
  307. package/src/card-followup/core.ts +109 -0
  308. package/src/chat/index.ts +1 -0
  309. package/src/cli/drive-input-jail.ts +101 -0
  310. package/src/cli/hooks-cli.ts +17 -3
  311. package/src/cli/make-cli.ts +43 -0
  312. package/src/cli/persona-cli.ts +90 -4
  313. package/src/cli/plugin-cli.ts +31 -22
  314. package/src/cli/pod-cli.ts +19 -9
  315. package/src/cli/pr-cli.ts +51 -15
  316. package/src/cli/pty-drive-cli.ts +41 -7
  317. package/src/cli/pty-drive-verdict.ts +40 -0
  318. package/src/cli/release-cli.ts +17 -4
  319. package/src/cli/setup-cli.ts +43 -1
  320. package/src/cli/skills-cli.ts +17 -1
  321. package/src/context-bus/claude-hooks-install.ts +10 -0
  322. package/src/context-bus/context-now-surfaces.ts +48 -0
  323. package/src/context-bus/context-now.ts +104 -0
  324. package/src/context-bus/coord-events.ts +2 -2
  325. package/src/context-bus/external-events.ts +52 -0
  326. package/src/context-bus/long-term-memory.ts +108 -0
  327. package/src/dashboard/index.ts +9 -4
  328. package/src/dashboard/slash-runtime/dashboard-handlers.ts +7 -1
  329. package/src/decisions/decision-cards.ts +10 -3
  330. package/src/decisions/decision-ledger.ts +13 -5
  331. package/src/discord/sprint21-runtime.ts +5 -1
  332. package/src/discord-self-message.ts +32 -0
  333. package/src/discord-slash-wire.ts +5 -1
  334. package/src/discord.ts +17 -1
  335. package/src/domains/core-tools.ts +3 -2
  336. package/src/domains/outbound-alert.ts +6 -1
  337. package/src/domains/telegram-kind-route.ts +51 -13
  338. package/src/exec-requests/default-outputs.ts +25 -0
  339. package/src/exec-requests/planner.ts +10 -2
  340. package/src/exec-requests/runner.ts +20 -6
  341. package/src/exec-requests/store.ts +3 -0
  342. package/src/execution-loop/launch-gate.ts +100 -0
  343. package/src/field/field-reel.ts +5 -1
  344. package/src/field-note/index.ts +201 -0
  345. package/src/field-note/transcribe.ts +248 -0
  346. package/src/graph-kinds/registry.ts +3 -3
  347. package/src/graph-runner/graph-cli.ts +61 -1
  348. package/src/graph-runner/graph-grow-llm.ts +37 -0
  349. package/src/graph-runner/graph-grow.ts +47 -0
  350. package/src/graph-runner/runner.ts +317 -25
  351. package/src/grok/agent-search.ts +8 -2
  352. package/src/guardian/secret-exposure-scan.ts +101 -0
  353. package/src/harness/harness-cli-command.ts +247 -7
  354. package/src/harness/harness-pod-dispatch.ts +43 -23
  355. package/src/harness/harness-seams.ts +38 -4
  356. package/src/harness/harness-stop.ts +23 -4
  357. package/src/harness/launch-registry.ts +91 -0
  358. package/src/harness/process-garbage.ts +5 -3
  359. package/src/hooks/dispatch.ts +82 -0
  360. package/src/hooks/expose.ts +254 -0
  361. package/src/hooks/github-shadow.ts +108 -0
  362. package/src/hooks/providers.ts +71 -5
  363. package/src/hooks/queue.ts +1 -1
  364. package/src/hooks/receiver.ts +21 -20
  365. package/src/index.ts +52 -14
  366. package/src/intake-plane/categorize.ts +18 -46
  367. package/src/intake-plane/discord-seat-work.ts +10 -0
  368. package/src/intake-plane/front-route-classifier.ts +33 -39
  369. package/src/intake-plane/persona-answer.ts +30 -0
  370. package/src/intake-plane/runtime-callables.ts +8 -1
  371. package/src/intake-plane/telegram-seat-work.ts +36 -2
  372. package/src/intake-plane/wish-folder.ts +67 -0
  373. package/src/intent-gate/gate.ts +30 -10
  374. package/src/live/detail-switch.ts +5 -0
  375. package/src/llm/difficulty.ts +57 -0
  376. package/src/llm/judge-layer.ts +101 -0
  377. package/src/llm/mission-router.ts +33 -19
  378. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  379. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  380. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  381. package/src/loops/contract/loop-agent.schema.json +138 -0
  382. package/src/loops/contract/validate.ts +64 -0
  383. package/src/loops/registry.ts +20 -6
  384. package/src/market/signed-index.ts +4 -0
  385. package/src/maturity/bot-command-maturity.ts +45 -0
  386. package/src/maturity/feature-maturity.ts +26 -4
  387. package/src/model-tier/index.ts +2 -2
  388. package/src/model-tier/tier-resolver.ts +20 -20
  389. package/src/model-tier/types.ts +6 -6
  390. package/src/mss/logging/log-store.ts +15 -0
  391. package/src/nexus/api/config-model-tier.ts +5 -4
  392. package/src/nexus/api/fabric-plans.ts +53 -0
  393. package/src/nexus/api/harness-api.ts +5 -1
  394. package/src/nexus/api/health.ts +2 -0
  395. package/src/nexus/api/http-server.ts +36 -0
  396. package/src/nexus/api/inside-events.ts +226 -0
  397. package/src/nexus/api/meta-api.ts +21 -8
  398. package/src/nexus/api/missions.ts +6 -1
  399. package/src/nexus/api/ops-seats.ts +33 -0
  400. package/src/nexus/api/outputs-route.ts +44 -0
  401. package/src/nexus/api/seat-requests.ts +44 -2
  402. package/src/nexus/api/sessions-store.ts +17 -38
  403. package/src/nexus/api/sub-seat-charters.ts +34 -0
  404. package/src/nexus/api/tabs-mutations.ts +3 -1
  405. package/src/nexus/api/trace.ts +18 -6
  406. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  407. package/src/nexus/api/workflows.ts +80 -6
  408. package/src/nexus/index.ts +2 -0
  409. package/src/nexus/kinds/webterm.ts +2 -1
  410. package/src/nexus/webterm/pty.ts +6 -2
  411. package/src/onboarding/cli-auth-status.ts +80 -0
  412. package/src/onboarding/voice-ai.ts +3 -3
  413. package/src/org/seat-budget.ts +48 -0
  414. package/src/org/stall-escalation.ts +63 -0
  415. package/src/outputs/ledger.ts +120 -0
  416. package/src/persona/persona-todo.ts +79 -0
  417. package/src/persona/presets.ts +116 -2
  418. package/src/persona/team-set.ts +155 -0
  419. package/src/plugins/install/plugin-install.ts +14 -0
  420. package/src/plugins/maker/market-bundle.ts +109 -0
  421. package/src/plugins/maker/plugin-maker.ts +254 -11
  422. package/src/plugins/maker/wizard-generate.ts +103 -0
  423. package/src/pod-lease/admission.ts +150 -0
  424. package/src/pod-lease/dependency-state.ts +85 -0
  425. package/src/pod-lease/host-lease.ts +127 -0
  426. package/src/project/project-suggestion.ts +16 -0
  427. package/src/project/session-context.ts +24 -0
  428. package/src/release-loop/checklist.ts +2 -2
  429. package/src/release-loop/feature-store.ts +5 -3
  430. package/src/release-loop/next-md-merge.ts +58 -0
  431. package/src/release-loop/release-note.ts +59 -4
  432. package/src/seat-dispatch/seat-ask.ts +130 -0
  433. package/src/seat-dispatch/seat-questions.ts +100 -0
  434. package/src/seat-loop/seat-loop.ts +548 -29
  435. package/src/seat-loop/seat-report.ts +37 -2
  436. package/src/self-dev/dev-cli.ts +4 -1
  437. package/src/self-dev/dev-pipeline.ts +37 -1
  438. package/src/self-dev/draft-sweep.ts +38 -33
  439. package/src/self-dev/draft-triage-rules.ts +8 -5
  440. package/src/self-dev/fabric-plan-core.ts +297 -0
  441. package/src/self-dev/orchestrate.ts +36 -4
  442. package/src/self-dev/release-path-guard.ts +62 -0
  443. package/src/self-dev/shard-aggregate.ts +230 -0
  444. package/src/self-dev/shard-placement.ts +60 -0
  445. package/src/self-dev/shard-plan.ts +48 -0
  446. package/src/self-dev/shard-pod-run.ts +77 -0
  447. package/src/self-dev/shard-run.ts +105 -0
  448. package/src/self-implement/gate-baseline.ts +1 -1
  449. package/src/self-implement/gate-cli.ts +40 -1
  450. package/src/self-implement/gate-scope.ts +37 -7
  451. package/src/self-implement/graph-variant.ts +171 -0
  452. package/src/self-implement/harness-policy-gates.ts +1 -1
  453. package/src/self-implement/headless-elanous-driver.ts +22 -3
  454. package/src/self-implement/host-regate.ts +22 -1
  455. package/src/self-implement/importer-test-index.ts +2 -1
  456. package/src/self-implement/l8-merge-queue.ts +130 -0
  457. package/src/self-implement/orchestrator.ts +242 -9
  458. package/src/self-implement/route-consumer-test-index.ts +122 -0
  459. package/src/self-implement/seams.ts +72 -11
  460. package/src/self-implement/test-seams.ts +1 -0
  461. package/src/self-implement/uncommitted-work.ts +59 -0
  462. package/src/session/index.ts +36 -13
  463. package/src/skills/gift-install.ts +215 -0
  464. package/src/skills/tools/web-search.ts +4 -1
  465. package/src/steward/launch.ts +42 -26
  466. package/src/steward/steward-cards.ts +17 -9
  467. package/src/steward/triage.ts +46 -14
  468. package/src/task-cards/card-cli.ts +25 -1
  469. package/src/task-orchestrator/mission.ts +5 -0
  470. package/src/task-orchestrator/store.ts +12 -3
  471. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  472. package/src/task-orchestrator/surfaces/pod-lease.ts +156 -19
  473. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  474. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  475. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  476. package/src/task-orchestrator/types.ts +2 -0
  477. package/src/telegram-card-followup.ts +16 -96
  478. package/src/telegram-commands.ts +10 -1
  479. package/src/telegram-project-command.ts +71 -0
  480. package/src/telegram-report.ts +21 -17
  481. package/src/telegram.ts +29 -3
  482. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  483. package/src/user-config.ts +102 -20
  484. package/src/version/packed-revision.json +1 -1
  485. package/src/web-search/grok.ts +7 -1
  486. package/src/web-search/index.ts +28 -1
  487. package/src/web-search/provider.ts +4 -0
  488. package/src/web-search/x-intent.ts +5 -0
  489. package/src/workflow-runtime/executor.ts +277 -47
  490. package/src/workflow-runtime/index.ts +1 -0
  491. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  492. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  493. package/src/workflow-runtime/schema.ts +79 -4
  494. package/src/workflow-runtime/types.ts +35 -4
  495. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  505. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  506. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/8195-6fde3d7a39859206.js +0 -10
  525. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  533. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  534. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +0 -1
  535. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +0 -1
  536. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +0 -1
  537. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +0 -1
  538. package/apps/pwa/out/_next/static/chunks/app/chat/page-f8116559c7424da7.js +0 -1
  539. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +0 -1
  540. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +0 -1
  541. package/apps/pwa/out/_next/static/chunks/app/design-check/page-54e1f966ab60a4dc.js +0 -1
  542. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +0 -1
  543. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +0 -1
  544. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +0 -1
  545. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  546. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +0 -1
  547. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +0 -1
  548. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +0 -1
  549. package/apps/pwa/out/_next/static/chunks/app/market/page-b31610deda76337b.js +0 -1
  550. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6cb0e1bbefe5fb3d.js +0 -1
  551. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +0 -1
  552. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +0 -1
  553. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +0 -1
  554. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +0 -1
  555. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +0 -1
  556. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +0 -1
  557. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +0 -1
  558. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +0 -1
  559. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +0 -1
  560. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +0 -1
  561. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e168fa1030af0548.js +0 -1
  562. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +0 -1
  563. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +0 -1
  564. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  565. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +0 -1
  566. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +0 -1
  567. package/apps/pwa/out/_next/static/chunks/app/showroom/page-4fddc90b23feb215.js +0 -1
  568. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +0 -1
  569. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +0 -1
  570. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +0 -1
  571. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +0 -1
  572. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +0 -1
  573. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +0 -1
  574. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +0 -1
  575. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +0 -1
  576. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +0 -1
  577. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  578. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  579. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +0 -1
  580. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  581. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  582. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  583. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +0 -1
  584. /package/apps/pwa/out/_next/static/{2DsiDfDCks44g1cPUMhiW → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -0,0 +1,104 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { debug, redactSecretText } from '../debug/log.js';
3
+ import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
4
+ import { listChecklist, devVersion, type Checklist } from '../release-loop/checklist.js';
5
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
6
+ import { seatDay, seatLedgerPath, type SeatEntry } from '../seat-loop/seat-loop.js';
7
+ import { listCoordEvents, type CoordEvent } from './coord-events.js';
8
+
9
+ export type ContextFact =
10
+ | { kind: 'version'; version: string; source: string }
11
+ | { kind: 'cell'; version: string; id: string; title: string; status: string; owner: string | null; source: string }
12
+ | { kind: 'decision'; id: string; title: string; status: string; dueAt: string | null; source: string }
13
+ | { kind: 'seat'; seat: string; at: string; status: string; id: string | null; title: string | null; source: string };
14
+ export type ContextEvent = { at: string; kind: string; summary: string; source: string };
15
+ export type ContextNowAnswer = { at: string; topic: string | null; facts: ContextFact[]; events: ContextEvent[]; guide: string[] };
16
+
17
+ export interface ContextNowDeps {
18
+ now?: () => Date;
19
+ version?: () => string;
20
+ checklist?: (version: string) => Checklist;
21
+ decisions?: () => DecisionEntry[];
22
+ seatEntries?: (now: Date) => Array<{ entry: SeatEntry; source: string }>;
23
+ events?: (since: string) => CoordEvent[];
24
+ }
25
+
26
+ function todaySeatEntries(now: Date): Array<{ entry: SeatEntry; source: string }> {
27
+ const root = effectiveInstanceRoot();
28
+ const day = seatDay(now);
29
+ return ['OP', 'TC', 'MK', 'UX'].flatMap(seat => {
30
+ const path = seatLedgerPath(seat, root, now);
31
+ if (!existsSync(path)) return [];
32
+ return readFileSync(path, 'utf8').split('\n').flatMap((line, index) => line.trim() ? [{
33
+ entry: JSON.parse(line) as SeatEntry,
34
+ source: `elanous://seat-loop/${seat}/${day}#${index + 1}`,
35
+ }] : []);
36
+ });
37
+ }
38
+
39
+ /** Read a bounded, source-labelled view of current ledgers, never a transcript or decision body. */
40
+ export function contextNow(options: { topic?: string; limit?: number } = {}, deps: ContextNowDeps = {}): ContextNowAnswer {
41
+ const now = (deps.now ?? (() => new Date()))();
42
+ const at = `${now.toISOString().slice(0, 16)}:00.000Z`;
43
+ const topic = options.topic?.trim() || null;
44
+ const limit = typeof options.limit === 'number' && Number.isFinite(options.limit)
45
+ ? Math.max(1, Math.min(100, Math.floor(options.limit))) : 20;
46
+ const version = (deps.version ?? devVersion)().replace(/-.*$/, '');
47
+ const next = version.replace(/^(\d+)\.(\d+)\.(\d+)$/, (_, major: string, minor: string, patch: string) => `${major}.${minor}.${Number(patch) + 1}`);
48
+ const facts: ContextFact[] = [];
49
+ for (const v of [...new Set([version, next])]) {
50
+ const checklist = (deps.checklist ?? listChecklist)(v);
51
+ if (v === version) facts.push({ kind: 'version', version: v, source: `elanous://release/${v}/checklist` });
52
+ for (const item of checklist.items) {
53
+ if (item.status === 'done') continue;
54
+ facts.push({ kind: 'cell', version: v, id: item.id, title: redactSecretText(item.title.split(/\r?\n/, 1)[0] ?? '').slice(0, 120),
55
+ status: item.status, owner: item.owner ?? null, source: `elanous://release/${v}/checklist#${encodeURIComponent(item.id)}` });
56
+ }
57
+ }
58
+ for (const item of (deps.decisions ?? (() => new DecisionLedger().list({ status: 'open' })))()) {
59
+ if (item.status !== 'open') continue;
60
+ facts.push({ kind: 'decision', id: item.id, title: redactSecretText(item.title.split(/\r?\n/, 1)[0] ?? '').slice(0, 120),
61
+ status: item.status, dueAt: item.dueAt ?? null, source: `elanous://decisions/${encodeURIComponent(item.id)}` });
62
+ }
63
+ for (const { entry, source } of (deps.seatEntries ?? todaySeatEntries)(now)) {
64
+ facts.push({ kind: 'seat', seat: entry.seat, at: entry.at, status: entry.status,
65
+ id: entry.item?.id ?? null, title: entry.item?.source === 'checklist'
66
+ ? redactSecretText(entry.item.title.split(/\r?\n/, 1)[0] ?? '').slice(0, 120) : null, source });
67
+ }
68
+ const rawEvents = (deps.events ?? (since => listCoordEvents({ since })))(new Date(now.getTime() - 7 * 86_400_000).toISOString());
69
+ const chronologicalEvents = rawEvents.slice().sort((a, b) => a.at.localeCompare(b.at) || a.id.localeCompare(b.id));
70
+ const matches = (text: string) => !topic || text.toLocaleLowerCase().includes(topic.toLocaleLowerCase());
71
+ const selectedFacts = facts.filter(f => !topic || (f.kind === 'cell' && matches(`${f.id} ${f.title}`))
72
+ || (f.kind === 'seat' && matches(`${f.id ?? ''} ${f.title ?? ''}`)));
73
+ const groups = (['version', 'cell', 'decision', 'seat'] as const).map(kind => selectedFacts.filter(f => f.kind === kind));
74
+ groups[3]!.sort((a, b) => (a.kind === 'seat' && b.kind === 'seat' ? b.at.localeCompare(a.at) : 0));
75
+ const nonEmpty = groups.filter(group => group.length > 0);
76
+ const perKind = Math.floor(limit / nonEmpty.length);
77
+ const chosen = groups.map(group => group.splice(0, perKind));
78
+ let remaining = limit - chosen.reduce((total, group) => total + group.length, 0);
79
+ for (const index of [2, 3, 0, 1]) {
80
+ if (remaining <= 0) break;
81
+ if (chosen[index]!.length === 0 && groups[index]!.length > 0) {
82
+ chosen[index]!.push(...groups[index]!.splice(0, 1));
83
+ remaining--;
84
+ }
85
+ }
86
+ for (const index of [1, 2, 3, 0]) {
87
+ if (remaining <= 0) break;
88
+ const extra = groups[index]!.splice(0, remaining);
89
+ chosen[index]!.push(...extra);
90
+ remaining -= extra.length;
91
+ }
92
+ const boundedFacts = chosen.flat();
93
+ const summaries = chronologicalEvents.map(event => ({
94
+ at: event.at, kind: event.kind, summary: redactSecretText(event.summary.split(/\r?\n/, 1)[0] ?? '').slice(0, 120),
95
+ source: event.refs.source ?? event.refs.url ?? `elanous://context/event/${encodeURIComponent(event.id)}`,
96
+ }));
97
+ const selectedEvents = summaries.filter(event => matches(event.summary)).reverse().slice(0, limit);
98
+ const guide = summaries.filter(event => event.kind === 'guide-changed' || event.summary.startsWith('📌안내'))
99
+ .filter(event => matches(event.summary)).reverse().slice(0, limit)
100
+ .map(event => `📌 ${event.summary} — ${event.source}`);
101
+ const answer = { at, topic, facts: boundedFacts, events: selectedEvents, guide };
102
+ debug.log('context.now', 'answer', { topic, facts: answer.facts, events: answer.events });
103
+ return answer;
104
+ }
@@ -22,7 +22,7 @@ export interface CoordEvent {
22
22
  text: string;
23
23
  summary: string;
24
24
  kind: string;
25
- refs: Pick<CoordEventInput, 'seat' | 'recipients' | 'all' | 'kind' | 'slot' | 'deadline' | 'url'>;
25
+ refs: Pick<CoordEventInput, 'seat' | 'recipients' | 'all' | 'kind' | 'slot' | 'deadline' | 'url'> & { origin?: string; source?: string };
26
26
  }
27
27
 
28
28
  function seatIds(): Set<string> {
@@ -104,7 +104,7 @@ export function listCoordEvents(opts: { since: string; seat?: string }, deps: {
104
104
  const db = deps.db ?? openSurfaceEventsDb();
105
105
  try {
106
106
  const rows = db.prepare(`SELECT id, ts, text, summary, kind, refs FROM events
107
- WHERE surface='coord:channel' AND direction='outbound' AND ts>=?
107
+ WHERE surface IN ('coord:channel', 'context:external') AND direction='outbound' AND ts>=?
108
108
  ${opts.seat ? "AND json_extract(refs,'$.seat')=?" : ''} ORDER BY ts ASC, rowid ASC`)
109
109
  .all(...(opts.seat ? [opts.since, opts.seat] : [opts.since])) as Array<{
110
110
  id: string; ts: string; text: string; summary: string; kind: string; refs: string;
@@ -0,0 +1,52 @@
1
+ import type { Database } from 'bun:sqlite';
2
+ import { debug, redactSecretText } from '../debug/log.js';
3
+ import { openSurfaceEventsDb, recordEvent } from '../domains/surface-events.js';
4
+
5
+ export type ExternalEventOrigin = 'claude-code' | 'codex-agent-mission' | 'harness-child';
6
+ export type ContextHookKind = 'claimed' | 'done' | 'asked' | 'guide-changed';
7
+ export type ExternalEventKind = ContextHookKind | 'started' | 'finished';
8
+ export const CONTEXT_SUMMARY_MAX = 120;
9
+ export const CONTEXT_SOURCE_MAX = 512;
10
+
11
+ export interface ExternalEventInput {
12
+ origin: ExternalEventOrigin;
13
+ kind: ExternalEventKind;
14
+ summary: string;
15
+ source: string;
16
+ at?: string;
17
+ }
18
+
19
+ /** Only a single, bounded, redacted summary and a locator enter the journal; no prompt or transcript. */
20
+ export function recordExternalEvent(input: ExternalEventInput, deps: { db?: Database } = {}): string {
21
+ if (!['claude-code', 'codex-agent-mission', 'harness-child'].includes(input.origin)) throw new Error('invalid context origin');
22
+ if (input.origin === 'claude-code'
23
+ ? !['claimed', 'done', 'asked', 'guide-changed'].includes(input.kind)
24
+ : !['started', 'finished'].includes(input.kind)) throw new Error('invalid context kind');
25
+ const summary = redactSecretText(input.summary.split(/\r?\n/, 1)[0] ?? '').trim().slice(0, CONTEXT_SUMMARY_MAX);
26
+ const source = redactSecretText(input.source.split(/\r?\n/, 1)[0] ?? '').trim();
27
+ if (!summary) throw new Error('context summary must be one nonempty line');
28
+
29
+ if (!/^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(source) || source.length > CONTEXT_SOURCE_MAX) {
30
+ throw new Error('context source must be a link (http(s) or elanous://), at most 512 characters');
31
+ }
32
+ const db = deps.db ?? openSurfaceEventsDb();
33
+ try {
34
+ return recordEvent(db, {
35
+ surface: 'context:external', direction: 'outbound', domain: 'elanous', category: 'coordination',
36
+ kind: input.kind, text: summary, summary,
37
+ refs: JSON.stringify({ seat: input.origin, recipients: [], all: false, kind: input.kind,
38
+ slot: null, deadline: null, url: source, origin: input.origin, source }),
39
+ ...(input.at ? { ts: input.at } : {}),
40
+ });
41
+ } finally { if (!deps.db) db.close(); }
42
+ }
43
+
44
+ /** Instrumentation never changes an agent's result. Failures remain visible without logging untrusted payloads. */
45
+ export function observeExternalEvent(input: ExternalEventInput, record: typeof recordExternalEvent = recordExternalEvent): void {
46
+ try { record(input); }
47
+ catch (error) {
48
+ try { debug.log('context.external', 'record-failed', {
49
+ origin: input.origin, kind: input.kind, reason: error instanceof Error ? error.message : String(error),
50
+ }, { level: 'warn' }); } catch { /* A broken logger cannot change an agent's result. */ }
51
+ }
52
+ }
@@ -0,0 +1,108 @@
1
+ import { redactSecretText } from '../debug/log.js';
2
+ import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
3
+ import { listCoordEvents, type CoordEvent } from './coord-events.js';
4
+
5
+ export interface MemoryItem {
6
+ project: string;
7
+ seat: string;
8
+ topic: string;
9
+ summary: string;
10
+ source: string;
11
+ updatedAt: string;
12
+ status: 'active' | 'retired';
13
+ /** A normalized, comparable assertion supplied by the summarizer; absent means no conflict judgment. */
14
+ claim?: { key: string; value: string };
15
+ conflict?: { owner: 'OP'; sources: [string, string] };
16
+ }
17
+
18
+ export type MemorySource =
19
+ | { kind: 'event'; at: string; seat: string; text: string; source: string }
20
+ | { kind: 'decision'; at: string; seat: string; text: string; source: string };
21
+
22
+ export interface MemorySummary {
23
+ project: string;
24
+ topic: string;
25
+ summary: string;
26
+ /** Only supply this when the two strings identify the same fact and mutually exclusive values. */
27
+ claim?: { key: string; value: string };
28
+ }
29
+
30
+ export interface MemoryDeps {
31
+ summarize: (source: MemorySource) => Promise<MemorySummary>;
32
+ events?: (since: string) => CoordEvent[];
33
+ decisions?: () => DecisionEntry[];
34
+ }
35
+
36
+ const DAY_MS = 86_400_000;
37
+ const oneLine = (text: string) => redactSecretText(text.split(/\r?\n/, 1)[0] ?? '').trim();
38
+
39
+ /** Condense one UTC day; callers persist the returned snapshot, not raw transcripts. Read-only ledger access. */
40
+ export async function condenseContextDay(
41
+ day: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
42
+ ): Promise<MemoryItem[]> {
43
+ if (!/^\d{4}-\d\d-\d\d$/.test(day) || new Date(`${day}T00:00:00.000Z`).toISOString().slice(0, 10) !== day) {
44
+ throw new Error('day must be a valid UTC YYYY-MM-DD');
45
+ }
46
+ const since = `${day}T00:00:00.000Z`;
47
+ const start = Date.parse(since);
48
+ const until = start + DAY_MS;
49
+ // The store compares timestamp text, so fetch the previous local day too before UTC filtering.
50
+ const events = (deps.events ?? (from => listCoordEvents({ since: from })))(new Date(start - DAY_MS).toISOString());
51
+ const decisions = (deps.decisions ?? (() => new DecisionLedger().list({ status: 'all' })))();
52
+ const sources: MemorySource[] = [
53
+ ...events.map(event => ({ kind: 'event' as const, at: event.at, seat: event.refs.seat,
54
+ text: event.summary, source: event.refs.source ?? event.refs.url ?? `elanous://context/event/${encodeURIComponent(event.id)}` })),
55
+ ...decisions.map(decision => ({ kind: 'decision' as const,
56
+ at: decision.decidedAt ?? decision.raisedAt ?? decision.importedAt ?? '',
57
+ seat: decision.raisedBy.track ?? 'OP',
58
+ text: decision.status === 'decided' ? `${decision.title}: ${decision.options.find(o => o.key === decision.choice)?.label ?? decision.note ?? decision.scqa.a ?? '결정됨'}` : `${decision.title}: ${decision.status}`,
59
+ source: `elanous://decisions/${encodeURIComponent(decision.id)}` })),
60
+ ].filter(source => {
61
+ const at = Date.parse(source.at);
62
+ return Number.isFinite(at) && at >= start && at < until;
63
+ }).sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.source.localeCompare(b.source));
64
+
65
+ const items = new Map<string, MemoryItem>();
66
+ const keyFor = (item: Pick<MemoryItem, 'project' | 'seat' | 'topic'>) => JSON.stringify([item.project, item.seat, item.topic]);
67
+ for (const item of previous) items.set(keyFor(item), { ...item });
68
+ for (const source of sources) {
69
+ const summary = await deps.summarize(source);
70
+ const project = oneLine(summary.project);
71
+ const seat = oneLine(source.seat);
72
+ const topic = oneLine(summary.topic);
73
+ const text = oneLine(summary.summary);
74
+ const link = oneLine(source.source);
75
+ if (!project || !seat || !topic || !text || !/^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(link)) {
76
+ throw new Error('memory summary needs a project, seat, topic, one-line summary and source link');
77
+ }
78
+ const claim = summary.claim && { key: oneLine(summary.claim.key), value: oneLine(summary.claim.value) };
79
+ if (claim && (!claim.key || !claim.value)) throw new Error('memory claim needs key and value');
80
+ const key = keyFor({ project, seat, topic });
81
+ const old = items.get(key);
82
+ // Equal timestamps are resolved by the source link, so shuffled input does not change the winner.
83
+ if (old && (Date.parse(old.updatedAt) > Date.parse(source.at)
84
+ || (Date.parse(old.updatedAt) === Date.parse(source.at) && old.source >= link))) continue;
85
+ items.set(key, { project, seat, topic, summary: text, source: link, updatedAt: source.at,
86
+ status: 'active', ...(claim ? { claim } : {}) });
87
+ }
88
+ const cutoff = now.getTime() - 30 * DAY_MS;
89
+ // Compare only the winning, non-retired assertions; a superseded claim cannot keep a decision candidate alive.
90
+ const finalItems: MemoryItem[] = [...items.values()].map(({ conflict: _conflict, ...item }) => ({ ...item,
91
+ status: Date.parse(item.updatedAt) < cutoff ? 'retired' as const : 'active' as const,
92
+ }));
93
+ finalItems.sort((a, b) => Date.parse(a.updatedAt) - Date.parse(b.updatedAt) || a.source.localeCompare(b.source)
94
+ || a.seat.localeCompare(b.seat));
95
+ for (let index = 0; index < finalItems.length; index++) {
96
+ const item = finalItems[index]!;
97
+ if (item.status !== 'active' || !item.claim) continue;
98
+ const { key, value } = item.claim;
99
+ const conflicting = finalItems.slice(0, index).find(other => other.status === 'active'
100
+ && other.project === item.project
101
+ && other.claim?.key === key && other.claim.value !== value);
102
+ if (conflicting) {
103
+ finalItems[index] = { ...item, conflict: { owner: 'OP', sources: [conflicting.source, item.source] } };
104
+ }
105
+ }
106
+ return finalItems.sort((a, b) => a.project.localeCompare(b.project) || a.seat.localeCompare(b.seat)
107
+ || a.topic.localeCompare(b.topic));
108
+ }
@@ -18,6 +18,7 @@ import type { ShootRunOptions } from '../ad-pipeline/shoot-run.js';
18
18
  import type { QcThresholds } from '../ad-pipeline/qc.js';
19
19
  import { elanousStateRoot } from '../autopilot/state-paths.js';
20
20
  import { suggestProjectForFolder } from '../project/project-store.js';
21
+ import { createProjectSuggester } from '../project/project-suggestion.js';
21
22
 
22
23
  const dashboardDefaultShootRunOptions: Required<Pick<ShootRunOptions, 'pollIntervalMs' | 'maxPollsPerJob' | 'submitStaggerMs'>> = {
23
24
  pollIntervalMs: 3_000,
@@ -15791,12 +15792,16 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15791
15792
  chatLines.push(C.muted(` ${resume.turns} turns restored. Next turn continues this conversation.`));
15792
15793
  }
15793
15794
  }
15795
+ // 제안은 세션 작업 폴더 기준 — 시작 폴더 한 번이 아니라 폴더가 바뀔 때마다 다시 계산한다(IA1 리뷰 must-fix).
15796
+ const nextProjectSuggestion = createProjectSuggester(suggestProjectForFolder);
15797
+ const pushProjectSuggestion = (folder: string): void => {
15798
+ const project = nextProjectSuggestion(folder);
15799
+ if (project) chatLines.push(C.muted(` Project suggestion: ${project.name} (${project.id})`));
15800
+ };
15794
15801
  if (dashboardAcpBootResult.mode !== 'resumed' && !chat.history.some((message) => message.role !== 'system')) {
15795
- try {
15796
- const project = suggestProjectForFolder(process.cwd());
15797
- if (project) chatLines.push(C.muted(` Project suggestion: ${project.name} (${project.id})`));
15798
- } catch { /* suggestions must not interrupt the dashboard conversation */ }
15802
+ pushProjectSuggestion(getSessionCwd());
15799
15803
  }
15804
+ subscribeSessionCwd((state) => { pushProjectSuggestion(state.cwd); });
15800
15805
  chatScrollOffset = -1;
15801
15806
  void renderDashboardFirstScreenBand({
15802
15807
  history: chat.history,
@@ -272,6 +272,8 @@
272
272
  // `../index.ts`) instead of importing the type — keeps this module free
273
273
  // of circular dependency on dashboard/index.ts.
274
274
 
275
+ import { tuiNowSlash } from '../../context-bus/context-now-surfaces.js';
276
+ import type { ContextNowDeps } from '../../context-bus/context-now.js';
275
277
  import { runRebindCommand } from '../../input-core/index.js';
276
278
  // B4 (TUI half) — /design reuses the CLI's own resolution so the three
277
279
  // surfaces (CLI, PWA panel, this) cannot answer differently.
@@ -1358,9 +1360,13 @@ function emitReplyToChat(ctx: Pick<DashboardSlashContext, 'pushChatLine'>, line:
1358
1360
  ctx.pushChatLine(line);
1359
1361
  }
1360
1362
 
1361
- export function buildDashboardSlashRegistry(): SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn> {
1363
+ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn> {
1362
1364
  const registry = new SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn>();
1363
1365
 
1366
+ registry.register(['now'], (args, ctx) => {
1367
+ for (const line of tuiNowSlash(args, nowDeps)) ctx.pushChatLine(line);
1368
+ });
1369
+
1364
1370
  // ── B-1.a pilot ────────────────────────────────────────────────────
1365
1371
  registry.register(['quit', 'q', 'exit'], (_args, ctx) => {
1366
1372
  (ctx.exitTui ?? ctx.closeTui)();
@@ -43,6 +43,7 @@ export function renderCardText(e: DecisionEntry, extra: { note?: string; confirm
43
43
  '',
44
44
  `상황: ${e.scqa.s}`,
45
45
  `문제: ${e.scqa.c}`,
46
+ ...(e.pendingQuestion ? ['', `대기 질문 전문:\n${e.pendingQuestion}`] : []),
46
47
  ...(e.scqa.q ? [`질문: ${e.scqa.q}`] : []),
47
48
  ...(e.scqa.a ? [`제안: ${e.scqa.a}`] : []),
48
49
  '',
@@ -180,7 +181,8 @@ export class DecisionCardService {
180
181
  const entry = this.ledger.raise({
181
182
  title: question.question.split(/\r?\n/, 1)[0]!.slice(0, 120),
182
183
  category: question.impact === 'critical' ? 'irreversible' : 'scope',
183
- scqa: { s: question.question, c: question.question },
184
+ scqa: { s: Array.from(question.question.split(/\r?\n/, 1)[0]!).slice(0, 200).join(''), c: 'The requesting run is waiting for this choice.' },
185
+ pendingQuestion: question.question,
184
186
  options: question.options.map((option, index) => ({ key: keys[index]!, label: option.label, consequence: option.description })),
185
187
  recommendation: recommended !== undefined && Number.isInteger(recommended) && recommended >= 0 && recommended < question.options.length
186
188
  ? { option: keys[recommended]!, why: '런이 추천' } : { skipped: true, reason: '추천 없음' },
@@ -251,9 +253,12 @@ export class DecisionCardService {
251
253
  return { kind: 'confirm', view: renderCard(entry, { confirm: parsed.key, ...(card?.note ? { note: card.note } : {}) }), toast: '되돌릴 수 없는 결정 — 한 번 더 확인해 주세요' };
252
254
  }
253
255
  let decided: DecisionEntry;
256
+ let answerDelivery: ReturnType<DecisionLedger['decideWithDelivery']>['delivery'] = null;
254
257
  try {
255
258
  // Same ledger method the CLI `decide` uses — one record path.
256
- decided = this.ledger.decide(entry.id, parsed.key, { kind: 'human' }, card?.note);
259
+ const recorded = this.ledger.decideWithDelivery(entry.id, parsed.key, { kind: 'human' }, card?.note);
260
+ decided = recorded.entry;
261
+ answerDelivery = recorded.delivery;
257
262
  } catch (error) {
258
263
  debug.log('decisions.telegram', 'decide-failed', { platform, id: entry.id, reason: error instanceof Error ? error.message.slice(0, 80) : 'unknown' }, { level: 'warn' });
259
264
  try { const now = this.ledger.show(entry.id); if (now.status !== 'open') return { kind: 'closed', view: renderCard(now), toast: '이미 정해진 결정입니다' }; } catch { /* fall through */ }
@@ -269,7 +274,9 @@ export class DecisionCardService {
269
274
  await this.opts.replyToRaiser?.(decided, platform).catch((error: unknown) => {
270
275
  debug.log('decisions.telegram', 'reply-failed', { platform, id: decided.id, reason: error instanceof Error ? error.message.slice(0, 80) : 'unknown' }, { level: 'warn' });
271
276
  });
272
- return { kind: 'decided', view, toast: `결정했습니다: ${decided.choice?.toUpperCase()}` };
277
+ return { kind: 'decided', view, toast: answerDelivery && !answerDelivery.ok
278
+ ? `결정은 기록됐지만 런 답 전달 실패 — decisions retry-answer ${decided.id}`
279
+ : `결정했습니다: ${decided.choice?.toUpperCase()}` };
273
280
  }
274
281
 
275
282
  /** Store a memo typed after «메모 달기»; the card shows it and the eventual decision carries it. */
@@ -7,7 +7,8 @@ import { createVersionResolver, type VersionOptions, type Versions } from '../di
7
7
  import { getUserConfig } from '../user-config.js';
8
8
 
9
9
  export type DecisionCategory = 'secret' | 'publish' | 'money' | 'security' | 'scope' | 'irreversible' | 'other';
10
- export type DecisionTrack = 'S' | 'T' | 'F' | 'O';
10
+ /** Old track letters (S·T·F·O) stay readable; seat names (OP·MK·TC·UX) are the current identities. */
11
+ export type DecisionTrack = 'S' | 'T' | 'F' | 'O' | 'OP' | 'MK' | 'TC' | 'UX';
11
12
  export interface DecisionOption { key: string; label: string; consequence: string }
12
13
  export type Recommendation = { option: string; why: string } | { skipped: true; reason: string };
13
14
  export type DecisionActor = { kind: 'human' } | { kind: 'auto'; agent: string; track?: DecisionTrack; delegation: string };
@@ -17,13 +18,15 @@ export interface DecisionEntry {
17
18
  raisedBy: { agent: string; track?: DecisionTrack; session?: string }; version?: Versions;
18
19
  status: 'open' | 'decided' | 'withdrawn'; refs?: string[];
19
20
  resume?: { questionId: string; runId?: string };
21
+ /** Original pending question, including its newlines; SCQA fields are short summaries. */
22
+ pendingQuestion?: string;
20
23
  /** DEC-TG — optional deadline (UTC). Cards remind the owner two hours before it. */
21
24
  dueAt?: string;
22
25
  decidedAt?: string; decidedBy?: DecisionActor; choice?: string; note?: string; versionAtDecision?: Versions;
23
26
  withdrawnAt?: string; withdrawReason?: string;
24
27
  history: Array<{ type: 'raised' | 'options-added' | 'decided' | 'withdrawn'; at?: string; by: string; version?: Versions; choice?: string; reason?: string }>;
25
28
  }
26
- export type RaiseInput = Pick<DecisionEntry, 'title' | 'category' | 'scqa' | 'options' | 'recommendation' | 'raisedBy' | 'refs' | 'dueAt' | 'resume'> & { raisedAt?: string };
29
+ export type RaiseInput = Pick<DecisionEntry, 'title' | 'category' | 'scqa' | 'options' | 'recommendation' | 'raisedBy' | 'refs' | 'dueAt' | 'resume' | 'pendingQuestion'> & { raisedAt?: string };
27
30
  type Event = { type: 'raised'; entry: DecisionEntry } | { type: 'options-added'; id: string; at: string; options: DecisionOption[]; by: string } | { type: 'decided'; id: string; at?: string; by: DecisionActor; choice?: string; version?: Versions; note?: string } | { type: 'withdrawn'; id: string; at: string; reason: string; version: Versions };
28
31
  export interface DecisionLedgerOptions extends VersionOptions { stateDir?: string; now?: () => Date; resolveVersion?: (at: string) => Versions; writeAnswer?: typeof writePendingQuestionAnswer }
29
32
 
@@ -38,7 +41,7 @@ export class DecisionAnswerDeliveryError extends Error {
38
41
  }
39
42
  }
40
43
  const CATEGORIES: readonly string[] = ['secret', 'publish', 'money', 'security', 'scope', 'irreversible', 'other'];
41
- const TRACKS: readonly string[] = ['S', 'T', 'F', 'O'];
44
+ const TRACKS: readonly string[] = ['S', 'T', 'F', 'O', 'OP', 'MK', 'TC', 'UX'];
42
45
 
43
46
  function required(value: string, name: string): string {
44
47
  if (typeof value !== 'string' || !value.trim()) throw new Error(`${name} is required`);
@@ -86,10 +89,12 @@ function validate(input: RaiseInput, historical = false): RaiseInput {
86
89
  ? { skipped: true as const, reason: safe(single(input.recommendation.reason, 'skip reason')) }
87
90
  : { option: input.recommendation.option, why: safe(single(input.recommendation.why, 'recommendation why')) };
88
91
  if ('option' in recommendation && !options.some(o => o.key === recommendation.option)) throw new Error('recommended option not found');
89
- if (input.resume && !/^auq:[0-9a-z]+:[0-9a-z]{5}(?![\s\S])/.test(input.resume.questionId)) throw new Error('invalid resume questionId');
92
+ if (input.resume && !/^(?:auq:[0-9a-z]+:[0-9a-z]{5}|execution:[A-Za-z0-9_-]+:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})(?![\s\S])/.test(input.resume.questionId)) throw new Error('invalid resume questionId');
90
93
  const resume = input.resume ? { questionId: input.resume.questionId,
91
94
  ...(input.resume.runId === undefined ? {} : { runId: single(input.resume.runId, 'resume runId') }) } : undefined;
95
+ if (input.pendingQuestion !== undefined) required(input.pendingQuestion, 'pending question');
92
96
  return { ...input, title, scqa: { s, c, ...(q ? { q } : {}), ...(a ? { a } : {}) }, options, recommendation,
97
+ ...(input.pendingQuestion === undefined ? {} : { pendingQuestion: safe(input.pendingQuestion) }),
93
98
  ...(resume ? { resume } : {}),
94
99
  raisedBy: { agent: safe(single(input.raisedBy.agent, 'agent')), ...(input.raisedBy.track ? { track: input.raisedBy.track } : {}), ...(input.raisedBy.session ? { session: safe(single(input.raisedBy.session, 'session')) } : {}) },
95
100
  ...(input.refs ? { refs: input.refs.map(r => safe(single(r, 'ref'))) } : {}),
@@ -263,13 +268,16 @@ export class DecisionLedger {
263
268
  if (!pending.ok) throw new Error(pending.error);
264
269
  const target = pending.questions.find(item => item.id === questionId);
265
270
  if (!target || target.questions.length !== 1 || !target.questions[0]?.id) throw new Error('pending question target not found or ambiguous');
271
+ if (questionId.startsWith('execution:') && (!runId || target.runId !== runId || !questionId.startsWith(`execution:${runId}:`))) throw new Error('pending question run differs from decision');
272
+ if (!questionId.startsWith('execution:') && runId !== undefined && target.runId !== undefined && target.runId !== runId) throw new Error('pending question run differs from decision');
273
+ if (target.expiresAt && Date.parse(target.expiresAt) <= this.now().getTime()) throw new Error('pending question expired');
266
274
  const question = target.questions[0];
267
275
  if (question.options.length !== entry.options.length || question.options.some((option, index) =>
268
276
  entry.options[index]?.key !== String.fromCharCode(97 + index) || entry.options[index]?.label !== option.label)) {
269
277
  throw new Error('pending question options differ from decision');
270
278
  }
271
279
  const answerKey = question.id;
272
- const answer = { id: questionId, result: { answers: { [answerKey]: `${entry.choice}) ${label}` },
280
+ const answer = { id: questionId, result: { answers: { [answerKey]: questionId.startsWith('execution:') ? label : `${entry.choice}) ${label}` },
273
281
  ...(entry.note ? { otherText: { [answerKey]: entry.note } } : {}) } };
274
282
  const existing = readPendingQuestionAnswer(questionId, deps);
275
283
  if (!existing.ok) throw new Error('existing answer unreadable');
@@ -21,6 +21,7 @@
21
21
  // ⏳ Mention parser → persona LLM dispatch wire
22
22
 
23
23
  import { debug } from '../debug/log.js';
24
+ import { botAudienceFor, gateDiscordSchemas } from '../maturity/bot-command-maturity.js';
24
25
  import type { DiscordBot } from '../discord.js';
25
26
  import {
26
27
  loadLayeredPersonaDirs,
@@ -204,7 +205,10 @@ export async function wireSprint21Runtime(opts: WireSprint21Opts): Promise<Sprin
204
205
  // 6. Slash command registration
205
206
  const cmdRest = makeCommandRest({ token: opts.token, fetchImpl: opts.fetchImpl });
206
207
  async function registerSlashCommands(): Promise<number> {
207
- const schemas = router.schemas();
208
+ const allSchemas = router.schemas();
209
+ const audience = botAudienceFor('discord');
210
+ const { schemas, hidden } = gateDiscordSchemas(allSchemas, audience);
211
+ debug.log('discord.command', 'menu-filtered', { total: allSchemas.length, shown: schemas.length - hidden, ...audience });
208
212
  if (opts.devGuildId) {
209
213
  const out = await cmdRest.bulkOverwriteGuild(opts.appId, opts.devGuildId, schemas);
210
214
  log(`[sprint21-wire] registered ${out.length} slash commands to guild ${opts.devGuildId}`);
@@ -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[];