elanous 0.2.17 → 0.2.19

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 (491) 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/J5H7BvqgSI8DUjtRRcA_S/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/1105-c473ed46272fba79.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1157-cba0e8553d35a980.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{726.83532d69452ee24b.js → 1470.98a6ab61ccb4131c.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/171-23a877ed11720de9.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2336-e830c777eb6bce04.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{9247-269e4464cc4b491c.js → 2375-e061bb24cd2b5cde.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{211-9c9090c59e2c5907.js → 2977-e38442e86289f1d3.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/{f6b7c9ba.64a895b8b2fedbdc.js → 2ea96958.e8c7e112de50b067.js} +1 -1
  15. package/apps/pwa/out/_next/static/chunks/3094.67339318c7f72a3a.js +2 -0
  16. package/apps/pwa/out/_next/static/chunks/338-ceb956612dcfaa86.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3395-835e4ceaa6335ba4.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3403.4bc97b4066945a94.js +5 -0
  19. package/apps/pwa/out/_next/static/chunks/3441-be82e3595aef3a20.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/3689-89905039bf3e7257.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/3822-6f268bc26c6a84ac.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/{1268.0840ff91e083fcbd.js → 4323.c5dfb6f503304775.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/4457-d7df81c90f9d0268.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/4551-77dadae76f6cbace.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/{6110.1297d4093fdfbb0d.js → 4847.56f5bc454fbcc827.js} +2 -2
  28. package/apps/pwa/out/_next/static/chunks/4978.6fd93b974441e87f.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5012-f746f6549b869f91.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/5791-bb82743fa6299562.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/{7327-2d69fb321ba73389.js → 6062-73e68343a9814f6a.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/62-5bfed1e23790b745.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/6320-9bc8561fde416e07.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/649-ca96d1c5d09e02b8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/6726-85a7db59b288c53f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/6735-33267c4630d0d963.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/7633-141682015873beb6.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/{935.4ddaa99fc1d1126c.js → 7647.2fcd30d894d5ee30.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/{f6231284-45726550aba08fca.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/8067-143791231c99bd02.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/8430-a10cfba8ef282d4e.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/8870-9614a213b990b10b.js +25 -0
  49. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/9454-12c2f30dea62291c.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/976-4a9b4ebdca7c739a.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/9989-15cdf568bafa9fc8.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/ae787e4c-e5b1ef00b2dbceca.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/approvals/page-deef5ee191830f14.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-b193da485f9a14b2.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/board/page-b6e1a79df0e214f4.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/botlab/page-e84df4d907f703c7.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/bots/page-a5fb9a5db89ae4ea.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1499f481d7fc52d6.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/chat/page-c7cf753aeb648790.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/consult/page-4118469e514e10fd.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/control/page-e968f7630654332a.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/decisions/page-505915052f947022.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c2f962fb4a3b06c6.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/editor/page-7b5ed0bd8a589ec4.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/exec/page-cf8193250dd42dd4.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/field/page-cf3f5dfa35dea8e9.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/inside/page-5b29d9b06ceadade.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/intake/page-a15e7fcc283e9ca7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/layout-d2915e7b70ff3d0e.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/live/page-b70cd005d9169e25.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/loops/page-2595f0eba29f57e1.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/market/page-c776322454156e36.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-77bbd17f4435aa10.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/morning/page-049689e611ecc936.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/not-found-85954a4fb09c44eb.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/observatory/page-e773e552e1068ee6.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-5633c4bfa7684b4a.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-d829b92bd01b4994.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-061c317fc771becf.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/outputs/page-3a45ece8865ed4b6.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/page-0be990618c0d56bd.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/reflection/page-bfea4865dc244b73.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-0d6ba8e337741484.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/sessions/page-76a88b55d816482e.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-bba1542df59f61ce.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/app/settings/page-4185c95689bb372e.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-b699e1a8d5d34b8b.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/setup/page-c225e338c31bf274.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/share/page-449353f70507ca1f.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90b889714102476d.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f1ee7fbeb7f96ddc.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/app/term/page-d59a95e1434dfaa0.js +1 -0
  97. package/apps/pwa/out/_next/static/chunks/app/today/page-71491a23414d1a47.js +1 -0
  98. package/apps/pwa/out/_next/static/chunks/app/trace/page-de7112bdb65fa4fe.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/app/vault/page-feb4d0d88079660d.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-f17d887b40dd6b1d.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/app/workflows/page-8da77be2192bcd5d.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/app/workspace/page-72c3b9a5e9582b29.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3afe4c8d0c48c39.js +1 -0
  104. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
  105. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
  106. package/apps/pwa/out/_next/static/chunks/main-app-b9308f2d8a931531.js +1 -0
  107. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
  108. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
  109. package/apps/pwa/out/_next/static/chunks/webpack-6f35f9d892fa630b.js +1 -0
  110. package/apps/pwa/out/_next/static/css/{e99e3cff94ced78a.css → 599369d853c61df7.css} +1 -1
  111. package/apps/pwa/out/_next/static/css/85f53c6f35d11382.css +1 -0
  112. package/apps/pwa/out/_next/static/css/d3427996da11e480.css +1 -0
  113. package/apps/pwa/out/approvals/index.html +1 -1
  114. package/apps/pwa/out/approvals/index.txt +23 -23
  115. package/apps/pwa/out/autopilot/index.html +1 -1
  116. package/apps/pwa/out/autopilot/index.txt +23 -23
  117. package/apps/pwa/out/board/index.html +1 -1
  118. package/apps/pwa/out/board/index.txt +23 -23
  119. package/apps/pwa/out/botlab/index.html +1 -1
  120. package/apps/pwa/out/botlab/index.txt +22 -22
  121. package/apps/pwa/out/bots/index.html +1 -1
  122. package/apps/pwa/out/bots/index.txt +22 -22
  123. package/apps/pwa/out/ceo/index.html +1 -1
  124. package/apps/pwa/out/ceo/index.txt +23 -23
  125. package/apps/pwa/out/chat/index.html +1 -1
  126. package/apps/pwa/out/chat/index.txt +22 -22
  127. package/apps/pwa/out/consult/index.html +1 -1
  128. package/apps/pwa/out/consult/index.txt +23 -23
  129. package/apps/pwa/out/control/index.html +1 -1
  130. package/apps/pwa/out/control/index.txt +23 -23
  131. package/apps/pwa/out/decisions/index.html +1 -0
  132. package/apps/pwa/out/decisions/index.txt +30 -0
  133. package/apps/pwa/out/design-check/index.html +1 -1
  134. package/apps/pwa/out/design-check/index.txt +23 -23
  135. package/apps/pwa/out/editor/index.html +1 -1
  136. package/apps/pwa/out/editor/index.txt +25 -24
  137. package/apps/pwa/out/exec/index.html +1 -1
  138. package/apps/pwa/out/exec/index.txt +23 -23
  139. package/apps/pwa/out/field/index.html +1 -1
  140. package/apps/pwa/out/field/index.txt +23 -23
  141. package/apps/pwa/out/healthz +1 -1
  142. package/apps/pwa/out/index.html +1 -1
  143. package/apps/pwa/out/index.txt +22 -22
  144. package/apps/pwa/out/inside/index.html +1 -1
  145. package/apps/pwa/out/inside/index.txt +24 -23
  146. package/apps/pwa/out/intake/index.html +1 -1
  147. package/apps/pwa/out/intake/index.txt +23 -23
  148. package/apps/pwa/out/live/index.html +1 -1
  149. package/apps/pwa/out/live/index.txt +23 -23
  150. package/apps/pwa/out/loops/index.html +1 -1
  151. package/apps/pwa/out/loops/index.txt +28 -27
  152. package/apps/pwa/out/market/index.html +1 -1
  153. package/apps/pwa/out/market/index.txt +22 -22
  154. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  155. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  156. package/apps/pwa/out/morning/index.html +1 -1
  157. package/apps/pwa/out/morning/index.txt +23 -23
  158. package/apps/pwa/out/observatory/index.html +1 -1
  159. package/apps/pwa/out/observatory/index.txt +23 -23
  160. package/apps/pwa/out/ops/checklist/index.html +1 -1
  161. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  162. package/apps/pwa/out/ops/release/index.html +1 -1
  163. package/apps/pwa/out/ops/release/index.txt +22 -22
  164. package/apps/pwa/out/ops/seats/index.html +1 -1
  165. package/apps/pwa/out/ops/seats/index.txt +22 -22
  166. package/apps/pwa/out/outputs/index.html +1 -1
  167. package/apps/pwa/out/outputs/index.txt +22 -22
  168. package/apps/pwa/out/reflection/index.html +1 -1
  169. package/apps/pwa/out/reflection/index.txt +23 -23
  170. package/apps/pwa/out/scheduler/index.html +1 -1
  171. package/apps/pwa/out/scheduler/index.txt +23 -23
  172. package/apps/pwa/out/sessions/index.html +1 -1
  173. package/apps/pwa/out/sessions/index.txt +22 -22
  174. package/apps/pwa/out/settings/devices/index.html +1 -1
  175. package/apps/pwa/out/settings/devices/index.txt +23 -23
  176. package/apps/pwa/out/settings/index.html +1 -1
  177. package/apps/pwa/out/settings/index.txt +23 -23
  178. package/apps/pwa/out/setup/done/index.html +1 -1
  179. package/apps/pwa/out/setup/done/index.txt +23 -23
  180. package/apps/pwa/out/setup/index.html +1 -1
  181. package/apps/pwa/out/setup/index.txt +23 -23
  182. package/apps/pwa/out/share/index.html +1 -1
  183. package/apps/pwa/out/share/index.txt +23 -23
  184. package/apps/pwa/out/showroom/index.html +1 -1
  185. package/apps/pwa/out/showroom/index.txt +22 -22
  186. package/apps/pwa/out/tasks/index.html +1 -1
  187. package/apps/pwa/out/tasks/index.txt +23 -23
  188. package/apps/pwa/out/term/index.html +1 -1
  189. package/apps/pwa/out/term/index.txt +23 -23
  190. package/apps/pwa/out/today/index.html +1 -1
  191. package/apps/pwa/out/today/index.txt +23 -23
  192. package/apps/pwa/out/trace/index.html +1 -1
  193. package/apps/pwa/out/trace/index.txt +23 -23
  194. package/apps/pwa/out/vault/index.html +1 -1
  195. package/apps/pwa/out/vault/index.txt +23 -23
  196. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  197. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  198. package/apps/pwa/out/workflows/index.html +1 -1
  199. package/apps/pwa/out/workflows/index.txt +23 -23
  200. package/apps/pwa/out/workspace/index.html +1 -1
  201. package/apps/pwa/out/workspace/index.txt +23 -23
  202. package/apps/pwa/out/worktrees/index.html +1 -1
  203. package/apps/pwa/out/worktrees/index.txt +23 -23
  204. package/graphs/ops/guardian.yaml +26 -0
  205. package/graphs/ops/recipes.yaml +3 -0
  206. package/package.json +2 -1
  207. package/samples/workflows/templates/approval-gated-deploy.yaml +30 -0
  208. package/samples/workflows/templates/chat-driven-research.yaml +35 -0
  209. package/samples/workflows/templates/discord-listen-and-respond.yaml +29 -0
  210. package/samples/workflows/templates/http-poll-then-alert.yaml +30 -0
  211. package/samples/workflows/templates/manual-quickstart.yaml +21 -0
  212. package/samples/workflows/templates/scheduled-daily-digest.yaml +30 -0
  213. package/samples/workflows/templates/webhook-deploy-hook.yaml +27 -0
  214. package/scripts/ci-public-export-import-gate.ts +7 -2
  215. package/scripts/ci-typecheck-changed.ts +21 -6
  216. package/scripts/coord-channel-watch.sh +2 -2
  217. package/scripts/discord-seat-probe.ts +6 -3
  218. package/scripts/docs/generate-cli-anatomy.ts +162 -0
  219. package/scripts/goals/authoring-metrics.ts +73 -0
  220. package/scripts/landing-heal/must-fix-match.ts +7 -7
  221. package/scripts/landing-heal/run-mission.ts +3 -2
  222. package/scripts/lib/pch-regress-map.ts +23 -0
  223. package/scripts/morning-report.ts +18 -4
  224. package/scripts/pch-regress.ts +88 -0
  225. package/scripts/public-export-test-run.ts +37 -2
  226. package/scripts/release-loop/checklist-gate-node.ts +13 -11
  227. package/scripts/release-loop/cut-branch.ts +2 -1
  228. package/scripts/release-loop/cutoff.ts +4 -2
  229. package/scripts/release-loop/docs-land-node.ts +8 -1
  230. package/scripts/release-loop/docs-node.ts +1 -1
  231. package/scripts/release-loop/gate-diff.ts +18 -0
  232. package/scripts/release-loop/gate-node.ts +436 -59
  233. package/scripts/release-loop/gate-partial.ts +106 -0
  234. package/scripts/release-loop/gate-timing.ts +120 -0
  235. package/scripts/release-loop/node-verdict.ts +2 -1
  236. package/scripts/release-loop/npm-publish-node.ts +11 -7
  237. package/scripts/release-loop/ops-upgrade-node.ts +8 -1
  238. package/scripts/release-loop/preflight.ts +246 -0
  239. package/scripts/release-loop/publish-node.ts +8 -1
  240. package/scripts/release-loop/release-readiness.ts +11 -6
  241. package/scripts/release-loop/release-version.ts +48 -0
  242. package/scripts/release-loop/resume-release.ts +174 -0
  243. package/scripts/release-loop/shard-plan.ts +65 -0
  244. package/scripts/release-loop/unattended-release.ts +45 -7
  245. package/scripts/release-loop/version-node.ts +145 -40
  246. package/scripts/rhythm/daily.ts +25 -4
  247. package/scripts/skills/public-check.ts +62 -12
  248. package/scripts/test-deterministic.ts +51 -33
  249. package/scripts/test-diet/effectiveness.ts +62 -1
  250. package/scripts/test-diet/lib.ts +1 -1
  251. package/scripts/test-diet/node.ts +8 -1
  252. package/scripts/video/scene-table.ts +159 -0
  253. package/src/acp/core-turn-bridge.ts +39 -2
  254. package/src/agent-mission/review-loop.ts +4 -3
  255. package/src/agent-substrate/pr-comment-meta.ts +20 -0
  256. package/src/autopilot/build/llm-conflict-merge.ts +28 -8
  257. package/src/away/presence.ts +40 -0
  258. package/src/away/release-watch.ts +255 -0
  259. package/src/briefing/brief-items.ts +86 -17
  260. package/src/chat/index.ts +2 -1
  261. package/src/chat/short-question.ts +3 -40
  262. package/src/cli/autopilot-cli.ts +30 -11
  263. package/src/cli/away-cli.ts +68 -0
  264. package/src/cli/brief-cli.ts +36 -2
  265. package/src/cli/freeze-cli.ts +3 -2
  266. package/src/cli/guardian-cli.ts +49 -0
  267. package/src/cli/intake-cli.ts +85 -7
  268. package/src/cli/log-event-names.ts +1 -0
  269. package/src/cli/notify-cli.ts +75 -0
  270. package/src/cli/pod-cli.ts +5 -1
  271. package/src/cli/provider-cli.ts +12 -2
  272. package/src/cli/pty-drive-cli.ts +14 -3
  273. package/src/cli/release-cli.ts +128 -19
  274. package/src/cli/setup-cli.ts +31 -1
  275. package/src/cli/tasks-cli.ts +403 -4
  276. package/src/context-bus/condense.ts +19 -3
  277. package/src/context-bus/context-day.ts +77 -24
  278. package/src/context-bus/context-first.ts +33 -0
  279. package/src/context-bus/context-now-public.ts +1 -1
  280. package/src/context-bus/context-now-surfaces.ts +16 -6
  281. package/src/context-bus/context-now.ts +97 -3
  282. package/src/core-turn/short-question.ts +100 -0
  283. package/src/dashboard/dashboard-status-lines.ts +24 -4
  284. package/src/dashboard/index.ts +50 -5
  285. package/src/dashboard/input/slash-executor.ts +6 -2
  286. package/src/dashboard/slash-runtime/dashboard-handlers.ts +63 -36
  287. package/src/dashboard/slash-runtime/decide-slash.ts +60 -0
  288. package/src/dashboard/slash-runtime/help-from-registry.ts +21 -8
  289. package/src/dashboard/slash-runtime/help-groups.ts +44 -0
  290. package/src/dashboard/slash-runtime/model-choices.ts +125 -0
  291. package/src/discord-self-message.ts +20 -2
  292. package/src/discord-slash-wire.ts +1 -0
  293. package/src/domains/guardian-tick.ts +138 -0
  294. package/src/domains/outbound-alert.ts +244 -40
  295. package/src/domains/outbound-log-probe.ts +16 -1
  296. package/src/domains/release-tool.ts +2 -2
  297. package/src/domains/schedule-registry.ts +23 -0
  298. package/src/domains/telegram-kind-route.ts +39 -0
  299. package/src/domains/watch-default-scheduler.ts +95 -0
  300. package/src/esc-abort-gate.ts +44 -0
  301. package/src/execution-loop/gates.ts +11 -2
  302. package/src/flow-loop/split.ts +8 -4
  303. package/src/git-fs/worktree.ts +23 -3
  304. package/src/graph-runner/runner.ts +25 -5
  305. package/src/harness/harness-cli-command.ts +12 -2
  306. package/src/harness/harness-queue.ts +388 -33
  307. package/src/harness/harness-salvage-cli.ts +78 -0
  308. package/src/harness/harness-worktree-add.ts +68 -0
  309. package/src/harness/helper-scan.ts +10 -3
  310. package/src/harness/ledger-write-guard.ts +6 -16
  311. package/src/hq/hq.ts +6 -1
  312. package/src/hq/seats.ts +369 -0
  313. package/src/index.ts +185 -18
  314. package/src/instance/test-write-guard-spy-first.fixture.ts +13 -0
  315. package/src/instance/test-write-guard.ts +200 -0
  316. package/src/intake-plane/adopt-gate.ts +49 -0
  317. package/src/intake-plane/check.ts +3 -0
  318. package/src/intake-plane/collect-github.ts +57 -25
  319. package/src/intake-plane/digest.ts +39 -15
  320. package/src/intake-plane/discord-seat-work.ts +8 -2
  321. package/src/intake-plane/harness-target.ts +4 -3
  322. package/src/intake-plane/intake-sources.ts +1 -0
  323. package/src/intake-plane/items.ts +1 -0
  324. package/src/intake-plane/requirement-funnel.ts +316 -0
  325. package/src/knowledge/know-find.ts +10 -1
  326. package/src/loops/budget.ts +36 -0
  327. package/src/loops/orchestrator/degradation.ts +57 -0
  328. package/src/loops/orchestrator/finish-rate.ts +106 -4
  329. package/src/loops/orchestrator/grid-place.ts +11 -0
  330. package/src/loops/orchestrator/tick.ts +450 -54
  331. package/src/maturity/feature-maturity.ts +8 -5
  332. package/src/maturity/tui-slash-maturity.ts +1 -0
  333. package/src/mss/logging/log-store.ts +3 -0
  334. package/src/nexus/api/decisions-route.ts +11 -1
  335. package/src/nexus/api/http-server.ts +16 -5
  336. package/src/nexus/api/loop-edges.ts +352 -23
  337. package/src/nexus/api/ops-api.ts +96 -28
  338. package/src/nexus/api/ops-seats-sources.ts +2 -2
  339. package/src/nexus/api/workflow-templates.ts +3 -0
  340. package/src/nexus/index.ts +7 -0
  341. package/src/oauth/codex.ts +38 -0
  342. package/src/onboarding/first-chat-gate.ts +29 -0
  343. package/src/release-loop/checklist.ts +106 -6
  344. package/src/release-loop/feature-store.ts +64 -16
  345. package/src/release-loop/landing-freeze.ts +6 -4
  346. package/src/release-loop/light-rc.ts +173 -0
  347. package/src/release-loop/placement.ts +64 -20
  348. package/src/release-loop/rubric.ts +136 -0
  349. package/src/seat-dispatch/seat-ask.ts +79 -16
  350. package/src/seat-loop/seat-loop.ts +12 -6
  351. package/src/self-dev/ask-launch-flow.ts +24 -3
  352. package/src/self-dev/ask-path-candidates.ts +68 -0
  353. package/src/self-dev/dev-cli.ts +36 -0
  354. package/src/self-dev/dev-pipeline.ts +4 -0
  355. package/src/self-dev/draft-triage-rules.ts +1 -1
  356. package/src/self-dev/orchestrate-cli.ts +22 -0
  357. package/src/self-dev/orchestrate.ts +1 -0
  358. package/src/self-dev/pr-preview.ts +179 -0
  359. package/src/self-dev/round-pr-supersede.ts +244 -0
  360. package/src/self-dev/run-supervisor.ts +14 -0
  361. package/src/self-dev/shard-run.ts +23 -7
  362. package/src/self-implement/follow-up-goals.ts +2 -2
  363. package/src/self-implement/gate-cli.ts +29 -8
  364. package/src/self-implement/gate-remote.ts +427 -0
  365. package/src/self-implement/goal-author-clarification.ts +88 -13
  366. package/src/self-implement/goal-author.ts +8 -3
  367. package/src/self-implement/goal-clarification-escalation.ts +2 -0
  368. package/src/self-implement/l8-merge-queue.ts +29 -5
  369. package/src/self-implement/orchestrator.ts +61 -13
  370. package/src/self-implement/prior-draft-findings.ts +4 -3
  371. package/src/self-implement/salvage-branches.ts +254 -0
  372. package/src/skills/skill-feedback.ts +76 -0
  373. package/src/task-agent/actions.ts +101 -7
  374. package/src/task-agent/board.ts +132 -0
  375. package/src/task-agent/card-evidence.ts +203 -0
  376. package/src/task-agent/completion-evidence.ts +126 -0
  377. package/src/task-agent/judge.ts +49 -5
  378. package/src/task-agent/live-moves.ts +176 -0
  379. package/src/task-agent/mission.ts +449 -0
  380. package/src/task-agent/shadow.ts +258 -0
  381. package/src/task-agent/task-hand.ts +337 -0
  382. package/src/task-orchestrator/surfaces/pod-command-job.ts +64 -4
  383. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +10 -0
  384. package/src/task-orchestrator/surfaces/pod-lease.ts +142 -13
  385. package/src/task-orchestrator/surfaces/pod-pool.ts +13 -5
  386. package/src/task-orchestrator/surfaces/self-implement-pod.ts +88 -20
  387. package/src/telegram-away-command.ts +38 -0
  388. package/src/telegram-commands.ts +11 -0
  389. package/src/telegram-loops-command.ts +1 -1
  390. package/src/telegram-report-log-probe.ts +21 -0
  391. package/src/telegram-report.ts +27 -3
  392. package/src/telegram.ts +5 -20
  393. package/src/tui-client/dashboard-session.ts +1 -0
  394. package/src/user-config.ts +196 -4
  395. package/src/version/packed-revision.json +1 -1
  396. package/apps/pwa/out/_next/static/chunks/1307-aac7332576c4d6fc.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/1844-9ec4b1d12f6e80eb.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/1948.15f356b15a625597.js +0 -5
  401. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/4614-21403aac730d4586.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/4795-f372490b178583fc.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +0 -2
  420. package/apps/pwa/out/_next/static/chunks/6877-fe94e76a5841ee9b.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/8401-4ce1c42c8d89903f.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +0 -25
  429. package/apps/pwa/out/_next/static/chunks/8949-ecb4128a0858b911.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/899-f41be85e8a680651.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/9103-60fbef33e66f0a46.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/a9f2b54d-7865aba0f4d2ee49.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/approvals/page-3143b07a817d1da9.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-17ba086ef82d339b.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/board/page-7eb661ff23af7da2.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/botlab/page-6b8444914da74ca6.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/bots/page-6e5c3a867557298b.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/ceo/page-7110f732f45bf944.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/chat/page-9a41d65355fc25b9.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/app/consult/page-5e77cd1802567c89.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/app/control/page-41afec0b255ed2bb.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4187666132fc98c4.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/app/editor/page-09328d499162bae4.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/app/exec/page-29ff3a8f84f03b6a.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/app/field/page-80ec7401bc83f511.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/app/inside/page-ab93b240f687d69a.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/app/intake/page-fc7d86467291ae38.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/app/layout-de43a186e5ba5683.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/app/live/page-8b08cafad27759c9.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/app/loops/page-0e2d6985fb42cd2f.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/app/market/page-13e4108999f58d13.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e8296ee284aa1293.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/app/morning/page-043a9111e74f6b24.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/app/not-found-23a4601ceb7f452e.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1ed1324b1dc36754.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-10d958605a77b3bd.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-166745e51ac0c7b0.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-a04260b8ccc968d4.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/app/outputs/page-7a1d85e121065cc8.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/app/page-1f50e3d4e13435f4.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/app/reflection/page-12916918988ac97e.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-d8a3347a9d0c3f4a.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/app/sessions/page-a9117d2cd3093d36.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-5a64cb5208c6a8fd.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/app/settings/page-1407ec83ea64c1f9.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-1d824510df025d18.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/setup/page-6e878ebbe1737d00.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/share/page-7f49fe645d5132f7.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/showroom/page-81a4e7f8dbfe74cb.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/tasks/page-27d71f131acd9320.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/term/page-3ab59d19913b200e.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/today/page-bf85b2f5bf077925.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/trace/page-424659bacfb8cdd7.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/vault/page-ab32a8f522a60371.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-fc2df261f8a2c7df.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/workflows/page-09461366af82ef13.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d4fef9ccd7d406b.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ea90a8ac73ce2749.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +0 -1
  489. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +0 -1
  490. package/apps/pwa/out/_next/static/yRA5ur6DcPl0YHSRheH2O/_buildManifest.js +0 -1
  491. /package/apps/pwa/out/_next/static/{yRA5ur6DcPl0YHSRheH2O → J5H7BvqgSI8DUjtRRcA_S}/_ssgManifest.js +0 -0
@@ -283,6 +283,8 @@ import { runRebindCommand } from '../../input-core/index.js';
283
283
  import { resolveRepositoryDesignCheck } from '../../cli/repo-cli.js';
284
284
  import { renderDesignCheckLines, type DesignCheckTone } from '../../design/design-check-render.js';
285
285
  import { handleDesignPick } from './design-pick.js';
286
+ import { createDecideSlashHandler } from './decide-slash.js';
287
+ import type { DecisionLedger } from '../../decisions/decision-ledger.js';
286
288
  import { listDesignDirections, parseDeclaredDirection } from '../../design/design-directions.js';
287
289
  import {
288
290
  dashboardDeltaHelpLines,
@@ -303,8 +305,6 @@ import {
303
305
  rotateNextProvider, jumpToRotationEntry, rotationEntryLabel,
304
306
  currentRotationIndex,
305
307
  } from '../../user-config.js';
306
- import type { LLMProviderName } from '../../user-config.js';
307
- import { lookupLlmTierSpec } from '../../model-tier/index.js';
308
308
  import { listProviders } from '../../llm.js';
309
309
  import { enabledModels, loadCatalog } from '../../intelligence-map/model-catalog.js';
310
310
  import { selfOrchestrateRuntime } from '../../self-dev/self-orchestrate-runtime.js';
@@ -343,6 +343,7 @@ import { terminalModalRouter } from '../input/terminal-modal-router.js';
343
343
  import { termSize, stripAnsi } from '../../tui.js';
344
344
  import { SLASH_COMMANDS } from '../../chat/index.js';
345
345
  import { buildEssentialHelpLines } from './help-from-registry.js';
346
+ import { HUMAN_MODEL_WORDS, MODEL_CODE_NAMES, modelChangeLine, modelOverviewLines, resolveModelChoice } from './model-choices.js';
346
347
  import type { FoldMode } from '../../log-entry.js';
347
348
  import { resolveDashboardChatMainLogCommand } from '../input/chat-main-log-command.js';
348
349
  import {
@@ -375,6 +376,7 @@ import { dirname as pathDirname, relative } from 'node:path';
375
376
  import { readFileSync, writeFileSync } from 'node:fs';
376
377
  import type { SurfaceUx } from '../../agent/surface-ux/types.js';
377
378
  import type { AskUserQuestionRequest } from '../../ask-user-question/types.js';
379
+ import { DASHBOARD_CLARIFICATION_REQUEST } from '../../esc-abort-gate.js';
378
380
  import type { GoalDocumentClarification } from '../../self-implement/goal-author-clarification.js';
379
381
  import { renderReplayPreviewLines } from '../replay-preview.js';
380
382
  import {
@@ -1295,6 +1297,7 @@ let selfOrchestrateSlashRuntime: SelfOrchestrateSlashRuntime = selfOrchestrateRu
1295
1297
  export async function readDashboardAskClarification(
1296
1298
  clarification: GoalDocumentClarification,
1297
1299
  surfaceUx: Pick<SurfaceUx, 'question'> | undefined,
1300
+ notify?: (line: string) => void,
1298
1301
  ): Promise<string> {
1299
1302
  if (!surfaceUx) return '';
1300
1303
  const request: AskUserQuestionRequest = {
@@ -1309,10 +1312,13 @@ export async function readDashboardAskClarification(
1309
1312
  includeOther: clarification.includeOther,
1310
1313
  }],
1311
1314
  };
1315
+ Object.defineProperty(request, DASHBOARD_CLARIFICATION_REQUEST, { value: true });
1312
1316
  try {
1313
- const result = await surfaceUx.question(request);
1314
- if (!result || result.cancelled) return '';
1317
+ const pending = surfaceUx.question(request);
1315
1318
  const id = clarification.questionId;
1319
+ notify?.('하니스가 묻는다 — 창에서 고르거나 Esc 로 답 없이 진행');
1320
+ const result = await pending;
1321
+ if (!result || result.cancelled) return '';
1316
1322
  const answer = result.answers[id];
1317
1323
  const selectedLabel = Array.isArray(answer) ? answer[0] : answer;
1318
1324
  const optionIndex = clarification.options.findIndex((option) => option.label === selectedLabel);
@@ -1393,8 +1399,14 @@ async function fetchLoopsDaemonJson<T>(url: string, headers: Record<string, stri
1393
1399
  }
1394
1400
  }
1395
1401
 
1396
- export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn> {
1402
+ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps, decideLedger?: DecisionLedger): SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn> {
1397
1403
  const registry = new SlashCommandRegistry<DashboardSlashContext, DashboardSlashReturn>();
1404
+ const decide = createDecideSlashHandler(decideLedger);
1405
+ registry.register(['decide', 'dec'], (args, ctx) => {
1406
+ // showDashboard in src/dashboard/index.ts dispatches into this registry.
1407
+ for (const line of decide(args).split('\n')) ctx.pushChatLine(line);
1408
+ ctx.setChatScrollOffset(-1);
1409
+ });
1398
1410
 
1399
1411
  registry.register(['now'], (args, ctx) => {
1400
1412
  for (const line of tuiNowSlash(args, nowDeps)) ctx.pushChatLine(line);
@@ -2436,7 +2448,11 @@ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashComm
2436
2448
  print: (line) => { for (const part of line.split('\n')) ctx.chatLines.push(` ${part}`); },
2437
2449
  log: (event, data, level) => debug.log('dev-pipeline', event, { ...data, surface: 'tui-slash' }, { level }),
2438
2450
  readLine: async () => '',
2439
- readClarification: (clarification) => readDashboardAskClarification(clarification, ctx.surfaceUx),
2451
+ readClarification: (clarification) => readDashboardAskClarification(clarification, ctx.surfaceUx, (line) => {
2452
+ ctx.pushChatLine(line);
2453
+ ctx.setChatScrollOffset(-1);
2454
+ ctx.draw();
2455
+ }),
2440
2456
  readFile: (file) => readFileSync(file, 'utf8'),
2441
2457
  writeFile: (file, data) => writeFileSync(file, data, 'utf8'),
2442
2458
  cwd: () => process.cwd(),
@@ -6161,46 +6177,57 @@ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashComm
6161
6177
  });
6162
6178
 
6163
6179
  registry.register(['model', 'm'], (args, ctx) => {
6164
- // /model — 현재 모델 + 스위치 가능 목록
6165
- // /model <alias> — 아래 curated 목록의 alias
6180
+ // /model — 지금 모델(사람 낱말) + 고르기
6181
+ // /model 빠름|보통|깊음 — 지금 provider 의 budget·better·best 티어 (model-choices.ts)
6182
+ // /model <코드명> — codex·terra·sol·luna·opus·sonnet·grok (그대로 동작)
6166
6183
  // effort(low/med/high)는 /reasoning. NL 미지원(대표 OK·slash-only).
6167
- //
6168
- // 명시 target 맵(id + provider) — catalog/prefix 추론 대신 확정값. 핵심:
6169
- // Codex 계열(gpt-5.5/5.6-sol/terra/luna)은 gpt- prefix 라 자동 추론은
6170
- // 'openai'(Chat Completions)로 오분류하나, 실제론 Responses API 필수라
6171
- // provider=openai-codex 여야 한다(omni-crawl 2026-07-17 확인). GPT(openai·
6172
- // Chat Completions)는 노출하지 않는다(대표 지시 — 혼란 방지·codex만).
6173
- // 비-OpenAI fallback 은 claude(anthropic)·grok.
6174
- const MODEL_TARGETS: Record<string, { model: string; provider: LLMProviderName }> = {
6175
- codex: { model: 'gpt-5.5', provider: 'openai-codex' },
6176
- // 🩸 2026-09-23 — GPT-6 에는 terra 가 없다(결정). 별칭은 남기되 codex 사다리 better 칸(sol 한 칸 아래 자리)을 가리킨다.
6177
- terra: { model: lookupLlmTierSpec('openai-codex', 'better').model, provider: 'openai-codex' },
6178
- sol: { model: lookupLlmTierSpec('openai-codex', 'best').model, provider: 'openai-codex' },
6179
- luna: { model: lookupLlmTierSpec('openai-codex', 'budget').model, provider: 'openai-codex' },
6180
- opus: { model: 'claude-opus-4-8', provider: 'anthropic' },
6181
- sonnet: { model: 'claude-sonnet-5', provider: 'anthropic' },
6182
- // ⭐ 티어 표를 따른다(옆 luna 와 같은 형태) — 하드코딩이면 표를 바꿔도 «안 따라온다»(2026-08-18 대표 4.6 재편)
6183
- grok: { model: lookupLlmTierSpec('grok', 'best').model, provider: 'grok' },
6184
- };
6185
6184
  const arg = (args[0] ?? '').toLowerCase().trim();
6186
6185
  const curCfg = getUserConfig();
6187
- const curModel = curCfg.llm.model ?? '(provider default)';
6188
- const aliases = Object.keys(MODEL_TARGETS);
6186
+ const curProvider = curCfg.llm.provider;
6187
+ const curModel = curCfg.llm.model;
6188
+ const curReasoning = effectiveReasoningLevel(curCfg.llm, curProvider, curModel);
6189
6189
  if (!arg) {
6190
- ctx.chatLines.push(`model: ${curModel} · provider ${curCfg.llm.provider}`);
6191
- ctx.chatLines.push(ctx.muted(` switch: /model <${aliases.join('|')}> · effort: /reasoning`));
6190
+ const [now, codes] = modelOverviewLines(curProvider, curModel, curReasoning);
6191
+ ctx.chatLines.push(now);
6192
+ ctx.chatLines.push(ctx.muted(codes));
6192
6193
  ctx.setChatScrollOffset(-1);
6193
6194
  return;
6194
6195
  }
6195
- const target = MODEL_TARGETS[arg];
6196
- if (!target) {
6197
- ctx.chatLines.push(ctx.muted(`/model: unknown "${arg}". try ${aliases.join('/')}.`));
6196
+ const resolution = resolveModelChoice(arg, curProvider);
6197
+ if (resolution.kind === 'needs-provider') {
6198
+ debug.log('dashboard.model', 'human-word-needs-provider', { word: resolution.word, provider: curProvider });
6199
+ ctx.chatLines.push(ctx.muted(`/model ${resolution.word}: provider 가 auto 라 고를 사다리가 없다 — /provider 로 먼저 하나를 고르거나 코드명(${MODEL_CODE_NAMES.join('/')})을 쓴다.`));
6198
6200
  ctx.setChatScrollOffset(-1);
6199
6201
  return;
6200
6202
  }
6201
- saveUserConfig({ ...curCfg, llm: { ...curCfg.llm, provider: target.provider, model: target.model } });
6203
+ if (resolution.kind === 'unknown') {
6204
+ ctx.chatLines.push(ctx.muted(`/model: "${arg}" 를 모른다. ${HUMAN_MODEL_WORDS.join(' | ')} 또는 ${MODEL_CODE_NAMES.join('/')}.`));
6205
+ ctx.setChatScrollOffset(-1);
6206
+ return;
6207
+ }
6208
+ const { target } = resolution;
6209
+ // A human word picks a whole tier (model ⊕ reasoning, like /reasoning it clears the codex override);
6210
+ // a code name changes the model only.
6211
+ const tierReasoning = resolution.kind === 'human' ? resolution.reasoningLevel : undefined;
6212
+ saveUserConfig({
6213
+ ...curCfg,
6214
+ llm: {
6215
+ ...curCfg.llm,
6216
+ provider: target.provider,
6217
+ model: target.model,
6218
+ ...(tierReasoning ? { reasoningLevel: tierReasoning, codexReasoning: undefined } : {}),
6219
+ },
6220
+ });
6202
6221
  reloadUserConfig();
6203
- ctx.chatLines.push(`model: ${curModel} → ${target.model} (provider ${target.provider})`);
6222
+ if (tierReasoning) ctx.refreshReasoningHudSegment();
6223
+ debug.log('dashboard.model', 'switch', {
6224
+ via: resolution.kind,
6225
+ choice: resolution.kind === 'human' ? resolution.tier : resolution.name,
6226
+ from: { provider: curProvider, model: curModel ?? null },
6227
+ to: target,
6228
+ reasoning: tierReasoning ?? null,
6229
+ });
6230
+ ctx.chatLines.push(modelChangeLine({ provider: curProvider, model: curModel, reasoning: curReasoning }, resolution));
6204
6231
  ctx.chatLines.push(ctx.muted(' └─ 다음 턴부터 반영 · 실행 뱃지(#4442)로 확인'));
6205
6232
  ctx.setChatScrollOffset(-1);
6206
6233
  });
@@ -6453,7 +6480,7 @@ export function buildDashboardSlashRegistry(nowDeps?: ContextNowDeps): SlashComm
6453
6480
  { getStatusLines: ctx.getStatusLines ?? (() => []) },
6454
6481
  );
6455
6482
  if (!result) {
6456
- ctx.chatLines.push(ctx.warning(' /status takes no arguments'));
6483
+ ctx.chatLines.push(ctx.warning(' /status 는 인자 없이 · 자세히는 /status --debug'));
6457
6484
  ctx.setChatScrollOffset(-1);
6458
6485
  return;
6459
6486
  }
@@ -0,0 +1,60 @@
1
+ import { isIrreversible, renderCardText } from '../../decisions/decision-cards.js';
2
+ import { DecisionLedger, type DecisionEntry } from '../../decisions/decision-ledger.js';
3
+
4
+ function ageOf(entry: DecisionEntry, now: Date): string {
5
+ const raised = Date.parse(entry.raisedAt ?? entry.importedAt ?? '');
6
+ if (!Number.isFinite(raised)) return '나이 미상';
7
+ const minutes = Math.max(0, Math.floor((now.getTime() - raised) / 60_000));
8
+ if (minutes < 60) return `${minutes}분 전`;
9
+ if (minutes < 1440) return `${Math.floor(minutes / 60)}시간 전`;
10
+ return `${Math.floor(minutes / 1440)}일 전`;
11
+ }
12
+
13
+ /** One handler per dashboard registry: number-to-id mapping refreshes on list open or a successful answer. */
14
+ export function createDecideSlashHandler(ledger: DecisionLedger = new DecisionLedger(), now: () => Date = () => new Date()) {
15
+ let snapshot: DecisionEntry[] | undefined;
16
+ let pendingConfirm: { id: string; key: string } | undefined;
17
+ const openList = (): string => {
18
+ pendingConfirm = undefined;
19
+ snapshot = ledger.list({ status: 'open' });
20
+ if (!snapshot.length) return '열린 결정 없음';
21
+ return [...snapshot.map((entry, index) =>
22
+ `${index + 1}. ${entry.title} [${entry.options.map((option) => option.key).join('/')}] · ${entry.raisedBy.track ?? entry.raisedBy.agent} · ${ageOf(entry, now())}`),
23
+ '답하기: /decide <번호> <선택지 키>'].join('\n');
24
+ };
25
+ return (args: readonly string[]): string => {
26
+ if (!args.length) return openList();
27
+
28
+ const numbers = snapshot?.length ? `1~${snapshot.length}` : '없음 (/decide 로 목록 열기)';
29
+ const invalidNumber = `쓸 수 있는 번호: ${numbers}`;
30
+ const index = /^(?:[1-9]\d*)$/.test(args[0] ?? '') ? Number(args[0]) - 1 : -1;
31
+ const selected = snapshot?.[index];
32
+ if (!selected || !Number.isSafeInteger(index)) return invalidNumber;
33
+ const entry = ledger.list({ status: 'open' }).find((card) => card.id === selected.id);
34
+ if (!entry) return `이미 닫힌 결정입니다 (${args[0]}) — /decide 로 목록을 다시 여세요`;
35
+ if (args.length === 1) {
36
+ pendingConfirm = undefined;
37
+ return renderCardText(entry);
38
+ }
39
+
40
+ const key = args[1];
41
+ const option = entry.options.find((item) => item.key === key);
42
+ const keys = entry.options.length ? entry.options.map((item) => item.key).join(', ') : '없음';
43
+ if (!option || args.length > 3 || (args.length === 3 && args[2] !== '확인')) return `쓸 수 있는 선택지 키: ${keys}`;
44
+ if (isIrreversible(entry)) {
45
+ if (args[2] !== '확인' || pendingConfirm?.id !== entry.id || pendingConfirm.key !== key) {
46
+ pendingConfirm = { id: entry.id, key: option.key };
47
+ return `${renderCardText(entry, { confirm: option.key })}\n/decide ${args[0]} ${option.key} 확인`;
48
+ }
49
+ pendingConfirm = undefined;
50
+ }
51
+ let acknowledgement: string;
52
+ try {
53
+ const { delivery } = ledger.decideWithDelivery(entry.id, option.key, { kind: 'human' });
54
+ acknowledgement = `✓ ${entry.title} → ${option.label}${delivery && !delivery.ok ? ` · 런 답 전달 실패: decisions retry-answer ${entry.id}` : ''}`;
55
+ } catch {
56
+ return `결정을 기록하지 못했습니다 (${args[0]}) — /decide 로 목록을 다시 여세요`;
57
+ }
58
+ return `${acknowledgement}\n${openList()}`;
59
+ };
60
+ }
@@ -2,6 +2,7 @@ import type { SlashCommand } from '../../chat/index.js';
2
2
  import { readTuiSlashAudience, slashMaturity, slashVisibleFor } from '../../maturity/tui-slash-maturity.js';
3
3
  import type { Role } from '../../maturity/feature-maturity.js';
4
4
  import { visibleWidth } from '../../tui.js';
5
+ import { HELP_GROUP_ORDER, helpGroupFor } from './help-groups.js';
5
6
 
6
7
  const ESSENTIAL_KEYS = [
7
8
  ['Enter', 'Send message'],
@@ -31,6 +32,11 @@ function fitLine(text: string, width: number): string {
31
32
  return fitted + '…';
32
33
  }
33
34
 
35
+ /** Head line for one help group (never starts with `/`, so it can't read as a command). */
36
+ export function helpGroupHeading(group: string): string {
37
+ return `▸ ${group}`;
38
+ }
39
+
34
40
  /** Plain content for the dashboard modal; registration, not the picker, decides which slash names exist. */
35
41
  export function buildEssentialHelpLines({
36
42
  names,
@@ -65,15 +71,22 @@ export function buildEssentialHelpLines({
65
71
  ? Math.max(1, Math.floor(width / (readableCellWidth + 1)))
66
72
  : 1;
67
73
  const columnWidth = Math.floor(width / columns);
74
+ // Task order (TUI-SLASH-DECIDE-NOW C): group head line, then that group's commands by name.
75
+ // Empty groups print no head; unmapped commands land in «그 밖», so none disappears.
68
76
  const commandLines: string[] = [];
69
- for (let start = 0; start < entries.length; start += columns) {
70
- const cells = entries.slice(start, start + columns).map(({ label, description }) => {
71
- const cellWidth = columnWidth - (columns > 1 ? 1 : 0);
72
- return visibleWidth(`${label} `) > cellWidth ? '' : fitLine(`${label} ${description}`, cellWidth);
73
- });
74
- commandLines.push(cells.map((cell, index) =>
75
- index === cells.length - 1 ? cell : cell + ' '.repeat(columnWidth - visibleWidth(cell)),
76
- ).join('').trimEnd());
77
+ for (const group of HELP_GROUP_ORDER) {
78
+ const members = entries.filter(({ name }) => helpGroupFor(name) === group);
79
+ if (members.length === 0) continue;
80
+ commandLines.push(fitLine(helpGroupHeading(group), width));
81
+ for (let start = 0; start < members.length; start += columns) {
82
+ const cells = members.slice(start, start + columns).map(({ label, description }) => {
83
+ const cellWidth = columnWidth - (columns > 1 ? 1 : 0);
84
+ return visibleWidth(`${label} `) > cellWidth ? '' : fitLine(`${label} ${description}`, cellWidth);
85
+ });
86
+ commandLines.push(cells.map((cell, index) =>
87
+ index === cells.length - 1 ? cell : cell + ' '.repeat(columnWidth - visibleWidth(cell)),
88
+ ).join('').trimEnd());
89
+ }
77
90
  }
78
91
  const keyItems = ESSENTIAL_KEYS.map(([key, description]) => `${key} ${description}`);
79
92
  const keyLines: string[] = [];
@@ -0,0 +1,44 @@
1
+ // TUI-SLASH-DECIDE-NOW C · 2026-10-07 — `/help` was one alphabetical block of ~70 commands (F3 · F14:
2
+ // `/model` sat three PgUps deep). Commands are now shown in task order: «시작», then the seven public
3
+ // verbs (내부 문서 `MAP-value-props-to-features` — same words), then «그 밖».
4
+ //
5
+ // The table maps a canonical slash name to its group. A registered command that is not in the table
6
+ // falls into «그 밖» automatically — grouping never hides a command (registration and maturity decide that).
7
+
8
+ export const HELP_START_GROUP = '시작';
9
+ export const HELP_OTHER_GROUP = '그 밖';
10
+
11
+ /** The seven public verbs, in the order the public map uses them. */
12
+ export const HELP_VERB_GROUPS = [
13
+ '지켜보기',
14
+ '조사하기',
15
+ '판단하기',
16
+ '만들기',
17
+ '실행하기',
18
+ '확인하기',
19
+ '기억하기',
20
+ ] as const;
21
+
22
+ /** Render order: start, the seven verbs, then everything else. */
23
+ export const HELP_GROUP_ORDER: readonly string[] = [HELP_START_GROUP, ...HELP_VERB_GROUPS, HELP_OTHER_GROUP];
24
+
25
+ const GROUP_MEMBERS: Readonly<Record<string, readonly string[]>> = {
26
+ [HELP_START_GROUP]: ['help', 'status', 'model', 'clear', 'new', 'resume'],
27
+ 지켜보기: ['now', 'loops', 'mission', 'log', 'term', 'surface', 'conv', 'capture', 'telegram', 'remaining', 'debug'],
28
+ 조사하기: ['research', 'prompt', 'context'],
29
+ 판단하기: ['decide', 'plan', 'wish', 'directive'],
30
+ 만들기: ['harness', 'ad', 'export'],
31
+ 실행하기: ['run-skill', 'claude', 'codex', 'gemini', 'acp', 'agent-room', 'showroom', 'relay', 'lane', 'reply', 'handoff', 'inject', 'control', 'qc', 'default'],
32
+ 확인하기: ['delta', 'design', 'rewind'],
33
+ 기억하기: ['memory', 'session', 'fork', 'resume-turn', 'history', 'persona'],
34
+ };
35
+
36
+ /** Canonical slash name → group. */
37
+ export const HELP_GROUP_BY_NAME: ReadonlyMap<string, string> = new Map(
38
+ Object.entries(GROUP_MEMBERS).flatMap(([group, names]) => names.map((name) => [name, group] as const)),
39
+ );
40
+
41
+ /** Group for a canonical slash name; anything unmapped is «그 밖». */
42
+ export function helpGroupFor(name: string): string {
43
+ return HELP_GROUP_BY_NAME.get(name) ?? HELP_OTHER_GROUP;
44
+ }
@@ -0,0 +1,125 @@
1
+ // TUI-SLASH-DECIDE-NOW C · 2026-10-07 — `/model` offered only code names (terra·sol·luna…), so a first
2
+ // user had nothing to choose by (F14). Three human words now sit in front: 빠름·보통·깊음 resolve to
3
+ // the CURRENT provider's budget·better·best tiers via `lookupLlmTierSpec` (no hard-coded model ids).
4
+ // The seven code names keep going to the same model and provider as before.
5
+ // A tier is «model ⊕ reasoning level» — on several ladders better and best share a model and differ only
6
+ // in reasoning (codex gpt-6-sol medium/high · opus medium/medium), so a human word also carries the tier's
7
+ // reasoning level; otherwise 보통 and 깊음 would be the same choice. Code names leave reasoning alone.
8
+ import type { LLMProviderName, ReasoningLevel } from '../../user-config.js';
9
+ import { lookupLlmTierSpec } from '../../model-tier/index.js';
10
+ import type { ModelTier } from '../../model-tier/types.js';
11
+
12
+ export interface ModelTarget {
13
+ readonly model: string;
14
+ readonly provider: LLMProviderName;
15
+ }
16
+
17
+ // 명시 target 맵(id + provider) — catalog/prefix 추론 대신 확정값. 핵심:
18
+ // Codex 계열(gpt-5.5/5.6-sol/terra/luna)은 gpt- prefix 라 자동 추론은
19
+ // 'openai'(Chat Completions)로 오분류하나, 실제론 Responses API 필수라
20
+ // provider=openai-codex 여야 한다(omni-crawl 2026-07-17 확인). GPT(openai·
21
+ // Chat Completions)는 노출하지 않는다(대표 지시 — 혼란 방지·codex만).
22
+ // 비-OpenAI fallback 은 claude(anthropic)·grok.
23
+ export const MODEL_TARGETS: Readonly<Record<string, ModelTarget>> = {
24
+ codex: { model: 'gpt-5.5', provider: 'openai-codex' },
25
+ // 🩸 2026-09-23 — GPT-6 에는 terra 가 없다(결정). 별칭은 남기되 codex 사다리 better 칸(sol 한 칸 아래 자리)을 가리킨다.
26
+ terra: { model: lookupLlmTierSpec('openai-codex', 'better').model, provider: 'openai-codex' },
27
+ sol: { model: lookupLlmTierSpec('openai-codex', 'best').model, provider: 'openai-codex' },
28
+ luna: { model: lookupLlmTierSpec('openai-codex', 'budget').model, provider: 'openai-codex' },
29
+ opus: { model: 'claude-opus-4-8', provider: 'anthropic' },
30
+ sonnet: { model: 'claude-sonnet-5', provider: 'anthropic' },
31
+ // ⭐ 티어 표를 따른다(옆 luna 와 같은 형태) — 하드코딩이면 표를 바꿔도 «안 따라온다»(2026-08-18 대표 4.6 재편)
32
+ grok: { model: lookupLlmTierSpec('grok', 'best').model, provider: 'grok' },
33
+ };
34
+
35
+ export const MODEL_CODE_NAMES: readonly string[] = Object.keys(MODEL_TARGETS);
36
+
37
+ export interface HumanModelChoice {
38
+ readonly word: '빠름' | '보통' | '깊음';
39
+ readonly english: 'fast' | 'normal' | 'deep';
40
+ readonly tier: ModelTier;
41
+ }
42
+
43
+ export const HUMAN_MODEL_CHOICES: readonly HumanModelChoice[] = [
44
+ { word: '빠름', english: 'fast', tier: 'budget' },
45
+ { word: '보통', english: 'normal', tier: 'better' },
46
+ { word: '깊음', english: 'deep', tier: 'best' },
47
+ ];
48
+
49
+ export const HUMAN_MODEL_WORDS: readonly string[] = HUMAN_MODEL_CHOICES.map(({ word }) => word);
50
+
51
+ export type ModelChoiceResolution =
52
+ | { readonly kind: 'human'; readonly word: HumanModelChoice['word']; readonly tier: ModelTier; readonly target: ModelTarget; readonly reasoningLevel?: ReasoningLevel }
53
+ | { readonly kind: 'code'; readonly name: string; readonly target: ModelTarget }
54
+ /** A human word while the provider is `auto` — there is no single ladder to read. */
55
+ | { readonly kind: 'needs-provider'; readonly word: HumanModelChoice['word'] }
56
+ | { readonly kind: 'unknown'; readonly arg: string };
57
+
58
+ /** Resolve a `/model` argument against the provider that is active right now. */
59
+ export function resolveModelChoice(rawArg: string, currentProvider: LLMProviderName): ModelChoiceResolution {
60
+ const arg = rawArg.trim().toLowerCase();
61
+ const human = HUMAN_MODEL_CHOICES.find(({ word, english }) => arg === word || arg === english);
62
+ if (human) {
63
+ if (currentProvider === 'auto') return { kind: 'needs-provider', word: human.word };
64
+ const spec = lookupLlmTierSpec(currentProvider, human.tier);
65
+ return {
66
+ kind: 'human', word: human.word, tier: human.tier, target: { model: spec.model, provider: currentProvider },
67
+ ...(spec.reasoningLevel ? { reasoningLevel: spec.reasoningLevel } : {}),
68
+ };
69
+ }
70
+ const target = MODEL_TARGETS[arg];
71
+ if (target) return { kind: 'code', name: arg, target };
72
+ return { kind: 'unknown', arg };
73
+ }
74
+
75
+ /**
76
+ * The human word for (`model`, `reasoning`) on `provider`: an exact tier match first; without a reasoning
77
+ * level (or no exact match) only a model that names a single tier counts — an ambiguous model gets no word.
78
+ */
79
+ export function humanWordForModel(
80
+ provider: LLMProviderName,
81
+ model: string | undefined | null,
82
+ reasoning?: ReasoningLevel,
83
+ ): HumanModelChoice['word'] | undefined {
84
+ if (!model || provider === 'auto') return undefined;
85
+ const sameModel = HUMAN_MODEL_CHOICES.filter(({ tier }) => lookupLlmTierSpec(provider, tier).model === model);
86
+ if (reasoning) {
87
+ const exact = sameModel.find(({ tier }) => {
88
+ const level = lookupLlmTierSpec(provider, tier).reasoningLevel;
89
+ return level === undefined || level === reasoning;
90
+ });
91
+ if (exact) return exact.word;
92
+ }
93
+ return sameModel.length === 1 ? sameModel[0]!.word : undefined;
94
+ }
95
+
96
+ function currentLabel(provider: LLMProviderName, model: string | undefined | null, reasoning?: ReasoningLevel): string {
97
+ return humanWordForModel(provider, model, reasoning) ?? (model?.trim() || '기본 모델');
98
+ }
99
+
100
+ /** `/model` with no argument: the current choice, then how to choose. The second line is muted by the caller. */
101
+ export function modelOverviewLines(
102
+ provider: LLMProviderName,
103
+ model: string | undefined | null,
104
+ reasoning?: ReasoningLevel,
105
+ ): [string, string] {
106
+ const word = humanWordForModel(provider, model, reasoning);
107
+ const now = word ? `${word} (${model})` : (model?.trim() || '기본 모델');
108
+ return [
109
+ `지금: ${now} · 고르기: /model ${HUMAN_MODEL_WORDS.join(' | ')}`,
110
+ ` 코드명도 됨: ${MODEL_CODE_NAMES.join(' · ')} · provider ${provider} · 생각 깊이: /reasoning`,
111
+ ];
112
+ }
113
+
114
+ /** Confirmation after a switch: «보통 → 깊음 (<model>)». */
115
+ export function modelChangeLine(
116
+ before: { provider: LLMProviderName; model: string | undefined | null; reasoning?: ReasoningLevel },
117
+ resolution: Extract<ModelChoiceResolution, { kind: 'human' | 'code' }>,
118
+ ): string {
119
+ const after = resolution.kind === 'human'
120
+ ? resolution.word
121
+ : humanWordForModel(resolution.target.provider, resolution.target.model) ?? resolution.name;
122
+ const reasoningNote = resolution.kind === 'human' && resolution.reasoningLevel ? ` · 생각 ${resolution.reasoningLevel}` : '';
123
+ const providerNote = resolution.target.provider === before.provider ? '' : ` · provider ${resolution.target.provider}`;
124
+ return `모델: ${currentLabel(before.provider, before.model, before.reasoning)} → ${after} (${resolution.target.model}${reasoningNote})${providerNote}`;
125
+ }
@@ -65,7 +65,9 @@ import { detectCard, runCardFollowup, NotACardError } from './card-followup/core
65
65
  import { localOcrText } from './telegram-card-followup.js';
66
66
  import type { runGraph } from './graph-runner/runner.js';
67
67
  import { readSlashContextNow, renderTelegramNow } from './context-bus/context-now-surfaces.js';
68
- import type { ContextNowDeps } from './context-bus/context-now.js';
68
+ import { contextNow, type ContextNowDeps } from './context-bus/context-now.js';
69
+ import { createContextFirstGate, renderContextFirst } from './context-bus/context-first.js';
70
+ import { telegramLoopsStatus, type LoopsSources } from './telegram-loops-command.js';
69
71
  import { botUnavailableSlashReply, deriveTuiSlashAvailability, type TuiSlashAvailability } from './maturity/tui-slash-availability.js';
70
72
 
71
73
  const SLASH_FOCUS_TURNS_DEFAULT = 8;
@@ -143,6 +145,9 @@ export interface DiscordSelfMessageDeps {
143
145
  log?: (msg: string) => void;
144
146
  cardFollowupDeps?: { ocrText?: (path: string) => Promise<string | null>; runGraph?: typeof runGraph; rootDir?: () => string };
145
147
  nowDeps?: ContextNowDeps;
148
+ /** Override the clock for the context-first gate in isolated message-path tests. */
149
+ contextFirstNow?: () => number;
150
+ loopsSources?: LoopsSources;
146
151
  /** Override the TUI slash projection for isolated message-path tests. */
147
152
  tuiSlashAvailability?: readonly TuiSlashAvailability[];
148
153
  }
@@ -151,6 +156,15 @@ export interface DiscordSelfMessageDeps {
151
156
  export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessageHandler {
152
157
  const log = deps.log ?? ((): void => {});
153
158
  const cfg = deps.userConfig;
159
+ const contextFirstGate = createContextFirstGate({ now: deps.contextFirstNow ?? Date.now });
160
+ const maybeSendContextFirst = async (channelId: string): Promise<void> => {
161
+ if (!contextFirstGate.take(channelId) || cfg.discord?.contextFirst === false) return;
162
+ try {
163
+ await deps.getBot()?.sendMessage(channelId, renderContextFirst(contextNow, deps.nowDeps, renderTelegramNow));
164
+ } catch (err) {
165
+ log(`discord context-first failed: ${err instanceof Error ? err.message : String(err)}`);
166
+ }
167
+ };
154
168
  // C2+ (2026-07-12 dogfood): 디스코드 첨부는 메시지 단위라 "사진 먼저,
155
169
  // !cdx 명령은 다음 메시지" 패턴에서 위임 턴이 빈손이 된다. 채널별로
156
170
  // 마지막 첨부를 기억해 두고, 첨부 없는 위임 명령이 2분 내 같은
@@ -359,6 +373,7 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
359
373
 
360
374
  return async (ctx, streamer) => {
361
375
  if (deps.channelScope && ctx.channelId !== deps.channelScope) return undefined;
376
+ contextFirstGate.note(ctx.channelId);
362
377
  // Voice text commands pass the guild gate for the voice adapter's
363
378
  // sake — never route them into the LLM turn (legacy behavior).
364
379
  if (/^\/voice-(join|leave|status)\b/.test(ctx.text.trim())) return undefined;
@@ -367,6 +382,8 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
367
382
  const args = nowCommand[1]?.trim().split(/\s+/).filter(Boolean) ?? [];
368
383
  return renderTelegramNow(readSlashContextNow(args, deps.nowDeps));
369
384
  }
385
+ if (/^[/!]loops$/.test(ctx.text.trim())) return telegramLoopsStatus([], deps.loopsSources);
386
+ if (/^[/!]loops\s+\S/.test(ctx.text.trim())) return telegramLoopsStatus(['extra'], deps.loopsSources);
370
387
  if (!ctx.text.trim() && ctx.attachments.length === 1 && ctx.attachments[0]!.contentType?.startsWith('image/')) {
371
388
  const photo = ctx.attachments[0]!;
372
389
  if (detectCard({ width: photo.width, height: photo.height, ocrText: null }).decision !== 'skip') {
@@ -408,6 +425,8 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
408
425
  const unavailable = botUnavailableSlashReply(entry, 'discord');
409
426
  if (unavailable) return unavailable;
410
427
  }
428
+ const cmd = parseDiscordAcpCommand(ctx.text);
429
+ if (!cmd && !/^[!/]/.test(ctx.text.trim())) await maybeSendContextFirst(ctx.channelId);
411
430
  log(`◀ ${ctx.userName ?? ctx.userId}: ${ctx.text.slice(0, 80)}${ctx.attachments.length ? ` (+첨부 ${ctx.attachments.length})` : ''}`);
412
431
  debug.log('discord.self.turn', 'inbound', { channelId: ctx.channelId, chars: ctx.text.length, attachments: ctx.attachments.length });
413
432
  // C2+ — 첨부 기억(사진만 먼저 올리는 패턴 지원). 명령 여부와 무관.
@@ -416,7 +435,6 @@ export function buildDiscordSelfOnMessage(deps: DiscordSelfMessageDeps): DcMessa
416
435
  const delegationKey = delegationChatKey('dc', ctx.channelId);
417
436
 
418
437
  // 1) Explicit text commands — /cc·/cdx·/gem delegate, /brain exits.
419
- const cmd = parseDiscordAcpCommand(ctx.text);
420
438
  if (cmd?.kind === 'brain') {
421
439
  clearActiveDelegation(delegationKey);
422
440
  return '🧠 브레인 모드로 복귀했습니다 (active delegation 해제).';
@@ -48,6 +48,7 @@ export const ELANOUS_SLASH_COMMANDS: readonly SlashCommandSchema[] = [
48
48
  { name: 'now', description: '지금 판·칸·결정·자리·최근 맥락 보기', options: [
49
49
  { name: 'topic', description: '좁혀 볼 주제 (선택)', type: 3, required: false },
50
50
  ] },
51
+ { name: 'loops', description: '루프·크론 현황 (늦음·실패 먼저)' },
51
52
  { name: 'sessions', description: '바인딩된 세션 목록' },
52
53
  { name: 'new', description: '새 세션으로 시작 (이전 대화 보존)' },
53
54
  { name: 'attach', description: '이 채널을 다른 세션에 연결', options: [