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
@@ -0,0 +1,254 @@
1
+ import { closeSync, existsSync, fsyncSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
2
+ import { createHash, randomUUID } from 'node:crypto';
3
+ import { dirname, join } from 'node:path';
4
+ import { debug } from '../../debug/log.js';
5
+ import { effectiveInstanceRoot } from '../../instance/resolve.js';
6
+ import { submitIntakeWork } from '../../intake-plane/submit-intake-work.js';
7
+ import { parseSeatAddress, resolveSeat } from '../../seat-address/seat-address.js';
8
+ import trackData from '../../../scripts/coord-tracks.json';
9
+ import { jsonResponse } from './json-response.js';
10
+
11
+ export const SEAT_REQUESTS_PATH = '/v1/seat-requests';
12
+
13
+ interface SeatRequestItem {
14
+ receiptId: string;
15
+ seat: string;
16
+ text: string;
17
+ queuedAt: string;
18
+ status: 'queued';
19
+ }
20
+
21
+ interface PendingRequest {
22
+ status: 'pending';
23
+ key: string;
24
+ seat: string;
25
+ text: string;
26
+ queuedAt: string;
27
+ ref: string;
28
+ }
29
+
30
+ interface RejectedRequest extends Omit<PendingRequest, 'status'> {
31
+ status: 'rejected';
32
+ }
33
+
34
+ type RecordEntry = (SeatRequestItem & { key: string }) | PendingRequest | RejectedRequest;
35
+
36
+ export interface SeatRequestsDeps {
37
+ root?: () => string;
38
+ submit?: typeof submitIntakeWork;
39
+ now?: () => string;
40
+ append?: (path: string, entry: RecordEntry) => void;
41
+ }
42
+
43
+ const seats = trackData.tracks.filter((entry): entry is typeof entry & { title: string } => 'title' in entry)
44
+ .map(({ id, title }) => ({ id, title }));
45
+ const inFlight = new Map<string, { payload: string; task: Promise<Response> }>();
46
+
47
+ function unknownSeat(): Response {
48
+ debug.log('seat-address.pwa', 'rejected', { reason: 'unknown-seat' });
49
+ return jsonResponse({ error: 'unknown-seat', seats }, 400);
50
+ }
51
+
52
+ function file(root: string): string {
53
+ return join(root, 'seat-requests', 'requests.jsonl');
54
+ }
55
+
56
+ function append(path: string, entry: RecordEntry): void {
57
+ mkdirSync(dirname(path), { recursive: true });
58
+ // Atomically replace the journal so a failed write cannot leave a truncated JSON line.
59
+ const previous = existsSync(path) ? readFileSync(path, 'utf8') : '';
60
+ const temp = `${path}.${randomUUID()}.tmp`;
61
+ try {
62
+ writeFileSync(temp, `${previous}${JSON.stringify(entry)}\n`);
63
+ const fd = openSync(temp, 'r');
64
+ try { fsyncSync(fd); } finally { closeSync(fd); }
65
+ renameSync(temp, path);
66
+ } catch (error) {
67
+ try { unlinkSync(temp); } catch { /* no temp file to remove */ }
68
+ throw error;
69
+ }
70
+ }
71
+
72
+ function records(path: string): Map<string, RecordEntry> {
73
+ let data: string;
74
+ try { data = readFileSync(path, 'utf8'); }
75
+ catch (error) {
76
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT' && !existsSync(path)) data = '';
77
+ else throw error;
78
+ }
79
+ const entries = new Map<string, RecordEntry>();
80
+ for (const line of data.split('\n')) {
81
+ if (!line) continue;
82
+ const entry = JSON.parse(line) as RecordEntry;
83
+ if (typeof entry.key !== 'string' || typeof entry.seat !== 'string' || typeof entry.text !== 'string'
84
+ || typeof entry.queuedAt !== 'string' || !['pending', 'queued', 'rejected'].includes(entry.status)
85
+ || (entry.status === 'queued' && typeof entry.receiptId !== 'string')
86
+ || (entry.status !== 'queued' && typeof entry.ref !== 'string')) {
87
+ throw new Error('invalid seat request journal');
88
+ }
89
+ entries.set(entry.key, entry);
90
+ }
91
+ const dir = join(dirname(path), 'outcomes');
92
+ let names: string[];
93
+ try { names = readdirSync(dir); }
94
+ catch (error) {
95
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return entries;
96
+ throw error;
97
+ }
98
+ for (const name of names) {
99
+ if (!/^[a-f0-9]{64}\.json$/.test(name)) continue;
100
+ const item = JSON.parse(readFileSync(join(dir, name), 'utf8')) as RecordEntry;
101
+ if (item.key !== name.slice(0, -5) || (item.status !== 'queued' && item.status !== 'rejected')) {
102
+ throw new Error('invalid seat request outcome');
103
+ }
104
+ const current = entries.get(item.key);
105
+ if (current?.status === 'queued') continue;
106
+ if (current?.status === 'pending') {
107
+ if (item.status === 'rejected' && current.ref !== item.ref) continue;
108
+ if (item.status === 'queued' && current.queuedAt !== item.queuedAt) continue;
109
+ }
110
+ entries.set(item.key, item);
111
+ }
112
+ return entries;
113
+ }
114
+
115
+ function outcomeFile(path: string, key: string): string {
116
+ return join(dirname(path), 'outcomes', `${key}.json`);
117
+ }
118
+
119
+ function persistOutcome(path: string, entry: RecordEntry, write: typeof append): void {
120
+ try {
121
+ write(path, entry);
122
+ const sidecar = outcomeFile(path, entry.key);
123
+ if (existsSync(sidecar)) unlinkSync(sidecar);
124
+ } catch {
125
+ // The primary journal may be unavailable while the outcome is already committed
126
+ // to the intake queue. Keep a durable sidecar rather than a process-local receipt.
127
+ const target = outcomeFile(path, entry.key);
128
+ mkdirSync(dirname(target), { recursive: true });
129
+ const temp = `${target}.${randomUUID()}.tmp`;
130
+ try {
131
+ writeFileSync(temp, JSON.stringify(entry));
132
+ const fd = openSync(temp, 'r');
133
+ try { fsyncSync(fd); } finally { closeSync(fd); }
134
+ renameSync(temp, target);
135
+ } catch (error) {
136
+ try { unlinkSync(temp); } catch { /* no temp file */ }
137
+ throw error;
138
+ }
139
+ }
140
+ }
141
+
142
+ function receipt(item: SeatRequestItem): Response {
143
+ return jsonResponse({ receiptId: item.receiptId, seat: item.seat, queuedAt: item.queuedAt }, 202);
144
+ }
145
+
146
+ function unavailable(): Response {
147
+ debug.log('seat-address.pwa', 'rejected', { reason: 'journal-unavailable' });
148
+ return jsonResponse({ error: 'enqueue-failed' }, 503);
149
+ }
150
+
151
+ export async function handleSeatRequests(req: Request, deps: SeatRequestsDeps = {}): Promise<Response> {
152
+ const path = file((deps.root ?? effectiveInstanceRoot)());
153
+ if (req.method === 'GET') {
154
+ const params = new URL(req.url).searchParams;
155
+ const requestedSeat = params.get('seat');
156
+ const seat = requestedSeat ? resolveSeat(requestedSeat) : undefined;
157
+ if (requestedSeat && !seat) return unknownSeat();
158
+ const rawLimit = params.get('limit');
159
+ const limit = rawLimit === null ? 20 : Number(rawLimit);
160
+ if (!Number.isInteger(limit) || limit < 1 || limit > 100) return jsonResponse({ error: 'bad_request' }, 400);
161
+ try {
162
+ const items = [...records(path).values()].filter((entry): entry is RecordEntry & SeatRequestItem => entry.status === 'queued')
163
+ .reverse().filter((item) => !seat || item.seat === seat.id).slice(0, limit)
164
+ .map(({ receiptId, seat: id, text, queuedAt, status }) => ({ receiptId, seat: id, text, queuedAt, status }));
165
+ return jsonResponse({ items, seats });
166
+ } catch { return unavailable(); }
167
+ }
168
+ if (req.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405);
169
+
170
+ let body: unknown;
171
+ try { body = await req.json(); }
172
+ catch { return jsonResponse({ error: 'bad_request' }, 400); }
173
+ if (!body || typeof body !== 'object' || Array.isArray(body)) return jsonResponse({ error: 'bad_request' }, 400);
174
+ const input = body as Record<string, unknown>;
175
+ if (typeof input.text !== 'string' || !input.text.trim()
176
+ || (input.seat !== undefined && typeof input.seat !== 'string')) {
177
+ debug.log('seat-address.pwa', 'rejected', { reason: 'invalid-body' });
178
+ return jsonResponse({ error: 'bad_request' }, 400);
179
+ }
180
+ // An implicit address must start the text; parseSeatAddress also accepts later lines.
181
+ const parsed = input.text.startsWith('@') ? parseSeatAddress(input.text) : null;
182
+ const prefix = parsed ? `@${parsed.seats.join(',')}` : '';
183
+ const firstAddress = parsed && input.text.startsWith(prefix)
184
+ && (input.text.length === prefix.length || /[ \t\r\n]/.test(input.text[prefix.length]!)) ? parsed : null;
185
+ const address = input.seat ?? (firstAddress?.seats.length === 1 ? firstAddress.seats[0] : undefined);
186
+ const seat = address ? resolveSeat(address) : undefined;
187
+ if (!seat) return unknownSeat();
188
+ const text = input.seat === undefined ? firstAddress!.body.trim() : input.text.trim();
189
+ if (!text) {
190
+ debug.log('seat-address.pwa', 'rejected', { reason: 'empty-text', seat: seat.id });
191
+ return jsonResponse({ error: 'bad_request' }, 400);
192
+ }
193
+ debug.log('seat-address.pwa', 'parsed', { seat: seat.id, textLength: text.length });
194
+ const now = (deps.now ?? (() => new Date().toISOString()))();
195
+ const header = req.headers.get('idempotency-key');
196
+ if (header !== null && (!header.trim() || header.length > 200)) return jsonResponse({ error: 'bad_request' }, 400);
197
+ const payload = `${seat.id}\0${text}`;
198
+ const key = header === null ? createHash('sha256').update(randomUUID()).digest('hex')
199
+ : createHash('sha256').update(`client\0${header}`).digest('hex');
200
+ const lock = `${path}\0${key}`;
201
+ const pending = inFlight.get(lock);
202
+ if (pending) {
203
+ if (pending.payload !== payload) return jsonResponse({ error: 'idempotency-conflict' }, 409);
204
+ return pending.task.then((response) => response.clone());
205
+ }
206
+ // Install the lock before starting disk or queue work; a second caller must
207
+ // wait for the first instead of independently submitting the same key.
208
+ let start!: () => void;
209
+ const gate = new Promise<void>((resolve) => { start = resolve; });
210
+ const task = (async (): Promise<Response> => {
211
+ await gate;
212
+ let existing: RecordEntry | undefined;
213
+ try { existing = records(path).get(key); }
214
+ catch { return unavailable(); }
215
+ if (existing) {
216
+ if (existing.seat !== seat.id || existing.text !== text) return jsonResponse({ error: 'idempotency-conflict' }, 409);
217
+ if (existing.status === 'queued') return receipt(existing);
218
+ }
219
+ const intent: PendingRequest = existing?.status === 'pending'
220
+ ? existing : { key, seat: seat.id, text, queuedAt: now, status: 'pending', ref: `pwa:${randomUUID()}` };
221
+ if (existing?.status !== 'pending') {
222
+ try {
223
+ (deps.append ?? append)(path, intent);
224
+ if (existing?.status === 'rejected') {
225
+ const sidecar = outcomeFile(path, key);
226
+ if (existsSync(sidecar)) unlinkSync(sidecar);
227
+ }
228
+ } catch { return unavailable(); }
229
+ }
230
+ let result: Awaited<ReturnType<typeof submitIntakeWork>>;
231
+ try {
232
+ result = await (deps.submit ?? submitIntakeWork)({
233
+ text: `@${seat.title ?? seat.id} ${text}`,
234
+ track: 'graph',
235
+ origin: { kind: 'external', ledgerSource: 'pwa', provider: 'other', ref: intent.ref, reportTo: { channel: 'pwa' } },
236
+ });
237
+ } catch { return unavailable(); }
238
+ if (!result.ok || result.track !== 'graph') {
239
+ debug.log('seat-address.pwa', 'rejected', { reason: result.ok ? 'unexpected-track' : result.reason, seat: seat.id });
240
+ try { persistOutcome(path, { ...intent, status: 'rejected' }, deps.append ?? append); }
241
+ catch { return unavailable(); }
242
+ return unavailable();
243
+ }
244
+ const item: RecordEntry & SeatRequestItem = { key, receiptId: result.acceptanceId, seat: seat.id, text, queuedAt: intent.queuedAt, status: 'queued' };
245
+ try { persistOutcome(path, item, deps.append ?? append); }
246
+ catch { return unavailable(); }
247
+ debug.log('seat-address.pwa', 'enqueued', { seat: seat.id, receiptId: item.receiptId });
248
+ return receipt(item);
249
+ })();
250
+ inFlight.set(lock, { payload, task });
251
+ start();
252
+ try { return await task; }
253
+ finally { inFlight.delete(lock); }
254
+ }
@@ -0,0 +1,91 @@
1
+ // SC1 — 앱 안 «공유용 캡처»(beta) 받는 쪽. PWA 가 화면을 가린 뒤 PNG 로 올린다(티저·현장·보고용).
2
+ // POST /v1/captures body = image/png(≤ 8 MiB) → { id, bytes, droppedChunks }
3
+ // GET /v1/captures → { items: [{ id, bytes, at }] } (최신 먼저 · 50개)
4
+ // GET /v1/captures/<id> → image/png
5
+ // ⛔ 두 번째 방어: 받은 PNG 에서 «그림 아닌» 덩어리(tEXt·iTXt·zTXt·eXIf·iCCP·tIME 등 메타)를 전부 걷고 저장한다 —
6
+ // 화면 쪽 가림이 놓친 것이 파일 메타로 새지 않게. 남기는 것은 그림을 그리는 데 필요한 덩어리뿐이다.
7
+ import { randomBytes } from 'node:crypto';
8
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ import { debug } from '../../debug/log.js';
11
+ import { getElanousConfigDir } from '../../elanous-config-dir.js';
12
+ import { jsonResponse } from './json-response.js';
13
+ import { checkAuth, type MetaApiOpts } from './meta-api.js';
14
+
15
+ export const SHARE_CAPTURES_PATH = '/v1/captures';
16
+ const MAX_BYTES = 8 * 1024 * 1024;
17
+ const PNG_SIGNATURE = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
18
+ /** Chunks needed to draw the picture. Everything else (text, EXIF, ICC profile, time…) is dropped. */
19
+ const KEEP = new Set(['IHDR', 'PLTE', 'IDAT', 'IEND', 'tRNS', 'gAMA', 'cHRM', 'sRGB', 'pHYs']);
20
+ const ID = /^share-\d{8}-\d{6}-[0-9a-f]{6}$/;
21
+
22
+ export interface ShareCapturesDeps { configDir?: string; now?: () => Date }
23
+
24
+ /** Rebuild a PNG with only drawing chunks. Returns null when it is not a well-formed PNG. */
25
+ export function stripPngMetadata(input: Uint8Array): { png: Buffer; dropped: string[] } | null {
26
+ const buf = Buffer.from(input);
27
+ if (buf.length < 8 + 12 || !buf.subarray(0, 8).equals(PNG_SIGNATURE)) return null;
28
+ const parts: Buffer[] = [PNG_SIGNATURE];
29
+ const dropped: string[] = [];
30
+ let offset = 8;
31
+ let sawHeader = false;
32
+ let sawEnd = false;
33
+ while (offset + 12 <= buf.length) {
34
+ const length = buf.readUInt32BE(offset);
35
+ const type = buf.subarray(offset + 4, offset + 8).toString('latin1');
36
+ const end = offset + 12 + length;
37
+ if (end > buf.length || !/^[A-Za-z]{4}$/.test(type)) return null;
38
+ if (!sawHeader && type !== 'IHDR') return null;
39
+ sawHeader = true;
40
+ if (KEEP.has(type)) parts.push(buf.subarray(offset, end)); else dropped.push(type);
41
+ offset = end;
42
+ if (type === 'IEND') { sawEnd = true; break; }
43
+ }
44
+ if (!sawEnd) return null;
45
+ return { png: Buffer.concat(parts), dropped };
46
+ }
47
+
48
+ function capturesDir(deps: ShareCapturesDeps): string {
49
+ return join(deps.configDir ?? getElanousConfigDir(), 'captures');
50
+ }
51
+
52
+ function newId(now: Date): string {
53
+ const p = (n: number) => String(n).padStart(2, '0');
54
+ return `share-${now.getFullYear()}${p(now.getMonth() + 1)}${p(now.getDate())}-${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}-${randomBytes(3).toString('hex')}`;
55
+ }
56
+
57
+ export async function handleShareCaptures(req: Request, metaApi: MetaApiOpts, deps: ShareCapturesDeps = {}): Promise<Response> {
58
+ if (!checkAuth(req, metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
59
+ const pathname = new URL(req.url).pathname;
60
+ const dir = capturesDir(deps);
61
+
62
+ if (pathname === SHARE_CAPTURES_PATH && req.method === 'POST') {
63
+ const declared = Number(req.headers.get('content-length') ?? 0);
64
+ if (declared > MAX_BYTES) return jsonResponse({ error: 'too-large', maxBytes: MAX_BYTES }, 413);
65
+ const body = new Uint8Array(await req.arrayBuffer());
66
+ if (body.byteLength > MAX_BYTES) return jsonResponse({ error: 'too-large', maxBytes: MAX_BYTES }, 413);
67
+ const cleaned = stripPngMetadata(body);
68
+ if (!cleaned) return jsonResponse({ error: 'not-png' }, 400);
69
+ const id = newId((deps.now ?? (() => new Date()))());
70
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
71
+ writeFileSync(join(dir, `${id}.png`), cleaned.png, { mode: 0o600, flag: 'wx' });
72
+ debug.log('share-capture', 'stored', { id, bytes: cleaned.png.length, dropped: cleaned.dropped });
73
+ return jsonResponse({ id, bytes: cleaned.png.length, droppedChunks: cleaned.dropped }, 201);
74
+ }
75
+
76
+ if (pathname === SHARE_CAPTURES_PATH && req.method === 'GET') {
77
+ const items = existsSync(dir)
78
+ ? readdirSync(dir).filter((f) => f.endsWith('.png') && ID.test(f.slice(0, -4))).sort().reverse().slice(0, 50)
79
+ .map((f) => { const s = statSync(join(dir, f)); return { id: f.slice(0, -4), bytes: s.size, at: s.mtime.toISOString() }; })
80
+ : [];
81
+ return jsonResponse({ items });
82
+ }
83
+
84
+ const id = pathname.slice(SHARE_CAPTURES_PATH.length + 1);
85
+ if (req.method === 'GET' && ID.test(id)) {
86
+ const file = join(dir, `${id}.png`);
87
+ if (!existsSync(file)) return jsonResponse({ error: 'not-found' }, 404);
88
+ return new Response(readFileSync(file), { headers: { 'content-type': 'image/png', 'cache-control': 'no-store' } });
89
+ }
90
+ return jsonResponse({ error: 'not-found' }, 404);
91
+ }
@@ -0,0 +1,56 @@
1
+ // SK2 (PWA) — 색인이 못 읽은 스킬을 PWA 에도 «안내 ⊕ 확인 뒤 수리»로.
2
+ // GET /v1/skills/problems → { items: [{ id, name, code, fixable, hint? }] } — 경로 대신 해시 id 만 내보낸다
3
+ // POST /v1/skills/problems/repair → body { id } · 고칠 수 있는 것만 고친다(남의 도구 파일 — PWA 가 먼저 확인을 받는다)
4
+ import { createHash } from 'node:crypto';
5
+ import { debug } from '../../debug/log.js';
6
+ import { getSkillIndex, skillIndexProblems, type SkillIndexProblem } from '../../skills/index.js';
7
+ import { applySkillRepair, planSkillRepair } from '../../skills/repair.js';
8
+ import { jsonResponse } from './json-response.js';
9
+ import { checkAuth, type MetaApiOpts } from './meta-api.js';
10
+
11
+ export const SKILL_PROBLEMS_PATH = '/v1/skills/problems';
12
+
13
+ export interface SkillProblemsDeps {
14
+ problems?: () => SkillIndexProblem[];
15
+ }
16
+
17
+ function problemId(p: SkillIndexProblem): string {
18
+ return createHash('sha256').update(`${p.dir}\u0000${p.name}`).digest('hex').slice(0, 16);
19
+ }
20
+
21
+ function current(deps: SkillProblemsDeps): SkillIndexProblem[] {
22
+ if (deps.problems) return deps.problems();
23
+ getSkillIndex();
24
+ return skillIndexProblems();
25
+ }
26
+
27
+ export async function handleSkillProblems(req: Request, metaApi: MetaApiOpts, deps: SkillProblemsDeps = {}): Promise<Response> {
28
+ if (!checkAuth(req, metaApi)) return jsonResponse({ error: 'unauthorized' }, 401);
29
+ const pathname = new URL(req.url).pathname;
30
+ if (pathname === SKILL_PROBLEMS_PATH) {
31
+ if (req.method !== 'GET') return jsonResponse({ error: 'method-not-allowed' }, 405);
32
+ const items = current(deps).map((p) => {
33
+ const plan = planSkillRepair(p);
34
+ return {
35
+ id: problemId(p), name: p.name, code: p.code,
36
+ fixable: plan.kind === 'fixable' || plan.kind === 'fixable-permission',
37
+ ...(plan.kind === 'permission' || plan.kind === 'manual' ? { hint: plan.hint.replace(p.dir, '…') } : {}),
38
+ };
39
+ });
40
+ return jsonResponse({ items });
41
+ }
42
+ if (pathname === `${SKILL_PROBLEMS_PATH}/repair`) {
43
+ if (req.method !== 'POST') return jsonResponse({ error: 'method-not-allowed' }, 405);
44
+ let body: unknown;
45
+ try { body = await req.json(); } catch { return jsonResponse({ error: 'bad_request' }, 400); }
46
+ const id = body && typeof body === 'object' && typeof (body as { id?: unknown }).id === 'string' ? (body as { id: string }).id : '';
47
+ const problem = current(deps).find((p) => problemId(p) === id);
48
+ if (!problem) return jsonResponse({ error: 'not-found' }, 404);
49
+ const plan = planSkillRepair(problem);
50
+ if (plan.kind !== 'fixable' && plan.kind !== 'fixable-permission') return jsonResponse({ error: 'not-fixable', hint: plan.hint.replace(problem.dir, '…') }, 409);
51
+ const result = applySkillRepair(plan);
52
+ debug.log('skills.repair', 'pwa-request', { name: problem.name, ok: result.ok });
53
+ return result.ok ? jsonResponse({ ok: true, name: problem.name }) : jsonResponse({ ok: false, reason: result.reason }, 409);
54
+ }
55
+ return jsonResponse({ error: 'not-found' }, 404);
56
+ }
@@ -34,6 +34,7 @@ import { classifyPtyOwnerRunUsage, joinPtyLineage, resolveRunTermination, type P
34
34
  import { loadRunLedger, resolveFederatedRunLedgerDirectories, runLedgerDir } from '../../self-implement/run-ledger.js';
35
35
  import { requestRemotePtyControl, type PtyControlAction, type PtyControlPayload, type PtyControlRequestOptions, type PtyControlResult } from '../../pty-shell/pty-control-ipc.js';
36
36
  import { encodeSgrMouse, type PtyMouseInput } from '../../pty-shell/pty-mouse.js';
37
+ import { createRunningRunsBackground, type RunningRunsBackground } from './running-runs-background.js';
37
38
  import { queryRunningRuns, type RunningRunAssessment, type RunningRunPresence, type RunningRunStatus, type RunningRunsResult } from '../../self-implement/running-runs.js';
38
39
  // ⭐P4 §4-1 — 표시 폭(wide-char/CJK/emoji 인지) SSOT. PNG 캔버스 cols 파생용.
39
40
  import { cellWidth } from '../../ui/printer.js';
@@ -494,6 +495,12 @@ function runningRunsCacheFor(
494
495
  return binding.cache;
495
496
  }
496
497
 
498
+ let liveBackground: RunningRunsBackground | null = null;
499
+ function liveRunningRunsBackground(): RunningRunsBackground {
500
+ liveBackground ??= createRunningRunsBackground();
501
+ return liveBackground;
502
+ }
503
+
497
504
  const liveTerminalsListDeps: TerminalsListDeps = {
498
505
  ptyManifestTargets,
499
506
  listPtyManifestRows,
@@ -1054,17 +1061,26 @@ export function handleTerminalsList(req: Request, opts: MetaApiOpts, deps: Termi
1054
1061
  const resolveRunTerminationForRow = (runId: string): RunTermination => deps.runTerminated
1055
1062
  ? deps.runTerminated(runId, { includeTest })
1056
1063
  : 'ledger-indeterminate';
1057
- const runningRunsCache = runningRunsCacheFor(getRunningRuns, deps.now ?? Date.now);
1058
1064
  let runningRuns: RunningRunsResult | null = null;
1059
1065
  let runningRunsAgeMs = 0;
1060
1066
  let runningRunsQueryFailed = false;
1061
- try {
1062
- const cached = runningRunsCache({ includeTest });
1063
- runningRuns = cached.result;
1064
- runningRunsAgeMs = cached.ageMs;
1065
- } catch {
1066
- // Fail-soft and do not cache: subjects keep unknown assessments.
1067
- runningRunsQueryFailed = true;
1067
+ if (getRunningRuns === queryRunningRuns) {
1068
+ // Live daemon: never scan ledgers on this event loop (it froze web-terminal typing) — use the last snapshot a
1069
+ // separate process refreshed. Before the first one lands, subjects keep unknown assessments and the age is null.
1070
+ const snap = liveRunningRunsBackground().snapshot({ includeTest });
1071
+ runningRuns = snap.result;
1072
+ if (snap.ageMs === null) runningRunsQueryFailed = true;
1073
+ else runningRunsAgeMs = snap.ageMs;
1074
+ } else {
1075
+ const runningRunsCache = runningRunsCacheFor(getRunningRuns, deps.now ?? Date.now);
1076
+ try {
1077
+ const cached = runningRunsCache({ includeTest });
1078
+ runningRuns = cached.result;
1079
+ runningRunsAgeMs = cached.ageMs;
1080
+ } catch {
1081
+ // Fail-soft and do not cache: subjects keep unknown assessments.
1082
+ runningRunsQueryFailed = true;
1083
+ }
1068
1084
  }
1069
1085
  const ptyLessSubAgentCollection = collectPtyLessSubAgents(
1070
1086
  getLogStore,
@@ -0,0 +1,37 @@
1
+ import { statSync } from 'node:fs';
2
+ import type { LogInstanceView } from '../../mss/logging/instance-registry.js';
3
+ import type { LogStore } from '../../mss/logging/log-store.js';
4
+
5
+ /** Bounded, read-only fallback when a run's ledger is not on this host. */
6
+ export function scanStoresForRun(
7
+ runId: string,
8
+ views: readonly LogInstanceView[],
9
+ open: (view: LogInstanceView) => Pick<LogStore, 'queryTraceRun' | 'close'> | null,
10
+ opts: { sinceMs?: number; untilMs?: number; maxStores?: number; deadlineMs?: number; now?: () => number } = {},
11
+ ): { universe?: string; checked: number; truncated: boolean; unresolved?: boolean } {
12
+ const now = opts.now ?? Date.now;
13
+ const deadline = now() + (opts.deadlineMs ?? 3000);
14
+ const maxStores = opts.maxStores ?? 200;
15
+ const mtime = (view: LogInstanceView): number => {
16
+ try { return statSync(view.dbPath).mtimeMs; } catch { return -Infinity; }
17
+ };
18
+ const candidates = views.map((view) => ({ view, at: view.dbExists ? mtime(view) : -Infinity }))
19
+ .sort((a, b) => (b.at === a.at ? a.view.name.localeCompare(b.view.name) : b.at - a.at));
20
+ let checked = 0;
21
+ let unresolved = false;
22
+ for (const { view } of candidates) {
23
+ if (checked >= maxStores || now() >= deadline) return { checked, truncated: true };
24
+ checked++;
25
+ let store: ReturnType<typeof open> = null;
26
+ try {
27
+ if (!view.dbExists) continue;
28
+ store = open(view);
29
+ if (!store) { unresolved = true; continue; }
30
+ if (store.queryTraceRun(runId, { sinceMs: opts.sinceMs, untilMs: opts.untilMs, limit: 1 }).length) {
31
+ return { universe: view.name, checked, truncated: false };
32
+ }
33
+ } catch { unresolved = true; }
34
+ finally { try { store?.close(); } catch { /* read-only handle cleanup is best effort */ } }
35
+ }
36
+ return { checked, truncated: false, ...(unresolved ? { unresolved } : {}) };
37
+ }
@@ -8,6 +8,7 @@ import { activeStoreNames, parseSinceParam, type LogFabricDeps, registeredLogSto
8
8
  import { checkAuth, type MetaApiOpts } from './meta-api.js';
9
9
  import { jsonResponse } from './json-response.js';
10
10
  import { runLedgerDir, runLedgerPath } from '../../self-implement/run-ledger.js';
11
+ import { scanStoresForRun } from './trace-run-scan.js';
11
12
 
12
13
  export type TraceLevel = 'L0' | 'L1' | 'L2' | 'L3';
13
14
  export interface TraceEvent {
@@ -346,7 +347,8 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
346
347
  }
347
348
  if (!opened) return jsonResponse({ ok: false, error: 'log-store-unavailable', failedStores: failures }, 503);
348
349
  const runId = url.searchParams.get('runId');
349
- let resolution: { universe?: string; checked: number } | undefined;
350
+ let resolution: { universe?: string; checked: number; truncated?: boolean; unresolved?: boolean } | undefined;
351
+ let resolvedFrom: 'ledger' | 'log-scan' | undefined;
350
352
  if ((parsed.level === 'L2' || parsed.level === 'L3') && runId && !rows.some((row) => matches(row, url)) && !requestedUniverse) {
351
353
  // A store with no log db at all («has no log store yet» — e.g. the daemon's own tree-derived universe) cannot
352
354
  // hold the run, so it must not block the ledger lookup. A store that exists but failed to open/read still does:
@@ -356,6 +358,20 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
356
358
  try { resolution = findRunUniverse(runId, names, deps); }
357
359
  catch (error) { return jsonResponse({ ok: false, error: 'run-universe-unavailable', reason: error instanceof Error ? error.message : String(error) }, 503); }
358
360
  try { debug.log('nexus.trace', 'run-universe-resolved', { runId, universe: resolution.universe ?? 'not-found', checked: resolution.checked }); } catch { /* observation must not block reads */ }
361
+ if (resolution.universe) resolvedFrom = 'ledger';
362
+ else {
363
+ let views: LogInstanceView[];
364
+ try { views = (deps.instances ?? readLogInstances)(); }
365
+ catch (error) { return jsonResponse({ ok: false, error: 'run-universe-unavailable', reason: error instanceof Error ? error.message : String(error) }, 503); }
366
+ resolution = scanStoresForRun(runId, views, (view) => {
367
+ const store = (deps.openRemoteStore ?? ((v: LogInstanceView) => LogStore.openReadOnly(v.dbPath)))(view);
368
+ if (!store) return null;
369
+ return { queryTraceRun: (id, query) => store.queryTraceRun(id, query),
370
+ close: () => (deps.closeRemoteStore ?? ((s: LogStore) => s.close()))(store) };
371
+ }, { sinceMs: parsed.from, untilMs: parsed.to });
372
+ if (resolution.universe) resolvedFrom = 'log-scan';
373
+ try { debug.log('nexus.trace', 'run-universe-scanned', { runId, universe: resolution.universe ?? (resolution.truncated || resolution.unresolved ? 'unresolved' : 'not-found'), checked: resolution.checked, truncated: resolution.truncated }); } catch { /* observation must not block reads */ }
374
+ }
359
375
  if (resolution.universe && !names.includes(resolution.universe)) {
360
376
  const name = resolution.universe;
361
377
  names.push(name);
@@ -373,9 +389,9 @@ export function handleTrace(req: Request, opts: MetaApiOpts, deps: TraceDeps = {
373
389
  }
374
390
  }
375
391
  const filtered = rows.filter((row) => matches(row, url)).sort(sortRows);
376
- const trace = buildTrace(filtered.slice(0, parsed.limit), parsed.level, filtered.length > parsed.limit || candidateLimitReached || failures.length > 0);
392
+ const trace = buildTrace(filtered.slice(0, parsed.limit), parsed.level, filtered.length > parsed.limit || candidateLimitReached || failures.length > 0 || resolution?.truncated === true);
377
393
  try { (deps.queryLog ?? ((details) => debug.log('logs.trace', 'query', details)))({ stores: names, level: parsed.level, count: trace.events.length, truncated: trace.truncated, mode: 'rows', runs: trace.nodes.filter((node) => node.level === 'L1' && node.count > 0).length }); } catch { /* query logging must not block reads */ }
378
- return jsonResponse({ ok: true, ...trace, stores: names, registeredStores: registeredStoreCount(deps), ...(resolution ? resolution.universe ? { resolvedFrom: 'ledger' as const, runUniverse: resolution.universe } : { runUniverse: 'not-found' as const, checked: resolution.checked } : {}), ...(failures.length ? { failedStores: failures } : {}) }, 200);
394
+ return jsonResponse({ ok: true, ...trace, stores: names, registeredStores: registeredStoreCount(deps), ...(resolution ? resolution.universe ? { resolvedFrom, runUniverse: resolution.universe, ...(resolvedFrom === 'log-scan' ? { checked: resolution.checked } : {}) } : { runUniverse: resolution.truncated || resolution.unresolved ? 'unresolved' as const : 'not-found' as const, checked: resolution.checked } : {}), ...(failures.length ? { failedStores: failures } : {}) }, 200);
379
395
  } finally { closeStores(stores, deps); }
380
396
  }
381
397
 
@@ -2609,10 +2609,16 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2609
2609
  runTurnImpl,
2610
2610
  getBot: () => getBotRef.current,
2611
2611
  });
2612
+ // DEC-TG(대표 10-02) — decision cards in the owner's Discord DM. Filled once the bot exists (below).
2613
+ let decisionWire: import('../decisions/discord-decision-cards.js').DiscordDecisionWire | null = null;
2612
2614
  const composedOnMessage = async (
2613
2615
  ctx: import('../discord.js').DcIncoming,
2614
2616
  streamer?: import('../discord.js').DcMessageStreamer,
2615
2617
  ): Promise<string | void> => {
2618
+ if (decisionWire && ctx.text.trim() === '/decisions') {
2619
+ const { discordDecisionsCommand } = await import('../decisions/discord-decision-cards.js');
2620
+ return discordDecisionsCommand(decisionWire.service, ctx);
2621
+ }
2616
2622
  const voiceReply = await voiceWire.dispatchVoiceCommand(ctx);
2617
2623
  if (voiceReply !== null) return voiceReply;
2618
2624
  return selfOnMessage(ctx, streamer as never);
@@ -2626,13 +2632,17 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2626
2632
  getBot: () => getBotRef.current,
2627
2633
  allowedUsers: [...cfg.discord.allowedUsers],
2628
2634
  });
2635
+ const chatChannels = cfg.discord.chatChannels ?? [];
2636
+ debug.log('nexus.discord', 'chat-channels', { count: chatChannels.length });
2629
2637
  const handle = createNexusDiscordTriggerBot({
2630
2638
  token: discordToken,
2631
2639
  allowedUsers: [...cfg.discord.allowedUsers],
2640
+ ...(chatChannels.length ? { guildTextChannels: chatChannels } : {}),
2632
2641
  dispatch: (event) => workflowDaemon!.dispatchDiscord(event),
2633
2642
  onMessage: composedOnMessage,
2634
2643
  onInteraction: async (raw) => {
2635
- // C1 버튼 탭 우선(elanous-q: 소비) → 아니면 C3 슬래시.
2644
+ // DEC-TG 결정 카드(dec:/decmemo:) → C1 버튼 탭(elanous-q: 소비) → 아니면 C3 슬래시.
2645
+ if (decisionWire && await decisionWire.onInteraction(raw)) return;
2636
2646
  if (await questionRuntime.handleComponentInteraction(raw)) return;
2637
2647
  await slashWire.onInteraction(raw);
2638
2648
  },
@@ -2641,6 +2651,14 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2641
2651
  if (handle) {
2642
2652
  getBotRef.current = handle.bot;
2643
2653
  workflowDiscordBot = handle;
2654
+ if ((cfg.raw?.decisions as { cards?: unknown } | undefined)?.cards !== false) {
2655
+ try {
2656
+ const { attachDiscordDecisionCards } = await import('../decisions/discord-decision-cards.js');
2657
+ decisionWire = attachDiscordDecisionCards(handle.bot, cfg);
2658
+ } catch (err) {
2659
+ console.warn(`[nexus] decision cards (discord) not attached: ${err instanceof Error ? err.message : String(err)}`);
2660
+ }
2661
+ }
2644
2662
  void slashWire.registerCommands().catch((err: unknown) => {
2645
2663
  console.warn(`[nexus] slash registration failed: ${err instanceof Error ? err.message : String(err)}`);
2646
2664
  });
@@ -2916,6 +2934,15 @@ export async function runNexus(opts: RunNexusOptions = {}): Promise<RunNexusHand
2916
2934
  : readyCount === 1 ? readyServerIds[0] : undefined,
2917
2935
  readyCount,
2918
2936
  });
2937
+ // TERM1 ④ — the first web-terminal spawn used to pay the login-shell env capture (spawnSync · ~3 s with the
2938
+ // daemon frozen) and the terminal module import (~0.9 s). Warm both off the event loop shortly after boot.
2939
+ if (!opts.startNexusHttpServerFn) {
2940
+ const warm = setTimeout(() => {
2941
+ void import('../shell-env-bootstrap.js').then(({ warmCapturedEnv }) => warmCapturedEnv()).catch(() => undefined);
2942
+ void import('../web-terminal/preview-tap-registry.js').then(() => import('../preview/terminal.js')).catch(() => undefined);
2943
+ }, 1_500);
2944
+ (warm as { unref?: () => void }).unref?.();
2945
+ }
2919
2946
  httpServer = startHttpServer({
2920
2947
  state,
2921
2948
  registry,