elanous 0.2.0 → 0.2.5

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 (1249) hide show
  1. package/.env.example +6 -0
  2. package/README.md +17 -15
  3. package/apps/pwa/out/404/index.html +1 -1
  4. package/apps/pwa/out/404.html +1 -1
  5. package/apps/pwa/out/_next/static/chunks/06ef2eba-d1b99ac6d2ea663c.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1413-7179a49332db5a80.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/150-25c277137a9638ab.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1640-bc99ec23bbe29d12.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/1751.50b96ea9c37b49b6.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2300-de059d0ab2a16630.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/2684-05b0a81e75298c16.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/3026-2ed7e317d9b02102.js +25 -0
  13. package/apps/pwa/out/_next/static/chunks/3611-77496ada38cd6358.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/3717-68957c4dc9ceec90.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/3929-46ba9af6f9d895cc.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/4015-62c5a3744698a8da.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/4217-3c4317a7905f9eeb.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/4240-3230f230fae2d7d5.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/4271-e53e075ebd48593b.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4377.3b125fef79576dfd.js +5 -0
  21. package/apps/pwa/out/_next/static/chunks/4440-18bb160d95819f4e.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/4853-35fdbe6bdbcdc0fc.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{2a79f15a-2013e4d421d6df88.js → 4daa2798-1363d725c79f3593.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/5007-6a3b30e23ff68a6e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/5244-12b424c42070f5ff.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/5348-bbf838e119661eee.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/5391-8793f88b03b30161.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/5611-df00af3e4e4a61bb.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/5890-dca2321773be829a.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6123-37c9dc57ddaa7ab0.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/6588-b7975304a302ab91.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6604-c03961962b6273b6.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7109-cf8c87e968e83a65.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7178-30f77877525a2efb.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7262-871563fc9663730b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7294-f0eeebb92aa93459.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/7429-881af44deed6958f.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/{5927.8378481a12cc8044.js → 7527.943753b6ed829f09.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/764-a01e858525a190c0.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/7868-e35d5f7985ecd3f1.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8320-51484af7b01fdfa5.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/{6804.8ed6ab492bc5fc0a.js → 8617.465ca22a319c95b3.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/{320-d7013ec072863e5e.js → 8918-482b30ce2a8ebf4c.js} +4 -4
  44. package/apps/pwa/out/_next/static/chunks/9131-52f5fad51cceca04.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/{2598.814f64e3268634e7.js → 9238.2cd5f47f4bf7a2c3.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/9704-3221c102623c8d73.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-70c404c7bb6d7f0f.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/approvals/page-3ffd80166b1050aa.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f89e9a303515a033.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/board/page-4f403cd060442c95.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0ac5d053f0444d21.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/bots/page-4bcc63e272ae8205.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/chat/page-880734bd07d62882.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/control/page-72bd384898d88955.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/design-check/page-1018bbc9586ecae5.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/editor/page-de86f8c38076dd7b.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/healthz/route-70c404c7bb6d7f0f.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/intake/page-cd6447196d8bafe6.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/layout-e733630c47fa102b.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/live/page-d1c1a8357b161875.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/market/page-60c514128e81d787.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8b637b7e325e13c1.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/morning/page-bccce0babceae30f.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/not-found-63301d11dbe73836.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/observatory/page-a8cf0dc472060db2.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/page-02fba54881ccfa49.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d5e8e90f0038f8de.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-b1ef6824f494466a.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/sessions/page-d589bb0f233b1009.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-c9bbaebef4cda059.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/settings/page-3639ce7916b92fed.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-7450ae7478955653.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/setup/layout-70c404c7bb6d7f0f.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/setup/page-b41ca4abfd618452.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/share/page-b333dd79ebee3fa9.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/showroom/page-8be1227492793655.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6e03e9371b552e0d.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/term/page-911411e9fca18d03.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/trace/page-792170e649b37088.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/vault/page-c40463466937bc91.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-509c798b80384536.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/workflows/page-1cac2aa21d77c417.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/workspace/page-9626799d9b4a2cd1.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-84cd419494e16b0f.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/f6f3850d-7f324c6566002113.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/framework-361487d2da603917.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/main-app-607f5beb94cb2e07.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/main-d0222d219e29dddf.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/pages/_app-a26980a31126decc.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/pages/_error-edb2b37decf23e4d.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/webpack-2bf59c720fe9542c.js +1 -0
  92. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +1 -0
  93. package/apps/pwa/out/_next/static/ktMqfrBbf8BMGac0jqLrb/_buildManifest.js +1 -0
  94. package/apps/pwa/out/approvals/index.html +1 -0
  95. package/apps/pwa/out/approvals/index.txt +32 -0
  96. package/apps/pwa/out/autopilot/index.html +1 -1
  97. package/apps/pwa/out/autopilot/index.txt +29 -26
  98. package/apps/pwa/out/board/index.html +1 -0
  99. package/apps/pwa/out/board/index.txt +32 -0
  100. package/apps/pwa/out/botlab/index.html +1 -1
  101. package/apps/pwa/out/botlab/index.txt +26 -23
  102. package/apps/pwa/out/bots/index.html +1 -1
  103. package/apps/pwa/out/bots/index.txt +26 -23
  104. package/apps/pwa/out/chat/index.html +1 -2
  105. package/apps/pwa/out/chat/index.txt +26 -23
  106. package/apps/pwa/out/control/index.html +1 -1
  107. package/apps/pwa/out/control/index.txt +29 -26
  108. package/apps/pwa/out/design-check/index.html +1 -1
  109. package/apps/pwa/out/design-check/index.txt +29 -26
  110. package/apps/pwa/out/editor/index.html +1 -0
  111. package/apps/pwa/out/editor/index.txt +33 -0
  112. package/apps/pwa/out/healthz +1 -1
  113. package/apps/pwa/out/icons/elanous-16.png +0 -0
  114. package/apps/pwa/out/icons/elanous-180.png +0 -0
  115. package/apps/pwa/out/icons/elanous-192.png +0 -0
  116. package/apps/pwa/out/icons/elanous-32.png +0 -0
  117. package/apps/pwa/out/icons/elanous-512.png +0 -0
  118. package/apps/pwa/out/index.html +1 -1
  119. package/apps/pwa/out/index.txt +26 -23
  120. package/apps/pwa/out/intake/index.html +1 -1
  121. package/apps/pwa/out/intake/index.txt +29 -26
  122. package/apps/pwa/out/live/index.html +1 -0
  123. package/apps/pwa/out/live/index.txt +32 -0
  124. package/apps/pwa/out/manifest.webmanifest +5 -2
  125. package/apps/pwa/out/market/index.html +1 -0
  126. package/apps/pwa/out/market/index.txt +29 -0
  127. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  128. package/apps/pwa/out/missions/placeholder/index.txt +26 -23
  129. package/apps/pwa/out/morning/index.html +1 -1
  130. package/apps/pwa/out/morning/index.txt +29 -26
  131. package/apps/pwa/out/observatory/index.html +1 -1
  132. package/apps/pwa/out/observatory/index.txt +29 -26
  133. package/apps/pwa/out/reflection/index.html +1 -1
  134. package/apps/pwa/out/reflection/index.txt +27 -24
  135. package/apps/pwa/out/scheduler/index.html +1 -1
  136. package/apps/pwa/out/scheduler/index.txt +29 -26
  137. package/apps/pwa/out/sessions/index.html +1 -1
  138. package/apps/pwa/out/sessions/index.txt +26 -23
  139. package/apps/pwa/out/settings/devices/index.html +1 -1
  140. package/apps/pwa/out/settings/devices/index.txt +29 -26
  141. package/apps/pwa/out/settings/index.html +1 -1
  142. package/apps/pwa/out/settings/index.txt +29 -26
  143. package/apps/pwa/out/setup/done/index.html +1 -1
  144. package/apps/pwa/out/setup/done/index.txt +29 -26
  145. package/apps/pwa/out/setup/index.html +1 -1
  146. package/apps/pwa/out/setup/index.txt +29 -26
  147. package/apps/pwa/out/share/index.html +1 -1
  148. package/apps/pwa/out/share/index.txt +29 -26
  149. package/apps/pwa/out/showroom/index.html +1 -1
  150. package/apps/pwa/out/showroom/index.txt +26 -23
  151. package/apps/pwa/out/sw-strategies.js +44 -0
  152. package/apps/pwa/out/sw.js +6 -44
  153. package/apps/pwa/out/tasks/index.html +1 -1
  154. package/apps/pwa/out/tasks/index.txt +29 -26
  155. package/apps/pwa/out/term/index.html +1 -1
  156. package/apps/pwa/out/term/index.txt +29 -26
  157. package/apps/pwa/out/trace/index.html +1 -0
  158. package/apps/pwa/out/trace/index.txt +32 -0
  159. package/apps/pwa/out/vault/index.html +1 -1
  160. package/apps/pwa/out/vault/index.txt +29 -26
  161. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  162. package/apps/pwa/out/workflows/chat-ui/index.txt +29 -26
  163. package/apps/pwa/out/workflows/index.html +1 -1
  164. package/apps/pwa/out/workflows/index.txt +29 -27
  165. package/apps/pwa/out/workspace/index.html +1 -1
  166. package/apps/pwa/out/workspace/index.txt +29 -26
  167. package/apps/pwa/out/worktrees/index.html +1 -1
  168. package/apps/pwa/out/worktrees/index.txt +29 -26
  169. package/catalog/resources.yaml +14 -17
  170. package/docs/design/systems/LICENSE +201 -0
  171. package/docs/design/systems/NOTICE.md +29 -0
  172. package/docs/design/systems/SOURCE.json +291 -0
  173. package/docs/design/systems/application/DESIGN.md +71 -0
  174. package/docs/design/systems/application/manifest.json +54 -0
  175. package/docs/design/systems/application/tokens.css +63 -0
  176. package/docs/design/systems/artistic/DESIGN.md +71 -0
  177. package/docs/design/systems/artistic/manifest.json +54 -0
  178. package/docs/design/systems/artistic/tokens.css +63 -0
  179. package/docs/design/systems/bento/DESIGN.md +71 -0
  180. package/docs/design/systems/bento/manifest.json +54 -0
  181. package/docs/design/systems/bento/tokens.css +63 -0
  182. package/docs/design/systems/bold/DESIGN.md +71 -0
  183. package/docs/design/systems/bold/manifest.json +54 -0
  184. package/docs/design/systems/bold/tokens.css +63 -0
  185. package/docs/design/systems/brutalism/DESIGN.md +71 -0
  186. package/docs/design/systems/brutalism/manifest.json +54 -0
  187. package/docs/design/systems/brutalism/tokens.css +63 -0
  188. package/docs/design/systems/cafe/DESIGN.md +71 -0
  189. package/docs/design/systems/cafe/manifest.json +54 -0
  190. package/docs/design/systems/cafe/tokens.css +63 -0
  191. package/docs/design/systems/claymorphism/DESIGN.md +71 -0
  192. package/docs/design/systems/claymorphism/manifest.json +54 -0
  193. package/docs/design/systems/claymorphism/tokens.css +63 -0
  194. package/docs/design/systems/clean/DESIGN.md +71 -0
  195. package/docs/design/systems/clean/manifest.json +54 -0
  196. package/docs/design/systems/clean/tokens.css +63 -0
  197. package/docs/design/systems/colorful/DESIGN.md +71 -0
  198. package/docs/design/systems/colorful/manifest.json +54 -0
  199. package/docs/design/systems/colorful/tokens.css +63 -0
  200. package/docs/design/systems/contemporary/DESIGN.md +71 -0
  201. package/docs/design/systems/contemporary/manifest.json +54 -0
  202. package/docs/design/systems/contemporary/tokens.css +63 -0
  203. package/docs/design/systems/corporate/DESIGN.md +71 -0
  204. package/docs/design/systems/corporate/manifest.json +54 -0
  205. package/docs/design/systems/corporate/tokens.css +63 -0
  206. package/docs/design/systems/cosmic/DESIGN.md +71 -0
  207. package/docs/design/systems/cosmic/manifest.json +54 -0
  208. package/docs/design/systems/cosmic/tokens.css +63 -0
  209. package/docs/design/systems/creative/DESIGN.md +71 -0
  210. package/docs/design/systems/creative/manifest.json +54 -0
  211. package/docs/design/systems/creative/tokens.css +63 -0
  212. package/docs/design/systems/dashboard/DESIGN.md +71 -0
  213. package/docs/design/systems/dashboard/manifest.json +54 -0
  214. package/docs/design/systems/dashboard/tokens.css +63 -0
  215. package/docs/design/systems/default/DESIGN.md +62 -0
  216. package/docs/design/systems/default/manifest.json +69 -0
  217. package/docs/design/systems/default/tokens.css +200 -0
  218. package/docs/design/systems/dithered/DESIGN.md +71 -0
  219. package/docs/design/systems/dithered/manifest.json +54 -0
  220. package/docs/design/systems/dithered/tokens.css +63 -0
  221. package/docs/design/systems/doodle/DESIGN.md +71 -0
  222. package/docs/design/systems/doodle/manifest.json +54 -0
  223. package/docs/design/systems/doodle/tokens.css +63 -0
  224. package/docs/design/systems/dramatic/DESIGN.md +71 -0
  225. package/docs/design/systems/dramatic/manifest.json +54 -0
  226. package/docs/design/systems/dramatic/tokens.css +63 -0
  227. package/docs/design/systems/editorial/DESIGN.md +71 -0
  228. package/docs/design/systems/editorial/manifest.json +54 -0
  229. package/docs/design/systems/editorial/tokens.css +63 -0
  230. package/docs/design/systems/elegant/DESIGN.md +71 -0
  231. package/docs/design/systems/elegant/manifest.json +54 -0
  232. package/docs/design/systems/elegant/tokens.css +63 -0
  233. package/docs/design/systems/energetic/DESIGN.md +72 -0
  234. package/docs/design/systems/energetic/manifest.json +54 -0
  235. package/docs/design/systems/energetic/tokens.css +63 -0
  236. package/docs/design/systems/enterprise/DESIGN.md +71 -0
  237. package/docs/design/systems/enterprise/manifest.json +54 -0
  238. package/docs/design/systems/enterprise/tokens.css +63 -0
  239. package/docs/design/systems/expressive/DESIGN.md +71 -0
  240. package/docs/design/systems/expressive/manifest.json +54 -0
  241. package/docs/design/systems/expressive/tokens.css +63 -0
  242. package/docs/design/systems/fantasy/DESIGN.md +71 -0
  243. package/docs/design/systems/fantasy/manifest.json +54 -0
  244. package/docs/design/systems/fantasy/tokens.css +63 -0
  245. package/docs/design/systems/flat/DESIGN.md +71 -0
  246. package/docs/design/systems/flat/manifest.json +54 -0
  247. package/docs/design/systems/flat/tokens.css +63 -0
  248. package/docs/design/systems/friendly/DESIGN.md +71 -0
  249. package/docs/design/systems/friendly/manifest.json +54 -0
  250. package/docs/design/systems/friendly/tokens.css +63 -0
  251. package/docs/design/systems/glassmorphism/DESIGN.md +71 -0
  252. package/docs/design/systems/glassmorphism/manifest.json +54 -0
  253. package/docs/design/systems/glassmorphism/tokens.css +63 -0
  254. package/docs/design/systems/gradient/DESIGN.md +71 -0
  255. package/docs/design/systems/gradient/manifest.json +54 -0
  256. package/docs/design/systems/gradient/tokens.css +63 -0
  257. package/docs/design/systems/luxury/DESIGN.md +71 -0
  258. package/docs/design/systems/luxury/manifest.json +54 -0
  259. package/docs/design/systems/luxury/tokens.css +63 -0
  260. package/docs/design/systems/minimal/DESIGN.md +71 -0
  261. package/docs/design/systems/minimal/manifest.json +54 -0
  262. package/docs/design/systems/minimal/tokens.css +63 -0
  263. package/docs/design/systems/modern/DESIGN.md +71 -0
  264. package/docs/design/systems/modern/manifest.json +54 -0
  265. package/docs/design/systems/modern/tokens.css +63 -0
  266. package/docs/design/systems/mono/DESIGN.md +71 -0
  267. package/docs/design/systems/mono/manifest.json +54 -0
  268. package/docs/design/systems/mono/tokens.css +63 -0
  269. package/docs/design/systems/neobrutalism/DESIGN.md +71 -0
  270. package/docs/design/systems/neobrutalism/manifest.json +54 -0
  271. package/docs/design/systems/neobrutalism/tokens.css +63 -0
  272. package/docs/design/systems/neon/DESIGN.md +71 -0
  273. package/docs/design/systems/neon/manifest.json +54 -0
  274. package/docs/design/systems/neon/tokens.css +63 -0
  275. package/docs/design/systems/neumorphism/DESIGN.md +71 -0
  276. package/docs/design/systems/neumorphism/manifest.json +54 -0
  277. package/docs/design/systems/neumorphism/tokens.css +63 -0
  278. package/docs/design/systems/paper/DESIGN.md +71 -0
  279. package/docs/design/systems/paper/manifest.json +54 -0
  280. package/docs/design/systems/paper/tokens.css +63 -0
  281. package/docs/design/systems/perspective/DESIGN.md +71 -0
  282. package/docs/design/systems/perspective/manifest.json +54 -0
  283. package/docs/design/systems/perspective/tokens.css +63 -0
  284. package/docs/design/systems/premium/DESIGN.md +71 -0
  285. package/docs/design/systems/premium/manifest.json +54 -0
  286. package/docs/design/systems/premium/tokens.css +63 -0
  287. package/docs/design/systems/professional/DESIGN.md +71 -0
  288. package/docs/design/systems/professional/manifest.json +54 -0
  289. package/docs/design/systems/professional/tokens.css +63 -0
  290. package/docs/design/systems/publication/DESIGN.md +71 -0
  291. package/docs/design/systems/publication/manifest.json +54 -0
  292. package/docs/design/systems/publication/tokens.css +63 -0
  293. package/docs/design/systems/refined/DESIGN.md +71 -0
  294. package/docs/design/systems/refined/manifest.json +54 -0
  295. package/docs/design/systems/refined/tokens.css +63 -0
  296. package/docs/design/systems/retro/DESIGN.md +71 -0
  297. package/docs/design/systems/retro/manifest.json +54 -0
  298. package/docs/design/systems/retro/tokens.css +63 -0
  299. package/docs/design/systems/shadcn/DESIGN.md +71 -0
  300. package/docs/design/systems/shadcn/manifest.json +54 -0
  301. package/docs/design/systems/shadcn/tokens.css +255 -0
  302. package/docs/design/systems/simple/DESIGN.md +71 -0
  303. package/docs/design/systems/simple/manifest.json +54 -0
  304. package/docs/design/systems/simple/tokens.css +63 -0
  305. package/docs/design/systems/skeumorphism/DESIGN.md +71 -0
  306. package/docs/design/systems/skeumorphism/manifest.json +54 -0
  307. package/docs/design/systems/skeumorphism/tokens.css +63 -0
  308. package/docs/design/systems/sleek/DESIGN.md +71 -0
  309. package/docs/design/systems/sleek/manifest.json +54 -0
  310. package/docs/design/systems/sleek/tokens.css +63 -0
  311. package/docs/design/systems/spacious/DESIGN.md +71 -0
  312. package/docs/design/systems/spacious/manifest.json +54 -0
  313. package/docs/design/systems/spacious/tokens.css +63 -0
  314. package/docs/design/systems/storytelling/DESIGN.md +71 -0
  315. package/docs/design/systems/storytelling/manifest.json +54 -0
  316. package/docs/design/systems/storytelling/tokens.css +63 -0
  317. package/docs/design/systems/tom-modern/DESIGN.md +361 -0
  318. package/docs/design/systems/tom-modern/manifest.json +16 -0
  319. package/docs/design/systems/tom-modern/tokens.css +99 -0
  320. package/docs/design/systems/vibrant/DESIGN.md +71 -0
  321. package/docs/design/systems/vibrant/manifest.json +54 -0
  322. package/docs/design/systems/vibrant/tokens.css +63 -0
  323. package/docs/design/systems/vintage/DESIGN.md +71 -0
  324. package/docs/design/systems/vintage/manifest.json +54 -0
  325. package/docs/design/systems/vintage/tokens.css +63 -0
  326. package/docs/design/systems/warm-editorial/DESIGN.md +65 -0
  327. package/docs/design/systems/warm-editorial/manifest.json +54 -0
  328. package/docs/design/systems/warm-editorial/tokens.css +63 -0
  329. package/docs/goal-context/01-goal-authoring-checks.md +18 -0
  330. package/docs/goal-context/steward-directives-2026-09-28.md +42 -0
  331. package/graphs/catalog/catalog.yaml +615 -0
  332. package/graphs/default-loop.yaml +1 -1
  333. package/graphs/document-loop.yaml +1 -1
  334. package/graphs/examples/stale-draft-digest/digest.ts +26 -0
  335. package/graphs/examples/stale-draft-digest/recipes.yaml +8 -0
  336. package/graphs/examples/stale-draft-digest/stale-draft-digest.yaml +22 -0
  337. package/graphs/heal/heal-loop.yaml +155 -0
  338. package/graphs/heal/recipes.yaml +19 -0
  339. package/graphs/implement-loop.yaml +2 -2
  340. package/graphs/intake/intake-daily.yaml +55 -0
  341. package/graphs/intake/recipes.yaml +16 -0
  342. package/graphs/landing/landing-heal.yaml +22 -0
  343. package/graphs/landing/recipes.yaml +8 -0
  344. package/graphs/launch/launch-head.yaml +66 -0
  345. package/graphs/launch/recipes.yaml +8 -0
  346. package/graphs/operate-loop.yaml +1 -1
  347. package/graphs/ops/docs-publish.yaml +31 -0
  348. package/graphs/ops/recipes.yaml +8 -0
  349. package/graphs/release/recipes.yaml +45 -0
  350. package/graphs/release/release-loop.yaml +45 -0
  351. package/graphs/research-loop.yaml +1 -1
  352. package/graphs/steward/recipes.yaml +12 -0
  353. package/graphs/steward/steward.yaml +45 -0
  354. package/graphs/video/README.md +1 -0
  355. package/graphs/video/broll-line.yaml +54 -0
  356. package/graphs/video/explainer-line.yaml +58 -0
  357. package/package.json +9 -5
  358. package/plugins/job-coach/.agents/plugins/marketplace.json +12 -0
  359. package/plugins/job-coach/.codex-plugin/plugin.json +23 -0
  360. package/plugins/job-coach/.mcp.json +9 -0
  361. package/plugins/job-coach/README.md +32 -0
  362. package/plugins/job-coach/connectors/ncs/server.ts +90 -0
  363. package/plugins/job-coach/examples/interview-sample.md +11 -0
  364. package/plugins/job-coach/graphs/course-links.ts +46 -0
  365. package/plugins/job-coach/graphs/gap-judge.ts +44 -0
  366. package/plugins/job-coach/graphs/recipes.yaml +45 -0
  367. package/plugins/job-coach/graphs/report-enterprise.yaml +28 -0
  368. package/plugins/job-coach/graphs/report.yaml +20 -0
  369. package/plugins/job-coach/graphs/run-step.ts +302 -0
  370. package/plugins/job-coach/plugin.json +26 -0
  371. package/plugins/job-coach/plugin.ts +6 -0
  372. package/plugins/job-coach/skills/career-report/SKILL.md +6 -0
  373. package/plugins/job-coach/skills/enterprise-ai-fit/SKILL.md +8 -0
  374. package/plugins/job-coach/skills/enterprise-competency/SKILL.md +8 -0
  375. package/plugins/job-coach/skills/enterprise-needs/SKILL.md +8 -0
  376. package/plugins/job-coach/skills/enterprise-roadmap/SKILL.md +8 -0
  377. package/plugins/job-coach/skills/enterprise-task-analysis/SKILL.md +8 -0
  378. package/plugins/job-coach/skills/interview-to-profile/SKILL.md +23 -0
  379. package/plugins/job-coach/skills/run-report/SKILL.md +15 -0
  380. package/scripts/backup/elanous-backup.sh +143 -14
  381. package/scripts/backup/elanous-restore.sh +122 -0
  382. package/scripts/build-docs-site.ts +4 -3
  383. package/scripts/ci-daemon-port-gate.ts +164 -0
  384. package/scripts/ci-typecheck-changed.ts +15 -1
  385. package/scripts/cli-json-exit-scan.ts +126 -0
  386. package/scripts/codex-quota-alert-format.ts +52 -0
  387. package/scripts/codex-quota-alert.ts +63 -13
  388. package/scripts/continuation-dogfood.ts +2 -1
  389. package/scripts/coord-channel-watch.sh +28 -9
  390. package/scripts/coord-post.sh +49 -12
  391. package/scripts/coord-tracks.json +6 -5
  392. package/scripts/cron-run.ts +37 -16
  393. package/scripts/daemon-port-baseline.txt +9 -0
  394. package/scripts/design/extract-corpus-bench.ts +469 -0
  395. package/scripts/design/extract-corpus-score.ts +510 -0
  396. package/scripts/design/vendor-open-design-systems.ts +100 -0
  397. package/scripts/docs-lint.ts +1 -1
  398. package/scripts/eval-bot-vs-grok.ts +79 -0
  399. package/scripts/eval-grok-live-baseline.ts +36 -0
  400. package/scripts/fixtures/front-route-corpus.sample.json +17 -0
  401. package/scripts/fleet/node-setup.sh +80 -7
  402. package/scripts/heal/run-role.ts +328 -0
  403. package/scripts/install-role-watch.sh +95 -0
  404. package/scripts/install.ps1 +11 -1
  405. package/scripts/install.sh +81 -9
  406. package/scripts/isolation-hardcode-baseline.txt +1 -1
  407. package/scripts/landing-heal/must-fix-match.ts +63 -0
  408. package/scripts/landing-heal/run-mission.ts +195 -0
  409. package/scripts/lib/front-route-measurement.ts +131 -0
  410. package/scripts/lib/pwa-scenarios/acp-replay.ts +79 -0
  411. package/scripts/lib/pwa-scenarios/cdp-driver.ts +67 -0
  412. package/scripts/lib/pwa-scenarios/runner.ts +200 -0
  413. package/scripts/lib/pwa-scenarios/scenarios.ts +202 -0
  414. package/scripts/lib/tui-sim-keys.ts +42 -0
  415. package/scripts/market/db-check.ts +212 -0
  416. package/scripts/market-posture-cycle.ts +63 -4
  417. package/scripts/measure-front-route-classifier.ts +56 -0
  418. package/scripts/media/ocr-leak-scan.sh +21 -0
  419. package/scripts/nightly-docops.ts +1 -1
  420. package/scripts/ops/channel-bot-token-migrate.ts +146 -0
  421. package/scripts/ops-health-check.ts +15 -3
  422. package/scripts/pty-scenarios/run.ts +272 -0
  423. package/scripts/public-export.ts +2 -3
  424. package/scripts/public-import-pr.ts +80 -6
  425. package/scripts/public-leak-baseline.txt +0 -7
  426. package/scripts/publish-internal-dist.ts +118 -0
  427. package/scripts/pwa-scenarios.ts +106 -0
  428. package/scripts/rebrand-elanous.ts +1 -1
  429. package/scripts/red-sweep-diff.ts +1 -1
  430. package/scripts/release-loop/auto-approve-node.ts +72 -0
  431. package/scripts/release-loop/cutoff.ts +139 -0
  432. package/scripts/release-loop/docs-land-node.ts +65 -0
  433. package/scripts/release-loop/docs-node.ts +172 -0
  434. package/scripts/release-loop/gate-diff.ts +29 -0
  435. package/scripts/release-loop/gate-node.ts +641 -0
  436. package/scripts/release-loop/node-verdict.ts +85 -0
  437. package/scripts/release-loop/prepare-node.ts +31 -0
  438. package/scripts/release-loop/publish-node.ts +61 -0
  439. package/scripts/release-loop/pwa-node-verdict.ts +45 -0
  440. package/scripts/release-loop/pwa-scenarios-node.ts +140 -0
  441. package/scripts/release-loop/release-docs.ts +109 -0
  442. package/scripts/release-loop/test-durations.ts +63 -0
  443. package/scripts/release-loop/tui-node-checks.ts +29 -0
  444. package/scripts/release-loop/tui-sim-node.ts +113 -0
  445. package/scripts/release-loop/upgrade-node.ts +26 -0
  446. package/scripts/release-loop/verify-node.ts +21 -0
  447. package/scripts/release-loop/version-node.ts +147 -0
  448. package/scripts/release-upgrade-check.sh +53 -0
  449. package/scripts/se-mission-prepare.ts +9 -15
  450. package/scripts/test-audit-coverage-per-file.sh +16 -0
  451. package/scripts/test-audit-meaningless.ts +88 -0
  452. package/scripts/test-audit-overlap-and-weak.ts +73 -0
  453. package/scripts/test-deterministic.ts +29 -6
  454. package/scripts/tui-sim.ts +59 -14
  455. package/scripts/uninstall.sh +10 -3
  456. package/scripts/ux/bump-installed.sh +20 -0
  457. package/scripts/ux/lib/cdp.ts +72 -0
  458. package/scripts/ux/record-page.ts +50 -0
  459. package/scripts/video-broll-line.ts +89 -0
  460. package/scripts/waitlist-daily-digest.ts +108 -0
  461. package/scripts/window-landings.sh +1 -1
  462. package/skills/explainer-video/SKILL.md +99 -0
  463. package/skills/explainer-video/engine/brand/elanous-mark-on-dark.svg +1 -0
  464. package/skills/explainer-video/engine/build.mjs +431 -0
  465. package/skills/explainer-video/engine/vo.ts +37 -0
  466. package/skills/explainer-video/examples/elanous-0.2.5/script.json +155 -0
  467. package/skills/motion-broll/LICENSE +25 -0
  468. package/skills/motion-broll/SKILL.md +150 -0
  469. package/skills/motion-broll/SOURCE.md +6 -0
  470. package/skills/motion-broll/engine/base.css +19 -0
  471. package/skills/motion-broll/engine/beats.js +11 -0
  472. package/skills/motion-broll/engine/build.py +21 -0
  473. package/skills/motion-broll/engine/fonts/Geist-Variable.woff2 +0 -0
  474. package/skills/motion-broll/engine/fonts/GeistMono-Medium.woff2 +0 -0
  475. package/skills/motion-broll/engine/fonts/OFL-Geist.txt +92 -0
  476. package/skills/motion-broll/engine/motion.js +146 -0
  477. package/skills/motion-broll/engine/render.js +22 -0
  478. package/skills/motion-broll/examples/opus-aoe2/01-opus-drop.html +41 -0
  479. package/skills/motion-broll/examples/opus-aoe2/02-pip-builds.html +110 -0
  480. package/skills/motion-broll/examples/opus-aoe2/03-effort-slider.html +38 -0
  481. package/skills/motion-broll/examples/opus-aoe2/04-master-prompt.html +56 -0
  482. package/skills/motion-broll/examples/opus-aoe2/05-low-vs-max.html +49 -0
  483. package/skills/motion-broll/examples/opus-aoe2/06-chapter.html +18 -0
  484. package/skills/motion-broll/examples/opus-aoe2/plan.json +14 -0
  485. package/skills/motion-broll/reference/engine-api.md +106 -0
  486. package/skills/motion-broll/scripts/composite.py +14 -0
  487. package/skills/motion-broll/scripts/inspect_video.py +34 -0
  488. package/skills/motion-broll/scripts/make_pages.py +34 -0
  489. package/skills/motion-broll/scripts/setup.sh +16 -0
  490. package/skills/motion-broll/scripts/words.py +13 -0
  491. package/skills/motion-broll/templates/compare.html +135 -0
  492. package/skills/motion-broll/templates/viewer.html +97 -0
  493. package/skills/omni-digest/SKILL.md +260 -0
  494. package/skills/omni-digest/digest.md +197 -0
  495. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +32 -0
  496. package/skills/omni-digest/package-lock.json +558 -0
  497. package/skills/omni-digest/package.json +13 -0
  498. package/skills/omni-digest/scripts/main.ts +404 -0
  499. package/skills/omni-digest/src/delegate.ts +34 -0
  500. package/skills/omni-digest/src/detect.ts +46 -0
  501. package/skills/omni-digest/src/diagram.ts +72 -0
  502. package/skills/omni-digest/src/domain-staging.ts +50 -0
  503. package/skills/omni-digest/src/env.ts +38 -0
  504. package/skills/omni-digest/src/fetch.ts +237 -0
  505. package/skills/omni-digest/src/media.ts +180 -0
  506. package/skills/omni-digest/src/obsidian.ts +69 -0
  507. package/skills/omni-digest/src/ocr.ts +149 -0
  508. package/skills/omni-digest/src/router.ts +60 -0
  509. package/skills/omni-digest/src/summarize.ts +87 -0
  510. package/skills/omni-digest/src/types.ts +124 -0
  511. package/skills/omni-digest/src/util.ts +34 -0
  512. package/skills/omni-digest/src/visual.ts +129 -0
  513. package/skills/omni-digest/src/x-api.ts +151 -0
  514. package/skills/omni-digest/tsconfig.json +13 -0
  515. package/src/a2a/agent-card.ts +29 -0
  516. package/src/a2a/server.ts +173 -0
  517. package/src/acp/auto-persist.ts +3 -6
  518. package/src/acp/backend-registry.ts +6 -10
  519. package/src/acp/channel-browser-shell.ts +1 -535
  520. package/src/acp/client.ts +106 -36
  521. package/src/acp/fs-roots.ts +20 -4
  522. package/src/acp/obsidian-tags.ts +6 -3
  523. package/src/acp/server.ts +189 -32
  524. package/src/agent/nested-agent-env.ts +1 -0
  525. package/src/agent/pty-binding.ts +1 -1
  526. package/src/agent/spawn-embodied-agent-in-vw.ts +5 -336
  527. package/src/agent/subagent-callable.ts +39 -1
  528. package/src/agent-mission/browser-login.ts +241 -0
  529. package/src/agent-mission/capabilities-cli.ts +33 -0
  530. package/src/agent-mission/capability-matrix.ts +31 -0
  531. package/src/agent-mission/capability-readers.ts +79 -0
  532. package/src/agent-mission/capability-types.ts +15 -0
  533. package/src/agent-mission/claude-headless.ts +131 -0
  534. package/src/agent-mission/claude-subscription.ts +57 -0
  535. package/src/agent-mission/doc-guided-install.ts +185 -0
  536. package/src/agent-mission/driver.ts +900 -69
  537. package/src/agent-mission/env-profile.ts +68 -0
  538. package/src/agent-mission/handoff.ts +128 -0
  539. package/src/agent-mission/in-agent-plugin.ts +311 -0
  540. package/src/agent-mission/mission-cli.ts +25 -1
  541. package/src/agent-mission/parse.ts +9 -4
  542. package/src/agent-mission/pr-review-watch.ts +62 -13
  543. package/src/agent-mission/resource-ladder.ts +235 -0
  544. package/src/agent-mission/tool-install.ts +249 -0
  545. package/src/agent-mission/tool-ladder.ts +37 -0
  546. package/src/agent-room/room-builder.ts +6 -600
  547. package/src/agent-substrate/review-intent.ts +43 -1
  548. package/src/auth/acp-token.ts +16 -0
  549. package/src/auth/github-app-token.ts +112 -0
  550. package/src/auth/scope.ts +8 -4
  551. package/src/auth/temp-tokens.ts +131 -0
  552. package/src/auto-research/obsidian-bridge.ts +2 -0
  553. package/src/autopilot/build/integrity-gate.ts +27 -4
  554. package/src/autopilot/build/isolated-instance.ts +2 -1
  555. package/src/autopilot/build/llm-conflict-merge.ts +27 -0
  556. package/src/autopilot/gh-rest-fallback.ts +177 -0
  557. package/src/autopilot/llm-control-brain.ts +54 -12
  558. package/src/autopilot/mission-engine.ts +15 -1
  559. package/src/autopilot/mission-notify.ts +4 -2
  560. package/src/autopilot/mission-origin.ts +5 -5
  561. package/src/autopilot/mission-phase-track.ts +52 -0
  562. package/src/autopilot/mission-tool.ts +1 -1
  563. package/src/autopilot/pr-manager.ts +51 -4
  564. package/src/autopilot/pty-control-loop.ts +94 -2
  565. package/src/autopilot/track-agent.ts +138 -0
  566. package/src/autopilot/track-courier.ts +153 -0
  567. package/src/boot/acp-server.ts +18 -18
  568. package/src/boot/check-same-origin.ts +19 -5
  569. package/src/boot/daemon-multi-llm-runtime.ts +14 -8
  570. package/src/boot/daemon-prompt-turn.ts +2 -0
  571. package/src/boot/daemon-runtime.ts +36 -14
  572. package/src/boot/daemon-tools/index.ts +16 -0
  573. package/src/boot/daemon-tools/self-implement.ts +4 -0
  574. package/src/boot/daemon-tools/types.ts +3 -0
  575. package/src/boot/daemon-tools/web-search.ts +2 -1
  576. package/src/boot/ws-bridge.ts +11 -0
  577. package/src/browser-cdp/chrome-flags.ts +7 -0
  578. package/src/browser-cdp/client.ts +2 -0
  579. package/src/browser-pane/mount.ts +1 -246
  580. package/src/budget/codex-reset-credit-state.ts +15 -0
  581. package/src/budget/fetchers/codex.ts +2 -1
  582. package/src/budget/run-usage-rollup.ts +11 -4
  583. package/src/budget/unified-usage.ts +2 -1
  584. package/src/capture/tui-self-report.ts +10 -0
  585. package/src/channel-bot-token.ts +126 -0
  586. package/src/chat/index.ts +57 -52
  587. package/src/chat/input-text-key.ts +2 -2
  588. package/src/chat/log-scroll-key.ts +12 -0
  589. package/src/chat/mode/manual.ts +2 -4
  590. package/src/chat/pickers/state.ts +10 -27
  591. package/src/chat/search/modal.ts +14 -2
  592. package/src/chat/submit-trace.ts +30 -0
  593. package/src/chat/tool-render/types.ts +1 -1
  594. package/src/chat/turn-typeahead.ts +2 -1
  595. package/src/chat/wrap.ts +6 -8
  596. package/src/cli/__fixtures__/agent-help-before/agent.txt +18 -0
  597. package/src/cli/__fixtures__/agent-help-before/registry.txt +13 -0
  598. package/src/cli/__fixtures__/agent-help-before/root.txt +100 -0
  599. package/src/cli/__fixtures__/agent-help-before/tier.txt +15 -0
  600. package/src/cli/a2a-cli.ts +52 -0
  601. package/src/cli/agent-cli.ts +726 -0
  602. package/src/cli/autopilot-cli.ts +739 -0
  603. package/src/cli/bg-launch.ts +20 -4
  604. package/src/cli/claude-plugin-setup.ts +199 -0
  605. package/src/cli/config-test-sync.ts +94 -10
  606. package/src/cli/connector-cli.ts +204 -0
  607. package/src/cli/control-cli.ts +220 -0
  608. package/src/cli/control-join-cli.ts +142 -0
  609. package/src/cli/decisions-cli.ts +94 -0
  610. package/src/cli/docs-cli.ts +38 -0
  611. package/src/cli/doctor-cli.ts +262 -77
  612. package/src/cli/doctor-distro.ts +17 -1
  613. package/src/cli/doctor-fix.ts +145 -17
  614. package/src/cli/doctor-llm-assist.ts +71 -0
  615. package/src/cli/doctor-private-files.ts +66 -0
  616. package/src/cli/doctor-readiness.ts +23 -28
  617. package/src/cli/fleet-cli.ts +63 -0
  618. package/src/cli/g6a-registration-probe.ts +16 -0
  619. package/src/cli/git-install-plan.ts +51 -0
  620. package/src/cli/hooks-cli.ts +44 -0
  621. package/src/cli/ingest-token-cli.ts +21 -0
  622. package/src/cli/intake-cli.ts +416 -0
  623. package/src/cli/labels-cli.ts +74 -0
  624. package/src/cli/leader-cli.ts +10 -159
  625. package/src/cli/live-cli.ts +48 -0
  626. package/src/cli/llm-detect-cli.ts +130 -0
  627. package/src/cli/logs-cli.ts +231 -7
  628. package/src/cli/machine-cli.ts +38 -0
  629. package/src/cli/mcp-call.ts +52 -13
  630. package/src/cli/mcp-gateway-cli.ts +94 -0
  631. package/src/cli/mcp-login.ts +116 -8
  632. package/src/cli/mcp-reload.ts +20 -3
  633. package/src/cli/nexus-dist.ts +18 -13
  634. package/src/cli/nexus-ios-bind.ts +20 -7
  635. package/src/cli/ops-cli.ts +225 -0
  636. package/src/cli/plugin-cli.ts +201 -0
  637. package/src/cli/pod-cli.ts +83 -0
  638. package/src/cli/port-lease-local.ts +124 -0
  639. package/src/cli/pr-cli.ts +12 -1
  640. package/src/cli/pr-lineage.ts +2 -1
  641. package/src/cli/provider-cli.ts +720 -0
  642. package/src/cli/pty-record-cli.ts +63 -0
  643. package/src/cli/pty-takeover-cli.ts +57 -40
  644. package/src/cli/pty-web-address.ts +52 -0
  645. package/src/cli/publish-cli.ts +44 -0
  646. package/src/cli/pwa-build.ts +18 -12
  647. package/src/cli/pwa-dev.ts +13 -8
  648. package/src/cli/pwa-registry.ts +6 -0
  649. package/src/cli/pwa-share.ts +65 -22
  650. package/src/cli/pwa-show.ts +20 -4
  651. package/src/cli/pwa-start.ts +102 -27
  652. package/src/cli/pwa-test.ts +201 -96
  653. package/src/cli/release-cli.ts +177 -7
  654. package/src/cli/remotes-cli.ts +20 -1
  655. package/src/cli/remotes.ts +2 -1
  656. package/src/cli/repo-cli.ts +315 -36
  657. package/src/cli/research-cli.ts +66 -0
  658. package/src/cli/role-cli.ts +320 -0
  659. package/src/cli/schedule-cli.ts +246 -0
  660. package/src/cli/self-update.ts +103 -12
  661. package/src/cli/setup-cli.ts +64 -2
  662. package/src/cli/shadow-cli.ts +188 -0
  663. package/src/cli/share-auto-mount.ts +12 -8
  664. package/src/cli/start-cli.ts +240 -0
  665. package/src/cli/stdout-flush.ts +8 -0
  666. package/src/cli/storage-cli.ts +334 -0
  667. package/src/cli/tailscale-serve.ts +34 -19
  668. package/src/cli/tasks-cli.ts +176 -0
  669. package/src/cli/test-state-dir-flag.ts +1 -1
  670. package/src/cli/usage-cli.ts +15 -4
  671. package/src/cli/where-cli.ts +22 -55
  672. package/src/code-edit/diff-render.ts +6 -32
  673. package/src/code-edit/source-delta-browser.ts +0 -1
  674. package/src/codex/models.ts +1 -1
  675. package/src/config.ts +8 -13
  676. package/src/connectors/event-ledger.ts +41 -0
  677. package/src/connectors/linear-projector.ts +88 -0
  678. package/src/connectors/linear.ts +143 -0
  679. package/src/connectors/types.ts +19 -0
  680. package/src/control-plane/lease-holder.ts +41 -0
  681. package/src/control-plane/ledger.ts +146 -0
  682. package/src/control-plane/light-member.ts +164 -0
  683. package/src/control-plane/member-tokens.ts +88 -0
  684. package/src/control-plane/member.ts +159 -0
  685. package/src/control-plane/port-lease-heartbeat.ts +101 -0
  686. package/src/control-plane/ports.ts +40 -0
  687. package/src/control-plane/primary.ts +169 -0
  688. package/src/control-plane/server.ts +216 -0
  689. package/src/dashboard/acp-boot.ts +14 -38
  690. package/src/dashboard/assistant-render-state-runtime.ts +2 -2
  691. package/src/dashboard/assistant-sample-runtime.ts +2 -2
  692. package/src/dashboard/auto-compact-runtime.ts +11 -16
  693. package/src/dashboard/browser-help-runtime.ts +2 -3
  694. package/src/dashboard/chat/acp-chat.ts +2 -5
  695. package/src/dashboard/code-edit-post-turn-runtime.ts +1 -2
  696. package/src/dashboard/compact-surface-assembly.ts +0 -14
  697. package/src/dashboard/compact-surface-dispatch.ts +1 -38
  698. package/src/dashboard/compact-surface-inventory.ts +0 -8
  699. package/src/dashboard/compact-surface-runtime.ts +0 -14
  700. package/src/dashboard/context-menu-provider-boot.ts +0 -9
  701. package/src/dashboard/context-window-runtime-boot.ts +0 -37
  702. package/src/dashboard/index.ts +331 -2562
  703. package/src/dashboard/input/chat-main-plain-turn-runtime.ts +0 -6
  704. package/src/dashboard/input/chat-main-slash-command.ts +2 -2
  705. package/src/dashboard/input/dashboard-priority-key-route.ts +1 -43
  706. package/src/dashboard/input/mouse-modal-surface-runtime.ts +1 -5
  707. package/src/dashboard/input/mouse-wiring.ts +2 -3
  708. package/src/dashboard/input/no-provider-reply.ts +13 -0
  709. package/src/dashboard/layout-display-runtime-registration.ts +0 -10
  710. package/src/dashboard/modals/pane-multi.ts +1 -1
  711. package/src/dashboard/replay-preview.ts +1 -1
  712. package/src/dashboard/runtime/approvers.ts +0 -1
  713. package/src/dashboard/runtime/state-snapshot.ts +0 -90
  714. package/src/dashboard/scenario-runtime-boot.ts +16 -16
  715. package/src/dashboard/slash-runtime/dashboard-handlers.ts +134 -1456
  716. package/src/dashboard/slash-runtime/design-pick.ts +96 -0
  717. package/src/dashboard/slash-runtime/help-from-registry.ts +77 -0
  718. package/src/dashboard/slash-runtime/unknown-slash-reply.ts +78 -0
  719. package/src/dashboard/stale-install-check.ts +77 -0
  720. package/src/dashboard/surface-registry-wiring.ts +2 -15
  721. package/src/dashboard/turn-message-runtime.ts +0 -4
  722. package/src/dashboard/turn-preamble.ts +1 -1
  723. package/src/dashboard/turn-stream-formatter.ts +2 -2
  724. package/src/dashboard/turn-stream-runtime.ts +2 -2
  725. package/src/dashboard-pane-context-menu.ts +0 -14
  726. package/src/debug/log.ts +7 -0
  727. package/src/decide/boundary-shadow.ts +131 -0
  728. package/src/decisions/decision-ledger.ts +404 -0
  729. package/src/design/apply-direction.ts +209 -0
  730. package/src/design/css-root-tokens.ts +89 -0
  731. package/src/design/custom-system-create.ts +230 -0
  732. package/src/design/design-directions.ts +61 -41
  733. package/src/design/design-gate.ts +241 -0
  734. package/src/design/design-library.ts +57 -0
  735. package/src/design/design-previews.ts +138 -0
  736. package/src/design/design-systems.ts +61 -0
  737. package/src/design/lint-artifact-run.ts +46 -6
  738. package/src/design/lint-artifact.ts +5 -1
  739. package/src/design/open-design-client.ts +247 -0
  740. package/src/design/system-from-extract.ts +767 -0
  741. package/src/design/system-schema.ts +123 -0
  742. package/src/discord-slash-wire.ts +2 -1
  743. package/src/discord-test-runner.ts +2 -1
  744. package/src/discord-voice-wire.ts +2 -1
  745. package/src/discord.ts +1 -1
  746. package/src/dispatch/opportunistic-launcher.ts +24 -1
  747. package/src/domains/ops-status.ts +35 -2
  748. package/src/domains/outbound-alert.ts +24 -6
  749. package/src/domains/repeated-failure.ts +89 -0
  750. package/src/domains/schedule-manage-tool.ts +28 -8
  751. package/src/domains/schedule-migrate.ts +22 -4
  752. package/src/domains/schedule-registry.ts +88 -33
  753. package/src/domains/schedule-runs.ts +132 -0
  754. package/src/domains/schedule-state.ts +152 -0
  755. package/src/domains/telegram-kind-route.ts +21 -0
  756. package/src/element-registry/address.ts +1 -3
  757. package/src/execution-loop/dispatch-task.ts +139 -0
  758. package/src/execution-loop/gate-decision-summary.ts +29 -0
  759. package/src/execution-loop/gates.ts +197 -0
  760. package/src/git-fs/gh-cli.ts +47 -17
  761. package/src/git-fs/retry.ts +1 -0
  762. package/src/git-fs/runner.ts +1 -0
  763. package/src/git-fs/worktree.ts +6 -5
  764. package/src/github/pr-labels.ts +68 -0
  765. package/src/graph-kinds/installed-plugin-nodes.ts +95 -0
  766. package/src/graph-kinds/plugin-nodes.ts +68 -0
  767. package/src/graph-kinds/registry.ts +118 -0
  768. package/src/graph-runner/graph-cli.ts +103 -0
  769. package/src/graph-runner/graph-notify.ts +220 -0
  770. package/src/graph-runner/graph-tick.ts +119 -0
  771. package/src/graph-runner/release-manifest.ts +93 -0
  772. package/src/graph-runner/runner.ts +487 -0
  773. package/src/grok/credential.ts +158 -5
  774. package/src/grounding/ensure-grounding.ts +131 -0
  775. package/src/grounding/mirror-export.ts +89 -0
  776. package/src/grounding/mirror-manifest.ts +252 -0
  777. package/src/grounding/sources-cli.ts +209 -0
  778. package/src/grounding/sources.ts +333 -0
  779. package/src/grounding/token.ts +121 -0
  780. package/src/harness/harness-cli-command.ts +415 -23
  781. package/src/harness/harness-pod-dispatch.ts +116 -0
  782. package/src/harness/harness-stop.ts +260 -0
  783. package/src/harness/harness-substrate-default.ts +40 -0
  784. package/src/harness/harness-worktrees.ts +1 -1
  785. package/src/harness/harness-write-boundary.ts +2 -0
  786. package/src/harness/llm-decompose.ts +2 -1
  787. package/src/harness/self-send-decide.ts +233 -0
  788. package/src/harness/self-send-target.ts +22 -1
  789. package/src/harness/shell-relay.ts +5 -0
  790. package/src/harness/worktree-branch-prefix.ts +4 -3
  791. package/src/hooks/providers.ts +77 -0
  792. package/src/hooks/queue.ts +73 -0
  793. package/src/hooks/receiver.ts +133 -0
  794. package/src/index.ts +712 -3409
  795. package/src/input-core/dispatcher.ts +1 -1
  796. package/src/input-core/drag-esc-interceptor.ts +1 -1
  797. package/src/instance/current.ts +5 -5
  798. package/src/instance/leader.ts +10 -326
  799. package/src/instance/nexus-run-refusal.ts +27 -92
  800. package/src/instance/project-local-exclude.ts +27 -0
  801. package/src/instance/resolve.ts +46 -175
  802. package/src/intake-plane/capture.ts +14 -1
  803. package/src/intake-plane/check.ts +34 -14
  804. package/src/intake-plane/collect-github.ts +258 -0
  805. package/src/intake-plane/collect-pod.ts +100 -0
  806. package/src/intake-plane/collect-telegram-saved.ts +91 -0
  807. package/src/intake-plane/digest.ts +111 -0
  808. package/src/intake-plane/enrich-plugins.ts +1 -1
  809. package/src/intake-plane/front-classifier.ts +56 -0
  810. package/src/intake-plane/front-route-classifier.ts +80 -0
  811. package/src/intake-plane/front-route-rules.ts +152 -0
  812. package/src/intake-plane/grounding-docs.ts +185 -0
  813. package/src/intake-plane/harness-target.ts +20 -0
  814. package/src/intake-plane/intake-to-tasks.ts +303 -0
  815. package/src/intake-plane/items.ts +247 -0
  816. package/src/intake-plane/link-ledger.ts +27 -0
  817. package/src/intake-plane/route.ts +104 -0
  818. package/src/intake-plane/runtime-callables.ts +3 -1
  819. package/src/intake-plane/sketch-ledger.ts +34 -0
  820. package/src/intake-plane/slash.ts +30 -2
  821. package/src/intake-plane/submit-intake-work.ts +160 -0
  822. package/src/intake-plane/telegram-work.ts +56 -0
  823. package/src/iul/sidebar-shell.ts +0 -165
  824. package/src/launch-head/capability-check.ts +162 -0
  825. package/src/launch-head/launch-head-cli.ts +242 -0
  826. package/src/launch-head/load-balance.ts +81 -0
  827. package/src/live/detail-switch.ts +180 -0
  828. package/src/llm/provider-detect.ts +247 -0
  829. package/src/llm/provider-probe.ts +57 -0
  830. package/src/llm/usable-llm.ts +32 -0
  831. package/src/llm.ts +55 -23
  832. package/src/log-entry.ts +1 -1
  833. package/src/log-pane/attachment-popup.ts +1 -2
  834. package/src/loops/loop-cli.ts +37 -0
  835. package/src/loops/registry.ts +196 -0
  836. package/src/market/official-keys.ts +5 -0
  837. package/src/market/publish.ts +329 -0
  838. package/src/market/signed-index.ts +156 -0
  839. package/src/market/tgz.ts +121 -0
  840. package/src/mcp/client.ts +1 -1
  841. package/src/mcp/mcp-oauth.ts +40 -5
  842. package/src/mcp/server.ts +75 -7
  843. package/src/mcp-gateway/gateway.ts +128 -0
  844. package/src/mcp-gateway/pat-store.ts +124 -0
  845. package/src/mss/logging/log-store.ts +91 -2
  846. package/src/mss/logging/scoped-level.ts +5 -9
  847. package/src/native-tool-catalog.ts +66 -345
  848. package/src/nexus/api/connect-info.ts +23 -25
  849. package/src/nexus/api/context-url.ts +1 -1
  850. package/src/nexus/api/design-check.ts +90 -59
  851. package/src/nexus/api/design-previews-route.ts +182 -0
  852. package/src/nexus/api/design-system-create-route.ts +123 -0
  853. package/src/nexus/api/graph-approvals.ts +125 -0
  854. package/src/nexus/api/graph-kinds.ts +69 -0
  855. package/src/nexus/api/graphs-api.ts +221 -0
  856. package/src/nexus/api/harness-api.ts +31 -2
  857. package/src/nexus/api/health.ts +15 -0
  858. package/src/nexus/api/http-server.ts +328 -43
  859. package/src/nexus/api/image-generate.ts +3 -0
  860. package/src/nexus/api/ingest-token.ts +175 -0
  861. package/src/nexus/api/intake-ledger-api.ts +54 -0
  862. package/src/nexus/api/intake-route.ts +111 -0
  863. package/src/nexus/api/json-response.ts +33 -0
  864. package/src/nexus/api/live-detail.ts +47 -0
  865. package/src/nexus/api/live-shipped.ts +78 -0
  866. package/src/nexus/api/log-fabric.ts +42 -2
  867. package/src/nexus/api/markdown-publish.ts +3 -2
  868. package/src/nexus/api/mcp-http.ts +14 -9
  869. package/src/nexus/api/mcp-oauth-hint.ts +31 -0
  870. package/src/nexus/api/mcp-public-allowlist.ts +33 -0
  871. package/src/nexus/api/merge-approvals-gate.ts +79 -0
  872. package/src/nexus/api/merge-approvals.ts +307 -0
  873. package/src/nexus/api/meta-api.ts +18 -0
  874. package/src/nexus/api/next-fluent-wiring.ts +7 -9
  875. package/src/nexus/api/plugins-credentials-api.ts +47 -0
  876. package/src/nexus/api/plugins-market.ts +209 -0
  877. package/src/nexus/api/pod-credential-api.ts +187 -0
  878. package/src/nexus/api/public-routes.ts +87 -0
  879. package/src/nexus/api/rest-route-paths.ts +15 -0
  880. package/src/nexus/api/schedules-action.ts +12 -3
  881. package/src/nexus/api/schedules-read.ts +228 -0
  882. package/src/nexus/api/sessions-store.ts +8 -2
  883. package/src/nexus/api/setup-answer-priority.ts +130 -0
  884. package/src/nexus/api/setup-channel-bot.ts +56 -0
  885. package/src/nexus/api/setup-child-llm.ts +228 -0
  886. package/src/nexus/api/setup-obsidian-skills.ts +93 -0
  887. package/src/nexus/api/task-cards-api.ts +34 -0
  888. package/src/nexus/api/tasks-create.ts +136 -0
  889. package/src/nexus/api/telegram-trigger-bot.ts +3 -3
  890. package/src/nexus/api/terminals.ts +204 -18
  891. package/src/nexus/api/trace.ts +405 -0
  892. package/src/nexus/api/vault-api.ts +140 -7
  893. package/src/nexus/api/worktrees.ts +18 -2
  894. package/src/nexus/boot/register-mcp-clients.ts +135 -16
  895. package/src/nexus/daemon-endpoint.ts +85 -0
  896. package/src/nexus/default-port.ts +1 -0
  897. package/src/nexus/index.ts +362 -143
  898. package/src/nexus/onboarding/pwa-share-prompt.ts +31 -8
  899. package/src/nexus/onboarding/tailscale-probe.ts +9 -1
  900. package/src/nexus/outbound/router.ts +1 -1
  901. package/src/nexus/setup-status.ts +58 -0
  902. package/src/nexus/supervisor/lock.ts +8 -0
  903. package/src/nexus/webterm/pty.ts +15 -0
  904. package/src/oauth/codex-account-rotation.ts +122 -16
  905. package/src/oauth/codex-account-store.ts +88 -19
  906. package/src/oauth/codex-quota-policy.ts +55 -0
  907. package/src/oauth/store.ts +28 -25
  908. package/src/obsidian/default-vault.ts +17 -0
  909. package/src/obsidian/vault-write-guard.ts +46 -0
  910. package/src/onboarding/help/llm.md +2 -2
  911. package/src/onboarding.ts +27 -4
  912. package/src/panes/alt-skip-predicate.ts +4 -3
  913. package/src/panes/external-terminal-pane.ts +3 -4
  914. package/src/panes/factory.ts +1 -18
  915. package/src/panes/index.ts +1 -5
  916. package/src/panes/pane-cycle.ts +1 -1
  917. package/src/panes/types.ts +1 -1
  918. package/src/platform/env-literal.ts +4 -0
  919. package/src/plugin-hooks/dispatcher.ts +10 -4
  920. package/src/plugins/core/host.ts +108 -4
  921. package/src/plugins/core/manifest.ts +104 -7
  922. package/src/plugins/install/market-fetch.ts +250 -0
  923. package/src/plugins/install/plugin-credentials.ts +106 -0
  924. package/src/plugins/install/plugin-install.ts +518 -0
  925. package/src/policy/claude-subscription-guard.ts +25 -0
  926. package/src/preview/terminal.ts +24 -1
  927. package/src/preview-pane/mount.ts +1 -142
  928. package/src/prompt/references.ts +4 -51
  929. package/src/prompt-library/universal-preamble.ts +8 -2
  930. package/src/pty-shell/pty-control-ipc.ts +57 -3
  931. package/src/pty-shell/pty-mouse.ts +75 -0
  932. package/src/pty-shell/pty-recording.ts +108 -0
  933. package/src/pty-shell/registry.ts +53 -3
  934. package/src/python/resolve-python.ts +1 -1
  935. package/src/release-loop/checklist.ts +191 -0
  936. package/src/release-loop/manifest.ts +65 -0
  937. package/src/release-loop/release-note.ts +177 -0
  938. package/src/roles/lease-cas.ts +52 -0
  939. package/src/roles/machine-name.ts +48 -0
  940. package/src/roles/machine-profile.ts +65 -0
  941. package/src/roles/role-lease-read.ts +81 -0
  942. package/src/roles/role-lease.ts +63 -0
  943. package/src/roles/role-watch.ts +69 -0
  944. package/src/self-dev/ask-launch-flow.ts +39 -22
  945. package/src/self-dev/ask-launch-io.ts +1 -1
  946. package/src/self-dev/dev-cli.ts +290 -159
  947. package/src/self-dev/dev-pipeline.ts +157 -9
  948. package/src/self-dev/draft-sweep.ts +231 -0
  949. package/src/self-dev/draft-triage-rules.ts +42 -0
  950. package/src/self-dev/entrance-baseline.ts +156 -4
  951. package/src/self-dev/fabric-decompose-adapter.ts +10 -0
  952. package/src/self-dev/launch-preflight.ts +1 -1
  953. package/src/self-dev/orchestrate.ts +297 -20
  954. package/src/self-dev/repair-signals.ts +6 -0
  955. package/src/self-dev/run-store.ts +20 -5
  956. package/src/self-dev/run-supervisor.ts +43 -3
  957. package/src/self-implement/abandoned-classification.ts +15 -1
  958. package/src/self-implement/blocked-draft-policy.ts +6 -0
  959. package/src/self-implement/budget-gate.ts +236 -0
  960. package/src/self-implement/child-llm-preference.ts +84 -0
  961. package/src/self-implement/decision-events.ts +57 -0
  962. package/src/self-implement/decision-signal-press.ts +5 -2
  963. package/src/self-implement/dependency-change-gate.ts +90 -0
  964. package/src/self-implement/gate-baseline.ts +35 -2
  965. package/src/self-implement/gate-cli.ts +29 -7
  966. package/src/self-implement/gate-scope.ts +7 -1
  967. package/src/self-implement/goal-author-decompose.ts +22 -4
  968. package/src/self-implement/goal-author.ts +41 -3
  969. package/src/self-implement/goal-boundary.ts +65 -0
  970. package/src/self-implement/goal-clarification-escalation.ts +1 -0
  971. package/src/self-implement/goal-documents-dir.ts +6 -1
  972. package/src/self-implement/goal-execution-records.ts +58 -0
  973. package/src/self-implement/goal-run-store.ts +42 -24
  974. package/src/self-implement/graph-authority.ts +13 -28
  975. package/src/self-implement/graph-catalog.ts +48 -0
  976. package/src/self-implement/graph-run-contract.ts +127 -0
  977. package/src/self-implement/graph-templates.ts +14 -7
  978. package/src/self-implement/graph-yaml.ts +35 -6
  979. package/src/self-implement/harness-policy-gates.ts +42 -0
  980. package/src/self-implement/headless-elanous-driver.ts +30 -6
  981. package/src/self-implement/heal-triage.ts +229 -0
  982. package/src/self-implement/host-regate.ts +244 -0
  983. package/src/self-implement/non-git-consent.ts +23 -0
  984. package/src/self-implement/orchestrator.ts +488 -134
  985. package/src/self-implement/pipeline-shape.ts +1 -1
  986. package/src/self-implement/pr-evidence-artifact.ts +6 -19
  987. package/src/self-implement/prior-draft-findings.ts +128 -0
  988. package/src/self-implement/pwa-import-graph.ts +65 -0
  989. package/src/self-implement/repo-provision.ts +18 -21
  990. package/src/self-implement/report-origin.ts +39 -0
  991. package/src/self-implement/rework-policy.ts +21 -0
  992. package/src/self-implement/run-ledger-compact.ts +438 -0
  993. package/src/self-implement/run-ledger.ts +212 -29
  994. package/src/self-implement/run-report.ts +24 -0
  995. package/src/self-implement/run-status-mapping.ts +2 -0
  996. package/src/self-implement/running-runs.ts +112 -14
  997. package/src/self-implement/scoped-typecheck-config.ts +49 -0
  998. package/src/self-implement/seams.ts +207 -40
  999. package/src/self-implement/self-implement-cli.ts +17 -2
  1000. package/src/self-implement/shadow-repo.ts +350 -0
  1001. package/src/self-implement/supervision-vocabulary.ts +7 -0
  1002. package/src/shell-runner/dispatch.ts +3 -0
  1003. package/src/shell-runner/index.ts +0 -7
  1004. package/src/skills/runner.ts +0 -69
  1005. package/src/skills/tools/agent-room-slash.ts +2 -1
  1006. package/src/skills/tools/agent-room.ts +8 -140
  1007. package/src/skills/tools/agent.ts +5 -3
  1008. package/src/skills/tools/context.ts +5 -140
  1009. package/src/skills/tools/control.ts +4 -188
  1010. package/src/skills/tools/dashboard-slash.ts +3 -11
  1011. package/src/skills/tools/dashboard-state.ts +3 -5
  1012. package/src/skills/tools/dev-harness.ts +45 -2
  1013. package/src/skills/tools/omni-search.ts +29 -1
  1014. package/src/skills/tools/shell.ts +11 -29
  1015. package/src/skills/tools/spawn-coding-agent-headless.ts +6 -9
  1016. package/src/ssh/ssh-picker-modal.ts +1 -1
  1017. package/src/steward/directive.ts +99 -0
  1018. package/src/steward/steward-cards.ts +45 -0
  1019. package/src/steward/triage.ts +239 -0
  1020. package/src/storage/cli-bin.ts +67 -0
  1021. package/src/storage/object-store-visibility.ts +169 -0
  1022. package/src/storage/object-store.ts +452 -0
  1023. package/src/storage/s3.ts +53 -10
  1024. package/src/storage/storage-detect.ts +337 -0
  1025. package/src/surface/address.ts +3 -5
  1026. package/src/surface/index.ts +0 -6
  1027. package/src/surface/llm-tools.ts +1 -2
  1028. package/src/surface/shell-runner-boot.ts +4 -89
  1029. package/src/task-cards/card-cli.ts +64 -0
  1030. package/src/task-cards/card-store.ts +241 -0
  1031. package/src/task-orchestrator/boot.ts +27 -1
  1032. package/src/task-orchestrator/dispatcher.ts +49 -7
  1033. package/src/task-orchestrator/external-fingerprint.ts +10 -0
  1034. package/src/task-orchestrator/external-policy.ts +86 -0
  1035. package/src/task-orchestrator/feedback-loop.ts +4 -2
  1036. package/src/task-orchestrator/graph.ts +18 -1
  1037. package/src/task-orchestrator/runtimes/create.ts +128 -0
  1038. package/src/task-orchestrator/store.ts +78 -18
  1039. package/src/task-orchestrator/surface-registry.ts +5 -2
  1040. package/src/task-orchestrator/surfaces/external-exec.ts +180 -0
  1041. package/src/task-orchestrator/surfaces/idea-approval-pr.ts +118 -0
  1042. package/src/task-orchestrator/surfaces/index.ts +4 -3
  1043. package/src/task-orchestrator/surfaces/pod-account-broker.ts +60 -5
  1044. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +194 -0
  1045. package/src/task-orchestrator/surfaces/pod-command-job.ts +369 -0
  1046. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +207 -0
  1047. package/src/task-orchestrator/surfaces/pod-pool.ts +128 -12
  1048. package/src/task-orchestrator/surfaces/pod-skills.ts +11 -3
  1049. package/src/task-orchestrator/surfaces/pod-source-bundle.ts +147 -0
  1050. package/src/task-orchestrator/surfaces/pod-source-receive.ts +129 -0
  1051. package/src/task-orchestrator/surfaces/pod-source-spec.ts +77 -0
  1052. package/src/task-orchestrator/surfaces/self-implement-pod.ts +930 -45
  1053. package/src/task-orchestrator/surfaces/self-implement.ts +8 -1
  1054. package/src/task-orchestrator/tox-loop.ts +204 -0
  1055. package/src/task-orchestrator/types.ts +21 -1
  1056. package/src/telegram-commands.ts +8 -0
  1057. package/src/telegram-report.ts +14 -7
  1058. package/src/telegram-run.ts +18 -5
  1059. package/src/telegram.ts +92 -22
  1060. package/src/tool-runtime/acp-session-runtime.ts +28 -7
  1061. package/src/tool-runtime/agent-runtime.ts +1 -0
  1062. package/src/tool-runtime/context-runtime.ts +18 -35
  1063. package/src/tool-runtime/control-runtime.ts +8 -21
  1064. package/src/tool-runtime/dashboard-state-runtime.ts +1 -5
  1065. package/src/tool-runtime/elanous-asana-runtimes.ts +205 -0
  1066. package/src/tool-runtime/elanous-control-runtimes.ts +16 -2
  1067. package/src/tool-runtime/elanous-task-submit-runtime.ts +124 -0
  1068. package/src/tool-runtime/index.ts +4 -1
  1069. package/src/tool-runtime/scenario-runtimes.ts +22 -21
  1070. package/src/tool-runtime/scenario-target-mount.ts +6 -32
  1071. package/src/tool-runtime/types.ts +8 -0
  1072. package/src/tui/chat/attachment-summary.ts +4 -4
  1073. package/src/tui-client/acp-transport-ws-client.ts +72 -5
  1074. package/src/tui-client/headless-core-guard.ts +0 -1
  1075. package/src/tui.ts +11 -7
  1076. package/src/typecheck-ratchet.ts +15 -0
  1077. package/src/ui/hover-presenter.ts +3 -7
  1078. package/src/user-config.ts +453 -190
  1079. package/src/util/spawn-sync-output.ts +15 -0
  1080. package/src/version/code-revision.ts +34 -9
  1081. package/src/version/packed-revision.json +1 -1
  1082. package/src/video-pipeline/broll-agent.ts +102 -0
  1083. package/src/video-pipeline/recipes/broll.ts +263 -0
  1084. package/src/video-pipeline/walk-line.ts +2 -1
  1085. package/src/views/scratch-image-size.ts +10 -0
  1086. package/src/voice/channel-adapters/pwa-voice-adapter.ts +2 -18
  1087. package/src/voice/channel-adapters/telegram-voice-adapter.ts +1 -6
  1088. package/src/voice/voice-tts-singleton.ts +46 -8
  1089. package/src/web-push/vapid-keys.ts +1 -1
  1090. package/src/web-search/free.ts +77 -0
  1091. package/src/web-search/index.ts +7 -5
  1092. package/src/web-terminal/preview-tap-registry.ts +152 -43
  1093. package/src/workflow-runtime/deps-bridge.ts +4 -3
  1094. package/src/workflow-runtime/executor.ts +18 -22
  1095. package/src/workflow-runtime/node-catalog.ts +20 -1
  1096. package/src/workflow-runtime/nodes/bash.ts +1 -0
  1097. package/src/workflow-runtime/nodes/task.ts +74 -0
  1098. package/src/workflow-runtime/nodes/triggers.ts +24 -1
  1099. package/src/workflow-runtime/plugin-kind-node.ts +166 -0
  1100. package/src/workflow-runtime/schema.ts +98 -2
  1101. package/src/workflow-runtime/triggers/webhook-router.ts +43 -3
  1102. package/src/workflow-runtime/triggers/webhook-source.ts +19 -1
  1103. package/src/workflow-runtime/types.ts +76 -5
  1104. package/apps/pwa/out/_next/static/Ksn99fVf2HUWhSyV1-FC8/_buildManifest.js +0 -1
  1105. package/apps/pwa/out/_next/static/chunks/1176-4943bd83b00e53a2.js +0 -25
  1106. package/apps/pwa/out/_next/static/chunks/1380-9c652e1002032c86.js +0 -1
  1107. package/apps/pwa/out/_next/static/chunks/1863-2ea170dfec26b88b.js +0 -1
  1108. package/apps/pwa/out/_next/static/chunks/2091-2d6b7f4f16300f83.js +0 -1
  1109. package/apps/pwa/out/_next/static/chunks/2347-366617ec73babe3b.js +0 -1
  1110. package/apps/pwa/out/_next/static/chunks/3089-e60cc8f5d35ee239.js +0 -1
  1111. package/apps/pwa/out/_next/static/chunks/3158-c77ed9ea34f9cc9f.js +0 -1
  1112. package/apps/pwa/out/_next/static/chunks/3607-f0b5c5ee18fcf56a.js +0 -1
  1113. package/apps/pwa/out/_next/static/chunks/3659-05b2d5d971b42dbc.js +0 -1
  1114. package/apps/pwa/out/_next/static/chunks/3736-b6a7bfdcb8f56ec6.js +0 -1
  1115. package/apps/pwa/out/_next/static/chunks/3839-eebf298e89a7bb77.js +0 -1
  1116. package/apps/pwa/out/_next/static/chunks/4012.832a2824b978e7ee.js +0 -5
  1117. package/apps/pwa/out/_next/static/chunks/4706-d85d7f3345f3ff91.js +0 -1
  1118. package/apps/pwa/out/_next/static/chunks/526-17a6a2e0e6dc4498.js +0 -1
  1119. package/apps/pwa/out/_next/static/chunks/5508-676ac75f2757ad40.js +0 -1
  1120. package/apps/pwa/out/_next/static/chunks/5583-8a0317f4b8e68db9.js +0 -1
  1121. package/apps/pwa/out/_next/static/chunks/6258-b58f722b10e8d31d.js +0 -1
  1122. package/apps/pwa/out/_next/static/chunks/6820-32fd097e6186fdc9.js +0 -1
  1123. package/apps/pwa/out/_next/static/chunks/6852-983db7ca958634b7.js +0 -1
  1124. package/apps/pwa/out/_next/static/chunks/7235-e583e4a015e159b9.js +0 -1
  1125. package/apps/pwa/out/_next/static/chunks/7482-7b417330b24a996a.js +0 -1
  1126. package/apps/pwa/out/_next/static/chunks/7701-99b7d4dedb955bf8.js +0 -1
  1127. package/apps/pwa/out/_next/static/chunks/77f1928d-bd3777589042d3ea.js +0 -1
  1128. package/apps/pwa/out/_next/static/chunks/8366-36956cafc5e33614.js +0 -1
  1129. package/apps/pwa/out/_next/static/chunks/8370-23d0de995be666f8.js +0 -1
  1130. package/apps/pwa/out/_next/static/chunks/8617-307421f3b0e8b2a2.js +0 -1
  1131. package/apps/pwa/out/_next/static/chunks/8837-e45de46a9fedf1f5.js +0 -1
  1132. package/apps/pwa/out/_next/static/chunks/9058-0e8055a5ea3ebdd6.js +0 -1
  1133. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-84b84eca12acada4.js +0 -1
  1134. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-1f23568c29e88f7a.js +0 -1
  1135. package/apps/pwa/out/_next/static/chunks/app/botlab/page-bf6fc9aafd6135a7.js +0 -1
  1136. package/apps/pwa/out/_next/static/chunks/app/bots/page-3f8539ae4b719d2e.js +0 -1
  1137. package/apps/pwa/out/_next/static/chunks/app/chat/page-d2900febb74ecf32.js +0 -1
  1138. package/apps/pwa/out/_next/static/chunks/app/control/page-e82dec647dd79c36.js +0 -1
  1139. package/apps/pwa/out/_next/static/chunks/app/dashboard/page-096ec743a8ee955a.js +0 -1
  1140. package/apps/pwa/out/_next/static/chunks/app/design-check/page-255c2ceded4b3493.js +0 -1
  1141. package/apps/pwa/out/_next/static/chunks/app/healthz/route-84b84eca12acada4.js +0 -1
  1142. package/apps/pwa/out/_next/static/chunks/app/intake/page-2db61b2f9efba61f.js +0 -1
  1143. package/apps/pwa/out/_next/static/chunks/app/layout-e11ee17f221aaa9b.js +0 -1
  1144. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-e075974e029ce4c4.js +0 -1
  1145. package/apps/pwa/out/_next/static/chunks/app/morning/page-ec5b68a9af3366a0.js +0 -1
  1146. package/apps/pwa/out/_next/static/chunks/app/not-found-986c177afb4adae6.js +0 -1
  1147. package/apps/pwa/out/_next/static/chunks/app/observatory/page-a72781461af2f2a3.js +0 -1
  1148. package/apps/pwa/out/_next/static/chunks/app/page-92781cbaafc487ad.js +0 -1
  1149. package/apps/pwa/out/_next/static/chunks/app/reflection/page-2c14d2a1f2930f33.js +0 -1
  1150. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-22ee4bf8070b2b4d.js +0 -1
  1151. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9c06e4a8297942a8.js +0 -1
  1152. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-d0cd32f6d373798f.js +0 -1
  1153. package/apps/pwa/out/_next/static/chunks/app/settings/page-0c1642018d87ebb8.js +0 -1
  1154. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-93e5e97d614df487.js +0 -1
  1155. package/apps/pwa/out/_next/static/chunks/app/setup/layout-84b84eca12acada4.js +0 -1
  1156. package/apps/pwa/out/_next/static/chunks/app/setup/page-b1ce871940d7393e.js +0 -1
  1157. package/apps/pwa/out/_next/static/chunks/app/share/page-ae154addf345febe.js +0 -1
  1158. package/apps/pwa/out/_next/static/chunks/app/showroom/page-90af3aa813d2053c.js +0 -1
  1159. package/apps/pwa/out/_next/static/chunks/app/tasks/page-f3f962fda428a738.js +0 -1
  1160. package/apps/pwa/out/_next/static/chunks/app/term/page-1c1f7cc1d5def06d.js +0 -1
  1161. package/apps/pwa/out/_next/static/chunks/app/vault/page-09d4cbdaaacf06c1.js +0 -1
  1162. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-e877b8e8ca90b27f.js +0 -1
  1163. package/apps/pwa/out/_next/static/chunks/app/workflows/page-71d902f463dea526.js +0 -1
  1164. package/apps/pwa/out/_next/static/chunks/app/workspace/page-40ea576cefa25fe6.js +0 -1
  1165. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-a56a3f178c3831f6.js +0 -1
  1166. package/apps/pwa/out/_next/static/chunks/bcc499d1-804a270d878e0842.js +0 -1
  1167. package/apps/pwa/out/_next/static/chunks/framework-b25b7c5501c59b6d.js +0 -1
  1168. package/apps/pwa/out/_next/static/chunks/main-a9ec9da43387afff.js +0 -1
  1169. package/apps/pwa/out/_next/static/chunks/main-app-aa2c05954aaa6754.js +0 -1
  1170. package/apps/pwa/out/_next/static/chunks/pages/_app-bb3768fb4b84aebb.js +0 -1
  1171. package/apps/pwa/out/_next/static/chunks/pages/_error-2820ba75b87b2f5e.js +0 -1
  1172. package/apps/pwa/out/_next/static/chunks/webpack-58c73b145f189b1c.js +0 -1
  1173. package/apps/pwa/out/_next/static/css/effec8adf767aa75.css +0 -1
  1174. package/apps/pwa/out/dashboard/index.html +0 -1
  1175. package/apps/pwa/out/dashboard/index.txt +0 -26
  1176. package/src/acp/resident-shell.ts +0 -244
  1177. package/src/agent/spawn-local-llm-in-vw.ts +0 -240
  1178. package/src/dashboard/agent-spawn-tools-boot.ts +0 -14
  1179. package/src/dashboard/dock-picker-inventory.ts +0 -41
  1180. package/src/dashboard/goal-plan-hud-bridge.ts +0 -112
  1181. package/src/dashboard/hud-mirror.ts +0 -207
  1182. package/src/dashboard/input/mouse-dock-runtime.ts +0 -156
  1183. package/src/dashboard/input/mouse-hover-runtime.ts +0 -46
  1184. package/src/dashboard/input/mouse-pill-context-runtime.ts +0 -74
  1185. package/src/dashboard/input/mouse-workspace-restore-runtime.ts +0 -64
  1186. package/src/dashboard/input/scratch-actions.ts +0 -36
  1187. package/src/dashboard/input/virtual-window-key-router.ts +0 -24
  1188. package/src/dashboard/rich-scratch-viewers.ts +0 -77
  1189. package/src/dashboard/scratch-slash-runtime.ts +0 -36
  1190. package/src/dashboard/scratch-state-runtime.ts +0 -42
  1191. package/src/dashboard/scratch-widget-runtime.ts +0 -37
  1192. package/src/dashboard/sim-shell.ts +0 -313
  1193. package/src/dashboard/virtual-window-control-runtime.ts +0 -57
  1194. package/src/dashboard/virtual-window-help-runtime.ts +0 -113
  1195. package/src/dashboard/virtual-window-input-runtime.ts +0 -99
  1196. package/src/dashboard/virtual-window-mutation-runtime.ts +0 -102
  1197. package/src/dashboard/virtual-window-split-runtime.ts +0 -70
  1198. package/src/dashboard/vw-popup-openers.ts +0 -126
  1199. package/src/dashboard/window-picker-popup.ts +0 -74
  1200. package/src/dashboard/window-slash-runtime.ts +0 -108
  1201. package/src/dashboard/windowing/companion-slash.ts +0 -78
  1202. package/src/dashboard/windowing/fast-switch.ts +0 -138
  1203. package/src/dashboard/windowing/lifecycle.ts +0 -185
  1204. package/src/dashboard/windowing/virtual-windows.ts +0 -259
  1205. package/src/dashboard/windowing/visibility-chord.ts +0 -77
  1206. package/src/iul/resident-vw.ts +0 -55
  1207. package/src/panes/content-adapter.ts +0 -162
  1208. package/src/prompt/vw-reference.ts +0 -153
  1209. package/src/shell-runner/external-terminal-pane.ts +0 -157
  1210. package/src/sim/resident-vw.ts +0 -48
  1211. package/src/skills/tools/spawn-coding-agent-vw.ts +0 -181
  1212. package/src/skills/tools/virtual-windows.ts +0 -632
  1213. package/src/surface/adapters/window-surface-adapter.ts +0 -141
  1214. package/src/tool-runtime/layout-runtimes.ts +0 -81
  1215. package/src/tool-runtime/scenario-window-mount.ts +0 -207
  1216. package/src/views/ui-mode.ts +0 -100
  1217. package/src/virtual-window-context-menu.ts +0 -97
  1218. package/src/virtual-windows/addressing.ts +0 -195
  1219. package/src/virtual-windows/benchmark-preset.ts +0 -119
  1220. package/src/virtual-windows/event-bus.ts +0 -202
  1221. package/src/virtual-windows/layout/apply-planner.ts +0 -189
  1222. package/src/virtual-windows/layout/index.ts +0 -68
  1223. package/src/virtual-windows/layout/layout-commands.ts +0 -186
  1224. package/src/virtual-windows/layout/layout-tools.ts +0 -262
  1225. package/src/virtual-windows/layout/persistence.ts +0 -219
  1226. package/src/virtual-windows/layout/presets.ts +0 -114
  1227. package/src/virtual-windows/layout/restore-planner.ts +0 -223
  1228. package/src/virtual-windows/layout/serializer.ts +0 -197
  1229. package/src/virtual-windows/layout/tree.ts +0 -147
  1230. package/src/virtual-windows/layout/types.ts +0 -63
  1231. package/src/virtual-windows/layout-tree.ts +0 -389
  1232. package/src/virtual-windows/navigation.ts +0 -251
  1233. package/src/virtual-windows/pane-capture.ts +0 -142
  1234. package/src/virtual-windows/pane-content.ts +0 -611
  1235. package/src/virtual-windows/pane-spawner.ts +0 -278
  1236. package/src/virtual-windows/pty-tail-content.ts +0 -291
  1237. package/src/virtual-windows/resident-window.ts +0 -59
  1238. package/src/virtual-windows/secondary-action.ts +0 -25
  1239. package/src/virtual-windows/terminal-slot-content.ts +0 -165
  1240. package/src/virtual-windows/virtual-window.ts +0 -1301
  1241. package/src/virtual-windows/vw-local-input-target-popup.ts +0 -110
  1242. package/src/virtual-windows/vw-rename-modal.ts +0 -77
  1243. package/src/virtual-windows/vw-selector-popup.ts +0 -177
  1244. package/src/virtual-windows/window-registry.ts +0 -519
  1245. package/src/voice/channel-adapters/discord-voice-adapter.ts +0 -202
  1246. package/src/voice/voice-pwa-dispatch.ts +0 -245
  1247. package/src/window-picker-modal.ts +0 -135
  1248. /package/apps/pwa/out/_next/static/{Ksn99fVf2HUWhSyV1-FC8 → ktMqfrBbf8BMGac0jqLrb}/_ssgManifest.js +0 -0
  1249. /package/src/dashboard/{companion-slash-runtime.ts → companion-messages-runtime.ts} +0 -0
@@ -7,10 +7,11 @@
7
7
  // - 막히면 omni-crawl 자율 검색 → worktree 파일로 떨궈 codex 에 read 지시(TUI 멀티라인 회피)
8
8
  // - 증거 게이트(doc 존재 / tsc 0 / test pass)로 완료 판정 후 commit
9
9
  // 재사용: startPty(Bun 네이티브 PTY)·createWorktree·streamLLM·worktreeHasChanges·commitWorktree.
10
- import { execFileSync, spawnSync } from 'node:child_process';
10
+ import { execFileSync, spawnSync, spawn as spawnChild } from 'node:child_process';
11
11
  import { homedir } from 'node:os';
12
- import { existsSync, writeFileSync, mkdirSync, readdirSync, readFileSync, statSync } from 'node:fs';
13
- import { join } from 'node:path';
12
+ import { existsSync, writeFileSync, mkdirSync, readdirSync, readFileSync, statSync, readlinkSync, realpathSync, lstatSync, unlinkSync } from 'node:fs';
13
+ import { createHash } from 'node:crypto';
14
+ import { join, relative, resolve, isAbsolute, sep } from 'node:path';
14
15
  import { startPty, mintPtyId, onPtyEvent, type PtyHandle, type StartOpts } from '../pty-shell/registry.js';
15
16
  import { withChildPtyIdentity } from '../agent/pty-identity.js';
16
17
  import { NESTED_AGENT_ENV_BLOCKLIST } from '../agent/nested-agent-env.js';
@@ -19,6 +20,8 @@ import { configuredWorktreeRoot } from '../user-config.js';
19
20
  import { worktreeHasChanges, commitWorktree, changedFiles } from '../self-implement/seams.js';
20
21
  import { streamLLM, type LLMMessage } from '../llm.js';
21
22
  import { debug } from '../debug/log.js';
23
+ import { emitDecision } from '../live/detail-switch.js';
24
+ import { resolvePtyWebAddress } from '../cli/pty-web-address.js';
22
25
  import { reemitPtyUsage } from '../budget/pty-usage-reemit.js';
23
26
  import { classifyAuthError } from '../oauth/codex.js';
24
27
  import type { FallbackStep } from '../oauth/fallback-chain.js';
@@ -29,10 +32,17 @@ import { createLlmControlBrain, type StreamLLMFn } from '../autopilot/llm-contro
29
32
  import { buildExecutorPtyRef, execSurfaceId } from '../self-implement/executor-contract.js';
30
33
  import { runWithControlObserve } from '../capture/control-observe-adapter.js';
31
34
  import { buildMissionProvision, sanitizeForPtyInput, type ProvisionRequest, type ProvisionResult } from './provision.js';
35
+ import { checkClaudeSubscription } from './claude-subscription.js';
36
+ import { handoffMission, waitForHumanLogin, type HandoffOperations } from './handoff.js';
37
+ import { dispatchOpenPullRequest } from '../tool-runtime/git-pr-runtime.js';
38
+ import { runClaudeHeadless } from './claude-headless.js';
39
+ import { installPluginInsideAgent, type PluginRequest, type PluginDeps } from './in-agent-plugin.js';
40
+ import { planMissionResources, type ResourceLadderDeps, type ResourcePlan } from './resource-ladder.js';
32
41
  import { ensureRunId } from '../harness/harness-space.js';
33
42
  import { recordHarnessWorktreeProvenance } from '../harness/harness-worktree-add.js';
34
43
  import { resolveInstanceName } from '../instance-identity.js';
35
44
  import { getChannelBus } from '../terminal-matrix/index.js';
45
+ import { envLiteral } from '../platform/env-literal.js';
36
46
  import { publishSelfReportFrame } from '../capture/self-report-frame.js';
37
47
  import { buildExecutorSelfReportFrame } from '../self-implement/executor-frame.js';
38
48
  import { classifierFrameLines, classifyFrameState, type FrameState } from '../capture/frame-state-detect.js';
@@ -47,6 +57,52 @@ import type { IngestionEntry } from '../agent-substrate/execution/ingestion-poli
47
57
 
48
58
  const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
49
59
 
60
+ /** Terminate the PTY process and every descendant found before signalling it. */
61
+ export async function terminateMissionProcessTree(rootPid: number, deps: {
62
+ processTable?: () => string;
63
+ signal?: (pid: number, signal: NodeJS.Signals | 0) => void;
64
+ wait?: (ms: number) => Promise<void>;
65
+ } = {}): Promise<number[]> {
66
+ if (!Number.isSafeInteger(rootPid) || rootPid <= 0) return [];
67
+ const signal = deps.signal ?? ((pid, sig) => process.kill(pid, sig));
68
+ const processTable = deps.processTable ?? (() => execFileSync('ps', ['-eo', 'pid=,ppid='], { encoding: 'utf8' }));
69
+ const children = new Map<number, number[]>();
70
+ try {
71
+ for (const line of processTable().split('\n')) {
72
+ const match = line.trim().match(/^(\d+)\s+(\d+)$/);
73
+ if (!match) continue;
74
+ const pid = Number(match[1]);
75
+ const ppid = Number(match[2]);
76
+ if (!Number.isSafeInteger(pid) || !Number.isSafeInteger(ppid)) continue;
77
+ const siblings = children.get(ppid) ?? [];
78
+ siblings.push(pid);
79
+ children.set(ppid, siblings);
80
+ }
81
+ } catch (error) {
82
+ debug.log('agent-mission', 'process-tree-snapshot-failed', { rootPid, error: String(error) }, { level: 'warn' });
83
+ }
84
+ const visited = new Set<number>();
85
+ const order: number[] = [];
86
+ const collect = (pid: number): void => {
87
+ if (visited.has(pid)) return;
88
+ visited.add(pid);
89
+ for (const child of children.get(pid) ?? []) collect(child);
90
+ order.push(pid);
91
+ };
92
+ collect(rootPid);
93
+ for (const pid of order) {
94
+ try { signal(pid, 'SIGTERM'); } catch { /* The process may already have exited. */ }
95
+ }
96
+ await (deps.wait ?? sleep)(2000);
97
+ for (const pid of order) {
98
+ try {
99
+ signal(pid, 0);
100
+ signal(pid, 'SIGKILL');
101
+ } catch { /* The process has exited or is no longer accessible. */ }
102
+ }
103
+ return order;
104
+ }
105
+
50
106
  /** Bounded classifier-compatible screen tail retained with each screen observation. */
51
107
  export const SCREEN_LOG_TAIL_MAX_LINES = 10;
52
108
  /** Maximum code points retained for each logged screen-tail line. */
@@ -119,15 +175,45 @@ export const codexBackend: AgentBackend = {
119
175
  // ⚠️ "알려진 경로"까지의 보장 — CLI 가 새 과금 env 를 추가하면 추적 필요(flag/scrub drift 는 테스트로 감시).
120
176
 
121
177
  /** claude Code 백엔드 — --dangerously-skip-permissions(권한 자동)·구독(oauth) 모드.
122
- * scrub: API 키 + 대체 인증 토큰(ANTHROPIC_AUTH_TOKEN) + 프로바이더 스위치(Bedrock/Vertex) → oauth 로 좁힘.
123
- * CLAUDE_CODE_USE_BEDROCK/VERTEX 가 unset 이면 AWS/GCP creds 가 있어도 그 경로로 라우팅되지 않는다. */
178
+ * scrub: API 키 + 대체 인증 토큰(ANTHROPIC_AUTH_TOKEN) + 프로바이더 스위치(Bedrock/Vertex/Foundry) → oauth 로 좁힘.
179
+ * CLAUDE_CODE_USE_BEDROCK/VERTEX/FOUNDRY 가 unset 이면 다른 provider 의 creds 가 있어도 그 경로로 라우팅되지 않는다. */
124
180
  export const claudeBackend: AgentBackend = {
125
181
  name: 'claude',
126
182
  cmd: 'claude',
127
183
  args: ['--dangerously-skip-permissions'],
128
- scrubEnv: ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX'],
184
+ // CLAUDE_CODE_OAUTH_TOKEN: a launching Claude Code session exports its own token; the child must use the
185
+ // machine's stored login, never the parent session's (09-29 🅞: status read `oauth_token` → «api-key» · 대표 R2).
186
+ scrubEnv: ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'CLAUDE_CODE_OAUTH_TOKEN', 'CLAUDE_CODE_USE_BEDROCK', 'CLAUDE_CODE_USE_VERTEX', 'CLAUDE_CODE_USE_FOUNDRY'],
187
+ // 🩸 09-29 S6: new worktree → «Accessing workspace … ❯ No, exit / Yes, I trust this folder». The default is «No», so
188
+ // the brain spent all 16 steps on this screen. Move to «Yes» (↓) then confirm (Enter) on the next observation —
189
+ // the worktree is an isolated tree elanous created (same reasoning as agy).
190
+ handleTrust: (screen, write) => {
191
+ const choice = claudeTrustChoice(screen);
192
+ if (choice === 'no-selected') { write('\x1b[B'); return true; }
193
+ if (choice === 'yes-selected') { write('\r'); return true; }
194
+ return false;
195
+ },
129
196
  };
130
197
 
198
+ /** claude reads «text + Enter» arriving in one chunk as a paste, so the Enter becomes a newline and nothing is sent
199
+ * (09-29 실물: three brain inputs piled up in the input box). Send the text, then Enter on its own a moment later. */
200
+ export function submitSeparately(inject: (text: string) => boolean, delay: (fn: () => void) => void = (fn) => { setTimeout(fn, 250); }): (text: string) => boolean {
201
+ return (text) => {
202
+ if (text.length < 2 || !/\r$/.test(text)) return inject(text);
203
+ if (!inject(text.slice(0, -1))) return false;
204
+ delay(() => { inject('\r'); });
205
+ return true;
206
+ };
207
+ }
208
+
209
+ /** claude 첫 화면의 폴더 신뢰 메뉴 — 어느 줄이 선택돼 있나(기본 선택 = «No, exit»). */
210
+ export function claudeTrustChoice(screen: string): 'no-selected' | 'yes-selected' | null {
211
+ if (!/Yes, I trust this folder/.test(screen) || !/(?:Accessing workspace|Quick safety check)/.test(screen)) return null;
212
+ if (/^\s*[❯>]\s*(?:\d+\.\s*)?Yes, I trust this folder/m.test(screen)) return 'yes-selected';
213
+ if (/^\s*[❯>]\s*(?:\d+\.\s*)?No, exit/m.test(screen)) return 'no-selected';
214
+ return null;
215
+ }
216
+
131
217
  /** gemini 백엔드 — ⭐ 실행체는 **Antigravity CLI(`agy`)** 다(대표 2026-08-18 결정).
132
218
  *
133
219
  * ⛔⭐⭐⭐ 왜 `gemini` 가 아닌가 — ***그 CLI 는 «죽었다»***(2026-08-19 라이브 실측):
@@ -153,8 +239,20 @@ export const geminiBackend: AgentBackend = {
153
239
  cmd: 'agy',
154
240
  args: ['--dangerously-skip-permissions'],
155
241
  scrubEnv: ['GEMINI_API_KEY', 'GOOGLE_API_KEY', 'GOOGLE_GENAI_USE_VERTEXAI', 'GOOGLE_APPLICATION_CREDENTIALS'],
242
+ // 🩸 09-27(agy 1.2.12): 새 워크트리마다 «Do you trust the contents of this project?» 메뉴가 먼저 뜨고 첫 줄이
243
+ // «> Yes, I trust this folder» 다. 처리가 없어 미션 문장이 이 메뉴로 들어가 사라졌고, 두뇌가 약 10분 뒤에야
244
+ // 다시 보냈다(탐침 run 06:03 → 06:13). 기본 선택(신뢰)을 Enter 로 고른다 — 워크트리는 elanous 가 만든 격리 트리다.
245
+ handleTrust: (screen, write) => {
246
+ if (agyNeedsTrust(screen)) { write('\r'); return true; }
247
+ return false;
248
+ },
156
249
  };
157
250
 
251
+ /** agy 첫 화면의 폴더 신뢰 메뉴(기본 선택 = 신뢰). */
252
+ export function agyNeedsTrust(screen: string): boolean {
253
+ return /Do you trust the contents of this project\?/.test(screen) && /Yes, I trust this folder/.test(screen);
254
+ }
255
+
158
256
  /** xAI grok Build CLI 백엔드 — --always-approve(전툴 자동승인)·구독(grok login) 모드.
159
257
  * scrub: XAI/GROK API 키 전부 → 남는 인증 = ~/.grok/auth.json(grok login oauth).
160
258
  * ⚠️ 바이너리는 xAI install.sh 가 ~/.grok/bin/grok 에 둔다 — cron/launchd 최소 PATH 에선 ensure-bin-path
@@ -339,8 +437,12 @@ export interface AgentMissionSpec {
339
437
  mission: string;
340
438
  /** repo 루트(기본: CWD 에서 resolveMainRepoRoot). */
341
439
  repo?: string;
342
- /** 새 worktree 브랜치명. */
343
- branch: string;
440
+ /** 새 worktree 브랜치명. workdir 모드에서는 관측 라벨(선택). */
441
+ branch?: string;
442
+ /** 저장소 worktree 대신 직접 실행할 비-git 작업 폴더. */
443
+ workdir?: string;
444
+ /** 호출자의 벽시계 마감/취소 신호. */
445
+ signal?: AbortSignal;
344
446
  /** 분기 base(기본 HEAD). 이전 미션 산출 위에 쌓으려면 그 브랜치명. */
345
447
  base?: string;
346
448
  /** 완료 증거 판정. */
@@ -355,6 +457,16 @@ export interface AgentMissionSpec {
355
457
  omniCrawlPath?: string;
356
458
  /** 구동할 에이전트 backend(기본 codexBackend). claude/gemini/grok 확장점. */
357
459
  agent?: AgentBackend;
460
+ /** Claude CLI 비대화형 stream-json 실행. 다른 backend 에서는 거부. */
461
+ headless?: boolean;
462
+ /** Install in the agent's own UI before sending the mission. */
463
+ plugin?: Pick<PluginRequest, 'plugin' | 'marketplace'>;
464
+ /** Pre-mission resource judgment; off skips it. */
465
+ resources?: 'on' | 'off';
466
+ /** Only the caller's explicit backend selection, not the resolved default. */
467
+ backendExplicit?: string;
468
+ /** Explicit backend sequence; transitions use the same handoff coordinator as brain decisions. */
469
+ chain?: readonly ('codex' | 'claude' | 'elanous')[];
358
470
  /**
359
471
  * ★ elanous 내부 프롬프트 인핸싱(가산·anti-drift) — 원문 verbatim 보존 + 커버리지 체크리스트 부착.
360
472
  * 명시 override(undefined 면 entry 정책이 결정: elanous-apparatus→ON·external-verbatim→OFF·§6e capability 구동).
@@ -389,12 +501,17 @@ export type CodexMissionSpec = AgentMissionSpec;
389
501
  export interface AgentMissionResult {
390
502
  ok: boolean;
391
503
  worktree: string;
392
- branch: string;
504
+ branch: string | null;
505
+ reason?: 'aborted' | 'outside-write' | 'origin-not-github';
506
+ pr?: 'skipped';
507
+ paths?: string[];
393
508
  rounds: number;
394
509
  evidencePath: string | null;
395
510
  committed: boolean;
396
511
  usedOmniCrawl: boolean;
397
512
  detail: string;
513
+ ptyId?: string;
514
+ webUrl?: string | null;
398
515
  }
399
516
  /** @deprecated codex 특정 이름 — AgentMissionResult 을 쓰라. */
400
517
  export type CodexMissionResult = AgentMissionResult;
@@ -405,10 +522,40 @@ export interface AgentMissionDeps {
405
522
  createWorktree?: typeof createWorktree;
406
523
  recordWorktreeProvenance?: typeof recordHarnessWorktreeProvenance;
407
524
  runControlLoop?: typeof runPtyControlLoop;
525
+ /** Inject controller responses while keeping the production control loop. */
526
+ controlStream?: StreamLLMFn;
527
+ resolvePtyWebAddress?: typeof resolvePtyWebAddress;
408
528
  resolveRunFallback?: (input: { currentStep: FallbackStep; currentCredentialRateLimited: true }) => { action: string; backend?: string };
409
529
  /** 재귀 재시도 사이에만 전달되는 런 로컬 폴백 진행 상태. */
410
530
  runtimeFallback?: RuntimeFallbackContext;
411
531
  reemitPtyUsage?: typeof reemitPtyUsage;
532
+ checkClaudeSubscription?: typeof checkClaudeSubscription;
533
+ runClaudeHeadless?: typeof runClaudeHeadless;
534
+ checkEvidence?: typeof checkEvidence;
535
+ commitWorktree?: typeof commitWorktree;
536
+ dispatchOpenPullRequest?: typeof dispatchOpenPullRequest;
537
+ terminateProcessTree?: typeof terminateMissionProcessTree;
538
+ repoStatus?: (repo: string) => string | Promise<string>;
539
+ emitDecision?: typeof emitDecision;
540
+ onPtyStarted?: (handle: PtyHandle, stop: () => void) => void;
541
+ onHeadlessStarted?: (pid: number, kill: () => void) => void;
542
+ isMissionAborted?: () => boolean;
543
+ awaitMission?: <T>(pending: Promise<T>) => Promise<T>;
544
+ onControlPending?: (pending: Promise<unknown>) => void;
545
+ onControlActive?: (pending: Promise<unknown>) => void;
546
+ /** Upper bound for waiting on control stages after cancel (default 10s). */
547
+ cancelDrainMs?: number;
548
+ handoffOperations?: Partial<HandoffOperations>;
549
+ /** Internal continuation: retain this exact worktree rather than calling createWorktree again. */
550
+ handoffWorktree?: { path: string; branch: string; base: string };
551
+ originalMission?: string;
552
+ /** The last controller reason from the previous backend (not a PTY transcript). */
553
+ previousSummary?: string;
554
+ initialTsc?: ReturnType<typeof collectTscDiagnostics> | null;
555
+ installPlugin?: typeof installPluginInsideAgent;
556
+ pluginDeps?: PluginDeps;
557
+ resourceLadder?: ResourceLadderDeps;
558
+ planMissionResources?: typeof planMissionResources;
412
559
  }
413
560
 
414
561
  const DEFAULT_OMNI = `${process.env.HOME}/.claude/skills/omni-crawl/scripts/main.ts`;
@@ -487,6 +634,13 @@ type EvidenceCheckDeps = {
487
634
  readonly executeTsc?: TscExecutor;
488
635
  readonly runTest?: (wt: string, testPath: string) => { ok: boolean; out: string };
489
636
  readonly hasChanges?: (wt: string) => boolean;
637
+ /** Worktree-relative paths this mission changed (uncommitted · untracked · committed since fork). */
638
+ readonly changedPaths?: (wt: string) => readonly string[];
639
+ /** Non-git mission: compare against the files present before execution, not only mtime. */
640
+ readonly startedAt?: number;
641
+ readonly initialFiles?: ReadonlyMap<string, string>;
642
+ /** test mode: whether the worktree is a TypeScript project (default: tsconfig.json exists). */
643
+ readonly hasTsconfig?: boolean;
490
644
  };
491
645
 
492
646
  export function checkEvidence(
@@ -501,8 +655,26 @@ export function checkEvidence(
501
655
  if (ev.kind === 'doc') {
502
656
  const dir = join(wt, ev.dirRel);
503
657
  if (!existsSync(dir)) return { ok: false, path: null, retry: `${ev.dirRel} 아래 문서가 아직 없다.` };
504
- const hit = readdirSync(dir).find((f) => ev.glob.test(f));
505
- return hit ? { ok: true, path: join(dir, hit) } : { ok: false, path: null, retry: `${ev.dirRel} 아래 대상 문서가 아직 없다.` };
658
+ // Only a document this mission wrote counts: a matching file that was already in the
659
+ // worktree (e.g. an old PLAN-*.md) passed round 0 without any work (🅞 09-27).
660
+ const changed = deps.startedAt === undefined ? (deps.changedPaths ?? changedFiles)(wt) : [];
661
+ const dirPrefix = `${ev.dirRel.replace(/\/+$/, '')}/`;
662
+ const isChanged = (f: string) => {
663
+ if (deps.startedAt !== undefined) {
664
+ try {
665
+ const stat = statSync(join(dir, f));
666
+ return stat.isFile() && stat.mtimeMs >= deps.startedAt
667
+ && (!deps.initialFiles || deps.initialFiles.get(f) !== fileFingerprint(join(dir, f)));
668
+ } catch { return false; }
669
+ }
670
+ return changed.some((c) => c === `${dirPrefix}${f}` || (c.endsWith('/') && `${dirPrefix}${f}`.startsWith(c)));
671
+ };
672
+ const matching = readdirSync(dir).filter((f) => ev.glob.test(f));
673
+ const hit = matching.find(isChanged);
674
+ if (hit) return { ok: true, path: join(dir, hit) };
675
+ return { ok: false, path: null, retry: matching.length
676
+ ? `${ev.dirRel} 아래 이름이 맞는 문서는 있지만 이번 미션이 쓴 것이 아니다 — 새로 작성하거나 고쳐라.`
677
+ : `${ev.dirRel} 아래 대상 문서가 아직 없다.` };
506
678
  }
507
679
  if (!hasChanges(wt)) return { ok: false, path: null, retry: '아직 변경사항이 없다. 실제 파일을 작성하라.' };
508
680
  if (ev.kind === 'tsc') {
@@ -516,7 +688,10 @@ export function checkEvidence(
516
688
  // 없으면 비파싱 원시 출력의 **끝** 1500자(out.slice(-1500))를 쓴다. output.slice(0,1500)(앞부분)로
517
689
  // 바꾸면 종전 문구와 어긋나 수용기준을 위반한다.
518
690
  if (ev.fileRel && !existsSync(join(wt, ev.fileRel))) return { ok: false, path: null, retry: `${ev.fileRel} 가 아직 없다.` };
519
- const tsc = executeTsc(wt);
691
+ // 🩸 09-29 S6(retry-demo): without a tsconfig.json tsc prints its help and exits 1, so a mission whose tests passed
692
+ // 2/2 was sent «tsc 실패» with the help text as the error. The test is the evidence the caller asked for — the
693
+ // tsc pre-check only applies to a TypeScript project.
694
+ const tsc: ReturnType<TscExecutor> = deps.hasTsconfig ?? existsSync(join(wt, 'tsconfig.json')) ? executeTsc(wt) : { ran: true, diagnostics: [] };
520
695
  if (!tsc.ran || tsc.diagnostics.length > 0) {
521
696
  const parsed = tsc.diagnostics.map((diagnostic) => diagnostic.line);
522
697
  const errors = parsed.slice(0, 25).join('\n') || (tsc.output ?? tsc.failure ?? '').slice(-1500);
@@ -652,6 +827,9 @@ export interface MissionControlBrainOpts {
652
827
  * 행동은 no-op(wait)로 폴백 = 무회귀. 반환 detail 을 자식에 input 으로 전달(설치됨/거부 안내). */
653
828
  readonly provision?: (req: ProvisionRequest) => Promise<ProvisionResult>;
654
829
  readonly stream?: StreamLLMFn;
830
+ readonly handoffEnabled?: boolean;
831
+ /** Screen-grounded brain summary for the next backend, never the raw PTY screen. */
832
+ readonly onSummary?: (summary: string) => void;
655
833
  }
656
834
 
657
835
  /** 기존 5-action mission 판단을 canonical PTY control-loop 결정으로 축소한다. search는 이 경계에서만
@@ -678,7 +856,9 @@ export function createMissionControlBrain(opts: MissionControlBrainOpts): RunSup
678
856
  - "wait": ${backendName} 가 아직 작업 중(도구 실행/생성)이면 대기.
679
857
  - "verify": ${backendName} 가 MISSION-COMPLETE/완료를 주장하면 증거 게이트(내가 실행)로 검증. 완료주장 시 이걸 우선.
680
858
  - "send": ${backendName} 가 멈추거나 계속 진행이 필요하면 짧고 명확한 지시(text).
681
- - "search": ${backendName} 가 정보/라이브러리/문법/외부자원에 막혔으면 omni-crawl 로 조사(query). ⭐외부 힌트 없이 스스로 판단.${opts.provision ? `
859
+ - "search": ${backendName} 가 정보/라이브러리/문법/외부자원에 막혔으면 omni-crawl 로 조사(query). ⭐외부 힌트 없이 스스로 판단.
860
+ ${opts.handoffEnabled ? ` - "handoff": 다른 담당자로 넘길 때 to(codex/claude/elanous), mission(다음 지시), carry(diff/summary 선택). carry=summary 면 reason 에 화면에서 확인한 리뷰 지적을 구체적으로 요약하라(다음 backend 에 전달된다). claude 리뷰 뒤 지적은 codex 에 summary 로 되돌려라. elanous 는 증거 게이트와 PR.
861
+ - "ask-human": 화면에 로그인 URL/장치 코드가 보이면 reason, url/code(보이는 것만). 토큰·자격파일을 열지 마라.` : ''}${opts.provision ? `
682
862
  - "provision": ${backendName} 가 **없는 Node 패키지**에 막혔으면(예: "Cannot find module 'X'"·"Module not found: X") 격리 worktree 에 자율 설치(spec=**npm 패키지명**, layer="pkg"). ⚠️Node 패키지만 — 파이썬/시스템 도구(pip·apt·brew)는 아직 미지원이니 provision 하지 말고 다른 방법(search·send)으로. 내가 설치 후 재시도를 지시한다.` : ''}
683
863
  증거상태: ${opts.evidenceReady() ? '충족(완료 가능)' : '아직'}.
684
864
  JSON 만: {"action":"...","text":"...","query":"...","spec":"...","layer":"pkg","reason":"..."}`;
@@ -691,7 +871,11 @@ export function createMissionControlBrain(opts: MissionControlBrainOpts): RunSup
691
871
  decisionFromRaw: async (raw, obs): Promise<ControlDecision> => {
692
872
  const decision = parseBrainDecision(raw);
693
873
  history.push(`s${obs.step}:${decision.action}`);
694
- debug.log('agent-mission', 'brain', { step: obs.step, action: decision.action, reason: decision.reason.slice(0, 120) });
874
+ debug.log('agent-mission', 'brain', { step: obs.step, action: decision.action,
875
+ ...(decision.action === 'ask-human' ? {} : { reason: decision.reason.slice(0, 120) }) });
876
+ if (decision.reason && decision.action !== 'ask-human') opts.onSummary?.(decision.reason.slice(0, 2000));
877
+ if (decision.action === 'handoff' && decision.to && decision.mission) return { action: 'handoff', to: decision.to, mission: decision.mission, ...(decision.carry ? { carry: decision.carry } : {}) };
878
+ if (decision.action === 'ask-human') return { action: 'ask-human', reason: decision.reason, ...(decision.url ? { url: decision.url } : {}), ...(decision.code ? { code: decision.code } : {}) };
695
879
  if (decision.action === 'send') return { action: 'input', text: `${decision.text || 'continue'}\r` };
696
880
  if (decision.action === 'verify' || decision.action === 'done') return { action: 'done', reason: decision.reason || 'agent completed' };
697
881
  if (decision.action === 'search') {
@@ -838,7 +1022,7 @@ export function buildMissionWorktreeRequest(
838
1022
  ctx: { repoRoot: string; worktreeRoot: string },
839
1023
  ): CreateWorktreeOpts {
840
1024
  return {
841
- repoRoot: ctx.repoRoot, branch: spec.branch, worktreeRoot: ctx.worktreeRoot, resetExisting: true,
1025
+ repoRoot: ctx.repoRoot, branch: spec.branch!, worktreeRoot: ctx.worktreeRoot, resetExisting: true,
842
1026
  ...(spec.base ? { base: spec.base } : {}),
843
1027
  ...(spec.reuseOwnedWorktree === true ? { reuseOwnedWorktree: true } : {}),
844
1028
  };
@@ -868,10 +1052,334 @@ export function recordMissionWorktreeProvenance(
868
1052
  }
869
1053
 
870
1054
  // ══════════════════ 메인 ══════════════════
1055
+ export const AGENT_MISSION_TAKEOVER_WAIT_MS = 1_800_000;
1056
+
1057
+ function containingGitRepo(dir: string): string | null {
1058
+ const probe = spawnSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], { encoding: 'utf8' });
1059
+ return probe.status === 0 && probe.stdout.trim() ? probe.stdout.trim() : null;
1060
+ }
1061
+
1062
+ function missionRepoStatus(repo: string): Promise<string> {
1063
+ return new Promise((resolve, reject) => {
1064
+ const child = spawnChild('git', ['status', '--porcelain', '-z', '--untracked-files=all'], { cwd: repo });
1065
+ let out = '', err = '';
1066
+ child.stdout.setEncoding('utf8').on('data', (chunk: string) => { out += chunk; });
1067
+ child.stderr.setEncoding('utf8').on('data', (chunk: string) => { err += chunk; });
1068
+ child.on('error', reject);
1069
+ child.on('close', (code) => code === 0 ? resolve(out) : reject(new Error(`git status failed (${code}): ${err}`)));
1070
+ });
1071
+ }
1072
+
1073
+ function statusEntries(status: string): Map<string, string> {
1074
+ const nulDelimited = status.includes('\0');
1075
+ const entries = nulDelimited ? status.split('\0') : status.split('\n');
1076
+ const paths = new Map<string, string>();
1077
+ for (let i = 0; i < entries.length; i++) {
1078
+ const entry = entries[i]!;
1079
+ if (!entry) continue;
1080
+ // Porcelain -z lists the source of a rename in the following entry; its path is literal.
1081
+ if (nulDelimited && /[RC]/.test(entry.slice(0, 2))) i++;
1082
+ const raw = nulDelimited ? entry.slice(3) : entry.slice(3).replace(/^.* -> /, '');
1083
+ if (raw) paths.set(raw, entry.slice(0, 2));
1084
+ }
1085
+ return paths;
1086
+ }
1087
+
1088
+ function insideWorkdir(repo: string, workdir: string, path: string): boolean {
1089
+ const root = realpathSync(repo);
1090
+ const folder = realpathSync(workdir);
1091
+ const candidate = resolve(root, path);
1092
+ const rel = relative(folder, candidate);
1093
+ return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
1094
+ }
1095
+
1096
+ function initialEvidenceFiles(workdir: string, evidence: Extract<EvidenceMode, { kind: 'doc' }>): Map<string, string> {
1097
+ const dir = join(workdir, evidence.dirRel);
1098
+ const files = new Map<string, string>();
1099
+ if (existsSync(dir)) for (const name of readdirSync(dir)) {
1100
+ if (!evidence.glob.test(name)) continue;
1101
+ const fingerprint = fileFingerprint(join(dir, name));
1102
+ if (fingerprint !== null) files.set(name, fingerprint);
1103
+ }
1104
+ return files;
1105
+ }
1106
+
1107
+ function fileFingerprint(path: string): string | null {
1108
+ try {
1109
+ const stat = statSync(path);
1110
+ if (!stat.isFile()) return null;
1111
+ return createHash('sha256').update(readFileSync(path)).digest('hex');
1112
+ } catch { return null; }
1113
+ }
1114
+
1115
+ function linkedTargetFingerprint(path: string, seen: Set<string> = new Set()): string | null {
1116
+ try {
1117
+ const real = realpathSync(path);
1118
+ // A link cycle is hashed as a marker; the content was already hashed on the first visit.
1119
+ if (seen.has(real)) return 'cycle';
1120
+ const stat = statSync(real);
1121
+ if (stat.isFile()) return fileFingerprint(real);
1122
+ if (!stat.isDirectory()) return null;
1123
+ seen.add(real);
1124
+ const hash = createHash('sha256');
1125
+ for (const entry of readdirSync(real, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
1126
+ const child = join(real, entry.name);
1127
+ // Follow nested links too: writing through `link/nested` changes the nested target, not the link text.
1128
+ const content = linkedTargetFingerprint(child, seen) ?? 'missing';
1129
+ hash.update(entry.name).update(entry.isSymbolicLink() ? `link:${readlinkSync(child)}:${content}` : content);
1130
+ }
1131
+ return hash.digest('hex');
1132
+ } catch { return null; }
1133
+ }
1134
+
1135
+ // Git control files change what git and its hooks execute. Objects, the index and refs churn during
1136
+ // ordinary reads and sibling worktrees, so they are not compared.
1137
+ const GIT_CONTROL_ENTRIES = ['config', 'config.worktree', 'hooks', 'info'];
1138
+
1139
+ function gitControlFingerprints(root: string, add: (label: string, hash: string | null) => void): void {
1140
+ const dotGit = join(root, '.git');
1141
+ const dirs: Array<{ dir: string; label: string }> = [];
1142
+ try {
1143
+ const stat = lstatSync(dotGit);
1144
+ if (stat.isDirectory()) dirs.push({ dir: dotGit, label: '.git' });
1145
+ else if (stat.isFile()) {
1146
+ add('.git', fileFingerprint(dotGit));
1147
+ const gitdir = /^gitdir:\s*(.+)$/m.exec(readFileSync(dotGit, 'utf8'))?.[1]?.trim();
1148
+ if (gitdir) {
1149
+ const dir = resolve(root, gitdir);
1150
+ dirs.push({ dir, label: '.git:gitdir' });
1151
+ const common = existsSync(join(dir, 'commondir')) ? readFileSync(join(dir, 'commondir'), 'utf8').trim() : '';
1152
+ if (common) dirs.push({ dir: resolve(dir, common), label: '.git:common' });
1153
+ }
1154
+ }
1155
+ } catch { return; }
1156
+ const walk = (absolute: string, label: string): void => {
1157
+ try {
1158
+ const stat = lstatSync(absolute);
1159
+ if (stat.isDirectory()) {
1160
+ for (const name of readdirSync(absolute).sort()) walk(join(absolute, name), `${label}/${name}`);
1161
+ } else add(label, stat.isSymbolicLink() ? `link:${readlinkSync(absolute)}:${linkedTargetFingerprint(absolute)}` : fileFingerprint(absolute));
1162
+ } catch { /* Absent control files are compared as absent. */ }
1163
+ };
1164
+ for (const { dir, label } of dirs) for (const name of GIT_CONTROL_ENTRIES) walk(join(dir, name), `${label}/${name}`);
1165
+ }
1166
+
1167
+ function externalLinks(dir: string, workdir: string, label: (path: string) => string): Map<string, string | null> {
1168
+ const links = new Map<string, string | null>();
1169
+ const folder = realpathSync(workdir);
1170
+ const visit = (current: string): void => {
1171
+ for (const entry of readdirSync(current, { withFileTypes: true })) {
1172
+ if (entry.name === '.git' && entry.isDirectory()) continue;
1173
+ const path = join(current, entry.name);
1174
+ if (entry.isDirectory()) visit(path);
1175
+ else if (entry.isSymbolicLink()) {
1176
+ try {
1177
+ const target = realpathSync(path);
1178
+ const rel = relative(folder, target);
1179
+ if (rel === '..' || rel.startsWith(`..${sep}`) || isAbsolute(rel)) {
1180
+ links.set(label(path), linkedTargetFingerprint(target));
1181
+ }
1182
+ } catch { /* A broken link cannot be used to write an existing target. */ }
1183
+ }
1184
+ }
1185
+ };
1186
+ visit(dir);
1187
+ return links;
1188
+ }
1189
+
1190
+ function repoFingerprints(repo: string, workdir: string, paths: readonly string[]): Map<string, string | null> {
1191
+ const fingerprints = new Map<string, string | null>();
1192
+ const root = realpathSync(repo);
1193
+ const folder = realpathSync(workdir);
1194
+ const inside = (absolute: string): boolean => {
1195
+ const rel = relative(folder, absolute);
1196
+ return rel === '' || (rel !== '..' && !rel.startsWith(`..${sep}`) && !isAbsolute(rel));
1197
+ };
1198
+ const visit = (dir: string): void => {
1199
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1200
+ if (dir === root && entry.name === '.git') continue;
1201
+ const absolute = join(dir, entry.name);
1202
+ if (inside(absolute)) continue;
1203
+ const path = relative(root, absolute);
1204
+ if (entry.isDirectory()) visit(absolute);
1205
+ else if (entry.isSymbolicLink()) {
1206
+ fingerprints.set(path, `link:${readlinkSync(absolute)}`);
1207
+ try {
1208
+ const target = realpathSync(absolute);
1209
+ if (!target.startsWith(`${root}${sep}`) && target !== root) {
1210
+ fingerprints.set(path, `link:${readlinkSync(absolute)}:${linkedTargetFingerprint(target)}`);
1211
+ }
1212
+ } catch { /* Broken link. */ }
1213
+ }
1214
+ else fingerprints.set(path, fileFingerprint(absolute));
1215
+ }
1216
+ };
1217
+ // Include ignored files as well as tracked/untracked paths: git status alone cannot see them.
1218
+ visit(root);
1219
+ gitControlFingerprints(root, (label, hash) => fingerprints.set(label, hash));
1220
+ for (const path of paths) {
1221
+ if (insideWorkdir(repo, workdir, path) || fingerprints.has(path)) continue;
1222
+ fingerprints.set(path, fileFingerprint(resolve(root, path)));
1223
+ }
1224
+ return fingerprints;
1225
+ }
1226
+
1227
+ /** Abort owns the PTY tree even while the control brain or an external tool is awaiting. */
871
1228
  export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMissionDeps = {}): Promise<AgentMissionResult> {
872
- const backend = spec.agent ?? resolveDefaultBackend();
1229
+ if (spec.signal?.aborted) {
1230
+ debug.log('agent-mission', 'aborted', { workdir: spec.workdir, pids: [], reason: 'aborted' });
1231
+ return { ok: false, reason: 'aborted', worktree: spec.workdir ?? '', branch: spec.workdir ? null : spec.branch ?? null,
1232
+ rounds: 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' };
1233
+ }
1234
+ if (spec.workdir && spec.evidence.kind !== 'doc') throw new Error('workdir 모드는 doc 증거만');
1235
+ const workdir = spec.workdir;
1236
+ const status = deps.repoStatus ?? missionRepoStatus;
1237
+ // Compare the repository that contains the workdir, not the launcher's cwd: a workdir outside any repository
1238
+ // (e.g. a media folder) must not be failed by unrelated writes in the launcher's tree, such as its own logs.
1239
+ const repo = workdir ? (spec.repo ?? containingGitRepo(workdir)) : null;
1240
+ if (workdir && !repo) debug.log('agent-mission', 'workdir-outside-repo', { workdir, pids: [], reason: 'links-only' });
1241
+ const signal = spec.signal;
1242
+ let before = '';
1243
+ let beforeFingerprints = new Map<string, string | null>();
1244
+ let beforeLinks = new Map<string, string | null>();
1245
+ if (workdir) {
1246
+ let stopWaiting!: () => void;
1247
+ const interrupted = new Promise<never>((_resolve, reject) => { stopWaiting = () => reject(new Error('MISSION_ABORTED')); });
1248
+ signal?.addEventListener('abort', stopWaiting, { once: true });
1249
+ try {
1250
+ if (repo) {
1251
+ before = await Promise.race([status(repo), interrupted]);
1252
+ beforeFingerprints = repoFingerprints(repo, workdir, [...statusEntries(before).keys()]);
1253
+ }
1254
+ beforeLinks = externalLinks(workdir, workdir, (path) => path);
1255
+ } catch (error) {
1256
+ if (!signal?.aborted) throw error;
1257
+ debug.log('agent-mission', 'aborted', { workdir, pids: [], reason: 'aborted' });
1258
+ return { ok: false, reason: 'aborted', worktree: workdir, branch: null,
1259
+ rounds: 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' };
1260
+ } finally { signal?.removeEventListener('abort', stopWaiting); }
1261
+ }
1262
+ const startedAt = Date.now();
1263
+ const initialFiles = workdir && spec.evidence.kind === 'doc' ? initialEvidenceFiles(workdir, spec.evidence) : undefined;
1264
+ let handle: PtyHandle | undefined;
1265
+ let childPid: number | undefined;
1266
+ let killChild: (() => void) | undefined;
1267
+ let stopPty: (() => void) | undefined;
1268
+ let abort!: () => void;
1269
+ let cancelled = false;
1270
+ let controlPending: Promise<unknown> | undefined;
1271
+ let controlSettled = false;
1272
+ let activeControl: Promise<unknown> | undefined;
1273
+ let activeSettled = false;
1274
+ const aborted = new Promise<never>((_resolve, reject) => {
1275
+ abort = () => {
1276
+ if (cancelled) return;
1277
+ cancelled = true;
1278
+ debug.log('agent-mission', 'aborted', { workdir, pids: childPid ? [childPid] : handle?.pid ? [handle.pid] : [], reason: 'aborted' });
1279
+ void (async () => {
1280
+ stopPty?.();
1281
+ const pid = childPid ?? handle?.pid;
1282
+ try {
1283
+ if (pid) {
1284
+ const pids = await (deps.terminateProcessTree ?? terminateMissionProcessTree)(pid);
1285
+ debug.log('agent-mission', 'tree-killed', { workdir, pids, reason: 'aborted' });
1286
+ }
1287
+ } finally {
1288
+ if (handle) try { handle.kill(); } catch { /* Already exited. */ }
1289
+ killChild?.();
1290
+ }
1291
+ })().then(() => reject(new Error('MISSION_ABORTED')), reject);
1292
+ };
1293
+ });
1294
+ signal?.addEventListener('abort', abort, { once: true });
1295
+ if (signal?.aborted) abort();
1296
+ let result: AgentMissionResult;
1297
+ try {
1298
+ if (cancelled) await aborted;
1299
+ const body = runAgentMissionBody(spec, { ...deps, onControlActive: (pending) => {
1300
+ activeSettled = false;
1301
+ activeControl = pending.then(() => { activeSettled = true; }, () => { activeSettled = true; });
1302
+ }, onControlPending: (pending) => {
1303
+ controlSettled = false;
1304
+ controlPending = pending.then(() => { controlSettled = true; }, () => { controlSettled = true; });
1305
+ }, awaitMission: (pending) => Promise.race([pending, aborted]), isMissionAborted: () => cancelled, onPtyStarted: (h, stop) => { handle = h; stopPty = stop; deps.onPtyStarted?.(h, stop); if (signal?.aborted) abort(); }, onHeadlessStarted: (pid, kill) => { childPid = pid; killChild = kill; deps.onHeadlessStarted?.(pid, kill); if (signal?.aborted) abort(); } }, startedAt, initialFiles);
1306
+ if (signal) {
1307
+ try { result = await Promise.race([body, aborted]); }
1308
+ catch (error) {
1309
+ if (cancelled) {
1310
+ // The tree is already terminated by abort; a stage not tied to cancellation (e.g. ownership wait)
1311
+ // must not hold the return forever, so the drain is bounded.
1312
+ const drain = (async () => {
1313
+ await body.catch(() => {});
1314
+ if (activeControl && !activeSettled) await activeControl;
1315
+ if (controlPending && !controlSettled) await controlPending;
1316
+ })();
1317
+ let timer: ReturnType<typeof setTimeout> | undefined;
1318
+ const drained = await Promise.race([drain.then(() => true), new Promise<false>((done) => { timer = setTimeout(() => done(false), deps.cancelDrainMs ?? 10_000); })]);
1319
+ clearTimeout(timer);
1320
+ if (!drained) debug.log('agent-mission', 'cancel-drain-timeout', { workdir, pids: childPid ? [childPid] : handle?.pid ? [handle.pid] : [], reason: 'aborted' });
1321
+ }
1322
+ throw error;
1323
+ }
1324
+ } else result = await body;
1325
+ if (cancelled) await aborted;
1326
+ } catch (error) {
1327
+ if (!cancelled) throw error;
1328
+ result = { ok: false, reason: 'aborted', worktree: workdir ?? '', branch: workdir ? null : spec.branch ?? null,
1329
+ rounds: 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' };
1330
+ } finally {
1331
+ signal?.removeEventListener('abort', abort);
1332
+ }
1333
+ if (workdir) {
1334
+ let after = '';
1335
+ if (repo) {
1336
+ let stopWaiting!: () => void;
1337
+ const interrupted = new Promise<never>((_resolve, reject) => { stopWaiting = () => reject(new Error('MISSION_ABORTED')); });
1338
+ signal?.addEventListener('abort', stopWaiting, { once: true });
1339
+ try { after = await Promise.race([status(repo), interrupted]); }
1340
+ catch (error) {
1341
+ if (!signal?.aborted) throw error;
1342
+ after = await status(repo);
1343
+ } finally { signal?.removeEventListener('abort', stopWaiting); }
1344
+ }
1345
+ // A cancel that lands during the final inspection still cancels the mission; it never reports success.
1346
+ if (signal?.aborted && result.reason !== 'aborted') {
1347
+ debug.log('agent-mission', 'aborted', { workdir, pids: [], reason: 'aborted-during-final-inspection' });
1348
+ result = { ...result, ok: false, reason: 'aborted', committed: false, detail: 'aborted' };
1349
+ }
1350
+ const afterEntries = statusEntries(after);
1351
+ const beforeEntries = statusEntries(before);
1352
+ const afterFingerprints = repo
1353
+ ? repoFingerprints(repo, workdir, [...new Set([...beforeFingerprints.keys(), ...afterEntries.keys()])])
1354
+ : new Map<string, string | null>();
1355
+ const paths = [...afterFingerprints].filter(([path, hash]) =>
1356
+ (beforeFingerprints.has(path) && beforeFingerprints.get(path) !== hash)
1357
+ || (!beforeFingerprints.has(path) && hash !== null)
1358
+ || (afterEntries.has(path) && beforeEntries.get(path) !== afterEntries.get(path)))
1359
+ .map(([path]) => path);
1360
+ for (const [link, hash] of externalLinks(workdir, workdir, (path) => path)) {
1361
+ if (beforeLinks.get(link) !== hash && !paths.includes(link)) paths.push(link);
1362
+ }
1363
+ paths.sort();
1364
+ if (paths.length) {
1365
+ debug.log('agent-mission', 'outside-write', { workdir, pids: [], reason: paths.join(', ') });
1366
+ (deps.emitDecision ?? emitDecision)({ kind: 'ESCALATE', what: '작업 폴더 밖 쓰기', reason: paths.join(', '), purpose: '사람이 확인한다', target: 'human' });
1367
+ return { ...result, ok: false, reason: 'outside-write', paths, committed: false };
1368
+ }
1369
+ }
1370
+ return result;
1371
+ }
1372
+
1373
+ async function runAgentMissionBody(spec: AgentMissionSpec, deps: AgentMissionDeps, startedAt: number, initialFiles?: ReadonlyMap<string, string>): Promise<AgentMissionResult> {
1374
+ const backend = spec.agent ?? (spec.chain?.[0] && spec.chain[0] !== 'elanous' ? resolveBackend(spec.chain[0]) : resolveDefaultBackend());
1375
+ if (spec.chain && (spec.chain[0] !== backend.name || !['codex,claude,elanous', 'codex,claude,codex,elanous'].some((allowed) => allowed === spec.chain!.join(',') || (deps.handoffWorktree && allowed.endsWith(`,${spec.chain!.join(',')}`))))) throw new Error('invalid agent mission chain');
1376
+ if (spec.chain && spec.commit === false && spec.chain.includes('elanous')) throw new Error('elanous PR handoff requires commit authorization');
1377
+ if (spec.headless && spec.chain) throw new Error('--headless cannot run a PTY handoff chain');
1378
+ if (spec.headless && backend.name !== 'claude') throw new Error('--headless 는 --backend claude 전용입니다');
1379
+ if (spec.plugin && (spec.headless || (backend.name !== 'codex' && backend.name !== 'claude'))) throw new Error('--plugin 은 codex/claude PTY 전용입니다');
1380
+ if (spec.workdir && spec.chain) throw new Error('workdir 모드는 넘기기 사슬을 쓰지 않는다');
873
1381
  const runtimeFallback = deps.runtimeFallback ?? {
874
- fallbackEligible: spec.agent === undefined,
1382
+ fallbackEligible: spec.agent === undefined && !spec.chain,
875
1383
  attemptedSteps: new Set<FallbackStep>([backend.name === 'grok' ? 'grok' : 'codex-rotate']),
876
1384
  descents: 0,
877
1385
  maxDescents: backend.name === 'codex' ? 1 : 0,
@@ -880,6 +1388,7 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
880
1388
  const createMissionWorktree = deps.createWorktree ?? createWorktree;
881
1389
  const recordWorktreeProvenance = deps.recordWorktreeProvenance ?? recordHarnessWorktreeProvenance;
882
1390
  const repoRoot = spec.repo ?? resolveMainRepoRoot(process.cwd()) ?? process.cwd();
1391
+ if (!spec.workdir && !spec.branch) throw new Error('branch is required without workdir');
883
1392
  const maxRounds = spec.maxRounds ?? 16;
884
1393
  const omniPath = spec.omniCrawlPath ?? DEFAULT_OMNI;
885
1394
  const screensDir = spec.screensDir ?? join(process.env.TMPDIR || '/tmp', `agent-mission-${backend.name}-${spec.branch}`);
@@ -903,32 +1412,124 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
903
1412
  role: 'controller', executor: describeRole({ role: 'executor', executorKind: 'agent', agentBackend: backend.name }),
904
1413
  entry: missionEntry, capabilities: [...caps.active], enhanceActive,
905
1414
  });
906
- // ⭐ 이 자리는 «별칭»(createMissionWorktree)이라 소스 스캔이 못 찾았고 타입 검사가 찾았다
907
- // — 리뷰 3R must-fix ② 가 경고한 그 형태다(별칭·래퍼는 정규식으로 못 센다).
908
- const wt = createMissionWorktree(buildMissionWorktreeRequest(spec, { repoRoot, worktreeRoot: configuredWorktreeRoot() }));
909
- recordMissionWorktreeProvenance(wt.path, buildMissionWorktreeProvenance(spec.branch), recordWorktreeProvenance);
910
- debug.log('agent-mission', 'worktree', { path: wt.path, branch: wt.branch, base: wt.base });
911
- const baselineTsc = spec.evidence.kind === 'tsc' ? collectTscDiagnostics(wt.path) : null;
912
- const evidenceCheck: typeof checkEvidence = (worktree, evidence) => evidence.kind === 'tsc' && baselineTsc
913
- ? checkEvidence(worktree, evidence, { baseline: baselineTsc.diagnostics, executeTsc: baselineTsc.ran ? collectTscDiagnostics : () => baselineTsc })
914
- : checkEvidence(worktree, evidence);
915
-
916
1415
  // 구독 모드 — backend 지정 env 키 스크럽 후 강제 env까지 적용해 cmd·args·env를 순수 seam으로 구성한다.
917
1416
  // forcedEnv 이름의 후속 로깅은 이 착지의 의도적 경계이며, spawn에는 같은 seam의 env가 그대로 흐른다.
918
1417
  const spawnParams = resolveBackendSpawn(backend, process.env);
919
1418
  const env = spawnParams.env;
920
- env.TERM = 'xterm-256color';
921
- // ⭐run-identity — runId 를 spawn 前 확정해 child env(ELANOUS_RUN_ID 상속)와 executorRef 에 동일 배선한다.
922
- // spawn 後 mint 하면 child 는 자기 runId 를 모르고 ref 와도 상관이 끊긴다(K join 정합·review).
1419
+ if (backend.name === 'claude') {
1420
+ const status = (deps.checkClaudeSubscription ?? checkClaudeSubscription)({ env });
1421
+ debug.log('agent-mission.claude-subscription', 'status', {
1422
+ ok: status.ok, authMethod: status.authMethod, apiProvider: status.apiProvider, reason: status.reason,
1423
+ });
1424
+ if (!status.ok) {
1425
+ const action = status.reason === 'logged-out'
1426
+ ? 'Run `claude login` to authenticate a Claude subscription, then retry.'
1427
+ : status.reason === 'api-key'
1428
+ ? 'Claude is using API-key/provider authentication rather than a Claude subscription. Run `claude login` with a subscription account, then retry.'
1429
+ : 'Run `claude auth status --json` and check the Claude CLI/login, then retry.';
1430
+ return {
1431
+ ok: false, worktree: spec.workdir ?? '', branch: spec.branch ?? null, rounds: 0,
1432
+ evidencePath: null, committed: false, usedOmniCrawl: false,
1433
+ detail: `Claude subscription check failed (${status.reason}). ${action}`,
1434
+ };
1435
+ }
1436
+ }
1437
+ // ⭐ 이 자리는 «별칭»(createMissionWorktree)이라 소스 스캔이 못 찾았고 타입 검사가 찾았다
1438
+ // — 리뷰 3R must-fix ② 가 경고한 그 형태다(별칭·래퍼는 정규식으로 못 센다).
1439
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1440
+ const wt = spec.workdir
1441
+ ? { path: spec.workdir, branch: spec.branch ?? null, base: null }
1442
+ : deps.handoffWorktree ?? createMissionWorktree(buildMissionWorktreeRequest(spec, { repoRoot, worktreeRoot: configuredWorktreeRoot() }));
1443
+ if (spec.workdir) debug.log('agent-mission', 'workdir-mode', { workdir: wt.path, pids: [], reason: 'non-git' });
1444
+ else {
1445
+ if (!deps.handoffWorktree) recordMissionWorktreeProvenance(wt.path, buildMissionWorktreeProvenance(spec.branch!), recordWorktreeProvenance);
1446
+ debug.log('agent-mission', 'worktree', { path: wt.path, branch: wt.branch, base: wt.base });
1447
+ }
1448
+ const baselineTsc = spec.evidence.kind === 'tsc' ? (deps.initialTsc ?? collectTscDiagnostics(wt.path)) : null;
1449
+ const evidenceCheck: typeof checkEvidence = (worktree, evidence) => evidence.kind === 'tsc' && baselineTsc
1450
+ ? checkEvidence(worktree, evidence, { baseline: baselineTsc.diagnostics, executeTsc: baselineTsc.ran ? collectTscDiagnostics : () => baselineTsc })
1451
+ : checkEvidence(worktree, evidence, spec.workdir ? { startedAt, initialFiles } : {});
1452
+ const verifyEvidence: typeof checkEvidence = spec.workdir ? evidenceCheck : deps.checkEvidence ?? evidenceCheck;
1453
+ // Resolve identity before either child path; both receive the same run ID through env.
1454
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
923
1455
  const runId = ensureRunId();
924
1456
  env.ELANOUS_RUN_ID = runId;
1457
+ if (spec.headless === true) {
1458
+ // The non-interactive child receives the mission on stdin, never via a PTY or argv.
1459
+ // Keep the same enhancement and additive memory policy as the interactive mission.
1460
+ let prompt = spec.mission;
1461
+ let checklist: string[] = [];
1462
+ if (enhanceActive) {
1463
+ const { enhancePrompt } = await import('../prompt-enhance/enhance.js');
1464
+ const enhanced = await enhancePrompt(spec.mission, {
1465
+ ...(spec.deliverableHint ? { deliverableHint: spec.deliverableHint } : {}),
1466
+ });
1467
+ prompt = enhanced.enhanced;
1468
+ checklist = enhanced.checklist;
1469
+ debug.log('agent-mission', 'enhance', {
1470
+ checklist: checklist.length, enhancedBy: enhanced.enhancedBy,
1471
+ origChars: enhanced.original.length, enhancedChars: prompt.length, verbatimPreserved: enhanced.verbatimPreserved,
1472
+ });
1473
+ }
1474
+ if (spec.memory !== false) {
1475
+ const { recallMemoryContext } = await import('../agent-substrate/execution/memory-context.js');
1476
+ const mem = await recallMemoryContext(spec.mission.slice(0, 300), { limit: 5 });
1477
+ if (mem) { prompt = `${prompt}\n\n${mem}`; debug.log('agent-mission', 'memory', { injected: true, chars: mem.length }); }
1478
+ else debug.log('agent-mission', 'memory', { injected: false });
1479
+ }
1480
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1481
+ const headlessRun = (deps.runClaudeHeadless ?? runClaudeHeadless)({ cwd: wt.path, prompt, env: envLiteral(env), maxTurns: maxRounds, onSpawn: deps.onHeadlessStarted });
1482
+ const result = await (deps.awaitMission ? deps.awaitMission(headlessRun) : headlessRun);
1483
+ debug.log('agent-mission', 'headless-result', {
1484
+ ok: result.ok, reason: result.reason, exitCode: result.exitCode,
1485
+ sessionId: result.sessionId, numTurns: result.numTurns, toolEvents: result.toolEvents,
1486
+ malformedLines: result.malformedLines, durationMs: result.durationMs,
1487
+ });
1488
+ if (!result.ok) return {
1489
+ ok: false, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1490
+ evidencePath: null, committed: false, usedOmniCrawl: false,
1491
+ detail: `Claude headless failed (${result.reason}; exit ${result.exitCode ?? 'unknown'}). Check the Claude CLI and retry.`,
1492
+ };
1493
+ if (deps.isMissionAborted?.()) return { ok: false, reason: 'aborted', worktree: wt.path, branch: wt.branch,
1494
+ rounds: result.numTurns ?? 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' };
1495
+ const finalEv = verifyEvidence(wt.path, spec.evidence);
1496
+ if (!finalEv.ok) return {
1497
+ ok: false, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1498
+ evidencePath: null, committed: false, usedOmniCrawl: false,
1499
+ detail: `미완(증거 부족): ${finalEv.retry ?? '증거가 아직 부족하다.'}`,
1500
+ };
1501
+ if (checklist.length) {
1502
+ const { verifyCoverage } = await import('../prompt-enhance/coverage.js');
1503
+ const coverage = await verifyCoverage(gatherArtifactText(wt.path, finalEv.path), checklist, {});
1504
+ debug.log('agent-mission', 'coverage', { covered: coverage.covered.length, missing: coverage.missing.length,
1505
+ ratio: Number(coverage.ratio.toFixed(2)), method: coverage.method, coverageRetries: 0 });
1506
+ if (coverage.missing.length) debug.log('agent-mission', 'coverage-exhausted', { missing: coverage.missing.length });
1507
+ }
1508
+ let committed = false;
1509
+ if (!deps.isMissionAborted?.() && !spec.workdir && (spec.commit ?? true)) {
1510
+ const c = (deps.commitWorktree ?? commitWorktree)(wt.path, `chore(agent-mission): ${spec.branch} — claude-in-elanous headless 산출`);
1511
+ committed = c.ok;
1512
+ debug.log('agent-mission', 'commit', { ok: c.ok, out: c.out.slice(0, 120) });
1513
+ if (!c.ok) {
1514
+ debug.log('agent-mission', 'result', { ok: false, rounds: result.numTurns ?? 0, evidencePath: finalEv.path, committed: false, usedOmni: false });
1515
+ return { ok: false, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1516
+ evidencePath: finalEv.path, committed: false, usedOmniCrawl: false,
1517
+ detail: `미완(자동 커밋 실패): ${c.out.slice(0, 120)}` };
1518
+ }
1519
+ }
1520
+ debug.log('agent-mission', 'result', { ok: true, rounds: result.numTurns ?? 0, evidencePath: finalEv.path, committed, usedOmni: false });
1521
+ return { ok: true, worktree: wt.path, branch: wt.branch, rounds: result.numTurns ?? 0,
1522
+ evidencePath: finalEv.path, committed, usedOmniCrawl: false, detail: '완료(증거 충족)' };
1523
+ }
1524
+ env.TERM = 'xterm-256color';
1525
+ // ⭐run-identity — runId was resolved before either headless or PTY spawn.
925
1526
  debug.log('agent-mission', 'spawn', { agent: backend.name, cmd: `${spawnParams.cmd} ${spawnParams.args.join(' ')}`, scrubbed: backend.scrubEnv ?? [], nestedEnvRemovedCount: spawnParams.nestedEnvRemovedCount, runId });
926
1527
  // PTY 정체성 — kind(=backend)·nickname(goto 로 나중 접근)·accessMode='auto'(헤드리스 자율·brain 이 write 소유).
927
1528
  const ptyOpts = buildAgentMissionPtySpawnOptions({
928
1529
  backend,
929
1530
  spawn: { cmd: spawnParams.cmd, args: spawnParams.args, env, unsetEnv: spawnParams.unsetEnv },
930
1531
  workdir: wt.path,
931
- nickname: spec.nickname ?? spec.branch,
1532
+ nickname: spec.nickname ?? spec.branch ?? backend.name,
932
1533
  });
933
1534
  const childStartedAt = Date.now();
934
1535
  let liveDirty = false, liveChunks = 0;
@@ -970,8 +1571,12 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
970
1571
  }
971
1572
  });
972
1573
  let h: PtyHandle;
1574
+ if (deps.isMissionAborted?.()) { offLive(); throw new Error('MISSION_ABORTED'); }
973
1575
  try { h = spawnPty(ptyOpts); }
974
1576
  catch (error) { offLive(); throw error; }
1577
+ deps.onPtyStarted?.(h, () => { if (liveTimer) clearInterval(liveTimer); offLive(); });
1578
+ if (deps.isMissionAborted?.()) return { ok: false, reason: 'aborted', worktree: wt.path, branch: wt.branch,
1579
+ rounds: 0, evidencePath: null, committed: false, usedOmniCrawl: false, detail: 'aborted' };
975
1580
  const executorRef = buildExecutorPtyRef({
976
1581
  ptyId: h.id,
977
1582
  backend: backend.name,
@@ -980,6 +1585,9 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
980
1585
  instance: resolveInstanceName(),
981
1586
  });
982
1587
  debug.log('agent-mission', 'pty', { id: h.id, kind: h.kind, nickname: h.nickname, accessMode: h.accessMode, executorRef });
1588
+ const webAddress = (deps.resolvePtyWebAddress ?? resolvePtyWebAddress)(h.id);
1589
+ process.stderr.write(`[agent-mission] pty=${h.id} watch=${webAddress.webUrl ?? `(web unavailable: ${webAddress.pwaUnavailableReason ?? 'not-resolved'})`}\n`);
1590
+ debug.log('agent-mission', 'pty-link', { id: h.id, webUrl: webAddress.webUrl, webUrlSource: webAddress.webUrlSource });
983
1591
 
984
1592
  // ⭐P2 arbiter: the child is accessMode='auto' (brain-owned), so the driver —
985
1593
  // the autonomous brain — must write as 'agent' or the arbiter denies it. This
@@ -987,10 +1595,10 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
987
1595
  // inject unless they takeover (auto→write, needs 'open' policy). `h['write']`
988
1596
  // form so the drive helper isn't caught by the h.write→drive rewrite.
989
1597
  //
990
- // ⭐takeover 조율/셀프힐(review): 사람이 takeover 하면(auto→write) agent write 가 arbiter 에
991
- // 거부된다. 조용히 유실돼 미션이 헛도는 대신, drive 前 canWrite 로 감지해 미션을 깔끔히 중단
992
- // (사람이 제어를 가져갔으니 자율 구동 멈춤 = HITL 로 넘어감). 관측 남김.
1598
+ // 초기 미션 전송 중 소유권을 잃으면 write 를 시도하지 않는다. 제어 루프에 들어간 뒤의
1599
+ // takeover 는 awaitOwnership 으로 기다리며, 사람이 release 하면 미션을 재개한다.
993
1600
  const drive = (s: string): void => {
1601
+ if (deps.isMissionAborted?.()) return;
994
1602
  // ⭐공용 seam(P2b P-a′) — 판정은 `pty-control-stance` 한 곳. 집행(throw)은 종전 그대로다(무회귀).
995
1603
  const stance = probeControlStance(h, 'agent', (e) =>
996
1604
  debug.log('agent-mission', 'hascontrol-error', { id: h.id, error: (e as Error)?.message ?? String(e) }));
@@ -1033,11 +1641,69 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1033
1641
  const stopLive = (): void => { if (liveTimer) clearInterval(liveTimer); offLive(); };
1034
1642
 
1035
1643
  // ready + trust — backend 별 신뢰/권한 프롬프트 처리(codex=1, 이후 백엔드는 자체 handler).
1036
- await waitForQuiet(h, 1500, 20000);
1037
- let screen = await capture('ready');
1038
- if (backend.handleTrust?.(screen, (s) => drive(s))) {
1039
- debug.log('agent-mission', 'trust', { agent: backend.name, action: 'handled' });
1040
- await waitForQuiet(h, 1500, 15000); screen = await capture('trusted');
1644
+ await (deps.awaitMission ? deps.awaitMission(waitForQuiet(h, 1500, 20000)) : waitForQuiet(h, 1500, 20000));
1645
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1646
+ let screen = await (deps.awaitMission ? deps.awaitMission(capture('ready')) : capture('ready'));
1647
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1648
+ // A trust menu can need more than one key (claude: ↓ to «Yes», then Enter) — answer until it is gone, at most 3 times.
1649
+ // 🩸 09-29: one pass sent only ↓, the mission text went into the menu and its Enter confirmed «Yes» instead of
1650
+ // submitting the mission, which then sat unsent in the input box.
1651
+ for (let pass = 0; pass < 3 && backend.handleTrust?.(screen, (s) => drive(s)); pass++) {
1652
+ debug.log('agent-mission', 'trust', { agent: backend.name, action: 'handled', pass });
1653
+ const trusted = (async () => { await waitForQuiet(h, 1500, 15000); return capture('trusted'); })();
1654
+ screen = await (deps.awaitMission ? deps.awaitMission(trusted) : trusted);
1655
+ }
1656
+
1657
+ let resourcePlan: ResourcePlan | undefined;
1658
+ // Only an explicit `on` runs the ladder (the CLI passes `on` by default). Library callers and fake-PTY
1659
+ // tests that leave it unset keep the pre-ladder behaviour — 09-29 #21849 made them wait on a real LLM.
1660
+ if (spec.resources === 'on') {
1661
+ let timer: ReturnType<typeof setTimeout> | undefined;
1662
+ try {
1663
+ resourcePlan = await Promise.race([
1664
+ (deps.planMissionResources ?? planMissionResources)(spec.mission, deps.resourceLadder ?? {}, {
1665
+ backend: spec.backendExplicit,
1666
+ plugin: spec.plugin ? `${spec.plugin.plugin}@${spec.plugin.marketplace}` : undefined,
1667
+ resources: spec.resources,
1668
+ deadlineMs: 20_000,
1669
+ }),
1670
+ new Promise<never>((_resolve, reject) => { timer = setTimeout(() => reject(new Error('resource ladder timeout (20s)')), 20_000); }),
1671
+ ]);
1672
+ debug.log('agent-mission.resources', 'planned', {
1673
+ have: resourcePlan.have.length, gaps: resourcePlan.gaps.length,
1674
+ suggestedPlugin: resourcePlan.plugin?.plugin, suggestedBackend: resourcePlan.backend?.name,
1675
+ executedBackend: backend.name,
1676
+ });
1677
+ } catch (error) {
1678
+ debug.log('agent-mission.resources', 'plan-failed', { reason: error instanceof Error ? error.message : String(error) }, { level: 'warn' });
1679
+ } finally { if (timer) clearTimeout(timer); }
1680
+ }
1681
+ const suggestedPlugin = resourcePlan?.plugin?.source === 'official-index' && !spec.plugin
1682
+ && !spec.headless && (backend.name === 'codex' || backend.name === 'claude')
1683
+ && resourcePlan.plugin.marketplace === 'elanous'
1684
+ && /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/.test(resourcePlan.plugin.plugin)
1685
+ ? { plugin: resourcePlan.plugin.plugin, marketplace: resourcePlan.plugin.marketplace } : undefined;
1686
+ const requestedPlugin = spec.plugin ?? suggestedPlugin;
1687
+ let installedPlugin: typeof requestedPlugin;
1688
+ if (requestedPlugin) {
1689
+ try {
1690
+ const plugin = await (deps.installPlugin ?? installPluginInsideAgent)(
1691
+ { agent: backend.name as 'codex' | 'claude', ...requestedPlugin }, h,
1692
+ { ...(backend.handleTrust ? { handleTrust: backend.handleTrust } : {}), ...deps.pluginDeps, write: drive },
1693
+ );
1694
+ if (plugin.outcome !== 'installed') {
1695
+ if (spec.plugin) {
1696
+ stopLive();
1697
+ try { h.kill(); } catch { /* already exited */ }
1698
+ return { ok: false, worktree: wt.path, branch: wt.branch, rounds: 0, evidencePath: null,
1699
+ committed: false, usedOmniCrawl: false, detail: `플러그인 설치 ESCALATE (${plugin.reason}) — 미션은 전송하지 않았다` };
1700
+ }
1701
+ debug.log('agent-mission.resources', 'install-failed', { plugin: requestedPlugin.plugin, reason: plugin.reason }, { level: 'warn' });
1702
+ } else installedPlugin = requestedPlugin;
1703
+ } catch (error) {
1704
+ if (spec.plugin) throw error;
1705
+ debug.log('agent-mission.resources', 'install-failed', { plugin: requestedPlugin.plugin, reason: 'installer-error' }, { level: 'warn' });
1706
+ }
1041
1707
  }
1042
1708
 
1043
1709
  // ★ elanous 내부 인핸싱(opt-in) — 원문 verbatim 보존 + 커버리지 체크리스트 부착(anti-drift).
@@ -1066,6 +1732,16 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1066
1732
  else debug.log('agent-mission', 'memory', { injected: false });
1067
1733
  }
1068
1734
 
1735
+ if (installedPlugin) missionText = `${missionText}\n\n설치된 ${installedPlugin.plugin} 플러그인에서 실제 스킬을 확인하고 ${installedPlugin.plugin}:<skill> 형식으로 해당 스킬을 사용하라.`;
1736
+ if (resourcePlan) {
1737
+ const safeName = (name: string) => sanitizeForPtyInput(name, 80)
1738
+ .replace(/\b(?:bearer|secret|password|token|api.?key|authorization)\s*[:=]\s*\S+|(?:sk-[\w-]+|gh[pousr]_[\w-]+|github_pat_[\w-]+|AIza[\w-]+|xox[baprs]-[\w-]+)/gi, '[redacted]')
1739
+ .replace(/https?:\/\/\S+/gi, '[redacted]');
1740
+ const have = resourcePlan.have.slice(0, 5).map(safeName);
1741
+ const gaps = resourcePlan.gaps.slice(0, 5).map(gap => safeName(gap.need));
1742
+ missionText = `${missionText}\n\n[elanous 자원]\n보유: ${have.join(', ') || '없음'}\n설치: ${installedPlugin ? `${installedPlugin.plugin}@${installedPlugin.marketplace}` : '없음'}\n미충족: ${gaps.join(', ') || '없음'}`;
1743
+ }
1744
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1069
1745
  // 미션 전송 — 인핸싱/기억으로 가공됐으면(멀티라인) 파일로 떨궈 read 지시(TUI 멀티라인 위험·verbatim 보존).
1070
1746
  // 원문 그대로면(단문) 타이핑.
1071
1747
  if (missionText !== spec.mission) {
@@ -1082,12 +1758,17 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1082
1758
  }
1083
1759
 
1084
1760
  let usedOmni = false;
1761
+ let controllerSummary = deps.previousSummary ?? '';
1762
+ let reviewSummary = '';
1085
1763
  let coverageRetries = enhanceActive && checklist.length ? 2 : 0;
1086
1764
  const search = createMissionSearch({ worktree: wt.path, omniPath });
1087
1765
  const brain = createMissionControlBrain({
1088
1766
  mission: spec.mission,
1089
1767
  backend,
1090
- evidenceReady: () => evidenceCheck(wt.path, spec.evidence).ok,
1768
+ stream: deps.controlStream,
1769
+ handoffEnabled: true,
1770
+ onSummary: (summary) => { controllerSummary = summary; },
1771
+ evidenceReady: () => spec.chain && spec.chain.length > 1 ? true : verifyEvidence(wt.path, spec.evidence).ok,
1091
1772
  search: async (query, step) => {
1092
1773
  usedOmni = true;
1093
1774
  await search(query, step);
@@ -1098,12 +1779,84 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1098
1779
  // buildMissionProvision 팩토리로 배선(wt.path→cwd 를 DI 로 행동검증 가능·소스 tripwire 불요).
1099
1780
  provision: buildMissionProvision(wt.path),
1100
1781
  });
1782
+ // A trust/permission prompt can appear after the first screen (09-29 S6: codex 0.157 showed «Folder access»
1783
+ // 70 s after launch, after the one-shot check). Answer it at every step with the backend's own bytes
1784
+ // before asking the LLM — otherwise the mission text is typed into the prompt and the run stalls.
1785
+ const trustAwareBrain: RunSupervisor = {
1786
+ decide: (obs, signal) => {
1787
+ let answer: string | undefined;
1788
+ if (backend.handleTrust?.(obs.screen, (bytes) => { answer = bytes; }) && answer !== undefined) {
1789
+ debug.log('agent-mission', 'trust', { agent: backend.name, action: 'handled-in-loop', step: obs.step });
1790
+ emitDecision({ kind: 'ROUTE', what: `${backend.name} 폴더 신뢰 화면 → 계속`, reason: '시작 뒤 늦게 뜬 신뢰 확인', purpose: '미션을 이어 간다', target: backend.name, phase: 'implement' });
1791
+ return { action: 'input', text: answer };
1792
+ }
1793
+ return brain.decide(obs, signal);
1794
+ },
1795
+ };
1796
+ let pendingHandoff: { to: 'codex' | 'claude'; mission: string; summary: string } | undefined;
1797
+ let gatedHandoff = false;
1798
+ let handoffPrSkipped = false;
1799
+ let transitionFailure: string | undefined;
1800
+ const handoffOps: HandoffOperations = {
1801
+ start: async (to, mission) => { pendingHandoff = { to, mission, summary: controllerSummary }; },
1802
+ claudeLoggedIn: () => (deps.checkClaudeSubscription ?? checkClaudeSubscription)({ env: resolveBackendSpawn(claudeBackend, process.env).env }).ok,
1803
+ screen: () => h.renderScreen(),
1804
+ alive: () => h.isAlive(),
1805
+ login: async (worktree) => {
1806
+ const loginSpawn = resolveBackendSpawn(claudeBackend, process.env);
1807
+ const handle = spawnPty(buildAgentMissionPtySpawnOptions({
1808
+ backend: claudeBackend,
1809
+ spawn: { cmd: loginSpawn.cmd, args: ['login'], env: { ...loginSpawn.env, TERM: 'xterm-256color' }, unsetEnv: loginSpawn.unsetEnv },
1810
+ workdir: worktree,
1811
+ nickname: `${spec.branch}-login`,
1812
+ }));
1813
+ return { screen: () => handle.renderScreen(), alive: () => handle.isAlive(), close: () => handle.kill() };
1814
+ },
1815
+ sleep: async (ms) => { await sleep(ms); },
1816
+ notify: async (card) => {
1817
+ const { sendOutbound } = await import('../domains/outbound-alert.js');
1818
+ if (!sendOutbound([card.text, card.url, card.code].filter(Boolean).join('\n'), 'agent-mission')) {
1819
+ throw new Error('agent mission login card delivery failed');
1820
+ }
1821
+ },
1822
+ gateAndPr: async (worktree) => {
1823
+ if (spec.commit === false) throw new Error('elanous PR handoff requires commit authorization');
1824
+ const ev = (deps.checkEvidence ?? evidenceCheck)(worktree, spec.evidence);
1825
+ if (!ev.ok) throw new Error(`handoff evidence gate failed: ${ev.retry ?? 'missing evidence'}`);
1826
+ const promptFile = join(worktree, '.mission-prompt.md');
1827
+ let promptExisted = false;
1828
+ try { lstatSync(promptFile); promptExisted = true; } catch (error) {
1829
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
1830
+ }
1831
+ if (promptExisted) unlinkSync(promptFile);
1832
+ debug.log('agent-mission', 'handoff-prompt-file-removed', { existed: promptExisted });
1833
+ const stagedPrompt = execFileSync('git', ['ls-files', '--cached', '--', '.mission-prompt.md'], { cwd: worktree, encoding: 'utf8' });
1834
+ if (stagedPrompt.trim()) {
1835
+ execFileSync('git', ['rm', '-f', '--cached', '--', '.mission-prompt.md'], { cwd: worktree, encoding: 'utf8' });
1836
+ }
1837
+ if (execFileSync('git', ['ls-files', '--cached', '--', '.mission-prompt.md'], { cwd: worktree, encoding: 'utf8' }).trim()) {
1838
+ throw new Error('handoff prompt file remains in commit index');
1839
+ }
1840
+ const commit = (deps.commitWorktree ?? commitWorktree)(worktree, `chore(agent-mission): ${spec.branch} handoff`);
1841
+ if (!commit.ok) throw new Error(`handoff commit failed: ${commit.out}`);
1842
+ execFileSync('git', ['push', '-u', 'origin', `HEAD:${spec.branch}`], { cwd: worktree, encoding: 'utf8', timeout: 120_000 });
1843
+ const pushUrls = execFileSync('git', ['remote', 'get-url', '--push', '--all', 'origin'], { cwd: worktree, encoding: 'utf8' }).trim().split('\n');
1844
+ if (!pushUrls.length || !pushUrls.every((url) => /^(?:https:\/\/github\.com\/|ssh:\/\/git@github\.com\/|git@github\.com:)[^/]+\/[^/]+\/?$/i.test(url))) {
1845
+ handoffPrSkipped = true;
1846
+ debug.log('agent-mission', 'handoff-pr-skipped', { reason: 'origin-not-github' });
1847
+ } else {
1848
+ (deps.dispatchOpenPullRequest ?? dispatchOpenPullRequest)({ title: `agent-mission: ${spec.branch}`, body: deps.originalMission ?? spec.mission, head: spec.branch, base: spec.base ?? 'main' }, { cwd: worktree });
1849
+ }
1850
+ gatedHandoff = true;
1851
+ },
1852
+ ...deps.handoffOperations,
1853
+ };
1101
1854
  const verifyDone = createMissionVerifyDone({
1102
1855
  worktree: wt.path,
1103
1856
  evidence: spec.evidence,
1104
1857
  checklist,
1105
1858
  coverageRetries,
1106
- checkEvidence: evidenceCheck,
1859
+ checkEvidence: verifyEvidence,
1107
1860
  });
1108
1861
  // ★ P3b-2 observe 어댑터(2026-07-25) — EMIT-side(makeMissionObserveStep)가 프레임 버스에 흘리는 executor
1109
1862
  // 화면을 **구조화 진행 다이제스트**(state+요약·throttle)로 압축해 observe 서피스(로그 패브릭)에 노출한다.
@@ -1114,24 +1867,99 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1114
1867
  // ★ 동일 버스 인스턴스 보장(리뷰 should-fix) — observe 구독과 EMIT(makeMissionObserveStep)이 **같은** 버스를
1115
1868
  // 봐야 프레임이 흐른다. getChannelBus() 를 두 번 부르지 말고 지역 캐시로 구조적 보장.
1116
1869
  const bus = getChannelBus();
1117
- const control = await runWithControlObserve(
1870
+ const observeStep = makeMissionObserveStep({
1871
+ capture,
1872
+ renderPng: () => h.renderScreenPng(),
1873
+ bus,
1874
+ ident: { ptyId: h.id, instance: resolveInstanceName(), runId },
1875
+ });
1876
+ debug.log('agent-mission', 'takeover-wait', { id: h.id, maxWaitMs: AGENT_MISSION_TAKEOVER_WAIT_MS });
1877
+ const controlRun = runWithControlObserve(
1118
1878
  bus, execSurfaceId(h.id),
1119
1879
  (d) => debug.log('agent-mission.observe', 'progress', { surfaceId: d.surfaceId, state: d.state, summary: d.summary, unknownInput: d.unknownInput, frame: d.frameCount, runId: d.runId }),
1120
- () => (deps.runControlLoop ?? runPtyControlLoop)(brain, {
1121
- ...controlDepsForHandle(h),
1122
- settle: async () => { await waitForQuiet(h, 6000, 360000); },
1880
+ () => {
1881
+ const handleDeps = controlDepsForHandle(h);
1882
+ const pending = (deps.runControlLoop ?? runPtyControlLoop)(trustAwareBrain, {
1883
+ ...handleDeps,
1884
+ ...(backend.name === 'claude' ? { inject: submitSeparately(handleDeps.inject) } : {}),
1885
+ ...(deps.isMissionAborted ? {
1886
+ controlStance: () => deps.isMissionAborted!() ? 'unknown' as const : probeControlStance(h, 'agent', (e) =>
1887
+ debug.log('agent-mission', 'hascontrol-error', { id: h.id, error: (e as Error)?.message ?? String(e) })),
1888
+ } : {}),
1889
+ awaitOwnership: { maxWaitMs: AGENT_MISSION_TAKEOVER_WAIT_MS },
1890
+ settle: async () => {
1891
+ const quiet = waitForQuiet(h, 6000, 360000);
1892
+ await (deps.awaitMission ? deps.awaitMission(quiet) : quiet);
1893
+ },
1123
1894
  // ★ U5 — 제어스텝 관측: 프레임 버스 발행(PTY 감독 계열 통일 합류·headless #5379 와 동일 seam) + 화면 전사.
1124
1895
  // 발행 먼저·내부 fail-soft(관측이 미션 안 깸)·capture 는 예외 전파(원 semantics). 배선=makeMissionObserveStep.
1125
- onStep: makeMissionObserveStep({
1126
- capture,
1127
- renderPng: () => h.renderScreenPng(),
1128
- bus,
1129
- ident: { ptyId: h.id, instance: resolveInstanceName(), runId },
1130
- }),
1131
- verifyDone,
1132
- }, { maxSteps: maxRounds }),
1896
+ onStep: async (obs, decision) => {
1897
+ await observeStep(obs, decision);
1898
+ if (backend.name === 'claude') {
1899
+ // Keep only review findings, not authentication screens or a raw PTY transcript.
1900
+ const finding = obs.screen.match(/(?:review findings?|finding|issues?|지적|문제)\s*[::]\s*([^\r\n]+)/i)?.[1]?.trim();
1901
+ if (finding && !/login|log in|sign in|https?:\/\/|token|cookie|password|장치 코드|로그인/i.test(finding)) {
1902
+ reviewSummary = `${reviewSummary}\n${finding}`.trim().slice(-3500);
1903
+ }
1904
+ }
1905
+ },
1906
+ verifyDone: spec.chain && spec.chain.length > 1 ? async () => ({ ok: true as const }) : verifyDone,
1907
+ handoff: async (decision, obs) => {
1908
+ try {
1909
+ if (spec.chain && decision.to !== spec.chain[1]) throw new Error(`chain order violation: expected ${spec.chain[1] ?? 'completion'}, got ${decision.to}`);
1910
+ if (await handoffMission(decision, wt.path, handoffOps, reviewSummary || controllerSummary) === 'gated') gatedHandoff = true;
1911
+ } catch (error) {
1912
+ transitionFailure = error instanceof Error ? error.message : String(error);
1913
+ throw error;
1914
+ }
1915
+ },
1916
+ askHuman: async (decision) => {
1917
+ try {
1918
+ await waitForHumanLogin(decision, handoffOps);
1919
+ } catch (error) {
1920
+ transitionFailure = error instanceof Error ? error.message : String(error);
1921
+ throw error;
1922
+ }
1923
+ },
1924
+ }, { maxSteps: maxRounds });
1925
+ deps.onControlActive?.(pending);
1926
+ return pending;
1927
+ },
1133
1928
  { onSettled: (digestCount) => debug.log('agent-mission.observe', 'summary', { digestCount }), observeOpts: { expectRunId: runId } },
1134
1929
  );
1930
+ deps.onControlPending?.(controlRun);
1931
+ const control = await (deps.awaitMission ? deps.awaitMission(controlRun) : controlRun);
1932
+ if (deps.isMissionAborted?.()) throw new Error('MISSION_ABORTED');
1933
+ if (control.termination.kind === 'success' && !control.handoff && spec.chain && spec.chain.length > 1) {
1934
+ const to = spec.chain[1]!;
1935
+ try {
1936
+ if (await handoffMission({ action: 'handoff', to, mission: to === 'claude' ? 'Review changes and report findings; do not rewrite the original mission.' : to === 'codex' ? `Address the review findings and finish the original mission.\nOriginal mission:\n${deps.originalMission ?? spec.mission}` : deps.originalMission ?? spec.mission, carry: to === 'claude' ? 'diff' : 'summary' }, wt.path, handoffOps, reviewSummary || controllerSummary) === 'gated') gatedHandoff = true;
1937
+ } catch (error) {
1938
+ stopLive();
1939
+ try { h.kill(); } catch { /* noop */ }
1940
+ throw error;
1941
+ }
1942
+ }
1943
+ const nextChain = spec.chain?.slice(1);
1944
+ if (transitionFailure || (control.termination.kind !== 'success' && pendingHandoff)) {
1945
+ stopLive();
1946
+ try { h.kill(); } catch { /* noop */ }
1947
+ throw new Error(transitionFailure ?? 'handoff was requested but the PTY control loop did not finish successfully');
1948
+ }
1949
+ if (pendingHandoff) {
1950
+ stopLive();
1951
+ try { h.kill(); } catch { /* noop */ }
1952
+ const next = pendingHandoff;
1953
+ return runAgentMission({ ...spec, plugin: undefined, agent: resolveBackend(next.to), mission: next.mission, chain: nextChain && nextChain.length > 1 ? nextChain : undefined, enhance: false, memory: false }, { ...deps, handoffWorktree: { path: wt.path, branch: wt.branch!, base: wt.base! }, originalMission: deps.originalMission ?? spec.mission, previousSummary: next.summary, initialTsc: baselineTsc, runtimeFallback: { ...runtimeFallback, fallbackEligible: false } });
1954
+ }
1955
+ // A gate/PR handoff only counts as success when the control loop itself did not end in error.
1956
+ if (gatedHandoff && control.termination.kind !== 'error') {
1957
+ stopLive();
1958
+ try { h.kill(); } catch { /* noop */ }
1959
+ return { ok: true, worktree: wt.path, branch: wt.branch, rounds: control.steps, evidencePath: wt.path, committed: true, usedOmniCrawl: usedOmni,
1960
+ ...(handoffPrSkipped ? { pr: 'skipped' as const, reason: 'origin-not-github' as const } : {}),
1961
+ detail: handoffPrSkipped ? '완료(증거 게이트·push·PR 생략)' : '완료(증거 게이트·PR)' };
1962
+ }
1135
1963
  const done = control.termination.kind === 'success';
1136
1964
  const round = control.steps;
1137
1965
  if (control.termination.kind === 'error') {
@@ -1156,46 +1984,49 @@ export async function runAgentMission(spec: AgentMissionSpec, deps: AgentMission
1156
1984
  descents: runtimeFallback.descents,
1157
1985
  maxFallbackDescents: runtimeFallback.maxDescents,
1158
1986
  });
1159
- if (nextBackend) {
1987
+ if (nextBackend && !deps.isMissionAborted?.()) {
1160
1988
  const nextStep: FallbackStep = nextBackend.name === 'grok' ? 'grok' : 'codex-rotate';
1161
1989
  stopLive();
1990
+ if (h.pid) await (deps.terminateProcessTree ?? terminateMissionProcessTree)(h.pid);
1162
1991
  try { h.kill(); } catch { /* noop */ }
1163
- return runAgentMission(
1164
- { ...spec, agent: nextBackend, reuseOwnedWorktree: true },
1165
- {
1166
- ...deps,
1167
- runtimeFallback: {
1168
- ...runtimeFallback,
1169
- attemptedSteps: new Set([...runtimeFallback.attemptedSteps, nextStep]),
1170
- descents: runtimeFallback.descents + 1,
1171
- },
1992
+ const nextSpec = { ...spec, agent: nextBackend, reuseOwnedWorktree: true };
1993
+ const nextDeps = {
1994
+ ...deps,
1995
+ runtimeFallback: {
1996
+ ...runtimeFallback,
1997
+ attemptedSteps: new Set([...runtimeFallback.attemptedSteps, nextStep]),
1998
+ descents: runtimeFallback.descents + 1,
1172
1999
  },
1173
- );
2000
+ };
2001
+ // Keep the original signal owner and outside-write baseline across a replacement PTY.
2002
+ return spec.signal || spec.workdir ? runAgentMissionBody(nextSpec, nextDeps, startedAt, initialFiles) : runAgentMission(nextSpec, nextDeps);
1174
2003
  }
1175
2004
  }
1176
2005
  debug.log('agent-mission', 'control-result', { termination: control.termination.kind, steps: control.steps });
1177
2006
  if (control.termination.kind === 'cancelled') {
1178
2007
  stopLive();
1179
- try { h.kill(); } catch { /* noop */ }
1180
2008
  throw new Error('AGENT_YIELDED: PTY 제어가 사람에게 이양됨(takeover) — 자율 미션 중단');
1181
2009
  }
1182
2010
 
1183
- const finalEv = evidenceCheck(wt.path, spec.evidence);
2011
+ if (deps.isMissionAborted?.()) return { ok: false, reason: 'aborted', worktree: wt.path, branch: wt.branch,
2012
+ rounds: round, evidencePath: null, committed: false, usedOmniCrawl: usedOmni, detail: 'aborted' };
2013
+ const finalEv = verifyEvidence(wt.path, spec.evidence);
1184
2014
  const evidencePath = finalEv.ok ? finalEv.path : null;
1185
2015
  let committed = false;
1186
- if (finalEv.ok && (spec.commit ?? true)) {
1187
- const c = commitWorktree(wt.path, `chore(agent-mission): ${spec.branch} — ${backend.name}-in-elanous PTY RFC 산출`);
2016
+ if (!deps.isMissionAborted?.() && !spec.workdir && finalEv.ok && (spec.commit ?? true)) {
2017
+ const c = (deps.commitWorktree ?? commitWorktree)(wt.path, `chore(agent-mission): ${spec.branch} — ${backend.name}-in-elanous PTY RFC 산출`);
1188
2018
  committed = c.ok;
1189
2019
  debug.log('agent-mission', 'commit', { ok: c.ok, out: c.out.slice(0, 120) });
1190
2020
  }
1191
2021
  debug.log('agent-mission', 'result', { ok: finalEv.ok, rounds: round, evidencePath, committed, usedOmni });
1192
2022
  stopLive();
2023
+ if (h.pid) await terminateMissionProcessTree(h.pid);
1193
2024
  try { h.kill(); } catch { /* noop */ }
1194
- await sleep(400);
1195
2025
 
1196
2026
  return {
1197
2027
  ok: finalEv.ok, worktree: wt.path, branch: wt.branch, rounds: round,
1198
2028
  evidencePath, committed, usedOmniCrawl: usedOmni,
2029
+ ptyId: h.id, webUrl: webAddress.webUrl,
1199
2030
  detail: done ? '완료(증거 충족)' : (finalEv.ok ? '증거 충족(루프 종료)' : '미완(증거 부족)'),
1200
2031
  };
1201
2032
  }