elanous 0.2.12 → 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 (534) 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/{9906-482c6619b3fdda20.js → 8060-cc008cc6fedfa818.js} +1 -1
  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-1abfe3aec3be6bdc.js +1 -0
  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-a67e11ba962337b5.js → page-15374909d8f1aebd.js} +1 -1
  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-793fa536b3a6ef37.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-e1e47d8d940aba3d.js +1 -0
  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-e297866051595d81.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/examples/teams/personas/backend.yaml +4 -0
  194. package/examples/teams/personas/pm.yaml +4 -0
  195. package/examples/teams/personas/qa.yaml +4 -0
  196. package/examples/teams/small-product.yaml +20 -0
  197. package/graphs/lecture/lecture-note.yaml +28 -0
  198. package/graphs/lecture/recipes.yaml +17 -0
  199. package/graphs/orchestrator/orchestrator.yaml +59 -0
  200. package/graphs/orchestrator/recipes.yaml +18 -0
  201. package/graphs/release/recipes.yaml +3 -0
  202. package/graphs/release/release-loop.yaml +4 -1
  203. package/graphs/rhythm/daily.yaml +38 -0
  204. package/graphs/rhythm/recipes.yaml +18 -0
  205. package/graphs/rhythm/weekly.yaml +39 -0
  206. package/graphs/video/recipes.yaml +3 -0
  207. package/graphs/video/youtube-summary.yaml +17 -0
  208. package/package.json +2 -1
  209. package/scripts/audit-test-state-writes.ts +72 -17
  210. package/scripts/brand/check.ts +186 -0
  211. package/scripts/context-hooks/emit.ts +36 -14
  212. package/scripts/coord-channel-watch.sh +1 -0
  213. package/scripts/coord-post.sh +11 -1
  214. package/scripts/discord-seat-probe.ts +144 -0
  215. package/scripts/docs-cli-check.ts +23 -6
  216. package/scripts/expose-rubric-check.ts +118 -0
  217. package/scripts/expose-rubric-suggest.ts +163 -0
  218. package/scripts/heal/run-role.ts +14 -0
  219. package/scripts/hq/hq-standby.sh +65 -0
  220. package/scripts/hq/standby-promote.ts +173 -0
  221. package/scripts/hq/standby-role.ts +59 -0
  222. package/scripts/hq/standby-snapshot.ts +226 -0
  223. package/scripts/hq/standby-verify.ts +223 -0
  224. package/scripts/lecture-note/node.ts +256 -0
  225. package/scripts/lessons/handbook-page.ts +85 -0
  226. package/scripts/release-loop/gate-node.ts +62 -15
  227. package/scripts/release-loop/ops-upgrade-node.ts +17 -5
  228. package/scripts/release-loop/publish-node.ts +14 -1
  229. package/scripts/release-loop/unattended-release.ts +23 -1
  230. package/scripts/release-loop/vault-note-node.ts +196 -0
  231. package/scripts/release-story/draft.ts +172 -0
  232. package/scripts/rhythm/daily.ts +383 -0
  233. package/scripts/rhythm/weekly.ts +294 -0
  234. package/scripts/skills/public-check.ts +169 -0
  235. package/scripts/test-deterministic.ts +57 -11
  236. package/scripts/test-diet/effectiveness.ts +195 -0
  237. package/scripts/test-diet/lib.ts +16 -8
  238. package/scripts/test-diet/node.ts +77 -9
  239. package/scripts/ux/demo-rehearsal.ts +2 -2
  240. package/scripts/ux/lib/rehearsal-verdict.ts +1 -0
  241. package/scripts/video/youtube-summary.ts +133 -0
  242. package/skills/diagram-master/SKILL.md +42 -49
  243. package/skills/diagram-master/references/pyproject.toml +4 -1
  244. package/skills/diagram-master/references/routing-guide.md +4 -2
  245. package/skills/grill-me/SKILL.md +1 -0
  246. package/skills/omni-crawl/SKILL.md +16 -16
  247. package/skills/omni-crawl/package-lock.json +1 -544
  248. package/skills/omni-crawl/package.json +3 -4
  249. package/skills/omni-crawl/scripts/main.ts +13 -13
  250. package/skills/omni-crawl/scripts/monitor.ts +8 -8
  251. package/skills/omni-crawl/src/apify.ts +13 -2
  252. package/skills/omni-crawl/src/capture.ts +6 -0
  253. package/skills/omni-crawl/src/registry.ts +2 -2
  254. package/skills/omni-crawl/src/stdout-json.ts +4 -0
  255. package/skills/omni-digest/SKILL.md +10 -11
  256. package/skills/omni-digest/package-lock.json +0 -545
  257. package/skills/omni-digest/package.json +2 -5
  258. package/skills/omni-digest/scripts/main.ts +3 -3
  259. package/skills/omni-digest/src/delegate.ts +5 -5
  260. package/skills/omni-digest/src/fetch.ts +2 -2
  261. package/skills/omni-digest/src/media.ts +25 -11
  262. package/skills/omni-digest/src/visual.ts +3 -2
  263. package/skills/omni-market/scripts/main.ts +1 -1
  264. package/skills/omni-market/scripts/stdout-json.ts +4 -0
  265. package/skills/youtube-master/SKILL.md +13 -13
  266. package/skills/youtube-master/package-lock.json +0 -545
  267. package/skills/youtube-master/package.json +2 -5
  268. package/skills/youtube-master/scripts/main.ts +2 -2
  269. package/skills/youtube-master/scripts/transcribe-local.ts +1 -1
  270. package/skills/youtube-master/src/util.ts +30 -7
  271. package/src/agent/identity-env.ts +11 -0
  272. package/src/agent-mission/driver.ts +15 -10
  273. package/src/agent-mission/duo.ts +94 -0
  274. package/src/agent-mission/pty-decision.ts +1 -1
  275. package/src/autopilot/build/llm-conflict-merge.ts +45 -8
  276. package/src/autopilot/build/merge-intent.ts +53 -0
  277. package/src/autopilot/track-agent-shadow.ts +2 -1
  278. package/src/autopilot/track-agent.ts +4 -1
  279. package/src/boot/daemon-prompt-request.ts +3 -0
  280. package/src/boot/daemon-prompt-turn.ts +6 -2
  281. package/src/boot/daemon-runtime.ts +48 -16
  282. package/src/chat/index.ts +2 -0
  283. package/src/claims/claims-ledger.ts +232 -0
  284. package/src/claims/claims-render.ts +157 -0
  285. package/src/cli/claims-cli.ts +94 -0
  286. package/src/cli/decisions-cli.ts +84 -20
  287. package/src/cli/docs-cli.ts +2 -0
  288. package/src/cli/flow-cli.ts +11 -0
  289. package/src/cli/freeze-cli.ts +45 -0
  290. package/src/cli/lesson-cli.ts +108 -0
  291. package/src/cli/logs-cli.ts +104 -2
  292. package/src/cli/logs-model-input.ts +131 -0
  293. package/src/cli/outputs-cli.ts +92 -0
  294. package/src/cli/persona-cli.ts +29 -0
  295. package/src/cli/pod-cli.ts +10 -3
  296. package/src/cli/pr-cli.ts +44 -2
  297. package/src/cli/private-ledger-commands.ts +54 -4
  298. package/src/cli/pty-drive-verdict.ts +11 -2
  299. package/src/cli/release-cli.ts +248 -45
  300. package/src/cli/rfc-status.ts +91 -0
  301. package/src/connectors/linear.ts +5 -3
  302. package/src/context-bus/condense.ts +189 -0
  303. package/src/context-bus/context-day.ts +82 -0
  304. package/src/context-bus/context-now-public.ts +86 -0
  305. package/src/context-bus/context-now-surfaces.ts +70 -4
  306. package/src/context-bus/context-now.ts +4 -3
  307. package/src/context-bus/long-term-memory.ts +201 -24
  308. package/src/context-bus/nightly-condense.ts +1 -1
  309. package/src/dashboard/index.ts +75 -22
  310. package/src/dashboard/slash-runtime/chat-persona.ts +66 -0
  311. package/src/dashboard/slash-runtime/dashboard-handlers.ts +99 -0
  312. package/src/dashboard/slash-runtime/loops-table.ts +77 -0
  313. package/src/decisions/decision-cards.ts +13 -7
  314. package/src/decisions/decision-ledger.ts +83 -18
  315. package/src/discord/sprint21-runtime.ts +16 -0
  316. package/src/discord-self-message.ts +8 -0
  317. package/src/discord-slash-wire.ts +31 -2
  318. package/src/domains/outbound-alert.ts +1 -1
  319. package/src/elanous-config-dir.ts +29 -4
  320. package/src/execution-loop/launch-gate.ts +29 -14
  321. package/src/flow/cards.ts +239 -0
  322. package/src/graph-kinds/registry.ts +2 -2
  323. package/src/graph-runner/graph-approval-card.ts +2 -2
  324. package/src/graph-runner/graph-grow.ts +64 -11
  325. package/src/graph-runner/graph-tick.ts +13 -2
  326. package/src/graph-runner/runner.ts +111 -22
  327. package/src/harness/harness-cli-command.ts +178 -27
  328. package/src/harness/harness-incidents.ts +43 -12
  329. package/src/harness/harness-pod-dispatch.ts +2 -0
  330. package/src/harness/harness-queue-child.ts +37 -0
  331. package/src/harness/harness-queue.ts +494 -0
  332. package/src/harness/helper-scan.ts +139 -0
  333. package/src/harness/model-input-observation.ts +37 -0
  334. package/src/hooks/github-shadow.ts +72 -2
  335. package/src/hooks/receiver.ts +4 -2
  336. package/src/hq/hq.ts +318 -0
  337. package/src/hq/lease.ts +210 -0
  338. package/src/hq/ledger-write-target.ts +41 -0
  339. package/src/hq/promote.ts +301 -0
  340. package/src/index.ts +281 -27
  341. package/src/intake-plane/discord-seat-work.ts +13 -6
  342. package/src/intake-plane/linear-wish.ts +72 -0
  343. package/src/intake-plane/persona-answer.ts +5 -0
  344. package/src/intake-plane/telegram-seat-work.ts +13 -6
  345. package/src/intake-plane/wish-benchmark-ledger.ts +129 -0
  346. package/src/intake-plane/wish-card.ts +53 -2
  347. package/src/intake-plane/wish-folder.ts +5 -2
  348. package/src/intake-plane/wish-reply.ts +72 -0
  349. package/src/lessons/lesson-import.ts +106 -0
  350. package/src/lessons/lesson-ledger.ts +206 -0
  351. package/src/loops/activity.ts +203 -0
  352. package/src/loops/budget.ts +32 -0
  353. package/src/loops/checker.ts +180 -0
  354. package/src/loops/contract/examples/orchestrator.loop.yaml +1 -1
  355. package/src/loops/loop-cli.ts +72 -6
  356. package/src/loops/neighbors.ts +40 -0
  357. package/src/loops/observe.ts +59 -0
  358. package/src/loops/orchestrator/author-depth.ts +164 -0
  359. package/src/loops/orchestrator/author-input-check.ts +77 -0
  360. package/src/loops/orchestrator/author-ledger.ts +230 -0
  361. package/src/loops/orchestrator/finish-rate.ts +175 -0
  362. package/src/loops/orchestrator/overlap.ts +180 -0
  363. package/src/loops/orchestrator/resync-shadow.ts +227 -0
  364. package/src/loops/orchestrator/tick.ts +302 -0
  365. package/src/loops/orchestrator/traffic.ts +165 -0
  366. package/src/loops/package/verify.ts +125 -0
  367. package/src/loops/registry.ts +454 -9
  368. package/src/loops/status-rows.ts +118 -0
  369. package/src/loops/verdict.ts +34 -0
  370. package/src/maturity/bot-slash-catalog.ts +66 -0
  371. package/src/maturity/feature-maturity.ts +8 -5
  372. package/src/memory.ts +37 -4
  373. package/src/nexus/api/card-followup-route.ts +118 -0
  374. package/src/nexus/api/dashboard.ts +6 -1
  375. package/src/nexus/api/decisions-route.ts +68 -0
  376. package/src/nexus/api/http-server.ts +65 -2
  377. package/src/nexus/api/merge-approvals-gate.ts +1 -1
  378. package/src/nexus/api/personas.ts +2 -1
  379. package/src/nexus/api/schedules-read.ts +5 -1
  380. package/src/nexus/api/skill-exec-route.ts +97 -0
  381. package/src/nexus/api/task-cards-api.ts +44 -5
  382. package/src/nexus/api/workflows.ts +2 -1
  383. package/src/persona/loader.ts +18 -0
  384. package/src/persona/persona-todo.ts +14 -9
  385. package/src/persona/presets.ts +2 -1
  386. package/src/persona/team.ts +105 -0
  387. package/src/persona/types.ts +4 -0
  388. package/src/pod-lease/host-lease.ts +130 -20
  389. package/src/release-loop/auto-start.ts +48 -0
  390. package/src/release-loop/checklist.ts +13 -4
  391. package/src/release-loop/feature-store.ts +28 -16
  392. package/src/release-loop/landed-but-yellow.ts +12 -0
  393. package/src/release-loop/landing-freeze.ts +121 -0
  394. package/src/release-loop/placement.ts +242 -0
  395. package/src/release-loop/release-schedule.ts +3 -1
  396. package/src/seat-dispatch/ceo-commands.ts +2 -2
  397. package/src/seat-dispatch/seat-ask.ts +73 -15
  398. package/src/seat-dispatch/seat-questions.ts +58 -0
  399. package/src/seat-loop/seat-loop.ts +495 -46
  400. package/src/seat-loop/seat-pack.ts +127 -0
  401. package/src/self-dev/ask-launch-flow.ts +3 -0
  402. package/src/self-dev/dev-pipeline.ts +6 -2
  403. package/src/self-dev/draft-sweep.ts +76 -0
  404. package/src/self-dev/run-store.ts +73 -1
  405. package/src/self-dev/run-supervisor.ts +8 -0
  406. package/src/self-dev/self-orchestrate-runtime.ts +5 -1
  407. package/src/self-implement/budget-gate.ts +29 -0
  408. package/src/self-implement/expose-gate.ts +47 -0
  409. package/src/self-implement/frozen-merges.ts +294 -0
  410. package/src/self-implement/gate-cli.ts +10 -4
  411. package/src/self-implement/gate-scope.ts +46 -9
  412. package/src/self-implement/goal-archive.ts +222 -0
  413. package/src/self-implement/goal-lookup.ts +147 -0
  414. package/src/self-implement/heal-intake.ts +79 -0
  415. package/src/self-implement/host-regate.ts +25 -2
  416. package/src/self-implement/l8-merge-queue.ts +157 -1
  417. package/src/self-implement/orchestrator.ts +114 -10
  418. package/src/self-implement/revert-guard.ts +62 -0
  419. package/src/self-implement/run-ledger.ts +1 -1
  420. package/src/self-implement/seams.ts +28 -8
  421. package/src/session/index.ts +2 -0
  422. package/src/steward/launch.ts +5 -1
  423. package/src/steward/readiness.ts +125 -0
  424. package/src/steward/rescue.ts +75 -0
  425. package/src/steward/triage.ts +48 -9
  426. package/src/task-cards/card-cli.ts +86 -0
  427. package/src/task-cards/card-store.ts +10 -6
  428. package/src/task-orchestrator/surfaces/pod-lease.ts +23 -8
  429. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +46 -6
  430. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
  431. package/src/task-orchestrator/surfaces/self-implement-pod.ts +112 -18
  432. package/src/telegram-commands.ts +27 -3
  433. package/src/telegram-dispatch-forward.ts +1 -1
  434. package/src/telegram-fabric-plan.ts +90 -0
  435. package/src/telegram-loops-command.ts +44 -0
  436. package/src/telegram-project-command.ts +10 -4
  437. package/src/telegram.ts +5 -0
  438. package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
  439. package/src/user-config.ts +204 -16
  440. package/src/version/packed-revision.json +1 -1
  441. package/src/workflow-runtime/executor.ts +4 -0
  442. package/src/workflow-runtime/node-catalog.ts +13 -0
  443. package/src/workflow-runtime/nodes/knowledge.ts +38 -0
  444. package/src/workflow-runtime/schema.ts +17 -1
  445. package/src/workflow-runtime/storage.ts +31 -13
  446. package/src/workflow-runtime/types.ts +13 -1
  447. package/apps/pwa/out/_next/static/-6XrAoD3eU7NyMHAE4xPS/_buildManifest.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/1147-466ec15afacc1a65.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/1159-0c5ef8bc21cb6afb.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/1394-ee3795b112907122.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/2176-c379e3ebce874151.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/2426.8c6ee0c668d53def.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/3170-7ec60a56fa0e4728.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/3278-ae6ef084769f6195.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/3454-988a53dac60a1763.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/3638-aa80b4641ea439db.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/3735-fe3402553ac1c9c6.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/3992-2e522a8ac5f2afde.js +0 -25
  459. package/apps/pwa/out/_next/static/chunks/4215-c9d91dd644ec1719.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/4508-5d0161d1e724d735.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/4733-623a79345b171e1c.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/4883.3bd969ea1d3aaaff.js +0 -2
  463. package/apps/pwa/out/_next/static/chunks/5532.6899a12958a89b72.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/5845-e6c003ce8d5fd36a.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/5978-b27004c677119115.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/6048-4c5c33579bc4ef78.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/6303-422dbf0f67c72aac.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/6751-bb3595948b2e311b.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/7274-19b7279f18b47ab3.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/7277-0b2fc7c16ba2d64b.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/7551-a0e0707b79164ddd.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/7758-0b018a9e4fc56200.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/7777-79f845045aa1ed86.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/8025-cc24ee5d8f2fb6f8.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/8206-c6684dcde12be721.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/8605-0259994816604c49.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/8725.bf46bb1395dd0fa2.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/8734-252d511cd276c0dc.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/8834-ca1cb09c1cb1a6bf.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/9269.d2a4795bc6812717.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/9538-2816b90923606459.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/9920-5cfdc5e8fd209825.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-d16993889b125c42.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/approvals/page-cb169620945d1952.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-cefe8169e36cf62a.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/board/page-d2e35228bc497119.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/botlab/page-5c05b0da4bc1392c.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/bots/page-e0ccdb576812b523.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/chat/page-813c4a52c9d05ca8.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/consult/page-4731f2c215358937.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/control/page-a022b257950f39e6.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a2ffc717e04b8b0a.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/editor/page-b59ee4e70fc18fcf.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/field/page-c172dabd796eb7a3.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/healthz/route-d16993889b125c42.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/inside/page-1efd0cb9e0ed9d13.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/intake/page-d5dc7ff69a37b4cf.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/layout-e71c84189e8c683e.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/live/page-1d6a533ce5a6efcb.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/market/page-dfca9b7d2de0b135.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/morning/page-6dd3bfbbde2a5c2c.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/not-found-490653e8d4cc8852.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/observatory/page-77447356baad75d2.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-29e961191f6057df.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-c822f55b884d9104.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-fa4ccfc432424e52.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/outputs/page-6d604227e1664b71.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/page-9c7e8f744a765daf.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/app/reflection/page-4048543b4b7ad5e0.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-4c3a3126abed551a.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/app/sessions/page-64b0aecb1ca75e75.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-4292d0149020e8a7.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/app/settings/page-db14d0be7f1c460c.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-e5d8072cd7cfef73.js +0 -1
  515. package/apps/pwa/out/_next/static/chunks/app/setup/layout-d16993889b125c42.js +0 -1
  516. package/apps/pwa/out/_next/static/chunks/app/setup/page-9547b5cec7cf76bf.js +0 -1
  517. package/apps/pwa/out/_next/static/chunks/app/share/page-e6dcb83265530636.js +0 -1
  518. package/apps/pwa/out/_next/static/chunks/app/showroom/page-45248f08d6aecae5.js +0 -1
  519. package/apps/pwa/out/_next/static/chunks/app/tasks/page-2cddeb9e2aeedc55.js +0 -1
  520. package/apps/pwa/out/_next/static/chunks/app/today/page-baba2e047872fb0f.js +0 -1
  521. package/apps/pwa/out/_next/static/chunks/app/trace/page-e895da4ec9cac345.js +0 -1
  522. package/apps/pwa/out/_next/static/chunks/app/vault/page-4344faf20216233b.js +0 -1
  523. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-698bb29f11864b89.js +0 -1
  524. package/apps/pwa/out/_next/static/chunks/app/workflows/page-bd6a6dc1ddc273f2.js +0 -1
  525. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2e9e2fb01c9da1c9.js +0 -1
  526. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-d62ceb13411ebba4.js +0 -1
  527. package/apps/pwa/out/_next/static/chunks/framework-172630e6aa5e2d7f.js +0 -1
  528. package/apps/pwa/out/_next/static/chunks/main-app-f0fe2d7477845181.js +0 -1
  529. package/apps/pwa/out/_next/static/chunks/main-fc98ac70b44eccb0.js +0 -1
  530. package/apps/pwa/out/_next/static/chunks/pages/_app-5bd7f10271f72413.js +0 -1
  531. package/apps/pwa/out/_next/static/chunks/pages/_error-97d0d85ea05fef9b.js +0 -1
  532. package/apps/pwa/out/_next/static/chunks/webpack-9e4d02d567b98240.js +0 -1
  533. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +0 -1
  534. /package/apps/pwa/out/_next/static/{-6XrAoD3eU7NyMHAE4xPS → x6mjI6vqOkVVu0jFuDkz1}/_ssgManifest.js +0 -0
@@ -1,5 +1,8 @@
1
- import { redactSecretText } from '../debug/log.js';
1
+ import { Database } from 'bun:sqlite';
2
+ import { existsSync } from 'node:fs';
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,15 +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
 
41
+ export type CondenseSkipReason = 'no-seat' | 'no-project' | 'no-topic' | 'no-summary' | 'bad-link' | 'bad-claim' | 'no-claim' | 'summarize-failed';
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
+ }
52
+ export interface CondenseWindowReport {
53
+ cards: MemoryItem[];
54
+ funnel: CondenseFunnel;
55
+ skipped: CondenseSkipped;
56
+ folded: number;
57
+ droppedHarnessChild: number;
58
+ }
59
+
36
60
  const DAY_MS = 86_400_000;
37
61
  const oneLine = (text: string) => redactSecretText(text.split(/\r?\n/, 1)[0] ?? '').trim();
38
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
+
39
77
  /** Condense one UTC day; callers persist the returned snapshot, not raw transcripts. Read-only ledger access. */
40
78
  export async function condenseContextDay(
41
79
  day: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
@@ -43,47 +81,176 @@ export async function condenseContextDay(
43
81
  if (!/^\d{4}-\d\d-\d\d$/.test(day) || new Date(`${day}T00:00:00.000Z`).toISOString().slice(0, 10) !== day) {
44
82
  throw new Error('day must be a valid UTC YYYY-MM-DD');
45
83
  }
46
- const since = `${day}T00:00:00.000Z`;
84
+ return condenseContextWindow(`${day}T00:00:00.000Z`, new Date(Date.parse(`${day}T00:00:00.000Z`) + DAY_MS).toISOString(), previous, deps, now);
85
+ }
86
+
87
+ /** Read-only condensation over an exact rolling UTC window; the caller owns any persistence. */
88
+ export async function condenseContextWindow(
89
+ since: string, until: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
90
+ ): Promise<MemoryItem[]> {
91
+ return (await condenseContextWindowWithReport(since, until, previous, deps, now)).cards;
92
+ }
93
+
94
+ export async function condenseContextWindowWithReport(
95
+ since: string, until: string, previous: readonly MemoryItem[], deps: MemoryDeps, now: Date = new Date(),
96
+ ): Promise<CondenseWindowReport> {
47
97
  const start = Date.parse(since);
48
- const until = start + DAY_MS;
49
- // The store compares timestamp text, so fetch the previous local day too before UTC filtering.
50
- const events = (deps.events ?? (from => listCoordEvents({ since: from })))(new Date(start - DAY_MS).toISOString());
98
+ const end = Date.parse(until);
99
+ if (!Number.isFinite(start) || !Number.isFinite(end) || end <= start) throw new Error('invalid context window');
100
+ // Fetch the previous local day as well: the store compares timestamp text rather than instants.
101
+ const events = (deps.events ?? readCondenseEvents)(new Date(start - DAY_MS).toISOString());
51
102
  const decisions = (deps.decisions ?? (() => new DecisionLedger().list({ status: 'all' })))();
103
+ const inWindow = (at: string) => {
104
+ const time = Date.parse(at);
105
+ return Number.isFinite(time) && time >= start && time < end;
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
+ };
119
+ const harnessRuns: Array<{ events: CoordEvent[]; locators: Set<string> }> = [];
120
+ const earlierStarts: CoordEvent[] = [];
121
+ const otherEvents: CoordEvent[] = [];
122
+ let harnessEvents = 0;
123
+ let droppedHarnessChild = 0;
124
+ for (const event of events) {
125
+ if (!inWindow(event.at)) {
126
+ const time = Date.parse(event.at);
127
+ if (event.kind === 'started' && event.refs.seat !== 'harness-child'
128
+ && Number.isFinite(time) && time >= start - DAY_MS && time < start) {
129
+ earlierStarts.push(event);
130
+ }
131
+ continue;
132
+ }
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';
142
+ if (!isLifecycle) {
143
+ otherEvents.push(event);
144
+ continue;
145
+ }
146
+ harnessEvents++;
147
+ const locators = new Set([event.refs.source, event.refs.url].filter((ref): ref is string => !!ref));
148
+ const group = { events: [event], locators };
149
+ // Either locator can identify the run, including a bridge through a third event.
150
+ for (let i = 0; i < harnessRuns.length;) {
151
+ const prior = harnessRuns[i]!;
152
+ if (locators.size && [...prior.locators].some(ref => locators.has(ref))) {
153
+ group.events.push(...prior.events);
154
+ for (const ref of prior.locators) locators.add(ref);
155
+ harnessRuns.splice(i, 1);
156
+ i = 0;
157
+ } else i++;
158
+ }
159
+ harnessRuns.push(group);
160
+ }
161
+ const eventSource = (event: CoordEvent): MemorySource => ({ kind: 'event', at: event.at, seat: event.refs.seat,
162
+ text: event.summary, source: event.refs.source ?? event.refs.url ?? `elanous://context/event/${encodeURIComponent(event.id)}` });
163
+ const foldedSources: MemorySource[] = [];
164
+ for (const group of harnessRuns) {
165
+ group.events.sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.id.localeCompare(b.id));
166
+ const finish = group.events.filter(event => event.kind === 'finished').at(-1);
167
+ const inWindowStart = group.events.find(event => event.kind === 'started');
168
+ const begin = inWindowStart ?? (finish ? earlierStarts.filter(event =>
169
+ (group.locators.has(event.refs.source ?? '') || group.locators.has(event.refs.url ?? '')))
170
+ .sort((a, b) => Date.parse(b.at) - Date.parse(a.at)).at(0) : undefined);
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);
173
+ const frequency = (ref: string) => group.events.filter(event => event.refs.source === ref || event.refs.url === ref).length;
174
+ const run = [...group.locators].sort((a, b) => frequency(b) - frequency(a))[0] ?? eventSource(winner).source;
175
+ const duration = finish && begin && Date.parse(finish.at) >= Date.parse(begin.at)
176
+ ? `${Math.floor((Date.parse(finish.at) - Date.parse(begin.at)) / 60_000)}m ${Math.floor(((Date.parse(finish.at) - Date.parse(begin.at)) % 60_000) / 1_000)}s`
177
+ : '알 수 없음';
178
+ const result = finish ? oneLine(finish.summary).replace(/^Harness child finished:\s*/i, '') || '종료'
179
+ : '진행 중';
180
+ foldedSources.push({ ...eventSource(winner), seat: winner.refs.seat || begin?.refs.seat || '',
181
+ source: run, text: `${run} ${result} · ${duration}` });
182
+ }
183
+ const folded = harnessEvents - foldedSources.length;
184
+ try { debug.log('context.condense', 'folded', { harnessEvents, kept: foldedSources.length, droppedHarnessChild }); }
185
+ catch { /* Observation must not stop condensation. */ }
52
186
  const sources: MemorySource[] = [
53
- ...events.map(event => ({ kind: 'event' as const, at: event.at, seat: event.refs.seat,
54
- text: event.summary, source: event.refs.source ?? event.refs.url ?? `elanous://context/event/${encodeURIComponent(event.id)}` })),
187
+ ...otherEvents.map(eventSource), ...foldedSources,
55
188
  ...decisions.map(decision => ({ kind: 'decision' as const,
56
189
  at: decision.decidedAt ?? decision.raisedAt ?? decision.importedAt ?? '',
57
190
  seat: decision.raisedBy.track ?? 'OP',
58
191
  text: decision.status === 'decided' ? `${decision.title}: ${decision.options.find(o => o.key === decision.choice)?.label ?? decision.note ?? decision.scqa.a ?? '결정됨'}` : `${decision.title}: ${decision.status}`,
59
192
  source: `elanous://decisions/${encodeURIComponent(decision.id)}` })),
60
- ].filter(source => {
61
- const at = Date.parse(source.at);
62
- return Number.isFinite(at) && at >= start && at < until;
63
- }).sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.source.localeCompare(b.source));
193
+ ].filter(source => inWindow(source.at))
194
+ .sort((a, b) => Date.parse(a.at) - Date.parse(b.at) || a.source.localeCompare(b.source));
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
+ }
64
206
 
65
207
  const items = new Map<string, MemoryItem>();
208
+ const skipped: CondenseSkipped = {};
209
+ let kept = 0;
210
+ const skip = (reason: CondenseSkipReason, source: MemorySource) => {
211
+ skipped[reason] = (skipped[reason] ?? 0) + 1;
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. */ }
218
+ };
219
+ const safeLine = (value: unknown) => typeof value === 'string' ? oneLine(value) : '';
66
220
  const keyFor = (item: Pick<MemoryItem, 'project' | 'seat' | 'topic'>) => JSON.stringify([item.project, item.seat, item.topic]);
67
221
  for (const item of previous) items.set(keyFor(item), { ...item });
222
+ const candidateKeys = new Set<string>();
223
+ const updatedKeys = new Set<string>();
68
224
  for (const source of sources) {
69
- const summary = await deps.summarize(source);
70
- const project = oneLine(summary.project);
71
- const seat = oneLine(source.seat);
72
- const topic = oneLine(summary.topic);
73
- const text = oneLine(summary.summary);
74
- const link = oneLine(source.source);
75
- if (!project || !seat || !topic || !text || !/^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(link)) {
76
- throw new Error('memory summary needs a project, seat, topic, one-line summary and source link');
77
- }
78
- const claim = summary.claim && { key: oneLine(summary.claim.key), value: oneLine(summary.claim.value) };
79
- if (claim && (!claim.key || !claim.value)) throw new Error('memory claim needs key and value');
225
+ const seat = safeLine(source.seat);
226
+ if (!seat) { skip('no-seat', source); continue; }
227
+ let summary: MemorySummary;
228
+ try { summary = await deps.summarize(source); }
229
+ catch { skip('summarize-failed', source); continue; }
230
+ bump(funnel.summarized, source.seat);
231
+ const project = safeLine(summary?.project);
232
+ const topic = safeLine(summary?.topic);
233
+ const text = safeLine(summary?.summary);
234
+ const link = safeLine(source.source);
235
+ if (!project) { skip('no-project', source); continue; }
236
+ if (!topic) { skip('no-topic', source); continue; }
237
+ if (!text) { skip('no-summary', source); continue; }
238
+ if (!/^(?:https?:\/\/|elanous:\/\/)[^\s]+$/.test(link)) { skip('bad-link', source); continue; }
239
+ const claim = summary.claim && { key: safeLine(summary.claim.key), value: safeLine(summary.claim.value) };
240
+ if (claim && (!claim.key || !claim.value)) { skip('bad-claim', source); continue; }
241
+ if (source.kind === 'event' && !claim) { skip('no-claim', source); continue; }
242
+ kept++;
243
+ bump(funnel.summarized.accepted, source.seat);
80
244
  const key = keyFor({ project, seat, topic });
245
+ if (candidateKeys.has(key)) bump(funnel.collapsed, source.seat);
246
+ else candidateKeys.add(key);
81
247
  const old = items.get(key);
82
248
  // Equal timestamps are resolved by the source link, so shuffled input does not change the winner.
83
249
  if (old && (Date.parse(old.updatedAt) > Date.parse(source.at)
84
250
  || (Date.parse(old.updatedAt) === Date.parse(source.at) && old.source >= link))) continue;
85
251
  items.set(key, { project, seat, topic, summary: text, source: link, updatedAt: source.at,
86
252
  status: 'active', ...(claim ? { claim } : {}) });
253
+ updatedKeys.add(key);
87
254
  }
88
255
  const cutoff = now.getTime() - 30 * DAY_MS;
89
256
  // Compare only the winning, non-retired assertions; a superseded claim cannot keep a decision candidate alive.
@@ -103,6 +270,16 @@ export async function condenseContextDay(
103
270
  finalItems[index] = { ...item, conflict: { owner: 'OP', sources: [conflicting.source, item.source] } };
104
271
  }
105
272
  }
106
- return finalItems.sort((a, b) => a.project.localeCompare(b.project) || a.seat.localeCompare(b.seat)
273
+ const cards = finalItems.sort((a, b) => a.project.localeCompare(b.project) || a.seat.localeCompare(b.seat)
107
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
+ }
280
+ try { debug.log('context.condense', 'window', { sources: sources.length, kept, skipped }); }
281
+ catch { /* Observation must not stop condensation. */ }
282
+ try { debug.log('context.condense', 'funnel', funnel); }
283
+ catch { /* Observation must not stop condensation. */ }
284
+ return { cards, skipped, folded, droppedHarnessChild, funnel };
108
285
  }
@@ -64,7 +64,7 @@ function storedItems(root: string): MemoryItem[] {
64
64
  return items;
65
65
  }
66
66
 
67
- async function summarize(source: MemorySource, injected?: Record<string, MemorySummary>): Promise<MemorySummary> {
67
+ export async function summarize(source: MemorySource, injected?: Record<string, MemorySummary>): Promise<MemorySummary> {
68
68
  if (injected) {
69
69
  const result = injected[source.source];
70
70
  if (!result) throw new Error(`missing test summary: ${source.source}`);
@@ -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
  }
@@ -0,0 +1,66 @@
1
+ import { awaitGlobalPersonaLoad, getGlobalPersonaRegistry } from '../../persona/global-registry.js';
2
+ import { resolvePersonaAddress } from '../../intake-plane/persona-answer.js';
3
+ import { loadSession, updateSessionMeta } from '../../session/index.js';
4
+ import { assemblePersonaPrompt } from '../../persona/prompt-assembler.js';
5
+ import type { LLMMessage } from '../../llm.js';
6
+
7
+ /** Unattached conversations use a dashboard-local id; attached sessions use persisted metadata. */
8
+ export class TuiChatPersonaSelection {
9
+ private readonly selected = new Map<string, string>();
10
+
11
+ get(sessionId: string): string | undefined {
12
+ return loadSession(sessionId)?.meta.chatPersonaId ?? this.selected.get(sessionId);
13
+ }
14
+
15
+ set(sessionId: string, personaId: string | undefined): void {
16
+ const meta = updateSessionMeta(sessionId, (m) => {
17
+ if (personaId) m.chatPersonaId = personaId;
18
+ else delete m.chatPersonaId;
19
+ });
20
+ if (!meta) {
21
+ if (personaId) this.selected.set(sessionId, personaId);
22
+ else this.selected.delete(sessionId);
23
+ } else {
24
+ this.selected.delete(sessionId);
25
+ }
26
+ }
27
+ }
28
+
29
+ export async function handleTuiChatPersona(
30
+ args: readonly string[], sessionId: string,
31
+ selection: TuiChatPersonaSelection,
32
+ ): Promise<string> {
33
+ if (args[0] === '-') {
34
+ if (args.length !== 1) return '쓰는 법: /persona [이름|-]';
35
+ selection.set(sessionId, undefined);
36
+ return '이 대화의 페르소나를 뺐습니다';
37
+ }
38
+ try {
39
+ await awaitGlobalPersonaLoad();
40
+ const registry = getGlobalPersonaRegistry();
41
+ if (!args.length) {
42
+ const selected = selection.get(sessionId);
43
+ const personas = registry.list().sort((a, b) => a.displayName.localeCompare(b.displayName));
44
+ const current = personas.find(p => p.personaId === selected);
45
+ return `이 대화의 페르소나: ${current ? `${current.displayName} (${current.personaId})` : '없음'}\n고를 수 있는 페르소나: ${personas.map(p => `${p.displayName} (${p.personaId})${p.personaId === selected ? ' (지금)' : ''}`).join(' · ') || '없음'}\n고르기: /persona <이름> · 빼기: /persona -`;
46
+ }
47
+ const name = args.join(' ');
48
+ const persona = resolvePersonaAddress(name, registry);
49
+ if (!persona) return `그런 페르소나가 없습니다: ${name}`;
50
+ selection.set(sessionId, persona.personaId);
51
+ return `${persona.displayName} 페르소나를 이 대화에 골랐습니다`;
52
+ } catch {
53
+ return '페르소나 목록을 읽지 못했습니다';
54
+ }
55
+ }
56
+
57
+ export function tuiChatPersonaMessages(
58
+ sessionId: string, selection: TuiChatPersonaSelection,
59
+ ): LLMMessage[] {
60
+ const id = selection.get(sessionId);
61
+ if (!id) return [];
62
+ const persona = getGlobalPersonaRegistry().get(id);
63
+ if (!persona) return [];
64
+ const prompt = assemblePersonaPrompt(persona, '').systemPrompt;
65
+ return prompt ? [{ role: 'system', content: prompt }] : [];
66
+ }