elanous 0.2.6 → 0.2.7

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 (450) hide show
  1. package/.env.example +10 -7
  2. package/README.md +4 -0
  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/1105-c473ed46272fba79.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/1377-1efeffbbac0963ff.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/1470.98a6ab61ccb4131c.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/216-5acd01cbde4acda7.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/2330-c2f07246c8788af1.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/{3995-bee512a7063036ce.js → 2375-cb6debea56cce4fb.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/2415.ed6428ff334c7ce2.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/2977-e38442e86289f1d3.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/2982-2a2df543be3d2e5a.js +25 -0
  16. package/apps/pwa/out/_next/static/chunks/{8d0d9faf-97887ff99713b84e.js → 2ea96958-e8c7e112de50b067.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/{5329.8b7e4bca684b6eec.js → 3403.4bc97b4066945a94.js} +2 -2
  18. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/371-4ad6af55dad02ced.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/4114-3e63117452378807.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/{7633.0654fe6ea259c1ee.js → 4323.c5dfb6f503304775.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/4836-2eba6079f31b88d4.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/{2984-977cfa339fcd65a5.js → 5080-1613752891fcadb4.js} +3 -3
  24. package/apps/pwa/out/_next/static/chunks/5609-012bb3557211f07e.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/{763-7d0e1913b5074e98.js → 5791-bb82743fa6299562.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/{4240-7153881b0b377306.js → 6062-73e68343a9814f6a.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/6320-baac91d68843a9a6.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/6725-68ab7cc0b1faf00d.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/6998-a654fe5c58271600.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/8067-f74bf14d67ad98f1.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8519-f6b5d8a5833a7603.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8894-e92491a164192cc1.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9740-67051141115ce775.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/9804-64c02c3c127d5014.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/ae787e4c-f2998de7f1ba72a4.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-859cea1570b6227a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d5ebde04844689e6.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-10ed24f413ed66e9.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-211810b6c93ab66a.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/page-624f7a2f89b60c8f.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-5a5fb385a4df0ffe.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-80497e93a878ddc5.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/consult/page-7457f52683562161.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/control/page-cee86394de407aa6.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/design-check/page-4ff8edab44196b8c.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/editor/page-92e4f44428d5e290.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/exec/page-b7dcf5d48a854b76.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/field/page-51480757e7d68b92.js +1 -0
  61. package/apps/pwa/out/_next/static/chunks/app/healthz/route-859cea1570b6227a.js +1 -0
  62. package/apps/pwa/out/_next/static/chunks/app/intake/page-2a05dfe80c8bdf16.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/layout-d9979633aa82f8e6.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/live/page-30533117beb53625.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/market/page-f7857826c2f97ad7.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-07267f1391b96aba.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/morning/page-55d50e9b0006a91d.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/not-found-9699b1786be6171d.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/observatory/page-43dd772aee8af75f.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/page-a7e37af7d0399cb8.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/reflection/page-973ab7af59c718b7.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a9f8d3865b91e436.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/sessions/page-15e5a15db84c56aa.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-a2e572e4511f2a16.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/settings/page-dd107bf1c7d52a90.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-d42b99cdfb9f27c6.js +1 -0
  77. package/apps/pwa/out/_next/static/chunks/app/setup/layout-859cea1570b6227a.js +1 -0
  78. package/apps/pwa/out/_next/static/chunks/app/setup/page-2764ff40dad5a995.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/share/page-c5f1152db116ff96.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/showroom/page-f933bca81ea38a6f.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/tasks/page-c1c95739c0bff3b8.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/term/page-a7aaffcf771662d8.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/trace/page-71d18eec33b9c667.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/vault/page-56adbe1a1f1379bd.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-2c4e59079ee357ac.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/workflows/page-a81f113f3ef89d72.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5d422f38c052967e.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-fc27b00e20733146.js +1 -0
  89. package/apps/pwa/out/_next/static/chunks/framework-ea806bb57f2b8923.js +1 -0
  90. package/apps/pwa/out/_next/static/chunks/main-8d2302399c2c8f87.js +1 -0
  91. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +1 -0
  92. package/apps/pwa/out/_next/static/chunks/pages/_app-b10928c60f0b8488.js +1 -0
  93. package/apps/pwa/out/_next/static/chunks/pages/_error-689039227d443fab.js +1 -0
  94. package/apps/pwa/out/_next/static/chunks/{webpack-37b8bf78a3c6eec5.js → webpack-e5bea36f183b25e6.js} +1 -1
  95. package/apps/pwa/out/_next/static/css/f537bdb19c69306a.css +1 -0
  96. package/apps/pwa/out/_next/static/keajW7DDyghEs1y8oJe6T/_buildManifest.js +1 -0
  97. package/apps/pwa/out/approvals/index.html +1 -1
  98. package/apps/pwa/out/approvals/index.txt +22 -22
  99. package/apps/pwa/out/autopilot/index.html +1 -1
  100. package/apps/pwa/out/autopilot/index.txt +22 -22
  101. package/apps/pwa/out/board/index.html +1 -1
  102. package/apps/pwa/out/board/index.txt +22 -22
  103. package/apps/pwa/out/botlab/index.html +1 -1
  104. package/apps/pwa/out/botlab/index.txt +21 -21
  105. package/apps/pwa/out/bots/index.html +1 -1
  106. package/apps/pwa/out/bots/index.txt +21 -21
  107. package/apps/pwa/out/chat/index.html +1 -1
  108. package/apps/pwa/out/chat/index.txt +21 -21
  109. package/apps/pwa/out/consult/index.html +1 -0
  110. package/apps/pwa/out/consult/index.txt +32 -0
  111. package/apps/pwa/out/control/index.html +1 -1
  112. package/apps/pwa/out/control/index.txt +22 -22
  113. package/apps/pwa/out/design-check/index.html +1 -1
  114. package/apps/pwa/out/design-check/index.txt +22 -22
  115. package/apps/pwa/out/editor/index.html +1 -1
  116. package/apps/pwa/out/editor/index.txt +22 -22
  117. package/apps/pwa/out/exec/index.html +1 -0
  118. package/apps/pwa/out/exec/index.txt +32 -0
  119. package/apps/pwa/out/field/index.html +1 -0
  120. package/apps/pwa/out/field/index.txt +32 -0
  121. package/apps/pwa/out/healthz +1 -1
  122. package/apps/pwa/out/index.html +1 -1
  123. package/apps/pwa/out/index.txt +21 -21
  124. package/apps/pwa/out/intake/index.html +1 -1
  125. package/apps/pwa/out/intake/index.txt +22 -22
  126. package/apps/pwa/out/live/index.html +1 -1
  127. package/apps/pwa/out/live/index.txt +22 -22
  128. package/apps/pwa/out/market/index.html +1 -1
  129. package/apps/pwa/out/market/index.txt +21 -21
  130. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  131. package/apps/pwa/out/missions/placeholder/index.txt +21 -21
  132. package/apps/pwa/out/morning/index.html +1 -1
  133. package/apps/pwa/out/morning/index.txt +22 -22
  134. package/apps/pwa/out/observatory/index.html +1 -1
  135. package/apps/pwa/out/observatory/index.txt +22 -22
  136. package/apps/pwa/out/reflection/index.html +1 -1
  137. package/apps/pwa/out/reflection/index.txt +22 -22
  138. package/apps/pwa/out/scheduler/index.html +1 -1
  139. package/apps/pwa/out/scheduler/index.txt +22 -22
  140. package/apps/pwa/out/sessions/index.html +1 -1
  141. package/apps/pwa/out/sessions/index.txt +21 -21
  142. package/apps/pwa/out/settings/devices/index.html +1 -1
  143. package/apps/pwa/out/settings/devices/index.txt +22 -22
  144. package/apps/pwa/out/settings/index.html +1 -1
  145. package/apps/pwa/out/settings/index.txt +22 -22
  146. package/apps/pwa/out/setup/done/index.html +1 -1
  147. package/apps/pwa/out/setup/done/index.txt +22 -22
  148. package/apps/pwa/out/setup/index.html +1 -1
  149. package/apps/pwa/out/setup/index.txt +22 -22
  150. package/apps/pwa/out/share/index.html +1 -1
  151. package/apps/pwa/out/share/index.txt +22 -22
  152. package/apps/pwa/out/showroom/index.html +1 -1
  153. package/apps/pwa/out/showroom/index.txt +21 -21
  154. package/apps/pwa/out/tasks/index.html +1 -1
  155. package/apps/pwa/out/tasks/index.txt +22 -22
  156. package/apps/pwa/out/term/index.html +1 -1
  157. package/apps/pwa/out/term/index.txt +22 -22
  158. package/apps/pwa/out/trace/index.html +1 -1
  159. package/apps/pwa/out/trace/index.txt +22 -22
  160. package/apps/pwa/out/vault/index.html +1 -1
  161. package/apps/pwa/out/vault/index.txt +22 -22
  162. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  163. package/apps/pwa/out/workflows/chat-ui/index.txt +22 -22
  164. package/apps/pwa/out/workflows/index.html +1 -1
  165. package/apps/pwa/out/workflows/index.txt +22 -22
  166. package/apps/pwa/out/workspace/index.html +1 -1
  167. package/apps/pwa/out/workspace/index.txt +22 -22
  168. package/apps/pwa/out/worktrees/index.html +1 -1
  169. package/apps/pwa/out/worktrees/index.txt +22 -22
  170. package/bin/elanous.cjs +107 -0
  171. package/graphs/field/field-feed.yaml +24 -0
  172. package/graphs/field/recipes.yaml +12 -0
  173. package/graphs/landing/landing-heal.yaml +3 -1
  174. package/graphs/landing/recipes.yaml +3 -0
  175. package/graphs/release/recipes.yaml +10 -0
  176. package/graphs/release/release-loop.yaml +10 -3
  177. package/package.json +5 -5
  178. package/plugins/card-followup/README.md +13 -4
  179. package/plugins/card-followup/graphs/card-followup.yaml +3 -1
  180. package/plugins/card-followup/graphs/recipes.yaml +5 -1
  181. package/plugins/card-followup/graphs/run-step.ts +122 -18
  182. package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
  183. package/plugins/card-followup/plugin.json +1 -1
  184. package/plugins/doc-draft/README.md +13 -0
  185. package/plugins/doc-draft/examples/brief-sample.json +8 -0
  186. package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
  187. package/plugins/doc-draft/graphs/recipes.yaml +9 -0
  188. package/plugins/doc-draft/graphs/run-step.ts +113 -0
  189. package/plugins/doc-draft/plugin.json +24 -0
  190. package/plugins/geo-check/README.md +3 -1
  191. package/plugins/geo-check/graphs/run-step.ts +24 -3
  192. package/plugins/geo-check/plugin.json +1 -1
  193. package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
  194. package/plugins/job-coach/README.md +1 -1
  195. package/plugins/job-coach/graphs/run-step.ts +2 -2
  196. package/plugins/job-coach/plugin.json +1 -1
  197. package/plugins/job-coach/plugin.ts +1 -1
  198. package/scripts/affinity/endcard.sh +3 -2
  199. package/scripts/affinity/reframe.sh +3 -1
  200. package/scripts/ci-public-export-leak-gate.ts +48 -0
  201. package/scripts/codex-quota-alert.ts +122 -258
  202. package/scripts/coord-tracks.json +4 -4
  203. package/scripts/field-feed/deliver-node.ts +52 -0
  204. package/scripts/field-feed/feed-node.ts +67 -0
  205. package/scripts/field-feed/lib.ts +162 -0
  206. package/scripts/field-feed/reel-node.ts +24 -0
  207. package/scripts/field-feed/start.ts +62 -0
  208. package/scripts/fixtures/vflow-sample.ndjson +6 -0
  209. package/scripts/install.sh +92 -13
  210. package/scripts/landing-heal/clean-checkout.ts +114 -0
  211. package/scripts/landing-heal/run-mission.ts +38 -3
  212. package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
  213. package/scripts/public-export.ts +8 -4
  214. package/scripts/release-loop/baseline-from-logs.ts +92 -0
  215. package/scripts/release-loop/env-known-failures.ts +31 -0
  216. package/scripts/release-loop/gate-node.ts +81 -19
  217. package/scripts/release-loop/known-issues-node.ts +159 -0
  218. package/scripts/release-loop/npm-publish-node.ts +118 -0
  219. package/scripts/release-loop/ops-upgrade-node.ts +87 -0
  220. package/scripts/release-loop/release-readiness.ts +58 -0
  221. package/scripts/release-loop/unattended-release.ts +93 -0
  222. package/scripts/ux/measure-first-text.ts +102 -0
  223. package/scripts/vflow-collect.ts +20 -7
  224. package/scripts/vflow-search.ts +73 -0
  225. package/scripts/video-free-line-test-walker.ts +36 -0
  226. package/scripts/video-pipeline.ts +2 -2
  227. package/skills/explainer-video/SKILL.md +12 -9
  228. package/skills/explainer-video/engine/build.mjs +1 -1
  229. package/skills/explainer-video/engine/reel.mjs +138 -31
  230. package/skills/explainer-video/engine/reel.sh +2 -1
  231. package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
  232. package/skills/grill-me/SKILL.md +1 -1
  233. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
  234. package/src/a2a/server.ts +2 -0
  235. package/src/acp/elanous-aware-peers.ts +30 -0
  236. package/src/acp/server.ts +91 -15
  237. package/src/agent/monad-agent-turn.ts +25 -6
  238. package/src/agent/shared-app-tools.ts +5 -3
  239. package/src/agent-substrate/review-intent.ts +6 -2
  240. package/src/agent-substrate/shard-boundary-intent.ts +70 -0
  241. package/src/auth/github-app-token.ts +4 -1
  242. package/src/autopilot/track-agent-shadow.ts +151 -0
  243. package/src/autopilot/track-agent.ts +23 -3
  244. package/src/boot/acp-server-help.ts +16 -0
  245. package/src/boot/daemon-prompt-turn.ts +31 -10
  246. package/src/boot/daemon-tools/index.ts +7 -1
  247. package/src/budget/codex-credit-pace.ts +84 -0
  248. package/src/budget/codex-credit-plan.ts +154 -0
  249. package/src/budget/codex-reset-credit-state.ts +2 -0
  250. package/src/chat/chat-cli.ts +4 -1
  251. package/src/chat/short-question.ts +40 -0
  252. package/src/cli/agent-cli.ts +24 -15
  253. package/src/cli/ask-bare.ts +42 -0
  254. package/src/cli/cli-user-error.ts +25 -0
  255. package/src/cli/codex-credit-plan-view.ts +25 -0
  256. package/src/cli/dependence-gauge.ts +237 -0
  257. package/src/cli/doctor-cli.ts +5 -0
  258. package/src/cli/doctor-modes.ts +48 -0
  259. package/src/cli/intake-cli.ts +12 -2
  260. package/src/cli/intake-source-cli.ts +58 -0
  261. package/src/cli/machine-cli.ts +23 -0
  262. package/src/cli/msg-cli.ts +137 -0
  263. package/src/cli/plugin-cli.ts +25 -0
  264. package/src/cli/pr-cli.ts +36 -0
  265. package/src/cli/provider-cli.ts +5 -0
  266. package/src/cli/release-cli.ts +42 -1
  267. package/src/cli/release-run-if-ready.ts +48 -0
  268. package/src/cli/setup-cli.ts +27 -6
  269. package/src/cli/setup-recommend.ts +8 -0
  270. package/src/cli/usage-cli.ts +11 -1
  271. package/src/codex/setup.ts +27 -8
  272. package/src/dashboard/index.ts +1 -1
  273. package/src/discord.ts +56 -6
  274. package/src/domains/core-tools.ts +37 -14
  275. package/src/domains/outbound-alert.ts +50 -11
  276. package/src/exec-requests/planner.ts +140 -0
  277. package/src/exec-requests/runner.ts +338 -0
  278. package/src/exec-requests/store.ts +76 -0
  279. package/src/field/field-media.ts +8 -3
  280. package/src/field/field-reel.ts +196 -0
  281. package/src/grok/credential.ts +7 -5
  282. package/src/harness/aside-run-status.ts +33 -0
  283. package/src/harness/browser-verify.ts +48 -17
  284. package/src/harness/harness-cli-command.ts +25 -3
  285. package/src/harness/image-load-state.ts +21 -0
  286. package/src/harness/process-garbage.ts +45 -0
  287. package/src/index.ts +86 -14
  288. package/src/instance/leader.ts +13 -1
  289. package/src/intake-plane/collect-github.ts +5 -3
  290. package/src/intake-plane/collect-telegram-saved.ts +15 -3
  291. package/src/intake-plane/discord-seat-work.ts +56 -0
  292. package/src/intake-plane/intake-sources.ts +202 -0
  293. package/src/intake-plane/items.ts +28 -12
  294. package/src/intake-plane/seat-doc-route.ts +207 -0
  295. package/src/intake-plane/submit-intake-work.ts +9 -1
  296. package/src/intake-plane/telegram-seat-work.ts +72 -0
  297. package/src/llm/model-defaults.ts +24 -1
  298. package/src/llm.ts +5 -4
  299. package/src/machines/machine-ledger.ts +127 -0
  300. package/src/market/pack-internal-refs.ts +28 -0
  301. package/src/market/publish.ts +31 -4
  302. package/src/maturity/feature-maturity.ts +65 -0
  303. package/src/model-tier/turn-tier.ts +24 -0
  304. package/src/msg/msg-store.ts +149 -0
  305. package/src/native-tool-catalog.ts +2 -1
  306. package/src/nexus/api/chat-fast-path-config.ts +28 -0
  307. package/src/nexus/api/consult-requests.ts +117 -0
  308. package/src/nexus/api/discord-trigger-bot.ts +3 -0
  309. package/src/nexus/api/exec-requests.ts +51 -0
  310. package/src/nexus/api/field-uploads.ts +40 -3
  311. package/src/nexus/api/graph-approvals-feed.ts +140 -0
  312. package/src/nexus/api/graph-approvals.ts +71 -4
  313. package/src/nexus/api/http-server.ts +61 -7
  314. package/src/nexus/api/seat-requests.ts +254 -0
  315. package/src/nexus/api/trace-run-scan.ts +37 -0
  316. package/src/nexus/api/trace.ts +19 -3
  317. package/src/nexus/index.ts +3 -0
  318. package/src/nexus/setup-status.ts +4 -5
  319. package/src/notify/llm-budget-alert.ts +16 -10
  320. package/src/oauth/codex-account-rotation.ts +26 -4
  321. package/src/oauth/codex-account-store.ts +20 -2
  322. package/src/oauth/codex-browser-login.ts +173 -0
  323. package/src/oauth/codex.ts +4 -1
  324. package/src/oauth/fallback-chain.ts +5 -5
  325. package/src/oauth/store.ts +42 -5
  326. package/src/onboarding/continue-here.ts +29 -0
  327. package/src/onboarding/entry-hints.ts +8 -0
  328. package/src/onboarding/full-screen-io.ts +35 -3
  329. package/src/onboarding/help/skills.md +1 -1
  330. package/src/onboarding/non-interactive.ts +1 -0
  331. package/src/onboarding/subscription-hint.ts +27 -0
  332. package/src/onboarding/web-first.ts +83 -0
  333. package/src/onboarding.ts +230 -23
  334. package/src/plugins/maker/node-install.ts +92 -0
  335. package/src/plugins/maker/node-maker.ts +139 -0
  336. package/src/plugins/maker/plugin-maker.ts +19 -7
  337. package/src/policy/llm-policy.ts +268 -0
  338. package/src/prompt-library/session-guidance.ts +7 -1
  339. package/src/prompt-library/universal-preamble.ts +2 -2
  340. package/src/relay/relay-cli.ts +20 -0
  341. package/src/relay/relay-server.ts +220 -0
  342. package/src/release-loop/checklist.ts +9 -8
  343. package/src/self-dev/decompose.ts +1 -0
  344. package/src/self-dev/dev-pipeline.ts +6 -3
  345. package/src/self-dev/run-record-mirror.ts +44 -0
  346. package/src/self-dev/run-store.ts +12 -3
  347. package/src/self-implement/harness-policy-gates.ts +4 -1
  348. package/src/self-implement/report-origin.ts +12 -1
  349. package/src/session/chat.ts +17 -5
  350. package/src/skills/shared-agent-skill-roots.ts +25 -0
  351. package/src/steward/failure-streak.ts +78 -0
  352. package/src/steward/steward-cards.ts +24 -0
  353. package/src/steward/triage-budget.ts +127 -0
  354. package/src/steward/triage-plan.ts +75 -0
  355. package/src/steward/triage.ts +244 -42
  356. package/src/task-cards/card-store.ts +1 -1
  357. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  358. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
  359. package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
  360. package/src/telegram.ts +38 -2
  361. package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
  362. package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
  363. package/src/user-config.ts +80 -4
  364. package/src/version/packed-revision.json +1 -1
  365. package/src/video-pipeline/recipes/character.ts +42 -5
  366. package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
  367. package/src/video-pipeline/recipes/free-line.ts +16 -3
  368. package/src/video-pipeline/recipes/remote.ts +7 -1
  369. package/src/video-pipeline/vflow-reference.ts +109 -0
  370. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
  371. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
  372. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
  373. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
  374. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
  375. package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
  376. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
  379. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
  382. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
  383. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
  384. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
  385. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
  386. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
  387. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
  388. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
  389. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
  390. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
  391. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
  392. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
  393. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
  394. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
  395. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
  396. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
  397. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
  398. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
  399. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
  400. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
  401. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
  402. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
  403. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
  404. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
  405. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
  406. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
  407. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
  408. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
  409. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
  410. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
  411. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
  412. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
  413. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
  414. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
  415. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
  416. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
  417. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
  418. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
  419. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
  420. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
  421. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
  422. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
  423. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
  424. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
  425. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
  426. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
  427. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
  428. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
  429. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
  430. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
  431. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
  432. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
  433. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
  434. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
  435. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
  436. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
  437. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
  446. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
  448. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
  449. package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
  450. /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → keajW7DDyghEs1y8oJe6T}/_ssgManifest.js +0 -0
@@ -0,0 +1,33 @@
1
+ import { stripScreenAnsi } from './harness-screen.js';
2
+
3
+ export type AsideRunStatus = {
4
+ kind: 'ok' | 'not-running' | 'auth-timeout' | 'repl-error' | 'no-marker';
5
+ hint?: string;
6
+ };
7
+
8
+ export function classifyAsideRun({ stdout, stderr, exitCode, timedOut = false }: {
9
+ stdout: string;
10
+ stderr: string;
11
+ exitCode: number | null;
12
+ /** We killed `aside` at our own timeout. */
13
+ timedOut?: boolean;
14
+ }): AsideRunStatus {
15
+ const diagnosticStdout = stripScreenAnsi(stdout).split(/\r?\n/).filter((line) => {
16
+ try { JSON.parse(line.trim()); return false; } catch { return true; }
17
+ }).join('\n');
18
+ const diagnostics = `${diagnosticStdout}\n${stripScreenAnsi(stderr)}`;
19
+ if (exitCode !== 0 && /Aside isn't running/i.test(diagnostics)) {
20
+ return { kind: 'not-running', hint: 'Aside 브라우저가 안 떠 있음 — Aside 브라우저를 켜고 다시 · 또는 `aside exec --host <host>`' };
21
+ }
22
+ if (exitCode !== 0 && /daemon auth challenge/i.test(diagnostics) && /timeout|timed out/i.test(diagnostics)) {
23
+ return { kind: 'auth-timeout', hint: 'Aside 인증 요청 시간 초과 — Aside 브라우저를 켜고 다시 · 또는 `aside exec --host <host>`' };
24
+ }
25
+ // Killed by our timeout with nothing printed: aside never answered — the browser daemon is not reachable.
26
+ if (timedOut && !stripScreenAnsi(stdout).trim()) {
27
+ return { kind: 'auth-timeout', hint: 'Aside 가 응답하지 않음 — Aside 브라우저가 떠 있는지 확인하고 다시 · 또는 `aside exec --host <host>`' };
28
+ }
29
+ const marker = stripScreenAnsi(stdout).trimEnd().match(/\[(ok|error)\s*\|[^\]]+\]\s*$/);
30
+ if (marker?.[1] === 'error') return { kind: 'repl-error' };
31
+ if (marker?.[1] === 'ok') return { kind: 'ok' };
32
+ return { kind: 'no-marker' };
33
+ }
@@ -16,9 +16,12 @@ import { createCdpClientFromEndpoint, CdpUnavailable, type CdpClient } from '../
16
16
  import { captureWithTimeout, describeCaptureFailure } from './browser-capture-timeout.js';
17
17
  import { debug } from '../debug/log.js';
18
18
  import { stripScreenAnsi } from './harness-screen.js';
19
+ import { classifyAsideRun, type AsideRunStatus } from './aside-run-status.js';
20
+ import { IMAGE_LOAD_STATE_SOURCE } from './image-load-state.js';
19
21
 
20
22
  const execFileAsync = promisify(execFile);
21
- const DEFAULT_ASIDE_TIMEOUT_MS = 10_000;
23
+ // Longer than aside's own ~10 s daemon auth-challenge timeout, so «Aside isn't running» reaches stderr before we kill it (10-01 live).
24
+ const DEFAULT_ASIDE_TIMEOUT_MS = 30_000;
22
25
  // Observed load events and complete page content arrive within this window; timeout remains non-blocking.
23
26
  const CDP_LOAD_WAIT_TIMEOUT_MS = 3_000;
24
27
 
@@ -74,6 +77,8 @@ export interface DeployVerifyResult {
74
77
  title?: string;
75
78
  bodyLength?: number;
76
79
  screenshotBytes?: number;
80
+ /** 아직 화면 밖이라 요청되지 않은 lazy 그림 수(실패가 아님). */
81
+ pendingLazyImages?: number;
77
82
  /** 렌더 문제(빈 페이지·에러 등). ok=false 면 최소 1건. */
78
83
  findings: string[];
79
84
  /** findings와 병행하는 등급 있는 판정. 정상·CDP 미가용 결과에는 생략해 기존 결과 모양을 보존한다. */
@@ -81,10 +86,10 @@ export interface DeployVerifyResult {
81
86
  /** 측정하지 못한 선택적 신호. 빈 배열은 생략하므로 문제 없음과 구별된다. */
82
87
  unmeasured?: DeployUnmeasuredCheck[];
83
88
  /** 관측 백엔드 미가용으로 검증 스킵됨(배포는 막지 않음). */
84
- skipped?: 'no-cdp' | 'no-aside';
89
+ skipped?: 'no-cdp' | 'no-aside' | 'aside-not-running';
85
90
  }
86
91
 
87
- export type AsideRunner = (command: string, args: string[], options: { timeout: number }) => Promise<{ stdout: string }>;
92
+ export type AsideRunner = (command: string, args: string[], options: { timeout: number }) => Promise<{ stdout: string; stderr?: string; exitCode?: number | null; timedOut?: boolean }>;
88
93
 
89
94
  export interface DeployVerifyDeps {
90
95
  /** 기본 CDP 또는 별도 브라우저 세션을 쓰는 aside. */
@@ -121,9 +126,18 @@ interface PageState {
121
126
  title: string;
122
127
  bodyLength: number;
123
128
  unloadedImageCount: number;
129
+ pendingLazyImages: number;
124
130
  consecutiveDuplicateText: string | null;
125
131
  }
126
132
 
133
+ const IMAGE_COUNTS_EXPRESSION = `(${IMAGE_LOAD_STATE_SOURCE})(Array.from(document.images, (image) => {
134
+ const rect = image.getBoundingClientRect();
135
+ return {
136
+ complete: image.complete, naturalWidth: image.naturalWidth, loading: image.loading,
137
+ inViewport: rect.bottom > 0 && rect.right > 0 && rect.top < window.innerHeight && rect.left < window.innerWidth,
138
+ };
139
+ }))`;
140
+
127
141
  const PAGE_STATE_EXPRESSION = `(() => {
128
142
  const isVisible = (element) => {
129
143
  for (let current = element; current; current = current.parentElement) {
@@ -141,10 +155,12 @@ const PAGE_STATE_EXPRESSION = `(() => {
141
155
  }
142
156
  }
143
157
  const consecutiveDuplicateText = textUnits.find((text, index) => text === textUnits[index + 1]) ?? null;
158
+ const imageCounts = ${IMAGE_COUNTS_EXPRESSION};
144
159
  return {
145
160
  title: document.title,
146
161
  bodyLength: document.body ? document.body.innerText.length : 0,
147
- unloadedImageCount: Array.from(document.images).filter((image) => !image.complete || image.naturalWidth === 0).length,
162
+ unloadedImageCount: imageCounts.broken,
163
+ pendingLazyImages: imageCounts.pendingLazy,
148
164
  consecutiveDuplicateText,
149
165
  };
150
166
  })()`;
@@ -169,7 +185,7 @@ function buildAsideScript(url: string): string {
169
185
  ' const page = await openTab(url);',
170
186
  ' try {',
171
187
  ' const title = await page.title();',
172
- " const state = await page.evaluate(() => ({ bodyLength: document.body?.innerText.length ?? 0, unloadedImageCount: Array.from(document.images).filter((image) => !image.complete || image.naturalWidth === 0).length }));",
188
+ ` const state = await page.evaluate(() => { const imageCounts = ${IMAGE_COUNTS_EXPRESSION}; return { bodyLength: document.body?.innerText.length ?? 0, unloadedImageCount: imageCounts.broken, pendingLazyImages: imageCounts.pendingLazy }; });`,
173
189
  ' const screenshot = await page.screenshot({ fullPage: true });',
174
190
  ' console.log(JSON.stringify({ title, ...state, screenshotBytes: screenshot?.length ?? 0 }));',
175
191
  ' } finally { try { await closeTab(page); } catch {} }',
@@ -177,7 +193,7 @@ function buildAsideScript(url: string): string {
177
193
  ].join('\n');
178
194
  }
179
195
 
180
- function parseAsideObservation(stdout: string): { title: string; bodyLength: number; unloadedImageCount: number; screenshotBytes: number } {
196
+ function parseAsideObservation(stdout: string): { title: string; bodyLength: number; unloadedImageCount: number; pendingLazyImages: number; screenshotBytes: number } {
181
197
  const normalizedStdout = stripScreenAnsi(stdout);
182
198
  const verdict = normalizedStdout.trimEnd().match(/\[(ok|error)\s*\|[^\]]+\]\s*$/);
183
199
  if (!verdict) throw new Error('aside observation output missing completion marker');
@@ -189,16 +205,17 @@ function parseAsideObservation(stdout: string): { title: string; bodyLength: num
189
205
  const result: unknown = JSON.parse(line.trim());
190
206
  parsedJson = true;
191
207
  if (!result || typeof result !== 'object' || Array.isArray(result)) throw new Error('aside observation JSON must be an object');
192
- const { title, bodyLength, unloadedImageCount, screenshotBytes } = result as Record<string, unknown>;
208
+ const { title, bodyLength, unloadedImageCount, pendingLazyImages = 0, screenshotBytes } = result as Record<string, unknown>;
193
209
  if (
194
210
  typeof title !== 'string'
195
211
  || typeof bodyLength !== 'number' || !Number.isFinite(bodyLength) || bodyLength < 0
196
212
  || typeof unloadedImageCount !== 'number' || !Number.isFinite(unloadedImageCount) || unloadedImageCount < 0
213
+ || typeof pendingLazyImages !== 'number' || !Number.isFinite(pendingLazyImages) || pendingLazyImages < 0
197
214
  || typeof screenshotBytes !== 'number' || !Number.isFinite(screenshotBytes) || screenshotBytes < 0
198
215
  ) {
199
216
  throw new Error('aside observation JSON has invalid required fields');
200
217
  }
201
- return { title, bodyLength, unloadedImageCount, screenshotBytes };
218
+ return { title, bodyLength, unloadedImageCount, pendingLazyImages, screenshotBytes };
202
219
  } catch (error) {
203
220
  if (parsedJson) throw error;
204
221
  }
@@ -206,9 +223,9 @@ function parseAsideObservation(stdout: string): { title: string; bodyLength: num
206
223
  throw new Error('aside observation output missing JSON');
207
224
  }
208
225
 
209
- function asideErrorResult(url: string, error: unknown): DeployVerifyResult {
226
+ function asideErrorResult(url: string, error: unknown, kind?: AsideRunStatus['kind']): DeployVerifyResult {
210
227
  const message = error instanceof Error ? error.message : String(error);
211
- const errorFinding = finding('aside-error', `aside 검증 오류: ${message.slice(0, 120)}`, 'confirmed');
228
+ const errorFinding = finding('aside-error', `aside 검증 오류: ${message.slice(0, 120)}${kind ? ` (${kind})` : ''}`, 'confirmed');
212
229
  observe('verify-aside-failed', { url: url.slice(0, 80), error: message.slice(0, 120) });
213
230
  return { ok: false, url, findings: [errorFinding.message], structuredFindings: [errorFinding], unmeasured: ['javascript-errors'] };
214
231
  }
@@ -224,15 +241,21 @@ async function verifyAsidePage(url: string, deps: DeployVerifyDeps): Promise<Dep
224
241
  const runAside = deps.runAside ?? (async (command, args, options) => {
225
242
  try {
226
243
  const result = await execFileAsync(command, args, { ...options, encoding: 'utf8' });
227
- return { stdout: result.stdout };
244
+ return { stdout: result.stdout, stderr: result.stderr, exitCode: 0 };
228
245
  } catch (error) {
229
246
  if ((error as NodeJS.ErrnoException).code === 'ENOENT') throw error;
230
- const stdout = (error as { stdout?: unknown }).stdout;
231
- if (typeof stdout === 'string') return { stdout };
247
+ const { stdout, stderr, code, killed, signal } = error as { stdout?: unknown; stderr?: unknown; code?: unknown; killed?: unknown; signal?: unknown };
248
+ if (typeof stdout === 'string' || typeof stderr === 'string') {
249
+ return {
250
+ stdout: typeof stdout === 'string' ? stdout : '', stderr: typeof stderr === 'string' ? stderr : '',
251
+ exitCode: typeof code === 'number' ? code : null,
252
+ ...(killed === true || typeof signal === 'string' ? { timedOut: true } : {}),
253
+ };
254
+ }
232
255
  throw error;
233
256
  }
234
257
  });
235
- let observation: { stdout: string };
258
+ let observation: Awaited<ReturnType<AsideRunner>>;
236
259
  try {
237
260
  observation = await runAside(deps.asideCommand ?? 'aside', ['repl', script], { timeout: deps.asideTimeoutMs ?? DEFAULT_ASIDE_TIMEOUT_MS });
238
261
  } catch (error) {
@@ -242,13 +265,20 @@ async function verifyAsidePage(url: string, deps: DeployVerifyDeps): Promise<Dep
242
265
  }
243
266
  return asideErrorResult(url, error);
244
267
  }
268
+ const status = classifyAsideRun({ stdout: observation.stdout, stderr: observation.stderr ?? '', exitCode: observation.exitCode ?? null, timedOut: observation.timedOut === true });
269
+ if (status.kind === 'not-running' || status.kind === 'auth-timeout') {
270
+ observe('verify-aside-skipped', { url, kind: status.kind });
271
+ return { ok: false, url, skipped: 'aside-not-running', findings: [status.hint ?? 'Aside 브라우저를 켜고 다시'], unmeasured: ['javascript-errors', 'screenshot'] };
272
+ }
273
+ if (status.kind === 'repl-error') return asideErrorResult(url, new Error('aside repl reported error'), status.kind);
274
+ if (status.kind === 'no-marker') return asideErrorResult(url, new Error('aside observation output missing completion marker'), status.kind);
245
275
  const state = parseAsideObservation(observation.stdout);
246
276
  const structuredFindings: DeployVerifyFinding[] = [];
247
277
  if (state.bodyLength < minBody) structuredFindings.push(finding('empty-body', `페이지 본문이 비어있음(len=${state.bodyLength}·렌더 실패 의심)`, 'suspected'));
248
278
  if (state.unloadedImageCount) structuredFindings.push(finding('unloaded-image', `로드되지 못한 그림 ${state.unloadedImageCount}개`, 'confirmed'));
249
279
  if (!state.title.trim()) structuredFindings.push(finding('empty-title', '문서 제목이 비어있음', 'confirmed'));
250
280
  const findings = structuredFindings.map(({ message }) => message);
251
- const result: DeployVerifyResult = { ok: findings.length === 0, url, title: state.title, bodyLength: state.bodyLength, screenshotBytes: state.screenshotBytes, findings, unmeasured: ['javascript-errors'], ...(structuredFindings.length ? { structuredFindings } : {}) };
281
+ const result: DeployVerifyResult = { ok: findings.length === 0, url, title: state.title, bodyLength: state.bodyLength, screenshotBytes: state.screenshotBytes, findings, unmeasured: ['javascript-errors'], ...(state.pendingLazyImages ? { pendingLazyImages: state.pendingLazyImages } : {}), ...(structuredFindings.length ? { structuredFindings } : {}) };
252
282
  observe('verified-aside', { url: url.slice(0, 80), ok: result.ok, bodyLength: state.bodyLength, bytes: state.screenshotBytes, findings: findings.length, unmeasured: result.unmeasured });
253
283
  return result;
254
284
  } catch (error) {
@@ -334,6 +364,7 @@ async function verifyCdpPage(url: string, deps: DeployVerifyDeps): Promise<Deplo
334
364
  return {
335
365
  ok, url, title, bodyLength, screenshotBytes: buf?.length ?? 0, findings,
336
366
  ...(structuredFindings.length ? { structuredFindings } : {}),
367
+ ...(state?.pendingLazyImages ? { pendingLazyImages: state.pendingLazyImages } : {}),
337
368
  ...(unmeasured ? { unmeasured } : {}),
338
369
  };
339
370
  } catch (e) {
@@ -362,9 +393,9 @@ export function formatVerifyUrlReport(
362
393
  const shotNote = shot === undefined ? '' : shot.written ? ` → ${shot.path}` : ` → ⛔ 못 씀(${shot.path})`;
363
394
  const lines = [
364
395
  `\n━━ 배포 검증: ${url} ━━`,
365
- ` ${result.ok ? '✅ 렌더 정상' : '⚠️ 문제 감지'}`,
396
+ ` ${result.skipped === 'aside-not-running' ? '⚪ Aside 검증 건너뜀' : result.ok ? '✅ 렌더 정상' : '⚠️ 문제 감지'}`,
366
397
  ` title: ${result.title || '(없음)'}`,
367
- ` 본문 길이: ${result.bodyLength ?? '?'} · 스크린샷: ${result.screenshotBytes ?? 0} bytes${shotNote}`,
398
+ ` 본문 길이: ${result.bodyLength ?? '?'} · 스크린샷: ${result.skipped === 'aside-not-running' ? '못 쟀다' : `${result.screenshotBytes ?? 0} bytes`}${shotNote}`,
368
399
  ];
369
400
  for (const f of result.findings) lines.push(` - ${f}`);
370
401
  // ⛔⭐ 「못 쟀다」는 초록도 빨강도 아닌 «제 칸»이다 — 「없다」로 접지 않는다.
@@ -21,6 +21,7 @@ import { getUserConfig } from '../user-config.js';
21
21
  import { installHarnessCliSinkHook } from './harness-cli-sink.js';
22
22
  import { resolveHarnessSubstrate, type ResolvedHarnessSubstrate } from './harness-substrate-default.js';
23
23
  import { runHarnessPlanRfc } from './harness-plan-rfc.js';
24
+ import { classifyGarbage, isGarbageProcessTarget, type GarbageProcess } from './process-garbage.js';
24
25
  import type { MissionSolveOutcome } from './mission-solve-loop.js';
25
26
  import { formatAxis, formatAxisObservations, inspectAskMarkers, inspectUnpressedDecisionSignals } from '../../scripts/ask-marker-check.js';
26
27
 
@@ -75,6 +76,7 @@ export class HarnessCliInputError extends Error {
75
76
  constructor(message: string) {
76
77
  super(message);
77
78
  this.name = 'HarnessCliInputError';
79
+ Object.defineProperty(this, Symbol.for('elanous.cli.HarnessCliInputError'), { value: true });
78
80
  }
79
81
  }
80
82
 
@@ -565,6 +567,7 @@ export interface HarnessProcessReport {
565
567
  readonly resourceConsuming: readonly HarnessProcessObservationRow[];
566
568
  readonly longRunningOnly: readonly HarnessProcessObservationRow[];
567
569
  readonly parentUnknown: readonly HarnessProcessObservationRow[];
570
+ readonly garbage: readonly GarbageProcess[];
568
571
  }
569
572
 
570
573
  export interface HarnessProcessObservationDeps {
@@ -762,16 +765,22 @@ export function buildHarnessProcessReport(
762
765
  resourceConsuming: [],
763
766
  longRunningOnly: [],
764
767
  parentUnknown: [],
768
+ garbage: [],
765
769
  };
766
770
  }
767
771
  const livePids = new Set(listed.livePids ?? listed.records.map((record) => record.pid));
768
772
  const universe = listed.pidUniverse ?? pidUniverse;
773
+ const garbage = classifyGarbage(listed.records.map((record) => ({
774
+ ...record,
775
+ launchd: resolveHarnessProcessLaunchdEvidence(record.pid, launchd),
776
+ })), { nowMs }).garbage;
769
777
  const resourceConsuming: HarnessProcessObservationRow[] = [];
770
778
  const longRunningOnly: HarnessProcessObservationRow[] = [];
771
779
  const parentUnknown: HarnessProcessObservationRow[] = [];
772
780
  let unclassifiedCount = 0;
773
781
  let parentPresentCount = 0;
774
782
  for (const record of listed.records) {
783
+ if (!record.command.includes('elanous.mjs')) continue;
775
784
  const classification = classifyHarnessProcess(record, thresholds);
776
785
  if (classification === undefined) {
777
786
  unclassifiedCount += 1;
@@ -811,6 +820,7 @@ export function buildHarnessProcessReport(
811
820
  resourceConsuming,
812
821
  longRunningOnly,
813
822
  parentUnknown,
823
+ garbage,
814
824
  };
815
825
  }
816
826
  return {
@@ -822,6 +832,7 @@ export function buildHarnessProcessReport(
822
832
  resourceConsuming,
823
833
  longRunningOnly,
824
834
  parentUnknown,
835
+ garbage,
825
836
  };
826
837
  }
827
838
 
@@ -855,6 +866,13 @@ function renderHarnessProcessRow(row: HarnessProcessObservationRow): string[] {
855
866
  ];
856
867
  }
857
868
 
869
+ function renderGarbageGroup(garbage: readonly GarbageProcess[]): string[] {
870
+ return [
871
+ `가비지 ${garbage.length}:`,
872
+ ...garbage.map((row) => `- pid=${row.pid} ppid=${row.ppid} elapsed=${formatHarnessProcessElapsed(row.elapsedSeconds)} reason=${row.reason} command=${row.command.slice(0, 120)}`),
873
+ ];
874
+ }
875
+
858
876
  export function renderHarnessProcessReport(report: HarnessProcessReport): string[] {
859
877
  const { resourceCpuPercent, longRunningElapsedSeconds } = report.thresholds;
860
878
  const lines = [
@@ -868,6 +886,7 @@ export function renderHarnessProcessReport(report: HarnessProcessReport): string
868
886
  const failure = report.observationFailure;
869
887
  lines.push(`관찰 실패/확인 불가 (${failure?.stage ?? 'ps-exec'}: ${failure?.reason ?? 'unknown observation failure'})`);
870
888
  lines.push('자원소비와 장기실행만은 확인하지 않았다 — 빈 관찰이 아니다');
889
+ lines.push('가비지 확인 불가 — ps 관찰 실패');
871
890
  return lines;
872
891
  }
873
892
  if (report.observationStatus === 'incomplete') {
@@ -877,7 +896,7 @@ export function renderHarnessProcessReport(report: HarnessProcessReport): string
877
896
  }
878
897
  if (report.excludedCount > 0) {
879
898
  lines.push(`모집단 제외 ${report.excludedCount}행`);
880
- lines.push('제외 기준: command에 elanous.mjs를 포함하지 않은 행');
899
+ lines.push('제외 기준: command에 elanous.mjs가 없고 시험 데몬·러너 대상도 아닌 행');
881
900
  }
882
901
  lines.push(`분류 제외 ${report.unclassifiedCount}행`);
883
902
  lines.push(`부모 생존 제외 ${report.parentPresentCount}행`);
@@ -885,11 +904,13 @@ export function renderHarnessProcessReport(report: HarnessProcessReport): string
885
904
  if (report.parentUnknown.length > 0) lines.push(`부모 확인 불가 ${report.parentUnknown.length} — 자원소비/장기실행만에 넣지 않았다`);
886
905
  const empty = report.resourceConsuming.length === 0 && report.longRunningOnly.length === 0 && report.parentUnknown.length === 0;
887
906
  if (empty && report.observationStatus === 'ok') {
888
- lines.push('관찰 대상 없음');
907
+ if (report.garbage.length === 0) lines.push('관찰 대상 없음');
908
+ lines.push(...renderGarbageGroup(report.garbage));
889
909
  return lines;
890
910
  }
891
911
  if (empty && report.observationStatus === 'incomplete') {
892
912
  lines.push('해석된 행 중 분류 대상이 없다 — 빈 관찰이 아니다');
913
+ lines.push(...renderGarbageGroup(report.garbage));
893
914
  return lines;
894
915
  }
895
916
  if (report.resourceConsuming.length > 0) {
@@ -904,6 +925,7 @@ export function renderHarnessProcessReport(report: HarnessProcessReport): string
904
925
  lines.push('부모 확인 불가:');
905
926
  for (const row of report.parentUnknown) lines.push(...renderHarnessProcessRow(row));
906
927
  }
928
+ lines.push(...renderGarbageGroup(report.garbage));
907
929
  return lines;
908
930
  }
909
931
 
@@ -944,7 +966,7 @@ function parseHarnessProcessPsLine(line: string): ParsedHarnessProcessPsLine {
944
966
  if (!match) return { kind: 'malformed', line: trimmed, pid: parseHarnessProcessPsPid(line) };
945
967
  const command = match[5]!.trim();
946
968
  const pid = Number(match[1]);
947
- if (!command.includes('elanous.mjs')) return { kind: 'excluded', pid };
969
+ if (!isGarbageProcessTarget(command)) return { kind: 'excluded', pid };
948
970
  const elapsedSeconds = parsePsEtime(match[4]!);
949
971
  if (elapsedSeconds === undefined) return { kind: 'malformed', line: trimmed, pid };
950
972
  return {
@@ -0,0 +1,21 @@
1
+ export interface ImageLoadState {
2
+ complete: boolean;
3
+ naturalWidth: number;
4
+ loading: string;
5
+ inViewport: boolean;
6
+ }
7
+
8
+ /** Shared by Node tests and the injected browser expression. */
9
+ export function classifyImages(images: ImageLoadState[]): { broken: number; pendingLazy: number } {
10
+ let broken = 0;
11
+ let pendingLazy = 0;
12
+ for (const image of images) {
13
+ if (image.complete && image.naturalWidth === 0) broken++;
14
+ else if (!image.complete && image.loading === 'lazy' && !image.inViewport) pendingLazy++;
15
+ else if (!image.complete) broken++;
16
+ }
17
+ return { broken, pendingLazy };
18
+ }
19
+
20
+ /** Executable function source for CDP and aside, without a module import in the page. */
21
+ export const IMAGE_LOAD_STATE_SOURCE = classifyImages.toString();
@@ -0,0 +1,45 @@
1
+ export interface GarbageProcessRow {
2
+ readonly pid: number;
3
+ readonly ppid: number;
4
+ readonly elapsedSeconds: number;
5
+ readonly command: string;
6
+ readonly launchd?: 'managed' | 'no-evidence' | 'unqueried';
7
+ }
8
+
9
+ export interface GarbageProcess extends Pick<GarbageProcessRow, 'pid' | 'ppid' | 'elapsedSeconds' | 'command'> {
10
+ readonly reason: 'orphan-test-daemon' | 'orphan-test-runner' | 'orphan-elanous';
11
+ }
12
+
13
+ const TEST_PATH = /elanous|repo\.worktrees|self-impl|\.elanous-test|gate-baseline/i;
14
+ const TEST_DAEMON = /(?:^|[\s/])daemon\.ts(?:\s|$)/;
15
+ const TEST_RUNNER = /\bbun\s+test\b|\bgate(?:[-\s]baseline|\b)/i;
16
+ const NEXUS_RUN = /\bnexus\s+run\b/;
17
+ const TEST_MARKER = /\.elanous-test|(?:^|\s)--test(?:\s|$)/;
18
+
19
+ /** A candidate for inspection only; this module never signals or removes a process. */
20
+ export function isGarbageProcessTarget(command: string): boolean {
21
+ return command.includes('elanous.mjs')
22
+ || (TEST_DAEMON.test(command) && /elanous-nexus-|\.elanous-test/.test(command))
23
+ || (TEST_RUNNER.test(command) && TEST_PATH.test(command));
24
+ }
25
+
26
+ export function classifyGarbage(
27
+ rows: readonly GarbageProcessRow[],
28
+ options: { nowMs?: number; minOrphanSeconds?: number } = {},
29
+ ): { garbage: GarbageProcess[] } {
30
+ const minOrphanSeconds = options.minOrphanSeconds ?? 3 * 60 * 60;
31
+ const garbage: GarbageProcess[] = [];
32
+ for (const row of rows) {
33
+ if (row.ppid !== 1 || row.elapsedSeconds < minOrphanSeconds || row.launchd === 'managed') continue;
34
+ if (!isGarbageProcessTarget(row.command)) continue;
35
+ if (NEXUS_RUN.test(row.command) && !TEST_MARKER.test(row.command)) continue;
36
+ const reason: GarbageProcess['reason'] = TEST_DAEMON.test(row.command)
37
+ && /elanous-nexus-|\.elanous-test/.test(row.command)
38
+ ? 'orphan-test-daemon'
39
+ : TEST_RUNNER.test(row.command) && TEST_PATH.test(row.command)
40
+ ? 'orphan-test-runner'
41
+ : 'orphan-elanous';
42
+ garbage.push({ pid: row.pid, ppid: row.ppid, elapsedSeconds: row.elapsedSeconds, command: row.command, reason });
43
+ }
44
+ return { garbage };
45
+ }
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ import { registerModelWatchCommand } from './cli/model-watch-cli.js';
62
62
  import { registerIntakeCommands } from './cli/intake-cli.js';
63
63
  import { registerTasksCommands } from './cli/tasks-cli.js';
64
64
  import { registerLabelsCommands } from './cli/labels-cli.js';
65
+ import { registerMsgCommands } from './cli/msg-cli.js';
65
66
  import { registerDoctorCommand } from './cli/doctor-cli.js';
66
67
  import { registerProviderCommands } from './cli/provider-cli.js';
67
68
  export { buildCodexAccountImportGuidance, setCodexAccountLogSinkModuleForTesting } from './cli/provider-cli.js';
@@ -74,6 +75,7 @@ import { registerGraphCommands } from './graph-runner/graph-cli.js';
74
75
  import { registerLoopCommands } from './loops/loop-cli.js';
75
76
  import { registerCardCommand } from './task-cards/card-cli.js';
76
77
  import { registerLaunchHeadCommands } from './launch-head/launch-head-cli.js';
78
+ import { acpServerHelpText } from './boot/acp-server-help.js';
77
79
  // IMPORTANT: parse `--config-dir <dir>` + `--test-state-dir <dir>`
78
80
  // BEFORE Commander loads — the resolvers are read at module-init
79
81
  // time by several config-touching imports below, so the override
@@ -95,7 +97,8 @@ import type { AdPipelinePlan, AdPipelineResult } from './ad-pipeline/run.js';
95
97
  import type { AcpPermissionApprover } from './acp/client.js';
96
98
  import { runGitCommand } from './git-fs/runner.js';
97
99
  import { debug, redactSecretText, redactSecrets } from './debug/log.js';
98
- import { HarnessCliInputError, installHarnessCliCommand, type HarnessAskSayChildLlmOptions, type HarnessAskSayOptions, type HarnessPlanOptions } from './harness/harness-cli-command.js';
100
+ import { installHarnessCliCommand, type HarnessAskSayChildLlmOptions, type HarnessAskSayOptions, type HarnessPlanOptions } from './harness/harness-cli-command.js';
101
+ import { formatCliUserError, isCliUserError } from './cli/cli-user-error.js';
99
102
  import { dispatchSolveMission } from './skills/tools/solve-mission.js';
100
103
  import { performBrowserAction, type BrowserActionDeps, type BrowserActionResult } from './harness/browser-act.js';
101
104
  import { decideTypeAction, type TypeActionDecision, type TypeTarget } from './harness/browser-act-type.js';
@@ -152,7 +155,7 @@ async function readStdinLine(prompt: string = ''): Promise<string> {
152
155
  finally { rl.close(); }
153
156
  }
154
157
  import { loginWithCodex, CODEX_DEVICE_LOGIN_URL } from './oauth/codex.js';
155
- import { authStorePath, loadTokens, deleteTokens, listProviders as listAuthProviders } from './oauth/store.js';
158
+ import { authStorePath, loadTokens, deleteTokens, saveTokens, listProviders as listAuthProviders, type CodexMirrorResult } from './oauth/store.js';
156
159
  import { renderKeyHelp, CONTEXT_LABELS, type KeyContext } from './keybindings.js';
157
160
  import { auditKeybindings, renderKeymapAudit } from './keymap-audit.js';
158
161
  import { runCodexSetup } from './codex/setup.js';
@@ -164,6 +167,7 @@ import { initSessionWorkingDir } from './session/working-dir.js';
164
167
  import { rewriteBareNexusToStatus } from './cli/nexus-entry.js';
165
168
  import { registerIngestTokenCommands } from './cli/ingest-token-cli.js';
166
169
  import { registerMcpGatewayCommands } from './cli/mcp-gateway-cli.js';
170
+ import { registerRelayCommand } from './relay/relay-cli.js';
167
171
  import type { ReviewImage } from './agent-substrate/pr-reviewer.js';
168
172
  import {
169
173
  describeMissionRouting,
@@ -864,6 +868,14 @@ registerDoctorCommand(program);
864
868
  registerControlCommands(program);
865
869
  registerHooksCommands(program);
866
870
  registerSetupCommand(program);
871
+ program.commands.find(command => command.name() === 'setup')!
872
+ .option('--terminal', 'Run the terminal onboarding wizard')
873
+ .hook('preAction', async (_command, action) => {
874
+ if (action.name() === 'setup' && action.opts().terminal) {
875
+ const completed = await runOnboardingForCli();
876
+ process.exit(completed ? 0 : (process.exitCode || 2));
877
+ }
878
+ });
867
879
  registerStartCommand(program);
868
880
  registerGroundingSourcesCli(program);
869
881
  registerGraphCommands(program);
@@ -905,6 +917,10 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
905
917
  console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}`);
906
918
  for (const warning of result.warnings) console.warn(`⚠ ${warning.dir}: ${warning.graph} uses recipes it does not ship (${warning.recipes.join(', ')}) — third-party graphs should use their own recipes.yaml or custom nodes`);
907
919
  }
920
+ for (const item of result.skipped) {
921
+ for (const hit of item.hits ?? []) console.error(`${item.dir}/${hit.file}:${hit.line}: ${hit.marker}`);
922
+ }
923
+ if (!result.ok) process.exitCode = 1;
908
924
  } catch (error) {
909
925
  console.error(`market publish: ${error instanceof Error ? error.message : 'failed'}`);
910
926
  process.exitCode = 1;
@@ -1002,7 +1018,7 @@ program.command('self-update')
1002
1018
 
1003
1019
  program
1004
1020
  .name('elanous')
1005
- .description('TUI skill runner — Yazi-style 3-pane + multi-LLM, with remote sync, smart diff, and SQLite logging')
1021
+ .description('말 한 줄로 시작: elanous harness say "…" — 끝까지 간다 · 다 보인다 · 스스로를 만들었다')
1006
1022
  .version(cliVersion())
1007
1023
  // Documentation-only entry — the flag is extracted from argv by
1008
1024
  // `applyConfigDirFlagFromArgv` at module init (above) so Commander
@@ -1028,6 +1044,7 @@ program
1028
1044
  '--test',
1029
1045
  '격리 테스트 인스턴스로 실행 — cwd 의 git 트리(worktree 포함)에서 `<트리>/.elanous-test` 를 루트로 잡고 state·config 두 축을 함께 격리한다. config 사본이 없으면 자동 물질화. `--test=<dir>` 로 루트 직접 지정 가능(값 문법은 `=` 형태 하나 — 모호함 없음). ELANOUS_STATE_DIR/--config-dir 을 손으로 줄 필요가 없다.',
1030
1046
  );
1047
+ program.addHelpText('after', acpServerHelpText());
1031
1048
 
1032
1049
  program
1033
1050
  .command('measure-fabric-arc-ab')
@@ -1076,6 +1093,8 @@ const mcpCmd = program
1076
1093
  .description('MCP (Model Context Protocol) server / client integration');
1077
1094
  registerMcpGatewayCommands(mcpCmd);
1078
1095
 
1096
+ registerRelayCommand(program);
1097
+
1079
1098
  mcpCmd
1080
1099
  .command('serve')
1081
1100
  .description('Run a stdio MCP server exposing the configured mcp.servers as proxy tools (used by `claude mcp add elanous -- elanous mcp serve`)')
@@ -2952,6 +2971,9 @@ const harnessOrchestrateCmd = registerHarnessOrchestrateCapabilityOptions(
2952
2971
 
2953
2972
  const selfCmd = program.command('self').description('Self-awareness memory — 외부 도구(Claude Code/Codex)가 구현/변경 이력을 elanous 기억에 주입·회상');
2954
2973
 
2974
+ // M12 Claude Code 의존도 계기판 — `elanous self dependence [--since] [--until] [--json]`(읽기만).
2975
+ { const { registerDependenceCommand } = await import('./cli/dependence-gauge.js'); registerDependenceCommand(selfCmd); }
2976
+
2955
2977
  function printExtendedOrchestrateHelp(command: Command, positional = '[goals...]'): void {
2956
2978
  const optionLines = command.options
2957
2979
  .filter((option) => option.long !== '--help-all')
@@ -7019,6 +7041,7 @@ missionCmd
7019
7041
 
7020
7042
  registerTasksCommands(program);
7021
7043
  registerLabelsCommands(program);
7044
+ registerMsgCommands(program);
7022
7045
 
7023
7046
  // ── ask alias (script-friendly one-shot query) ──
7024
7047
  program
@@ -7027,7 +7050,28 @@ program
7027
7050
  .option('--reuse', 'Reuse the active CLI session instead of creating a new one')
7028
7051
  .option('--session <id>', 'Continue an explicit session (id or unique prefix). Overrides --reuse and active session.')
7029
7052
  .option('--json', 'Emit a single JSON line {sessionId, provider, model, reply, logPath, budget} instead of streaming text + ui.info trailer. Stable shape for LLM self-spawn.')
7030
- .action(async (parts: string[], opts: { reuse?: boolean; session?: string; json?: boolean }) => {
7053
+ .option('--bare', 'Send only this text to the model: no Elanous preamble, project tree, memory, tools or session')
7054
+ .action(async (parts: string[], opts: { reuse?: boolean; session?: string; json?: boolean; bare?: boolean }) => {
7055
+ if (opts.bare) {
7056
+ try {
7057
+ const { askBare } = await import('./cli/ask-bare.js');
7058
+ const llm = await import('./llm.js');
7059
+ const { getUserConfig } = await import('./user-config.js');
7060
+ const { trimToBudget } = await import('./tokens.js');
7061
+ const result = await askBare({ text: parts.join(' '), deps: {
7062
+ getUserConfig, decideProviderForConfig: llm.decideProviderForConfig, getProviderForConfig: llm.getProviderForConfig,
7063
+ streamLLM: llm.streamLLM, trimToBudget,
7064
+ } });
7065
+ if (opts.json) process.stdout.write(JSON.stringify({ sessionId: null, ...result, logPath: null, budget: null, bare: true }) + '\n');
7066
+ else process.stdout.write(result.reply.endsWith('\n') ? result.reply : `${result.reply}\n`);
7067
+ } catch (error) {
7068
+ const message = error instanceof Error ? error.message : String(error);
7069
+ if (opts.json) process.stdout.write(JSON.stringify({ error: message, bare: true }) + '\n');
7070
+ else console.error(`ask --bare failed: ${message}`);
7071
+ process.exitCode = 1;
7072
+ }
7073
+ return;
7074
+ }
7031
7075
  const cfg = reloadUserConfig();
7032
7076
  await runChatTurnCli({
7033
7077
  cfg,
@@ -7193,9 +7237,13 @@ program
7193
7237
  });
7194
7238
 
7195
7239
  // ── login (provider OAuth) ──
7196
- export function codexLoginSuccessMessage(mirrorResult?: 'written' | 'not-created-missing-cli-fields' | 'write-failed'): string {
7240
+ export function codexLoginSuccessMessage(mirrorResult?: CodexMirrorResult): string {
7197
7241
  const prefix = `Signed in. Tokens at ${authStorePath()}.`;
7198
7242
  if (mirrorResult === 'written') return `${prefix} Mirrored to ~/.codex/auth.json.`;
7243
+ if (mirrorResult === 'skipped-different-account') {
7244
+ return `${prefix} Your Codex CLI login (~/.codex/auth.json) belongs to a different account — left unchanged. To switch it, run \`elanous login openai-codex --replace-codex-cli-login\`.`;
7245
+ }
7246
+ if (mirrorResult === 'skipped-unknown-account') return `${prefix} Codex CLI login left unchanged.`;
7199
7247
  if (mirrorResult === 'not-created-missing-cli-fields') {
7200
7248
  return `${prefix} Codex CLI mirror was not created because this login response lacked its required fields; run \`codex login\` once to initialize ~/.codex/auth.json.`;
7201
7249
  }
@@ -7208,7 +7256,8 @@ const loginCmd = program.command('login').description('Authenticate to an LLM pr
7208
7256
  loginCmd
7209
7257
  .command('openai-codex')
7210
7258
  .description('Sign in to OpenAI Codex via the ChatGPT device-code flow')
7211
- .action(async () => {
7259
+ .option('--replace-codex-cli-login', 'if the Codex CLI (~/.codex/auth.json) is signed in to a different account, replace it with this login')
7260
+ .action(async (opts: { replaceCodexCliLogin?: boolean }) => {
7212
7261
  ui.header('OpenAI Codex — device-code sign-in');
7213
7262
  try {
7214
7263
  const state = await loginWithCodex({
@@ -7233,7 +7282,21 @@ loginCmd
7233
7282
  }
7234
7283
  },
7235
7284
  });
7236
- ui.info(codexLoginSuccessMessage(state.codexMirrorResult));
7285
+ // AUTH1 — another account's Codex CLI login is left alone unless the user says to replace it.
7286
+ let mirrorResult = state.codexMirrorResult;
7287
+ if (mirrorResult === 'skipped-different-account') {
7288
+ let replace = opts.replaceCodexCliLogin === true;
7289
+ if (!replace && process.stdin.isTTY && process.stdout.isTTY) {
7290
+ const { createInterface } = await import('node:readline/promises');
7291
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
7292
+ try {
7293
+ const answer = await rl.question(' Your Codex CLI (~/.codex/auth.json) is signed in to a different account. Replace it with this login? [y/N] ');
7294
+ replace = /^y(es)?$/i.test(answer.trim());
7295
+ } finally { rl.close(); }
7296
+ }
7297
+ if (replace) mirrorResult = saveTokens('openai-codex', state.tokens, { replaceDifferentCodexAccount: true }).codexMirrorResult;
7298
+ }
7299
+ ui.info(codexLoginSuccessMessage(mirrorResult));
7237
7300
  ui.info(`Expires in ~${state.tokens.expiresAt ? Math.round((state.tokens.expiresAt - Date.now()) / 60000) : '?'}min; auto-refresh on next use.`);
7238
7301
  } catch (err: any) {
7239
7302
  ui.error(`login failed: ${err?.message ?? err}`);
@@ -7821,6 +7884,15 @@ sessionCmd
7821
7884
  console.log(res.formatted ?? '(no context)');
7822
7885
  });
7823
7886
 
7887
+ async function runFirstSetupForCli(cfg: ReturnType<typeof getUserConfig>, announceFallback = false): Promise<boolean> {
7888
+ if (cfg.onboarding.webFirst === true) {
7889
+ const { runWebFirstSetup } = await import('./onboarding/web-first.js');
7890
+ if (await runWebFirstSetup({ config: cfg }) === 'link-shown') return false;
7891
+ if (announceFallback) ui.info('No config yet — launching setup wizard first.');
7892
+ }
7893
+ return runOnboardingForCli();
7894
+ }
7895
+
7824
7896
  async function runOnboardingForCli(): Promise<boolean> {
7825
7897
  try {
7826
7898
  await runOnboarding();
@@ -7856,8 +7928,8 @@ program
7856
7928
  if (opts.tools) announceChatToolsCompatibility();
7857
7929
  const cfg = getUserConfig();
7858
7930
  if (needsOnboarding(cfg)) {
7859
- ui.info('No config yet — launching setup wizard first.');
7860
- if (!await runOnboardingForCli()) return;
7931
+ if (cfg.onboarding.webFirst !== true) ui.info('No config yet — launching setup wizard first.');
7932
+ if (!await runFirstSetupForCli(cfg, true)) return;
7861
7933
  }
7862
7934
  const refreshed = reloadUserConfig();
7863
7935
  // ★ 관측갭 수리(2026-07-21·제1원칙·트랙A) — self-implement 자식 goal-loop(`chat --goal-loop`)은 데몬과
@@ -7923,8 +7995,8 @@ program
7923
7995
  }
7924
7996
  const cfg = getUserConfig();
7925
7997
  if (needsOnboarding(cfg)) {
7926
- ui.info('No config yet — launching setup wizard first.');
7927
- if (!await runOnboardingForCli()) return;
7998
+ if (cfg.onboarding.webFirst !== true) ui.info('No config yet — launching setup wizard first.');
7999
+ if (!await runFirstSetupForCli(cfg, true)) return;
7928
8000
  }
7929
8001
  const refreshed = reloadUserConfig();
7930
8002
  const { runRepl } = await import('./repl/index.js');
@@ -10597,7 +10669,7 @@ async function main(): Promise<void> {
10597
10669
  // gates on config completeness — no entry-mode branch.)
10598
10670
  const cfg = getUserConfig();
10599
10671
  if (needsOnboarding(cfg)) {
10600
- if (!await runOnboardingForCli()) return;
10672
+ if (!await runFirstSetupForCli(cfg)) return;
10601
10673
  }
10602
10674
 
10603
10675
  // Active-provider banner so users know WHICH model/auth is about to
@@ -10681,8 +10753,8 @@ async function main(): Promise<void> {
10681
10753
  export function runCli(): void {
10682
10754
  main().catch((err) => {
10683
10755
  closeTui();
10684
- if (err instanceof HarnessCliInputError) {
10685
- console.error(`❌ ${err.message}`);
10756
+ if (isCliUserError(err)) {
10757
+ console.error(formatCliUserError(err));
10686
10758
  process.exit(1);
10687
10759
  }
10688
10760
  throw err;