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,22 +1,29 @@
1
- // Field reel — a folder of phone photos / short clips → one vertical 30–60 s HyperFrames composition (EV10).
2
- // Usage: node reel.mjs <folder> [--title "마케터의 밤"] [--sub "2026.10.02 · 서울"] [--max 60] [--min 30]
1
+ // Field reel — a folder of phone photos / short clips → one vertical HyperFrames composition (EV10 · EV10b).
2
+ // Usage: node reel.mjs <folder> [--title "마케터의 밤"] [--sub "2026.10.02 · 서울"] [--max 60] [--min 24] [--part full|body]
3
3
  // <folder>/captions.txt (optional): one line per item — `파일명 | 자막` (a line without « | » is ignored).
4
+ // Photos without a caption get one line from a vision model (codex · parallel · FIELD_REEL_VISION=0 turns it off);
5
+ // if that fails the caption is «현장 N». A slug-like title (`field-2026-10-01`) is replaced by a vision headline.
6
+ // BGM: FIELD_REEL_BGM=<wav/mp3> or ~/.cache/elanous-explainer/bgm/field.wav — cut to length, faded and leveled here.
7
+ // --part body: photos only, no intro/end cards and no music (the instant version stitches cached cards around it).
4
8
  // Output: <folder>/reel/hf/ (HyperFrames project) ⊕ reel/timeline.json. Render: npx hyperframes render --fps 30 in reel/hf.
5
9
  // Look = Elanvital CI: Real Black ground · Icarus Red single accent · V6 mark · Pretendard. Order = capture time (then name).
6
10
  import { readFileSync, writeFileSync, mkdirSync, copyFileSync, existsSync, readdirSync, statSync } from "node:fs";
7
11
  import { join, dirname, resolve, extname, basename } from "node:path";
8
12
  import { homedir } from "node:os";
9
- import { spawnSync } from "node:child_process";
13
+ import { spawn, spawnSync } from "node:child_process";
10
14
  import { fileURLToPath } from "node:url";
11
15
 
12
16
  const here = dirname(fileURLToPath(import.meta.url));
13
17
  const argv = process.argv.slice(2);
14
18
  const opt = (k, d) => { const i = argv.indexOf(`--${k}`); return i >= 0 ? argv[i + 1] : d; };
15
19
  const folder = resolve(argv.find((a, i) => !a.startsWith("--") && !(i > 0 && argv[i - 1].startsWith("--"))) ?? ".");
16
- const TITLE = opt("title", "현장 스케치"), SUB = opt("sub", ""), MAX = Number(opt("max", 60)), MIN = Number(opt("min", 30));
17
- const W = 1080, H = 1920, INTRO = 2.2, END = 3.0, PHOTO = 3.5, CLIP_MAX = 5, XF = 0.35;
20
+ const PART = opt("part", "full");
21
+ const MAX = Number(opt("max", 60)), MIN = Number(opt("min", 24));
22
+ const W = 1080, H = 1920, PHOTO = 4.2, CLIP_MAX = 5, XF = 0.5;
23
+ const INTRO = PART === "body" ? 0 : 3.0, END = PART === "body" ? 0 : 3.4;
18
24
  const out = join(folder, "reel"), hf = join(out, "hf"), media = join(hf, "assets", "media");
19
25
  mkdirSync(media, { recursive: true }); mkdirSync(join(hf, "assets", "fonts"), { recursive: true });
26
+ const T0 = Date.now(), lap = (s) => console.log(`reel: ${s} · ${((Date.now() - T0) / 1000).toFixed(1)}s`);
20
27
 
21
28
  // ── collect & order ────────────────────────────────────────────────────────────
22
29
  const IMG = [".jpg", ".jpeg", ".png", ".heic", ".webp"], VID = [".mp4", ".mov", ".m4v"];
@@ -41,9 +48,9 @@ if (!items.length) throw new Error(`no photos or clips in ${folder}`);
41
48
  for (const it of items) it.dur = it.video ? Math.min(CLIP_MAX, Math.max(1.5, probeDur(it.p))) : PHOTO;
42
49
  const body = () => items.reduce((a, it) => a + it.dur, 0);
43
50
  while (INTRO + body() + END > MAX && items.length > 1) items.splice(Math.floor(items.length / 2), 1); // drop from the middle, keep first & last
44
- if (INTRO + body() + END < MIN) { const photos = items.filter((i) => !i.video); const need = MIN - (INTRO + body() + END); for (const p of photos) p.dur += need / Math.max(1, photos.length); }
51
+ if (PART !== "body" && INTRO + body() + END < MIN) { const photos = items.filter((i) => !i.video); const need = MIN - (INTRO + body() + END); for (const p of photos) p.dur += need / Math.max(1, photos.length); }
45
52
 
46
- // ── media prep: HEIC → jpg · big photos down to 2160 · clips re-encoded short & muted ──
53
+ // ── media prep: HEIC → jpg · big photos down to 2160 · a small copy for the vision model · clips re-encoded short & muted ──
47
54
  let t = INTRO;
48
55
  items.forEach((it, i) => {
49
56
  it.start = +t.toFixed(3); t += it.dur;
@@ -55,10 +62,49 @@ items.forEach((it, i) => {
55
62
  it.src = `m${i}.jpg`;
56
63
  const r = run("sips", ["-s", "format", "jpeg", "-Z", "2160", it.p, "--out", join(media, it.src)]);
57
64
  if (r.status !== 0) throw new Error(`photo ${it.f}: ${r.stderr.slice(0, 200)}`);
65
+ const dim = run("sips", ["-g", "pixelWidth", "-g", "pixelHeight", join(media, it.src)]).stdout;
66
+ const pw = Number(/pixelWidth: (\d+)/.exec(dim)?.[1]), ph = Number(/pixelHeight: (\d+)/.exec(dim)?.[1]);
67
+ it.wide = pw > 0 && ph > 0 && pw / ph > 0.75; // anything wider than 3:4 is shown whole over a blurred fill
68
+ run("sips", ["-s", "format", "jpeg", "-Z", "1024", it.p, "--out", join(out, `v${i}.jpg`)]);
58
69
  }
59
- it.cap = caps[it.f] ?? "";
60
70
  });
61
71
  const TOTAL = +(t + END).toFixed(3);
72
+ lap(`${items.length} items prepared`);
73
+
74
+ // ── vision: one caption line per photo, in parallel; a headline when the title is only a slug ──
75
+ const CODEX = process.env.FIELD_REEL_CODEX_BIN || "codex";
76
+ const VISION = process.env.FIELD_REEL_VISION !== "0";
77
+ const ask = (prompt, images, ms = 30000) => new Promise((done) => {
78
+ const args = ["exec", "--skip-git-repo-check", "-c", "model_reasoning_effort=low", prompt, ...images.flatMap((im) => ["-i", im])];
79
+ let buf = "", settled = false;
80
+ const finish = (v) => { if (!settled) { settled = true; done(v); } };
81
+ let child;
82
+ try { child = spawn(CODEX, args, { stdio: ["ignore", "pipe", "ignore"] }); } catch { return finish(""); }
83
+ const timer = setTimeout(() => { child.kill("SIGKILL"); finish(""); }, ms);
84
+ child.stdout.on("data", (d) => { buf += d; });
85
+ child.on("error", () => { clearTimeout(timer); finish(""); });
86
+ child.on("close", (code) => { clearTimeout(timer); finish(code === 0 ? buf : ""); });
87
+ });
88
+ const oneLine = (s, max) => {
89
+ const line = s.split("\n").map((l) => l.trim().replace(/^["'«“]|["'»”]$/g, "")).filter(Boolean).pop() ?? "";
90
+ return line.length >= 2 && line.length <= max ? line.replace(/[.。]$/, "") : "";
91
+ };
92
+ const CAP_PROMPT = "행사 현장 사진이다. 세로 영상 자막으로 쓸 한국어 한 줄을 출력하라 — 18자 이내 · 마침표 없이 · 보이는 장면만 · 사람 이름·외모 묘사·추측 금지 · 화면 속 글자(행사명·회사명)는 읽히면 써도 된다. 그 한 줄만.";
93
+ const HEAD_PROMPT = "같은 행사에서 찍은 사진들이다. 영상 첫 화면 제목으로 쓸 한국어 행사 이름 한 줄을 출력하라 — 16자 이내 · 화면 속 글자에서 읽히는 행사·회사 이름을 우선 · 읽히지 않으면 장면을 짧게(예: «상장 기념식 현장») · 추측 금지. 그 한 줄만.";
94
+ const slugTitle = (s) => !s || /^[a-z0-9]+(?:-[a-z0-9]+)*$/i.test(s);
95
+ let TITLE = opt("title", ""), SUB = opt("sub", "");
96
+ const photos = items.filter((it) => !it.video);
97
+ const VT0 = Date.now();
98
+ const jobs = VISION ? [
99
+ ...photos.filter((it) => !caps[it.f]).map(async (it) => { it.vcap = oneLine(await ask(CAP_PROMPT, [join(out, `v${items.indexOf(it)}.jpg`)]), 22); }),
100
+ ...(PART !== "body" && slugTitle(TITLE) && photos.length ? [(async () => { TITLE = oneLine(await ask(HEAD_PROMPT, photos.slice(0, 3).map((it) => join(out, `v${items.indexOf(it)}.jpg`))), 20) || TITLE; })()] : []),
101
+ ] : [];
102
+ await Promise.all(jobs);
103
+ const visionMs = Date.now() - VT0;
104
+ items.forEach((it, i) => { it.cap = caps[it.f] || it.vcap || (it.video ? "" : `현장 ${i + 1}`); });
105
+ if (slugTitle(TITLE)) TITLE = "현장 스케치";
106
+ if (!SUB) { const d = new Date(items[0].t + 9 * 3600e3); SUB = `${d.getUTCFullYear()}.${String(d.getUTCMonth() + 1).padStart(2, "0")}.${String(d.getUTCDate()).padStart(2, "0")}`; }
107
+ lap(`vision ${jobs.length ? `${items.filter((i) => i.vcap).length}/${photos.filter((p) => !caps[p.f]).length} captions` : "off"} · title «${TITLE}»`);
62
108
 
63
109
  // ── assets (same cache as build.mjs) ───────────────────────────────────────────
64
110
  const CACHE = process.env.EXPLAINER_CACHE ?? join(homedir(), ".cache", "elanous-explainer");
@@ -69,54 +115,114 @@ for (const [rel, url] of Object.entries(ASSETS)) {
69
115
  if (!existsSync(c)) { const res = await fetch(url); if (!res.ok) throw new Error(`asset ${rel}: HTTP ${res.status}`); mkdirSync(dirname(c), { recursive: true }); writeFileSync(c, Buffer.from(await res.arrayBuffer())); }
70
116
  copyFileSync(c, join(hf, "assets", rel));
71
117
  }
118
+
119
+ // ── music: cut to length · fade in/out · level to about -16 LUFS (the whole mix is music) ──
120
+ const BGM = process.env.FIELD_REEL_BGM || join(CACHE, "bgm", "field.wav");
121
+ let music = "";
122
+ if (PART !== "body" && existsSync(BGM)) {
123
+ const r = run("ffmpeg", ["-v", "error", "-y", "-i", BGM, "-t", String(TOTAL), "-af", `afade=t=in:d=0.4,afade=t=out:st=${f(TOTAL - 1.6)}:d=1.6,loudnorm=I=-16:TP=-1.5:LRA=9`, "-ar", "48000", "-ac", "2", join(media, "bgm.wav")]);
124
+ if (r.status === 0) music = "bgm.wav"; else console.error(`reel: music skipped — ${r.stderr.slice(0, 160)}`);
125
+ }
126
+ function f(x) { return +x.toFixed(3); }
127
+
72
128
  const markPaths = [...readFileSync(join(here, "brand", "elanous-mark-on-dark.svg"), "utf8").matchAll(/<path d="([^"]*)"/g)].map((m) => m[1]);
73
129
  const mark = (s, cls = "") => `<svg class="${cls}" width="${s}" height="${s}" viewBox="150 150 724 724">${markPaths.map((d) => `<path d="${d}" fill="#F2F1EE"/>`).join("")}<circle cx="512" cy="512" r="76" fill="#E95047"/></svg>`;
74
130
  const esc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;");
75
- const f = (x) => +x.toFixed(3);
131
+ const words = (s, cls) => s.split(/\s+/).filter(Boolean).map((w) => `<span class="${cls}">${esc(w)}</span>`).join(" ");
76
132
 
77
133
  // ── composition ────────────────────────────────────────────────────────────────
134
+ // Each photo: blurred fill + the whole photo (wide) or a full-bleed crop (tall) · slow push · a cross-move into the next ·
135
+ // a red wipe on the cut · counter «0N / 0M» · caption words rising one by one.
78
136
  const tw = [];
137
+ const n = items.length;
79
138
  const clips = items.map((it, i) => {
80
- const vis = it.video
81
- ? `<video id="v${i}" class="clip med" src="assets/media/${it.src}" muted playsinline data-start="${f(it.start)}" data-duration="${f(it.dur)}" data-track-index="1"></video>`
82
- : `<div class="clip med" data-start="${f(it.start)}" data-duration="${f(it.dur)}" data-track-index="1"><img class="kb k${i}" src="assets/media/${it.src}"/></div>`;
83
- if (!it.video) tw.push(`tl.fromTo(".k${i}", {scale:1.0, x:0}, {scale:1.09, x:${i % 2 ? -24 : 24}, duration:${f(it.dur)}, ease:"none"}, ${f(it.start)});`);
84
- const cap = it.cap ? `<div class="clip capw" data-start="${f(it.start)}" data-duration="${f(it.dur)}" data-track-index="3"><div class="cap c${i}">${esc(it.cap)}</div></div>` : "";
85
- if (it.cap) tw.push(`tl.fromTo(".c${i}", {opacity:0, y:20}, {opacity:1, y:0, duration:0.4, ease:"power3.out"}, ${f(it.start + 0.15)});`);
139
+ const s = it.start, d = it.dur, last = i === n - 1;
140
+ const span = last ? d : d + XF; // overlap the next item so the move reads as one motion
141
+ let vis;
142
+ if (it.video) {
143
+ vis = `<video id="v${i}" class="clip med" src="assets/media/${it.src}" muted playsinline data-start="${f(s)}" data-duration="${f(d)}" data-track-index="1"></video>`;
144
+ } else if (it.wide) {
145
+ vis = `<div class="clip med" data-start="${f(s)}" data-duration="${f(span)}" data-track-index="${1 + (i % 2)}"><div class="g g${i}">`
146
+ + `<img class="fill" src="assets/media/${it.src}"/><div class="dim"></div>`
147
+ + `<div class="frame fr${i}"><img src="assets/media/${it.src}"/></div></div></div>`;
148
+ tw.push(`tl.fromTo(".fr${i}", {scale:1.0, x:${i % 2 ? 30 : -30}}, {scale:1.12, x:${i % 2 ? -30 : 30}, duration:${f(span)}, ease:"none"}, ${f(s)});`);
149
+ tw.push(`tl.fromTo(".g${i} .fill", {scale:1.25}, {scale:1.4, duration:${f(span)}, ease:"none"}, ${f(s)});`);
150
+ } else {
151
+ vis = `<div class="clip med" data-start="${f(s)}" data-duration="${f(span)}" data-track-index="${1 + (i % 2)}"><div class="g g${i}"><img class="kb k${i}" src="assets/media/${it.src}"/></div></div>`;
152
+ tw.push(`tl.fromTo(".k${i}", {scale:1.02, x:0}, {scale:1.12, x:${i % 2 ? -28 : 28}, duration:${f(span)}, ease:"none"}, ${f(s)});`);
153
+ }
154
+ if (!it.video && i > 0) tw.push(`tl.fromTo(".g${i}", {opacity:0, xPercent:${i % 2 ? 14 : -14}}, {opacity:1, xPercent:0, duration:${XF}, ease:"power3.out"}, ${f(s - XF)});`);
155
+ if (i > 0) tw.push(`tl.fromTo(".wipe", {scaleY:0, transformOrigin:"50% 100%"}, {scaleY:1, duration:${XF / 2}, ease:"power2.in", immediateRender:false}, ${f(s - XF / 2)});`
156
+ + `tl.to(".wipe", {scaleY:0, transformOrigin:"50% 0%", duration:${XF / 2}, ease:"power2.out"}, ${f(s)});`);
157
+ const no = `${String(i + 1).padStart(2, "0")} <i>/ ${String(n).padStart(2, "0")}</i>`;
158
+ const cap = it.cap ? `<div class="clip capw" data-start="${f(s)}" data-duration="${f(d)}" data-track-index="4"><div class="no n${i}">${no}</div><div class="cap c${i}">${words(it.cap, `w w${i}`)}</div><div class="rule r${i}"></div></div>` : "";
159
+ if (it.cap) {
160
+ tw.push(`tl.fromTo(".n${i}", {opacity:0, x:-24}, {opacity:1, x:0, duration:0.35, ease:"power3.out"}, ${f(s + 0.2)});`);
161
+ tw.push(`tl.fromTo(".r${i}", {scaleX:0}, {scaleX:1, duration:0.5, ease:"power3.out"}, ${f(s + 0.25)});`);
162
+ tw.push(`tl.fromTo(".w${i}", {opacity:0, y:46}, {opacity:1, y:0, duration:0.45, stagger:0.09, ease:"power3.out"}, ${f(s + 0.35)});`);
163
+ if (!last) tw.push(`tl.to(".c${i}, .n${i}, .r${i}", {opacity:0, duration:0.25}, ${f(s + d - 0.3)});`);
164
+ }
86
165
  return vis + cap;
87
166
  }).join("\n");
88
- tw.push(`tl.fromTo(".it", {opacity:0, y:30}, {opacity:1, y:0, duration:0.5, stagger:0.15, ease:"power3.out"}, 0.1);`);
89
- tw.push(`tl.fromTo(".im", {rotation:-160, scale:0.6}, {rotation:0, scale:1, duration:1.0, ease:"power3.out"}, 0.0);`);
90
- tw.push(`tl.fromTo(".em", {rotation:-200, scale:0.5}, {rotation:0, scale:1, duration:1.2, ease:"power3.out"}, ${f(t)});`);
91
- tw.push(`tl.fromTo(".et", {opacity:0, y:20}, {opacity:1, y:0, duration:0.5, stagger:0.12}, ${f(t + 0.5)});`);
167
+
168
+ const intro = PART === "body" ? "" : `<section class="clip card" data-start="0" data-duration="${INTRO}" data-track-index="6">`
169
+ + `<div class="kick ik">현장 스케치</div>${mark(200, "im")}<h1>${words(TITLE, "it")}</h1><div class="line il"></div><p class="is">${esc(SUB)}</p></section>`;
170
+ const end = PART === "body" ? "" : `<section class="clip card" data-start="${f(t)}" data-duration="${END}" data-track-index="6">`
171
+ + `${mark(230, "em")}<h1 class="et">Elanous</h1><p class="et">사진을 올리면, 영상이 됩니다</p><p class="et red">elanous.ai</p></section>`;
172
+ if (PART !== "body") {
173
+ tw.push(`tl.fromTo(".ik", {opacity:0, letterSpacing:"0.6em"}, {opacity:1, letterSpacing:"0.28em", duration:0.8, ease:"power3.out"}, 0.1);`);
174
+ tw.push(`tl.fromTo(".im", {rotation:-160, scale:0.6, opacity:0}, {rotation:0, scale:1, opacity:1, duration:1.0, ease:"power3.out"}, 0.0);`);
175
+ tw.push(`tl.fromTo(".it", {opacity:0, y:50}, {opacity:1, y:0, duration:0.55, stagger:0.1, ease:"power3.out"}, 0.45);`);
176
+ tw.push(`tl.fromTo(".il", {scaleX:0}, {scaleX:1, duration:0.6, ease:"power3.inOut"}, 0.9);`);
177
+ tw.push(`tl.fromTo(".is", {opacity:0, y:16}, {opacity:1, y:0, duration:0.45, ease:"power3.out"}, 1.15);`);
178
+ tw.push(`tl.fromTo(".em", {rotation:-200, scale:0.5}, {rotation:0, scale:1, duration:1.2, ease:"power3.out"}, ${f(t)});`);
179
+ tw.push(`tl.fromTo(".et", {opacity:0, y:20}, {opacity:1, y:0, duration:0.5, stagger:0.14, ease:"power3.out"}, ${f(t + 0.5)});`);
180
+ }
181
+ tw.unshift(`tl.set(".wipe", {scaleY:0}, 0);`);
92
182
  tw.push(`tl.fromTo(".bar i", {scaleX:0}, {scaleX:1, duration:${TOTAL}, ease:"none"}, 0);`);
183
+ const audio = music ? `<audio id="bgm" src="assets/media/${music}" data-start="0" data-duration="${TOTAL}" data-track-index="9"></audio>` : "";
93
184
  const html = `<!doctype html><html lang="ko"><head><meta charset="UTF-8"/><meta name="viewport" content="width=${W}, height=${H}"/><title>${esc(TITLE)}</title><script src="assets/gsap.min.js"></script><style>
94
185
  @font-face{font-family:"Pretendard";src:url("assets/fonts/Pretendard-Bold.woff2") format("woff2");font-weight:700}
95
186
  @font-face{font-family:"Pretendard";src:url("assets/fonts/Pretendard-ExtraBold.woff2") format("woff2");font-weight:800}
96
187
  @font-face{font-family:"Pretendard";src:url("assets/fonts/Pretendard-Black.woff2") format("woff2");font-weight:900}
97
188
  *{margin:0;padding:0;box-sizing:border-box}
98
189
  #root{position:relative;width:100%;height:100%;overflow:hidden;background:#000;font-family:"Pretendard",sans-serif;color:#F2F1EE;word-break:keep-all}
99
- .med{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;overflow:hidden}
190
+ .med{position:absolute;inset:0;width:100%;height:100%;overflow:hidden}
191
+ .g{position:absolute;inset:0;overflow:hidden}
100
192
  .kb{width:100%;height:100%;object-fit:cover;display:block}
101
- .shade{z-index:2;position:absolute;left:0;right:0;bottom:0;height:46%;background:linear-gradient(to top,rgba(0,0,0,.78),rgba(0,0,0,0));pointer-events:none}
193
+ .fill{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;filter:blur(42px) saturate(1.2);opacity:.85}
194
+ .dim{position:absolute;inset:0;background:rgba(0,0,0,.42)}
195
+ .frame{position:absolute;left:0;right:0;top:420px;height:960px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.55)}
196
+ .frame img{width:100%;height:100%;object-fit:cover;display:block}
197
+ .shade{z-index:3;position:absolute;left:0;right:0;bottom:0;height:42%;background:linear-gradient(to top,rgba(0,0,0,.82),rgba(0,0,0,0));pointer-events:none}
102
198
  .top{z-index:5;position:absolute;left:56px;right:56px;top:72px;display:flex;align-items:center;gap:18px;font-weight:800;font-size:34px;text-shadow:0 2px 12px rgba(0,0,0,.6)}
103
199
  .top b{color:#E95047;font-weight:800}
104
- .capw{z-index:4;position:absolute;left:64px;right:64px;bottom:250px}
105
- .cap{font-weight:800;font-size:64px;line-height:1.25;letter-spacing:-0.02em;text-shadow:0 3px 16px rgba(0,0,0,.7)}
106
- .card{z-index:6;position:absolute;inset:0;background:#000;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:0 80px}
107
- .card h1{margin-top:60px;font-weight:900;font-size:104px;line-height:1.1;letter-spacing:-0.03em}
108
- .card p{margin-top:28px;font-weight:700;font-size:40px;color:rgba(242,241,238,.62)}
200
+ .capw{z-index:4;position:absolute;left:64px;right:64px;bottom:210px}
201
+ .no{font-weight:800;font-size:34px;letter-spacing:.12em;color:#E95047}
202
+ .no i{font-style:normal;color:rgba(242,241,238,.55)}
203
+ .cap{margin-top:18px;font-weight:900;font-size:76px;line-height:1.18;letter-spacing:-0.03em;text-shadow:0 3px 18px rgba(0,0,0,.7)}
204
+ .w{display:inline-block}
205
+ .rule{margin-top:26px;width:120px;height:8px;background:#E95047;transform-origin:0 50%}
206
+ .wipe{z-index:8;position:absolute;inset:0;background:#E95047}
207
+ .card{z-index:7;position:absolute;inset:0;background:#000;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:0 80px}
208
+ .kick{margin-bottom:56px;font-weight:800;font-size:32px;letter-spacing:.28em;color:#E95047}
209
+ .card h1{margin-top:56px;font-weight:900;font-size:100px;line-height:1.12;letter-spacing:-0.03em}
210
+ .card h1 span{display:inline-block}
211
+ .line{margin-top:40px;width:160px;height:8px;background:#E95047}
212
+ .card p{margin-top:28px;font-weight:700;font-size:42px;color:rgba(242,241,238,.66)}
109
213
  .card .red{color:#E95047}
110
- .bar{z-index:7;position:absolute;left:0;right:0;bottom:0;height:10px;background:rgba(242,241,238,.12)}
214
+ .bar{z-index:9;position:absolute;left:0;right:0;bottom:0;height:10px;background:rgba(242,241,238,.12)}
111
215
  .bar i{position:absolute;inset:0;background:#E95047;transform-origin:0 50%;display:block}
112
216
  </style></head><body>
113
217
  <div id="root" data-composition-id="main" data-start="0" data-width="${W}" data-height="${H}" data-duration="${TOTAL}">
114
- <section class="clip card" data-start="0" data-duration="${INTRO}" data-track-index="5">${mark(220, "im")}<h1 class="it">${esc(TITLE)}</h1>${SUB ? `<p class="it">${esc(SUB)}</p>` : ""}</section>
218
+ ${intro}
115
219
  ${clips}
116
220
  <div class="shade"></div>
117
221
  <div class="top">${mark(60)}<span>Elanous <b>·</b> ${esc(TITLE)}</span></div>
118
- <section class="clip card" data-start="${f(t)}" data-duration="${END}" data-track-index="5">${mark(240, "em")}<h1 class="et">Elanous</h1><p class="et">현장에서 바로 만든 영상</p><p class="et red">elanous.ai</p></section>
222
+ <div class="wipe"></div>
223
+ ${end}
119
224
  <div class="bar"><i></i></div>
225
+ ${audio}
120
226
  </div>
121
227
  <script>const tl = gsap.timeline({ paused: true });
122
228
  ${tw.join("\n")}
@@ -124,5 +230,6 @@ window.__timelines["main"] = tl;</script></body></html>`;
124
230
  writeFileSync(join(hf, "index.html"), html);
125
231
  writeFileSync(join(hf, "hyperframes.json"), JSON.stringify({ $schema: "https://hyperframes.heygen.com/schema/hyperframes.json", paths: { assets: "assets" }, media: { autoProxy: true } }, null, 2) + "\n");
126
232
  writeFileSync(join(hf, "meta.json"), JSON.stringify({ id: "reel", name: "reel" }, null, 2) + "\n");
127
- writeFileSync(join(out, "timeline.json"), JSON.stringify({ total: TOTAL, items: items.map(({ f: file, video, start, dur, cap }) => ({ file, video, start, dur: f(dur), cap })) }, null, 2));
128
- console.log(`reel: ${items.length} items · ${TOTAL}s · ${items.filter((i) => i.cap).length} captions → ${hf}`);
233
+ writeFileSync(join(out, "timeline.json"), JSON.stringify({ total: TOTAL, part: PART, title: TITLE, sub: SUB, music: Boolean(music),
234
+ vision: { enabled: VISION, calls: jobs.length, captioned: items.filter((i) => i.vcap).length, fallback: items.filter((i) => !i.video && !caps[i.f] && !i.vcap).length, ms: visionMs }, items: items.map(({ f: file, video, start, dur, cap, vcap }) => ({ file, video, start, dur: f(dur), cap, vision: Boolean(vcap) })) }, null, 2));
235
+ console.log(`reel: ${items.length} items · ${TOTAL}s · ${items.filter((i) => i.vcap).length} vision captions · music ${music ? "on" : "off"} → ${hf}`);
@@ -1,8 +1,9 @@
1
1
  #!/bin/zsh
2
2
  # Field reel in one command: folder of phone photos/clips → <folder>/reel/<name>-9x16.mp4 · prints the elapsed seconds.
3
+ # The layout/contrast check is advisory: phone photos behind captions often miss 3:1, and a failed check must not stop the reel.
3
4
  # Usage: zsh reel.sh <folder> [--title "…"] [--sub "…"] (captions: <folder>/captions.txt — `파일 | 자막`)
4
5
  E=${0:A:h}; F=${1:A}; shift
5
6
  [[ -d $F ]] || { print -u2 "reel: no folder $F"; exit 2 }
6
7
  T0=$(date +%s)
7
- rm -rf $F/reel && node $E/reel.mjs $F "$@" && (cd $F/reel/hf && npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} check >../render.log 2>&1 && npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} render --fps 30 --workers 4 --output ../reel-9x16.mp4 >>../render.log 2>&1) || { print -u2 "reel: FAILED — see $F/reel/render.log"; exit 1 }
8
+ rm -rf $F/reel && node $E/reel.mjs $F "$@" && (cd $F/reel/hf && { npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} check >../render.log 2>&1 || print "reel: check reported issues (continuing — user photos vary; see above)" >>../render.log }; npx -y hyperframes@${HYPERFRAMES_VERSION:-0.8.95} render --fps 30 --workers 4 --output ../reel-9x16.mp4 >>../render.log 2>&1) || { print -u2 "reel: FAILED — see $F/reel/render.log"; exit 1 }
8
9
  print "reel: $F/reel/reel-9x16.mp4 · $(( $(date +%s) - T0 ))s"
@@ -5,7 +5,7 @@
5
5
  "name": "Seulki",
6
6
  "model": "eleven_v3"
7
7
  },
8
- "sources": "docs/marketing (LAUNCH-0.2.5 초안) · PR #21849 #21887 #21878 #21855 #22054 #21908 #21995 #22001 — 전부 컷 e24cb6e 안 MERGED",
8
+ "sources": "elanous 0.2.5 release notes — https://github.com/ElanvitalAI/elanous/releases/tag/v0.2.5",
9
9
  "intro": {
10
10
  "beats": [
11
11
  {
@@ -123,7 +123,7 @@ requires: []
123
123
  | 다음 | 언제 | 어떻게 |
124
124
  |---|---|---|
125
125
  | 🧹 **`first-principles`** | 요구가 많이 나왔을 때 | ⭐ ***캐낸 다음엔 «지운다»*** — 5단계 ①의심 ②삭제 가 순서상 여기다 |
126
- | 🎯 **골 문면** (`docs/goals/`) | 하니스가 구현할 일일 때 | 카드의 `S`·`C`·`Q` 가 그대로 ask 의 세 칸이 된다 |
126
+ | 🎯 **골 문면** (골 파일) | 하니스가 구현할 일일 때 | 카드의 `S`·`C`·`Q` 가 그대로 ask 의 세 칸이 된다 |
127
127
  | 📄 **문서** (`docs/`) | 결정을 남길 일일 때 | `## ✅ 정해진 것` 표가 그대로 결정 기록이다 |
128
128
  | 🧑‍🤝‍🧑 **팀 짜기** | 일이 여럿으로 갈릴 때 | ⭐ 목표를 심은 뒤 *"이 일을 할 팀을 짜 보라"* — 인터뷰의 «응용»이다 |
129
129
 
@@ -14,7 +14,7 @@ flowchart TD
14
14
  H{nonCodeOutcome exists?}:::gate
15
15
  I[observe deploy-no-changes<br/>return { ok:false, kind:none }]:::fail
16
16
  J{Evidence is artifact-created?}:::gate
17
- K{PR #8731 · 2026-08-13<br/>existsSync(resolve(cwd, ref))?}:::gate
17
+ K{2026-08-13<br/>existsSync(resolve(cwd, ref))?}:::gate
18
18
  L[observe deploy-noncode-blocked<br/>reason: artifact-created-ref-missing<br/>then deploy-no-changes]:::fail
19
19
  M[Do not treat ref as a file<br/>published-url / execution-completed]:::data
20
20
  N[observe deploy-noncode<br/>return terminal:<br/>published or executed]:::pass
package/src/a2a/server.ts CHANGED
@@ -91,6 +91,8 @@ export function startA2AServer(opts: A2AServerOptions): Bun.Server<undefined> {
91
91
  history ??= new DaemonSessionHistory();
92
92
  const result = await (opts.runDaemonTurn ?? runDaemonPromptTurn)({
93
93
  history,
94
+ // A peer agent delegates work, not small talk: never drop its tools for a short message (#22430 fast path).
95
+ fastPathEnabled: false,
94
96
  request: {
95
97
  sessionId: contextId,
96
98
  userText: text,
@@ -0,0 +1,30 @@
1
+ // EN10a — ACP auth methods: a client that runs terminals gets a `terminal` method (it re-launches `elanous --acp-server --login`);
2
+ // an older client gets an agent method carrying Zed's `terminal-auth` meta, so «sign-in needed → run elanous login in a terminal» shows either way.
3
+ import type { AuthMethod, ClientCapabilities } from '@agentclientprotocol/sdk';
4
+
5
+ export const ACP_LOGIN_METHOD_ID = 'elanous-login';
6
+ /** Appended to the configured agent invocation (`elanous --acp-server`) by a terminal-auth client. */
7
+ export const ACP_LOGIN_ARG = '--login';
8
+ /** What the login step runs (the ChatGPT/Codex subscription sign-in). */
9
+ export const ACP_LOGIN_SUBCOMMAND = ['login', 'openai-codex'] as const;
10
+
11
+ export interface AcpLaunch { command: string; args: string[] }
12
+
13
+ /** How this process was started (bun + script, or a compiled binary) so a client can start the same thing. */
14
+ export function currentLaunch(argv: readonly string[] = process.argv, execPath = process.execPath): AcpLaunch {
15
+ const script = argv[1];
16
+ return script && /\.(?:m?js|ts)$/.test(script) ? { command: execPath, args: [script] } : { command: execPath, args: [] };
17
+ }
18
+
19
+ export function acpAuthMethods(client: ClientCapabilities | null | undefined, launch: AcpLaunch = currentLaunch()): AuthMethod[] {
20
+ const name = 'Sign in to elanous';
21
+ if (client?.auth?.terminal === true) {
22
+ return [{ type: 'terminal', id: ACP_LOGIN_METHOD_ID, name, description: 'Opens a terminal to sign in with your ChatGPT (Codex) subscription', args: [ACP_LOGIN_ARG] }];
23
+ }
24
+ return [{
25
+ id: ACP_LOGIN_METHOD_ID,
26
+ name,
27
+ description: `Run \`elanous ${ACP_LOGIN_SUBCOMMAND.join(' ')}\` in a terminal, then try again`,
28
+ _meta: { 'terminal-auth': { command: launch.command, args: [...launch.args, ...ACP_LOGIN_SUBCOMMAND], label: 'elanous login' } },
29
+ }];
30
+ }
@@ -0,0 +1,30 @@
1
+ import type { ClientCapabilities } from '@agentclientprotocol/sdk';
2
+
3
+ /** The server's sessionPeers registry remains the source of truth for which
4
+ * connections are attached to a session. Awareness belongs to the connection,
5
+ * not the session: a plain client can load an aware client's session. */
6
+ export class ElanousAwarePeers<Peer extends object> {
7
+ private readonly aware = new WeakSet<Peer>();
8
+
9
+ constructor(private readonly sessionPeers: ReadonlyMap<string, ReadonlySet<Peer>>) {}
10
+
11
+ markPeer(connectionOrSessionId: Peer, clientCapabilities: ClientCapabilities | undefined | null): void {
12
+ const elanous = (clientCapabilities?._meta as Record<string, unknown> | undefined)?.elanous;
13
+ if (elanous !== null && typeof elanous === 'object' && !Array.isArray(elanous)) {
14
+ this.aware.add(connectionOrSessionId);
15
+ } else {
16
+ this.aware.delete(connectionOrSessionId);
17
+ }
18
+ }
19
+
20
+ isPeerAware(peer: Peer): boolean {
21
+ return this.aware.has(peer);
22
+ }
23
+
24
+ isElanousAware(sessionId: string): boolean {
25
+ for (const peer of this.sessionPeers.get(sessionId) ?? []) {
26
+ if (this.isPeerAware(peer)) return true;
27
+ }
28
+ return false;
29
+ }
30
+ }