elanous 0.2.9 → 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 (694) 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/5845-e6c003ce8d5fd36a.js +1 -0
  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-f972fc1c00a62e88.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-adfbd2ba9598e59a.js → page-35414e0a007b4082.js} +1 -1
  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 -0
  152. package/apps/pwa/out/ops/seats/index.txt +30 -0
  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 -0
  178. package/apps/pwa/out/today/index.txt +33 -0
  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/field/field-feed.yaml +2 -0
  194. package/graphs/implement-loop.yaml +1 -1
  195. package/graphs/intake/intake-daily.yaml +2 -1
  196. package/graphs/ops/recipes.yaml +3 -0
  197. package/graphs/ops/secret-exposure.yaml +24 -0
  198. package/graphs/release/recipes.yaml +3 -0
  199. package/graphs/release/release-loop.yaml +4 -1
  200. package/graphs/video/explainer-line.yaml +2 -2
  201. package/package.json +2 -1
  202. package/plugins/card-followup/graphs/run-step.ts +48 -13
  203. package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
  204. package/plugins/doc-draft/graphs/run-step.ts +13 -6
  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/ci-pwa-build-gate.ts +160 -0
  216. package/scripts/context-hooks/claude-stop.sh +4 -0
  217. package/scripts/context-hooks/emit.ts +24 -0
  218. package/scripts/context-hooks/post-tool-use.sh +4 -0
  219. package/scripts/coord-channel-watch.sh +54 -8
  220. package/scripts/coord-digest.ts +199 -0
  221. package/scripts/coord-post.sh +60 -3
  222. package/scripts/coord-requests-bridge.ts +143 -0
  223. package/scripts/field-feed/lib.ts +4 -0
  224. package/scripts/fleet/node-setup.sh +20 -3
  225. package/scripts/homebrew-formula-bump.ts +69 -0
  226. package/scripts/install.ps1 +68 -9
  227. package/scripts/install.sh +39 -12
  228. package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
  229. package/scripts/lib/tui-regress-checks.ts +146 -0
  230. package/scripts/marketing/measure-site-numbers.ts +169 -0
  231. package/scripts/measure-gate-shards.ts +309 -0
  232. package/scripts/memory-lifecycle-cycle.ts +2 -2
  233. package/scripts/mission-request-judge.ts +31 -1
  234. package/scripts/morning-report.ts +2 -2
  235. package/scripts/opportunity-scan.ts +1 -1
  236. package/scripts/ops-now-probe.ts +327 -0
  237. package/scripts/publish-internal-dist.ts +11 -1
  238. package/scripts/release-loop/auto-approve-node.ts +21 -4
  239. package/scripts/release-loop/cut-branch.ts +84 -0
  240. package/scripts/release-loop/cutoff.ts +19 -8
  241. package/scripts/release-loop/gate-node.ts +14 -13
  242. package/scripts/release-loop/mac-smoke-node.ts +229 -0
  243. package/scripts/release-loop/node-verdict.ts +3 -3
  244. package/scripts/release-loop/ops-upgrade-node.ts +72 -16
  245. package/scripts/release-loop/release-readiness.ts +9 -0
  246. package/scripts/release-loop/tui-node-checks.ts +42 -0
  247. package/scripts/release-loop/tui-sim-node.ts +32 -8
  248. package/scripts/release-loop/unattended-release.ts +5 -3
  249. package/scripts/release-loop/version-node.ts +112 -13
  250. package/scripts/repo-watch-cycle.ts +1 -1
  251. package/scripts/retro-cycle.ts +2 -2
  252. package/scripts/schedule-health-report.ts +1 -1
  253. package/scripts/signal-digest-cycle.ts +2 -7
  254. package/scripts/signal-digest-photo.ts +11 -0
  255. package/scripts/storyboard/storyboard.ts +5 -161
  256. package/scripts/tech1-public-measure.ts +94 -0
  257. package/scripts/test-diet/lib.ts +30 -1
  258. package/scripts/test-diet/node.ts +83 -8
  259. package/scripts/test-timeout-audit.ts +114 -0
  260. package/scripts/tree-sync-alert.ts +1 -1
  261. package/scripts/tui-regress.ts +71 -0
  262. package/scripts/ux/demo-rehearsal.ts +243 -0
  263. package/scripts/ux/lib/rehearsal-verdict.ts +73 -0
  264. package/scripts/waitlist-daily-digest.ts +1 -1
  265. package/skills/diagram-master/SKILL.md +366 -0
  266. package/skills/diagram-master/references/engines/excalidraw/color-palette.md +67 -0
  267. package/skills/diagram-master/references/engines/excalidraw/element-templates.md +182 -0
  268. package/skills/diagram-master/references/engines/excalidraw/json-schema.md +71 -0
  269. package/skills/diagram-master/references/engines/excalidraw/render_excalidraw.py +189 -0
  270. package/skills/diagram-master/references/engines/excalidraw/render_template.html +57 -0
  271. package/skills/diagram-master/references/engines/matplotlib/matplotlib-patterns.md +235 -0
  272. package/skills/diagram-master/references/engines/matplotlib/render_matplotlib.py +184 -0
  273. package/skills/diagram-master/references/engines/mermaid/domain-templates.md +435 -0
  274. package/skills/diagram-master/references/engines/mermaid/render_mermaid.py +116 -0
  275. package/skills/diagram-master/references/engines/mermaid/syntax-reference.md +2247 -0
  276. package/skills/diagram-master/references/engines/nanobannana/render_nanobannana2.py +168 -0
  277. package/skills/diagram-master/references/engines/svg/color-palette.md +188 -0
  278. package/skills/diagram-master/references/engines/svg/html-base-template.html +332 -0
  279. package/skills/diagram-master/references/engines/svg/interactivity-patterns.md +435 -0
  280. package/skills/diagram-master/references/engines/svg/render_interactive_svg.py +212 -0
  281. package/skills/diagram-master/references/engines/svg/svg-element-templates.md +349 -0
  282. package/skills/diagram-master/references/examples/README.md +69 -0
  283. package/skills/diagram-master/references/examples/ref_grid_architecture.png +0 -0
  284. package/skills/diagram-master/references/examples/ref_layered_flow.png +0 -0
  285. package/skills/diagram-master/references/pyproject.toml +16 -0
  286. package/skills/diagram-master/references/quality-checklist.md +33 -0
  287. package/skills/diagram-master/references/routing-guide.md +164 -0
  288. package/skills/diagram-master/references/uv.lock +1356 -0
  289. package/skills/explainer-video/SKILL.md +4 -0
  290. package/skills/explainer-video/engine/reel.mjs +18 -13
  291. package/skills/explainer-video/engine/reel.sh +24 -5
  292. package/skills/explainer-video/engine/stitch.mjs +145 -0
  293. package/skills/google-workspace/SKILL.md +33 -0
  294. package/skills/google-workspace/scripts/gws-safe.sh +177 -0
  295. package/skills/grill-me/SKILL.md +1 -1
  296. package/skills/lecture-note-digitizer/SKILL.md +926 -0
  297. package/skills/lecture-note-digitizer/references/convert_to_pdf.py +30 -0
  298. package/skills/lecture-note-digitizer/references/extract_slide_diagram.py +73 -0
  299. package/skills/lecture-note-digitizer/references/html-template.md +167 -0
  300. package/skills/lecture-note-digitizer/references/katex-style-guide.md +165 -0
  301. package/skills/omni-crawl/SKILL.md +21 -11
  302. package/skills/omni-crawl/scripts/main.ts +6 -6
  303. package/skills/omni-crawl/src/router.ts +3 -3
  304. package/skills/omni-digest/SKILL.md +17 -9
  305. package/skills/omni-digest/digest.md +2 -2
  306. package/skills/omni-digest/src/fetch.ts +30 -3
  307. package/skills/pitch-deck/SKILL.md +36 -0
  308. package/skills/pitch-deck/template.html +80 -0
  309. package/skills/youtube-master/SKILL.md +163 -0
  310. package/skills/youtube-master/package-lock.json +558 -0
  311. package/skills/youtube-master/package.json +13 -0
  312. package/skills/youtube-master/references/study-note-template.md +124 -0
  313. package/skills/youtube-master/scripts/main.ts +435 -0
  314. package/skills/youtube-master/scripts/transcribe-local.ts +24 -0
  315. package/skills/youtube-master/src/audio.ts +63 -0
  316. package/skills/youtube-master/src/domain-staging.ts +50 -0
  317. package/skills/youtube-master/src/env.ts +36 -0
  318. package/skills/youtube-master/src/obsidian.ts +155 -0
  319. package/skills/youtube-master/src/router.ts +110 -0
  320. package/skills/youtube-master/src/stt.ts +199 -0
  321. package/skills/youtube-master/src/study-note.ts +175 -0
  322. package/skills/youtube-master/src/summarize.ts +358 -0
  323. package/skills/youtube-master/src/transcript.ts +76 -0
  324. package/skills/youtube-master/src/types.ts +91 -0
  325. package/skills/youtube-master/src/util.ts +93 -0
  326. package/skills/youtube-master/src/youtube.ts +70 -0
  327. package/skills/youtube-master/tsconfig.json +13 -0
  328. package/src/acp/codex-approval-adapter.ts +9 -3
  329. package/src/acp/core-turn-bridge.ts +117 -18
  330. package/src/acp/fact-question.ts +11 -0
  331. package/src/acp/server.ts +53 -9
  332. package/src/acp/tool-approval.ts +126 -0
  333. package/src/acp/turn-runner.ts +36 -0
  334. package/src/agent/monad-agent-turn.ts +8 -0
  335. package/src/agent-mission/driver.ts +80 -10
  336. package/src/agent-mission/pty-decision.ts +71 -0
  337. package/src/agent-mission/review-loop.ts +30 -0
  338. package/src/ask-user-question/pending-questions.ts +25 -10
  339. package/src/ask-user-question/types.ts +9 -0
  340. package/src/auth/setup-link-tokens.ts +16 -5
  341. package/src/autopilot/build/integrity-gate.ts +12 -0
  342. package/src/autopilot/build/llm-conflict-merge.ts +16 -3
  343. package/src/autopilot/tool-registry.ts +2 -0
  344. package/src/boot/daemon-prompt-turn.ts +8 -0
  345. package/src/boot/daemon-runtime.ts +9 -3
  346. package/src/boot/daemon-tools/index.ts +8 -0
  347. package/src/boot/daemon-tools/types.ts +2 -0
  348. package/src/boot/daemon-tools/web-search.ts +4 -0
  349. package/src/card-followup/core.ts +109 -0
  350. package/src/chat/ctrl-c-in-input.ts +14 -0
  351. package/src/chat/index.ts +53 -0
  352. package/src/chat/pickers/state.ts +6 -2
  353. package/src/cli/agent-cli.ts +7 -0
  354. package/src/cli/decisions-cli.ts +25 -3
  355. package/src/cli/doctor-bundle-upload.ts +68 -0
  356. package/src/cli/doctor-cli.ts +38 -4
  357. package/src/cli/drive-input-jail.ts +101 -0
  358. package/src/cli/hooks-cli.ts +17 -3
  359. package/src/cli/make-cli.ts +43 -0
  360. package/src/cli/ops-cli.ts +20 -0
  361. package/src/cli/persona-cli.ts +90 -4
  362. package/src/cli/plugin-cli.ts +31 -22
  363. package/src/cli/pod-cli.ts +61 -2
  364. package/src/cli/pr-cli.ts +82 -17
  365. package/src/cli/pty-drive-cli.ts +41 -7
  366. package/src/cli/pty-drive-verdict.ts +40 -0
  367. package/src/cli/pwa-stop.ts +163 -13
  368. package/src/cli/pwa-test.ts +3 -0
  369. package/src/cli/release-cli.ts +80 -24
  370. package/src/cli/self-update.ts +14 -0
  371. package/src/cli/setup-cli.ts +43 -1
  372. package/src/cli/skills-cli.ts +17 -1
  373. package/src/cli/storyboard-cli.ts +37 -0
  374. package/src/cli/test-state-dir-flag.ts +11 -4
  375. package/src/connectors/linear.ts +146 -0
  376. package/src/context-bus/claude-hooks-install.ts +10 -0
  377. package/src/context-bus/context-now-surfaces.ts +48 -0
  378. package/src/context-bus/context-now.ts +104 -0
  379. package/src/context-bus/coord-events.ts +115 -0
  380. package/src/context-bus/external-events.ts +52 -0
  381. package/src/context-bus/long-term-memory.ts +108 -0
  382. package/src/dashboard/dashboard-boot-fixture.ts +23 -0
  383. package/src/dashboard/index.ts +63 -21
  384. package/src/dashboard/slash-runtime/dashboard-handlers.ts +14 -1
  385. package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
  386. package/src/decisions/decision-cards.ts +58 -3
  387. package/src/decisions/decision-ledger.ts +85 -6
  388. package/src/decisions/decision-linear-projection.ts +157 -0
  389. package/src/decisions/web-push-decision-cards.ts +50 -0
  390. package/src/directive/interpretation-card.ts +36 -0
  391. package/src/discord/sprint21-runtime.ts +5 -1
  392. package/src/discord-self-message.ts +32 -0
  393. package/src/discord-slash-wire.ts +5 -1
  394. package/src/discord.ts +19 -2
  395. package/src/domains/coo-admin-tool.ts +62 -0
  396. package/src/domains/core-tools.ts +13 -4
  397. package/src/domains/elanous-card.ts +29 -0
  398. package/src/domains/ops-facts-tool.ts +76 -0
  399. package/src/domains/outbound-alert.ts +46 -13
  400. package/src/domains/release-tool.ts +168 -0
  401. package/src/domains/telegram-kind-route.ts +65 -9
  402. package/src/exec-requests/default-outputs.ts +25 -0
  403. package/src/exec-requests/planner.ts +106 -28
  404. package/src/exec-requests/runner.ts +123 -12
  405. package/src/exec-requests/store.ts +11 -2
  406. package/src/execution-loop/launch-gate.ts +100 -0
  407. package/src/field/field-media.ts +10 -7
  408. package/src/field/field-reel.ts +19 -5
  409. package/src/field-note/index.ts +201 -0
  410. package/src/field-note/transcribe.ts +248 -0
  411. package/src/git-fs/worktree.ts +45 -6
  412. package/src/graph-kinds/registry.ts +3 -3
  413. package/src/graph-runner/graph-approval-card.ts +113 -0
  414. package/src/graph-runner/graph-cli.ts +189 -4
  415. package/src/graph-runner/graph-grow-llm.ts +37 -0
  416. package/src/graph-runner/graph-grow.ts +47 -0
  417. package/src/graph-runner/graph-notify.ts +25 -3
  418. package/src/graph-runner/runner.ts +453 -35
  419. package/src/grok/agent-search.ts +8 -2
  420. package/src/guardian/secret-exposure-scan.ts +101 -0
  421. package/src/harness/browser-verify.ts +25 -2
  422. package/src/harness/harness-cli-command.ts +478 -42
  423. package/src/harness/harness-incidents-writer.fixture.ts +24 -0
  424. package/src/harness/harness-incidents.ts +129 -0
  425. package/src/harness/harness-pod-dispatch.ts +43 -23
  426. package/src/harness/harness-seams.ts +38 -4
  427. package/src/harness/harness-stop.ts +75 -6
  428. package/src/harness/launch-registry.ts +91 -0
  429. package/src/harness/process-garbage.ts +5 -3
  430. package/src/hitl/clarification-policy.ts +3 -1
  431. package/src/hooks/dispatch.ts +82 -0
  432. package/src/hooks/expose.ts +254 -0
  433. package/src/hooks/github-shadow.ts +108 -0
  434. package/src/hooks/providers.ts +71 -5
  435. package/src/hooks/queue.ts +1 -1
  436. package/src/hooks/receiver.ts +21 -20
  437. package/src/index.ts +206 -49
  438. package/src/intake-plane/author-goals.ts +37 -4
  439. package/src/intake-plane/categorize.ts +18 -46
  440. package/src/intake-plane/check.ts +3 -0
  441. package/src/intake-plane/discord-seat-work.ts +57 -2
  442. package/src/intake-plane/front-route-classifier.ts +33 -39
  443. package/src/intake-plane/persona-answer.ts +30 -0
  444. package/src/intake-plane/runtime-callables.ts +8 -1
  445. package/src/intake-plane/seat-doc-route.ts +170 -6
  446. package/src/intake-plane/telegram-seat-work.ts +80 -4
  447. package/src/intake-plane/telegram-work.ts +1 -0
  448. package/src/intake-plane/wish-folder.ts +67 -0
  449. package/src/intent-gate/gate.ts +30 -10
  450. package/src/live/detail-switch.ts +5 -0
  451. package/src/llm/difficulty.ts +57 -0
  452. package/src/llm/judge-layer.ts +101 -0
  453. package/src/llm/mission-router.ts +33 -19
  454. package/src/loops/contract/examples/cmo-seat.loop.yaml +51 -0
  455. package/src/loops/contract/examples/orchestrator.loop.yaml +57 -0
  456. package/src/loops/contract/examples/steward.loop.yaml +50 -0
  457. package/src/loops/contract/loop-agent.schema.json +138 -0
  458. package/src/loops/contract/validate.ts +64 -0
  459. package/src/loops/registry.ts +20 -6
  460. package/src/market/signed-index.ts +4 -0
  461. package/src/maturity/bot-command-maturity.ts +45 -0
  462. package/src/maturity/cli-maturity.ts +34 -0
  463. package/src/maturity/feature-maturity.ts +120 -1
  464. package/src/maturity/tui-slash-maturity.ts +31 -0
  465. package/src/memory.ts +32 -4
  466. package/src/model-tier/index.ts +2 -2
  467. package/src/model-tier/tier-resolver.ts +20 -20
  468. package/src/model-tier/types.ts +6 -6
  469. package/src/mss/logging/log-store.ts +15 -0
  470. package/src/nexus/api/config-model-tier.ts +5 -4
  471. package/src/nexus/api/exec-requests.ts +33 -3
  472. package/src/nexus/api/fabric-plans.ts +53 -0
  473. package/src/nexus/api/harness-api.ts +5 -1
  474. package/src/nexus/api/health.ts +2 -0
  475. package/src/nexus/api/http-server.ts +89 -1
  476. package/src/nexus/api/inside-events.ts +226 -0
  477. package/src/nexus/api/meta-api.ts +32 -8
  478. package/src/nexus/api/missions.ts +6 -1
  479. package/src/nexus/api/ops-api.ts +10 -1
  480. package/src/nexus/api/ops-seats-sources.ts +37 -7
  481. package/src/nexus/api/ops-seats.ts +49 -6
  482. package/src/nexus/api/outputs-route.ts +44 -0
  483. package/src/nexus/api/seat-requests.ts +132 -10
  484. package/src/nexus/api/sessions-store.ts +42 -0
  485. package/src/nexus/api/setup-codex-login.ts +160 -0
  486. package/src/nexus/api/sub-seat-charters.ts +34 -0
  487. package/src/nexus/api/tabs-mutations.ts +3 -1
  488. package/src/nexus/api/trace.ts +18 -6
  489. package/src/nexus/api/usage-api.ts +102 -0
  490. package/src/nexus/api/workflow-run-event-bridge.ts +1 -0
  491. package/src/nexus/api/workflows.ts +80 -6
  492. package/src/nexus/index.ts +47 -4
  493. package/src/nexus/kinds/webterm.ts +2 -1
  494. package/src/nexus/webterm/pty.ts +6 -2
  495. package/src/onboarding/cli-auth-status.ts +80 -0
  496. package/src/onboarding/first-run.ts +89 -0
  497. package/src/onboarding/lan-address.ts +45 -0
  498. package/src/onboarding/voice-ai.ts +3 -3
  499. package/src/onboarding/web-first.ts +119 -24
  500. package/src/onboarding.ts +4 -0
  501. package/src/ops-now/ops-now-refresh.ts +100 -0
  502. package/src/org/seat-budget.ts +48 -0
  503. package/src/org/stall-escalation.ts +63 -0
  504. package/src/outputs/ledger.ts +120 -0
  505. package/src/persona/persona-todo.ts +79 -0
  506. package/src/persona/presets.ts +116 -2
  507. package/src/persona/team-set.ts +155 -0
  508. package/src/plugins/install/plugin-install.ts +14 -0
  509. package/src/plugins/maker/market-bundle.ts +109 -0
  510. package/src/plugins/maker/plugin-maker.ts +254 -11
  511. package/src/plugins/maker/wizard-generate.ts +103 -0
  512. package/src/pod-lease/admission.ts +150 -0
  513. package/src/pod-lease/dependency-state.ts +85 -0
  514. package/src/pod-lease/host-lease.ts +127 -0
  515. package/src/project/project-store.ts +115 -0
  516. package/src/project/project-suggestion.ts +16 -0
  517. package/src/project/session-context.ts +24 -0
  518. package/src/release-loop/checklist.ts +79 -5
  519. package/src/release-loop/feature-store.ts +159 -24
  520. package/src/release-loop/next-md-merge.ts +58 -0
  521. package/src/release-loop/release-note.ts +59 -4
  522. package/src/release-loop/release-schedule.ts +53 -0
  523. package/src/repl/index.ts +7 -0
  524. package/src/seat-dispatch/ceo-commands.ts +41 -8
  525. package/src/seat-dispatch/ceo-intent.ts +38 -0
  526. package/src/seat-dispatch/seat-ask.ts +130 -0
  527. package/src/seat-dispatch/seat-questions.ts +100 -0
  528. package/src/seat-loop/seat-loop.ts +750 -0
  529. package/src/seat-loop/seat-report.ts +99 -0
  530. package/src/self-dev/decompose.ts +66 -11
  531. package/src/self-dev/dev-cli.ts +4 -1
  532. package/src/self-dev/dev-pipeline.ts +37 -1
  533. package/src/self-dev/draft-sweep.ts +38 -33
  534. package/src/self-dev/draft-triage-rules.ts +8 -5
  535. package/src/self-dev/fabric-plan-core.ts +297 -0
  536. package/src/self-dev/orchestrate.ts +36 -4
  537. package/src/self-dev/release-path-guard.ts +62 -0
  538. package/src/self-dev/run-store.ts +10 -1
  539. package/src/self-dev/shard-aggregate.ts +230 -0
  540. package/src/self-dev/shard-placement.ts +60 -0
  541. package/src/self-dev/shard-plan.ts +48 -0
  542. package/src/self-dev/shard-pod-run.ts +77 -0
  543. package/src/self-dev/shard-run.ts +105 -0
  544. package/src/self-implement/execution-clarification.ts +115 -0
  545. package/src/self-implement/gate-baseline.ts +1 -1
  546. package/src/self-implement/gate-cli.ts +46 -4
  547. package/src/self-implement/gate-scope.ts +37 -7
  548. package/src/self-implement/graph-variant.ts +171 -0
  549. package/src/self-implement/harness-policy-gates.ts +1 -1
  550. package/src/self-implement/headless-elanous-driver.ts +22 -3
  551. package/src/self-implement/host-regate.ts +22 -1
  552. package/src/self-implement/importer-test-index.ts +2 -1
  553. package/src/self-implement/l8-merge-queue.ts +130 -0
  554. package/src/self-implement/orchestrator.ts +279 -12
  555. package/src/self-implement/pipeline-shape.ts +1 -1
  556. package/src/self-implement/route-consumer-test-index.ts +122 -0
  557. package/src/self-implement/run-status-mapping.ts +5 -3
  558. package/src/self-implement/seams.ts +72 -11
  559. package/src/self-implement/test-seams.ts +1 -0
  560. package/src/self-implement/uncommitted-work.ts +59 -0
  561. package/src/session/chat.ts +2 -0
  562. package/src/session/index.ts +45 -13
  563. package/src/shell-env-bootstrap.ts +17 -0
  564. package/src/skills/gift-install.ts +215 -0
  565. package/src/skills/tools/web-search.ts +4 -1
  566. package/src/steward/directive.ts +51 -2
  567. package/src/steward/launch.ts +266 -0
  568. package/src/steward/outcome-notify.ts +115 -0
  569. package/src/steward/steward-cards.ts +25 -11
  570. package/src/steward/triage-plan.ts +32 -4
  571. package/src/steward/triage.ts +129 -13
  572. package/src/steward/working-backwards.ts +64 -0
  573. package/src/storyboard/storyboard.ts +141 -0
  574. package/src/task-cards/card-cli.ts +25 -1
  575. package/src/task-cards/card-store.ts +2 -2
  576. package/src/task-orchestrator/mission.ts +5 -0
  577. package/src/task-orchestrator/store.ts +12 -3
  578. package/src/task-orchestrator/surfaces/pod-command-job.ts +29 -7
  579. package/src/task-orchestrator/surfaces/pod-lease.ts +397 -0
  580. package/src/task-orchestrator/surfaces/pod-pool.ts +127 -26
  581. package/src/task-orchestrator/surfaces/self-implement-pod.ts +214 -40
  582. package/src/task-orchestrator/surfaces/self-implement.ts +15 -2
  583. package/src/task-orchestrator/types.ts +2 -0
  584. package/src/telegram-card-followup.ts +153 -0
  585. package/src/telegram-commands.ts +10 -1
  586. package/src/telegram-project-command.ts +71 -0
  587. package/src/telegram-report.ts +29 -19
  588. package/src/telegram-run.ts +7 -0
  589. package/src/telegram.ts +58 -11
  590. package/src/tool-runtime/self-cognition-runtimes.ts +13 -3
  591. package/src/tool-runtime/types.ts +4 -0
  592. package/src/tui-client/dashboard-session.ts +2 -0
  593. package/src/user-config.ts +216 -27
  594. package/src/version/packed-revision.json +1 -1
  595. package/src/web-push/notify-exec-request.ts +32 -0
  596. package/src/web-search/grok.ts +7 -1
  597. package/src/web-search/index.ts +28 -1
  598. package/src/web-search/provider.ts +4 -0
  599. package/src/web-search/x-intent.ts +5 -0
  600. package/src/workflow-runtime/executor.ts +277 -47
  601. package/src/workflow-runtime/index.ts +1 -0
  602. package/src/workflow-runtime/nodes/subworkflow.ts +60 -0
  603. package/src/workflow-runtime/plugin-kind-node.ts +1 -1
  604. package/src/workflow-runtime/schema.ts +79 -4
  605. package/src/workflow-runtime/types.ts +35 -4
  606. package/apps/pwa/out/_next/static/bFI5EEwg7T6aG88ibX4Zq/_buildManifest.js +0 -1
  607. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +0 -1
  608. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  609. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +0 -1
  610. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +0 -1
  611. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +0 -1
  612. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +0 -1
  613. package/apps/pwa/out/_next/static/chunks/2375-cb6debea56cce4fb.js +0 -1
  614. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  615. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +0 -1
  616. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +0 -25
  617. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +0 -5
  618. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  619. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +0 -1
  620. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +0 -1
  621. package/apps/pwa/out/_next/static/chunks/5080-1613752891fcadb4.js +0 -10
  622. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +0 -1
  623. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +0 -1
  624. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
  625. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
  626. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +0 -1
  627. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  628. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +0 -1
  629. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +0 -1
  630. package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
  631. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +0 -1
  632. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +0 -1
  633. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +0 -1
  634. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
  635. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +0 -1
  636. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
  637. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +0 -1
  638. package/apps/pwa/out/_next/static/chunks/8630-cdfc61e82c653ffc.js +0 -1
  639. package/apps/pwa/out/_next/static/chunks/8894-e263b332208e3957.js +0 -1
  640. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +0 -1
  641. package/apps/pwa/out/_next/static/chunks/9454-139040d37c79dbc9.js +0 -1
  642. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +0 -1
  643. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
  644. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +0 -1
  645. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +0 -1
  646. package/apps/pwa/out/_next/static/chunks/app/approvals/page-fb7c2d458f32fcad.js +0 -1
  647. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-35e2713597dd24d1.js +0 -1
  648. package/apps/pwa/out/_next/static/chunks/app/board/page-918b656f064577ba.js +0 -1
  649. package/apps/pwa/out/_next/static/chunks/app/bots/page-5f30e00693527ebd.js +0 -1
  650. package/apps/pwa/out/_next/static/chunks/app/chat/page-7fbae173b3c103a7.js +0 -1
  651. package/apps/pwa/out/_next/static/chunks/app/consult/page-3b4edcce364062ea.js +0 -1
  652. package/apps/pwa/out/_next/static/chunks/app/control/page-c5794f484c251f44.js +0 -1
  653. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1130d8eefcd87e76.js +0 -1
  654. package/apps/pwa/out/_next/static/chunks/app/editor/page-973996d584f008c1.js +0 -1
  655. package/apps/pwa/out/_next/static/chunks/app/exec/page-016e2bda04eecb1f.js +0 -1
  656. package/apps/pwa/out/_next/static/chunks/app/field/page-b4d6d9a3b56f2940.js +0 -1
  657. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +0 -1
  658. package/apps/pwa/out/_next/static/chunks/app/intake/page-ec49a466121233ba.js +0 -1
  659. package/apps/pwa/out/_next/static/chunks/app/layout-f72db356744ceade.js +0 -1
  660. package/apps/pwa/out/_next/static/chunks/app/live/page-a6fd9b50d1b85378.js +0 -1
  661. package/apps/pwa/out/_next/static/chunks/app/market/page-8b0402a46a749a3d.js +0 -1
  662. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-037f83e2a1edc8e2.js +0 -1
  663. package/apps/pwa/out/_next/static/chunks/app/not-found-46fedff57d4bec67.js +0 -1
  664. package/apps/pwa/out/_next/static/chunks/app/observatory/page-0cf318e9dcee2bbf.js +0 -1
  665. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-899b327cb8b6845c.js +0 -1
  666. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-6c1e6ba743a29dfe.js +0 -1
  667. package/apps/pwa/out/_next/static/chunks/app/page-1a8e4807292f6b17.js +0 -1
  668. package/apps/pwa/out/_next/static/chunks/app/reflection/page-28deba6bcb834916.js +0 -1
  669. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-de68e2205794037d.js +0 -1
  670. package/apps/pwa/out/_next/static/chunks/app/sessions/page-c71c33a573230672.js +0 -1
  671. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-0df30e0c7e50148a.js +0 -1
  672. package/apps/pwa/out/_next/static/chunks/app/settings/page-4fc56790fe0d2682.js +0 -1
  673. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-59df2aebb38a7c1e.js +0 -1
  674. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +0 -1
  675. package/apps/pwa/out/_next/static/chunks/app/setup/page-4647be5dbcbd0795.js +0 -1
  676. package/apps/pwa/out/_next/static/chunks/app/share/page-e025892bda74b743.js +0 -1
  677. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a0955de73b89f078.js +0 -1
  678. package/apps/pwa/out/_next/static/chunks/app/tasks/page-369846d2921af584.js +0 -1
  679. package/apps/pwa/out/_next/static/chunks/app/term/page-72cbc1c8cdd0f65d.js +0 -1
  680. package/apps/pwa/out/_next/static/chunks/app/trace/page-77b28bfb0996d5ea.js +0 -1
  681. package/apps/pwa/out/_next/static/chunks/app/vault/page-3220c4934f49c6c2.js +0 -1
  682. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-90670589b87abea8.js +0 -1
  683. package/apps/pwa/out/_next/static/chunks/app/workflows/page-7df9243ed021a4cc.js +0 -1
  684. package/apps/pwa/out/_next/static/chunks/app/workspace/page-4498c3853f4f688d.js +0 -1
  685. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-35bedde6d885f08f.js +0 -1
  686. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +0 -1
  687. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +0 -1
  688. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
  689. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +0 -1
  690. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +0 -1
  691. package/apps/pwa/out/_next/static/chunks/webpack-e5bea36f183b25e6.js +0 -1
  692. package/apps/pwa/out/_next/static/css/f92f63a5df0ba229.css +0 -1
  693. package/src/dashboard/turn-typeahead-echo.ts +0 -23
  694. /package/apps/pwa/out/_next/static/{bFI5EEwg7T6aG88ibX4Zq → Y4zKzL62sakG5momz3SoQ}/_ssgManifest.js +0 -0
@@ -61,6 +61,9 @@ import { executionFooter } from './telegram-exec-footer.js';
61
61
  import { debug } from './debug/log.js';
62
62
  import { makeChunkProducer } from './session/streaming/chunk-producer.js';
63
63
  import { getUserConfig } from './user-config.js';
64
+ import { detectCard, runCardFollowup, NotACardError } from './card-followup/core.js';
65
+ import { localOcrText } from './telegram-card-followup.js';
66
+ import type { runGraph } from './graph-runner/runner.js';
64
67
 
65
68
  const SLASH_FOCUS_TURNS_DEFAULT = 8;
66
69
 
@@ -135,6 +138,7 @@ export interface DiscordSelfMessageDeps {
135
138
  * (legacy). */
136
139
  questionChannelFor?: (channelId: string) => import('./hitl/question.js').QuestionChannel;
137
140
  log?: (msg: string) => void;
141
+ cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
138
142
  }
139
143
 
140
144
  /** Compose the full discord self+interweave onMessage handler. */
@@ -352,6 +356,34 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
352
356
  // Voice text commands pass the guild gate for the voice adapter's
353
357
  // sake — never route them into the LLM turn (legacy behavior).
354
358
  if (/^\/voice-(join|leave|status)\b/.test(ctx.text.trim())) return undefined;
359
+ if (!ctx.text.trim() && ctx.attachments.length === 1 && ctx.attachments[0]!.contentType?.startsWith('image/')) {
360
+ const photo = ctx.attachments[0]!;
361
+ if (detectCard({ width: photo.width, height: photo.height, ocrText: null }).decision !== 'skip') {
362
+ const bot = deps.getBot();
363
+ if (bot) {
364
+ let imagePath: string;
365
+ let ocrText: string | null;
366
+ try {
367
+ imagePath = (await bot.downloadAttachment(photo)).localPath;
368
+ ocrText = await (deps.cardFollowupDeps?.ocrText ?? localOcrText)(imagePath);
369
+ } catch {
370
+ return '명함을 읽지 못했습니다';
371
+ }
372
+ if (detectCard({ width: photo.width, height: photo.height, ocrText }).decision === 'card') {
373
+ try {
374
+ const result = await runCardFollowup({ imagePath, ocrText,
375
+ ...(deps.cardFollowupDeps?.runGraph ? { runGraph: deps.cardFollowupDeps.runGraph } : {}),
376
+ ...(deps.cardFollowupDeps?.rootDir ? { rootDir: deps.cardFollowupDeps.rootDir() } : {}),
377
+ });
378
+ for (const reply of result.replies) await bot.sendMessage(ctx.channelId, reply);
379
+ return streamer ? '명함 정리를 완료했습니다.' : undefined;
380
+ } catch (error) {
381
+ if (!(error instanceof NotACardError)) return '명함을 읽지 못했습니다';
382
+ }
383
+ }
384
+ }
385
+ }
386
+ }
355
387
  // S1 session commands (!sessions·!new·!attach) — before delegation
356
388
  // and self-turn routing, mirroring telegram's command precedence.
357
389
  const sessionReply = handleSessionCommand(ctx);
@@ -21,6 +21,7 @@ import { makeCommandRest } from './discord/slash-registry.js';
21
21
  import { normalizeInteractionPayload } from './discord/slash-router.js';
22
22
  import type { SlashCommandSchema } from './discord/slash-types.js';
23
23
  import { debug } from './debug/log.js';
24
+ import { gateDiscordSchemas, readBotAudience } from './maturity/bot-command-maturity.js';
24
25
  import { resolveChannelBotToken } from './channel-bot-token.js';
25
26
 
26
27
  const REST_BASE = 'https://discord.com/api/v10';
@@ -121,8 +122,11 @@ export function buildDiscordSlashWire(deps: DiscordSlashWireDeps): DiscordSlashW
121
122
  if (!app.id) { log('[slash] application id unresolved — skip'); return; }
122
123
  const guilds = await (await fetchImpl(`${REST_BASE}/users/@me/guilds`, auth)).json() as Array<{ id: string }>;
123
124
  const rest = makeCommandRest({ token, ...(deps.__fetchImpl ? { fetchImpl: deps.__fetchImpl } : {}) });
125
+ const audience = readBotAudience((deps.userConfig.raw?.discord as { commandAudience?: unknown } | undefined)?.commandAudience);
126
+ const { schemas, hidden } = gateDiscordSchemas(ELANOUS_SLASH_COMMANDS, audience);
127
+ debug.log('discord.command', 'menu-filtered', { total: ELANOUS_SLASH_COMMANDS.length, shown: schemas.length - hidden, ...audience });
124
128
  for (const g of Array.isArray(guilds) ? guilds : []) {
125
- const registered = await rest.bulkOverwriteGuild(app.id, g.id, [...ELANOUS_SLASH_COMMANDS]);
129
+ const registered = await rest.bulkOverwriteGuild(app.id, g.id, schemas);
126
130
  log(`[slash] registered ${registered.length} commands in guild ${g.id}`);
127
131
  }
128
132
  }
package/src/discord.ts CHANGED
@@ -23,6 +23,7 @@
23
23
  import { markdownToTelegramHtml as _unusedMarkdownFn } from './telegram-format.js'; // eslint-disable-line @typescript-eslint/no-unused-vars
24
24
  import { debug } from './debug/log.js';
25
25
  import { getUserConfig } from './user-config.js';
26
+ import { deliverSeatAnswers } from './seat-dispatch/seat-ask.js';
26
27
  import { handleDiscordSeatWork, type DiscordSeatWorkDeps } from './intake-plane/discord-seat-work.js';
27
28
  import { parseSeatAddress } from './seat-address/seat-address.js';
28
29
 
@@ -226,6 +227,7 @@ export class DiscordBot {
226
227
  private ws: WebSocket | null = null;
227
228
  private running = false;
228
229
  private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
230
+ private seatAskTimer: ReturnType<typeof setInterval> | null = null;
229
231
  private lastHeartbeatAck = true;
230
232
  private lastSeq: number | null = null;
231
233
 
@@ -246,7 +248,15 @@ export class DiscordBot {
246
248
  this.onInteraction = opts.onInteraction ?? null;
247
249
  this.onReaction = opts.onReaction ?? null;
248
250
  this.onTriggerTap = opts.onTriggerTap ?? null;
249
- this.seatWorkDeps = opts.seatWorkDeps ?? {};
251
+ this.seatWorkDeps = { ...opts.seatWorkDeps, askDeps: {
252
+ ...opts.seatWorkDeps?.askDeps,
253
+ channel: 'discord',
254
+ send: opts.seatWorkDeps?.askDeps?.send ?? (async (origin, text) => {
255
+ if (origin.channel !== 'discord') throw new Error('seat ask belongs to another surface');
256
+ const posted = await this.sendMessage(origin.threadId ?? origin.channelId, text);
257
+ if (!posted) throw new Error('Discord seat ask reply was not posted');
258
+ }),
259
+ } };
250
260
  }
251
261
 
252
262
  /** Send a raw payload on the gateway WebSocket. Voice adapter uses
@@ -523,8 +533,12 @@ export class DiscordBot {
523
533
  async start(): Promise<void> {
524
534
  this.running = true;
525
535
  this.log(`discord bot starting (allowlist size ${this.allowedUsers.size})`);
536
+ this.seatAskTimer = setInterval(() => {
537
+ void deliverSeatAnswers(this.seatWorkDeps.askDeps!).catch((error) => this.log(`seat ask delivery failed: ${String(error)}`));
538
+ }, 10_000);
526
539
  while (this.running) {
527
540
  try {
541
+ await deliverSeatAnswers(this.seatWorkDeps.askDeps!);
528
542
  await this.runConnection();
529
543
  } catch (err: any) {
530
544
  this.log(`gateway error: ${err?.message ?? String(err)}`);
@@ -538,6 +552,8 @@ export class DiscordBot {
538
552
 
539
553
  stop(): void {
540
554
  this.running = false;
555
+ if (this.seatAskTimer) clearInterval(this.seatAskTimer);
556
+ this.seatAskTimer = null;
541
557
  if (this.ws) {
542
558
  try { this.ws.close(1000, 'client stop'); } catch { /* ignore */ }
543
559
  this.ws = null;
@@ -837,13 +853,14 @@ export class DiscordBot {
837
853
  }
838
854
 
839
855
  // Addressed work is submitted only by configured owners; ordinary chat keeps its existing path.
840
- if (this.allowedUsers.has(author.id) && parseSeatAddress(ctx.text)) {
856
+ if (this.allowedUsers.has(author.id) && (parseSeatAddress(ctx.text) || (ctx.isDm && ctx.text && !ctx.text.startsWith('/')))) {
841
857
  const threadId = typeof m.thread_id === 'string' && m.thread_id ? m.thread_id : undefined;
842
858
  const replyChannelId = threadId ?? ctx.channelId;
843
859
  try {
844
860
  const seatReply = await handleDiscordSeatWork(ctx.text, {
845
861
  channelId: replyChannelId,
846
862
  messageId: ctx.messageId,
863
+ userId: ctx.userId, isDm: ctx.isDm,
847
864
  ...(threadId ? { threadId } : {}),
848
865
  }, this.seatWorkDeps);
849
866
  if (seatReply !== null) {
@@ -0,0 +1,62 @@
1
+ import type { LLMToolSpec } from '../llm.js';
2
+ import { debug } from '../debug/log.js';
3
+ import { getUserConfig } from '../user-config.js';
4
+ import { getSecretAsync } from '../nexus/config/secrets/index.js';
5
+ import { fetchLinearProjectIssues, type LinearProjectIssue } from '../connectors/linear.js';
6
+ import { formatElanousCard } from './elanous-card.js';
7
+
8
+ export const COO_ADMIN_SPEC: LLMToolSpec = {
9
+ name: 'coo_admin',
10
+ description: '읽기 전용 Linear COO 행정 조회 — «행정 뭐 남았어 · COO 할 일 · 마감 다가오는 행정» 질문에 사용. 외부 행정 프로젝트의 열린 일을 마감 순으로 읽는다.',
11
+ parameters: { type: 'object', properties: {}, required: [] },
12
+ };
13
+
14
+ export interface CooAdminDeps {
15
+ getSecret?: (id: string) => Promise<string | undefined>;
16
+ fetch?: typeof fetch;
17
+ project?: string;
18
+ now?: Date;
19
+ }
20
+
21
+ /** Reads the configured Linear project without changing any Linear issue. */
22
+ export async function dispatchCooAdmin(_args: Record<string, unknown>, deps: CooAdminDeps = {}): Promise<string> {
23
+ let count = 0;
24
+ let overdue = 0;
25
+ try {
26
+ const apiKey = await (deps.getSecret ?? getSecretAsync)('connector.linear.apiKey');
27
+ if (!apiKey) {
28
+ debug.log('coo.admin', 'failed', { count, overdue, reason: 'missing-key' });
29
+ return 'Linear 키가 없습니다 — `elanous connector linear set-key`';
30
+ }
31
+ const project = deps.project ?? getUserConfig().coo?.linearProject ?? '외부 행정·큰 일 (COO)';
32
+ const issues = await fetchLinearProjectIssues({ apiKey, project, fetch: deps.fetch ?? fetch });
33
+ const today = deps.now ?? new Date();
34
+ const todayUtc = Date.UTC(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
35
+ const daysUntil = (date: string) => Math.round((Date.parse(`${date.slice(0, 10)}T00:00:00Z`) - todayUtc) / 86_400_000);
36
+ issues.sort((a, b) => {
37
+ const deadlineOrder = a.dueDate == null ? (b.dueDate == null ? 0 : 1)
38
+ : b.dueDate == null ? -1 : daysUntil(a.dueDate) - daysUntil(b.dueDate);
39
+ return deadlineOrder || (a.priority === 0 ? 5 : a.priority) - (b.priority === 0 ? 5 : b.priority);
40
+ });
41
+ count = issues.length;
42
+ const lines = issues.map((item: LinearProjectIssue) => {
43
+ const days = item.dueDate ? daysUntil(item.dueDate) : null;
44
+ if (days !== null && days < 0) overdue++;
45
+ const deadline = days === null ? '마감 없음' : days < 0 ? `지남 ${-days}일` : `D-${days}`;
46
+ return `${deadline} · ${item.title} · ${item.state.name} · ${item.assignee?.name ?? '미배정'}\n${item.url}`;
47
+ });
48
+ debug.log('coo.admin', 'read', { count, overdue, reason: 'ok' });
49
+ const scope = issues.truncated ? '최대 250건 중 조회한 항목만 마감 순으로 표시 (이후 항목은 포함되지 않음)\n' : '';
50
+ const prose = lines.length ? `${scope}${lines.join('\n')}` : '남은 행정 0건';
51
+ const card = formatElanousCard({ kind: 'coo-admin', items: issues.map(item => ({
52
+ title: item.title, due: item.dueDate?.slice(0, 10) ?? null,
53
+ daysLeft: item.dueDate ? daysUntil(item.dueDate) : null,
54
+ state: item.state.name, owner: item.assignee?.name ?? '미배정', url: item.url,
55
+ })) });
56
+ return `${prose}\n${card}`;
57
+ } catch (error) {
58
+ const reason = error instanceof Error ? error.message : String(error);
59
+ debug.log('coo.admin', 'failed', { count, overdue, reason: 'linear-read-failed' });
60
+ return `못 읽었습니다(${reason})`;
61
+ }
62
+ }
@@ -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';
@@ -28,6 +28,9 @@ import { OPS_STATUS_SPEC, dispatchOpsStatus } from './ops-status-tool.js';
28
28
  import { SE_BUILD_SPEC, dispatchSeBuild } from './se-build-tool.js';
29
29
  import { LOGS_QUERY_SPEC, dispatchLogsQuery } from './logs-tool.js';
30
30
  import { MISSION_DECIDE_SPEC, dispatchMissionDecide } from '../autopilot/mission-decide-tool.js';
31
+ import { COO_ADMIN_SPEC, dispatchCooAdmin } from './coo-admin-tool.js';
32
+ import { RELEASE_STATUS_SPEC, RELEASE_CHANGE_SPEC, dispatchReleaseStatus, dispatchReleaseChange } from './release-tool.js';
33
+ import { OPS_SEATS_SPEC, DECISIONS_PENDING_SPEC, dispatchOpsSeats, dispatchDecisionsPending } from './ops-facts-tool.js';
31
34
 
32
35
  /** memory_recall — 크로스서피스 기억(자기 발송 원장 회상). 도메인 무관(domain 필터는 옵션).
33
36
  * finance-tools 에서 L2 로 이관(2026-07-08). */
@@ -111,6 +114,7 @@ const CORE_TOOL_HANDLERS: Record<string, (args: Record<string, unknown>, onParti
111
114
  schedule_manage: dispatchScheduleManage,
112
115
  session_manage: (args) => dispatchSessionQuery(args), // 대화 세션 검색·열람·목록·삭제(내용/ID/텔레그램)
113
116
  memory_recall: dispatchMemoryRecall,
117
+ context_now: async (args) => (await import('../context-bus/context-now.js')).contextNow({ ...(typeof args.topic === 'string' ? { topic: args.topic } : {}) }),
114
118
  fact_check: dispatchFactCheck, // 팩트체크 캐스케이드(내부 발송원장→외부 X/레딧/웹)·도메인 무관 코어
115
119
  self_recall: dispatchSelfRecall, // self-awareness(elanous 구현 이력)·도메인 무관 코어
116
120
  autopilot_missions: dispatchAutopilotMissions, // 오토파일럿 계보 조회(AL3)·도메인 무관 코어
@@ -118,6 +122,8 @@ const CORE_TOOL_HANDLERS: Record<string, (args: Record<string, unknown>, onParti
118
122
  se_build: dispatchSeBuild, // SE 격리 빌드 관측(빌드 안 뭐 하나·로그 tail·worktree diff)·코어
119
123
  logs_query: dispatchLogsQuery, // 크로스서피스 디버그 로그 조회(logs.db·LF3)·READ-ONLY 코어
120
124
  mission_decide: dispatchMissionDecide, // 미션 결정 기록(re-ground·defer·boundary…)·3박자 주입·코어
125
+ coo_admin: (args) => dispatchCooAdmin(args),
126
+ release_status: async (args) => dispatchReleaseStatus(args),
121
127
  };
122
128
 
123
129
  /** Worker-side search entry: dispatch must not re-enter its own budget wrapper. */
@@ -136,7 +142,7 @@ const SELF_RECALL_BUDGET_SPEC: LLMToolSpec = {
136
142
  },
137
143
  },
138
144
  };
139
- 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];
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];
140
146
 
141
147
  export interface CoreTools {
142
148
  specs: LLMToolSpec[];
@@ -153,11 +159,14 @@ export function buildCoreTools(recallSearch?: RecallSearch): CoreTools {
153
159
  names,
154
160
  dispatch: async (name, args, onPartial, context) => {
155
161
  const h = CORE_TOOL_HANDLERS[name];
156
- if (!h) return { error: `unknown core tool: ${name}` };
162
+ if (!h && name !== 'release_change' && name !== 'ops_seats' && name !== 'decisions_pending') return { error: `unknown core tool: ${name}` };
163
+ if (name === 'release_change') return dispatchReleaseChange(args, context);
164
+ if (name === 'ops_seats') return dispatchOpsSeats(args, context);
165
+ if (name === 'decisions_pending') return dispatchDecisionsPending(context);
157
166
  if (name === 'memory_recall' || name === 'self_recall') {
158
167
  return runRecall(name, args, context ?? { surface: 'skill' }, recallSearch, onPartial);
159
168
  }
160
- return h(args, onPartial);
169
+ return h!(args, onPartial);
161
170
  },
162
171
  };
163
172
  }
@@ -0,0 +1,29 @@
1
+ export interface ElanousCard {
2
+ kind: 'coo-admin' | 'release-schedule' | 'release-checklist';
3
+ items: Array<{ title: string; due: string | null; daysLeft: number | null; state: string; owner: string; url?: string }>;
4
+ meta?: Record<string, unknown>;
5
+ }
6
+
7
+ export function formatElanousCard(card: ElanousCard): string {
8
+ return `\`\`\`elanous-card\n${JSON.stringify(card)}\n\`\`\``;
9
+ }
10
+
11
+ /** Reads every valid card block in a tool response; ignores unrelated prose and malformed blocks. */
12
+ export function parseElanousCard(text: string): ElanousCard[] {
13
+ const cards: ElanousCard[] = [];
14
+ for (const match of text.matchAll(/```elanous-card\r?\n([^\r\n]+)\r?\n```/g)) {
15
+ try {
16
+ const value: unknown = JSON.parse(match[1]!);
17
+ if (!value || typeof value !== 'object') continue;
18
+ const card = value as Partial<ElanousCard>;
19
+ if (!['coo-admin', 'release-schedule', 'release-checklist'].includes(card.kind ?? '') || !Array.isArray(card.items)) continue;
20
+ if (!card.items.every(item => item && typeof item.title === 'string' &&
21
+ (item.due === null || /^\d{4}-\d{2}-\d{2}$/.test(item.due)) &&
22
+ (item.daysLeft === null || typeof item.daysLeft === 'number') &&
23
+ typeof item.state === 'string' && typeof item.owner === 'string' &&
24
+ (item.url === undefined || typeof item.url === 'string'))) continue;
25
+ cards.push(card as ElanousCard);
26
+ } catch { /* Other fenced content is not a card. */ }
27
+ }
28
+ return cards;
29
+ }
@@ -0,0 +1,76 @@
1
+ import type { LLMToolSpec } from '../llm.js';
2
+ import type { ToolRuntimeContext } from '../tool-runtime/types.js';
3
+ import { debug } from '../debug/log.js';
4
+ import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
5
+ import { buildSeatsBoard, todayKst, type SeatsSources } from '../nexus/api/ops-seats.js';
6
+ import { liveSeatsSources } from '../nexus/api/ops-seats-sources.js';
7
+
8
+ export const OPS_SEATS_SPEC: LLMToolSpec = {
9
+ name: 'ops_seats',
10
+ description: '오너 전용 읽기 — 지금 자리(OP·TC·MK·UX)별 진행(now), 오늘 착지(landed), 빨강(red), 미결 결정 수를 원장에서 확인한다. null은 못 읽은 원천이며 0이나 빈 목록이 아니다.',
11
+ parameters: { type: 'object', properties: { date: { type: 'string', description: '조회할 KST 날짜 YYYY-MM-DD. 생략하면 KST 오늘.' } }, required: [] },
12
+ };
13
+
14
+ export const DECISIONS_PENDING_SPEC: LLMToolSpec = {
15
+ name: 'decisions_pending',
16
+ description: '오너 전용 읽기 — 결정 원장의 열린 결정을 기한 순으로 조회한다. 결정 선택·기록은 하지 않는다.',
17
+ parameters: { type: 'object', properties: {}, required: [] },
18
+ };
19
+
20
+ export interface OpsFactsDeps {
21
+ seatsSources?: () => SeatsSources;
22
+ listDecisions?: (filters: { status: 'open' }) => DecisionEntry[];
23
+ today?: () => string;
24
+ }
25
+
26
+ function ownerVerified(context?: ToolRuntimeContext): boolean {
27
+ return context?.requestOrigin !== 'external-agent' && !!context?.sessionId && !!context?.verifiedOwner?.id.trim();
28
+ }
29
+
30
+ function logCall(tool: 'ops_seats' | 'decisions_pending', ok: boolean, nulls: number): void {
31
+ debug.log('ops.facts', 'call', { tool, ok, nulls });
32
+ }
33
+
34
+ /** Unreadable seat sources remain null; only rename the board's `blocked` field to `red` for this tool. */
35
+ export async function dispatchOpsSeats(args: Record<string, unknown>, context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
36
+ if (!ownerVerified(context)) {
37
+ logCall('ops_seats', false, 0);
38
+ return { error: '오너 확인이 필요한 조회다' };
39
+ }
40
+ const date = args.date === undefined ? (deps.today ?? todayKst)() : args.date;
41
+ if (typeof date !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(date) ||
42
+ !Number.isFinite(Date.parse(`${date}T00:00:00Z`)) || new Date(`${date}T00:00:00Z`).toISOString().slice(0, 10) !== date) {
43
+ logCall('ops_seats', false, 0);
44
+ return { error: 'KST 날짜는 YYYY-MM-DD 여야 한다' };
45
+ }
46
+ try {
47
+ const board = await buildSeatsBoard(date, (deps.seatsSources ?? liveSeatsSources)());
48
+ const seats = board.seats.map(({ seat, role, now, landed, blocked, pendingDecisions }) =>
49
+ ({ seat, role, now, landed, red: blocked, pendingDecisions }));
50
+ const nulls = seats.reduce((n, row) => n + [row.now, row.landed, row.red, row.pendingDecisions].filter(v => v === null).length, 0);
51
+ logCall('ops_seats', true, nulls);
52
+ return { note: 'null = 못 읽음(없다가 아님); now는 오늘 기록이 없어도 null일 수 있다.', date: board.date, seats };
53
+ } catch {
54
+ logCall('ops_seats', false, 1);
55
+ return { error: '자리 현황을 읽지 못했다', seats: null };
56
+ }
57
+ }
58
+
59
+ /** Only the five requested fields leave the decision ledger. Undated entries follow dated entries. */
60
+ export function dispatchDecisionsPending(context?: ToolRuntimeContext, deps: OpsFactsDeps = {}) {
61
+ if (!ownerVerified(context)) {
62
+ logCall('decisions_pending', false, 0);
63
+ return { error: '오너 확인이 필요한 조회다' };
64
+ }
65
+ try {
66
+ const entries = (deps.listDecisions ?? ((filters) => new DecisionLedger().list(filters)))({ status: 'open' });
67
+ const items = entries.filter(e => e.status === 'open')
68
+ .sort((a, b) => (a.dueAt ?? '\uffff').localeCompare(b.dueAt ?? '\uffff') || a.id.localeCompare(b.id))
69
+ .map(({ id, title, category, dueAt, raisedBy }) => ({ id, title, category, dueAt: dueAt ?? null, raisedBy: { track: raisedBy.track ?? null } }));
70
+ logCall('decisions_pending', true, items.reduce((n, item) => n + Number(item.dueAt === null) + Number(item.raisedBy.track === null), 0));
71
+ return { items };
72
+ } catch {
73
+ logCall('decisions_pending', false, 1);
74
+ return { error: '결정 원장을 읽지 못했다', items: null };
75
+ }
76
+ }
@@ -13,9 +13,9 @@ import { conatusEnv } from './conatus-env.js';
13
13
  import { spillLongContent } from '../storage/content-spill.js';
14
14
  import { openSurfaceEventsDb, recordEvent } from './surface-events.js';
15
15
  import { latestUserIntentTs } from '../user-intent/index.js';
16
- import { getUserConfig } from '../user-config.js';
16
+ import { getUserConfig, type UserConfig } from '../user-config.js';
17
17
  import { resolveChannelBotToken } from '../channel-bot-token.js';
18
- import { kindRouteTarget } from './telegram-kind-route.js';
18
+ import { isOperationalKind, kindRouteTarget, logKindRouteFallback, mainHomeTarget } from './telegram-kind-route.js';
19
19
  import { debug } from '../debug/log.js';
20
20
  import { resolveDaemonEndpoint } from '../nexus/daemon-endpoint.js';
21
21
  import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
@@ -210,9 +210,14 @@ export function flushDeferred(path = DEFERRED_PATH): number {
210
210
  ].join('\n');
211
211
  observeFlush(items.length, lagMin, [...new Set(items.map(i => i.kind))]);
212
212
  let delivered = 0;
213
- // origin 없는 것 = report 묶음(기존 동작·finance/크론 알림).
213
+ // origin 없는 매매 알림만 report 묶음. 그 외는 kind 별로 운영 봇 경계를 유지한다.
214
214
  const noOrigin = items.filter(i => !i.origin);
215
- if (noOrigin.length && deliver(fmt(noOrigin), 'report')) delivered += noOrigin.length;
215
+ const tradingBatch = noOrigin.filter(i => !isOperationalKind(i.kind));
216
+ if (tradingBatch.length && deliver(fmt(tradingBatch), 'report')) delivered += tradingBatch.length;
217
+ for (const kind of new Set(noOrigin.filter(i => isOperationalKind(i.kind)).map(i => i.kind))) {
218
+ const batch = noOrigin.filter(i => i.kind === kind);
219
+ if (deliver(fmt(batch), kind)) delivered += batch.length;
220
+ }
216
221
  // origin 있는 것 = 발신 채널·스레드별로 묶어 서로 다른 수신자에게 섞이지 않게 발송.
217
222
  const groups = new Map<string, { origin: MissionOrigin; list: Item[] }>();
218
223
  for (const it of items) {
@@ -402,20 +407,48 @@ function sendTelegramRaw(token: string, chatId: string | number, text: string, t
402
407
  return ok;
403
408
  }
404
409
 
405
- /** 텔레그램 직접 발송(TELEGRAM_BOT_TOKEN/CHAT_ID — env 우선, 없으면 CONATUS/.env). report 폴백. */
406
- function sendTelegramDirect(text: string, kind?: string): boolean {
407
- // A purpose kind with a channel role (intake · ops-*) goes to that channel even when the daemon is down.
408
- try {
409
- const target = kindRouteTarget(getUserConfig(), kind);
410
- if (target) return sendTelegramRaw(target.botToken, String(target.chatId), text);
411
- } catch { /* fall through to the legacy direct path */ }
410
+ /** 텔레그램 직접 발송. 운영 kind 는 메인 봇/홈 밖의 env 로 내려가지 않는다. */
411
+ export function sendTelegramDirect(
412
+ text: string, kind?: string,
413
+ deps: { config?: UserConfig; sendRaw?: typeof sendTelegramRaw; legacyEnv?: typeof conatusEnv } = {},
414
+ ): boolean {
415
+ const sendRaw = deps.sendRaw ?? sendTelegramRaw;
416
+ let cfg: UserConfig | undefined;
417
+ try { cfg = deps.config ?? getUserConfig(); } catch { /* unavailable config: operational delivery still fails closed */ }
418
+ if (cfg) {
419
+ try {
420
+ const target = kindRouteTarget(cfg, kind);
421
+ if (target) return sendRaw(target.botToken, String(target.chatId), text);
422
+ } catch { /* unavailable channel routing: fall through */ }
423
+ // A declared table is authoritative: missing roles cannot escape to an env bot.
424
+ if (cfg.telegram.channels?.length) {
425
+ logKindRouteFallback(kind, 'none', false);
426
+ return false;
427
+ }
428
+ }
429
+ if (isOperationalKind(kind)) {
430
+ let home: ReturnType<typeof mainHomeTarget> = null;
431
+ let mainToken = '';
432
+ try {
433
+ if (cfg) {
434
+ mainToken = resolveChannelBotToken('telegram', cfg).token;
435
+ home = mainHomeTarget(cfg);
436
+ }
437
+ } catch { /* unavailable main bot */ }
438
+ const rcToken = cfg?.telegram.reportChannel?.botToken;
439
+ logKindRouteFallback(kind, home ? 'main-home' : 'none', !!mainToken && !!cfg?.telegram.reportChannel && (!rcToken || rcToken === mainToken));
440
+ if (home) return sendRaw(home.botToken, String(home.chatId), text);
441
+ console.error('[outbound] 운영 kind 메인 봇/홈 미설정 — 콘솔 출력\n' + text);
442
+ return false;
443
+ }
412
444
  let tok = process.env.TELEGRAM_BOT_TOKEN || '';
413
445
  let chat = process.env.TELEGRAM_CHAT_ID || '';
414
446
  if (!tok || !chat) {
415
- const env = conatusEnv();
447
+ const env = (deps.legacyEnv ?? conatusEnv)();
416
448
  tok = tok || env.TELEGRAM_BOT_TOKEN || '';
417
449
  chat = chat || env.TELEGRAM_CHAT_ID || '';
418
450
  }
451
+ logKindRouteFallback(kind, tok && chat ? 'conatus-env' : 'none', false);
419
452
  if (!tok || !chat) { console.error('[outbound] 토큰/chat 미설정 — 콘솔 출력\n' + text); return false; }
420
- return sendTelegramRaw(tok, chat, text);
453
+ return sendRaw(tok, chat, text);
421
454
  }
@@ -0,0 +1,168 @@
1
+ import type { LLMToolSpec } from '../llm.js';
2
+ import type { ToolRuntimeContext } from '../tool-runtime/types.js';
3
+ import { debug } from '../debug/log.js';
4
+ import { requestConfirmation, type ConfirmOpts, type ConfirmResult } from '../hitl/confirm.js';
5
+ import { devVersion, listChecklist, summarizeChecklist, type Checklist } from '../release-loop/checklist.js';
6
+ import { add, move, validateVersion } from '../release-loop/feature-store.js';
7
+ import { formatKst, getSchedule, setSchedule, type ReleaseSchedule } from '../release-loop/release-schedule.js';
8
+ import { latestGraphRun, type GraphRunState } from '../graph-runner/runner.js';
9
+ import { formatElanousCard, type ElanousCard } from './elanous-card.js';
10
+
11
+ export const RELEASE_STATUS_SPEC: LLMToolSpec = {
12
+ name: 'release_status',
13
+ description: '판 어디까지 · 컷 언제 · 릴리스 상황 · 판올림 · 체크리스트 · 남은 칸을 읽는다. 판 일정, 빨강 칸, 담당별 노랑, 최근 발행 런을 조회하는 읽기 전용 도구. 행정 업무는 coo_admin.',
14
+ parameters: { type: 'object', properties: { version: { type: 'string', description: '조회할 판. 생략하면 개발 판과 다음 판.' } }, required: [] },
15
+ };
16
+
17
+ export const RELEASE_CHANGE_SPEC: LLMToolSpec = {
18
+ name: 'release_change',
19
+ description: '오너의 판올림 변경 요청 — 체크리스트 칸 추가, 칸을 다른 판으로 이동, 판 컷·착지 마감 수정. 실행 전에 사람에게 한 번 확인받는다. 읽기 요청에는 release_status 사용.',
20
+ parameters: { type: 'object', properties: {
21
+ action: { type: 'string', enum: ['add-item', 'move-item', 'set-schedule'] },
22
+ version: { type: 'string', description: 'add-item 또는 set-schedule 대상 판' },
23
+ id: { type: 'string', description: '칸 id (add-item 또는 move-item)' },
24
+ title: { type: 'string', description: '추가할 칸 제목' },
25
+ owner: { type: 'string', description: '추가할 칸 담당(선택)' },
26
+ from: { type: 'string', description: '이동 전 판' },
27
+ to: { type: 'string', description: '이동 후 판' },
28
+ cutAt: { type: 'string', description: 'ISO 오프셋 포함 컷 시각(선택)' },
29
+ landBy: { type: 'string', description: 'ISO 오프셋 포함 착지 마감(선택)' },
30
+ }, required: ['action'] },
31
+ };
32
+
33
+ export interface ReleaseToolDeps {
34
+ now?: Date;
35
+ devVersion?: () => string;
36
+ checklist?: (version: string) => Checklist;
37
+ schedule?: (version: string) => ReleaseSchedule | null;
38
+ latestRun?: (graphId: string) => GraphRunState | null;
39
+ confirm?: (options: ConfirmOpts) => Promise<ConfirmResult>;
40
+ add?: typeof add;
41
+ move?: typeof move;
42
+ setSchedule?: typeof setSchedule;
43
+ }
44
+
45
+ function nextVersion(version: string): string {
46
+ const [major, minor, patch] = version.split('.').map(Number);
47
+ return `${major}.${minor}.${patch! + 1}`;
48
+ }
49
+
50
+ function kstDate(iso: string): string {
51
+ return new Date(Date.parse(iso) + 9 * 60 * 60_000).toISOString().slice(0, 10);
52
+ }
53
+
54
+ function daysLeft(iso: string, now: Date): number {
55
+ const due = Date.parse(`${kstDate(iso)}T00:00:00Z`);
56
+ const today = Date.parse(`${kstDate(now.toISOString())}T00:00:00Z`);
57
+ return (due - today) / 86_400_000;
58
+ }
59
+
60
+ function dDay(days: number): string {
61
+ return days < 0 ? `D+${-days}` : days === 0 ? 'D-day' : `D-${days}`;
62
+ }
63
+
64
+ export function dispatchReleaseStatus(args: Record<string, unknown>, deps: ReleaseToolDeps = {}): { text: string; structured: Record<string, unknown> } {
65
+ const requested = args.version;
66
+ if (requested !== undefined && (typeof requested !== 'string' || !requested.trim())) throw new Error('판 이름이 필요합니다');
67
+ const version = requested ? null : (deps.devVersion?.() ?? devVersion());
68
+ const versions = requested ? [requested as string] : [version!, nextVersion(version!)];
69
+ const now = deps.now ?? new Date();
70
+ const run = (deps.latestRun ?? latestGraphRun)('release-loop');
71
+ const latest = run ? { status: run.status, lastNode: run.path.at(-1) ?? null, startedAt: run.startedAt ?? null } : null;
72
+ const entries = versions.map(v => {
73
+ validateVersion(v);
74
+ const schedule = (deps.schedule ?? getSchedule)(v);
75
+ const data = (deps.checklist ?? listChecklist)(v);
76
+ const summary = summarizeChecklist(data);
77
+ const red = data.items.filter(item => item.status === 'red').map(({ id, title, owner }) => ({ id, title, owner: owner ?? '미배정' }));
78
+ const yellowByOwner: Record<string, number> = {};
79
+ for (const item of data.items.filter(item => item.status === 'yellow')) {
80
+ const owner = item.owner ?? '미배정';
81
+ yellowByOwner[owner] = (yellowByOwner[owner] ?? 0) + 1;
82
+ }
83
+ const cutDaysLeft = schedule ? daysLeft(schedule.cutAt, now) : null;
84
+ const landDaysLeft = schedule?.landBy ? daysLeft(schedule.landBy, now) : null;
85
+ const cutHoursLeft = schedule ? Math.ceil((Date.parse(schedule.cutAt) - now.getTime()) / 3_600_000) : null;
86
+ const landHoursLeft = schedule?.landBy ? Math.ceil((Date.parse(schedule.landBy) - now.getTime()) / 3_600_000) : null;
87
+ return { version: v, schedule: schedule ? { cutAt: schedule.cutAt, landBy: schedule.landBy, cutDaysLeft, landDaysLeft, cutHoursLeft, landHoursLeft } : null,
88
+ counts: { green: summary.green, yellow: summary.yellow, red: summary.red, done: summary.done }, red, yellowByOwner };
89
+ });
90
+ const lines = entries.map(entry => {
91
+ const schedule = entry.schedule;
92
+ return `${entry.version} · 컷 ${schedule ? formatKst(schedule.cutAt) : '미정'}${schedule?.landBy ? ` · 착지 마감 ${formatKst(schedule.landBy)}` : ' · 착지 마감 미정'}${schedule ? ` · 컷 ${dDay(schedule.cutDaysLeft!)} (${schedule.cutHoursLeft}시간)${schedule.landDaysLeft !== null ? ` · 착지 ${dDay(schedule.landDaysLeft)} (${schedule.landHoursLeft}시간)` : ''}` : ''}\n` +
93
+ `초록 ${entry.counts.green} · 노랑 ${entry.counts.yellow} · 빨강 ${entry.counts.red} · 끝 ${entry.counts.done}\n` +
94
+ `빨강 칸: ${entry.red.length ? entry.red.map(item => `${item.id} ${item.title} (${item.owner})`).join(', ') : '없음'}\n` +
95
+ `담당별 노랑: ${Object.keys(entry.yellowByOwner).length ? Object.entries(entry.yellowByOwner).map(([owner, count]) => `${owner} ${count}`).join(', ') : '없음'}`;
96
+ });
97
+ lines.push(latest ? `최근 발행 런: ${latest.status} · 끝 노드 ${latest.lastNode ?? '없음'} · 시작 ${latest.startedAt ?? '미상'}` : '최근 발행 런: 없음');
98
+ const cards: ElanousCard[] = [
99
+ { kind: 'release-schedule', items: entries.map(entry => ({ title: `${entry.version} 컷`, due: entry.schedule ? kstDate(entry.schedule.cutAt) : null, daysLeft: entry.schedule?.cutDaysLeft ?? null, state: entry.schedule ? 'scheduled' : 'undecided', owner: 'release' })), meta: { versions: entries.map(entry => entry.version) } },
100
+ { kind: 'release-checklist', items: entries.flatMap(entry => entry.red.map(item => ({ title: `${item.id} ${item.title}`, due: null, daysLeft: null, state: 'red', owner: item.owner }))), meta: { counts: entries.map(entry => ({ version: entry.version, ...entry.counts })) } },
101
+ ];
102
+ const structured = { releases: entries, latestRun: latest };
103
+ debug.log('release.tool', 'status', { action: 'status', version: versions.join(',') });
104
+ return { text: `${lines.join('\n\n')}\n${cards.map(formatElanousCard).join('\n')}`, structured };
105
+ }
106
+
107
+ function requiredString(args: Record<string, unknown>, key: string): string {
108
+ const value = args[key];
109
+ if (typeof value !== 'string' || !value.trim()) throw new Error(`${key} 값이 필요합니다`);
110
+ return value.trim();
111
+ }
112
+
113
+ /** Never accepts a user-supplied owner claim: only a human, server-assigned owner context can authorize a mutation. */
114
+ export async function dispatchReleaseChange(args: Record<string, unknown>, context?: ToolRuntimeContext, deps: ReleaseToolDeps = {}): Promise<string> {
115
+ const action = typeof args.action === 'string' ? args.action : '';
116
+ const version = typeof args.version === 'string' ? args.version : typeof args.from === 'string' ? args.from : '';
117
+ const ownerId = context?.verifiedOwner?.id;
118
+ if (context?.requestOrigin === 'external-agent' || !context?.sessionId || !ownerId?.trim()) {
119
+ debug.log('release.tool', 'change-refused', { action, version });
120
+ return '서버에서 검증한 오너의 사람 채팅 문맥에서만 판올림을 바꿀 수 있습니다.';
121
+ }
122
+ const channels = context.confirmChannels;
123
+ if (!channels?.length) {
124
+ debug.log('release.tool', 'change-refused', { action, version });
125
+ return '원래 채팅의 확인 채널이 없어 판올림을 바꾸지 않았습니다.';
126
+ }
127
+ let prompt: string;
128
+ let apply: () => void;
129
+ if (action === 'add-item') {
130
+ const v = requiredString(args, 'version');
131
+ const id = requiredString(args, 'id');
132
+ const title = requiredString(args, 'title');
133
+ validateVersion(v);
134
+ const owner = args.owner === undefined ? undefined : requiredString(args, 'owner');
135
+ prompt = `${v} 에 칸 ${id} «${title}» 추가 — 진행할까요?`;
136
+ apply = () => { (deps.add ?? add)(v, { id, title, status: 'yellow', ...(owner ? { owner } : {}), updatedAt: new Date().toISOString(), updatedBy: ownerId }); };
137
+ } else if (action === 'move-item') {
138
+ const id = requiredString(args, 'id');
139
+ const from = requiredString(args, 'from');
140
+ const to = requiredString(args, 'to');
141
+ validateVersion(from); validateVersion(to);
142
+ if (from === to) throw new Error('같은 판으로 옮길 수 없습니다');
143
+ prompt = `${id} 칸을 ${from} 에서 ${to} 로 이동 — 진행할까요?`;
144
+ apply = () => { (deps.move ?? move)(id, from, to, ownerId); };
145
+ } else if (action === 'set-schedule') {
146
+ const v = requiredString(args, 'version');
147
+ validateVersion(v);
148
+ const cutAt = args.cutAt === undefined ? undefined : requiredString(args, 'cutAt');
149
+ const landBy = args.landBy === undefined ? undefined : requiredString(args, 'landBy');
150
+ if (!cutAt && !landBy) throw new Error('cutAt 또는 landBy 가 필요합니다');
151
+ // Validate offsets before asking for approval, not after.
152
+ for (const iso of [cutAt, landBy].filter((value): value is string => !!value)) {
153
+ if (!/^\d{4}-\d\d-\d\dT\d\d:\d\d(?::\d\d(?:\.\d{1,3})?)?(?:Z|[+-]\d\d:\d\d)$/.test(iso) || !Number.isFinite(Date.parse(iso))) throw new Error(`잘못된 ISO 시각: ${iso}`);
154
+ }
155
+ prompt = `${v} 판 일정 ${cutAt ? `컷 ${formatKst(cutAt)}` : ''}${landBy ? ` 착지 마감 ${formatKst(landBy)}` : ''} 변경 — 진행할까요?`;
156
+ apply = () => { (deps.setSchedule ?? setSchedule)(v, { ...(cutAt ? { cutAt } : {}), ...(landBy ? { landBy } : {}) }, ownerId); };
157
+ } else throw new Error('action 은 add-item, move-item, set-schedule 중 하나여야 합니다');
158
+ debug.log('release.tool', 'change-requested', { action, version });
159
+ const result = await (deps.confirm ?? requestConfirmation)({ prompt, onTimeout: () => false, failOpen: false,
160
+ channels });
161
+ if (!result.answer || !channels.some(channel => channel.name === result.channel)) {
162
+ debug.log('release.tool', 'change-refused', { action, version });
163
+ return `${prompt} — 승인되지 않아 변경하지 않았습니다.`;
164
+ }
165
+ apply();
166
+ debug.log('release.tool', 'change-confirmed', { action, version });
167
+ return `${prompt} — 변경했습니다.`;
168
+ }