elanous 0.2.8 → 0.2.10

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 (385) hide show
  1. package/apps/pwa/out/404/index.html +1 -1
  2. package/apps/pwa/out/404.html +1 -1
  3. package/apps/pwa/out/_next/static/2DsiDfDCks44g1cPUMhiW/_buildManifest.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/24-e6d28380efab7faf.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/3615-740dd270764ffa24.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/3734-1015fe991eb9b7e7.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/5012-a7fe6f3c8b6fe45a.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/5783-ef40ebfc91d55230.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/62-ff227c3edb8b248b.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/7633-1b3709b0854c9412.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/7951-8b03dbb10b24cd06.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/8067-87ce253ec2f23697.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/{5080-1613752891fcadb4.js → 8195-6fde3d7a39859206.js} +5 -5
  14. package/apps/pwa/out/_next/static/chunks/8894-b5fb36bab837cd29.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/9454-84d0d5d77446bd92.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/988-5f92e7ab7ccea3dd.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/9989-5582582f06429840.js +1 -0
  18. package/apps/pwa/out/_next/static/chunks/app/approvals/page-d2253d7e58ef1890.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-5441d4a9ab35e518.js +1 -0
  20. package/apps/pwa/out/_next/static/chunks/app/board/page-d9b12d680922d980.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-d4b6df905f185055.js → page-54b32bd27726588d.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/bots/page-e268d6d145827a22.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/chat/{page-186fc6952f027acc.js → page-f8116559c7424da7.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/consult/page-1085399fd0f32112.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/control/page-1df686a14f65895a.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-cbbe03c0da7b081a.js → page-54e1f966ab60a4dc.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/editor/page-7ed95f296f5ee8e9.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/exec/page-11edac668d77e5c3.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/field/page-59e2b9a22cd5a6d5.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/intake/page-2976df21ad74ab48.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/app/layout-34d211a67f16d510.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/live/page-8ff652d5d950639b.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/market/{page-c0c0d19d51342d3e.js → page-b31610deda76337b.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-2e002ad8faa43291.js → page-6cb0e1bbefe5fb3d.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/morning/page-a286a152cf584576.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/not-found-d9237581e7003bcb.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/observatory/page-83274e761643fdd2.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-ceee6cfb1d4e6d68.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-9c9f8bb3e4a64091.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-c17217603ea3426a.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/app/page-efb6177cbf1e8a51.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/reflection/page-d307523703bc4df3.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-462f4e4bab45ce7d.js +1 -0
  44. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8990e96f33af3837.js +1 -0
  45. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-7464c1987dd76e6d.js → page-e168fa1030af0548.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/settings/page-1233e05a4068bc9c.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-13a1840b1fac337a.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/setup/page-64a29fc80b11ab55.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/share/page-68e787339a16b3dc.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-d8ab4d1489223459.js → page-4fddc90b23feb215.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/tasks/page-886ba8260dc79c79.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/term/page-0f26507a1722e40b.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/today/page-109d4ccd42259c9d.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/trace/page-f5c2a4e510726774.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/vault/page-b2e0039416f42522.js +1 -0
  56. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-26102fd7350f171e.js +1 -0
  57. package/apps/pwa/out/_next/static/chunks/app/workflows/page-708948bc8a802794.js +1 -0
  58. package/apps/pwa/out/_next/static/chunks/app/workspace/page-5e5611620f78fb0b.js +1 -0
  59. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-139ba5c367bbf60a.js +1 -0
  60. package/apps/pwa/out/_next/static/chunks/main-app-202b4e81f33895ea.js +1 -0
  61. package/apps/pwa/out/_next/static/css/43feca5fd387dcbd.css +1 -0
  62. package/apps/pwa/out/approvals/index.html +1 -1
  63. package/apps/pwa/out/approvals/index.txt +30 -29
  64. package/apps/pwa/out/autopilot/index.html +1 -1
  65. package/apps/pwa/out/autopilot/index.txt +30 -29
  66. package/apps/pwa/out/board/index.html +1 -1
  67. package/apps/pwa/out/board/index.txt +30 -29
  68. package/apps/pwa/out/botlab/index.html +1 -1
  69. package/apps/pwa/out/botlab/index.txt +27 -26
  70. package/apps/pwa/out/bots/index.html +1 -1
  71. package/apps/pwa/out/bots/index.txt +27 -26
  72. package/apps/pwa/out/chat/index.html +1 -1
  73. package/apps/pwa/out/chat/index.txt +27 -26
  74. package/apps/pwa/out/consult/index.html +1 -1
  75. package/apps/pwa/out/consult/index.txt +30 -29
  76. package/apps/pwa/out/control/index.html +1 -1
  77. package/apps/pwa/out/control/index.txt +30 -29
  78. package/apps/pwa/out/design-check/index.html +1 -1
  79. package/apps/pwa/out/design-check/index.txt +30 -29
  80. package/apps/pwa/out/editor/index.html +1 -1
  81. package/apps/pwa/out/editor/index.txt +30 -29
  82. package/apps/pwa/out/exec/index.html +1 -1
  83. package/apps/pwa/out/exec/index.txt +30 -29
  84. package/apps/pwa/out/field/index.html +1 -1
  85. package/apps/pwa/out/field/index.txt +30 -29
  86. package/apps/pwa/out/healthz +1 -1
  87. package/apps/pwa/out/index.html +1 -1
  88. package/apps/pwa/out/index.txt +27 -26
  89. package/apps/pwa/out/intake/index.html +1 -1
  90. package/apps/pwa/out/intake/index.txt +30 -29
  91. package/apps/pwa/out/live/index.html +1 -1
  92. package/apps/pwa/out/live/index.txt +30 -29
  93. package/apps/pwa/out/market/index.html +1 -1
  94. package/apps/pwa/out/market/index.txt +27 -26
  95. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  96. package/apps/pwa/out/missions/placeholder/index.txt +27 -26
  97. package/apps/pwa/out/morning/index.html +1 -1
  98. package/apps/pwa/out/morning/index.txt +30 -29
  99. package/apps/pwa/out/observatory/index.html +1 -1
  100. package/apps/pwa/out/observatory/index.txt +30 -29
  101. package/apps/pwa/out/ops/checklist/index.html +1 -0
  102. package/apps/pwa/out/ops/checklist/index.txt +30 -0
  103. package/apps/pwa/out/ops/release/index.html +1 -0
  104. package/apps/pwa/out/ops/release/index.txt +30 -0
  105. package/apps/pwa/out/ops/seats/index.html +1 -0
  106. package/apps/pwa/out/ops/seats/index.txt +30 -0
  107. package/apps/pwa/out/reflection/index.html +1 -1
  108. package/apps/pwa/out/reflection/index.txt +28 -27
  109. package/apps/pwa/out/scheduler/index.html +1 -1
  110. package/apps/pwa/out/scheduler/index.txt +30 -29
  111. package/apps/pwa/out/sessions/index.html +1 -1
  112. package/apps/pwa/out/sessions/index.txt +27 -26
  113. package/apps/pwa/out/settings/devices/index.html +1 -1
  114. package/apps/pwa/out/settings/devices/index.txt +30 -29
  115. package/apps/pwa/out/settings/index.html +1 -1
  116. package/apps/pwa/out/settings/index.txt +30 -29
  117. package/apps/pwa/out/setup/done/index.html +1 -1
  118. package/apps/pwa/out/setup/done/index.txt +30 -29
  119. package/apps/pwa/out/setup/index.html +1 -1
  120. package/apps/pwa/out/setup/index.txt +30 -29
  121. package/apps/pwa/out/share/index.html +1 -1
  122. package/apps/pwa/out/share/index.txt +30 -29
  123. package/apps/pwa/out/showroom/index.html +1 -1
  124. package/apps/pwa/out/showroom/index.txt +27 -26
  125. package/apps/pwa/out/tasks/index.html +1 -1
  126. package/apps/pwa/out/tasks/index.txt +30 -29
  127. package/apps/pwa/out/term/index.html +1 -1
  128. package/apps/pwa/out/term/index.txt +30 -29
  129. package/apps/pwa/out/today/index.html +1 -0
  130. package/apps/pwa/out/today/index.txt +33 -0
  131. package/apps/pwa/out/trace/index.html +1 -1
  132. package/apps/pwa/out/trace/index.txt +30 -29
  133. package/apps/pwa/out/vault/index.html +1 -1
  134. package/apps/pwa/out/vault/index.txt +30 -29
  135. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  136. package/apps/pwa/out/workflows/chat-ui/index.txt +30 -29
  137. package/apps/pwa/out/workflows/index.html +1 -1
  138. package/apps/pwa/out/workflows/index.txt +30 -29
  139. package/apps/pwa/out/workspace/index.html +1 -1
  140. package/apps/pwa/out/workspace/index.txt +30 -29
  141. package/apps/pwa/out/worktrees/index.html +1 -1
  142. package/apps/pwa/out/worktrees/index.txt +30 -29
  143. package/graphs/field/field-feed.yaml +2 -0
  144. package/graphs/implement-loop.yaml +1 -1
  145. package/graphs/release/recipes.yaml +3 -0
  146. package/graphs/release/release-loop.yaml +3 -1
  147. package/graphs/test-diet/nightly-audit.yaml +18 -0
  148. package/graphs/test-diet/recipes.yaml +3 -0
  149. package/package.json +1 -1
  150. package/plugins/card-followup/graphs/run-step.ts +48 -13
  151. package/plugins/doc-draft/graphs/doc-draft.yaml +3 -3
  152. package/plugins/doc-draft/graphs/run-step.ts +14 -7
  153. package/scripts/ci-pwa-build-gate.ts +160 -0
  154. package/scripts/coord-channel-watch.sh +42 -8
  155. package/scripts/coord-digest.ts +199 -0
  156. package/scripts/coord-post.sh +60 -3
  157. package/scripts/install.ps1 +68 -9
  158. package/scripts/install.sh +39 -12
  159. package/scripts/lib/pwa-scenarios/scenarios.ts +20 -0
  160. package/scripts/lib/tui-regress-checks.ts +146 -0
  161. package/scripts/memory-lifecycle-cycle.ts +2 -2
  162. package/scripts/ops-now-probe.ts +327 -0
  163. package/scripts/publish-internal-dist.ts +11 -1
  164. package/scripts/release-loop/cutoff.ts +31 -2
  165. package/scripts/release-loop/export-check-node.ts +85 -0
  166. package/scripts/release-loop/gate-node.ts +18 -4
  167. package/scripts/release-loop/node-verdict.ts +8 -1
  168. package/scripts/release-loop/ops-upgrade-node.ts +72 -16
  169. package/scripts/release-loop/release-readiness.ts +9 -0
  170. package/scripts/release-loop/version-node.ts +58 -10
  171. package/scripts/repo-watch-cycle.ts +1 -1
  172. package/scripts/retro-cycle.ts +2 -2
  173. package/scripts/schedule-health-report.ts +1 -1
  174. package/scripts/storyboard/storyboard.ts +5 -161
  175. package/scripts/test-diet/lib.ts +31 -1
  176. package/scripts/test-diet/node.ts +134 -17
  177. package/scripts/test-timeout-audit.ts +114 -0
  178. package/scripts/tree-sync-alert.ts +1 -1
  179. package/scripts/tui-regress.ts +71 -0
  180. package/scripts/waitlist-daily-digest.ts +1 -1
  181. package/skills/explainer-video/SKILL.md +4 -0
  182. package/skills/explainer-video/engine/reel.mjs +18 -13
  183. package/skills/explainer-video/engine/reel.sh +24 -5
  184. package/skills/explainer-video/engine/stitch.mjs +145 -0
  185. package/skills/pitch-deck/SKILL.md +36 -0
  186. package/skills/pitch-deck/template.html +80 -0
  187. package/src/acp/codex-approval-adapter.ts +9 -3
  188. package/src/acp/core-turn-bridge.ts +117 -18
  189. package/src/acp/fact-question.ts +11 -0
  190. package/src/acp/server.ts +48 -7
  191. package/src/acp/tool-approval.ts +126 -0
  192. package/src/acp/turn-runner.ts +36 -0
  193. package/src/agent/monad-agent-turn.ts +8 -0
  194. package/src/ask-user-question/pending-questions.ts +25 -10
  195. package/src/ask-user-question/types.ts +9 -0
  196. package/src/auth/setup-link-tokens.fixture.ts +19 -0
  197. package/src/auth/setup-link-tokens.ts +164 -0
  198. package/src/autopilot/build/integrity-gate.ts +12 -0
  199. package/src/boot/daemon-prompt-turn.ts +3 -0
  200. package/src/boot/daemon-tools/index.ts +8 -0
  201. package/src/boot/daemon-tools/types.ts +2 -0
  202. package/src/boot/daemon-tools/web-search.ts +1 -0
  203. package/src/budget/unified-usage.ts +75 -0
  204. package/src/channel-bot-token.ts +37 -10
  205. package/src/chat/ctrl-c-in-input.ts +14 -0
  206. package/src/chat/index.ts +61 -9
  207. package/src/chat/pickers/state.ts +6 -2
  208. package/src/cli/agent-cli.ts +7 -0
  209. package/src/cli/decisions-cli.ts +25 -3
  210. package/src/cli/doctor-bundle-upload.ts +68 -0
  211. package/src/cli/doctor-cli.ts +38 -4
  212. package/src/cli/op-proxy-sync.ts +83 -0
  213. package/src/cli/ops-cli.ts +20 -0
  214. package/src/cli/pod-cli.ts +49 -0
  215. package/src/cli/pr-cli.ts +37 -4
  216. package/src/cli/pwa-stop.ts +163 -13
  217. package/src/cli/pwa-test.ts +3 -0
  218. package/src/cli/release-cli.ts +98 -15
  219. package/src/cli/self-update.ts +14 -0
  220. package/src/cli/storyboard-cli.ts +37 -0
  221. package/src/cli/test-state-dir-flag.ts +11 -4
  222. package/src/connectors/linear.ts +146 -0
  223. package/src/context-bus/coord-events.ts +115 -0
  224. package/src/dashboard/dashboard-boot-fixture.ts +23 -0
  225. package/src/dashboard/dashboard-status-lines.ts +45 -0
  226. package/src/dashboard/first-screen-band.ts +35 -0
  227. package/src/dashboard/index.ts +128 -34
  228. package/src/dashboard/provider-slash-runtime.ts +28 -11
  229. package/src/dashboard/slash-runtime/dashboard-handlers.ts +17 -0
  230. package/src/dashboard/slash-runtime/help-from-registry.ts +43 -28
  231. package/src/dashboard/turn-stream-formatter.ts +1 -1
  232. package/src/decisions/decision-cards.ts +50 -2
  233. package/src/decisions/decision-ledger.ts +75 -4
  234. package/src/decisions/decision-linear-projection.ts +157 -0
  235. package/src/decisions/web-push-decision-cards.ts +50 -0
  236. package/src/directive/interpretation-card.ts +36 -0
  237. package/src/discord.ts +2 -1
  238. package/src/domains/coo-admin-tool.ts +62 -0
  239. package/src/domains/core-tools.ts +11 -3
  240. package/src/domains/elanous-card.ts +29 -0
  241. package/src/domains/ops-facts-tool.ts +76 -0
  242. package/src/domains/outbound-alert.ts +41 -13
  243. package/src/domains/release-tool.ts +168 -0
  244. package/src/domains/telegram-kind-route.ts +19 -1
  245. package/src/exec-requests/planner.ts +98 -28
  246. package/src/exec-requests/runner.ts +107 -10
  247. package/src/exec-requests/store.ts +8 -2
  248. package/src/field/field-media.ts +10 -7
  249. package/src/field/field-reel.ts +14 -4
  250. package/src/git-fs/worktree.ts +45 -6
  251. package/src/graph-runner/graph-approval-card.ts +113 -0
  252. package/src/graph-runner/graph-cli.ts +129 -4
  253. package/src/graph-runner/graph-notify.ts +25 -3
  254. package/src/graph-runner/runner.ts +142 -16
  255. package/src/harness/browser-verify.ts +25 -2
  256. package/src/harness/harness-cli-command.ts +231 -35
  257. package/src/harness/harness-incidents-writer.fixture.ts +24 -0
  258. package/src/harness/harness-incidents.ts +129 -0
  259. package/src/harness/harness-stop.ts +53 -3
  260. package/src/hitl/clarification-policy.ts +3 -1
  261. package/src/index.ts +172 -41
  262. package/src/intake-plane/author-goals.ts +37 -4
  263. package/src/intake-plane/check.ts +3 -0
  264. package/src/intake-plane/discord-seat-work.ts +47 -2
  265. package/src/intake-plane/seat-doc-route.ts +170 -6
  266. package/src/intake-plane/telegram-seat-work.ts +44 -2
  267. package/src/intake-plane/telegram-work.ts +1 -0
  268. package/src/maturity/cli-maturity.ts +34 -0
  269. package/src/maturity/feature-maturity.ts +100 -1
  270. package/src/maturity/tui-slash-maturity.ts +31 -0
  271. package/src/memory.ts +32 -4
  272. package/src/nexus/api/exec-requests.ts +33 -3
  273. package/src/nexus/api/http-server.ts +73 -1
  274. package/src/nexus/api/meta-api.ts +38 -0
  275. package/src/nexus/api/operator.ts +61 -0
  276. package/src/nexus/api/ops-api.ts +148 -0
  277. package/src/nexus/api/ops-seats-sources.ts +95 -0
  278. package/src/nexus/api/ops-seats.ts +124 -0
  279. package/src/nexus/api/public-routes.ts +14 -1
  280. package/src/nexus/api/seat-requests.ts +88 -8
  281. package/src/nexus/api/sessions-store.ts +63 -0
  282. package/src/nexus/api/setup-claim.ts +21 -0
  283. package/src/nexus/api/setup-codex-login.ts +160 -0
  284. package/src/nexus/api/usage-api.ts +102 -0
  285. package/src/nexus/config/secrets/file-backend.ts +56 -18
  286. package/src/nexus/index.ts +45 -4
  287. package/src/onboarding/first-run-doctor.ts +54 -0
  288. package/src/onboarding/first-run.ts +89 -0
  289. package/src/onboarding/lan-address.ts +45 -0
  290. package/src/onboarding/web-first.ts +119 -24
  291. package/src/onboarding.ts +4 -0
  292. package/src/ops-now/ops-now-refresh.ts +100 -0
  293. package/src/project/project-store.ts +115 -0
  294. package/src/release-loop/checklist.ts +76 -61
  295. package/src/release-loop/feature-store.ts +460 -0
  296. package/src/release-loop/manifest.ts +59 -5
  297. package/src/release-loop/release-schedule.ts +53 -0
  298. package/src/repl/index.ts +7 -0
  299. package/src/seat-dispatch/ceo-commands.ts +98 -0
  300. package/src/seat-dispatch/ceo-intent.ts +38 -0
  301. package/src/seat-loop/seat-loop.ts +231 -0
  302. package/src/seat-loop/seat-report.ts +64 -0
  303. package/src/self-dev/decompose.ts +66 -11
  304. package/src/self-dev/run-store.ts +10 -1
  305. package/src/self-implement/execution-clarification.ts +115 -0
  306. package/src/self-implement/gate-cli.ts +6 -3
  307. package/src/self-implement/orchestrator.ts +37 -3
  308. package/src/self-implement/pipeline-shape.ts +1 -1
  309. package/src/self-implement/run-status-mapping.ts +5 -3
  310. package/src/session/chat.ts +2 -0
  311. package/src/session/index.ts +9 -0
  312. package/src/shell-env-bootstrap.ts +17 -0
  313. package/src/skills/tools/usage-slash.ts +8 -7
  314. package/src/steward/directive.ts +51 -2
  315. package/src/steward/launch.ts +250 -0
  316. package/src/steward/outcome-notify.ts +115 -0
  317. package/src/steward/steward-cards.ts +8 -2
  318. package/src/steward/triage-plan.ts +32 -4
  319. package/src/steward/triage.ts +93 -9
  320. package/src/steward/working-backwards.ts +64 -0
  321. package/src/storyboard/storyboard.ts +141 -0
  322. package/src/task-cards/card-store.ts +2 -2
  323. package/src/task-orchestrator/surfaces/pod-lease.ts +260 -0
  324. package/src/telegram-card-followup.ts +233 -0
  325. package/src/telegram-commands.ts +16 -0
  326. package/src/telegram-report.ts +15 -9
  327. package/src/telegram-run.ts +90 -13
  328. package/src/telegram.ts +29 -8
  329. package/src/thinking-line.ts +28 -20
  330. package/src/tool-runtime/types.ts +4 -0
  331. package/src/tui/chat/execution-badge.ts +1 -1
  332. package/src/tui-client/dashboard-session.ts +2 -0
  333. package/src/user-config.ts +118 -11
  334. package/src/ux-sim/turn-states.ts +1 -1
  335. package/src/version/packed-revision.json +1 -1
  336. package/src/web-push/notify-exec-request.ts +32 -0
  337. package/apps/pwa/out/_next/static/chunks/1201-09613bce624467c0.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/2929-617f1e66a55be184.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/3441-a1dd4acfeda14488.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/4836-3084c58c1725a40b.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/6090-85a3f05f954e5697.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/6284-69db415193d5f8bd.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/6416-505ba707cacafbba.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/7032-f4ef698518fba61c.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/8067-d976b441a104ab74.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/8519-5632377cce994f86.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/8894-7317998f6cb97d8c.js +0 -1
  348. package/apps/pwa/out/_next/static/chunks/9454-a3e0953307216c58.js +0 -1
  349. package/apps/pwa/out/_next/static/chunks/9740-8f5ff96c522b5f55.js +0 -1
  350. package/apps/pwa/out/_next/static/chunks/app/approvals/page-9aa47b0bc6d570ff.js +0 -1
  351. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f4d2ecbbae6088d6.js +0 -1
  352. package/apps/pwa/out/_next/static/chunks/app/board/page-2053e49930c82748.js +0 -1
  353. package/apps/pwa/out/_next/static/chunks/app/bots/page-a70ba27c33003dd0.js +0 -1
  354. package/apps/pwa/out/_next/static/chunks/app/consult/page-18166f885d831d3e.js +0 -1
  355. package/apps/pwa/out/_next/static/chunks/app/control/page-1c9d6cfe68b121c2.js +0 -1
  356. package/apps/pwa/out/_next/static/chunks/app/editor/page-c108ff1fdb665c97.js +0 -1
  357. package/apps/pwa/out/_next/static/chunks/app/exec/page-147a328753d983e2.js +0 -1
  358. package/apps/pwa/out/_next/static/chunks/app/field/page-799395a9df7d95aa.js +0 -1
  359. package/apps/pwa/out/_next/static/chunks/app/intake/page-6a8158b71d1fe9d7.js +0 -1
  360. package/apps/pwa/out/_next/static/chunks/app/layout-9567560270857531.js +0 -1
  361. package/apps/pwa/out/_next/static/chunks/app/live/page-e7ed3d010153d818.js +0 -1
  362. package/apps/pwa/out/_next/static/chunks/app/morning/page-b672e02e4bec8deb.js +0 -1
  363. package/apps/pwa/out/_next/static/chunks/app/not-found-1ba4a5d857276334.js +0 -1
  364. package/apps/pwa/out/_next/static/chunks/app/observatory/page-9db2226f38c542fb.js +0 -1
  365. package/apps/pwa/out/_next/static/chunks/app/page-70ee65f5f7a151dc.js +0 -1
  366. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0bb8e43203efb23b.js +0 -1
  367. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-092e1bcf27d751cc.js +0 -1
  368. package/apps/pwa/out/_next/static/chunks/app/sessions/page-eb52580c4b75b905.js +0 -1
  369. package/apps/pwa/out/_next/static/chunks/app/settings/page-e573437b0a1c728f.js +0 -1
  370. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-294300625e49e4d3.js +0 -1
  371. package/apps/pwa/out/_next/static/chunks/app/setup/page-085847b088675978.js +0 -1
  372. package/apps/pwa/out/_next/static/chunks/app/share/page-9fe9d38c36999eb4.js +0 -1
  373. package/apps/pwa/out/_next/static/chunks/app/tasks/page-6141f67f531d8b6a.js +0 -1
  374. package/apps/pwa/out/_next/static/chunks/app/term/page-295a02b1026b8255.js +0 -1
  375. package/apps/pwa/out/_next/static/chunks/app/trace/page-1eb9a0e1e054209d.js +0 -1
  376. package/apps/pwa/out/_next/static/chunks/app/vault/page-82efa177a30f3741.js +0 -1
  377. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d0be51cec4f62606.js +0 -1
  378. package/apps/pwa/out/_next/static/chunks/app/workflows/page-2e367075ffdbc062.js +0 -1
  379. package/apps/pwa/out/_next/static/chunks/app/workspace/page-647ed2b66c1d49f1.js +0 -1
  380. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-ba058c56e38f2792.js +0 -1
  381. package/apps/pwa/out/_next/static/chunks/main-app-6870f30ea99f8346.js +0 -1
  382. package/apps/pwa/out/_next/static/css/5ef72a618a8994f3.css +0 -1
  383. package/apps/pwa/out/_next/static/lywc8m_HDzH9MKSDQhyEj/_buildManifest.js +0 -1
  384. package/src/dashboard/turn-typeahead-echo.ts +0 -23
  385. /package/apps/pwa/out/_next/static/{lywc8m_HDzH9MKSDQhyEj → 2DsiDfDCks44g1cPUMhiW}/_ssgManifest.js +0 -0
@@ -0,0 +1,115 @@
1
+ import { randomUUID } from 'node:crypto';
2
+
3
+ import { createPendingQuestion, writePendingQuestion } from '../ask-user-question/pending-questions.js';
4
+ import { createFileAskUserQuestionResolver } from '../ask-user-question/tool.js';
5
+ import { debug } from '../debug/log.js';
6
+ import {
7
+ decideAndObserveClarification,
8
+ defaultClarificationBudget,
9
+ toAskUserQuestionRequest,
10
+ type ClarificationCandidate,
11
+ type ClarificationContext,
12
+ } from '../hitl/clarification-policy.js';
13
+ import { getUserConfig } from '../user-config.js';
14
+ import { appendRunLedgerEntry, type RunLedgerWriter } from './run-ledger.js';
15
+
16
+ export interface ExecutionClarificationContext {
17
+ runId: string;
18
+ budget?: ClarificationContext['budget'];
19
+ pendingQuestionCount?: number;
20
+ config?: { hitl?: { executionDeadlineMinutes?: number } };
21
+ }
22
+
23
+ export type ExecutionClarificationResult =
24
+ | { choice: string; by: 'assumed' | 'owner' | 'auto-recommended' }
25
+ | { deferred: true }
26
+ | { parked: true; reason: 'critical-unanswered'; questionId: string };
27
+
28
+ export interface ExecutionClarificationDeps {
29
+ now?: () => number;
30
+ createId?: () => string;
31
+ resolver?: typeof createFileAskUserQuestionResolver;
32
+ resolverDeps?: Parameters<typeof createFileAskUserQuestionResolver>[1];
33
+ writeRunLedger?: RunLedgerWriter;
34
+ }
35
+
36
+ /** A single execution-phase decision; the file resolver owns the pending/answer lifecycle. */
37
+ export async function askAtExecution(
38
+ candidate: ClarificationCandidate,
39
+ ctx: ExecutionClarificationContext,
40
+ deps: ExecutionClarificationDeps = {},
41
+ ): Promise<ExecutionClarificationResult> {
42
+ const recommended = candidate.options.filter(option => option.recommended);
43
+ if (candidate.options.length < 2 || candidate.options.length > 4 || recommended.length !== 1
44
+ || new Set(candidate.options.map(option => option.label)).size !== candidate.options.length) {
45
+ throw new Error('Execution clarification requires 2–4 distinct options and exactly one recommendation.');
46
+ }
47
+ const observe = (event: 'assumed' | 'asked' | 'answered' | 'auto-recommended' | 'parked', questionId?: string): void => {
48
+ try {
49
+ debug.log('hitl.execution', event, { runId: ctx.runId, questionId, impact: candidate.impact });
50
+ } catch { /* Logging must not decide the user's choice. */ }
51
+ };
52
+ const record = (event: 'assumed' | 'auto-recommended' | 'parked', data: Record<string, unknown>): void => {
53
+ (deps.writeRunLedger ?? appendRunLedgerEntry)({
54
+ timestamp: new Date((deps.now ?? Date.now)()).toISOString(),
55
+ runId: ctx.runId,
56
+ event,
57
+ data: { impact: candidate.impact, ...data },
58
+ });
59
+ };
60
+ const decision = decideAndObserveClarification(candidate, {
61
+ phase: 'execution',
62
+ budget: ctx.budget ?? defaultClarificationBudget('execution'),
63
+ pendingQuestionCount: ctx.pendingQuestionCount,
64
+ }, { consumer: 'self-implement-execution' });
65
+ if (decision.action === 'defer') return { deferred: true };
66
+ if (decision.action === 'assume') {
67
+ record('assumed', { choice: recommended[0]!.label, assumption: decision.assumption });
68
+ observe('assumed');
69
+ return { choice: recommended[0]!.label, by: 'assumed' };
70
+ }
71
+
72
+ const request = toAskUserQuestionRequest(decision);
73
+ if (!request) throw new Error('Execution clarification policy approved a question without a valid wire request.');
74
+ const configured = ctx.config === undefined
75
+ ? getUserConfig().hitl?.executionDeadlineMinutes
76
+ : ctx.config.hitl?.executionDeadlineMinutes;
77
+ const minutes = configured === undefined ? 30 : configured;
78
+ if (!Number.isSafeInteger(minutes) || minutes < 1 || minutes > 35_791) {
79
+ throw new Error('hitl.executionDeadlineMinutes must be a positive integer within the resolver timer limit.');
80
+ }
81
+ const questionId = (deps.createId ?? (() => `execution:${ctx.runId}:${randomUUID()}`))();
82
+ let expired = false;
83
+ let written = false;
84
+ const resolver = (deps.resolver ?? createFileAskUserQuestionResolver)(minutes * 60_000, {
85
+ ...deps.resolverDeps,
86
+ observe: (state, data) => {
87
+ if (state === 'answer-missing') expired = true;
88
+ deps.resolverDeps?.observe?.(state, data);
89
+ },
90
+ createId: () => questionId,
91
+ write: (pending) => {
92
+ const question = createPendingQuestion(questionId, { ...request, runId: ctx.runId }, undefined,
93
+ { now: () => new Date((deps.now ?? Date.now)()) },
94
+ { surface: 'file', delivery: 'file', expiresAt: pending.expiresAt });
95
+ (deps.resolverDeps?.write ?? writePendingQuestion)(question);
96
+ written = true;
97
+ observe('asked', questionId);
98
+ },
99
+ });
100
+ const answer = await resolver({ ...request, runId: ctx.runId });
101
+ const chosen = answer.answers[candidate.id];
102
+ if (written && !expired && !answer.cancelled && typeof chosen === 'string' && candidate.options.some(option => option.label === chosen)) {
103
+ observe('answered', questionId);
104
+ return { choice: chosen, by: 'owner' };
105
+ }
106
+ if (!written || !expired) throw new Error('Execution clarification was cancelled or could not read its answer before expiry.');
107
+ if (candidate.impact === 'critical') {
108
+ record('parked', { questionId, reason: 'critical-unanswered' });
109
+ observe('parked', questionId);
110
+ return { parked: true, reason: 'critical-unanswered', questionId };
111
+ }
112
+ record('auto-recommended', { questionId, choice: recommended[0]!.label, by: 'auto-recommended' });
113
+ observe('auto-recommended', questionId);
114
+ return { choice: recommended[0]!.label, by: 'auto-recommended' };
115
+ }
@@ -7,6 +7,7 @@ import { runModelHardcodeGate } from '../../scripts/ci-model-hardcode-gate.js';
7
7
  import { runDaemonPortGate } from '../../scripts/ci-daemon-port-gate.js';
8
8
  import { runAndroidUnitTestGate } from '../../scripts/ci-android-unit-tests.js';
9
9
  import { runIosUnitTestGate } from '../../scripts/ci-ios-unit-tests.js';
10
+ import { runPwaBuildGate } from '../../scripts/ci-pwa-build-gate.js';
10
11
  import { runMockModuleRestoreGate } from '../../scripts/ci-mock-module-restore-gate.js';
11
12
  import { parseReviewDepthFromFilesJson } from '../agent-mission/review-depth.js';
12
13
  import { buildGateBaselineReport, calculateGateTestCountChange, formatGateBaselineNote, formatGateTestCountNote, isGateTestFile, runGateBaseline, type BaselineProcessResult, type BaselineProcessStatus, type GateTestFailure } from './gate-baseline.js';
@@ -83,6 +84,7 @@ interface SelfGateCliDeps {
83
84
  runAndroidGate?: (out: GateOutput) => number;
84
85
  /** iOS 순수-로직 시험 게이트 심(시험 주입용). */
85
86
  runIosGate?: (out: GateOutput) => number;
87
+ runPwaGate?: (out: GateOutput) => number;
86
88
  listPrWorktrees?: (cwd: string) => PrWorktreeListEntry[] | { lookupFailed: string };
87
89
  inspectWorktreeDirtiness?: (path: string) => { dirty: boolean } | { lookupFailed: string };
88
90
  }
@@ -619,11 +621,12 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
619
621
  const androidPassed = runAdditionalGate('android-gate', deps.runAndroidGate ?? runAndroidUnitTestGate, selection.files, cwd, lines);
620
622
  // 🍎 iOS 축 — 안드로이드와 «같은 자리»에 둔다(skipTestStep 앞). Swift 만 바뀐 PR 도 여기서 조기 리턴한다.
621
623
  const iosPassed = runAdditionalGate('ios-gate', deps.runIosGate ?? runIosUnitTestGate, selection.files, cwd, lines);
624
+ const pwaPassed = runAdditionalGate('pwa-gate', deps.runPwaGate ?? runPwaBuildGate, selection.files, cwd, lines);
622
625
 
623
626
  if (scope.skipTestStep) {
624
627
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
625
628
  logGateCliBaseline(cwd, { ...ZERO_GATE_BASELINE, unrunImporterTotal, lookupFailed: importerLookupFailed });
626
- return { exitCode: androidPassed && iosPassed && policyPassed ? 0 : 1, lines, changedFiles: selection.files, testFiles: [], unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests };
629
+ return { exitCode: androidPassed && iosPassed && pwaPassed && policyPassed ? 0 : 1, lines, changedFiles: selection.files, testFiles: [], unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests };
627
630
  }
628
631
 
629
632
  const test = (deps.runTests ?? ((dir, files) => (deps.runCommand ?? defaultRunCommand)('bun', ['test', ...files], dir)))(cwd, testFiles);
@@ -632,7 +635,7 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
632
635
  lines.push(formatPartialObservationNote(unrunImporterTotal ?? 0, `tests: pass (${testFiles.length} files)`));
633
636
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
634
637
  logGateCliBaseline(cwd, { ...ZERO_GATE_BASELINE, unrunImporterTotal, lookupFailed: importerLookupFailed });
635
- return { exitCode: policyPassed && androidPassed && iosPassed ? 0 : 1, lines, changedFiles: selection.files, testFiles, unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests };
638
+ return { exitCode: policyPassed && androidPassed && iosPassed && pwaPassed ? 0 : 1, lines, changedFiles: selection.files, testFiles, unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests };
636
639
  }
637
640
 
638
641
  if (worktreeLog.includes('deterministic environment setup failed')) lines.push(worktreeLog);
@@ -653,6 +656,6 @@ export function runSelfGateCli(cwd: string, options: SelfGateCliOptions = {}, de
653
656
  });
654
657
  lines.push(formatPartialObservationNote(unrunImporterTotal ?? 0, formatGateBaselineNote(report, unrunImporterTotal ?? 0)));
655
658
  const policyPassed = runPolicyGates(selection.files, cwd, lines, deps);
656
- const exitCode = !androidPassed || !iosPassed || !policyPassed || report.introduced > 0 || report.unknown > 0 || report.preconditionUnmet > 0 ? 1 : 0;
659
+ const exitCode = !androidPassed || !iosPassed || !pwaPassed || !policyPassed || report.introduced > 0 || report.unknown > 0 || report.preconditionUnmet > 0 ? 1 : 0;
657
660
  return { exitCode, lines, changedFiles: selection.files, testFiles, unverified: scope.unverified, documentPaths: scope.documentPaths, documentsWithoutDerivedTests: scope.documentsWithoutDerivedTests };
658
661
  }
@@ -79,6 +79,7 @@ import { classifyReviewProviderFailure } from '../self-dev/review-provider-fallb
79
79
  import { classifyError } from '../session-runtime/retry-policy.js';
80
80
  import { decomposeSelfDevGoal, inferHotPaths, type SelfDevDecomposeOptions } from '../self-dev/decompose.js';
81
81
  import { boundaryGlobs, boundaryViolations } from './goal-boundary.js';
82
+ import { askAtExecution, type ExecutionClarificationResult } from './execution-clarification.js';
82
83
  import { targetScopedGoalText } from './goal-text-path-scope.js';
83
84
  import type { SelfDevGoalType } from '../self-dev/orchestrate.js';
84
85
  import { DEFAULT_BRANCH_WORKTREE_BASE, gateGitResidue, observeGitResidue } from '../git-fs/worktree.js';
@@ -967,6 +968,8 @@ export interface SelfImplementSeams {
967
968
  /** 변경 파일 목록 — 게이트 «라우터»가 「코드가 바뀌었나」를 판정하는 입력.
968
969
  * ⛔ 미지정이면 실물 `changedFiles(cwd)` 를 쓴다. 못 얻으면 `undefined` 이고, 라우터는 그때 «돌린다»(fail-safe). */
969
970
  changedFilesForGateRoute?: (cwd: string) => readonly string[] | undefined;
971
+ /** An execution choice is made only after a measured change crosses the authored target paths. */
972
+ askExecutionClarification?: typeof askAtExecution;
970
973
  /** ★ ⑤ 내부 리뷰 seam(2026-07-21·review-gated merge) — gate 통과 diff 를 리뷰어(reviewPullRequest·
971
974
  * agent-substrate·staged 하니스와 동일 엔진)가 비평. verdict fail(mustFix) → rework 재주입(A 확장),
972
975
  * clean(pass/warn) → 병합 결정. 미주입 시 리뷰 스킵(gate 통과=바로 결정·종전 동작). */
@@ -1082,7 +1085,7 @@ export const SELF_IMPLEMENT_PROGRESS_STAGES = [
1082
1085
  'gating', 'gated', 'reviewing', 'reviewed', 'awaiting-approval',
1083
1086
  'pr-opening', 'pr-opened', 'merging', 'merged', 'worktree-completed',
1084
1087
  'aborted', 'gate-failed', 'review-blocked', 'merge-conflict', 'pr-declined',
1085
- 'awaiting-clarification',
1088
+ 'awaiting-clarification', 'parked',
1086
1089
  // ⭐⭐ 🩸 2026-09-12 — ***「선언한 대상을 «하나도» 안 만들고 끝났다」를 사람 표면에 낸다.***
1087
1090
  // ⛔ 기존 단계 이름을 «빌려 쓰지 않는다» — `progress()` 는 단계 이름으로 원장 행을 만들므로
1088
1091
  // 빌려 쓰면 그 단계 집계가 오염된다([T] 2026-09-12 · `#17767` 이후 계약).
@@ -6268,6 +6271,39 @@ async function runSelfImplementInner(
6268
6271
  return { ok: false, stage: 'aborted', node: 'implement', ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, worktreePath: wt.path, branch: wt.branch, quotaExhaustionAssessment: quotaExhaustionAssessmentForRun(), ...(impl.completionDisposition ? { completionDisposition: impl.completionDisposition } : {}), ...(pr ? { prUrl: pr.url, prNumber: pr.number } : {}), detail: impl.summary };
6269
6272
  }
6270
6273
 
6274
+ // Measure the existing declared-target boundary after implementation, before gate/PR.
6275
+ // Unknown declaration or changed files cannot justify an invented choice.
6276
+ let gateRouteFiles: readonly string[] | undefined;
6277
+ try { gateRouteFiles = (s.changedFilesForGateRoute ?? changedFiles)(wt.path); } catch { gateRouteFiles = undefined; }
6278
+ if (opts.goalFile) {
6279
+ let goalDocument: string | undefined;
6280
+ try { goalDocument = readFileSync(opts.goalFile, 'utf8'); } catch { /* unknown scope */ }
6281
+ const scope = detectDeclaredScopeDiff({ goalDocument, changedFiles: gateRouteFiles?.filter(file => !harnessSeededPaths.includes(file)), nameCap: gateRouteFiles?.length });
6282
+ if (scope.status === 'known' && scope.outsideCount !== null && scope.outsideCount > 0) {
6283
+ const result: ExecutionClarificationResult = await (s.askExecutionClarification ?? askAtExecution)({
6284
+ id: 'execution_scope', decision: 'Changing files outside the authored target paths',
6285
+ prompt: 'Implementation changed files outside the authored target paths. Keep the original scope or expand it?',
6286
+ whyNow: 'Continuing to gate or publish these edits would expand the requested scope.',
6287
+ impact: scope.outsideNames.some(file => {
6288
+ if (existsSync(join(wt.path, file))) return false;
6289
+ const tracked = runGitCommand(wt.path, ['ls-files', '--error-unmatch', '--', file], { encoding: 'utf8', timeout: 20_000 });
6290
+ return tracked.status === 0;
6291
+ }) ? 'critical' : 'high',
6292
+ options: [
6293
+ { label: 'Stay in scope', description: 'Do not accept out-of-scope changes.', recommended: true },
6294
+ { label: 'Expand scope', description: 'Accept the changed files outside the target paths.' },
6295
+ ],
6296
+ }, { runId }, { writeRunLedger: s.writeRunLedger });
6297
+ if ('parked' in result || 'deferred' in result || result.choice !== 'Expand scope') {
6298
+ finalizeSupervisorDeliveries('execution-scope-not-approved');
6299
+ observe('execution-scope-held', { outsideCount: scope.outsideCount, disposition: 'parked' in result ? 'parked' : 'in-scope-only' });
6300
+ if ('parked' in result) progress('parked', `critical 질문 ${result.questionId} 미응답 — 이 런만 대기`);
6301
+ return { ok: false, stage: 'parked' in result ? 'parked' : 'soft-stopped', node: 'implement', ...resolveRunOutcome({ termination: 'abandoned' }), sessionId, worktreePath: wt.path, branch: wt.branch,
6302
+ detail: 'parked' in result ? `critical-unanswered: ${result.questionId}` : 'Out-of-scope edits held in the worktree; gate and publication skipped.' };
6303
+ }
6304
+ }
6305
+ }
6306
+
6271
6307
  // ⛔⭐ 이 템플릿이 `gate` 노드를 «안 가지면» 그 단계는 돌지 않는다.
6272
6308
  // 📌 research-loop 이 그렇다 — 문서만 바뀐 판에는 «변경 파일 범위» 게이트가 돌 시험이 없다.
6273
6309
  // ⚠️ 꺼진 상태에서는 언제나 implement-loop 이라 참이다 ⇒ 오늘과 «같은» 걸음이다.
@@ -6275,8 +6311,6 @@ async function runSelfImplementInner(
6275
6311
  // 기본이 꺼짐이라 지금 폭발 반경은 0이고, 기본을 켜기 «전»에 이 구멍을 먼저 막아야 한다.
6276
6312
  // ⛔⭐ 라우터가 판정한다 — 값은 «코드»가 낸다(RFC §4.3 ⑵). 「모른다」는 «돌린다» 쪽이다.
6277
6313
  // 🚨 이 줄이 2026-09-08 승격의 구멍 ⓐ 를 닫는다: research 골이 «코드»를 만지면 시험이 돈다.
6278
- let gateRouteFiles: readonly string[] | undefined;
6279
- try { gateRouteFiles = (s.changedFilesForGateRoute ?? changedFiles)(wt.path); } catch { gateRouteFiles = undefined; }
6280
6314
  const gateRoute = routeGate(graphTemplate, gateRouteFiles);
6281
6315
  const runsGate = gateRoute.runsGate;
6282
6316
  progress('gating', !runsGate
@@ -13,7 +13,7 @@ export type PipelineNodeId =
13
13
 
14
14
  /** Each pipeline node's possible terminal stages. Nodes without a terminal exit use an empty array. */
15
15
  export const TERMINAL_STAGES_BY_NODE = {
16
- implement: ['aborted', 'timed-out', 'soft-stopped'],
16
+ implement: ['aborted', 'timed-out', 'soft-stopped', 'parked'],
17
17
  gate: [],
18
18
  review: ['review-blocked'],
19
19
  rework: ['gate-failed', 'review-blocked'],
@@ -1,4 +1,4 @@
1
- export type RunStatus = 'completed' | 'failed' | 'cancelled';
1
+ export type RunStatus = 'completed' | 'failed' | 'cancelled' | 'parked';
2
2
  /** ⭐ `crashed` — ***잡히지 않은 예외로 죽어 종결을 스스로 못 적은*** 런 (2026-08-11 · 🅣⊕🅢 어휘 합의).
3
3
  * ⛔ `aborted`(스스로 중단을 «판정»했다)와 다른 값이다 — 이쪽은 «판정할 겨를이 없었다».
4
4
  * 📏 왜 생겼나: 실측 미완 57 중 ≈38 이 어느 원장에도 종결이 없었고, 멈춘 자리가 전부 「노드 안」이었다. */
@@ -20,7 +20,8 @@ export type SelfImplementStage =
20
20
  | 'aborted'
21
21
  | 'timed-out'
22
22
  | 'pr-declined'
23
- | 'soft-stopped';
23
+ | 'soft-stopped'
24
+ | 'parked';
24
25
 
25
26
  const STAGE_OUTCOMES = {
26
27
  merged: { runStatus: 'completed' },
@@ -34,9 +35,10 @@ const STAGE_OUTCOMES = {
34
35
  'timed-out': { runStatus: 'failed', failureKind: 'timed-out' },
35
36
  'pr-declined': { runStatus: 'cancelled' },
36
37
  'soft-stopped': { runStatus: 'cancelled' },
38
+ parked: { runStatus: 'parked' },
37
39
  } satisfies Record<SelfImplementStage, MappedRunOutcome>;
38
40
 
39
- const RUN_STATUSES = new Set<RunStatus>(['completed', 'failed', 'cancelled']);
41
+ const RUN_STATUSES = new Set<RunStatus>(['completed', 'failed', 'cancelled', 'parked']);
40
42
 
41
43
  /** Runtime guard for the run-status payload produced by observeRunOutcome. */
42
44
  export function isRunStatus(value: unknown): value is RunStatus {
@@ -109,6 +109,8 @@ export interface RunTurnOpts {
109
109
  * delegated coding agent's approval prompts back to the originating
110
110
  * chat via `delegate_code_agent`. */
111
111
  hitlConfirmChannel?: import('../hitl/confirm.js').ConfirmChannel;
112
+ /** Verified by the hosting bot from the inbound user id against its server-side owner allowlist. */
113
+ verifiedOwner?: { id: string };
112
114
  /** Paired multi-option question channel (Telegram sets this). Surface
113
115
  * runTurnImpls thread it into `delegate_code_agent` so a delegated
114
116
  * agent's structured questions render as option buttons in the
@@ -202,6 +202,8 @@ export interface SessionMeta {
202
202
  * 아님). ~/.elanous 공유 스토어 세션은 전부 'prod'. append 는 이 값을 보존(생성자 귀속).
203
203
  * 구 세션엔 부재(tolerant read) — 미상 = 필터 비매치. */
204
204
  originInstance?: string;
205
+ /** Project association, omitted for sessions without an assigned project (including legacy records). */
206
+ projectId?: string;
205
207
  /** For telegram-sourced sessions; undefined for cli. */
206
208
  tgChatId?: number;
207
209
  tgThreadId?: number;
@@ -383,6 +385,7 @@ export interface CreateSessionOpts {
383
385
  tgBotId?: string;
384
386
  /** Stable identity for a persona's resident conversation. */
385
387
  personaId?: string;
388
+ projectId?: string;
386
389
  title?: string;
387
390
  /** 표면 origin(cli/pwa/tg/dc/harness) — source 유니온 밖의 세밀 라벨(PWA 챗 구분용). */
388
391
  origin?: string;
@@ -534,6 +537,7 @@ export function createSession(opts: CreateSessionOpts = {}, root: string = sessi
534
537
  tgThreadId: opts.tgThreadId,
535
538
  ...(opts.tgBotId ? { tgBotId: opts.tgBotId } : {}),
536
539
  ...(opts.personaId ? { personaId: opts.personaId } : {}),
540
+ ...(opts.projectId !== undefined ? { projectId: opts.projectId } : {}),
537
541
  tgSessionKey,
538
542
  };
539
543
  writeFileSync(sessionFile(id, root), '', 'utf-8');
@@ -575,6 +579,7 @@ export function adoptSession(id: string, opts: CreateSessionOpts = {}, root: str
575
579
  sourceKind: opts.sourceKind ?? defaultSessionSourceKind(source),
576
580
  ...(opts.forkedFromId ? { forkedFromId: opts.forkedFromId } : {}),
577
581
  ...(origin ? { origin } : {}),
582
+ ...(opts.projectId !== undefined ? { projectId: opts.projectId } : {}),
578
583
  originInstance: resolveInstanceName(),
579
584
  ...(opts.tgChatId != null ? { tgChatId: opts.tgChatId } : {}),
580
585
  ...(opts.tgThreadId != null ? { tgThreadId: opts.tgThreadId } : {}),
@@ -601,6 +606,7 @@ export function forkSessionFromHistory(
601
606
  tgThreadId: opts.tgThreadId,
602
607
  title: opts.title,
603
608
  origin: opts.origin,
609
+ projectId: opts.projectId,
604
610
  }, root);
605
611
  for (const msg of opts.messages) {
606
612
  const content = typeof msg.content === 'string' ? msg.content : '';
@@ -738,6 +744,8 @@ export interface ListSessionsOpts {
738
744
  tgThreadId?: number;
739
745
  /** 생성 인스턴스 정확 일치(prod · test:<repo> …). 구 세션(부재)은 비매치. */
740
746
  originInstance?: string;
747
+ /** Project id exact match; sessions without the field do not match. */
748
+ projectId?: string;
741
749
  /** 표면 origin 정확 일치(cli | pwa | tg | dc | harness). */
742
750
  origin?: string;
743
751
  /** 제외할 origin(들) — 예: ['harness'] 로 하니스 자식 세션을 사용자 대화 목록에서 숨김. */
@@ -774,6 +782,7 @@ export function listSessions(opts: ListSessionsOpts = {}, root: string = session
774
782
  idx = idx.filter(m => !m.sourceKind || !opts.excludeSourceKinds!.includes(m.sourceKind));
775
783
  }
776
784
  if (opts.originInstance) idx = idx.filter(m => m.originInstance === opts.originInstance);
785
+ if (opts.projectId !== undefined) idx = idx.filter(m => m.projectId === opts.projectId);
777
786
  if (opts.origin) idx = idx.filter(m => m.origin === opts.origin);
778
787
  if (opts.excludeOrigins && opts.excludeOrigins.length > 0) {
779
788
  idx = idx.filter((m) => {
@@ -210,6 +210,23 @@ export function capturedEnvAvailable(): boolean {
210
210
  return captured && cached !== null;
211
211
  }
212
212
 
213
+ /** Append login-shell PATH entries to the target without replacing its existing environment. */
214
+ export function mergeCapturedPath(target: NodeJS.ProcessEnv = process.env, opts: { capture?: boolean } = {}): number {
215
+ // capture: 아직 캡처 전이면 여기서 한 번 캡처한다(호출부가 getCapturedEnv 를 직접 부르지 않게 — identity-env 래칫).
216
+ const capturedPath = opts.capture || capturedEnvAvailable() ? getCapturedEnv().PATH : undefined;
217
+ const seen = new Set((target.PATH ?? '').split(':'));
218
+ const missing: string[] = [];
219
+ for (const entry of capturedPath?.split(':') ?? []) {
220
+ if (!entry || seen.has(entry)) continue;
221
+ missing.push(entry);
222
+ seen.add(entry);
223
+ }
224
+ if (missing.length) target.PATH = target.PATH ? `${target.PATH}:${missing.join(':')}` : missing.join(':');
225
+ const added = missing.length;
226
+ debug.log('shell.envbootstrap.capture', 'path-merged', { added });
227
+ return added;
228
+ }
229
+
213
230
  /** Reset cache — test seam. */
214
231
  export function resetCapturedEnvForTesting(): void {
215
232
  cached = null;
@@ -1,9 +1,7 @@
1
- // Slash command: /usage
2
- //
3
- // Screen does not compute. It prints the same structured report the
4
- // `elanous usage` command produces.
1
+ // Slash commands: /usage and /remaining — compact TUI view of the unified report.
5
2
 
6
- import { collectUnifiedUsage, formatUnifiedUsage, type UnifiedUsageDeps } from '../../budget/unified-usage.js';
3
+ import { collectUnifiedUsage, formatUsageCompact, type UnifiedUsageDeps } from '../../budget/unified-usage.js';
4
+ import { visibleWidth } from '../../tui.js';
7
5
  import type { SlashExecuteRequest, SlashExecuteResult } from './dashboard-slash.js';
8
6
 
9
7
  export interface UsageSlashResult extends SlashExecuteResult {
@@ -12,15 +10,18 @@ export interface UsageSlashResult extends SlashExecuteResult {
12
10
  }
13
11
 
14
12
  export async function executeUsageSlash(
15
- req: SlashExecuteRequest,
13
+ req: SlashExecuteRequest & { width?: number },
16
14
  deps: UnifiedUsageDeps = {},
17
15
  ): Promise<UsageSlashResult | null> {
18
16
  if (req.name !== 'usage' && req.name !== 'remaining') return null;
19
17
  const report = await collectUnifiedUsage(deps);
18
+ const width = req.width ?? 120;
19
+ const hint = ['자세히: elanous usage', 'elanous usage', 'elanous…', '…']
20
+ .find((text) => visibleWidth(text) <= width) ?? '';
20
21
  return {
21
22
  ok: true,
22
23
  name: req.name,
23
24
  args: req.args,
24
- logLines: formatUnifiedUsage(report).split('\n').filter((line) => line.length > 0),
25
+ logLines: [...formatUsageCompact(report, { width, nowMs: Date.now() }), hint],
25
26
  };
26
27
  }
@@ -1,10 +1,15 @@
1
- import { createHash } from 'node:crypto';
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
2
5
  import { getSecretAsync } from '../nexus/config/secrets/index.js';
3
6
  import { getUserConfig } from '../user-config.js';
4
7
 
5
8
  export type DirectiveSource = 'telegram' | 'pwa' | 'tui' | 'cli';
6
9
  export interface DirectiveIssue { id: string; identifier: string; title: string; description?: string | null }
10
+ export interface DirectiveOrigin { chatId: number; botId?: string; threadId?: number }
7
11
  export interface DirectiveDeps {
12
+ root?: string;
8
13
  fetch?: typeof fetch;
9
14
  getSecret?: (id: string) => Promise<string | undefined>;
10
15
  team?: string;
@@ -31,8 +36,50 @@ async function graphql<T>(key: string, query: string, variables: Record<string,
31
36
  return payload.data;
32
37
  }
33
38
 
39
+ function originPath(root: string, identifier: string): string {
40
+ if (!/^[a-zA-Z0-9_-]+$/.test(identifier)) throw new Error('Invalid directive identifier');
41
+ return join(root, 'steward', 'origins', `${identifier}.json`);
42
+ }
43
+
44
+ type TelegramOrigin = import('../autopilot/mission-origin.js').MissionOrigin & { channel: 'telegram'; chatId: number };
45
+
46
+ function validOrigin(value: unknown): value is TelegramOrigin {
47
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
48
+ const origin = value as Record<string, unknown>;
49
+ return origin.channel === 'telegram' && Number.isSafeInteger(origin.chatId) &&
50
+ (origin.botId === undefined || typeof origin.botId === 'string' && /^\d+$/.test(origin.botId)) &&
51
+ (origin.threadId === undefined || Number.isSafeInteger(origin.threadId));
52
+ }
53
+
54
+ export function loadDirectiveOrigins(root: string, identifier: string): TelegramOrigin[] {
55
+ try {
56
+ const value: unknown = JSON.parse(readFileSync(originPath(root, identifier), 'utf8'));
57
+ const origins = Array.isArray(value) ? value : [value];
58
+ return origins.filter(validOrigin);
59
+ } catch { return []; }
60
+ }
61
+
62
+ export function saveDirectiveOrigin(root: string, identifier: string, origin: DirectiveOrigin): void {
63
+ if (!Number.isSafeInteger(origin.chatId) || origin.threadId !== undefined && !Number.isSafeInteger(origin.threadId)) throw new Error('Invalid directive origin');
64
+ const path = originPath(root, identifier);
65
+ const next: TelegramOrigin = { channel: 'telegram', chatId: origin.chatId,
66
+ ...(origin.botId && /^\d+$/.test(origin.botId) ? { botId: origin.botId } : {}),
67
+ ...(origin.threadId !== undefined ? { threadId: origin.threadId } : {}) };
68
+ const origins = loadDirectiveOrigins(root, identifier);
69
+ if (origins.some(item => JSON.stringify(item) === JSON.stringify(next))) return;
70
+ origins.push(next);
71
+ mkdirSync(join(root, 'steward', 'origins'), { recursive: true });
72
+ const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
73
+ writeFileSync(temp, JSON.stringify(origins.length === 1 ? next : origins));
74
+ renameSync(temp, path);
75
+ }
76
+
77
+ export function loadDirectiveOrigin(root: string, identifier: string): TelegramOrigin | null {
78
+ return loadDirectiveOrigins(root, identifier)[0] ?? null;
79
+ }
80
+
34
81
  /** The same entry point is used by the CLI and conversational surfaces. */
35
- export async function addDirective(text: string, options: { source?: DirectiveSource; dryRun?: boolean } = {}, deps: DirectiveDeps = {}): Promise<{ status: 'created' | 'repeated' | 'dry-run'; issue?: string; hash: string }> {
82
+ export async function addDirective(text: string, options: { source?: DirectiveSource; dryRun?: boolean; origin?: DirectiveOrigin } = {}, deps: DirectiveDeps = {}): Promise<{ status: 'created' | 'repeated' | 'dry-run'; issue?: string; hash: string }> {
36
83
  const original = text.trim();
37
84
  if (!original) throw new Error('directive text is required');
38
85
  const source = options.source ?? 'cli';
@@ -74,6 +121,7 @@ export async function addDirective(text: string, options: { source?: DirectiveSo
74
121
  'mutation($input:CommentCreateInput!){commentCreate(input:$input){success}}',
75
122
  { input: { issueId: duplicate.id, body: `다시 말함 · ${source} · ${at}\n\n${original}` } });
76
123
  if (!result.commentCreate?.success) throw new Error('Linear comment creation failed');
124
+ if (source === 'telegram' && options.origin) saveDirectiveOrigin(deps.root ?? effectiveInstanceRoot(), duplicate.identifier, options.origin);
77
125
  return { status: 'repeated', issue: duplicate.identifier, hash };
78
126
  }
79
127
  const labels = await request<{ issueLabels: { nodes: Array<{ id: string; name: string }> } }>(
@@ -90,6 +138,7 @@ export async function addDirective(text: string, options: { source?: DirectiveSo
90
138
  'mutation($input:IssueCreateInput!){issueCreate(input:$input){success issue{id identifier title}}}',
91
139
  { input: { teamId, title: original.slice(0, 240), description: `${original}\n\n출처: ${source}\n시각: ${at}\n[directive-hash:${hash}]`, labelIds: [labelId], priority: 0 } });
92
140
  if (!created.issueCreate?.success || !created.issueCreate.issue?.identifier) throw new Error('Linear issue creation failed');
141
+ if (source === 'telegram' && options.origin) saveDirectiveOrigin(deps.root ?? effectiveInstanceRoot(), created.issueCreate.issue.identifier, options.origin);
93
142
  return { status: 'created', issue: created.issueCreate.issue.identifier, hash };
94
143
  }
95
144