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
@@ -491,6 +491,8 @@ export interface AgentMissionSpec {
491
491
  memory?: boolean;
492
492
  /** PTY 닉네임(휴먼 리더블·goto 로 나중 접근). 기본 branch. */
493
493
  nickname?: string;
494
+ /** Concurrent missions may share one decision stream alias without sharing a PTY or worktree. */
495
+ decisionMissionId?: string;
494
496
  /** ⭐⭐ 그 브랜치를 이미 쥔 «소유» 워크트리가 있으면 지우지 말고 그대로 재사용하라고 요청한다
495
497
  * (`createWorktree` 의 같은 이름 인자로 그대로 내려간다 · 판정은 거기 `gateWorktreeReuse` 가 한다).
496
498
  *
@@ -513,11 +515,14 @@ export interface AgentMissionResult {
513
515
  paths?: string[];
514
516
  rounds: number;
515
517
  evidencePath: string | null;
518
+ /** The evidence check outcome, independent of the final mission and DRIVE-OK verdicts. */
519
+ evidenceSatisfied?: boolean;
516
520
  committed: boolean;
517
521
  usedOmniCrawl: boolean;
518
522
  detail: string;
519
523
  ptyId?: string;
520
524
  webUrl?: string | null;
525
+ driveVerdict?: ReturnType<typeof verdictForFinalPtyScreen>['kind'];
521
526
  }
522
527
  /** @deprecated codex 특정 이름 — AgentMissionResult 을 쓰라. */
523
528
  export type CodexMissionResult = AgentMissionResult;
@@ -1609,7 +1614,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
1609
1614
  const finalEv = verifyEvidence(wt.path, spec.evidence);
1610
1615
  if (!finalEv.ok) return {
1611
1616
  ok: false, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1612
- evidencePath: null, committed: false, usedOmniCrawl: false,
1617
+ evidencePath: null, evidenceSatisfied: false, committed: false, usedOmniCrawl: false,
1613
1618
  detail: `미완(증거 부족): ${finalEv.retry ?? '증거가 아직 부족하다.'}`,
1614
1619
  };
1615
1620
  if (checklist.length) {
@@ -1627,13 +1632,13 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
1627
1632
  if (!c.ok) {
1628
1633
  debug.log('agent-mission', 'result', { ok: false, rounds: result.numTurns ?? 0, evidencePath: finalEv.path, committed: false, usedOmni: false });
1629
1634
  return { ok: false, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1630
- evidencePath: finalEv.path, committed: false, usedOmniCrawl: false,
1635
+ evidencePath: finalEv.path, evidenceSatisfied: true, committed: false, usedOmniCrawl: false,
1631
1636
  detail: `미완(자동 커밋 실패): ${c.out.slice(0, 120)}` };
1632
1637
  }
1633
1638
  }
1634
1639
  debug.log('agent-mission', 'result', { ok: true, rounds: result.numTurns ?? 0, evidencePath: finalEv.path, committed, usedOmni: false });
1635
1640
  return { ok: true, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1636
- evidencePath: finalEv.path, committed, usedOmniCrawl: false, detail: '완료(증거 충족)' };
1641
+ evidencePath: finalEv.path, evidenceSatisfied: true, committed, usedOmniCrawl: false, detail: '완료(증거 충족)' };
1637
1642
  }
1638
1643
  env.TERM = 'xterm-256color';
1639
1644
  // ⭐run-identity — runId was resolved before either headless or PTY spawn.
@@ -1713,7 +1718,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
1713
1718
  process.stderr.write(`[agent-mission] pty=${h.id} watch=${webAddress.webUrl ?? `(web unavailable: ${webAddress.pwaUnavailableReason ?? 'not-resolved'})`}\n`);
1714
1719
  debug.log('agent-mission', 'pty-link', { id: h.id, webUrl: webAddress.webUrl, webUrlSource: webAddress.webUrlSource });
1715
1720
 
1716
- const decisionIdentity = { missionId: runId, sessionId: runId, terminalId: h.id, agent: backend.name };
1721
+ const decisionIdentity = { missionId: spec.decisionMissionId ?? runId, sessionId: runId, terminalId: h.id, agent: backend.name };
1717
1722
  const decisionEvent = (event: Omit<DecisionInput, keyof typeof decisionIdentity>): void => {
1718
1723
  try { (deps.emitPtyDecision ?? emitPtyDecision)({ ...decisionIdentity, ...event } as DecisionInput); }
1719
1724
  catch { /* Observation must not change mission control or handoff. */ }
@@ -2142,7 +2147,7 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
2142
2147
  try { h.kill(); } catch { /* noop */ }
2143
2148
  return { ok: true, worktree: wt.path, branch: wt.branch, rounds: control.steps, evidencePath: wt.path, committed: true, usedOmniCrawl: usedOmni,
2144
2149
  ...(handoffPrSkipped ? { pr: 'skipped' as const, reason: 'origin-not-github' as const } : {}),
2145
- detail: handoffPrSkipped ? '완료(증거 게이트·push·PR 생략)' : '완료(증거 게이트·PR)' };
2150
+ evidenceSatisfied: true, detail: handoffPrSkipped ? '완료(증거 게이트·push·PR 생략)' : '완료(증거 게이트·PR)' };
2146
2151
  }
2147
2152
  const done = control.termination.kind === 'success';
2148
2153
  const round = control.steps;
@@ -2197,8 +2202,8 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
2197
2202
  rounds: round, evidencePath: null, committed: false, usedOmniCrawl: usedOmni, detail: 'aborted' };
2198
2203
  const finalEv = verifyEvidence(wt.path, spec.evidence);
2199
2204
  const finalScreen = await h.renderScreen();
2200
- const driveVerdict = verdictForFinalPtyScreen({ screen: finalScreen, inputHistory: submittedInputs, exitCode: h.exitCode ?? null });
2201
- debug.log('agent-mission', 'drive-verdict', { kind: driveVerdict.kind, reason: driveVerdict.reason.slice(0, 200) });
2205
+ const driveVerdict = verdictForFinalPtyScreen({ screen: finalScreen, inputHistory: submittedInputs, exitCode: h.exitCode ?? null, artifactEvidence: finalEv.ok });
2206
+ debug.log('agent-mission', 'drive-ok', { verdict: driveVerdict.kind, reason: driveVerdict.reason.slice(0, 200), evidence: finalEv.ok });
2202
2207
  const recoverExhausted = control.termination.kind === 'stuck' && control.termination.reason.startsWith('사람 필요');
2203
2208
  const accepted = finalEv.ok && !recoverExhausted && driveVerdict.kind !== 'done-but-failed';
2204
2209
  const evidencePath = accepted ? finalEv.path : null;
@@ -2216,9 +2221,9 @@ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDep
2216
2221
 
2217
2222
  return {
2218
2223
  ok: accepted, worktree: wt.path, branch: wt.branch, rounds: round,
2219
- evidencePath, committed, usedOmniCrawl: usedOmni,
2220
- ptyId: h.id, webUrl: webAddress.webUrl,
2221
- detail: recoverExhausted ? control.termination.reason : driveVerdict.kind === 'done-but-failed' ? `미완(DRIVE-OK: ${driveVerdict.reason})` : done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
2224
+ evidencePath, evidenceSatisfied: finalEv.ok, committed, usedOmniCrawl: usedOmni,
2225
+ ptyId: h.id, webUrl: webAddress.webUrl, driveVerdict: driveVerdict.kind,
2226
+ detail: recoverExhausted ? control.termination.reason : driveVerdict.kind === 'done-but-failed' ? `미완(DRIVE-OK: ${driveVerdict.reason})` : driveVerdict.kind === 'success-unverified' && finalEv.ok ? '완료(증거)' : done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
2222
2227
  };
2223
2228
  } finally { stopLive(); finishContext(false); }
2224
2229
  }
@@ -0,0 +1,94 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import { readFileSync } from 'node:fs';
3
+ import { runAgentMission, resolveBackend, type AgentBackend, type AgentMissionResult, type AgentMissionSpec, type EvidenceMode } from './driver.js';
4
+
5
+ export interface DuoOptions {
6
+ left: string;
7
+ right: string;
8
+ branch: string;
9
+ base?: string;
10
+ missionFile?: string;
11
+ evidence?: string;
12
+ docDir?: string;
13
+ docGlob?: string;
14
+ testPath?: string;
15
+ file?: string;
16
+ maxRounds?: string;
17
+ commit?: boolean;
18
+ }
19
+
20
+ export interface DuoSide {
21
+ side: 'left' | 'right';
22
+ backend: string;
23
+ branch: string;
24
+ finished: number;
25
+ result?: AgentMissionResult;
26
+ error?: string;
27
+ }
28
+
29
+ export interface DuoResult {
30
+ missionId: string;
31
+ sides: [DuoSide, DuoSide];
32
+ lines: [string, string];
33
+ exitCode: number;
34
+ }
35
+
36
+ export interface DuoDeps {
37
+ resolveBackend?: typeof resolveBackend;
38
+ runMission?: typeof runAgentMission;
39
+ readFile?: (path: string) => string;
40
+ id?: () => string;
41
+ }
42
+
43
+ function evidenceFor(opts: DuoOptions): EvidenceMode {
44
+ if (opts.evidence === 'doc') return { kind: 'doc', dirRel: opts.docDir ?? 'docs/plans', glob: new RegExp(opts.docGlob ?? '^PLAN-.*[.]md$', 'i') };
45
+ if (opts.evidence === 'test') {
46
+ if (!opts.testPath) throw new Error('test 모드엔 --test-path 필요');
47
+ return { kind: 'test', testPath: opts.testPath, ...(opts.file ? { fileRel: opts.file } : {}) };
48
+ }
49
+ if (opts.evidence === undefined || opts.evidence === 'tsc') return { kind: 'tsc' };
50
+ throw new Error('--evidence 는 doc|tsc|test 여야 합니다');
51
+ }
52
+
53
+ function render(side: DuoSide, first: DuoSide | undefined): string {
54
+ const evidence = side.result?.evidenceSatisfied === true
55
+ ? side.result.evidencePath ? `충족(${side.result.evidencePath})` : '충족'
56
+ : side.result?.evidenceSatisfied === false ? '미충족' : '미측정';
57
+ const drive = side.result?.driveVerdict ?? '미측정';
58
+ const detail = side.error ?? (!side.result?.ok ? side.result?.detail : undefined);
59
+ return `${side.side} ${side.backend} · branch=${side.branch} · terminalId=${side.result?.ptyId ?? '없음'} · ${first === side ? '먼저' : '나중'} · 증거 ${evidence} · DRIVE-OK ${drive} · ${side.result?.ok ? '완료' : '실패'}${detail ? ` (${detail})` : ''}`;
60
+ }
61
+
62
+ /** Run two independent existing agent missions; neither branch waits for or cancels its peer. */
63
+ export async function runAgentMissionDuo(textParts: string[], opts: DuoOptions, deps: DuoDeps = {}): Promise<DuoResult> {
64
+ if (!opts.branch?.trim()) throw new Error('--branch 필요');
65
+ if (opts.left !== 'codex' && opts.left !== 'claude') throw new Error('--left 는 codex|claude 여야 합니다');
66
+ if (opts.right !== 'codex' && opts.right !== 'claude') throw new Error('--right 는 codex|claude 여야 합니다');
67
+ if (opts.left === opts.right) throw new Error('duo 에는 서로 다른 backend 둘이 필요합니다');
68
+ const mission = opts.missionFile ? (deps.readFile ?? ((path) => readFileSync(path, 'utf8')))(opts.missionFile) : textParts.join(' ');
69
+ if (!mission.trim()) throw new Error('미션 텍스트가 비었다 — <text...> 또는 --mission-file 필요');
70
+ const evidence = evidenceFor(opts);
71
+ const rounds = opts.maxRounds === undefined ? 16 : Number(opts.maxRounds);
72
+ if (!Number.isSafeInteger(rounds) || rounds <= 0) throw new Error('--max-rounds 는 양의 정수여야 합니다');
73
+ const resolve = deps.resolveBackend ?? resolveBackend;
74
+ const backends: [AgentBackend, AgentBackend] = [resolve(opts.left), resolve(opts.right)];
75
+ if (backends[0].name !== opts.left || backends[1].name !== opts.right) throw new Error('backend 선택이 요청과 다릅니다');
76
+ const id = `duo-${(deps.id ?? randomUUID)()}`;
77
+ const run = deps.runMission ?? runAgentMission;
78
+ let completed = 0;
79
+ const branches = [`${opts.branch}-${opts.left}`, `${opts.branch}-${opts.right}`] as const;
80
+ const tasks = backends.map(async (backend, index): Promise<DuoSide> => {
81
+ const side = index === 0 ? 'left' : 'right';
82
+ const branch = branches[index]!;
83
+ const spec: AgentMissionSpec = {
84
+ mission, branch, agent: backend, decisionMissionId: id, evidence, maxRounds: rounds,
85
+ ...(opts.base ? { base: opts.base } : {}), commit: opts.commit !== false,
86
+ enhance: false, memory: false, resources: 'off', entry: 'external-verbatim',
87
+ };
88
+ try { return { side, backend: backend.name, branch, result: await run(spec), finished: ++completed }; }
89
+ catch (error) { return { side, backend: backend.name, branch, error: error instanceof Error ? error.message : String(error), finished: ++completed }; }
90
+ });
91
+ const sides = await Promise.all(tasks) as [DuoSide, DuoSide];
92
+ const first = sides[0].finished <= sides[1].finished ? sides[0] : sides[1];
93
+ return { missionId: id, sides, lines: [render(sides[0], first), render(sides[1], first)], exitCode: sides.every((s) => s.result?.ok) ? 0 : 2 };
94
+ }
@@ -44,7 +44,7 @@ function safe(value: string): string {
44
44
  export function emitPtyDecision(input: DecisionInput, log: typeof debug.log = (category, event, data) => debug.log(category, event, data)): PtyDecision {
45
45
  let stream = missionStreams.get(input.missionId);
46
46
  if (!stream) {
47
- stream = { alias: `mission-${missionStreams.size + 1}`, seq: -1 };
47
+ stream = { alias: `${input.missionId.startsWith('duo-') ? 'duo' : 'mission'}-${missionStreams.size + 1}`, seq: -1 };
48
48
  missionStreams.set(input.missionId, stream);
49
49
  }
50
50
  stream.seq += 1;
@@ -10,6 +10,9 @@ import { readFileSync, writeFileSync } from 'node:fs';
10
10
  import { runGitCommand } from '../../git-fs/runner.js';
11
11
  import { countTestDeclarations } from '../../self-implement/test-declarations.js';
12
12
  import { NEXT_MD_PATH, resolveNextMdConflict } from '../../release-loop/next-md-merge.js';
13
+ import { getUserConfig } from '../../user-config.js';
14
+ import { debug } from '../../debug/log.js';
15
+ import { collectMergeIntent, defaultIntentGit, type IntentGit } from './merge-intent.js';
13
16
 
14
17
  function deterministicNextMd(git: MergeGitSeam, worktreePath: string, file: string): string | null {
15
18
  try {
@@ -328,27 +331,61 @@ export function defaultGitMergeSeam(): MergeGitSeam {
328
331
  }
329
332
 
330
333
  /** 실 LLM 충돌 해결 어댑터(streamLLM·sol). 코드펜스/설명 제거해 완결 파일만. */
331
- export async function defaultLlmResolve(filePath: string, conflicted: string, mergeTarget: string): Promise<string> {
332
- const { streamLLM } = await import('../../llm.js');
333
- const out = await streamLLM([{ role: 'user', content: conflictResolvePrompt(filePath, conflicted, mergeTarget) }], () => {}, { model: process.env.ELANOUS_CONFLICT_MODEL || tierModel('better'), reasoningEffort: 'medium' });
334
- // ⛔ 라우터는 공급자가 전부 막히면 던지지 않고 오류 문구를 돌려준다 — 그것은 파일 내용이 아니다. 던져서 «LLM 예외 → base 유지» 로 보낸다.
335
- if (PROVIDER_FAILURE_TEXT.test(out)) throw new Error(`conflict resolve: LLM provider failure for ${filePath}`);
336
- return `${out.replace(/^```[\w.-]*\n?/, '').replace(/\n?```\s*$/, '').trimEnd()}\n`;
334
+ export async function defaultLlmResolve(
335
+ filePath: string, conflicted: string, mergeTarget: string,
336
+ options: { worktreePath?: string; mode?: 'off' | 'shadow' | 'on'; git?: IntentGit; stream?: typeof import('../../llm.js')['streamLLM'] } = {},
337
+ ): Promise<string> {
338
+ let rawMode: unknown = options.mode;
339
+ if (rawMode === undefined) {
340
+ try { rawMode = (getUserConfig().raw.selfImplement as { mergeIntent?: unknown } | undefined)?.mergeIntent; }
341
+ catch { /* unreadable config keeps the old resolver */ }
342
+ }
343
+ const mode = rawMode === 'shadow' || rawMode === 'on' ? rawMode : 'off';
344
+ const streamLLM = options.stream ?? (await import('../../llm.js')).streamLLM;
345
+ const resolve = async (intent?: { ours: string | null; theirs: string[] }): Promise<string> => {
346
+ const out = await streamLLM([{ role: 'user', content: conflictResolvePrompt(filePath, conflicted, mergeTarget, intent) }], () => {}, { model: process.env.ELANOUS_CONFLICT_MODEL || tierModel('better'), reasoningEffort: 'medium' });
347
+ // ⛔ 라우터는 공급자가 전부 막히면 던지지 않고 오류 문구를 돌려준다 — 그것은 파일 내용이 아니다.
348
+ if (PROVIDER_FAILURE_TEXT.test(out)) throw new Error(`conflict resolve: LLM provider failure for ${filePath}`);
349
+ return `${out.replace(/^```[\w.-]*\n?/, '').replace(/\n?```\s*$/, '').trimEnd()}\n`;
350
+ };
351
+ if (mode === 'off') return resolve();
352
+ const collect = () => collectMergeIntent({ worktreePath: options.worktreePath ?? process.cwd(), filePath, mergeTarget, git: options.git ?? defaultIntentGit });
353
+ if (mode === 'on') return resolve(collect());
354
+ const plain = await resolve();
355
+ try {
356
+ const intent = collect();
357
+ const withIntent = await resolve(intent);
358
+ debug.log('self-dev.merge', 'intent-shadow', {
359
+ file: filePath, same: plain === withIntent,
360
+ plainMarkers: hasConflictMarkers(plain), intentMarkers: hasConflictMarkers(withIntent),
361
+ plainLines: lineCount(plain), intentLines: lineCount(withIntent),
362
+ oursIntent: intent.ours !== null, theirsCount: intent.theirs.length,
363
+ });
364
+ } catch (error) {
365
+ try { debug.log('self-dev.merge', 'intent-shadow-failed', { file: filePath, error: String(error) }); }
366
+ catch { /* shadow observation must not affect the adopted result */ }
367
+ }
368
+ return plain;
337
369
  }
338
370
 
339
371
  /** 편의 — 실 git+LLM 으로 호출부가 해석한 ref를 worktree 에 지능 정합한다. */
340
372
  export async function mergeMainIntoWorktreeWithLlm(worktreePath: string, mergeTarget: string): Promise<LlmMergeOutcome> {
341
- return mergeMainWithLlmResolve(worktreePath, mergeTarget, (filePath, conflicted) => defaultLlmResolve(filePath, conflicted, mergeTarget), defaultGitMergeSeam());
373
+ return mergeMainWithLlmResolve(worktreePath, mergeTarget, (filePath, conflicted) => defaultLlmResolve(filePath, conflicted, mergeTarget, { worktreePath }), defaultGitMergeSeam());
342
374
  }
343
375
 
344
376
  /** LLM 충돌 해결 프롬프트(순수·테스트) — ours(walker)·theirs(호출자가 전달한 정합 대상) 종합 지시. */
345
- export function conflictResolvePrompt(filePath: string, conflictedContent: string, mergeTarget: string): string {
377
+ export function conflictResolvePrompt(filePath: string, conflictedContent: string, mergeTarget: string, intent?: { ours: string | null; theirs: string[] }): string {
346
378
  return [
347
379
  '너는 git merge 충돌을 지능적으로 해결하는 엔지니어다. 아래 파일은 3-way merge 충돌 마커를 포함한다:',
348
380
  ' <<<<<<< ours = 현재 브랜치(walker 가 이 미션에서 만든 산출물)',
349
381
  ' ======= 사이 = 양쪽 버전',
350
382
  ` >>>>>>> theirs = ${mergeTarget}(호출자가 해석해 전달한 정합 대상)`,
351
383
  '',
384
+ ...(intent ? [
385
+ `ours 의 의도: ${intent.ours ?? '(확인 불가)'}`,
386
+ `theirs 에 먼저 착지한 변경: ${intent.theirs.length ? intent.theirs.join(' · ') : '(확인 불가)'}`,
387
+ '',
388
+ ] : []),
352
389
  '해결 원칙:',
353
390
  '- 양쪽의 의도를 **모두 보존**하며 종합한다(한쪽을 통째로 버리지 않는다).',
354
391
  `- 같은 목적의 중복(예: 같은 테스트·같은 함수)은 **theirs(${mergeTarget}) 버전을 채택**하고 ours 의 중복은 제거.`,
@@ -0,0 +1,53 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { runGitCommand } from '../../git-fs/runner.js';
4
+
5
+ export interface MergeIntent {
6
+ ours: string | null;
7
+ theirs: string[];
8
+ }
9
+
10
+ export type IntentGit = (worktreePath: string, args: string[]) => { status: number | null; stdout: string };
11
+
12
+ export const defaultIntentGit: IntentGit = (worktreePath, args) => {
13
+ const result = runGitCommand(worktreePath, args, { encoding: 'utf8' });
14
+ return { status: result.status, stdout: result.stdout ?? '' };
15
+ };
16
+
17
+ /** Read only commits introduced on each side of the merge base; a missing ref/document never blocks resolution. */
18
+ export function collectMergeIntent({ worktreePath, filePath, mergeTarget, git }: {
19
+ worktreePath: string;
20
+ filePath: string;
21
+ mergeTarget: string;
22
+ git: IntentGit;
23
+ }): MergeIntent {
24
+ const read = (...args: string[]): string | null => {
25
+ try {
26
+ const result = git(worktreePath, args);
27
+ return result.status === 0 ? result.stdout.trim() : null;
28
+ } catch { return null; }
29
+ };
30
+ const base = read('merge-base', 'HEAD', mergeTarget);
31
+ if (!base) return { ours: null, theirs: [] };
32
+
33
+ let ours: string | null = null;
34
+ const added = read('log', '--reverse', '--diff-filter=A', '--format=', '--name-only', '-z', `${base}..HEAD`, '--', 'docs/goals/');
35
+ for (const goal of added?.split('\0').map((file) => file.trim()).filter((file) => /^docs\/goals\/[^/]+$/.test(file)) ?? []) {
36
+ try {
37
+ const content = readFileSync(join(worktreePath, goal), 'utf8');
38
+ const lines = content.split(/\r?\n/);
39
+ const title = lines.find((line) => /^#\s+\S/.test(line))?.replace(/^#\s+/, '').trim()
40
+ ?? lines.find((line) => line.trim().length > 0)?.trim();
41
+ const situation = lines.find((line) => /^Situation:/.test(line))?.trim();
42
+ const complication = lines.find((line) => /^Complication:/.test(line))?.trim();
43
+ ours = [title, situation, complication].filter(Boolean).join('\n') || null;
44
+ if (ours) break;
45
+ } catch { /* missing goal: try the next added document */ }
46
+ }
47
+ if (!ours) {
48
+ const subjects = read('log', '-3', '--format=%s', `${base}..HEAD`);
49
+ ours = subjects || null;
50
+ }
51
+ const subjects = read('log', '-5', '--format=%s', `${base}..${mergeTarget}`, '--', filePath);
52
+ return { ours, theirs: subjects ? subjects.split('\n') : [] };
53
+ }
@@ -2,6 +2,7 @@ import { execFile } from 'node:child_process';
2
2
  import { appendFileSync, mkdirSync, readFileSync, rmdirSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
+ import { stateDirSourceForChild } from '../agent/identity-env.js';
5
6
  import { effectiveInstanceRoot } from '../instance/resolve.js';
6
7
  import type { TrackAgentDecision, TrackAgentInput, TrackAgentResult } from './track-agent.js';
7
8
 
@@ -127,7 +128,7 @@ export async function launchTrackAgentShadow(input: TrackAgentInput, options: Sh
127
128
  const instanceRoot = effectiveInstanceRoot();
128
129
  const { stdout } = await exec('bun', ['bin/elanous.mjs', '--config-dir', instanceRoot, ...values], {
129
130
  cwd: process.env.ELANOUS_TOOL_CWD?.trim() || resolve(import.meta.dir, '../..'),
130
- env: { ...process.env, ELANOUS_STATE_DIR: instanceRoot },
131
+ env: { ...process.env, ELANOUS_STATE_DIR: instanceRoot, ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(instanceRoot) },
131
132
  encoding: 'utf8', maxBuffer: 1024 * 1024,
132
133
  });
133
134
  return { stdout };
@@ -2,6 +2,7 @@ import { execFile, spawn } from 'node:child_process';
2
2
  import { readdirSync, readFileSync } from 'node:fs';
3
3
  import { resolve } from 'node:path';
4
4
  import { promisify } from 'node:util';
5
+ import { stateDirSourceForChild } from '../agent/identity-env.js';
5
6
  import { debug, redactSecretText } from '../debug/log.js';
6
7
  import { getOrCreatePersonaSession } from '../session/index.js';
7
8
  import { getUserConfig } from '../user-config.js';
@@ -38,7 +39,9 @@ const log = (event: string, data: Record<string, unknown>) => {
38
39
  };
39
40
  /** The daemon's own universe, resolved once and pinned on the child (config ⊕ state) — the harness isolates its own workers. */
40
41
  export function universeLaunch(args: readonly string[], root: string = effectiveInstanceRoot()): { argv: string[]; env: NodeJS.ProcessEnv } {
41
- return { argv: ['bin/elanous.mjs', '--config-dir', root, ...args], env: { ...process.env, ELANOUS_STATE_DIR: root } };
42
+ return { argv: ['bin/elanous.mjs', '--config-dir', root, ...args], env: {
43
+ ...process.env, ELANOUS_STATE_DIR: root, ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(root),
44
+ } };
42
45
  }
43
46
  const runCli = async (args: string[]): Promise<{ stdout: string }> => {
44
47
  const { argv, env } = universeLaunch(args);
@@ -38,6 +38,7 @@ export interface DaemonPromptBody {
38
38
  * (`none` · `readonly` · `chat` · `webterm`); invalid values reject
39
39
  * the request rather than silently falling back. */
40
40
  tools?: DaemonToolSurfaceKind;
41
+ personaId?: string;
41
42
  }
42
43
 
43
44
  export interface DaemonPromptRequest {
@@ -53,6 +54,7 @@ export interface DaemonPromptRequest {
53
54
  * not opt in; the caller-side (`handlePromptStreamPost`) interprets
54
55
  * null as "use the daemon's boot-time surface". */
55
56
  tools: DaemonToolSurfaceKind | null;
57
+ personaId?: string;
56
58
  }
57
59
 
58
60
  export type DaemonPromptRequestParseResult =
@@ -117,6 +119,7 @@ export function parseDaemonPromptBody(
117
119
  body.source ? writeInputSourceMeta(body.source) : undefined,
118
120
  ),
119
121
  tools: body.tools ?? null,
122
+ ...(typeof body.personaId === 'string' && body.personaId.trim() ? { personaId: body.personaId } : {}),
120
123
  },
121
124
  };
122
125
  }
@@ -10,7 +10,7 @@ import {
10
10
  appendUserPromptBlocksAndBuildMessages,
11
11
  } from './daemon-history-helper.js';
12
12
  import type { ContentBlock as AcpContentBlock } from '@agentclientprotocol/sdk';
13
- import type { DaemonSessionHistory } from './daemon-runtime.js';
13
+ import { personaPromptForChat, type DaemonSessionHistory } from './daemon-runtime.js';
14
14
  import type { DaemonPromptRequest } from './daemon-prompt-request.js';
15
15
  import type { DaemonToolSurface } from './daemon-tools/types.js';
16
16
  import type { ConfirmChannel } from '../hitl/confirm.js';
@@ -199,8 +199,9 @@ export async function runDaemonPromptTurn(opts: {
199
199
  // …" + active terminals) to the system prompt so the LLM has the
200
200
  // session id + terminal ids without an extra discovery call. Helper
201
201
  // returns the original prompt unchanged when there's nothing to add.
202
+ const personaPrompt = await personaPromptForChat(request.personaId);
202
203
  const augmentedSystemPrompt = appendWebtermContext(
203
- request.effectiveSystemPrompt,
204
+ personaPrompt ? [personaPrompt, request.effectiveSystemPrompt].filter(Boolean).join('\n\n') : request.effectiveSystemPrompt,
204
205
  request.sessionId,
205
206
  activeToolSurface,
206
207
  );
@@ -291,6 +292,9 @@ export async function runDaemonPromptTurn(opts: {
291
292
  if (!isFirstTurn && opts.projectInstructions) {
292
293
  messages.unshift({ role: 'system', content: opts.projectInstructions });
293
294
  }
295
+ if (!isFirstTurn && personaPrompt) {
296
+ messages.unshift({ role: 'system', content: personaPrompt });
297
+ }
294
298
  const result = await runCoreTurn({
295
299
  sessionId: request.sessionId,
296
300
  userText: request.userText,
@@ -55,6 +55,33 @@ import { elanousSelfAccessPrompt } from '../agent/self-ambient.js';
55
55
  import { createToolCwdResolver, type ToolCwdResolver } from './tool-cwd.js';
56
56
  import { resolveSessionProjectContext } from '../project/session-context.js';
57
57
  import { debug } from '../debug/log.js';
58
+ import { resolvePersonaAddress } from '../intake-plane/persona-answer.js';
59
+ import { awaitGlobalPersonaLoad, getGlobalPersonaRegistry } from '../persona/global-registry.js';
60
+
61
+ const turnPersonaPrompt = new AsyncLocalStorage<string | undefined>();
62
+
63
+ export function withChatPersonaPrompt<T>(prompt: string | undefined, run: () => T): T {
64
+ return turnPersonaPrompt.run(prompt, run);
65
+ }
66
+
67
+ export function prependChatPersonaOnLaterTurn(
68
+ messages: LLMMessage[], isFirstTurn: boolean,
69
+ ): LLMMessage[] {
70
+ const prompt = turnPersonaPrompt.getStore();
71
+ if (!isFirstTurn && prompt) messages.unshift({ role: 'system', content: prompt });
72
+ return messages;
73
+ }
74
+
75
+ export async function personaPromptForChat(
76
+ id: unknown,
77
+ deps: { load: () => Promise<unknown>; registry: () => Parameters<typeof resolvePersonaAddress>[1] } = {
78
+ load: awaitGlobalPersonaLoad, registry: getGlobalPersonaRegistry,
79
+ },
80
+ ): Promise<string | undefined> {
81
+ if (typeof id !== 'string' || !id.trim()) return undefined;
82
+ await deps.load();
83
+ return resolvePersonaAddress(id, deps.registry())?.systemPrompt;
84
+ }
58
85
 
59
86
  /** C4 (2026-07-12) — arm the non-detached-PTY kill ONCE per turn signal.
60
87
  * Multiple tool calls in one turn share the same AbortSignal instance;
@@ -135,7 +162,7 @@ export function composeDaemonSystemPrompt(
135
162
  promptMeta: Readonly<Record<string, unknown>> | undefined,
136
163
  sessionId?: string,
137
164
  ): string {
138
- return [basePrompt, buildDaemonInputSourceLine(promptMeta), elanousSelfAccessPrompt(sessionId)]
165
+ return [turnPersonaPrompt.getStore(), basePrompt, buildDaemonInputSourceLine(promptMeta), elanousSelfAccessPrompt(sessionId)]
139
166
  .filter((part): part is string => typeof part === 'string' && part.trim().length > 0)
140
167
  .join('\n\n');
141
168
  }
@@ -604,12 +631,11 @@ export function createDaemonRunTurn(
604
631
  const hasNonTextBlock = !!promptBlocks?.some(
605
632
  (b) => (b as { type?: string }).type !== 'text',
606
633
  );
607
- if (hasNonTextBlock) {
608
- return appendUserPromptBlocksAndBuildMessages(
609
- history, sessionId, promptBlocks!, systemPrompt,
610
- );
611
- }
612
- return appendUserAndBuildMessages(history, sessionId, userText, systemPrompt);
634
+ const isFirstTurn = history.get(sessionId).length === 0;
635
+ const messages = hasNonTextBlock
636
+ ? appendUserPromptBlocksAndBuildMessages(history, sessionId, promptBlocks!, systemPrompt)
637
+ : appendUserAndBuildMessages(history, sessionId, userText, systemPrompt);
638
+ return prependChatPersonaOnLaterTurn(messages, isFirstTurn);
613
639
  },
614
640
  getTools: () => surface.specs,
615
641
  dispatchTool: terminalCapableDispatch(surfaceHasPtyShell, async (name, args, ctx) => {
@@ -723,15 +749,21 @@ export function createDaemonRunTurn(
723
749
  });
724
750
  debugBridge.activate();
725
751
  }
726
- const projectCwd = resolveSessionProjectContext(turnCtx.sessionId)?.cwd;
727
- if (projectCwd || opts.acpSessionCwd) {
728
- // An ACP session's explicit cwd wins; the project folder is only the default when none was given (IA3 review).
729
- const explicitCwd = opts.acpSessionCwd && turnCtx.cwd ? turnCtx.cwd : undefined;
730
- const resolver = createToolCwdResolver({ tools: opts.tools ?? 'none', toolCwd: explicitCwd ?? projectCwd ?? turnCtx.cwd });
731
- await turnToolCwd.run(resolver, () => inner(turnCtx));
732
- } else {
733
- await inner(turnCtx);
734
- }
752
+ const elanousMeta = turnCtx.promptMeta?.elanous;
753
+ const personaId = elanousMeta && typeof elanousMeta === 'object' && !Array.isArray(elanousMeta)
754
+ && 'pwaPersonaId' in elanousMeta ? elanousMeta.pwaPersonaId : undefined;
755
+ const personaPrompt = await personaPromptForChat(personaId);
756
+ await withChatPersonaPrompt(personaPrompt, async () => {
757
+ const projectCwd = resolveSessionProjectContext(turnCtx.sessionId)?.cwd;
758
+ if (projectCwd || opts.acpSessionCwd) {
759
+ // An ACP session's explicit cwd wins; the project folder is only the default when none was given (IA3 review).
760
+ const explicitCwd = opts.acpSessionCwd && turnCtx.cwd ? turnCtx.cwd : undefined;
761
+ const resolver = createToolCwdResolver({ tools: opts.tools ?? 'none', toolCwd: explicitCwd ?? projectCwd ?? turnCtx.cwd });
762
+ await turnToolCwd.run(resolver, () => inner(turnCtx));
763
+ } else {
764
+ await inner(turnCtx);
765
+ }
766
+ });
735
767
  } finally {
736
768
  debugBridge?.dispose();
737
769
  }
package/src/chat/index.ts CHANGED
@@ -452,6 +452,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
452
452
  { name: 'reasoning', aliases: ['r', 'think'], description: 'Reasoning level (codex effort + summary, anthropic extended-thinking budget) — /reasoning [off|low|medium|high|xhigh] (cycle when no arg · xhigh 는 모델 상한이 xhigh 이상일 때만 wire 에 실린다, 아니면 high 로 깎인다)', subcommands: ['off', 'low', 'medium', 'high', 'xhigh'] },
453
453
  { name: 'local', aliases: ['ll'], description: 'Local OpenAI-compatible LLM — ping / models / test / use', subcommands: ['ping', 'models', 'test', 'use', 'status'] },
454
454
  { name: 'session', aliases: ['sess'], description: 'Session resume — list / load / sync / new (handoff from mobile)', subcommands: ['list', 'load', 'sync', 'new'] },
455
+ { name: 'persona', aliases: [], description: '이 대화의 페르소나 보기·고르기·빼기 — /persona [이름|-]', subcommands: ['-'] },
455
456
  { name: 'fork', aliases: [], description: 'Copy this session into a new one' },
456
457
  { name: 'rewind', aliases: [], description: 'Rewind — 과거 user 턴 픽커로 되감기 (원본 보존 · 새 세션 분기 · /rewind <n> 숏컷)' },
457
458
  // TUI 부활 C-a (2026-07-12): autopilot 미션 TUI 표면 — CLI 동등 단일 창구 소비.
@@ -463,6 +464,7 @@ export const SLASH_COMMANDS: SlashCommand[] = [
463
464
  { name: 'plugin', aliases: ['plugins'], description: 'Manage plugins', subcommands: ['list', 'activate', 'deactivate', 'reload'] },
464
465
  { name: 'widget', aliases: ['widgets'], description: 'Manage widgets', subcommands: ['list', 'reload', 'instances'] },
465
466
  { name: 'log', aliases: [], description: 'Chat Log controls — size / clear / filter / search / freeze / solo / turn / fold / help', subcommands: ['size', 'clear', 'filter', 'search', 'freeze', 'solo', 'turn', 'fold', 'help'] },
467
+ { name: 'loops', aliases: [], description: '루프와 크론 상태 표 — /loops [owner]' },
466
468
  { name: 'memory', aliases: ['mem'], description: 'Memory ops — list / show / search / add / delete (see `elanous memory --help`)', subcommands: ['list', 'show', 'search', 'add', 'delete', 'index'] },
467
469
  { name: 'export', aliases: [], description: 'Export this conversation transcript to a markdown file — /export [path] (default ~/temp/elanous-transcript-<stamp>.md)' },
468
470
  { name: 'delta', aliases: ['diffs'], description: 'Source delta browser — open the latest turn-scoped file diff popup', subcommands: ['open', 'help'] },