elanous 0.2.6 → 0.2.8

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 (517) 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-3084c58c1725a40b.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/7032-f4ef698518fba61c.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/7647.2fcd30d894d5ee30.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/7673-2eda1507d38dadb8.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/7990-e12efba616f46e03.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/{91ff6dda-3b33d42d20657429.js → 8046cadf-211ca3c0e00f2b72.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/8142-744998d65d7bb17e.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/8619-4365e03a2862830f.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/920-f7dceb78f1c585ef.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/9651-3cd291a6fa1370d2.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.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-9aa47b0bc6d570ff.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/botlab/page-d4b6df905f185055.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/chat/page-186fc6952f027acc.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/design-check/page-cbbe03c0da7b081a.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.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-6a8158b71d1fe9d7.js +1 -0
  63. package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +1 -0
  64. package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +1 -0
  65. package/apps/pwa/out/_next/static/chunks/app/market/page-c0c0d19d51342d3e.js +1 -0
  66. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2e002ad8faa43291.js +1 -0
  67. package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +1 -0
  68. package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +1 -0
  69. package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +1 -0
  70. package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +1 -0
  71. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +1 -0
  72. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +1 -0
  73. package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +1 -0
  74. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-7464c1987dd76e6d.js +1 -0
  75. package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +1 -0
  76. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.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-085847b088675978.js +1 -0
  79. package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +1 -0
  80. package/apps/pwa/out/_next/static/chunks/app/showroom/page-d8ab4d1489223459.js +1 -0
  81. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +1 -0
  82. package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +1 -0
  83. package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +1 -0
  84. package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +1 -0
  85. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +1 -0
  86. package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +1 -0
  87. package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +1 -0
  88. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.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/5ef72a618a8994f3.css +1 -0
  96. package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_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/graphs/test-diet/recipes.yaml +9 -0
  178. package/graphs/test-diet/test-diet.yaml +22 -0
  179. package/package.json +6 -5
  180. package/persona-presets/_index.yaml +10 -0
  181. package/persona-presets/_schema.json +146 -0
  182. package/persona-presets/dana.yaml +26 -0
  183. package/persona-presets/haru.yaml +27 -0
  184. package/persona-presets/kai.yaml +27 -0
  185. package/persona-presets/leo.yaml +27 -0
  186. package/persona-presets/mira.yaml +30 -0
  187. package/persona-presets/nari.yaml +27 -0
  188. package/persona-presets/sora.yaml +27 -0
  189. package/persona-presets/yuna.yaml +27 -0
  190. package/plugins/card-followup/README.md +13 -4
  191. package/plugins/card-followup/graphs/card-followup.yaml +3 -1
  192. package/plugins/card-followup/graphs/recipes.yaml +5 -1
  193. package/plugins/card-followup/graphs/run-step.ts +122 -18
  194. package/plugins/card-followup/graphs/sales-strategy.ts +173 -0
  195. package/plugins/card-followup/plugin.json +1 -1
  196. package/plugins/doc-draft/README.md +13 -0
  197. package/plugins/doc-draft/examples/brief-sample.json +8 -0
  198. package/plugins/doc-draft/graphs/doc-draft.yaml +14 -0
  199. package/plugins/doc-draft/graphs/recipes.yaml +9 -0
  200. package/plugins/doc-draft/graphs/run-step.ts +113 -0
  201. package/plugins/doc-draft/plugin.json +24 -0
  202. package/plugins/geo-check/README.md +3 -1
  203. package/plugins/geo-check/graphs/run-step.ts +58 -13
  204. package/plugins/geo-check/plugin.json +1 -1
  205. package/plugins/job-coach/.codex-plugin/plugin.json +1 -1
  206. package/plugins/job-coach/README.md +1 -1
  207. package/plugins/job-coach/graphs/run-step.ts +2 -2
  208. package/plugins/job-coach/plugin.json +1 -1
  209. package/plugins/job-coach/plugin.ts +1 -1
  210. package/scripts/affinity/endcard.sh +3 -2
  211. package/scripts/affinity/reframe.sh +3 -1
  212. package/scripts/ci-public-export-leak-gate.ts +48 -0
  213. package/scripts/codex-quota-alert.ts +122 -258
  214. package/scripts/coord-tracks.json +4 -4
  215. package/scripts/field-feed/deliver-node.ts +57 -0
  216. package/scripts/field-feed/feed-node.ts +67 -0
  217. package/scripts/field-feed/lib.ts +164 -0
  218. package/scripts/field-feed/reel-node.ts +24 -0
  219. package/scripts/field-feed/start.ts +62 -0
  220. package/scripts/fixtures/vflow-sample.ndjson +6 -0
  221. package/scripts/install.sh +109 -21
  222. package/scripts/landing-heal/clean-checkout.ts +114 -0
  223. package/scripts/landing-heal/run-mission.ts +38 -3
  224. package/scripts/lib/pwa-scenarios/scenarios.ts +2 -1
  225. package/scripts/persona-presets/lint.ts +79 -0
  226. package/scripts/public-export.ts +8 -4
  227. package/scripts/release-loop/baseline-from-logs.ts +92 -0
  228. package/scripts/release-loop/checklist-gate-node.ts +2 -2
  229. package/scripts/release-loop/env-known-failures.ts +31 -0
  230. package/scripts/release-loop/gate-node.ts +108 -22
  231. package/scripts/release-loop/known-issues-node.ts +159 -0
  232. package/scripts/release-loop/npm-publish-node.ts +124 -0
  233. package/scripts/release-loop/ops-upgrade-node.ts +95 -0
  234. package/scripts/release-loop/publish-node.ts +23 -1
  235. package/scripts/release-loop/release-readiness.ts +58 -0
  236. package/scripts/release-loop/shard-plan.ts +17 -0
  237. package/scripts/release-loop/unattended-release.ts +93 -0
  238. package/scripts/storyboard/storyboard.ts +167 -0
  239. package/scripts/test-diet/lib.ts +88 -0
  240. package/scripts/test-diet/node.ts +102 -0
  241. package/scripts/ux/measure-first-text.ts +102 -0
  242. package/scripts/vflow-collect.ts +20 -7
  243. package/scripts/vflow-search.ts +73 -0
  244. package/scripts/video-free-line-test-walker.ts +36 -0
  245. package/scripts/video-pipeline.ts +2 -2
  246. package/skills/explainer-video/SKILL.md +12 -9
  247. package/skills/explainer-video/engine/build.mjs +1 -1
  248. package/skills/explainer-video/engine/reel.mjs +138 -31
  249. package/skills/explainer-video/engine/reel.sh +2 -1
  250. package/skills/explainer-video/examples/elanous-0.2.5/script.json +1 -1
  251. package/skills/grill-me/SKILL.md +1 -1
  252. package/skills/omni-digest/docs/diagrams/noncode-terminal-promotion-path-2026-08-13.mmd +1 -1
  253. package/src/a2a/server.ts +2 -0
  254. package/src/acp/auth-methods.ts +30 -0
  255. package/src/acp/elanous-aware-peers.ts +30 -0
  256. package/src/acp/server.ts +96 -20
  257. package/src/agent/monad-agent-turn.ts +25 -6
  258. package/src/agent/shared-app-tools.ts +5 -3
  259. package/src/agent-substrate/review-intent.ts +6 -2
  260. package/src/agent-substrate/shard-boundary-intent.ts +70 -0
  261. package/src/auth/github-app-token.ts +4 -1
  262. package/src/autopilot/track-agent-shadow.ts +151 -0
  263. package/src/autopilot/track-agent.ts +23 -3
  264. package/src/boot/acp-server-help.ts +16 -0
  265. package/src/boot/daemon-prompt-turn.ts +31 -10
  266. package/src/boot/daemon-tools/index.ts +7 -1
  267. package/src/budget/codex-credit-pace.ts +84 -0
  268. package/src/budget/codex-credit-plan.ts +154 -0
  269. package/src/budget/codex-reset-credit-state.ts +2 -0
  270. package/src/channel-import/detect.ts +191 -0
  271. package/src/channel-import/run.ts +111 -0
  272. package/src/chat/chat-cli.ts +4 -1
  273. package/src/chat/short-question.ts +40 -0
  274. package/src/cli/agent-cli.ts +24 -15
  275. package/src/cli/ask-bare.ts +42 -0
  276. package/src/cli/cli-user-error.ts +25 -0
  277. package/src/cli/codex-credit-plan-view.ts +25 -0
  278. package/src/cli/config-test-sync.ts +4 -0
  279. package/src/cli/decisions-cli.ts +13 -3
  280. package/src/cli/dependence-gauge.ts +237 -0
  281. package/src/cli/doctor-bundle.ts +128 -0
  282. package/src/cli/doctor-cli.ts +41 -1
  283. package/src/cli/doctor-local-llm.ts +95 -0
  284. package/src/cli/doctor-modes.ts +48 -0
  285. package/src/cli/import-cli.ts +60 -0
  286. package/src/cli/intake-cli.ts +12 -2
  287. package/src/cli/intake-source-cli.ts +58 -0
  288. package/src/cli/machine-cli.ts +23 -0
  289. package/src/cli/mcp-login.ts +13 -4
  290. package/src/cli/msg-cli.ts +137 -0
  291. package/src/cli/persona-cli.ts +116 -0
  292. package/src/cli/plugin-cli.ts +25 -0
  293. package/src/cli/pr-cli.ts +36 -0
  294. package/src/cli/provider-cli.ts +5 -0
  295. package/src/cli/release-cli.ts +50 -8
  296. package/src/cli/release-run-if-ready.ts +48 -0
  297. package/src/cli/setup-cli.ts +27 -6
  298. package/src/cli/setup-recommend.ts +8 -0
  299. package/src/cli/skills-cli.ts +173 -0
  300. package/src/cli/start-cli.ts +13 -4
  301. package/src/cli/usage-cli.ts +11 -1
  302. package/src/codex/setup.ts +37 -16
  303. package/src/dashboard/index.ts +18 -2
  304. package/src/dashboard/slash-runtime/dashboard-handlers.ts +2 -2
  305. package/src/dashboard/slash-runtime/help-from-registry.ts +4 -2
  306. package/src/decisions/decision-cards.ts +291 -0
  307. package/src/decisions/decision-ledger.ts +5 -2
  308. package/src/decisions/discord-decision-cards.ts +133 -0
  309. package/src/decisions/telegram-decision-cards.ts +100 -0
  310. package/src/directives/version-at.ts +77 -0
  311. package/src/discord.ts +73 -6
  312. package/src/domains/core-tools.ts +37 -14
  313. package/src/domains/outbound-alert.ts +94 -12
  314. package/src/domains/repeated-failure.ts +1 -1
  315. package/src/error-report/report.ts +200 -0
  316. package/src/exec-requests/planner.ts +146 -0
  317. package/src/exec-requests/runner.ts +378 -0
  318. package/src/exec-requests/store.ts +78 -0
  319. package/src/field/field-feed-auto.ts +48 -0
  320. package/src/field/field-media.ts +8 -3
  321. package/src/field/field-reel.ts +203 -0
  322. package/src/grok/credential.ts +7 -5
  323. package/src/harness/aside-run-status.ts +33 -0
  324. package/src/harness/browser-verify.ts +48 -17
  325. package/src/harness/harness-cli-command.ts +25 -3
  326. package/src/harness/image-load-state.ts +21 -0
  327. package/src/harness/process-garbage.ts +45 -0
  328. package/src/hooks/error-report.ts +210 -0
  329. package/src/hooks/receiver.ts +41 -1
  330. package/src/import/apply.ts +128 -0
  331. package/src/import/plan.ts +197 -0
  332. package/src/index.ts +168 -21
  333. package/src/input-core/text-entry.ts +13 -5
  334. package/src/instance/leader.ts +13 -1
  335. package/src/intake-plane/collect-github.ts +5 -3
  336. package/src/intake-plane/collect-telegram-saved.ts +15 -3
  337. package/src/intake-plane/discord-seat-work.ts +56 -0
  338. package/src/intake-plane/intake-sources.ts +202 -0
  339. package/src/intake-plane/items.ts +28 -12
  340. package/src/intake-plane/seat-answer.ts +96 -0
  341. package/src/intake-plane/seat-doc-route.ts +221 -0
  342. package/src/intake-plane/submit-intake-work.ts +9 -1
  343. package/src/intake-plane/telegram-seat-work.ts +72 -0
  344. package/src/llm/model-defaults.ts +24 -1
  345. package/src/llm.ts +5 -4
  346. package/src/machines/machine-ledger.ts +127 -0
  347. package/src/market/pack-internal-refs.ts +28 -0
  348. package/src/market/publish.ts +31 -4
  349. package/src/maturity/feature-maturity.ts +65 -0
  350. package/src/model-tier/turn-tier.ts +24 -0
  351. package/src/msg/msg-store.ts +149 -0
  352. package/src/native-tool-catalog.ts +2 -1
  353. package/src/nexus/api/chat-fast-path-config.ts +28 -0
  354. package/src/nexus/api/consult-requests.ts +128 -0
  355. package/src/nexus/api/discord-trigger-bot.ts +3 -0
  356. package/src/nexus/api/error-reports.ts +108 -0
  357. package/src/nexus/api/exec-requests.ts +51 -0
  358. package/src/nexus/api/field-uploads.ts +40 -3
  359. package/src/nexus/api/graph-approvals-feed.ts +140 -0
  360. package/src/nexus/api/graph-approvals.ts +71 -4
  361. package/src/nexus/api/http-server.ts +82 -8
  362. package/src/nexus/api/outbound-report.ts +7 -0
  363. package/src/nexus/api/running-runs-background.ts +68 -0
  364. package/src/nexus/api/running-runs-child.ts +6 -0
  365. package/src/nexus/api/seat-requests.ts +254 -0
  366. package/src/nexus/api/share-captures.ts +91 -0
  367. package/src/nexus/api/skill-problems.ts +56 -0
  368. package/src/nexus/api/terminals.ts +24 -8
  369. package/src/nexus/api/trace-run-scan.ts +37 -0
  370. package/src/nexus/api/trace.ts +19 -3
  371. package/src/nexus/index.ts +28 -1
  372. package/src/nexus/setup-status.ts +11 -7
  373. package/src/notify/llm-budget-alert.ts +16 -10
  374. package/src/oauth/browser-availability.ts +14 -0
  375. package/src/oauth/codex-account-rotation.ts +26 -4
  376. package/src/oauth/codex-account-store.ts +20 -2
  377. package/src/oauth/codex-browser-login.ts +173 -0
  378. package/src/oauth/codex-quota-policy.ts +16 -0
  379. package/src/oauth/codex.ts +4 -1
  380. package/src/oauth/fallback-chain.ts +5 -5
  381. package/src/oauth/store.ts +42 -5
  382. package/src/onboarding/continue-here.ts +34 -0
  383. package/src/onboarding/entry-hints.ts +8 -0
  384. package/src/onboarding/full-screen-io.ts +35 -3
  385. package/src/onboarding/help/skills.md +1 -1
  386. package/src/onboarding/non-interactive.ts +1 -0
  387. package/src/onboarding/subscription-hint.ts +27 -0
  388. package/src/onboarding/web-first.ts +90 -0
  389. package/src/onboarding.ts +287 -26
  390. package/src/persona/presets.ts +105 -0
  391. package/src/plugins/maker/node-install.ts +92 -0
  392. package/src/plugins/maker/node-maker.ts +139 -0
  393. package/src/plugins/maker/plugin-maker.ts +19 -7
  394. package/src/policy/llm-policy.ts +268 -0
  395. package/src/prompt-library/session-guidance.ts +7 -1
  396. package/src/prompt-library/universal-preamble.ts +2 -2
  397. package/src/relay/relay-cli.ts +20 -0
  398. package/src/relay/relay-server.ts +220 -0
  399. package/src/release-loop/checklist.ts +37 -13
  400. package/src/self-dev/decompose.ts +1 -0
  401. package/src/self-dev/dev-pipeline.ts +6 -3
  402. package/src/self-dev/orchestrate.ts +18 -11
  403. package/src/self-dev/run-record-mirror.ts +44 -0
  404. package/src/self-dev/run-store.ts +12 -3
  405. package/src/self-implement/harness-policy-gates.ts +4 -1
  406. package/src/self-implement/report-origin.ts +12 -1
  407. package/src/session/chat.ts +17 -5
  408. package/src/shell-env-bootstrap.ts +44 -7
  409. package/src/skills/connect-claude-plugin.ts +142 -0
  410. package/src/skills/connect.ts +70 -0
  411. package/src/skills/index.ts +29 -1
  412. package/src/skills/repair.ts +124 -0
  413. package/src/skills/runner.ts +11 -2
  414. package/src/skills/shared-agent-skill-roots.ts +25 -0
  415. package/src/skills/trigger-extract.ts +1 -1
  416. package/src/steward/failure-streak.ts +78 -0
  417. package/src/steward/steward-cards.ts +24 -0
  418. package/src/steward/triage-budget.ts +127 -0
  419. package/src/steward/triage-plan.ts +75 -0
  420. package/src/steward/triage.ts +244 -42
  421. package/src/task-cards/card-store.ts +1 -1
  422. package/src/task-orchestrator/surfaces/pod-command-job.ts +16 -8
  423. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  424. package/src/task-orchestrator/surfaces/pod-ledger-events.ts +68 -0
  425. package/src/task-orchestrator/surfaces/self-implement-pod.ts +78 -4
  426. package/src/telegram-commands.ts +13 -0
  427. package/src/telegram.ts +72 -2
  428. package/src/tool-runtime/elanous-skills-list-runtime.ts +3 -3
  429. package/src/tool-runtime/self-cognition-runtimes.ts +160 -2
  430. package/src/user-config.ts +189 -15
  431. package/src/version/packed-revision.json +1 -1
  432. package/src/video-pipeline/recipes/character.ts +42 -5
  433. package/src/video-pipeline/recipes/ffmpeg.ts +1 -1
  434. package/src/video-pipeline/recipes/free-line.ts +16 -3
  435. package/src/video-pipeline/recipes/remote.ts +7 -1
  436. package/src/video-pipeline/vflow-reference.ts +109 -0
  437. package/apps/pwa/out/_next/static/chunks/1169-696263ac7af8c4d4.js +0 -1
  438. package/apps/pwa/out/_next/static/chunks/1883.c91a2648af4842fd.js +0 -1
  439. package/apps/pwa/out/_next/static/chunks/1947-9d64872eeb9cfdec.js +0 -1
  440. package/apps/pwa/out/_next/static/chunks/2808-dc06482994b40f70.js +0 -1
  441. package/apps/pwa/out/_next/static/chunks/3165-95515ca5f299a120.js +0 -1
  442. package/apps/pwa/out/_next/static/chunks/3190-e9282753e7ebde85.js +0 -1
  443. package/apps/pwa/out/_next/static/chunks/4259-99614149577e77cb.js +0 -1
  444. package/apps/pwa/out/_next/static/chunks/4283-329eabeaf233e6cd.js +0 -1
  445. package/apps/pwa/out/_next/static/chunks/4328-cb5058b1acc2de5e.js +0 -25
  446. package/apps/pwa/out/_next/static/chunks/4801-ecee6ef62b349b18.js +0 -1
  447. package/apps/pwa/out/_next/static/chunks/5110-8058791dd25a2e14.js +0 -1
  448. package/apps/pwa/out/_next/static/chunks/5330-c41e9670f594c38f.js +0 -1
  449. package/apps/pwa/out/_next/static/chunks/5423-e95fd125d1794349.js +0 -1
  450. package/apps/pwa/out/_next/static/chunks/5478-b5eae07dc79ea619.js +0 -1
  451. package/apps/pwa/out/_next/static/chunks/5787-96ee6642b9a661e5.js +0 -1
  452. package/apps/pwa/out/_next/static/chunks/6448-79d72368589f98bc.js +0 -1
  453. package/apps/pwa/out/_next/static/chunks/6522-9866c002b5531836.js +0 -1
  454. package/apps/pwa/out/_next/static/chunks/6729-b4a558b7aa891e0c.js +0 -1
  455. package/apps/pwa/out/_next/static/chunks/6969-cb03f9410b7d2e03.js +0 -1
  456. package/apps/pwa/out/_next/static/chunks/7007-3b9861091bb8dd21.js +0 -1
  457. package/apps/pwa/out/_next/static/chunks/7930-8dc7e20fc6fc19ca.js +0 -1
  458. package/apps/pwa/out/_next/static/chunks/8071-187edbc22a86741f.js +0 -1
  459. package/apps/pwa/out/_next/static/chunks/8198-d4101ad70cb9a579.js +0 -1
  460. package/apps/pwa/out/_next/static/chunks/8250.4a2c5553a07cdf47.js +0 -1
  461. package/apps/pwa/out/_next/static/chunks/8293-123b91e7b0330dc0.js +0 -1
  462. package/apps/pwa/out/_next/static/chunks/8296-d6d5d87dfa911d41.js +0 -1
  463. package/apps/pwa/out/_next/static/chunks/8305-603963c33ee8eec3.js +0 -1
  464. package/apps/pwa/out/_next/static/chunks/8612-4a0066f20951b8fd.js +0 -1
  465. package/apps/pwa/out/_next/static/chunks/9181-cf4e1b61c4783f92.js +0 -1
  466. package/apps/pwa/out/_next/static/chunks/9262-26c2ecbd5a67e4a2.js +0 -1
  467. package/apps/pwa/out/_next/static/chunks/9328-45abdebce4fbecb7.js +0 -1
  468. package/apps/pwa/out/_next/static/chunks/9355-dbc67205adf8745f.js +0 -1
  469. package/apps/pwa/out/_next/static/chunks/9763.255b90dd69875689.js +0 -1
  470. package/apps/pwa/out/_next/static/chunks/9933-66d31ec25c0231af.js +0 -1
  471. package/apps/pwa/out/_next/static/chunks/app/_not-found/page-bd06fb55f4da0381.js +0 -1
  472. package/apps/pwa/out/_next/static/chunks/app/approvals/page-4e586142da455a25.js +0 -1
  473. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-889ad5cddcf45b0d.js +0 -1
  474. package/apps/pwa/out/_next/static/chunks/app/board/page-47ba401d56572c27.js +0 -1
  475. package/apps/pwa/out/_next/static/chunks/app/botlab/page-0b54f9c83458e038.js +0 -1
  476. package/apps/pwa/out/_next/static/chunks/app/bots/page-4fcc296ef6c8ed90.js +0 -1
  477. package/apps/pwa/out/_next/static/chunks/app/chat/page-fe9fda494a87f4e5.js +0 -1
  478. package/apps/pwa/out/_next/static/chunks/app/control/page-f9642e25ce129590.js +0 -1
  479. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e1063e8ba5f3e2e7.js +0 -1
  480. package/apps/pwa/out/_next/static/chunks/app/editor/page-9d0df30027d8aa9d.js +0 -1
  481. package/apps/pwa/out/_next/static/chunks/app/healthz/route-bd06fb55f4da0381.js +0 -1
  482. package/apps/pwa/out/_next/static/chunks/app/intake/page-d97adf2c9455b8d0.js +0 -1
  483. package/apps/pwa/out/_next/static/chunks/app/layout-236b80d202110362.js +0 -1
  484. package/apps/pwa/out/_next/static/chunks/app/live/page-21a30f35f1a276ad.js +0 -1
  485. package/apps/pwa/out/_next/static/chunks/app/market/page-b1a5be357c4fa25d.js +0 -1
  486. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-1705a60303f7f573.js +0 -1
  487. package/apps/pwa/out/_next/static/chunks/app/morning/page-76b337c11ad0e237.js +0 -1
  488. package/apps/pwa/out/_next/static/chunks/app/not-found-7c0f85fbb56f64c6.js +0 -1
  489. package/apps/pwa/out/_next/static/chunks/app/observatory/page-65ca16b20617b44d.js +0 -1
  490. package/apps/pwa/out/_next/static/chunks/app/page-e7f9b8fc29411c1d.js +0 -1
  491. package/apps/pwa/out/_next/static/chunks/app/reflection/page-92ea6213d1aeec04.js +0 -1
  492. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-a913a005aacb5476.js +0 -1
  493. package/apps/pwa/out/_next/static/chunks/app/sessions/page-2d78804a40dfdef6.js +0 -1
  494. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-08a99148e780ac00.js +0 -1
  495. package/apps/pwa/out/_next/static/chunks/app/settings/page-deb674e7ed36f815.js +0 -1
  496. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-49f8de0534d192d7.js +0 -1
  497. package/apps/pwa/out/_next/static/chunks/app/setup/layout-bd06fb55f4da0381.js +0 -1
  498. package/apps/pwa/out/_next/static/chunks/app/setup/page-ba162a28d2176e18.js +0 -1
  499. package/apps/pwa/out/_next/static/chunks/app/share/page-1ff1a37b550ab41a.js +0 -1
  500. package/apps/pwa/out/_next/static/chunks/app/showroom/page-ccaa698f56c0fbdb.js +0 -1
  501. package/apps/pwa/out/_next/static/chunks/app/tasks/page-53b944082de1d7db.js +0 -1
  502. package/apps/pwa/out/_next/static/chunks/app/term/page-9b70adf1552789bf.js +0 -1
  503. package/apps/pwa/out/_next/static/chunks/app/trace/page-30d482aa63241fe8.js +0 -1
  504. package/apps/pwa/out/_next/static/chunks/app/vault/page-aecf786f54651d8a.js +0 -1
  505. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6ab9b62c7b6e08f2.js +0 -1
  506. package/apps/pwa/out/_next/static/chunks/app/workflows/page-08949735455e0c9a.js +0 -1
  507. package/apps/pwa/out/_next/static/chunks/app/workspace/page-a1479005234a46c2.js +0 -1
  508. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-c3890e90bd232732.js +0 -1
  509. package/apps/pwa/out/_next/static/chunks/beb5cf10-9adcc10b83930090.js +0 -1
  510. package/apps/pwa/out/_next/static/chunks/framework-d68d41c88e05c801.js +0 -1
  511. package/apps/pwa/out/_next/static/chunks/main-app-11c63971b7a9593b.js +0 -1
  512. package/apps/pwa/out/_next/static/chunks/main-d09452353bd0d9cc.js +0 -1
  513. package/apps/pwa/out/_next/static/chunks/pages/_app-184d727cebeca767.js +0 -1
  514. package/apps/pwa/out/_next/static/chunks/pages/_error-21bbbbac99b720b1.js +0 -1
  515. package/apps/pwa/out/_next/static/css/eaa74e8bb2f62269.css +0 -1
  516. package/apps/pwa/out/_next/static/qMI0jnlZaBjjm4aIDYNGM/_buildManifest.js +0 -1
  517. /package/apps/pwa/out/_next/static/{qMI0jnlZaBjjm4aIDYNGM → lywc8m_HDzH9MKSDQhyEj}/_ssgManifest.js +0 -0
@@ -1,7 +1,9 @@
1
1
  import { createRequire } from 'node:module';
2
+ import { isMainThread, parentPort, Worker } from 'node:worker_threads';
3
+ import { debug } from '../debug/log.js';
2
4
  import type { LLMToolSpec } from '../llm.js';
3
5
  import type { NativeToolCatalogEntry } from '../native-tool-catalog.js';
4
- import type { ToolRunResult, ToolRuntime } from './types.js';
6
+ import type { ToolRunResult, ToolRuntime, ToolRuntimeContext } from './types.js';
5
7
 
6
8
  /** Canonical names for the read-only self-cognition surface. */
7
9
  export const SELF_COGNITION_TOOL_NAMES = [
@@ -12,6 +14,7 @@ export const SELF_COGNITION_TOOL_NAMES = [
12
14
  ] as const;
13
15
 
14
16
  type SelfCognitionToolName = (typeof SELF_COGNITION_TOOL_NAMES)[number];
17
+ type RecallToolName = 'memory_recall' | 'self_recall';
15
18
  type CoreToolsModule = typeof import('../domains/core-tools.js');
16
19
 
17
20
  const require = createRequire(import.meta.url);
@@ -32,12 +35,167 @@ function toToolRunResult(result: unknown): ToolRunResult {
32
35
  : { output: String(result) };
33
36
  }
34
37
 
38
+ const PROGRESS_MS = 10_000;
39
+ const DEFAULT_BUDGET_MS = 60_000;
40
+ const MAX_TIMER_MS = 2_147_483_647;
41
+
42
+ function hitCount(result: ToolRunResult): number {
43
+ const data: Record<string, unknown> = result;
44
+ return Array.isArray(data.hits) ? data.hits.length
45
+ : Array.isArray(data.events) ? data.events.length
46
+ : data.restored ? 1 : 0;
47
+ }
48
+
49
+ function partialResult(name: RecallToolName, result: ToolRunResult | undefined, budgetMs: number): ToolRunResult {
50
+ const fallback = name === 'memory_recall' ? { hits: [], count: 0 } : { events: [], count: 0, docs: null };
51
+ const snapshot: Record<string, unknown> = result ?? fallback;
52
+ const budgetSeconds = budgetMs / 1000;
53
+ return {
54
+ ...snapshot,
55
+ partial: true,
56
+ note: `${typeof snapshot.note === 'string' ? `${snapshot.note}\n` : ''}(찾던 중 ${budgetSeconds}초에서 멈췄습니다 — 더 찾을까요?)`,
57
+ };
58
+ }
59
+
60
+ /** A deadline returns the latest completed search stage; late results cannot replace it. */
61
+ export async function runBudgetedRecall(
62
+ name: RecallToolName,
63
+ args: Record<string, unknown>,
64
+ ctx: ToolRuntimeContext,
65
+ search: (onPartial: (result: ToolRunResult) => void) => Promise<ToolRunResult>,
66
+ stop: () => void = () => {},
67
+ progressAfterMs = PROGRESS_MS,
68
+ ): Promise<ToolRunResult> {
69
+ const started = Date.now();
70
+ const budgetMs = typeof args.timeBudgetMs === 'number' && Number.isFinite(args.timeBudgetMs) && args.timeBudgetMs > 0
71
+ ? Math.min(args.timeBudgetMs, MAX_TIMER_MS) : DEFAULT_BUDGET_MS;
72
+ let latest: ToolRunResult | undefined;
73
+ let settled = false;
74
+ const query = typeof args.query === 'string' && args.query.trim()
75
+ ? args.query.trim().replace(/\s+/g, ' ').slice(0, 80)
76
+ : name === 'self_recall' ? '자기 구현·자율행동 이력' : '최근 발송·대화';
77
+ const log = (event: 'progress' | 'budget-hit' | 'done', result?: ToolRunResult) => {
78
+ debug.log('tool.memory-recall', event, { elapsedMs: Date.now() - started, hits: hitCount(result ?? latest ?? {}) });
79
+ };
80
+ let progressTimer: ReturnType<typeof setTimeout> | undefined;
81
+ let budgetTimer: ReturnType<typeof setTimeout> | undefined;
82
+ return new Promise<ToolRunResult>((resolve, reject) => {
83
+ const finish = (result: ToolRunResult, budgetHit: boolean) => {
84
+ if (settled) return;
85
+ settled = true;
86
+ clearTimeout(progressTimer);
87
+ clearTimeout(budgetTimer);
88
+ log(budgetHit ? 'budget-hit' : 'done', result);
89
+ if (budgetHit) stop();
90
+ resolve(result);
91
+ };
92
+ progressTimer = setTimeout(() => {
93
+ if (settled) return;
94
+ const line = `기억을 찾는 중 — ${query}`;
95
+ ctx.emitFeedback?.({
96
+ envelopeVersion: 1,
97
+ sessionId: ctx.sessionId ?? '',
98
+ blockId: `${ctx.sessionId ?? 'recall'}:tool:${ctx.toolCallId ?? name}:progress`,
99
+ parentToolCallId: ctx.toolCallId,
100
+ kind: 'tool.progress', phase: 'delta', emittedAt: Date.now(), seq: 1,
101
+ payload: { stream: 'generic', lines: [line] }, asciiFallback: [line],
102
+ });
103
+ log('progress');
104
+ }, progressAfterMs);
105
+ budgetTimer = setTimeout(() => finish(partialResult(name, latest, budgetMs), true), budgetMs);
106
+ try {
107
+ void search(result => { if (!settled) latest = result; }).then(
108
+ result => finish(result, false),
109
+ error => {
110
+ if (settled) return;
111
+ settled = true;
112
+ clearTimeout(progressTimer);
113
+ clearTimeout(budgetTimer);
114
+ stop();
115
+ reject(error);
116
+ },
117
+ );
118
+ } catch (error) {
119
+ settled = true;
120
+ clearTimeout(progressTimer);
121
+ clearTimeout(budgetTimer);
122
+ stop();
123
+ reject(error);
124
+ }
125
+ });
126
+ }
127
+
128
+ // Separate thread: synchronous SQLite and S3 restore must not block progress/deadline timers.
129
+ if (!isMainThread && parentPort) {
130
+ parentPort.on('message', async ({ name, args }: { name: RecallToolName; args: Record<string, unknown> }) => {
131
+ try {
132
+ if (name === 'self_recall') {
133
+ const { existsSync } = await import('node:fs');
134
+ const { surfaceEventsDbPath, openSurfaceEventsDb, recallEvents } = await import('../domains/surface-events.js');
135
+ if (existsSync(surfaceEventsDbPath())) {
136
+ const db = openSurfaceEventsDb();
137
+ try {
138
+ const query = typeof args.query === 'string' ? args.query : '';
139
+ const hits = recallEvents(db, {
140
+ domain: 'elanous',
141
+ sinceHours: typeof args.sinceHours === 'number' ? args.sinceHours : 720,
142
+ limit: typeof args.limit === 'number' ? args.limit : 8,
143
+ ...(query ? { query } : {}),
144
+ });
145
+ const events = hits.map(h => ({
146
+ when: h.ts, tool: h.surface, kind: h.kind ?? 'impl',
147
+ summary: (h.summary ?? h.text).slice(0, 300), score: Math.round(h.score * 1000) / 1000,
148
+ }));
149
+ parentPort?.postMessage({ stage: 'partial', result: { events, count: events.length, docs: null } });
150
+ } catch { /* self_recall's episode stage is fail-soft */ } finally { db.close(); }
151
+ }
152
+ }
153
+ const result = await coreTools().dispatchRecallUnbudgeted(name, args, partial => {
154
+ parentPort?.postMessage({ stage: 'partial', result: partial });
155
+ });
156
+ parentPort?.postMessage({ stage: 'done', result });
157
+ } catch (error) {
158
+ parentPort?.postMessage({ stage: 'error', error: error instanceof Error ? error.message : String(error) });
159
+ }
160
+ });
161
+ }
162
+
163
+ export type RecallSearch = (name: RecallToolName, args: Record<string, unknown>, onPartial: (result: ToolRunResult) => void) => Promise<ToolRunResult>;
164
+
165
+ export function runRecall(name: RecallToolName, args: Record<string, unknown>, ctx: ToolRuntimeContext, injectedSearch?: RecallSearch, onPartial?: (result: ToolRunResult) => void): Promise<ToolRunResult> {
166
+ const worker = injectedSearch ? undefined : new Worker(new URL(import.meta.url));
167
+ let active = true;
168
+ const search = (capture: (result: ToolRunResult) => void) => {
169
+ const report = (result: ToolRunResult) => {
170
+ if (!active) return;
171
+ capture(result);
172
+ onPartial?.(result);
173
+ };
174
+ return injectedSearch
175
+ ? injectedSearch(name, args, report)
176
+ : new Promise<ToolRunResult>((resolve, reject) => {
177
+ worker!.on('message', (message: { stage: string; result?: unknown; error?: string }) => {
178
+ if (message.stage === 'partial') report(toToolRunResult(message.result));
179
+ else if (message.stage === 'done') resolve(toToolRunResult(message.result));
180
+ else if (message.stage === 'error') reject(new Error(message.error));
181
+ });
182
+ worker!.on('error', reject);
183
+ worker!.on('exit', code => { if (code !== 0) reject(new Error(`Recall worker exited: ${code}`)); });
184
+ worker!.postMessage({ name, args });
185
+ });
186
+ };
187
+ return runBudgetedRecall(name, args, ctx, search, () => { active = false; if (worker) void worker.terminate(); })
188
+ .finally(() => { active = false; if (worker) void worker.terminate(); });
189
+ }
190
+
35
191
  export const SELF_COGNITION_RUNTIMES: readonly ToolRuntime[] = SELF_COGNITION_TOOL_NAMES.map(name => ({
36
192
  id: name,
37
193
  get spec() {
38
194
  return coreSpec(name);
39
195
  },
40
- run: async (args) => toToolRunResult(await coreTools().buildCoreTools().dispatch(name, args)),
196
+ run: async (args, ctx) => name === 'memory_recall' || name === 'self_recall'
197
+ ? runRecall(name, args, ctx)
198
+ : toToolRunResult(await coreTools().buildCoreTools().dispatch(name, args)),
41
199
  }));
42
200
 
43
201
  /** MCP catalog metadata derived from the same name ledger as the runtimes. */
@@ -47,6 +47,7 @@ import {
47
47
  GLM_MODEL,
48
48
  } from './config.js';
49
49
  import type { SkillTier } from './skills/runner.js';
50
+ import { sharedAgentSkillRoots } from './skills/shared-agent-skill-roots.js';
50
51
  import {
51
52
  isModelTier,
52
53
  isModelTierPersona,
@@ -436,9 +437,28 @@ const LLM_DEFAULTS: LLMConfig = { provider: 'auto' };
436
437
  export type SkillSetName =
437
438
  | 'claudecode' | 'opencode' | 'codex' | 'hermes' | 'openclaw' | 'custom';
438
439
 
440
+ export type SkillSourceKind = 'preset' | 'shared' | 'connected' | 'package';
441
+
442
+ /** One skill root elanous reads. `mode` is always `ref` today (the folder is read where it is). */
443
+ export interface SkillSource {
444
+ id: string;
445
+ path: string;
446
+ kind: SkillSourceKind;
447
+ enabled: boolean;
448
+ }
449
+
439
450
  export interface SkillsConfig {
440
451
  activeSet: SkillSetName;
441
452
  dirs: string[];
453
+ /** Include the existing shared ~/.agents/skills directory. Default true; false opts out. */
454
+ includeSharedAgentSkills?: boolean;
455
+ /**
456
+ * EN5 — the one source list. On disk it holds only what `elanous connect` registered (kind `connected`, read in
457
+ * place, never copied); the preset (`activeSet`/`dirs`), the shared `~/.agents/skills` root and Claude package
458
+ * roots are derived at read time by `resolveSkillSources()`. The dev-only `connected: string[]` key (EN9, never
459
+ * released) is read and folded in here; the next save writes `sources` instead.
460
+ */
461
+ sources?: SkillSource[];
442
462
  /** Explicit skill-name allowlist. When set and non-empty, the skill
443
463
  * index filters to this list — all other skills are hidden from
444
464
  * routing, autocompletion, and `/run-skill`. Use when a project
@@ -505,6 +525,8 @@ export interface DefaultSkillDirsOptions {
505
525
  pluginsRoot?: string;
506
526
  /** Override the installed package root. Tests pass a temp root; production omits this. */
507
527
  bundledSkillsRoot?: string;
528
+ /** Override the shared-agent home for isolated discovery tests. */
529
+ sharedAgentHome?: string;
508
530
  }
509
531
 
510
532
  /** Resolve the `skills/` directory shipped with this installed package. */
@@ -512,25 +534,84 @@ export function bundledSkillsDir(packageRoot: string = dirname(import.meta.dir))
512
534
  return join(packageRoot, 'skills');
513
535
  }
514
536
 
515
- export function defaultSkillDirs(
516
- cfg?: UserConfig,
517
- opts?: DefaultSkillDirsOptions,
518
- ): string[] {
537
+ function packageOptIn(cfg?: UserConfig): boolean {
538
+ try { return (cfg ?? getUserConfig()).skills.includeClaudePackageSkills === true; } catch { return false; }
539
+ }
540
+
541
+ /** Stable id for a connected root: the agent it belongs to, else the folder (plugin) name. */
542
+ export function skillSourceId(path: string): string {
543
+ if (/[/\\]\.codex[/\\]skills\/?$/.test(path)) return 'codex';
544
+ if (/[/\\]\.claude[/\\]skills\/?$/.test(path)) return 'claude';
545
+ const parts = path.split(/[/\\]/).filter(Boolean);
546
+ const last = parts[parts.length - 1] === 'skills' ? parts[parts.length - 2] : parts[parts.length - 1];
547
+ return `plugin:${last ?? 'skills'}`;
548
+ }
549
+
550
+ /** Parse persisted `skills.sources` ⊕ the dev-only `skills.connected` key into connected sources (deduped by path). */
551
+ export function parsePersistedSkillSources(rawSources: unknown, legacyConnected: unknown): SkillSource[] | undefined {
552
+ const out: SkillSource[] = [];
553
+ const seen = new Set<string>();
554
+ const push = (source: SkillSource) => { if (!seen.has(source.path)) { seen.add(source.path); out.push(source); } };
555
+ if (Array.isArray(rawSources)) {
556
+ for (const row of rawSources) {
557
+ if (!row || typeof row !== 'object') continue;
558
+ const r = row as Record<string, unknown>;
559
+ if (typeof r.path !== 'string' || !r.path.trim()) continue;
560
+ const path = r.path.trim();
561
+ push({ id: typeof r.id === 'string' && r.id.trim() ? r.id.trim() : skillSourceId(path), path, kind: 'connected', enabled: r.enabled !== false });
562
+ }
563
+ }
564
+ if (Array.isArray(legacyConnected)) {
565
+ for (const path of legacyConnected) if (typeof path === 'string' && path.trim()) push({ id: skillSourceId(path.trim()), path: path.trim(), kind: 'connected', enabled: true });
566
+ }
567
+ return out.length ? out : undefined;
568
+ }
569
+
570
+ /**
571
+ * ★ EN5 — the ONE function every reader uses (loader · setup checklist · connect · onboarding).
572
+ * Order is the historical order, so an old config resolves to exactly the old directory list:
573
+ * preset (activeSet/dirs) → shared ~/.agents/skills (only when it exists) → connected → Claude package roots (opt-in).
574
+ * Packaged `skills/` is appended by defaultSkillDirs, not listed here.
575
+ */
576
+ export function resolveSkillSources(cfg?: UserConfig, opts?: DefaultSkillDirsOptions): SkillSource[] {
519
577
  const claudecode = skillSetDir('claudecode')!; // = ~/.claude/skills (LOCAL_SKILLS_DIR)
520
- let base = [claudecode];
578
+ const preset = (paths: string[], id: string): SkillSource[] => paths.map((path, i) => ({ id: paths.length > 1 ? `${id}:${i + 1}` : id, path, kind: 'preset', enabled: true }));
579
+ let sources: SkillSource[] = preset([claudecode], 'claudecode');
521
580
  try {
522
581
  const sk = (cfg ?? getUserConfig()).skills;
523
582
  if (sk.activeSet !== 'custom') {
524
583
  const d = skillSetDir(sk.activeSet);
525
- if (d) base = [d];
526
- else if (sk.dirs.length > 0) base = [...sk.dirs];
584
+ if (d) sources = preset([d], sk.activeSet);
585
+ else if (sk.dirs.length > 0) sources = preset([...sk.dirs], 'custom');
527
586
  } else if (sk.dirs.length > 0) {
528
- base = [...sk.dirs];
587
+ sources = preset([...sk.dirs], 'custom');
588
+ }
589
+ const has = (path: string) => sources.some((source) => source.path === path);
590
+ const shared = sharedAgentSkillRoots({
591
+ home: opts?.sharedAgentHome ?? REMOTE_HOME,
592
+ includeSharedAgentSkills: sk.includeSharedAgentSkills !== false,
593
+ });
594
+ try { debug.log('skills.roots', 'shared-agent', { found: shared.length }); }
595
+ catch { /* discovery remains fail-soft when observation is unavailable */ }
596
+ for (const path of shared) if (!has(path)) sources.push({ id: 'agents', path, kind: 'shared', enabled: true });
597
+ for (const source of sk.sources ?? []) if (!has(source.path)) sources.push({ ...source, kind: 'connected' });
598
+ if (sk.includeClaudePackageSkills === true) {
599
+ for (const path of appendClaudePackageSkillDirs([], opts?.pluginsRoot)) {
600
+ if (!has(path)) sources.push({ id: `package:${skillSourceId(path).replace(/^plugin:/, '')}`, path, kind: 'package', enabled: true });
601
+ }
529
602
  }
530
- base = sk.includeClaudePackageSkills === true
531
- ? appendClaudePackageSkillDirs(base, opts?.pluginsRoot)
532
- : base;
533
603
  } catch { /* fail-soft — config 없거나 파싱 실패 시 기본 경로 */ }
604
+ return sources;
605
+ }
606
+
607
+ export function defaultSkillDirs(
608
+ cfg?: UserConfig,
609
+ opts?: DefaultSkillDirsOptions,
610
+ ): string[] {
611
+ const sources = resolveSkillSources(cfg, opts);
612
+ const paths = sources.filter((source) => source.enabled).map((source) => source.path);
613
+ // Historical: the package opt-in path also collapsed duplicate user dirs; the plain path kept them (index dedupes later).
614
+ const base = sources.some((source) => source.kind === 'package') || packageOptIn(cfg) ? uniqueSkillDirsPreserveOrder(paths) : paths;
534
615
  return appendBundledSkillsDir(base, opts?.bundledSkillsRoot);
535
616
  }
536
617
 
@@ -685,6 +766,7 @@ function skillsDefaults(): SkillsConfig {
685
766
  return {
686
767
  activeSet: 'claudecode',
687
768
  dirs: d ? [d] : [],
769
+ includeSharedAgentSkills: true,
688
770
  allow: [],
689
771
  deny: [],
690
772
  urlRouting: urlRoutingDefaults(),
@@ -886,6 +968,9 @@ export interface DiscordConfig {
886
968
  allowedUsers: string[];
887
969
  /** Channel snowflake for cron / push output (optional). */
888
970
  homeChannel?: string;
971
+ /** EV12c — guild text channels where the production bot chats like a DM (allowlist still applies).
972
+ * Empty / absent = DM-only, as before. */
973
+ chatChannels?: string[];
889
974
  /** Standalone `elanous discord-test` scope — same token, dedicated
890
975
  * channel, isolated state. See DiscordTestChannel. */
891
976
  testChannel?: DiscordTestChannel;
@@ -896,6 +981,7 @@ const DISCORD_DEFAULTS: DiscordConfig = { enabled: false, allowedUsers: [] };
896
981
  // ── Onboarding ───────────────────────────────────────────────────────
897
982
 
898
983
  export interface OnboardingConfig {
984
+ webFirst?: boolean;
899
985
  completed: boolean;
900
986
  completedAt?: string;
901
987
  version: number;
@@ -1365,7 +1451,31 @@ export interface ChatCompactConfig {
1365
1451
  archiveRetentionMb: number;
1366
1452
  }
1367
1453
 
1454
+ /** ER1 — 액티브 덤퍼(오류 자동 보고). 기본 켬(첫 실행 안내 한 번 · 대표 10-01) · 신원(email/telegramUser)은 따로 «예»일 때만. */
1455
+ export interface ErrorReportsConfig {
1456
+ /** false = never send. Absent = on (the first-run notice says how to turn it off). */
1457
+ enabled?: boolean;
1458
+ /** true = also send email / telegramUser below with each report. Default false. */
1459
+ identity?: boolean;
1460
+ email?: string;
1461
+ telegramUser?: string;
1462
+ }
1463
+
1464
+ export function parseErrorReportsConfig(raw: unknown): ErrorReportsConfig | undefined {
1465
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) return undefined;
1466
+ const r = raw as Record<string, unknown>;
1467
+ const out: ErrorReportsConfig = {};
1468
+ if (r.enabled === false || r.enabled === 'false') out.enabled = false;
1469
+ else if (r.enabled === true || r.enabled === 'true') out.enabled = true;
1470
+ if (r.identity === true || r.identity === 'true') out.identity = true;
1471
+ if (typeof r.email === 'string' && r.email.trim()) out.email = r.email.trim();
1472
+ if (typeof r.telegramUser === 'string' && r.telegramUser.trim()) out.telegramUser = r.telegramUser.trim();
1473
+ return Object.keys(out).length ? out : undefined;
1474
+ }
1475
+
1368
1476
  export interface ChatConfig {
1477
+ /** Short questions may receive a tool-free first reply. Default true. */
1478
+ fastPath: boolean;
1369
1479
  conciseness: ChatConcisenessConfig;
1370
1480
  toolOutput: ChatToolOutputConfig;
1371
1481
  autoCompact: ChatAutoCompactConfig;
@@ -1392,6 +1502,7 @@ const CHAT_CONCISENESS_DEFAULTS: ChatConcisenessConfig = {
1392
1502
  };
1393
1503
 
1394
1504
  export const CHAT_DEFAULTS: ChatConfig = {
1505
+ fastPath: true,
1395
1506
  conciseness: { ...CHAT_CONCISENESS_DEFAULTS },
1396
1507
  toolOutput: {
1397
1508
  persistOnOverflow: true,
@@ -3324,9 +3435,16 @@ export function resolveRoleModel(role: ModelRole, config: UserConfig = getUserCo
3324
3435
  }
3325
3436
 
3326
3437
 
3438
+ export type SeatLoopMode = 'off' | 'shadow' | 'on';
3439
+
3440
+ export interface SeatLoopConfig {
3441
+ /** No seat actions by default; shadow observes without execution, on permits the seat loop to act. */
3442
+ mode: SeatLoopMode;
3443
+ }
3444
+
3327
3445
  export interface UserConfig {
3328
- /** Steward loop: observe-only until an independently approved act implementation exists. */
3329
- loops?: { steward?: { mode?: 'observe' | 'act'; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string> } };
3446
+ /** Steward and seat loops are parsed independently. An absent seat remains off. */
3447
+ loops?: { steward?: { mode?: 'observe' | 'act'; linearTeam?: string; roles?: Record<string, { maxConcurrent?: number }>; budget?: number; tracks?: Record<string, string>; alertAfterFailures?: number }; seat?: SeatLoopConfig };
3330
3448
  /** 🔐 Pod 의 Grok API 키 과금은 명시 opt-in 만. 생략·잘못된 값은 false.
3331
3449
  * budgetGate 와 같은 `harness` 객체다 — 중복 선언이면 뒤 타입이 앞을 지운다. */
3332
3450
  harness?: { pod?: { grokApiKeyOptIn: boolean }; budgetGate?: HarnessBudgetGateConfig; defaultRepo?: string; substrate?: 'local' | 'pod'; podPool?: string };
@@ -3344,6 +3462,8 @@ export interface UserConfig {
3344
3462
  logs: LogsConfig;
3345
3463
  shell: ShellConfig;
3346
3464
  chat: ChatConfig;
3465
+ /** ER1 — optional so older fixtures stay valid; absent = defaults. */
3466
+ errorReports?: ErrorReportsConfig;
3347
3467
  voice: VoiceConfig;
3348
3468
  intake: IntakeConfig;
3349
3469
  dashboard: DashboardConfig;
@@ -3430,7 +3550,7 @@ export interface UserConfig {
3430
3550
  /** 승인 뒤 조율 채널 페이즈 배달(기본 OFF) 및 트랙 에이전트(기본 OFF, 최대 동시 2개). */
3431
3551
  autopilot?: {
3432
3552
  trackCourier?: { enabled?: boolean; channelPr?: number; handoffMinutes?: number };
3433
- trackAgent?: { enabled: boolean; maxConcurrent: number };
3553
+ trackAgent?: { enabled: boolean; maxConcurrent: number; shadow: boolean; shadowMaxDecisionsPerDay: number };
3434
3554
  };
3435
3555
  /** 하니스 예산 게이트 설정. 판정은 P15 — 여기선 칸만 싣는다. 파서는 항상 기본을 채운다.
3436
3556
  * 선택이다 — 기존 UserConfig 리터럴이 이 칸 없이 컴파일되게 한다. 해석기는 없으면 기본을 쓴다.
@@ -3618,6 +3738,10 @@ function parseAutopilotConfig(raw: unknown): UserConfig['autopilot'] | undefined
3618
3738
  enabled: v.enabled === true,
3619
3739
  maxConcurrent: typeof v.maxConcurrent === 'number' && Number.isSafeInteger(v.maxConcurrent) && v.maxConcurrent > 0
3620
3740
  ? Math.min(v.maxConcurrent, 2) : 2,
3741
+ shadow: v.shadow === true,
3742
+ shadowMaxDecisionsPerDay: typeof v.shadowMaxDecisionsPerDay === 'number'
3743
+ && Number.isSafeInteger(v.shadowMaxDecisionsPerDay) && v.shadowMaxDecisionsPerDay >= 0
3744
+ ? v.shadowMaxDecisionsPerDay : 20,
3621
3745
  };
3622
3746
  }
3623
3747
  return Object.keys(parsed).length > 0 ? parsed : undefined;
@@ -3649,6 +3773,7 @@ function parseRoleLlmConfig(raw: unknown): RoleLlmConfig | undefined {
3649
3773
 
3650
3774
  function defaultConfig(): UserConfig {
3651
3775
  return {
3776
+ loops: { seat: { mode: 'off' } },
3652
3777
  skillRouter: { ...SR_DEFAULTS },
3653
3778
  llm: { ...LLM_DEFAULTS },
3654
3779
  skills: skillsDefaults(),
@@ -3661,6 +3786,7 @@ function defaultConfig(): UserConfig {
3661
3786
  logs: { retention: { ...LOGS_DEFAULTS.retention } },
3662
3787
  shell: { ...SHELL_DEFAULTS },
3663
3788
  chat: {
3789
+ fastPath: CHAT_DEFAULTS.fastPath,
3664
3790
  conciseness: { ...CHAT_CONCISENESS_DEFAULTS },
3665
3791
  toolOutput: { ...CHAT_DEFAULTS.toolOutput },
3666
3792
  autoCompact: { ...CHAT_DEFAULTS.autoCompact },
@@ -4126,6 +4252,14 @@ function parseStewardTracks(input: unknown): Record<string, string> | undefined
4126
4252
  return Object.keys(out).length ? out : undefined;
4127
4253
  }
4128
4254
 
4255
+ function parseSeatLoopsConfig(input: unknown): SeatLoopConfig {
4256
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return { mode: 'off' };
4257
+ const seat = (input as Record<string, unknown>).seat;
4258
+ if (!seat || typeof seat !== 'object' || Array.isArray(seat)) return { mode: 'off' };
4259
+ const mode = (seat as Record<string, unknown>).mode;
4260
+ return { mode: mode === 'on' || mode === 'shadow' ? mode : 'off' };
4261
+ }
4262
+
4129
4263
  function parseStewardLoopsConfig(input: unknown): UserConfig['loops'] {
4130
4264
  if (!input || typeof input !== 'object' || Array.isArray(input)) return undefined;
4131
4265
  const value = (input as Record<string, unknown>).steward;
@@ -4144,6 +4278,7 @@ function parseStewardLoopsConfig(input: unknown): UserConfig['loops'] {
4144
4278
  ...(typeof s.linearTeam === 'string' && s.linearTeam.trim() ? { linearTeam: s.linearTeam.trim() } : {}),
4145
4279
  ...(Object.keys(roles).length ? { roles } : {}),
4146
4280
  ...(typeof s.budget === 'number' && Number.isFinite(s.budget) && s.budget >= 0 ? { budget: s.budget } : {}),
4281
+ ...(typeof s.alertAfterFailures === 'number' && Number.isSafeInteger(s.alertAfterFailures) && s.alertAfterFailures > 0 ? { alertAfterFailures: s.alertAfterFailures } : {}),
4147
4282
  ...(parseStewardTracks(s.tracks) ? { tracks: parseStewardTracks(s.tracks)! } : {}),
4148
4283
  } };
4149
4284
  }
@@ -4374,6 +4509,8 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4374
4509
  skills: {
4375
4510
  activeSet: normalizeSkillSet(sk.activeSet),
4376
4511
  dirs: strArray(sk.dirs, skillsDefaults().dirs),
4512
+ includeSharedAgentSkills: sk.includeSharedAgentSkills !== false,
4513
+ ...(() => { const sources = parsePersistedSkillSources(sk.sources, sk.connected); return sources ? { sources } : {}; })(),
4377
4514
  allow: strArray(sk.allow, []),
4378
4515
  deny: strArray(sk.deny, []),
4379
4516
  urlRouting: parseUrlRouting(sk.urlRouting),
@@ -4409,6 +4546,9 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4409
4546
  homeChannel: typeof dc.homeChannel === 'string' ? dc.homeChannel
4410
4547
  : typeof dc.homeChannel === 'number' ? String(dc.homeChannel) : undefined,
4411
4548
  testChannel: normalizeDiscordTestChannel((dc as Record<string, unknown>).testChannel),
4549
+ ...(Array.isArray(dc.chatChannels)
4550
+ ? { chatChannels: (dc.chatChannels as unknown[]).map(v => String(v).trim()).filter(Boolean) }
4551
+ : {}),
4412
4552
  // Sprint 21 wiring sub-block (2026-05-01)
4413
4553
  },
4414
4554
  finance: {
@@ -4452,6 +4592,7 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4452
4592
  relaxedGates: fin.relaxedGates === true,
4453
4593
  },
4454
4594
  onboarding: {
4595
+ ...(typeof ob2.webFirst === 'boolean' ? { webFirst: ob2.webFirst } : {}),
4455
4596
  completed: ob2.completed === true,
4456
4597
  completedAt: str(ob2.completedAt),
4457
4598
  version: typeof ob2.version === 'number' ? ob2.version : 0,
@@ -4497,7 +4638,9 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4497
4638
  // false required to short-circuit `buildDashboardOptionalToolSpecs`.
4498
4639
  allowDashboardOptionalTools: sh.allowDashboardOptionalTools !== false,
4499
4640
  },
4641
+ ...((): { errorReports?: ErrorReportsConfig } => { const er = parseErrorReportsConfig(rawObj.errorReports); return er ? { errorReports: er } : {}; })(),
4500
4642
  chat: {
4643
+ fastPath: chat.fastPath === false || chat.fastPath === 'false' ? false : CHAT_DEFAULTS.fastPath,
4501
4644
  conciseness: {
4502
4645
  enabled: chatConciseness.enabled === false ? false : CHAT_CONCISENESS_DEFAULTS.enabled,
4503
4646
  finalMessageMaxLines: clampNum(
@@ -4876,7 +5019,7 @@ export function buildUserConfig(path: string = defaultPath()): UserConfig {
4876
5019
  goals: parseGoalsConfig(rawObj.goals),
4877
5020
  registry: parseRegistryConfig(rawObj.registry),
4878
5021
  tools: parseToolsConfig(rawObj.tools),
4879
- ...(stewardLoops ? { loops: stewardLoops } : {}),
5022
+ loops: { ...(stewardLoops ?? {}), seat: parseSeatLoopsConfig(rawObj.loops) },
4880
5023
  // M1-1: sparse — undefined when the user hasn't set anything, so
4881
5024
  // resolvers fall through to zero-config defaults.
4882
5025
  ...spreadIfDefined('modelTier', parseModelTierConfig(rawObj.modelTier)),
@@ -5450,6 +5593,11 @@ export function saveUserConfig(
5450
5593
  const rawHarness = rawRest.harness && typeof rawRest.harness === 'object' && !Array.isArray(rawRest.harness)
5451
5594
  ? rawRest.harness as Record<string, unknown> : {};
5452
5595
  delete rawRest.harness;
5596
+ const rawAutopilot = rawRest.autopilot && typeof rawRest.autopilot === 'object' && !Array.isArray(rawRest.autopilot)
5597
+ ? rawRest.autopilot as Record<string, unknown> : {};
5598
+ const rawLoops = rawRest.loops && typeof rawRest.loops === 'object' && !Array.isArray(rawRest.loops)
5599
+ ? rawRest.loops as Record<string, unknown> : {};
5600
+ if (cfg.loops) delete rawRest.loops;
5453
5601
  if (cfg.pod?.hostMirror) {
5454
5602
  const rawPod = rawRest.pod && typeof rawRest.pod === 'object' && !Array.isArray(rawRest.pod)
5455
5603
  ? rawRest.pod as Record<string, unknown> : {};
@@ -5466,6 +5614,24 @@ export function saveUserConfig(
5466
5614
  podPool: cfg.harness?.podPool,
5467
5615
  }),
5468
5616
  ...rawRest,
5617
+ ...(cfg.loops ? { loops: {
5618
+ ...rawLoops,
5619
+ ...(cfg.loops.seat ? { seat: { mode: cfg.loops.seat.mode } } : {}),
5620
+ } } : {}),
5621
+ ...(cfg.autopilot ? { autopilot: {
5622
+ ...rawAutopilot,
5623
+ ...cfg.autopilot,
5624
+ ...(cfg.autopilot.trackAgent ? { trackAgent: {
5625
+ ...(rawAutopilot.trackAgent && typeof rawAutopilot.trackAgent === 'object' && !Array.isArray(rawAutopilot.trackAgent)
5626
+ ? rawAutopilot.trackAgent as Record<string, unknown> : {}),
5627
+ ...cfg.autopilot.trackAgent,
5628
+ } } : {}),
5629
+ ...(cfg.autopilot.trackCourier ? { trackCourier: {
5630
+ ...(rawAutopilot.trackCourier && typeof rawAutopilot.trackCourier === 'object' && !Array.isArray(rawAutopilot.trackCourier)
5631
+ ? rawAutopilot.trackCourier as Record<string, unknown> : {}),
5632
+ ...cfg.autopilot.trackCourier,
5633
+ } } : {}),
5634
+ } } : {}),
5469
5635
  skillRouter: cfg.skillRouter,
5470
5636
  llm: stripUndef({
5471
5637
  provider: cfg.llm.provider,
@@ -5517,6 +5683,10 @@ export function saveUserConfig(
5517
5683
  skills: stripUndef({
5518
5684
  activeSet: cfg.skills.activeSet,
5519
5685
  dirs: [...cfg.skills.dirs],
5686
+ // EN5 — only connected roots are persisted (preset/shared/package are derived). The dev-only `connected` key is not written back.
5687
+ sources: cfg.skills.sources && cfg.skills.sources.some((source) => source.kind === 'connected')
5688
+ ? cfg.skills.sources.filter((source) => source.kind === 'connected').map(({ id, path, enabled }) => ({ id, path, enabled }))
5689
+ : undefined,
5520
5690
  // Omit allow/deny when empty to keep disk JSON minimal for the
5521
5691
  // common "no filter" case.
5522
5692
  allow: cfg.skills.allow && cfg.skills.allow.length > 0 ? [...cfg.skills.allow] : undefined,
@@ -5575,6 +5745,7 @@ export function saveUserConfig(
5575
5745
  botToken: cfg.discord.botToken,
5576
5746
  allowedUsers: [...cfg.discord.allowedUsers],
5577
5747
  homeChannel: cfg.discord.homeChannel,
5748
+ chatChannels: cfg.discord.chatChannels ? [...cfg.discord.chatChannels] : undefined,
5578
5749
  testChannel: cfg.discord.testChannel
5579
5750
  ? stripUndef({
5580
5751
  channelId: cfg.discord.testChannel.channelId,
@@ -5601,6 +5772,7 @@ export function saveUserConfig(
5601
5772
  relaxedGates: cfg.finance.relaxedGates,
5602
5773
  }),
5603
5774
  onboarding: stripUndef({
5775
+ webFirst: cfg.onboarding.webFirst,
5604
5776
  completed: cfg.onboarding.completed,
5605
5777
  completedAt: cfg.onboarding.completedAt,
5606
5778
  version: cfg.onboarding.version,
@@ -5608,7 +5780,9 @@ export function saveUserConfig(
5608
5780
  debug: cfg.debug,
5609
5781
  logs: cfg.logs,
5610
5782
  shell: cfg.shell,
5783
+ errorReports: cfg.errorReports ? stripUndef({ ...cfg.errorReports }) : undefined,
5611
5784
  chat: stripUndef({
5785
+ fastPath: cfg.chat.fastPath,
5612
5786
  conciseness: stripUndef({
5613
5787
  enabled: cfg.chat.conciseness.enabled,
5614
5788
  finalMessageMaxLines: cfg.chat.conciseness.finalMessageMaxLines,
@@ -1 +1 @@
1
- {"commit":"65a4c7230c60a536b0f0f0428322c8ddcbcac752"}
1
+ {"commit":"eca73b4a3a106df99424c22206165f2fe4e5e910"}
@@ -14,8 +14,10 @@
14
14
  * (📏 2026-09-23: 연출 있는 shot2 = 0.708 < 연출 없는 대조군 0.851).
15
15
  */
16
16
  import { spawnSync } from 'node:child_process';
17
- import { existsSync, readdirSync } from 'node:fs';
17
+ import { existsSync, readFileSync, readdirSync } from 'node:fs';
18
+ import { homedir } from 'node:os';
18
19
  import { join } from 'node:path';
20
+ import { searchVflowReferences } from '../vflow-reference.js';
19
21
  import { run } from './ffmpeg.js';
20
22
  import { resolvePython } from '../../python/resolve-python.js';
21
23
  import { UNOBSERVED, type Recipe } from './types.js';
@@ -111,14 +113,49 @@ export const identityHoldsGate: Recipe = async (ctx) => {
111
113
  export const directedShots: Recipe = async (ctx) => {
112
114
  const src = ctx.state.shot_sources as Record<string, string> | undefined;
113
115
  if (!src) return need('shot_sources');
114
- const shot_paths = Object.values(src).filter(existsSync);
115
- if (shot_paths.length === 0) return { outcome: 'error', note: '샷 0 — ⛔ 이 러너는 크레딧을 안 쓴다(Seedance·Kling 생성은 스킬 세션의 몫)' };
116
+ const prompts = ctx.state.shot_prompts as Record<string, string> | undefined;
117
+ const existingShots = Object.entries(src).filter(([, path]) => existsSync(path));
118
+ const shot_paths = existingShots.map(([, path]) => path);
119
+ const promptedShots = existingShots.filter(([shot]) => prompts?.[shot] !== undefined);
120
+ const shot_references: Record<string, { url: string; videoUrl: string | null; model: string; name: string; author: string | null; authorUrl: string | null; camera: string[] }[]> = {};
121
+ let referenceNote = '';
122
+ if (prompts && Object.keys(prompts).length && shot_paths.length) {
123
+ const file = (ctx.state.vflow_file as string | undefined) ?? join(homedir(), 'docs', 'ref', 'vflow', 'prompts.ndjson');
124
+ const taxonomy = readFileSync(join(import.meta.dir, '../../../graphs/video/reference/vflow-taxonomy.yaml'), 'utf8');
125
+ const cameraAxis = taxonomy.match(/^ camera_movement:.*\n((?:^ .*\n)*)/m)?.[1] ?? '';
126
+ const cameraTerms = [...cameraAxis.matchAll(/term:\s*([^,}]+)/g)].map((match) => match[1]!.trim());
127
+ // These shot-prompt camera terms supplement the site's taxonomy axis.
128
+ cameraTerms.push('crash zoom', 'dolly', 'static');
129
+ const cameraPatterns = cameraTerms.map((term) => {
130
+ const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
+ return { term, pattern: new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, 'iu') };
132
+ });
133
+ let found = 0;
134
+ for (const [shot] of promptedShots) {
135
+ const prompt = prompts[shot]!;
136
+ const camera = cameraPatterns.filter(({ pattern }) => pattern.test(prompt)).map(({ term }) => term);
137
+ const result = searchVflowReferences(file, { camera, model: ctx.state.shot_model as string | undefined, limit: camera.length ? 3 : 0 });
138
+ if (!result.available) {
139
+ referenceNote = `vflow DB 없음(${result.reason})`;
140
+ break;
141
+ }
142
+ shot_references[shot] = result.rows.map((row) => ({
143
+ url: row.url, videoUrl: row.video?.url ?? null, model: row.model, name: row.name,
144
+ author: row.author, authorUrl: row.authorUrl, camera: row.spec?.camera ?? [],
145
+ }));
146
+ if (shot_references[shot]!.length) found++;
147
+ }
148
+ if (!referenceNote) referenceNote = `vflow 참고 ${found}샷/${shot_paths.length}샷`;
149
+ }
150
+ const note = (text: string) => referenceNote ? `${text}\n${referenceNote}` : text;
151
+ if (shot_paths.length === 0) return { outcome: 'error', produced: prompts ? { shot_references } : undefined, note: note('샷 0 — ⛔ 이 러너는 크레딧을 안 쓴다(Seedance·Kling 생성은 스킬 세션의 몫)') };
152
+ const produced = { shot_paths, shot_count: shot_paths.length, ...(prompts ? { shot_references } : {}) };
116
153
  // ⛔⭐ 되돌이 수렴(🩸 2026-09-23 실물: fullbleed → shots → «같은» 샷 → fullbleed … 예산 소진).
117
154
  // 리프레임 자가 「그 비율로 다시 렌더하라」고 돌려보냈는데 이 러너는 다시 그릴 수 없다(크레딧 무사용)
118
155
  // ⇒ 같은 샷을 또 내면 같은 판정이 난다. 「못 다시 그린다」를 error 로 말해 `rendered-unedited` 로 끝낸다.
119
156
  const pending = (ctx.state.fullbleed_cuts as string[] | undefined) ?? [];
120
- if (pending.length) return { outcome: 'error', produced: { shot_paths, shot_count: shot_paths.length }, note: `전용 비율 재렌더가 필요한 샷 ${pending.join(', ')} — ⛔ 이 러너는 다시 그리지 않는다(크레딧 무사용) · 그 비율 렌더를 스킬 세션에서 만든 뒤 native 로 넘겨라` };
121
- return { outcome: 'ok', produced: { shot_paths, shot_count: shot_paths.length }, note: `샷 ${shot_paths.length}개 · ⚠️ 이미 생성된 것을 받았다` };
157
+ if (pending.length) return { outcome: 'error', produced, note: note(`전용 비율 재렌더가 필요한 샷 ${pending.join(', ')} — ⛔ 이 러너는 다시 그리지 않는다(크레딧 무사용) · 그 비율 렌더를 스킬 세션에서 만든 뒤 native 로 넘겨라`) };
158
+ return { outcome: 'ok', produced, note: note(`샷 ${shot_paths.length}개 · ⚠️ 이미 생성된 것을 받았다`) };
122
159
  };
123
160
 
124
161
  /** `08_shot_ruler.py` 의 `shot_variation` 한 칸만 읽는다(그 자가 «살아남은 유일한 칸»이라 스스로 적었다). */