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
@@ -11,9 +11,14 @@
11
11
  // - 워크플로 트리거는 core 의 `POST /v1/workflows/telegram-dispatch` 로 넘긴다(`telegram-dispatch-forward.ts`).
12
12
 
13
13
  import { debug } from './debug/log.js';
14
+ import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';
15
+ import { randomUUID } from 'node:crypto';
16
+ import { dirname, join } from 'node:path';
17
+ import { effectiveInstanceRoot } from './instance/resolve.js';
14
18
  import { getUserConfig } from './user-config.js';
15
19
  import { resolveChannelBotToken } from './channel-bot-token.js';
16
20
  import { forwardTelegramDispatch } from './telegram-dispatch-forward.js';
21
+ import { mergeCapturedPath, warmCapturedEnv } from './shell-env-bootstrap.js';
17
22
  import type { NexusTelegramQaPollerWireHandle } from './nexus/index.js';
18
23
 
19
24
  export interface TelegramRunResult {
@@ -55,10 +60,10 @@ export function setTelegramPollLockForTesting(deps: typeof pollLockForTesting):
55
60
  /** 폴러를 띄우고 핸들을 돌려준다(시험용으로 분리 — 대기는 `runTelegramPoller` 가 한다). */
56
61
  export async function startTelegramPollers(): Promise<TelegramRunResult> {
57
62
  const cfg = getUserConfig();
58
- const mainToken = resolveChannelBotToken('telegram', cfg)?.token;
59
- if (!cfg.telegram.enabled || !mainToken) {
60
- throw new Error('telegram run: telegram.enabled 가 꺼져 있거나 bot token 이 없다');
61
- }
63
+ if (cfg.telegram.enabled === false) throw new Error('telegram run: telegram.enabled 가 꺼져 있다');
64
+ const resolved = resolveChannelBotToken('telegram', cfg);
65
+ if (!resolved.token) throw new Error(`telegram run: bot token 못 풂 — ${resolved.reason}`);
66
+ const mainToken = resolved.token;
62
67
  if (cfg.telegram.poller !== 'standalone') {
63
68
  console.warn('[telegram run] ⚠️ telegram.poller 가 standalone 이 아니다 — 넥서스도 같은 토큰을 폴링하려 한다. 토큰 잠금이 먼저 잡은 쪽만 띄운다.');
64
69
  }
@@ -105,25 +110,97 @@ export async function startTelegramPollers(): Promise<TelegramRunResult> {
105
110
  return { started, refusedBotIds, late };
106
111
  }
107
112
 
108
- /** SIGINT/SIGTERM 까지 돈다. */
109
- export async function runTelegramPoller(): Promise<void> {
113
+ function failurePath(): string { return join(effectiveInstanceRoot(), 'telegram', 'run-failures.json'); }
114
+
115
+ function failureState(): { count: number; alerted: boolean } {
116
+ const path = failurePath();
117
+ if (!existsSync(path)) return { count: 0, alerted: false };
118
+ const data: unknown = JSON.parse(readFileSync(path, 'utf8'));
119
+ if (!data || typeof data !== 'object' || !('count' in data) || !Number.isSafeInteger(data.count)
120
+ || (data.count as number) < 0 || ('alerted' in data && typeof data.alerted !== 'boolean')) {
121
+ throw new Error(`invalid telegram run failures at ${path}`);
122
+ }
123
+ return { count: data.count as number, alerted: 'alerted' in data ? data.alerted as boolean : false };
124
+ }
125
+
126
+ function writeFailureState(count: number, alerted: boolean): void {
127
+ const path = failurePath();
128
+ mkdirSync(dirname(path), { recursive: true });
129
+ const tmp = `${path}.${randomUUID()}.tmp`;
130
+ try {
131
+ writeFileSync(tmp, JSON.stringify({ count, alerted }), { mode: 0o600, flag: 'wx' });
132
+ renameSync(tmp, path);
133
+ } finally { if (existsSync(tmp)) unlinkSync(tmp); }
134
+ }
135
+
136
+ /** SIGINT/SIGTERM 까지 돈다. Failure exit policy is tested without a live poller. */
137
+ export async function runTelegramPoller(deps: {
138
+ start?: typeof startTelegramPollers;
139
+ alert?: (text: string, kind: string) => boolean;
140
+ wait?: () => Promise<void>;
141
+ registerLogSink?: () => Promise<void>;
142
+ warm?: () => Promise<boolean>;
143
+ } = {}): Promise<void> {
144
+ void (deps.warm ?? warmCapturedEnv)().then((ok) => {
145
+ if (ok) mergeCapturedPath();
146
+ }).catch((error) => {
147
+ debug.log('shell.envbootstrap.capture', 'warm-failed', { error: String(error) });
148
+ });
110
149
  try {
111
- const { registerStandaloneLogSink } = await import('./domains/standalone-log-sink.js');
112
- await registerStandaloneLogSink('telegram-run');
150
+ if (deps.registerLogSink) await deps.registerLogSink();
151
+ else {
152
+ const { registerStandaloneLogSink } = await import('./domains/standalone-log-sink.js');
153
+ await registerStandaloneLogSink('telegram-run');
154
+ }
113
155
  } catch (e) {
114
156
  console.error(`[telegram run] ⚠️ 로그 싱크 등록 실패(관측 유실 가능): ${e instanceof Error ? e.message : String(e)}`);
115
157
  }
116
158
  console.log('[telegram run] 토큰 잠금을 잡는 중 (토큰마다 최대 30초)…');
117
- const { started, refusedBotIds, late } = await startTelegramPollers();
159
+ let result: TelegramRunResult;
160
+ try {
161
+ if (!deps.start) {
162
+ const [{ registerAllCloudBackends }, { selectBackendFromConfig }] = await Promise.all([
163
+ import('./nexus/config/secrets/register-cloud.js'), import('./nexus/config/secrets/registry.js'),
164
+ ]);
165
+ registerAllCloudBackends();
166
+ selectBackendFromConfig();
167
+ }
168
+ result = await (deps.start ?? startTelegramPollers)();
169
+ if (result.started.length === 0) throw new Error('telegram run: 띄운 폴러가 없다');
170
+ } catch (error) {
171
+ const reason = error instanceof Error ? error.message : 'unknown start failure';
172
+ const previous = failureState();
173
+ const count = previous.count + 1;
174
+ writeFailureState(count, previous.alerted);
175
+ debug.log('telegram.run', 'start-failed', { count, reason });
176
+ if (count >= 3) {
177
+ if (!previous.alerted) {
178
+ try {
179
+ const alert = deps.alert ?? (await import('./domains/outbound-alert.js')).sendOutbound;
180
+ if (!alert(`텔레그램 폴러가 3번 연속 못 떴다 — ${reason}`, 'alert')) {
181
+ throw new Error('telegram run: outbound alert not delivered');
182
+ }
183
+ } catch (alertError) {
184
+ debug.log('telegram.run', 'alert-failed', { count, reason });
185
+ throw alertError;
186
+ }
187
+ writeFailureState(count, true);
188
+ debug.log('telegram.run', 'alerted', { count, reason });
189
+ }
190
+ return;
191
+ }
192
+ throw error;
193
+ }
194
+ const { started, refusedBotIds, late } = result;
195
+ const path = failurePath();
196
+ if (existsSync(path)) unlinkSync(path);
118
197
  debug.log('telegram.run', 'started', { pid: process.pid, pollers: started.length, refusedBotIds });
119
198
  for (const botId of refusedBotIds) {
120
199
  console.warn(`[telegram run] 봇 ${botId}: 다른 프로세스가 폴링 중 — 15초마다 다시 잡으러 간다 (elanous logs --category telegram.run)`);
121
200
  }
122
- // 하나도 못 잡았으면 끝낸다(exit≠0) — 서비스 관리자(launchd KeepAlive·systemd Restart)가 다시 띄운다.
123
- if (started.length === 0) throw new Error('telegram run: 띄운 폴러가 없다');
124
201
  for (const { channel } of started) console.log(`[telegram run] 채널 '${channel.name}' 폴러 활성 (${channel.botToken.split(':')[0]})`);
125
202
  console.log('[telegram run] Ctrl-C 로 멈춘다.');
126
- await new Promise<void>((resolve) => {
203
+ await (deps.wait?.() ?? new Promise<void>((resolve) => {
127
204
  const stop = (): void => {
128
205
  process.off('SIGINT', stop);
129
206
  process.off('SIGTERM', stop);
@@ -135,5 +212,5 @@ export async function runTelegramPoller(): Promise<void> {
135
212
  };
136
213
  process.on('SIGINT', stop);
137
214
  process.on('SIGTERM', stop);
138
- });
215
+ }));
139
216
  }
package/src/telegram.ts CHANGED
@@ -71,6 +71,7 @@ import { effectiveInstanceRoot } from './instance/resolve.js';
71
71
  import { getElanousConfigDir } from './elanous-config-dir.js';
72
72
  import { defaultFieldEvent, FieldUploadError, parseFieldCaption, resolveFieldMime, saveFieldMedia } from './field/field-media.js';
73
73
  import { scheduleFieldReel, type FieldReelOptions } from './field/field-reel.js';
74
+ import { maybeHandleCardPhoto, type CardPhotoDeps } from './telegram-card-followup.js';
74
75
 
75
76
  export type TgAttachmentKind = 'photo' | 'voice' | 'audio' | 'document';
76
77
 
@@ -216,6 +217,8 @@ export interface TelegramBotOpts {
216
217
  fieldRootDir?: () => string;
217
218
  /** 시험에서는 실제 렌더 대신 가짜 실행기를 주입한다. */
218
219
  fieldReel?: FieldReelOptions;
220
+ /** Test seam for the business-card graph and its private run root. */
221
+ cardFollowupDeps?: Pick<CardPhotoDeps, 'runGraph' | 'rootDir' | 'ocrText'>;
219
222
  }
220
223
 
221
224
  /** Surface-unification v2 (FU-2) — Telegram trigger event passed to
@@ -333,10 +336,11 @@ export class TelegramBot {
333
336
  private readonly fieldDefaultEventOpt?: string;
334
337
  private readonly fieldRootDir: () => string;
335
338
  private readonly fieldReel?: FieldReelOptions;
339
+ private readonly cardFollowupDeps: Pick<CardPhotoDeps, 'runGraph' | 'rootDir' | 'ocrText'>;
336
340
  private readonly seatWorkDeps: TelegramSeatWorkDeps;
337
341
  /** 앨범 id → `#현장` 행사 · 답장 디바운스. 태그된 앨범만 들어온다. */
338
342
  private readonly fieldGroups = new Map<string, {
339
- event: string; at: number; chatId: number; threadId?: number; replyTo: number; count: number;
343
+ event: string; mode?: 'instant'; at: number; chatId: number; threadId?: number; replyTo: number; count: number;
340
344
  timer?: ReturnType<typeof setTimeout>;
341
345
  }>();
342
346
 
@@ -368,6 +372,7 @@ export class TelegramBot {
368
372
  this.fieldDefaultEventOpt = opts.fieldDefaultEvent;
369
373
  this.fieldRootDir = opts.fieldRootDir ?? getElanousConfigDir;
370
374
  this.fieldReel = opts.fieldReel;
375
+ this.cardFollowupDeps = opts.cardFollowupDeps ?? {};
371
376
  this.seatWorkDeps = opts.seatWorkDeps ?? {};
372
377
  if (opts.onTriggerTap) {
373
378
  // Assign through `as unknown` to bypass `readonly` + private —
@@ -376,7 +381,7 @@ export class TelegramBot {
376
381
  }
377
382
  }
378
383
 
379
- private isOwnerAllowed(userId: number): boolean {
384
+ isOwnerAllowed(userId: number): boolean {
380
385
  return this.allowedUsers.size > 0 && this.allowedUsers.has(userId);
381
386
  }
382
387
 
@@ -1415,6 +1420,10 @@ export class TelegramBot {
1415
1420
  ? tag.event ?? defaultFieldEvent(
1416
1421
  this.fieldDefaultEventOpt ?? this.slashContext?.userConfig?.telegram?.fieldDefaultEvent, new Date(now))
1417
1422
  : group!.event;
1423
+ const captionMode = tag?.mode ?? group?.mode;
1424
+ const configMode = this.slashContext?.userConfig?.telegram?.fieldReelMode;
1425
+ const mode = captionMode ?? configMode ?? 'standard';
1426
+ const source = captionMode ? 'caption' : configMode ? 'config' : 'default';
1418
1427
 
1419
1428
  let count: number | null = null;
1420
1429
  let failure: string | null = null;
@@ -1435,14 +1444,16 @@ export class TelegramBot {
1435
1444
  // 글(자막)은 캡션이 달린 항목에만 있다 — 앨범이면 첫 장에만 붙는다(MK 2026-09-30).
1436
1445
  const saved = saveFieldMedia(inputs, { rootDir: this.fieldRootDir(), event, device: 'telegram', ...(tag?.text ? { caption: tag.text } : {}) });
1437
1446
  count = saved.count;
1447
+ debug.log('telegram.field', 'reel-mode', { event, mode, source });
1438
1448
  scheduleFieldReel(saved.dir, {
1439
1449
  ...this.fieldReel,
1450
+ mode,
1440
1451
  notificationKey: `telegram:${this.botId}:${ctx.chatId}:${ctx.threadId ?? ''}:${ctx.mediaGroupId ?? ctx.messageId}`,
1441
1452
  onDone: async (result) => {
1442
1453
  const reply = { replyTo: ctx.messageId, threadId: ctx.threadId };
1443
1454
  if (result.ok && result.file) {
1444
1455
  const sent = await this.sendDocument(ctx.chatId, readFileSync(result.file), 'reel-9x16.mp4', {
1445
- ...reply, mimeType: 'video/mp4', caption: `현장 영상 · ${event} · ${result.seconds}초`,
1456
+ ...reply, mimeType: 'video/mp4', caption: `현장 영상 · ${event} · ${result.seconds}초${mode === 'instant' ? ' (즉석판)' : ''}`,
1446
1457
  });
1447
1458
  if (!sent) throw new Error('sendDocument failed');
1448
1459
  if (result.feed?.started) {
@@ -1469,7 +1480,8 @@ export class TelegramBot {
1469
1480
  return true;
1470
1481
  }
1471
1482
  // 앨범은 항목마다 답하지 않는다 — 마지막 항목 뒤 한 번(디바운스)만 최신 수로 답한다.
1472
- const entry = group ?? { event, at: now, chatId: ctx.chatId, threadId: ctx.threadId, replyTo: ctx.messageId, count: 0 };
1483
+ const entry = group ?? { event, mode: tag?.mode, at: now, chatId: ctx.chatId, threadId: ctx.threadId, replyTo: ctx.messageId, count: 0 };
1484
+ if (group && tag?.mode) group.mode = tag.mode;
1473
1485
  entry.at = now;
1474
1486
  entry.count = count!;
1475
1487
  if (entry.timer) clearTimeout(entry.timer);
@@ -1490,10 +1502,18 @@ export class TelegramBot {
1490
1502
  }
1491
1503
  // 현장 업로드 — `#현장` 캡션(또는 태그된 앨범의 나머지)의 사진·영상은 LLM 대신 현장 폴더로.
1492
1504
  if (await this.tryHandleFieldUpload(ctx)) return;
1493
- if (ctx.text.startsWith('@')) {
1505
+ if (await maybeHandleCardPhoto(ctx, {
1506
+ isOwner: (userId) => this.isOwnerAllowed(userId),
1507
+ isFieldAlbum: (id) => this.fieldGroups.has(id),
1508
+ downloadFile: (fileId, dir) => this.downloadFile(fileId, dir),
1509
+ sendMessage: (chatId, text, opts) => this.sendMessage(chatId, text, opts),
1510
+ sendDocument: (chatId, body, filename, opts) => this.sendDocument(chatId, body, filename, opts),
1511
+ ...this.cardFollowupDeps,
1512
+ })) return;
1513
+ if (ctx.text.startsWith('@') || (ctx.isDm && ctx.text && !ctx.text.startsWith('/'))) {
1494
1514
  const reply = await handleTelegramSeatWork(ctx.text, {
1495
- chatId: ctx.chatId, messageId: ctx.messageId, threadId: ctx.threadId, botId: ctx.botId,
1496
- }, this.seatWorkDeps);
1515
+ chatId: ctx.chatId, userId: ctx.userId, messageId: ctx.messageId, threadId: ctx.threadId, botId: ctx.botId,
1516
+ }, { ...this.seatWorkDeps, config: this.seatWorkDeps.config ?? this.slashContext?.userConfig });
1497
1517
  if (reply !== null) {
1498
1518
  await this.sendMessage(ctx.chatId, reply, { replyTo: ctx.messageId, threadId: ctx.threadId });
1499
1519
  return;
@@ -1529,7 +1549,7 @@ export class TelegramBot {
1529
1549
  try {
1530
1550
  const { addDirective } = await import('./steward/directive.js');
1531
1551
  const text = ctx.text.startsWith('지시:') ? ctx.text.slice('지시:'.length) : ctx.text.slice('/directive '.length);
1532
- const result = await addDirective(text, { source: 'telegram' });
1552
+ const result = await addDirective(text, { source: 'telegram', origin: { chatId: ctx.chatId, botId: ctx.botId, threadId: ctx.threadId } });
1533
1553
  await this.sendMessage(ctx.chatId, `${result.issue}: ${result.status}`, { replyTo: ctx.messageId, threadId: ctx.threadId });
1534
1554
  } catch {
1535
1555
  await this.sendMessage(ctx.chatId, '지시 등록 실패 — Linear 연결과 자격을 확인하세요.', { replyTo: ctx.messageId, threadId: ctx.threadId });
@@ -2746,6 +2766,7 @@ export function botFromConfig(opts: BotFromConfigOpts): TelegramBot {
2746
2766
  // (makeTelegramAgentRunTurn) threads it into `delegate_code_agent`
2747
2767
  // so a delegated sub-agent's approval prompt lands back here.
2748
2768
  hitlConfirmChannel: botRef.hitlConfirmChannelForChat(ctx.chatId, ctx.threadId),
2769
+ ...(botRef.isOwnerAllowed(ctx.userId) ? { verifiedOwner: { id: `telegram:${ctx.userId}` } } : {}),
2749
2770
  hitlQuestionChannel: botRef.hitlQuestionChannelForChat(ctx.chatId, ctx.threadId),
2750
2771
  // P1.4 — surface file spill bound to THIS chat so the NL
2751
2772
  // `delegate_code_agent` path spills big diffs/stdout back here.
@@ -156,6 +156,14 @@ function renderLine(
156
156
  return `${applyColor(glyph, 'claude')} ${C.muted(message + '…')}${tail}`;
157
157
  }
158
158
 
159
+ function stoppedMessage(message: string, status: ThinkingStatus): string {
160
+ const taskName = message.replace(/^(?:Streaming|Thinking)(?=\b|…)(?:\s*…)?\s*/, '').trim();
161
+ const task = taskName ? `${taskName} ` : '';
162
+ if (status === 'completed') return `${task}완료`;
163
+ if (status === 'interrupted') return `${task}중단됨`;
164
+ return `${task}실패`;
165
+ }
166
+
159
167
  export function startThinking(opts: StartThinkingOpts): ThinkingHandle {
160
168
  let message = opts.message ?? 'Thinking';
161
169
  let messageFactory: ((frame: number) => string) | null = null;
@@ -222,20 +230,19 @@ export function startThinking(opts: StartThinkingOpts): ThinkingHandle {
222
230
  if (metrics.engine) detail.push(metrics.engine);
223
231
  const tail = detail.length ? ' ' + C.muted('(' + detail.join(' · ') + ')') : '';
224
232
  let finalText: string;
225
- if (stopOpts?.finalText) {
233
+ if (stopOpts?.finalText !== undefined) {
226
234
  finalText = stopOpts.finalText;
227
- } else if (status === 'completed') {
228
- // Green tick + verb kept visible (not muted) so it stands out.
229
- const currentMessage = messageFactory ? messageFactory(frame) : message;
230
- finalText = `${applyColor(FIGURES.TICK, 'success')} ${C.success(currentMessage)}${tail}`;
231
- } else if (status === 'interrupted') {
232
- const currentMessage = messageFactory ? messageFactory(frame) : message;
233
- finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.warning(currentMessage + ' · interrupted')}${tail}`;
234
235
  } else {
235
- // failed
236
- const err = stopOpts?.errorText ? ` — ${stopOpts.errorText}` : '';
237
236
  const currentMessage = messageFactory ? messageFactory(frame) : message;
238
- finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.error(currentMessage + ' · failed' + err)}${tail}`;
237
+ const label = stoppedMessage(currentMessage, status);
238
+ if (status === 'completed') {
239
+ finalText = `${applyColor(FIGURES.TICK, 'success')} ${C.success(label)}${tail}`;
240
+ } else if (status === 'interrupted') {
241
+ finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.warning(label)}${tail}`;
242
+ } else {
243
+ const err = stopOpts?.errorText ? ` — ${stopOpts.errorText}` : '';
244
+ finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.error(label + err)}${tail}`;
245
+ }
239
246
  }
240
247
 
241
248
  // Freeze at tail if sticky='tail' — the indicator was tracking
@@ -310,18 +317,19 @@ export function startPinnedThinking(opts: StartPinnedThinkingOpts): ThinkingHand
310
317
  if (metrics.engine) detail.push(metrics.engine);
311
318
  const tail = detail.length ? ' ' + C.muted('(' + detail.join(' · ') + ')') : '';
312
319
  let finalText: string;
313
- if (stopOpts?.finalText) {
320
+ if (stopOpts?.finalText !== undefined) {
314
321
  finalText = stopOpts.finalText;
315
- } else if (status === 'completed') {
316
- const currentMessage = messageFactory ? messageFactory(frame) : message;
317
- finalText = `${applyColor(FIGURES.TICK, 'success')} ${C.success(currentMessage)}${tail}`;
318
- } else if (status === 'interrupted') {
319
- const currentMessage = messageFactory ? messageFactory(frame) : message;
320
- finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.warning(currentMessage + ' · interrupted')}${tail}`;
321
322
  } else {
322
- const err = stopOpts?.errorText ? ` — ${stopOpts.errorText}` : '';
323
323
  const currentMessage = messageFactory ? messageFactory(frame) : message;
324
- finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.error(currentMessage + ' · failed' + err)}${tail}`;
324
+ const label = stoppedMessage(currentMessage, status);
325
+ if (status === 'completed') {
326
+ finalText = `${applyColor(FIGURES.TICK, 'success')} ${C.success(label)}${tail}`;
327
+ } else if (status === 'interrupted') {
328
+ finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.warning(label)}${tail}`;
329
+ } else {
330
+ const err = stopOpts?.errorText ? ` — ${stopOpts.errorText}` : '';
331
+ finalText = `${applyColor(FIGURES.CROSS, 'error')} ${C.error(label + err)}${tail}`;
332
+ }
325
333
  }
326
334
  opts.target.current = finalText;
327
335
  opts.onFrame();
@@ -91,8 +91,12 @@ export interface ToolRuntimeContext {
91
91
  sessionId?: string;
92
92
  /** Session that originated this execution when it was delegated through another runtime session. */
93
93
  originSessionId?: string;
94
+ /** Server-verified owner principal. Never derive this from session source, tool args, or a client-declared origin. */
95
+ verifiedOwner?: { id: string };
94
96
  /** Server-assigned provenance; omitted on existing owner tool paths. */
95
97
  requestOrigin?: RequestOrigin;
98
+ /** Confirm through the originating chat's channels, not another user's global channel. */
99
+ confirmChannels?: import('../hitl/confirm.js').ConfirmChannel[];
96
100
  }
97
101
 
98
102
  /** Output shape the LLM layer expects back. Most tools produce
@@ -1,7 +1,7 @@
1
1
  // ── TUI TRUE-execution badge (self-cognition §1) ──────────────────────
2
2
  //
3
3
  // Resolves the compact engine/model badge for the dashboard chat
4
- // completion line — `✔ Streaming (19s · ↓ 200 tokens · 🧠 terra(high))`.
4
+ // completion line — `✔ 완료 (19s · ↓ 200 tokens · 🧠 terra(high))`.
5
5
  // The value is the model that ACTUALLY ran this turn (from the route
6
6
  // decision recorded in the ACP bridge's getActiveModel), not the config
7
7
  // original — so the "config=opus but codex/terra ran" illusion is
@@ -210,6 +210,8 @@ export class DashboardSession {
210
210
  transportFactory: pair.transportFactory,
211
211
  shutdownSignal: shutdownCtrl.signal,
212
212
  runTurn,
213
+ // PCH-2b — the in-process TUI dispatcher carries its own approval modals.
214
+ toolApproval: 'off',
213
215
  ...(opts.agentName ? { agentName: opts.agentName } : {}),
214
216
  ...(opts.agentVersion ? { agentVersion: opts.agentVersion } : {}),
215
217
  ...(opts.serverOptions ?? {}),