elanous 0.2.13 → 0.2.14

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 (445) 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/chunks/{7334.a6679d9168f37aa8.js → 1268.0840ff91e083fcbd.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/1307-6e7e0377c1db0bdc.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/1826-5d279da64bbbc2de.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{9656-c1540088f5859c80.js → 1844-9ec4b1d12f6e80eb.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/1866-e7a6999f25548b30.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/{390.c373e31b3b9d0c41.js → 1948.15f356b15a625597.js} +2 -2
  9. package/apps/pwa/out/_next/static/chunks/211-9c9090c59e2c5907.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2196-4700653d12a44b40.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/224-798346143007ff65.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/2699-63b73fa4d3bcf6ff.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2957-96ffab3d521fb08f.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/311.e685738f92e6e2ec.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3194-807b212fb8eae065.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/3324-d1d2979821dfa845.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/3558-bbb5ab553a0471d3.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/3916-3d02dd1fc257dd3c.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/4144-a0041c8dc3be57ac.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4554-3565eea1f1495ba7.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/{4685-c3c1c625b71accd1.js → 4614-21403aac730d4586.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/{8039-df92e08fede5de8f.js → 4795-f372490b178583fc.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5135-44008fe8b1f2aa41.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5233-bf28bd6bd7a32099.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5360-be1f1d8f9fcc9755.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5994-ae92c0de046ab9ba.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/{1452.93700483b9d4fc61.js → 6110.1297d4093fdfbb0d.js} +1 -1
  30. package/apps/pwa/out/_next/static/chunks/6158.9c0449a9a8d0064b.js +2 -0
  31. package/apps/pwa/out/_next/static/chunks/6877-7c6208705c284124.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/726.83532d69452ee24b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7290-f09c928a06c52e2d.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/{1829-b453a1b22d3999bb.js → 7327-2d69fb321ba73389.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/7395-164e238f471b5d7b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7787-02a1c0b04e25e811.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/8060-cc008cc6fedfa818.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8085-d065d0393c1760f5.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8682-7061dd4fa7bd62ed.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8827-e2f5b09a8d37e83b.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8868-10a3a4440fb0ccd8.js +25 -0
  42. package/apps/pwa/out/_next/static/chunks/{8421-1c4acfb1395a0edf.js → 8949-ecb4128a0858b911.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/9070.067c106528ec1ae6.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9103-7a3ae7f60af3b99f.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{3869-3cd46526879f454c.js → 9247-29d889b809288e65.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/935.4ddaa99fc1d1126c.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/{79f8be9e-957d68913b590b08.js → a9f2b54d-7865aba0f4d2ee49.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-c1a96669158471ec.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-7b2b4f378354869a.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-f14987d5850a31e0.js → page-1abfe3aec3be6bdc.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-1512d91c7491ed62.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-a627db94ec6904a0.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/control/page-e6b84ab07d3567d5.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/editor/page-fed69f9759d248d0.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/exec/page-15374909d8f1aebd.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/field/page-012a742f81218b01.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/healthz/route-c1a96669158471ec.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/intake/page-99effd9d893a43dc.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/layout-938de57ae0315e46.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/loops/page-160089123a40dd91.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/market/page-629e1ede482ec6eb.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-66c27608f651b866.js → page-2ce5471926761fdd.js} +1 -1
  69. package/apps/pwa/out/_next/static/chunks/app/morning/page-27e99b4e54a849d4.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-32cde713be47cddb.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e7e10cd3386222e5.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e64caff084f7ce21.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-db07eaffba0df7e3.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-78b40a344ed22f24.js → page-e1e47d8d940aba3d.js} +1 -1
  81. package/apps/pwa/out/_next/static/chunks/app/settings/page-86f91034bd5cabe5.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/setup/layout-c1a96669158471ec.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/share/page-ce4d8a1215ce53a3.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/showroom/page-58043db78ba519ae.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/tasks/page-357750dfb91bf960.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/term/{page-63d14a4d8fe1e1c1.js → page-274703bc41b71333.js} +1 -1
  89. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/app/vault/page-3b7e821dc492bdfd.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/app/workflows/page-f8da5dc6f3752276.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2f810b18d89ecba6.js +1 -0
  95. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +1 -0
  96. package/apps/pwa/out/_next/static/chunks/{5cd7e3a9-404fd6b8d0ecd44c.js → f6231284-45726550aba08fca.js} +1 -1
  97. package/apps/pwa/out/_next/static/chunks/{f48170cc-d05d28bf87a43bf4.js → f6b7c9ba.64a895b8b2fedbdc.js} +1 -1
  98. package/apps/pwa/out/_next/static/chunks/framework-5c169757551134ed.js +1 -0
  99. package/apps/pwa/out/_next/static/chunks/main-59fe95fed40c7a01.js +1 -0
  100. package/apps/pwa/out/_next/static/chunks/main-app-4ca74dbbee7057c6.js +1 -0
  101. package/apps/pwa/out/_next/static/chunks/pages/_app-13a0a886df4d97a1.js +1 -0
  102. package/apps/pwa/out/_next/static/chunks/pages/_error-6af8591cd3a3f038.js +1 -0
  103. package/apps/pwa/out/_next/static/chunks/webpack-539cde99554be20f.js +1 -0
  104. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +1 -0
  105. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +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 -1
  134. package/apps/pwa/out/inside/index.txt +22 -22
  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/loops/index.html +1 -0
  140. package/apps/pwa/out/loops/index.txt +30 -0
  141. package/apps/pwa/out/market/index.html +1 -1
  142. package/apps/pwa/out/market/index.txt +22 -22
  143. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  144. package/apps/pwa/out/missions/placeholder/index.txt +22 -22
  145. package/apps/pwa/out/morning/index.html +1 -1
  146. package/apps/pwa/out/morning/index.txt +23 -23
  147. package/apps/pwa/out/observatory/index.html +1 -1
  148. package/apps/pwa/out/observatory/index.txt +23 -23
  149. package/apps/pwa/out/ops/checklist/index.html +1 -1
  150. package/apps/pwa/out/ops/checklist/index.txt +22 -22
  151. package/apps/pwa/out/ops/release/index.html +1 -1
  152. package/apps/pwa/out/ops/release/index.txt +22 -22
  153. package/apps/pwa/out/ops/seats/index.html +1 -1
  154. package/apps/pwa/out/ops/seats/index.txt +22 -22
  155. package/apps/pwa/out/outputs/index.html +1 -1
  156. package/apps/pwa/out/outputs/index.txt +22 -22
  157. package/apps/pwa/out/reflection/index.html +1 -1
  158. package/apps/pwa/out/reflection/index.txt +23 -23
  159. package/apps/pwa/out/scheduler/index.html +1 -1
  160. package/apps/pwa/out/scheduler/index.txt +23 -23
  161. package/apps/pwa/out/sessions/index.html +1 -1
  162. package/apps/pwa/out/sessions/index.txt +22 -22
  163. package/apps/pwa/out/settings/devices/index.html +1 -1
  164. package/apps/pwa/out/settings/devices/index.txt +23 -23
  165. package/apps/pwa/out/settings/index.html +1 -1
  166. package/apps/pwa/out/settings/index.txt +23 -23
  167. package/apps/pwa/out/setup/done/index.html +1 -1
  168. package/apps/pwa/out/setup/done/index.txt +23 -23
  169. package/apps/pwa/out/setup/index.html +1 -1
  170. package/apps/pwa/out/setup/index.txt +23 -23
  171. package/apps/pwa/out/share/index.html +1 -1
  172. package/apps/pwa/out/share/index.txt +23 -23
  173. package/apps/pwa/out/showroom/index.html +1 -1
  174. package/apps/pwa/out/showroom/index.txt +22 -22
  175. package/apps/pwa/out/tasks/index.html +1 -1
  176. package/apps/pwa/out/tasks/index.txt +23 -23
  177. package/apps/pwa/out/term/index.html +1 -1
  178. package/apps/pwa/out/term/index.txt +23 -23
  179. package/apps/pwa/out/today/index.html +1 -1
  180. package/apps/pwa/out/today/index.txt +23 -23
  181. package/apps/pwa/out/trace/index.html +1 -1
  182. package/apps/pwa/out/trace/index.txt +23 -23
  183. package/apps/pwa/out/vault/index.html +1 -1
  184. package/apps/pwa/out/vault/index.txt +23 -23
  185. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  186. package/apps/pwa/out/workflows/chat-ui/index.txt +23 -23
  187. package/apps/pwa/out/workflows/index.html +1 -1
  188. package/apps/pwa/out/workflows/index.txt +23 -23
  189. package/apps/pwa/out/workspace/index.html +1 -1
  190. package/apps/pwa/out/workspace/index.txt +23 -23
  191. package/apps/pwa/out/worktrees/index.html +1 -1
  192. package/apps/pwa/out/worktrees/index.txt +23 -23
  193. package/graphs/orchestrator/orchestrator.yaml +59 -0
  194. package/graphs/orchestrator/recipes.yaml +18 -0
  195. package/graphs/release/recipes.yaml +3 -0
  196. package/graphs/release/release-loop.yaml +4 -1
  197. package/graphs/rhythm/daily.yaml +38 -0
  198. package/graphs/rhythm/recipes.yaml +18 -0
  199. package/graphs/rhythm/weekly.yaml +39 -0
  200. package/graphs/video/recipes.yaml +3 -0
  201. package/graphs/video/youtube-summary.yaml +17 -0
  202. package/package.json +1 -1
  203. package/scripts/audit-test-state-writes.ts +72 -17
  204. package/scripts/brand/check.ts +186 -0
  205. package/scripts/coord-post.sh +11 -1
  206. package/scripts/docs-cli-check.ts +23 -6
  207. package/scripts/expose-rubric-check.ts +118 -0
  208. package/scripts/expose-rubric-suggest.ts +163 -0
  209. package/scripts/heal/run-role.ts +14 -0
  210. package/scripts/hq/standby-verify.ts +11 -7
  211. package/scripts/lessons/handbook-page.ts +85 -0
  212. package/scripts/release-loop/gate-node.ts +9 -0
  213. package/scripts/release-loop/publish-node.ts +14 -1
  214. package/scripts/release-loop/unattended-release.ts +23 -1
  215. package/scripts/release-loop/vault-note-node.ts +196 -0
  216. package/scripts/release-story/draft.ts +172 -0
  217. package/scripts/rhythm/daily.ts +383 -0
  218. package/scripts/rhythm/weekly.ts +294 -0
  219. package/scripts/skills/public-check.ts +169 -0
  220. package/scripts/test-deterministic.ts +4 -8
  221. package/scripts/test-diet/effectiveness.ts +195 -0
  222. package/scripts/test-diet/lib.ts +16 -8
  223. package/scripts/test-diet/node.ts +77 -9
  224. package/scripts/ux/demo-rehearsal.ts +2 -2
  225. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  226. package/scripts/video/youtube-summary.ts +133 -0
  227. package/skills/diagram-master/SKILL.md +42 -49
  228. package/skills/diagram-master/references/pyproject.toml +4 -1
  229. package/skills/diagram-master/references/routing-guide.md +4 -2
  230. package/skills/grill-me/SKILL.md +1 -0
  231. package/skills/youtube-master/SKILL.md +13 -13
  232. package/skills/youtube-master/package-lock.json +0 -545
  233. package/skills/youtube-master/package.json +2 -5
  234. package/skills/youtube-master/scripts/main.ts +2 -2
  235. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  236. package/skills/youtube-master/src/util.ts +30 -7
  237. package/src/agent-mission/driver.ts +3 -3
  238. package/src/autopilot/build/llm-conflict-merge.ts +45 -8
  239. package/src/autopilot/build/merge-intent.ts +53 -0
  240. package/src/chat/index.ts +2 -0
  241. package/src/claims/claims-ledger.ts +232 -0
  242. package/src/claims/claims-render.ts +157 -0
  243. package/src/cli/claims-cli.ts +94 -0
  244. package/src/cli/decisions-cli.ts +35 -3
  245. package/src/cli/docs-cli.ts +2 -0
  246. package/src/cli/flow-cli.ts +11 -0
  247. package/src/cli/freeze-cli.ts +45 -0
  248. package/src/cli/lesson-cli.ts +108 -0
  249. package/src/cli/logs-cli.ts +104 -2
  250. package/src/cli/logs-model-input.ts +131 -0
  251. package/src/cli/pr-cli.ts +29 -1
  252. package/src/cli/private-ledger-commands.ts +54 -4
  253. package/src/cli/pty-drive-verdict.ts +11 -2
  254. package/src/cli/release-cli.ts +144 -21
  255. package/src/cli/rfc-status.ts +91 -0
  256. package/src/connectors/linear.ts +5 -3
  257. package/src/context-bus/condense.ts +15 -20
  258. package/src/context-bus/context-now-public.ts +86 -0
  259. package/src/context-bus/context-now-surfaces.ts +55 -2
  260. package/src/context-bus/context-now.ts +4 -3
  261. package/src/context-bus/long-term-memory.ts +92 -11
  262. package/src/dashboard/index.ts +75 -22
  263. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  264. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  265. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  266. package/src/decisions/decision-ledger.ts +40 -1
  267. package/src/discord/sprint21-runtime.ts +16 -0
  268. package/src/discord-self-message.ts +8 -0
  269. package/src/discord-slash-wire.ts +31 -2
  270. package/src/domains/outbound-alert.ts +1 -1
  271. package/src/elanous-config-dir.ts +29 -4
  272. package/src/execution-loop/launch-gate.ts +29 -14
  273. package/src/flow/cards.ts +239 -0
  274. package/src/graph-runner/graph-tick.ts +4 -0
  275. package/src/graph-runner/runner.ts +30 -5
  276. package/src/harness/harness-cli-command.ts +152 -27
  277. package/src/harness/harness-incidents.ts +43 -12
  278. package/src/harness/harness-pod-dispatch.ts +2 -0
  279. package/src/harness/harness-queue-child.ts +3 -3
  280. package/src/harness/harness-queue.ts +216 -28
  281. package/src/harness/helper-scan.ts +139 -0
  282. package/src/harness/model-input-observation.ts +37 -0
  283. package/src/hq/hq.ts +1 -1
  284. package/src/hq/promote.ts +301 -0
  285. package/src/index.ts +83 -21
  286. package/src/intake-plane/linear-wish.ts +39 -11
  287. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  288. package/src/intake-plane/wish-card.ts +53 -2
  289. package/src/intake-plane/wish-folder.ts +5 -2
  290. package/src/intake-plane/wish-reply.ts +72 -0
  291. package/src/lessons/lesson-import.ts +106 -0
  292. package/src/lessons/lesson-ledger.ts +206 -0
  293. package/src/loops/activity.ts +203 -0
  294. package/src/loops/budget.ts +32 -0
  295. package/src/loops/checker.ts +5 -3
  296. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  297. package/src/loops/loop-cli.ts +36 -4
  298. package/src/loops/neighbors.ts +40 -0
  299. package/src/loops/observe.ts +10 -0
  300. package/src/loops/orchestrator/author-depth.ts +164 -0
  301. package/src/loops/orchestrator/author-input-check.ts +77 -0
  302. package/src/loops/orchestrator/author-ledger.ts +230 -0
  303. package/src/loops/orchestrator/finish-rate.ts +175 -0
  304. package/src/loops/orchestrator/overlap.ts +51 -12
  305. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  306. package/src/loops/orchestrator/tick.ts +302 -0
  307. package/src/loops/orchestrator/traffic.ts +18 -3
  308. package/src/loops/registry.ts +454 -9
  309. package/src/loops/status-rows.ts +118 -0
  310. package/src/loops/verdict.ts +17 -5
  311. package/src/maturity/bot-slash-catalog.ts +66 -0
  312. package/src/maturity/feature-maturity.ts +7 -4
  313. package/src/nexus/api/dashboard.ts +6 -1
  314. package/src/nexus/api/http-server.ts +18 -2
  315. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  316. package/src/nexus/api/schedules-read.ts +5 -1
  317. package/src/nexus/api/task-cards-api.ts +44 -5
  318. package/src/nexus/api/workflows.ts +2 -1
  319. package/src/release-loop/auto-start.ts +48 -0
  320. package/src/release-loop/checklist.ts +13 -4
  321. package/src/release-loop/feature-store.ts +28 -16
  322. package/src/release-loop/landing-freeze.ts +121 -0
  323. package/src/release-loop/placement.ts +242 -0
  324. package/src/release-loop/release-schedule.ts +3 -1
  325. package/src/seat-dispatch/ceo-commands.ts +2 -2
  326. package/src/seat-dispatch/seat-ask.ts +16 -3
  327. package/src/seat-dispatch/seat-questions.ts +58 -0
  328. package/src/seat-loop/seat-loop.ts +406 -31
  329. package/src/self-dev/dev-pipeline.ts +6 -2
  330. package/src/self-dev/draft-sweep.ts +76 -0
  331. package/src/self-dev/run-store.ts +73 -1
  332. package/src/self-dev/run-supervisor.ts +8 -0
  333. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  334. package/src/self-implement/budget-gate.ts +29 -0
  335. package/src/self-implement/expose-gate.ts +47 -0
  336. package/src/self-implement/frozen-merges.ts +294 -0
  337. package/src/self-implement/gate-cli.ts +10 -4
  338. package/src/self-implement/gate-scope.ts +46 -9
  339. package/src/self-implement/goal-archive.ts +222 -0
  340. package/src/self-implement/goal-lookup.ts +147 -0
  341. package/src/self-implement/heal-intake.ts +79 -0
  342. package/src/self-implement/host-regate.ts +25 -2
  343. package/src/self-implement/orchestrator.ts +114 -10
  344. package/src/self-implement/revert-guard.ts +62 -0
  345. package/src/self-implement/run-ledger.ts +1 -1
  346. package/src/self-implement/seams.ts +28 -8
  347. package/src/session/index.ts +2 -0
  348. package/src/task-cards/card-cli.ts +10 -2
  349. package/src/task-cards/card-store.ts +10 -6
  350. package/src/task-orchestrator/surfaces/pod-lease.ts +5 -2
  351. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  352. package/src/task-orchestrator/surfaces/self-implement-pod.ts +80 -16
  353. package/src/telegram-commands.ts +27 -3
  354. package/src/telegram-loops-command.ts +44 -0
  355. package/src/user-config.ts +120 -16
  356. package/src/version/packed-revision.json +1 -1
  357. package/src/workflow-runtime/storage.ts +31 -13
  358. package/apps/pwa/out/_next/static/D51l8jUBplJrIpWvENsDK/_buildManifest.js +0 -1
  359. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  360. package/apps/pwa/out/_next/static/chunks/1394-931c9827067b2276.js +0 -1
  361. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  362. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  363. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  364. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  365. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  366. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  367. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  368. package/apps/pwa/out/_next/static/chunks/3992-fe5c06d6b31f8b25.js +0 -25
  369. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  370. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  371. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  372. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  373. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +0 -1
  374. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  375. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  376. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  388. package/apps/pwa/out/_next/static/chunks/8605-9da4678f8a310d18.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-e49b9b76c565a0b1.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/app/chat/page-4c1dbb21861beab2.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/app/control/page-f6bc66b919b2ea7d.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/app/design-check/page-60603c1e056c9b25.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/app/editor/page-a0ea8f5a2341ee6b.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/app/intake/page-31012264da8a341f.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/app/layout-50309aeb0bdd554a.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/app/market/page-0696ee3fac9fe56c.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/app/morning/page-6ddd2109b7fc3a80.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/app/observatory/page-7a2bf830038a2264.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1f327f18694fabca.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-e5cef2fe6e4028df.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cf596128d2b420ba.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/app/outputs/page-f672536f14f0969f.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-29a709616df9b050.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/app/sessions/page-fee842d7c2151129.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-a011dd5b0bff29c5.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/setup/page-d92e32d51ab5157e.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/share/page-5f21aed6932ef375.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/showroom/page-1e1a53946804debf.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/tasks/page-a1833cd43d832601.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/today/page-2a511d320f762e06.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/trace/page-1c3e03f88dac1e21.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/vault/page-c4d5289c2ea97a89.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-b91f8952deaeaf27.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71b43a2588489987.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  444. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  445. /package/apps/pwa/out/_next/static/{D51l8jUBplJrIpWvENsDK → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
@@ -0,0 +1,86 @@
1
+ import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { checkBrand } from '../../scripts/brand/check.js';
5
+ import type { ContextFact, ContextNowAnswer } from './context-now.js';
6
+
7
+ const internalItem = (count: number) => `내부 항목 ${count}개`;
8
+ const seatNames: Record<string, string> = { OP: '운영', TC: '기술', MK: '마케팅', UX: '사용자 경험' };
9
+
10
+ /** Public copy only: no raw ledger identifiers or private sources in the rendered answer. */
11
+ export function filterPublicDemoContext(answer: ContextNowAnswer): ContextNowAnswer {
12
+ const dir = mkdtempSync(join(tmpdir(), 'context-now-public-'));
13
+ const file = join(dir, 'text.txt');
14
+ try {
15
+ const flagged = (texts: string[]): Set<number> => {
16
+ if (!texts.length) return new Set();
17
+ writeFileSync(file, texts.map(text => text.replace(/[\r\n]+/g, ' ')).join('\n') + '\n');
18
+ const result = checkBrand('public-docs', [file]);
19
+ if (result.missing) throw new Error('brand rules missing');
20
+ return new Set(result.findings.map(finding => finding.line - 1));
21
+ };
22
+ const publicText = (value: string): string => value
23
+ .replace(/\bhttps?:\/\/[^\s)\]}>]+/gi, '공개 주소')
24
+ .replace(/\b(?:[\w-]+\.)+[a-z]{2,}(?::\d+)?(?:\/[^\s)\]}>]*)?/gi, '공개 주소')
25
+ .replace(/(?:\/Users\/|\/home\/|\/root(?=[\/\s)\]}>]|$)|~(?=\/)|\$HOME(?=\/)|\$\{HOME\}(?=\/))[^\s)\]}>]*/g, '공개 경로')
26
+ .replace(/\b[\w.-]+@(?!공개)[a-z][\w-]*(?=\b|:\d)/gi, '공개 호스트')
27
+ .replace(/\b(?:run[-_][a-z0-9-]{8,}|[a-f0-9]{8}-[a-f0-9-]{16,})\b/gi, '공개 실행')
28
+ .replace(/\bPR[\s-]*#?\d+\b|#\d+\b/gi, '공개 변경')
29
+ .replace(/\b(?:OP|TC|MK|UX)\b/g, seat => seatNames[seat]!)
30
+ .replace(/\b(host(?:name)?|machine|server)\s*([:=])\s*([a-z][\w-]*)\b/gi, '$1$2 공개 호스트')
31
+ .replace(/\b(host(?:name)?|machine|server|ssh|scp|rsync|ping|mosh)\s+((?:to|from|into|on|at)\s+)?(?!공개)([a-z][\w-]*)(?=\b|:\d)/gi, '$1 $2공개 호스트')
32
+ .replace(/\b((?:deploy(?:ed|ing)?|connect(?:ed|ing)?)\s+(?:at|on|to|from))\s+[a-z][\w-]*(?=\b|:\d)/gi, '$1 공개 호스트')
33
+ .replace(/\b(at|on|to|from)\s+(?:msb\d+|localhost|[a-z][\w-]*\d+)(?=\b|:\d)/gi, '$1 공개 호스트')
34
+ .replace(/\b(?:msb\d+|node-c|cloud-vm|localhost)\b/gi, '공개 호스트');
35
+ const safe = (value: string, fallback: string): string => {
36
+ const text = publicText(value);
37
+ return flagged([text]).size ? fallback : text;
38
+ };
39
+ const source = (value: string) => value.startsWith('elanous://') ? '내부 원장' : safe(value, '공개 출처');
40
+ const titleFacts = answer.facts.filter((fact): fact is Extract<ContextFact, { kind: 'cell' | 'decision' }> =>
41
+ fact.kind === 'cell' || fact.kind === 'decision');
42
+ const privateTitles = flagged(titleFacts.map(fact => fact.title));
43
+ const privateFacts = new Set(titleFacts.filter((_, index) => privateTitles.has(index)));
44
+ const privateIds = new Set(titleFacts.filter(fact => privateFacts.has(fact)).map(fact => fact.id));
45
+ const hasOpenPrivateDecision = titleFacts.some(fact => fact.kind === 'decision' && privateFacts.has(fact) && fact.status === 'open');
46
+ const publicField = (value: string, fallback: string) => safe(value.replace(/\b(?:K|D)\d+\b/g, id =>
47
+ privateIds.has(id) ? '내부 항목' : id), fallback);
48
+ const collapsed = { cell: 0, decision: 0 };
49
+ const facts: ContextFact[] = [];
50
+ for (const fact of answer.facts) {
51
+ if (fact.kind === 'cell' || fact.kind === 'decision') {
52
+ if (privateFacts.has(fact)) {
53
+ collapsed[fact.kind]++;
54
+ continue;
55
+ }
56
+ facts.push(fact.kind === 'cell'
57
+ ? { ...fact, version: safe(fact.version, '공개 판'), id: safe(fact.id, '공개 항목'), title: safe(fact.title, '공개 항목'),
58
+ status: safe(fact.status, '진행 중'), owner: fact.owner ? safe(fact.owner, '공개 담당') : null, source: source(fact.source) }
59
+ : { ...fact, id: safe(fact.id, '공개 항목'), title: safe(fact.title, '공개 항목'),
60
+ status: safe(fact.status, '진행 중'), source: source(fact.source) });
61
+ } else if (fact.kind === 'seat') {
62
+ facts.push({ ...fact, seat: safe(fact.seat, '공개 담당'), status: safe(fact.status, '진행 중'),
63
+ id: fact.id ? (privateIds.has(fact.id) ? '내부 항목' : safe(fact.id, '공개 항목')) : null,
64
+ title: fact.title ? (privateIds.has(fact.id ?? '') ? '내부 항목' : safe(fact.title, '공개 항목')) : null, source: source(fact.source) });
65
+ } else facts.push({ ...fact, version: safe(fact.version, '공개 판'), source: source(fact.source) });
66
+ }
67
+ for (const kind of ['cell', 'decision'] as const) {
68
+ if (!collapsed[kind]) continue;
69
+ const title = internalItem(collapsed[kind]);
70
+ facts.push(kind === 'cell'
71
+ ? { kind, version: '공개 판', id: '내부 항목', title, status: '진행 중', owner: null, source: '내부 원장' }
72
+ : { kind, id: '내부 항목', title, status: hasOpenPrivateDecision ? 'open' : '진행 중', dueAt: null, source: '내부 원장' });
73
+ }
74
+ return {
75
+ at: answer.at,
76
+ topic: answer.topic === null ? null : publicField(answer.topic, '공개 주제'),
77
+ facts,
78
+ events: answer.events.map(event => ({ ...event, kind: safe(event.kind, '소식'),
79
+ summary: publicField(event.summary, '공개 소식'), source: source(event.source) })),
80
+ guide: answer.guide.map(text => publicField(text, '공개 안내')),
81
+ hiddenCount: collapsed.cell + collapsed.decision,
82
+ };
83
+ } finally {
84
+ rmSync(dir, { recursive: true, force: true });
85
+ }
86
+ }
@@ -1,4 +1,11 @@
1
1
  import { contextNow, type ContextFact, type ContextNowAnswer, type ContextNowDeps } from './context-now.js';
2
+ import { filterPublicDemoContext } from './context-now-public.js';
3
+
4
+ export type ContextNowAudience = 'operator' | 'user' | 'public-demo';
5
+ const publicSeatNames: Record<string, string> = { OP: '운영', MK: '마케팅', TC: '기술', UX: '사용자 경험' };
6
+ const seatOrder = ['OP', 'MK', 'TC', 'UX'] as const;
7
+ const forAudience = (answer: ContextNowAnswer, audience: ContextNowAudience): ContextNowAnswer =>
8
+ audience === 'public-demo' ? filterPublicDemoContext(answer) : answer;
2
9
 
3
10
  export function readSlashContextNow(args: string[], deps?: ContextNowDeps): ContextNowAnswer {
4
11
  return contextNow({ topic: args.join(' ').trim() }, deps);
@@ -30,7 +37,8 @@ function factText(fact: ContextFact): string {
30
37
  }
31
38
  }
32
39
 
33
- export function renderTelegramNow(answer: ContextNowAnswer): string {
40
+ export function renderTelegramNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): string {
41
+ const answer = forAudience(input, audience);
34
42
  const rows = [
35
43
  ['판', answer.facts.filter(f => f.kind === 'version')],
36
44
  ['칸', answer.facts.filter(f => f.kind === 'cell')],
@@ -42,7 +50,8 @@ export function renderTelegramNow(answer: ContextNowAnswer): string {
42
50
  return lines.map(line => line.replace(/\s+/g, ' ').trim()).join('\n');
43
51
  }
44
52
 
45
- export function renderTuiNow(answer: ContextNowAnswer): string[] {
53
+ export function renderTuiNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): string[] {
54
+ const answer = forAudience(input, audience);
46
55
  const shortText = (text: string) => {
47
56
  const characters = Array.from(text);
48
57
  return characters.length > 100 ? `${characters.slice(0, 100).join('')}…` : text;
@@ -69,6 +78,50 @@ export function renderTuiNow(answer: ContextNowAnswer): string[] {
69
78
  ];
70
79
  }
71
80
 
81
+ /** Voice keeps the source-labelled ledger view out of the spoken transcript. */
82
+ export function renderVoiceNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): string {
83
+ const answer = forAudience(input, audience);
84
+ const spoken = (text: string) => text.replace(/\s+/g, ' ').replace(/[.!?。!?]+/g, ' ').trim();
85
+ const sentence = (text: string) => `${Array.from(spoken(text)).slice(0, 119).join('').trimEnd()}。`;
86
+ const seats = seatOrder.flatMap(seat => {
87
+ const label = audience === 'public-demo' ? publicSeatNames[seat]! : seat;
88
+ const latest = answer.facts.filter((fact): fact is Extract<ContextFact, { kind: 'seat' }> => fact.kind === 'seat' && (fact.seat === label || (audience === 'public-demo' && fact.seat === seat)))
89
+ .sort((a, b) => Date.parse(b.at) - Date.parse(a.at))[0];
90
+ if (!latest) return [];
91
+ return [`${label} ${Array.from(spoken(latest.title ?? latest.status)).slice(0, 18).join('')}`];
92
+ });
93
+ const first = sentence(seats.length ? `지금 ${seats.join(', ')} 중입니다` : '지금 자리 현황은 확인되지 않았습니다');
94
+ const decision = answer.facts.find(fact => fact.kind === 'decision' && fact.status === 'open');
95
+ const cell = answer.facts.find(fact => fact.kind === 'cell' && fact.status !== 'done');
96
+ const shortTitle = (text: string) => Array.from(spoken(text)).slice(0, 75).join('').trimEnd();
97
+ const next = decision?.kind === 'decision' ? `다음은 ${shortTitle(decision.title)} 결정이 필요합니다`
98
+ : cell?.kind === 'cell' ? `다음은 ${shortTitle(cell.title)} 칸을 확인해야 합니다`
99
+ : '다음에 필요한 결정이나 미완료 칸은 확인되지 않았습니다';
100
+ return `${first} ${sentence(next)}`;
101
+ }
102
+
103
+ /** A compact card retains the same source-labelled public facts as the other /now surfaces. */
104
+ export function renderCardNow(input: ContextNowAnswer, audience: ContextNowAudience = 'operator'): {
105
+ title: string;
106
+ sections: Array<{ label: string; items: Array<{ text: string; source: string }> }>;
107
+ } {
108
+ const answer = forAudience(input, audience);
109
+ const kinds = [
110
+ ['판', 'version'], ['칸', 'cell'], ['결정', 'decision'], ['자리', 'seat'],
111
+ ] as const;
112
+ return {
113
+ title: `지금${answer.topic ? ` · ${answer.topic}` : ''} (${answer.at})`,
114
+ sections: [
115
+ ...kinds.map(([label, kind]) => ({
116
+ label,
117
+ items: answer.facts.filter(fact => fact.kind === kind).map(fact => ({ text: factText(fact), source: fact.source })),
118
+ })),
119
+ { label: '최근', items: answer.events.map(event => ({ text: event.summary, source: event.source })) },
120
+ { label: '안내', items: answer.guide.map(text => ({ text, source: '' })) },
121
+ ],
122
+ };
123
+ }
124
+
72
125
  export function telegramNowSlash(args: string[], deps?: ContextNowDeps): string {
73
126
  return renderTelegramNow(readSlashContextNow(args, deps));
74
127
  }
@@ -5,6 +5,7 @@ import { listChecklist, devVersion, type Checklist } from '../release-loop/check
5
5
  import { effectiveInstanceRoot } from '../instance/resolve.js';
6
6
  import { seatDay, seatLedgerPath, type SeatEntry } from '../seat-loop/seat-loop.js';
7
7
  import { listCoordEvents, type CoordEvent } from './coord-events.js';
8
+ import { filterPublicDemoContext } from './context-now-public.js';
8
9
 
9
10
  export type ContextFact =
10
11
  | { kind: 'version'; version: string; source: string }
@@ -12,7 +13,7 @@ export type ContextFact =
12
13
  | { kind: 'decision'; id: string; title: string; status: string; dueAt: string | null; source: string }
13
14
  | { kind: 'seat'; seat: string; at: string; status: string; id: string | null; title: string | null; source: string };
14
15
  export type ContextEvent = { at: string; kind: string; summary: string; source: string };
15
- export type ContextNowAnswer = { at: string; topic: string | null; facts: ContextFact[]; events: ContextEvent[]; guide: string[] };
16
+ export type ContextNowAnswer = { at: string; topic: string | null; facts: ContextFact[]; events: ContextEvent[]; guide: string[]; hiddenCount?: number };
16
17
 
17
18
  export interface ContextNowDeps {
18
19
  now?: () => Date;
@@ -37,7 +38,7 @@ function todaySeatEntries(now: Date): Array<{ entry: SeatEntry; source: string }
37
38
  }
38
39
 
39
40
  /** Read a bounded, source-labelled view of current ledgers, never a transcript or decision body. */
40
- export function contextNow(options: { topic?: string; limit?: number } = {}, deps: ContextNowDeps = {}): ContextNowAnswer {
41
+ export function contextNow(options: { topic?: string; limit?: number; audience?: 'operator' | 'user' | 'public-demo' } = {}, deps: ContextNowDeps = {}): ContextNowAnswer {
41
42
  const now = (deps.now ?? (() => new Date()))();
42
43
  const at = `${now.toISOString().slice(0, 16)}:00.000Z`;
43
44
  const topic = options.topic?.trim() || null;
@@ -100,5 +101,5 @@ export function contextNow(options: { topic?: string; limit?: number } = {}, dep
100
101
  .map(event => `📌 ${event.summary} — ${event.source}`);
101
102
  const answer = { at, topic, facts: boundedFacts, events: selectedEvents, guide };
102
103
  debug.log('context.now', 'answer', { topic, facts: answer.facts, events: answer.events });
103
- return answer;
104
+ return options.audience === 'public-demo' ? filterPublicDemoContext(answer) : answer;
104
105
  }
@@ -1,5 +1,8 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import { existsSync } from 'node:fs';
1
3
  import { debug, redactSecretText } from '../debug/log.js';
2
4
  import { DecisionLedger, type DecisionEntry } from '../decisions/decision-ledger.js';
5
+ import { surfaceEventsDbPath } from '../domains/surface-events.js';
3
6
  import { listCoordEvents, type CoordEvent } from './coord-events.js';
4
7
 
5
8
  export interface MemoryItem {
@@ -27,23 +30,50 @@ export interface MemorySummary {
27
30
  claim?: { key: string; value: string };
28
31
  }
29
32
 
33
+ type CondenseEvent = CoordEvent & { surface?: string };
34
+
30
35
  export interface MemoryDeps {
31
36
  summarize: (source: MemorySource) => Promise<MemorySummary>;
32
- events?: (since: string) => CoordEvent[];
37
+ events?: (since: string) => CondenseEvent[];
33
38
  decisions?: () => DecisionEntry[];
34
39
  }
35
40
 
36
- export type CondenseSkipReason = 'no-seat' | 'no-project' | 'no-topic' | 'no-summary' | 'bad-link' | 'bad-claim' | 'summarize-failed';
41
+ export type CondenseSkipReason = 'no-seat' | 'no-project' | 'no-topic' | 'no-summary' | 'bad-link' | 'bad-claim' | 'no-claim' | 'summarize-failed';
37
42
  export type CondenseSkipped = Partial<Record<CondenseSkipReason, number>>;
43
+ export interface FunnelCount { total: number; bySeat: Record<string, number> }
44
+ export interface CondenseFunnel {
45
+ read: FunnelCount & { bySurface: Record<string, number> };
46
+ sources: FunnelCount & { folded: FunnelCount; droppedHarnessChild: FunnelCount; noSeat: FunnelCount };
47
+ summarized: FunnelCount & { accepted: FunnelCount; summarizeFailed: FunnelCount;
48
+ skipped: Partial<Record<CondenseSkipReason, FunnelCount>> };
49
+ collapsed: FunnelCount;
50
+ cards: FunnelCount & { carried: FunnelCount; supersededByPrevious: FunnelCount };
51
+ }
38
52
  export interface CondenseWindowReport {
39
53
  cards: MemoryItem[];
54
+ funnel: CondenseFunnel;
40
55
  skipped: CondenseSkipped;
41
56
  folded: number;
57
+ droppedHarnessChild: number;
42
58
  }
43
59
 
44
60
  const DAY_MS = 86_400_000;
45
61
  const oneLine = (text: string) => redactSecretText(text.split(/\r?\n/, 1)[0] ?? '').trim();
46
62
 
63
+ /** Keep the reader's event selection unchanged; attach surfaces from the same read-only snapshot. */
64
+ export function readCondenseEvents(since: string): CondenseEvent[] {
65
+ const path = surfaceEventsDbPath();
66
+ if (!existsSync(path)) return [];
67
+ const db = new Database(path, { readonly: true, strict: true });
68
+ try {
69
+ const surfaces = db.prepare(`SELECT id, surface FROM events
70
+ WHERE surface IN ('coord:channel', 'context:external', 'context:session') AND direction='outbound' AND ts>=?`)
71
+ .all(since) as Array<{ id: string; surface: string }>;
72
+ const byEvent = new Map(surfaces.map(row => [row.id, row.surface]));
73
+ return listCoordEvents({ since }, { db }).map(event => ({ ...event, surface: byEvent.get(event.id) }));
74
+ } finally { db.close(); }
75
+ }
76
+
47
77
  /** Condense one UTC day; callers persist the returned snapshot, not raw transcripts. Read-only ledger access. */
48
78
  export async function condenseContextDay(
49
79
  day: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
@@ -68,27 +98,47 @@ export async function condenseContextWindowWithReport(
68
98
  const end = Date.parse(until);
69
99
  if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) throw new Error('invalid context window');
70
100
  // Fetch the previous local day as well: the store compares timestamp text rather than instants.
71
- const events = (deps.events ?? (from => listCoordEvents({ since: from })))(new Date(start - DAY_MS).toISOString());
101
+ const events = (deps.events ?? readCondenseEvents)(new Date(start - DAY_MS).toISOString());
72
102
  const decisions = (deps.decisions ?? (() => new DecisionLedger().list({ status: 'all' })))();
73
103
  const inWindow = (at: string) => {
74
104
  const time = Date.parse(at);
75
105
  return Number.isFinite(time) && time >= start && time < end;
76
106
  };
107
+ const count = (): FunnelCount => ({ total: 0, bySeat: {} });
108
+ const bump = (bucket: FunnelCount, seat: string | undefined) => {
109
+ const key = seat || '(unknown)';
110
+ bucket.total++;
111
+ bucket.bySeat[key] = (bucket.bySeat[key] ?? 0) + 1;
112
+ };
113
+ const funnel: CondenseFunnel = {
114
+ read: { ...count(), bySurface: {} },
115
+ sources: { ...count(), folded: count(), droppedHarnessChild: count(), noSeat: count() },
116
+ summarized: { ...count(), accepted: count(), summarizeFailed: count(), skipped: {} },
117
+ collapsed: count(), cards: { ...count(), carried: count(), supersededByPrevious: count() },
118
+ };
77
119
  const harnessRuns: Array<{ events: CoordEvent[]; locators: Set<string> }> = [];
78
120
  const earlierStarts: CoordEvent[] = [];
79
121
  const otherEvents: CoordEvent[] = [];
80
122
  let harnessEvents = 0;
123
+ let droppedHarnessChild = 0;
81
124
  for (const event of events) {
82
125
  if (!inWindow(event.at)) {
83
126
  const time = Date.parse(event.at);
84
- if (event.kind === 'started' && Number.isFinite(time) && time >= start - DAY_MS && time < start) {
127
+ if (event.kind === 'started' && event.refs.seat !== 'harness-child'
128
+ && Number.isFinite(time) && time >= start - DAY_MS && time < start) {
85
129
  earlierStarts.push(event);
86
130
  }
87
131
  continue;
88
132
  }
89
- // Harness lifecycle = started/finished, or any harness-child event that is not a report (review round 3).
90
- const isLifecycle = event.kind === 'started' || event.kind === 'finished'
91
- || (event.refs.seat === 'harness-child' && event.kind !== '보고');
133
+ bump(funnel.read, event.refs.seat);
134
+ const surface = event.surface ?? '(unknown)';
135
+ funnel.read.bySurface[surface] = (funnel.read.bySurface[surface] ?? 0) + 1;
136
+ if (event.refs.seat === 'harness-child' && event.kind !== '보고') {
137
+ droppedHarnessChild++;
138
+ bump(funnel.sources.droppedHarnessChild, event.refs.seat);
139
+ continue;
140
+ }
141
+ const isLifecycle = event.kind === 'started' || event.kind === 'finished';
92
142
  if (!isLifecycle) {
93
143
  otherEvents.push(event);
94
144
  continue;
@@ -119,6 +169,7 @@ export async function condenseContextWindowWithReport(
119
169
  (group.locators.has(event.refs.source ?? '') || group.locators.has(event.refs.url ?? '')))
120
170
  .sort((a, b) => Date.parse(b.at) - Date.parse(a.at)).at(0) : undefined);
121
171
  const winner = finish ?? inWindowStart ?? group.events.at(-1)!;
172
+ for (const event of group.events) if (event !== winner) bump(funnel.sources.folded, event.refs.seat);
122
173
  const frequency = (ref: string) => group.events.filter(event => event.refs.source === ref || event.refs.url === ref).length;
123
174
  const run = [...group.locators].sort((a, b) => frequency(b) - frequency(a))[0] ?? eventSource(winner).source;
124
175
  const duration = finish && begin && Date.parse(finish.at) >= Date.parse(begin.at)
@@ -130,7 +181,7 @@ export async function condenseContextWindowWithReport(
130
181
  source: run, text: `${run} ${result} · ${duration}` });
131
182
  }
132
183
  const folded = harnessEvents - foldedSources.length;
133
- try { debug.log('context.condense', 'folded', { harnessEvents, kept: foldedSources.length }); }
184
+ try { debug.log('context.condense', 'folded', { harnessEvents, kept: foldedSources.length, droppedHarnessChild }); }
134
185
  catch { /* Observation must not stop condensation. */ }
135
186
  const sources: MemorySource[] = [
136
187
  ...otherEvents.map(eventSource), ...foldedSources,
@@ -142,23 +193,41 @@ export async function condenseContextWindowWithReport(
142
193
  ].filter(source => inWindow(source.at))
143
194
  .sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.source.localeCompare(b.source));
144
195
 
196
+ for (const source of sources) {
197
+ if (typeof source.seat !== 'string' || !oneLine(source.seat)) bump(funnel.sources.noSeat, source.seat);
198
+ else bump(funnel.sources, source.seat);
199
+ }
200
+ for (const decision of decisions) {
201
+ const at = decision.decidedAt ?? decision.raisedAt ?? decision.importedAt ?? '';
202
+ if (!inWindow(at)) continue;
203
+ bump(funnel.read, decision.raisedBy.track ?? 'OP');
204
+ funnel.read.bySurface['decision:ledger'] = (funnel.read.bySurface['decision:ledger'] ?? 0) + 1;
205
+ }
206
+
145
207
  const items = new Map<string, MemoryItem>();
146
208
  const skipped: CondenseSkipped = {};
147
209
  let kept = 0;
148
210
  const skip = (reason: CondenseSkipReason, source: MemorySource) => {
149
211
  skipped[reason] = (skipped[reason] ?? 0) + 1;
150
- try { debug.log('context.condense', 'source-skipped', { reason, kind: source.kind, at: source.at }); }
151
- catch { /* Observation must not stop condensation. */ }
212
+ bump(funnel.summarized.skipped[reason] ??= count(), source.seat);
213
+ if (reason === 'summarize-failed') bump(funnel.summarized.summarizeFailed, source.seat);
214
+ try {
215
+ debug.log('context.condense', 'source-skipped', { reason, kind: source.kind, at: source.at });
216
+ if (reason === 'no-claim') debug.log('context.condense', 'skipped', { reason, source: source.source });
217
+ } catch { /* Observation must not stop condensation. */ }
152
218
  };
153
219
  const safeLine = (value: unknown) => typeof value === 'string' ? oneLine(value) : '';
154
220
  const keyFor = (item: Pick<MemoryItem, 'project' | 'seat' | 'topic'>) => JSON.stringify([item.project, item.seat, item.topic]);
155
221
  for (const item of previous) items.set(keyFor(item), { ...item });
222
+ const candidateKeys = new Set<string>();
223
+ const updatedKeys = new Set<string>();
156
224
  for (const source of sources) {
157
225
  const seat = safeLine(source.seat);
158
226
  if (!seat) { skip('no-seat', source); continue; }
159
227
  let summary: MemorySummary;
160
228
  try { summary = await deps.summarize(source); }
161
229
  catch { skip('summarize-failed', source); continue; }
230
+ bump(funnel.summarized, source.seat);
162
231
  const project = safeLine(summary?.project);
163
232
  const topic = safeLine(summary?.topic);
164
233
  const text = safeLine(summary?.summary);
@@ -169,14 +238,19 @@ export async function condenseContextWindowWithReport(
169
238
  if (!/^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(link)) { skip('bad-link', source); continue; }
170
239
  const claim = summary.claim && { key: safeLine(summary.claim.key), value: safeLine(summary.claim.value) };
171
240
  if (claim && (!claim.key || !claim.value)) { skip('bad-claim', source); continue; }
241
+ if (source.kind === 'event' && !claim) { skip('no-claim', source); continue; }
172
242
  kept++;
243
+ bump(funnel.summarized.accepted, source.seat);
173
244
  const key = keyFor({ project, seat, topic });
245
+ if (candidateKeys.has(key)) bump(funnel.collapsed, source.seat);
246
+ else candidateKeys.add(key);
174
247
  const old = items.get(key);
175
248
  // Equal timestamps are resolved by the source link, so shuffled input does not change the winner.
176
249
  if (old && (Date.parse(old.updatedAt) > Date.parse(source.at)
177
250
  || (Date.parse(old.updatedAt) === Date.parse(source.at) && old.source >= link))) continue;
178
251
  items.set(key, { project, seat, topic, summary: text, source: link, updatedAt: source.at,
179
252
  status: 'active', ...(claim ? { claim } : {}) });
253
+ updatedKeys.add(key);
180
254
  }
181
255
  const cutoff = now.getTime() - 30 * DAY_MS;
182
256
  // Compare only the winning, non-retired assertions; a superseded claim cannot keep a decision candidate alive.
@@ -198,7 +272,14 @@ export async function condenseContextWindowWithReport(
198
272
  }
199
273
  const cards = finalItems.sort((a, b) => a.project.localeCompare(b.project) || a.seat.localeCompare(b.seat)
200
274
  || a.topic.localeCompare(b.topic));
275
+ for (const card of cards) {
276
+ bump(funnel.cards, card.seat);
277
+ if (!candidateKeys.has(keyFor(card))) bump(funnel.cards.carried, card.seat);
278
+ else if (!updatedKeys.has(keyFor(card))) bump(funnel.cards.supersededByPrevious, card.seat);
279
+ }
201
280
  try { debug.log('context.condense', 'window', { sources: sources.length, kept, skipped }); }
202
281
  catch { /* Observation must not stop condensation. */ }
203
- return { cards, skipped, folded };
282
+ try { debug.log('context.condense', 'funnel', funnel); }
283
+ catch { /* Observation must not stop condensation. */ }
284
+ return { cards, skipped, folded, droppedHarnessChild, funnel };
204
285
  }
@@ -593,6 +593,8 @@ import { resolveDashboardChatMainSubmitIntent } from './input/chat-main-submit-r
593
593
  import { maybeEmitDashboardSubmitQuickPass } from './input/chat-main-submit-control.js';
594
594
  import { attachDashboardQuickPassConsumers } from './input/control-signal-consumers.js';
595
595
  import { resolveDashboardChatMainSlashCommand } from './input/chat-main-slash-command.js';
596
+ import { TuiChatPersonaSelection, tuiChatPersonaMessages } from './slash-runtime/chat-persona.js';
597
+ import { awaitGlobalPersonaLoad } from '../persona/global-registry.js';
596
598
  import { resolveDashboardChatMainCacheCommand } from './input/chat-main-cache-command.js';
597
599
  import {
598
600
  dashboardDeltaHelpLines,
@@ -956,6 +958,8 @@ import {
956
958
  resetDashboardAcpTurnRef,
957
959
  } from './turn-lifecycle-runtime.js';
958
960
  import { runDashboardTurnPrelude } from './turn-prelude-runtime.js';
961
+ import { askSeat, deliverSeatAnswers, getTuiSeatAskClientId, parseSeatAsk } from '../seat-dispatch/seat-ask.js';
962
+ import { ceoTaskDeps } from '../seat-dispatch/ceo-intent.js';
959
963
  import {
960
964
  commitDashboardAssistantRenderState,
961
965
  runDashboardTurnTailAutoCopy,
@@ -2136,6 +2140,7 @@ export function renderDashboardFirstScreenBand(input: {
2136
2140
  }
2137
2141
 
2138
2142
  export async function showDashboard(opts: ShowDashboardOptions = {}): Promise<DashboardAction> {
2143
+ await awaitGlobalPersonaLoad().catch(() => {});
2139
2144
  const dashboardQuitConfirmationState = createQuitConfirmationState();
2140
2145
  const signalQuitConfirmationState = createQuitConfirmationState();
2141
2146
  let disposeDashboardEventLoopMonitor = (): void => {};
@@ -2639,6 +2644,19 @@ When a user's request matches an installed skill (they will see a "hint: /run-sk
2639
2644
  Mode- and sync-specific instructions are injected per-turn when relevant — do not assume sync/diff context unless the current turn's Context: block explicitly mentions them.`,
2640
2645
  );
2641
2646
  let chatLines: string[] = []; // chat log — increasingly reserved for transcript / LLM speech
2647
+ const tuiSeatAskClientId = getTuiSeatAskClientId();
2648
+ const tuiSeatAskDeps = {
2649
+ channel: 'tui' as const, clientId: tuiSeatAskClientId,
2650
+ send: async (_origin: import('../seat-dispatch/seat-ask.js').AskOrigin, answer: string) => {
2651
+ chatLines.push(C.info(answer));
2652
+ chatScrollOffset = -1;
2653
+ draw();
2654
+ },
2655
+ };
2656
+ const pollTuiSeatAnswers = (): void => {
2657
+ void deliverSeatAnswers(tuiSeatAskDeps).catch((error) => debug.log('seat.ask', 'delivery-failed', { via: 'tui', error: String(error) }, { level: 'warn' }));
2658
+ };
2659
+ let tuiSeatAskTimer: ReturnType<typeof setInterval> | undefined;
2642
2660
  let debugLines: string[] = [
2643
2661
  C.muted('Dashboard ready. Press / for slash, Ctrl+L for plain input.'),
2644
2662
  C.muted('Debug window ready. Use /debug window open.'),
@@ -3121,6 +3139,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3121
3139
  // the laptop left off. `/telegram attach` creates this, `/telegram
3122
3140
  // detach` clears it. Lives for the dashboard lifetime.
3123
3141
  let attachedSessionId: string | null = null;
3142
+ const chatPersonaSelection = new TuiChatPersonaSelection();
3143
+ let chatPersonaConversationId = crypto.randomUUID();
3144
+ const currentChatPersonaSessionId = () => attachedSessionId ?? chatPersonaConversationId;
3124
3145
  let attachedChatId: number | null = null;
3125
3146
  const forkableSessionHistory = (source: ChatMessage[] = chat.history): ForkableSessionTurn[] =>
3126
3147
  flattenForkableSessionHistory(source);
@@ -3134,7 +3155,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3134
3155
  messages: forkableSessionHistory(),
3135
3156
  });
3136
3157
  const prev = attachedSessionId;
3158
+ const selectedPersona = chatPersonaSelection.get(currentChatPersonaSessionId());
3137
3159
  attachedSessionId = forked.meta.id;
3160
+ if (selectedPersona) chatPersonaSelection.set(forked.meta.id, selectedPersona);
3138
3161
  try { _setActive(forked.meta.id); } catch { /* best-effort */ }
3139
3162
  chatLines.push('');
3140
3163
  chatLines.push(prev
@@ -3174,7 +3197,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
3174
3197
  messages: forkableSessionHistory(truncatedChat),
3175
3198
  });
3176
3199
  const prev = attachedSessionId;
3200
+ const selectedPersona = chatPersonaSelection.get(currentChatPersonaSessionId());
3177
3201
  attachedSessionId = forked.meta.id;
3202
+ if (selectedPersona) chatPersonaSelection.set(forked.meta.id, selectedPersona);
3178
3203
  try { setActiveForTimetravel(forked.meta.id); } catch { /* best-effort */ }
3179
3204
  // in-memory 히스토리도 동일 경계로 절단 — 스토어와 문맥 일치 보장.
3180
3205
  chat.history.length = 0;
@@ -15522,20 +15547,23 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15522
15547
  rich: false,
15523
15548
  })
15524
15549
  : [];
15525
- return buildDashboardTurnPreamble({
15526
- userText,
15527
- sessionId,
15528
- cwd: getSessionCwd(),
15529
- turnProfile: acpTurnRef.turnProfile,
15530
- userConfig: getUserConfig(),
15531
- // Resolved per-turn so a mid-session model switch (codex →
15532
- // claude or vice-versa) takes effect immediately. Drives the
15533
- // codex-only behavioral addendum (fix L-1) inside the
15534
- // universal preamble.
15535
- modelFamily: getModelFamily(inspectActiveProvider().model),
15536
- rich: false,
15537
- enabledTools: tools.map(t => t.name),
15538
- });
15550
+ return [
15551
+ ...tuiChatPersonaMessages(currentChatPersonaSessionId(), chatPersonaSelection),
15552
+ ...buildDashboardTurnPreamble({
15553
+ userText,
15554
+ sessionId,
15555
+ cwd: getSessionCwd(),
15556
+ turnProfile: acpTurnRef.turnProfile,
15557
+ userConfig: getUserConfig(),
15558
+ // Resolved per-turn so a mid-session model switch (codex →
15559
+ // claude or vice-versa) takes effect immediately. Drives the
15560
+ // codex-only behavioral addendum (fix L-1) inside the
15561
+ // universal preamble.
15562
+ modelFamily: getModelFamily(inspectActiveProvider().model),
15563
+ rich: false,
15564
+ enabledTools: tools.map(t => t.name),
15565
+ }),
15566
+ ];
15539
15567
  },
15540
15568
  getTools: ({ userText }) => {
15541
15569
  const runtime = acpToolRuntime;
@@ -15819,6 +15847,9 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
15819
15847
  observe: (data) => { try { debug.log('dashboard.first-screen-band', 'resolved', data); } catch { /* observation is best-effort */ } },
15820
15848
  });
15821
15849
 
15850
+ tuiSeatAskTimer = setInterval(pollTuiSeatAnswers, 10_000);
15851
+ tuiSeatAskTimer.unref();
15852
+ pollTuiSeatAnswers();
15822
15853
  while (true) {
15823
15854
  draw();
15824
15855
  let wdInputExited = false; // set true when the input-mode loop exits this iter
@@ -17571,6 +17602,18 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
17571
17602
  const cmdText = submitIntent.kind === 'control-turn'
17572
17603
  ? (submitIntent.commandText ?? '')
17573
17604
  : submitIntent.text;
17605
+ if (submitIntent.kind === 'submit-turn' && parseSeatAsk(submitIntent.text)) {
17606
+ try {
17607
+ const receipt = await askSeat(submitIntent.text, { channel: 'tui', clientId: tuiSeatAskClientId },
17608
+ ceoTaskDeps(getUserConfig(), null), tuiSeatAskDeps);
17609
+ chatLines.push(C.info(receipt));
17610
+ } catch (error) {
17611
+ chatLines.push(C.error(`CTO 질문 전송 실패: ${String(error)}`));
17612
+ }
17613
+ chatScrollOffset = -1;
17614
+ draw();
17615
+ continue;
17616
+ }
17574
17617
  if (submitIntent.kind === 'submit-turn' && submitIntent.route === 'sticky-acp') {
17575
17618
  await runDashboardChatMainSubmitIntent({
17576
17619
  submit: createDashboardChatMainTurnSubmit(submitIntent),
@@ -18198,6 +18241,11 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
18198
18241
  },
18199
18242
  getAttachedSessionId: () => attachedSessionId,
18200
18243
  setAttachedSessionId: (id) => { attachedSessionId = id; },
18244
+ chatPersona: {
18245
+ sessionId: currentChatPersonaSessionId,
18246
+ selection: chatPersonaSelection,
18247
+ newConversation: () => { chatPersonaConversationId = crypto.randomUUID(); },
18248
+ },
18201
18249
  getAttachedChatId: () => attachedChatId,
18202
18250
  setAttachedChatId: (id) => { attachedChatId = id; },
18203
18251
  sessionSlash: {
@@ -19010,14 +19058,17 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19010
19058
  rich: false,
19011
19059
  })
19012
19060
  : [];
19013
- return buildDashboardTurnPreamble({
19014
- userText: args.userText,
19015
- cwd: args.cwd,
19016
- turnProfile: args.turnProfile as SessionTurnProfile,
19017
- userConfig: args.userConfig,
19018
- rich: false,
19019
- enabledTools: tools.map(t => t.name),
19020
- });
19061
+ return [
19062
+ ...tuiChatPersonaMessages(currentChatPersonaSessionId(), chatPersonaSelection),
19063
+ ...buildDashboardTurnPreamble({
19064
+ userText: args.userText,
19065
+ cwd: args.cwd,
19066
+ turnProfile: args.turnProfile as SessionTurnProfile,
19067
+ userConfig: args.userConfig,
19068
+ rich: false,
19069
+ enabledTools: tools.map(t => t.name),
19070
+ }),
19071
+ ];
19021
19072
  },
19022
19073
  runAutoCompact: async ({ preamble, chatHistory, userMsg }) => {
19023
19074
  const compactMod = await import('../compact/index.js');
@@ -19427,5 +19478,7 @@ Mode- and sync-specific instructions are injected per-turn when relevant — do
19427
19478
  } catch (err) {
19428
19479
  closeDashboardTui();
19429
19480
  throw err;
19481
+ } finally {
19482
+ if (tuiSeatAskTimer) clearInterval(tuiSeatAskTimer);
19430
19483
  }
19431
19484
  }