elanous 0.2.11 → 0.2.13

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 (342) 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/chunks/{1394-556de6d71bbdb0fb.js → 1394-931c9827067b2276.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/3869-3cd46526879f454c.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/{3992-2e522a8ac5f2afde.js → 3992-fe5c06d6b31f8b25.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/5250-c78ca295585bbf95.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/6511-ff8360be3d65aaa4.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/6588-ba380869b67c4fa7.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/7758-85c2b53f731da024.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/{8605-ea5bee9dfae512c7.js → 8605-9da4678f8a310d18.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/9906-e7b8b5310a169dfe.js +1 -0
  12. package/apps/pwa/out/_next/static/chunks/app/approvals/page-e5b4e1b256f41076.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-6a061bd97f0b9398.js → page-e49b9b76c565a0b1.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/board/page-b4f032c893b21cc9.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/botlab/page-f14987d5850a31e0.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/bots/page-b433308aad13ceb6.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/chat/{page-7c97dcdfe2e37a60.js → page-4c1dbb21861beab2.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/consult/page-c85b816e3b269dbb.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/control/{page-5721537ca8a3598c.js → page-f6bc66b919b2ea7d.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/design-check/{page-b654240a572e4c48.js → page-60603c1e056c9b25.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/editor/{page-b9750abc897508c3.js → page-a0ea8f5a2341ee6b.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/exec/page-45c5bf04b4a605e6.js +1 -0
  23. package/apps/pwa/out/_next/static/chunks/app/field/page-36c6c4183353ac9e.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/inside/page-cb2eb0fd809980da.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/intake/{page-c8b1ccda6ba7ae98.js → page-31012264da8a341f.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/{layout-d6b1d156fb95e74b.js → layout-50309aeb0bdd554a.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/live/page-bc00e964e855f4df.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/market/{page-9688f5b4f1ff950f.js → page-0696ee3fac9fe56c.js} +1 -1
  29. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66c27608f651b866.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/morning/{page-35414e0a007b4082.js → page-6ddd2109b7fc3a80.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/not-found-4e50b7298c1f69a5.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-e001823c35c9dd67.js → page-7a2bf830038a2264.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-371423d1a573d64b.js → page-1f327f18694fabca.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-4b764b125339ac17.js → page-e5cef2fe6e4028df.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-cdf8e4b01067bcf1.js → page-cf596128d2b420ba.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-3a24072b35470157.js → page-f672536f14f0969f.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/page-b52365662232db89.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/reflection/page-e7a6c80f288b1948.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-d37cb0d52f9b6bd5.js → page-29a709616df9b050.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-686da0df9bf23e5c.js → page-fee842d7c2151129.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-4dc614f3ce043e63.js → page-78b40a344ed22f24.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/settings/page-75d199f448171014.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/setup/done/{page-d02c782fc792e2af.js → page-a011dd5b0bff29c5.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/setup/{page-239c17b911b84e72.js → page-d92e32d51ab5157e.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/share/{page-dc6a79fb003f746c.js → page-5f21aed6932ef375.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-0fffb84cafdc9da6.js → page-1e1a53946804debf.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-1218753c82d585f6.js → page-a1833cd43d832601.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/term/{page-4b5935e67531efb5.js → page-63d14a4d8fe1e1c1.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/today/{page-3285bc87caf1eaa5.js → page-2a511d320f762e06.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/app/trace/{page-ad553b0e06505ecd.js → page-1c3e03f88dac1e21.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/vault/{page-fca096e3e860306f.js → page-c4d5289c2ea97a89.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-9ebfa78f0d9885c3.js → page-b91f8952deaeaf27.js} +1 -1
  53. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-3dc5f21f7e39622d.js → page-71b43a2588489987.js} +1 -1
  54. package/apps/pwa/out/_next/static/chunks/app/workspace/page-2b256feed331546f.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-505bb96d69a8622c.js +1 -0
  56. package/apps/pwa/out/_next/static/css/128fb7fb670b2fbf.css +1 -0
  57. package/apps/pwa/out/approvals/index.html +1 -1
  58. package/apps/pwa/out/approvals/index.txt +14 -14
  59. package/apps/pwa/out/autopilot/index.html +1 -1
  60. package/apps/pwa/out/autopilot/index.txt +14 -14
  61. package/apps/pwa/out/board/index.html +1 -1
  62. package/apps/pwa/out/board/index.txt +14 -14
  63. package/apps/pwa/out/botlab/index.html +1 -1
  64. package/apps/pwa/out/botlab/index.txt +14 -14
  65. package/apps/pwa/out/bots/index.html +1 -1
  66. package/apps/pwa/out/bots/index.txt +14 -14
  67. package/apps/pwa/out/chat/index.html +1 -1
  68. package/apps/pwa/out/chat/index.txt +14 -14
  69. package/apps/pwa/out/consult/index.html +1 -1
  70. package/apps/pwa/out/consult/index.txt +14 -14
  71. package/apps/pwa/out/control/index.html +1 -1
  72. package/apps/pwa/out/control/index.txt +14 -14
  73. package/apps/pwa/out/design-check/index.html +1 -1
  74. package/apps/pwa/out/design-check/index.txt +14 -14
  75. package/apps/pwa/out/editor/index.html +1 -1
  76. package/apps/pwa/out/editor/index.txt +14 -14
  77. package/apps/pwa/out/exec/index.html +1 -1
  78. package/apps/pwa/out/exec/index.txt +14 -14
  79. package/apps/pwa/out/field/index.html +1 -1
  80. package/apps/pwa/out/field/index.txt +14 -14
  81. package/apps/pwa/out/healthz +1 -1
  82. package/apps/pwa/out/index.html +1 -1
  83. package/apps/pwa/out/index.txt +14 -14
  84. package/apps/pwa/out/inside/index.html +1 -1
  85. package/apps/pwa/out/inside/index.txt +14 -14
  86. package/apps/pwa/out/intake/index.html +1 -1
  87. package/apps/pwa/out/intake/index.txt +14 -14
  88. package/apps/pwa/out/live/index.html +1 -1
  89. package/apps/pwa/out/live/index.txt +14 -14
  90. package/apps/pwa/out/market/index.html +1 -1
  91. package/apps/pwa/out/market/index.txt +14 -14
  92. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  93. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  94. package/apps/pwa/out/morning/index.html +1 -1
  95. package/apps/pwa/out/morning/index.txt +14 -14
  96. package/apps/pwa/out/observatory/index.html +1 -1
  97. package/apps/pwa/out/observatory/index.txt +14 -14
  98. package/apps/pwa/out/ops/checklist/index.html +1 -1
  99. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  100. package/apps/pwa/out/ops/release/index.html +1 -1
  101. package/apps/pwa/out/ops/release/index.txt +14 -14
  102. package/apps/pwa/out/ops/seats/index.html +1 -1
  103. package/apps/pwa/out/ops/seats/index.txt +14 -14
  104. package/apps/pwa/out/outputs/index.html +1 -1
  105. package/apps/pwa/out/outputs/index.txt +14 -14
  106. package/apps/pwa/out/reflection/index.html +1 -1
  107. package/apps/pwa/out/reflection/index.txt +15 -15
  108. package/apps/pwa/out/scheduler/index.html +1 -1
  109. package/apps/pwa/out/scheduler/index.txt +14 -14
  110. package/apps/pwa/out/sessions/index.html +1 -1
  111. package/apps/pwa/out/sessions/index.txt +14 -14
  112. package/apps/pwa/out/settings/devices/index.html +1 -1
  113. package/apps/pwa/out/settings/devices/index.txt +14 -14
  114. package/apps/pwa/out/settings/index.html +1 -1
  115. package/apps/pwa/out/settings/index.txt +14 -14
  116. package/apps/pwa/out/setup/done/index.html +1 -1
  117. package/apps/pwa/out/setup/done/index.txt +14 -14
  118. package/apps/pwa/out/setup/index.html +1 -1
  119. package/apps/pwa/out/setup/index.txt +14 -14
  120. package/apps/pwa/out/share/index.html +1 -1
  121. package/apps/pwa/out/share/index.txt +14 -14
  122. package/apps/pwa/out/showroom/index.html +1 -1
  123. package/apps/pwa/out/showroom/index.txt +14 -14
  124. package/apps/pwa/out/tasks/index.html +1 -1
  125. package/apps/pwa/out/tasks/index.txt +14 -14
  126. package/apps/pwa/out/term/index.html +1 -1
  127. package/apps/pwa/out/term/index.txt +14 -14
  128. package/apps/pwa/out/today/index.html +1 -1
  129. package/apps/pwa/out/today/index.txt +14 -14
  130. package/apps/pwa/out/trace/index.html +1 -1
  131. package/apps/pwa/out/trace/index.txt +14 -14
  132. package/apps/pwa/out/vault/index.html +1 -1
  133. package/apps/pwa/out/vault/index.txt +14 -14
  134. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  135. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  136. package/apps/pwa/out/workflows/index.html +1 -1
  137. package/apps/pwa/out/workflows/index.txt +14 -14
  138. package/apps/pwa/out/workspace/index.html +1 -1
  139. package/apps/pwa/out/workspace/index.txt +14 -14
  140. package/apps/pwa/out/worktrees/index.html +1 -1
  141. package/apps/pwa/out/worktrees/index.txt +14 -14
  142. package/examples/teams/personas/backend.yaml +4 -0
  143. package/examples/teams/personas/pm.yaml +4 -0
  144. package/examples/teams/personas/qa.yaml +4 -0
  145. package/examples/teams/small-product.yaml +20 -0
  146. package/graphs/context/nightly-condense.yaml +21 -0
  147. package/graphs/context/recipes.yaml +12 -0
  148. package/graphs/demo/inside-seed.yaml +20 -0
  149. package/graphs/demo/recipes.yaml +12 -0
  150. package/graphs/lecture/lecture-note.yaml +28 -0
  151. package/graphs/lecture/recipes.yaml +17 -0
  152. package/package.json +2 -1
  153. package/plugins/card-followup/examples/sample-card.png +0 -0
  154. package/scripts/context-hooks/emit.ts +36 -14
  155. package/scripts/coord-channel-watch.sh +1 -0
  156. package/scripts/discord-seat-probe.ts +144 -0
  157. package/scripts/hq/hq-standby.sh +65 -0
  158. package/scripts/hq/standby-promote.ts +173 -0
  159. package/scripts/hq/standby-role.ts +59 -0
  160. package/scripts/hq/standby-snapshot.ts +226 -0
  161. package/scripts/hq/standby-verify.ts +219 -0
  162. package/scripts/lecture-note/node.ts +256 -0
  163. package/scripts/lib/telegram-inject-args.ts +31 -0
  164. package/scripts/measure-pod-memory-by-goal.ts +130 -0
  165. package/scripts/release-loop/auto-approve-node.ts +1 -1
  166. package/scripts/release-loop/checklist-gate-node.ts +2 -1
  167. package/scripts/release-loop/gate-node.ts +61 -17
  168. package/scripts/release-loop/ops-upgrade-node.ts +59 -6
  169. package/scripts/release-loop/publish-node.ts +4 -1
  170. package/scripts/release-loop/version-node.ts +13 -3
  171. package/scripts/telegram-inject.ts +14 -6
  172. package/scripts/test-deterministic.ts +53 -3
  173. package/scripts/test-diet/remeasure-list.ts +112 -0
  174. package/scripts/ux/demo-rehearsal.ts +17 -13
  175. package/scripts/ux/demo-seed.ts +108 -0
  176. package/scripts/ux/lib/rehearsal-verdict.ts +12 -5
  177. package/skills/omni-crawl/SKILL.md +16 -16
  178. package/skills/omni-crawl/package-lock.json +1 -544
  179. package/skills/omni-crawl/package.json +3 -4
  180. package/skills/omni-crawl/scripts/main.ts +13 -13
  181. package/skills/omni-crawl/scripts/monitor.ts +8 -8
  182. package/skills/omni-crawl/src/apify.ts +13 -2
  183. package/skills/omni-crawl/src/capture.ts +6 -0
  184. package/skills/omni-crawl/src/registry.ts +2 -2
  185. package/skills/omni-crawl/src/stdout-json.ts +4 -0
  186. package/skills/omni-digest/SKILL.md +10 -11
  187. package/skills/omni-digest/package-lock.json +0 -545
  188. package/skills/omni-digest/package.json +2 -5
  189. package/skills/omni-digest/scripts/main.ts +3 -3
  190. package/skills/omni-digest/src/delegate.ts +5 -5
  191. package/skills/omni-digest/src/fetch.ts +2 -2
  192. package/skills/omni-digest/src/media.ts +25 -11
  193. package/skills/omni-digest/src/visual.ts +3 -2
  194. package/skills/omni-market/scripts/main.ts +1 -1
  195. package/skills/omni-market/scripts/stdout-json.ts +4 -0
  196. package/src/agent/identity-env.ts +11 -0
  197. package/src/agent-mission/driver.ts +150 -23
  198. package/src/agent-mission/duo.ts +94 -0
  199. package/src/agent-mission/pty-decision.ts +2 -2
  200. package/src/autopilot/pty-control-loop.ts +4 -1
  201. package/src/autopilot/track-agent-shadow.ts +2 -1
  202. package/src/autopilot/track-agent.ts +4 -1
  203. package/src/boot/daemon-prompt-request.ts +3 -0
  204. package/src/boot/daemon-prompt-turn.ts +6 -2
  205. package/src/boot/daemon-runtime.ts +48 -16
  206. package/src/chat/index.ts +1 -0
  207. package/src/cli/decisions-cli.ts +51 -19
  208. package/src/cli/outputs-cli.ts +92 -0
  209. package/src/cli/persona-cli.ts +45 -13
  210. package/src/cli/pod-cli.ts +27 -3
  211. package/src/cli/pod-memory-advice.ts +35 -0
  212. package/src/cli/pr-cli.ts +15 -1
  213. package/src/cli/release-cli.ts +135 -15
  214. package/src/context-bus/condense.ts +194 -0
  215. package/src/context-bus/context-day.ts +82 -0
  216. package/src/context-bus/context-now-surfaces.ts +32 -2
  217. package/src/context-bus/context-now.ts +2 -2
  218. package/src/context-bus/coord-events.ts +2 -2
  219. package/src/context-bus/long-term-memory.ts +118 -22
  220. package/src/context-bus/nightly-condense.ts +153 -0
  221. package/src/context-bus/session-events.ts +35 -0
  222. package/src/dashboard/dashboard-status-lines.ts +2 -0
  223. package/src/dashboard/index.ts +8 -0
  224. package/src/dashboard/slash-runtime/dashboard-handlers.ts +24 -0
  225. package/src/decisions/decision-cards.ts +13 -7
  226. package/src/decisions/decision-ledger.ts +43 -17
  227. package/src/domains/outbound-alert.ts +99 -31
  228. package/src/domains/outbound-log-probe.ts +8 -0
  229. package/src/execution-loop/launch-gate.ts +17 -8
  230. package/src/field-note/telegram.ts +142 -0
  231. package/src/graph-kinds/registry.ts +2 -2
  232. package/src/graph-runner/graph-approval-card.ts +2 -2
  233. package/src/graph-runner/graph-grow-llm.ts +23 -13
  234. package/src/graph-runner/graph-grow.ts +69 -13
  235. package/src/graph-runner/graph-tick.ts +9 -2
  236. package/src/graph-runner/runner.ts +92 -24
  237. package/src/harness/harness-cli-command.ts +165 -17
  238. package/src/harness/harness-queue-child.ts +37 -0
  239. package/src/harness/harness-queue.ts +306 -0
  240. package/src/hooks/consult-intake.ts +153 -0
  241. package/src/hooks/github-shadow.ts +72 -2
  242. package/src/hooks/receiver.ts +82 -3
  243. package/src/hq/hq.ts +318 -0
  244. package/src/hq/lease.ts +210 -0
  245. package/src/hq/ledger-write-target.ts +41 -0
  246. package/src/index.ts +232 -16
  247. package/src/intake-plane/discord-seat-work.ts +36 -3
  248. package/src/intake-plane/linear-wish.ts +44 -0
  249. package/src/intake-plane/persona-answer.ts +5 -0
  250. package/src/intake-plane/telegram-seat-work.ts +13 -6
  251. package/src/intake-plane/wish-card.ts +33 -0
  252. package/src/llm/difficulty.ts +1 -1
  253. package/src/loops/checker.ts +178 -0
  254. package/src/loops/loop-cli.ts +37 -3
  255. package/src/loops/observe.ts +49 -0
  256. package/src/loops/orchestrator/overlap.ts +141 -0
  257. package/src/loops/orchestrator/traffic.ts +150 -0
  258. package/src/loops/package/verify.ts +125 -0
  259. package/src/loops/verdict.ts +22 -0
  260. package/src/maturity/feature-maturity.ts +5 -4
  261. package/src/memory.ts +37 -4
  262. package/src/nexus/api/card-followup-route.ts +118 -0
  263. package/src/nexus/api/decisions-route.ts +68 -0
  264. package/src/nexus/api/http-server.ts +78 -8
  265. package/src/nexus/api/outbound-report.ts +40 -7
  266. package/src/nexus/api/personas.ts +97 -54
  267. package/src/nexus/api/skill-exec-route.ts +97 -0
  268. package/src/nexus/api/task-cards-api.ts +19 -0
  269. package/src/nexus/api/workflow-history.ts +88 -0
  270. package/src/nexus/api/workflows.ts +52 -0
  271. package/src/nexus/outbound/delivery-ledger.ts +17 -8
  272. package/src/nexus/outbound/router.ts +8 -3
  273. package/src/persona/loader.ts +18 -0
  274. package/src/persona/persona-todo.ts +14 -9
  275. package/src/persona/presets.ts +10 -2
  276. package/src/persona/team.ts +105 -0
  277. package/src/persona/types.ts +4 -0
  278. package/src/plugins/maker/market-bundle.ts +2 -1
  279. package/src/pod-lease/host-lease.ts +130 -20
  280. package/src/release-loop/checklist.ts +15 -3
  281. package/src/release-loop/feature-store.ts +24 -10
  282. package/src/release-loop/landed-but-yellow.ts +60 -0
  283. package/src/seat-dispatch/seat-ask.ts +57 -12
  284. package/src/seat-loop/seat-loop.ts +253 -18
  285. package/src/seat-loop/seat-pack.ts +127 -0
  286. package/src/self-dev/ask-launch-flow.ts +3 -0
  287. package/src/self-dev/draft-sweep.ts +44 -10
  288. package/src/self-dev/draft-triage-rules.ts +54 -2
  289. package/src/self-implement/budget-gate.ts +29 -12
  290. package/src/self-implement/gate-candidate-ranking.ts +60 -0
  291. package/src/self-implement/gate-scope.ts +9 -8
  292. package/src/self-implement/headless-elanous-driver.ts +7 -2
  293. package/src/self-implement/l8-merge-queue.ts +157 -1
  294. package/src/self-implement/orchestrator.ts +15 -0
  295. package/src/self-implement/test-seams.ts +1 -0
  296. package/src/steward/launch.ts +5 -1
  297. package/src/steward/readiness.ts +125 -0
  298. package/src/steward/rescue.ts +75 -0
  299. package/src/steward/triage.ts +48 -9
  300. package/src/task-cards/card-cli.ts +78 -0
  301. package/src/task-orchestrator/surfaces/pod-command-job.ts +14 -10
  302. package/src/task-orchestrator/surfaces/pod-lease.ts +18 -6
  303. package/src/task-orchestrator/surfaces/pod-pool.ts +27 -10
  304. package/src/task-orchestrator/surfaces/self-implement-pod.ts +46 -8
  305. package/src/telegram-commands.ts +21 -0
  306. package/src/telegram-dispatch-forward.ts +1 -1
  307. package/src/telegram-fabric-plan.ts +90 -0
  308. package/src/telegram-project-command.ts +84 -4
  309. package/src/telegram.ts +37 -5
  310. package/src/tool-runtime/elanous-control-runtimes.ts +2 -1
  311. package/src/user-config.ts +101 -13
  312. package/src/version/packed-revision.json +1 -1
  313. package/src/workflow-runtime/executor.ts +4 -0
  314. package/src/workflow-runtime/node-catalog.ts +13 -0
  315. package/src/workflow-runtime/nodes/knowledge.ts +38 -0
  316. package/src/workflow-runtime/schema.ts +17 -1
  317. package/src/workflow-runtime/types.ts +13 -1
  318. package/apps/pwa/out/_next/static/chunks/2310-7a59f043f6db207e.js +0 -1
  319. package/apps/pwa/out/_next/static/chunks/2548-6e316138d347cefe.js +0 -1
  320. package/apps/pwa/out/_next/static/chunks/3869-a39d1d9921e10d43.js +0 -1
  321. package/apps/pwa/out/_next/static/chunks/507-5acd98bd6dab2d8a.js +0 -1
  322. package/apps/pwa/out/_next/static/chunks/5445-745eb795c5963d79.js +0 -1
  323. package/apps/pwa/out/_next/static/chunks/9906-482c6619b3fdda20.js +0 -1
  324. package/apps/pwa/out/_next/static/chunks/app/approvals/page-1dcdc58fc2de2454.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/board/page-cb95a7432c9ee744.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/botlab/page-b12b6d2f8d2d1aea.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/bots/page-b9efe868bfd36255.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/consult/page-8fd36984f549270a.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/exec/page-4cd1de371f8ad048.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/field/page-d99f89657828421c.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/inside/page-50cb2a0eb15e6c83.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/live/page-2a9932ddb62d76bd.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-6b15dd6456c5df09.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/not-found-e7a529e0507ac86c.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/page-94c82eb0ae5abb32.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/reflection/page-0be8d4b2ced75ba0.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/settings/page-53f80edc6b8dc07c.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/workspace/page-c6da2b468454532d.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-93bc70e36f595414.js +0 -1
  340. package/apps/pwa/out/_next/static/css/543f56bcb1f79fca.css +0 -1
  341. /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_buildManifest.js +0 -0
  342. /package/apps/pwa/out/_next/static/{Y4zKzL62sakG5momz3SoQ → D51l8jUBplJrIpWvENsDK}/_ssgManifest.js +0 -0
package/src/index.ts CHANGED
@@ -41,6 +41,7 @@ import { registerRepoCommands } from './cli/repo-cli.js';
41
41
  import { registerAgentCommands, runChatTurnCli, emitDetachedProgress } from './cli/agent-cli.js';
42
42
  export { runChatTurnCli, buildCliAgentTools, setCliAgentDispatchForTesting, emitDetachedProgress, emitHarnessFeedbackProgress } from './cli/agent-cli.js';
43
43
  import { registerOpsCommands } from './cli/ops-cli.js';
44
+ import { registerOutputsCommands } from './cli/outputs-cli.js';
44
45
  import { registerPublishCommands } from './cli/publish-cli.js';
45
46
  import { registerAutopilotCommands } from './cli/autopilot-cli.js';
46
47
  import { registerRoleCommands } from './cli/role-cli.js';
@@ -103,7 +104,7 @@ import type { AcpPermissionApprover } from './acp/client.js';
103
104
  import { runGitCommand } from './git-fs/runner.js';
104
105
  import { debug, redactSecretText, redactSecrets } from './debug/log.js';
105
106
  import { installHarnessCliCommand, type HarnessAskSayChildLlmOptions, type HarnessAskSayOptions, type HarnessPlanOptions } from './harness/harness-cli-command.js';
106
- import { formatCliUserError, isCliUserError } from './cli/cli-user-error.js';
107
+ import { CliUserError, formatCliUserError, isCliUserError } from './cli/cli-user-error.js';
107
108
  import { dispatchSolveMission } from './skills/tools/solve-mission.js';
108
109
  import { performBrowserAction, type BrowserActionDeps, type BrowserActionResult } from './harness/browser-act.js';
109
110
  import { decideTypeAction, type TypeActionDecision, type TypeTarget } from './harness/browser-act-type.js';
@@ -868,7 +869,75 @@ registerUsageCommand(program);
868
869
  registerLiveCommands(program);
869
870
  registerResearchCommand(program);
870
871
  registerReleaseCommands(program);
872
+ // HQ-HB · HQ-FENCE (10-04): one writer across mbp · node-b · cloud-vm — lease on the arbiter, 2-of-3 quorum, generation fencing.
873
+ const hqCmd = program.command('hq').description('본부 임대 — 하트비트·중재·단일 작성자 래퍼');
874
+ async function hqSink(): Promise<void> {
875
+ try { await (await import('./domains/standalone-log-sink.js')).registerStandaloneLogSink('hq'); } catch { /* observation is fail-soft */ }
876
+ }
877
+ hqCmd.command('host').description('이 머신의 HQ 호스트 이름 설정')
878
+ .command('set <name>').description('호스트 로컬 ~/.elanous-hq/host 에 한 줄로 기록')
879
+ .action(async (name: string) => {
880
+ try {
881
+ const { hqHostSet } = await import('./hq/hq.js');
882
+ const path = hqHostSet(name);
883
+ console.log(`hq host set: ${name} (${path})`);
884
+ } catch (error) { console.error(`hq host set: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
885
+ });
886
+ hqCmd.command('lease').argument('<action>', 'acquire|renew|status|release')
887
+ .option('--host <name>', '이 호스트 이름(기본 ~/.elanous-hq/host · hq.hostName · os.hostname)')
888
+ .option('--json', 'JSON 출력')
889
+ .action(async (action: string, opts: { host?: string; json?: boolean }) => {
890
+ try {
891
+ if (!['acquire', 'renew', 'status', 'release'].includes(action)) throw new CliUserError(`hq lease: unknown action ${action}`, 'acquire|renew|status|release');
892
+ await hqSink();
893
+ const { hqLease } = await import('./hq/hq.js');
894
+ const result = hqLease(action as 'acquire', {}, opts.host ? { host: opts.host } : {});
895
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
896
+ else console.log(result.ok ? `hq lease ${action}: ok${'record' in result && result.record ? ` · holder ${result.record.holder} · generation ${result.record.generation}` : ''}` : `hq lease ${action}: refused — ${'reason' in result ? result.reason : ''}`);
897
+ if (!result.ok) process.exitCode = 2;
898
+ } catch (error) { if (error instanceof CliUserError) throw error; console.error(`hq lease: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
899
+ });
900
+ hqCmd.command('heartbeat').description('보유자는 갱신 · 아니면 대기(보유자에 닿는지 중재자에 보고)').option('--json', 'JSON 출력')
901
+ .action(async (opts: { json?: boolean }) => {
902
+ try {
903
+ await hqSink();
904
+ const result = (await import('./hq/hq.js')).hqHeartbeat();
905
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`); else console.log(`hq heartbeat: ${result.outcome}`);
906
+ } catch (error) { console.error(`hq heartbeat: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
907
+ });
908
+ hqCmd.command('arbiter-check').description('중재자(cloud-vm)에서: 보유자 탐침 · 연속 2회 이중 불통이면 대기 호스트 승격').option('--json', 'JSON 출력')
909
+ .action(async (opts: { json?: boolean }) => {
910
+ try {
911
+ await hqSink();
912
+ const result = (await import('./hq/hq.js')).hqArbiterCheck();
913
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`); else console.log(`hq arbiter-check: ${result.promoted ? `promoted ${result.holder} gen ${result.generation}` : `streak ${result.streak}`}`);
914
+ } catch (error) { console.error(`hq arbiter-check: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
915
+ });
916
+ hqCmd.command('seen').description('이 호스트가 본 가장 높은 임대 세대(한 번도 못 봤으면 exit 3 — 드릴 전 기본 원천 판단용)').option('--json', 'JSON 출력')
917
+ .action(async (opts: { json?: boolean }) => {
918
+ const result = (await import('./hq/hq.js')).hqSeen();
919
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
920
+ else console.log(result.seen ? `hq seen: generation ${result.generation}` : 'hq seen: never');
921
+ process.exitCode = result.seen ? 0 : 3;
922
+ });
923
+ hqCmd.command('fence').description('이 호스트가 임대를 쥐었을 때만 명령 실행(아니면 건너뜀 · exit 0)')
924
+ .requiredOption('--role <role>', 'telegram-poller|cron|seat-loop|release-run|conatus|git-push')
925
+ .argument('<command...>', '-- 뒤의 명령')
926
+ .action(async (command: string[], opts: { role: string }) => {
927
+ const { FENCE_ROLES, hqFenceRun } = await import('./hq/hq.js');
928
+ if (!(FENCE_ROLES as readonly string[]).includes(opts.role)) throw new CliUserError(`hq fence: unknown role ${opts.role}`, FENCE_ROLES.join('|'));
929
+ await hqSink();
930
+ process.exitCode = hqFenceRun(opts.role as 'cron', command);
931
+ });
871
932
  const seatCmd = program.command('seat').description('분배된 자리의 하루 루프와 보고');
933
+ seatCmd.command('answer <askId> <answer>').description('자리 질문에 답을 남기고 원래 표면으로 회신 예약')
934
+ .action(async (askId: string, answer: string) => {
935
+ try {
936
+ const { answerSeatAsk } = await import('./seat-dispatch/seat-ask.js');
937
+ answerSeatAsk(askId, answer);
938
+ console.log(`자리 답변 기록: ${askId}`);
939
+ } catch (error) { console.error(`seat answer: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
940
+ });
872
941
  seatCmd.command('loop').requiredOption('--seat <SEAT>', 'MK|OP|TC|UX')
873
942
  .requiredOption('--once', '한 번 실행')
874
943
  .option('--json', 'JSON 출력')
@@ -893,6 +962,20 @@ seatCmd.command('report').requiredOption('--seat <SEAT>', 'MK|OP|TC|UX')
893
962
  else console.log(result.body);
894
963
  } catch (error) { console.error(`seat report: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
895
964
  });
965
+ seatCmd.command('pack').description('자리 묶음').command('export')
966
+ .requiredOption('--seat <SEAT>', '내보낼 자리')
967
+ .requiredOption('--out <dir>', '비어 있는 산출 폴더')
968
+ .action(async (opts: { seat: string; out: string }) => {
969
+ try {
970
+ const { exportSeatPack } = await import('./seat-loop/seat-pack.js');
971
+ const result = exportSeatPack(opts.seat, opts.out);
972
+ console.log(`seat pack ${result.name} ${result.version}: ${result.files.length} files → ${opts.out}`);
973
+ } catch (error) {
974
+ const { SeatPackLeakError } = await import('./seat-loop/seat-pack.js');
975
+ console.error(`seat pack export: ${error instanceof Error ? error.message : String(error)}`);
976
+ process.exitCode = error instanceof SeatPackLeakError ? 2 : 1;
977
+ }
978
+ });
896
979
  registerModelWatchCommand(program);
897
980
  registerDoctorCommand(program);
898
981
  // runCli() → main() → program.parseAsync() dispatches control serve and resources where|list.
@@ -974,6 +1057,19 @@ marketCmd.command('keygen').description('Generate a local test index signing key
974
1057
  console.log(`Generated local index key ${pair.keyId}`);
975
1058
  });
976
1059
 
1060
+ const stewardCmd = program.command('steward').description('Steward loop controls');
1061
+ stewardCmd.command('readiness').description('Read-only shadow evidence for a live decision')
1062
+ .option('--since <duration>', 'Lookback duration, e.g. 24h', '24h')
1063
+ .option('--json', 'JSON output')
1064
+ .action(async (opts: { since: string; json?: boolean }) => {
1065
+ process.exitCode = (await import('./steward/readiness.js')).runStewardReadinessCli(opts);
1066
+ });
1067
+ stewardCmd.command('mode [mode]').description('Show or set the steward mode: off|shadow|live|rescue (rescue = HQ move: launch·harvest·alert·decision cards only)')
1068
+ .option('--json', 'JSON output')
1069
+ .action(async (mode: string | undefined, opts: { json?: boolean }) => {
1070
+ process.exitCode = await (await import('./steward/rescue.js')).runStewardModeCli(mode, opts);
1071
+ });
1072
+
977
1073
  program.command('directive').description('Turn instructions into Linear issues')
978
1074
  .command('add <text>').option('--source <source>', 'telegram|pwa|tui|cli', 'cli')
979
1075
  .option('--dry-run', 'Preview the normalized directive without contacting Linear')
@@ -3029,21 +3125,78 @@ const harnessOrchestrateCmd = registerHarnessOrchestrateCapabilityOptions(
3029
3125
  });
3030
3126
 
3031
3127
  const contextCmd = program.command('context').description('외부 에이전트 맥락 이벤트');
3128
+ contextCmd.command('day')
3129
+ .description('자리 넷과 외부 세션의 최근 맥락 이벤트 수·마지막 시각 (읽기 전용)')
3130
+ .option('--since <hours>', '최근 시간 창 (예: 24h)', '24h')
3131
+ .option('--json', '구조화된 JSON 출력')
3132
+ .action(async (opts: { since: string; json?: boolean }) => {
3133
+ try {
3134
+ const { contextDay, renderContextDay } = await import('./context-bus/context-day.js');
3135
+ const report = contextDay(opts.since);
3136
+ await writeStdoutJson(`${opts.json ? JSON.stringify(report) : renderContextDay(report)}\n`);
3137
+ } catch (error) {
3138
+ console.error(`context day: ${error instanceof Error ? error.message : String(error)}`);
3139
+ process.exitCode = 2;
3140
+ }
3141
+ });
3142
+ contextCmd.command('condense')
3143
+ .description('최근 맥락 이벤트·결정·조율 요약을 프로젝트/자리별 장기 기억 후보 JSON ⊕ Markdown 으로 응축')
3144
+ .option('--since <hours>', '최근 시간 창 (예: 24h)', '24h')
3145
+ .option('--dry-run', '읽고 stdout 에만 출력 (기본 동작)')
3146
+ .option('--apply', '충돌은 OP 결정 원장으로 올리고 비충돌 후보를 기존 프로젝트 기억에 저장 · 이전 버전 은퇴')
3147
+ .option('--json', '구조화된 JSON 출력')
3148
+ .action(async (opts: { since: string; dryRun?: boolean; apply?: boolean; json?: boolean }) => {
3149
+ try {
3150
+ if (opts.apply && opts.dryRun) throw new Error('--apply and --dry-run cannot be combined');
3151
+ if (!/^[1-9]\d*h$/.test(opts.since)) throw new Error('--since requires positive hours, e.g. 24h');
3152
+ const { runContextCondense } = await import('./context-bus/condense.js');
3153
+ const report = await runContextCondense({ hours: Number(opts.since.slice(0, -1)), apply: opts.apply === true });
3154
+ await writeStdoutJson(JSON.stringify({ since: report.since, until: report.until, cards: report.cards, conflicts: report.conflicts, skipped: report.skipped, folded: report.folded, ...(report.applied ? { applied: report.applied } : {}) }) + '\n');
3155
+ if (!opts.json) await writeStdoutFully(report.markdown);
3156
+ } catch (error) {
3157
+ console.error(`context condense: ${error instanceof Error ? error.message : String(error)}`);
3158
+ process.exitCode = 2;
3159
+ }
3160
+ });
3032
3161
  contextCmd.command('hooks').description('Claude Code 맥락 훅 설정').command('install')
3033
3162
  .requiredOption('--print', '설정 조각만 출력하고 설정 파일은 수정하지 않음')
3034
3163
  .action(async () => {
3035
3164
  const { claudeHooksSettings } = await import('./context-bus/claude-hooks-install.js');
3036
3165
  console.log(claudeHooksSettings(REPOSITORY_ROOT));
3037
3166
  });
3038
- contextCmd.command('emit')
3039
- .requiredOption('--kind <kind>', 'claimed|done|asked|guide-changed')
3040
- .requiredOption('--summary <line>', '한 줄 요약')
3041
- .requiredOption('--source <link>', '출처 링크')
3042
- .action(async (opts: { kind: string; summary: string; source: string }) => {
3043
- if (!['claimed', 'done', 'asked', 'guide-changed'].includes(opts.kind)) throw new Error('context emit --kind must be claimed|done|asked|guide-changed');
3044
- const { recordExternalEvent } = await import('./context-bus/external-events.js');
3045
- recordExternalEvent({ origin: 'claude-code', kind: opts.kind as import('./context-bus/external-events.js').ContextHookKind,
3046
- summary: opts.summary, source: opts.source });
3167
+ contextCmd.command('emit [sessionKind]')
3168
+ .description('외부 에이전트 세션 이벤트: emit <kind> --seat <자리> --text <한 줄> [--ref <PR|칸>]')
3169
+ .option('--seat <seat>', 'OP|TC|MK|UX')
3170
+ .option('--text <line>', '한 줄 이벤트')
3171
+ .option('--ref <PR|cell>', '연결 PR 또는 칸')
3172
+ .option('--kind <kind>', '기존 Claude Code 훅 이벤트 종류')
3173
+ .option('--summary <line>', '기존 훅 한 줄 요약')
3174
+ .option('--source <link>', '기존 훅 출처 링크')
3175
+ .action(async (sessionKind: string | undefined, opts: { seat?: string; text?: string; ref?: string; kind?: string; summary?: string; source?: string }) => {
3176
+ try {
3177
+ if (sessionKind !== undefined) {
3178
+ if (opts.seat === undefined || opts.text === undefined || opts.kind !== undefined || opts.summary !== undefined || opts.source !== undefined) {
3179
+ throw new Error('context emit <kind> requires --seat and --text (not hook options)');
3180
+ }
3181
+ const { emitSessionEvent, SESSION_EVENT_KINDS } = await import('./context-bus/session-events.js');
3182
+ if (!SESSION_EVENT_KINDS.includes(sessionKind as import('./context-bus/session-events.js').SessionEventKind)) {
3183
+ throw new Error(`unknown context session kind: ${sessionKind}`);
3184
+ }
3185
+ emitSessionEvent({ kind: sessionKind as import('./context-bus/session-events.js').SessionEventKind,
3186
+ seat: opts.seat, text: opts.text, ...(opts.ref === undefined ? {} : { ref: opts.ref }) });
3187
+ } else {
3188
+ if (opts.seat !== undefined || opts.text !== undefined || opts.ref !== undefined || !opts.kind || opts.summary === undefined || opts.source === undefined) {
3189
+ throw new Error('context emit hook requires --kind, --summary and --source');
3190
+ }
3191
+ if (!['claimed', 'done', 'asked', 'guide-changed'].includes(opts.kind)) throw new Error(`unknown context hook kind: ${opts.kind}`);
3192
+ const { recordExternalEvent } = await import('./context-bus/external-events.js');
3193
+ recordExternalEvent({ origin: 'claude-code', kind: opts.kind as import('./context-bus/external-events.js').ContextHookKind,
3194
+ summary: opts.summary, source: opts.source });
3195
+ }
3196
+ } catch (error) {
3197
+ console.error(`context emit: ${error instanceof Error ? error.message : String(error)}`);
3198
+ process.exitCode = 2;
3199
+ }
3047
3200
  });
3048
3201
 
3049
3202
  const coordCmd = program.command('coord').description('조율 채널 맥락 원장');
@@ -5592,6 +5745,7 @@ registerFleetCommands(program);
5592
5745
 
5593
5746
  // ── ops (운영 관측 — 지금 뭐 도나·이상 없나·상태 전이) ──
5594
5747
  registerOpsCommands(program);
5748
+ registerOutputsCommands(program);
5595
5749
 
5596
5750
  registerPublishCommands(program);
5597
5751
 
@@ -5770,6 +5924,33 @@ agentCmd
5770
5924
  process.exit(outcome.exitCode);
5771
5925
  });
5772
5926
 
5927
+ agentCmd.command('duo [text...]')
5928
+ .description('Run Codex and Claude Code concurrently on the same mission in separate worktrees; summarize both PTY decisions and evidence.')
5929
+ .requiredOption('--left <backend>', 'codex or claude')
5930
+ .requiredOption('--right <backend>', 'codex or claude')
5931
+ .requiredOption('--branch <name>', 'base name for two distinct worktree branches')
5932
+ .option('--base <ref>', 'common branch base (default HEAD)')
5933
+ .option('--mission-file <path>', 'read the same verbatim mission from a file')
5934
+ .option('--evidence <mode>', 'doc|tsc|test (default tsc)', 'tsc')
5935
+ .option('--doc-dir <rel>', 'doc evidence directory')
5936
+ .option('--doc-glob <re>', 'doc evidence filename pattern')
5937
+ .option('--test-path <p>', 'test evidence test path')
5938
+ .option('--file <rel>', 'test evidence output file')
5939
+ .option('--max-rounds <n>', 'maximum PTY control rounds', '16')
5940
+ .option('--no-commit', 'do not commit completed worktrees')
5941
+ .action(async (textParts: string[], opts: import('./agent-mission/duo.js').DuoOptions) => {
5942
+ try {
5943
+ delete process.env.OPENAI_API_KEY;
5944
+ const { runAgentMissionDuo } = await import('./agent-mission/duo.js');
5945
+ const result = await runAgentMissionDuo(textParts, opts);
5946
+ console.log(`[duo] missionId=${result.missionId}\n${result.lines.join('\n')}`);
5947
+ process.exitCode = result.exitCode;
5948
+ } catch (error) {
5949
+ console.error(`duo: ${error instanceof Error ? error.message : String(error)}`);
5950
+ process.exitCode = 1;
5951
+ }
5952
+ });
5953
+
5773
5954
  // ★ 리뷰 반응 완결 루프 (L1) — PR 리뷰(OK/보강/거절)를 트리거로 codex 가 자율 rework→재제출.
5774
5955
  // ⭐ 옵션 정의·opts 조립은 `agent-mission/review-loop-cli.ts`(테스트 가능 seam) — 액션은 I/O 만 한다.
5775
5956
  registerReviewLoopOptions(
@@ -6491,6 +6672,12 @@ const selfDevCmd = program
6491
6672
  let devRunId = '';
6492
6673
  let devRunIdSource: import('./harness/harness-space.js').RunIdSource | undefined;
6493
6674
  let preparedWorktree: import('./harness/harness-worktree-auto.js').PreparedDevWorktree | undefined;
6675
+ let authoringLease: import('./pod-lease/host-lease.js').HostLeaseHandle | undefined;
6676
+ const releaseAuthoringLease = () => {
6677
+ if (!authoringLease) return;
6678
+ authoringLease(); authoringLease = undefined;
6679
+ process.off('exit', releaseAuthoringLease);
6680
+ };
6494
6681
  try {
6495
6682
  // ⛔ raw 입력 배타성 검증은 try 안의 selectDevAuthorInput 하나가 소유한다 — 실패도 아래 catch가 관측한다.
6496
6683
  const { buildDevCommandInput, selectDevAuthorInput } = await import('./self-dev/dev-cli.js');
@@ -6583,6 +6770,23 @@ const selfDevCmd = program
6583
6770
  priorBlockSamples: () => askIo.priorBlockSamplesFrom(askLogRows),
6584
6771
  recentAuthoringSamples: () => askIo.recentAuthoringSamplesFrom(askLogRows),
6585
6772
  authorGoal: (args, options) => runGoalAuthorCli([...args], options as never),
6773
+ ...(devAskSubstrate?.substrate === 'pod' ? { beforeAuthoring: async () => {
6774
+ const { parsePodPool, podPoolHostLease } = await import('./task-orchestrator/surfaces/pod-pool.js');
6775
+ const { measurePoolLease, recommendConcurrency } = await import('./task-orchestrator/surfaces/pod-lease.js');
6776
+ const members = parsePodPool(devAskSubstrate.pool!);
6777
+ const host = podPoolHostLease(members);
6778
+ while (!authoringLease) {
6779
+ const status = recommendConcurrency(measurePoolLease(members), { capacity: members.reduce((n, m) => n + m.capacity, 0), accounts: 0, perAccount: 0 });
6780
+ if (status.recommended !== null && status.recommended > 0) {
6781
+ const { leaseHasPendingPod } = await import('./pod-lease/host-lease.js');
6782
+ const pendingJobs = status.pendingJobs ?? [];
6783
+ authoringLease = host.tryReserve((others) =>
6784
+ others - host.live().filter((r) => leaseHasPendingPod(r, pendingJobs)).length < status.recommended!, true) ?? undefined;
6785
+ if (authoringLease) process.once('exit', releaseAuthoringLease);
6786
+ }
6787
+ if (!authoringLease) await new Promise((resolve) => setTimeout(resolve, 15_000));
6788
+ }
6789
+ } } : {}),
6586
6790
  relativeToCwd: (file) => relativeToCwdPath(process.cwd(), file),
6587
6791
  });
6588
6792
  // ⛔ 종료는 «표면»이 한다 — 흐름은 세 값만 낸다.
@@ -6592,6 +6796,7 @@ const selfDevCmd = program
6592
6796
  }
6593
6797
  askAuthoredFile = flowResult.goalFile;
6594
6798
  if (flowResult.kind === 'stopped-by-preflight') {
6799
+ releaseAuthoringLease();
6595
6800
  completionGuard.conclude();
6596
6801
  process.exit(1);
6597
6802
  }
@@ -6609,12 +6814,21 @@ const selfDevCmd = program
6609
6814
  warnRecentIncidentBurst();
6610
6815
  let podOutput = '';
6611
6816
  let podRunId: string | undefined;
6612
- const status = await dispatchHarnessOnPod(devAskPodDispatchInput(opts, askAuthoredFile!, devAskSubstrate.pool!), {
6613
- onOutput: (text) => {
6614
- podRunId ??= harnessPodRunId(podOutput + text);
6615
- podOutput = (podOutput + text).slice(-16_000);
6616
- },
6617
- });
6817
+ const previousLeaseId = process.env.ELANOUS_POD_RESERVED_LEASE;
6818
+ if (authoringLease) process.env.ELANOUS_POD_RESERVED_LEASE = authoringLease.id;
6819
+ let status: number;
6820
+ try {
6821
+ status = await dispatchHarnessOnPod(devAskPodDispatchInput(opts, askAuthoredFile!, devAskSubstrate.pool!), {
6822
+ onOutput: (text) => {
6823
+ podRunId ??= harnessPodRunId(podOutput + text);
6824
+ podOutput = (podOutput + text).slice(-16_000);
6825
+ },
6826
+ });
6827
+ } finally {
6828
+ releaseAuthoringLease();
6829
+ if (previousLeaseId === undefined) delete process.env.ELANOUS_POD_RESERVED_LEASE;
6830
+ else process.env.ELANOUS_POD_RESERVED_LEASE = previousLeaseId;
6831
+ }
6618
6832
  if (status !== 0) {
6619
6833
  const classified = classifyHarnessPodExit({ status }, podOutput, { ...(podRunId ? { runId: podRunId } : {}) });
6620
6834
  recordClassifiedHarnessPodExit(podRunId ?? harnessPodRunId(podOutput), classified.reason, status);
@@ -6916,9 +7130,11 @@ const selfDevCmd = program
6916
7130
  completionGuard.conclude();
6917
7131
  process.exit(r.result.exitCode);
6918
7132
  }
7133
+ releaseAuthoringLease();
6919
7134
  completionGuard.conclude();
6920
7135
  process.exit(ok ? 0 : 2);
6921
7136
  } catch (e) {
7137
+ releaseAuthoringLease();
6922
7138
  const msg = e instanceof Error ? e.message : String(e);
6923
7139
  // ⚠️ 관측은 결론을 막지 않는다(리뷰 should-fix 5R) — 여기서 로거가 던지면 아래 `console.error` 와
6924
7140
  // 의도한 `exit(1)` 까지 통째로 우회해, 사용자는 이유도 모른 채 다른 종료코드를 받는다.
@@ -1,12 +1,15 @@
1
1
  import { debug } from '../debug/log.js';
2
2
  import { discordDecisionOwner } from '../decisions/discord-decision-cards.js';
3
3
  import { canonicalSeatId } from '../msg/msg-store.js';
4
+ import { awaitGlobalPersonaLoad, getGlobalPersonaRegistry } from '../persona/global-registry.js';
5
+ import type { PersonaSource } from '../persona/mention-parser.js';
4
6
  import { dispatchCeoTask, type CeoCommandDeps } from '../seat-dispatch/ceo-commands.js';
5
7
  import { ceoTaskDeps, classifyCeoIntent } from '../seat-dispatch/ceo-intent.js';
6
8
  import { askSeat, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-ask.js';
7
9
  import { parseSeatAddress, resolveSeat } from '../seat-address/seat-address.js';
8
10
  import { getUserConfig, type UserConfig } from '../user-config.js';
9
11
  import { answerAsSeat } from './seat-answer.js';
12
+ import { answerAsPersona, resolvePersonaAddress, seatOfPersona, type PersonaAnswerDeps } from './persona-answer.js';
10
13
  import {
11
14
  submitIntakeWork,
12
15
  type SubmitIntakeWorkDeps,
@@ -24,6 +27,9 @@ export interface DiscordSeatWorkDeps extends SubmitIntakeWorkDeps {
24
27
  submit?: typeof submitIntakeWork;
25
28
  config?: UserConfig;
26
29
  answer?: typeof answerAsSeat;
30
+ personaSource?: PersonaSource;
31
+ personaAnswer?: typeof answerAsPersona;
32
+ personaAnswerDeps?: PersonaAnswerDeps;
27
33
  dispatch?: typeof dispatchCeoTask;
28
34
  commandDeps?: CeoCommandDeps;
29
35
  askDeps?: SeatAskDeps;
@@ -54,7 +60,7 @@ export async function handleDiscordSeatWork(
54
60
  ...(msg.threadId ? { threadId: msg.threadId } : {}) },
55
61
  deps.commandDeps ?? ceoTaskDeps(auth.cfg, auth.id), deps.askDeps);
56
62
  }
57
- const address = parseSeatAddress(text);
63
+ const address = parseSeatAddress(text.replace(/^(@[A-Za-z][A-Za-z0-9_-]*(?:,@?[A-Za-z][A-Za-z0-9_-]*)*)/, (prefix) => prefix.replaceAll(',@', ',')));
58
64
  if (!address) {
59
65
  if (!text.trim() || /^\s*[/@@]/.test(text)) return null;
60
66
  const auth = owner();
@@ -66,12 +72,39 @@ export async function handleDiscordSeatWork(
66
72
  return ['OP', 'TC', 'MK', 'UX'].includes(id) ? sendTask(id, text, auth!.cfg, auth!.id) : null;
67
73
  }
68
74
 
69
- const seats = address.seats.map((name) => ({ name, seat: resolveSeat(name) }));
70
- const unknown = seats.filter(({ seat }) => !seat).map(({ name }) => `@${name}`);
75
+ const initialSeats = address.seats.map((name) => ({ name, seat: resolveSeat(name) }));
76
+ const hasUnresolved = initialSeats.some(({ seat }) => !seat);
77
+ const auth = hasUnresolved ? owner() : null;
78
+ if (hasUnresolved && auth && !deps.personaSource) await awaitGlobalPersonaLoad();
79
+ const source = hasUnresolved && auth ? (deps.personaSource ?? getGlobalPersonaRegistry()) : null;
80
+ const addressed = initialSeats.map(({ name, seat }) => {
81
+ const persona = !seat && source ? resolvePersonaAddress(name, source) : null;
82
+ const personaSeat = persona ? seatOfPersona(persona) : null;
83
+ if (persona && personaSeat) {
84
+ debug.log('persona.address', 'seat-alias', { name, personaId: persona.personaId, seat: personaSeat.id, via: 'discord' });
85
+ return { name: personaSeat.id, seat: personaSeat, persona: null };
86
+ }
87
+ return { name, seat, persona };
88
+ });
89
+ const seats = addressed;
90
+ const unknown = addressed.filter(({ seat, persona }) => !seat && (!persona || !auth)).map(({ name }) => `@${name}`);
71
91
  if (unknown.length) return `어느 좌석을 말씀하시나요? ${unknown.join(', ')}은(는) 등록된 좌석이 아닙니다. 좌석을 확인해 다시 보내 주세요.`;
92
+ const personas = addressed.filter(({ persona }) => persona);
93
+ if (personas.length && addressed.length !== 1) {
94
+ debug.log('persona.address', 'rejected', { reason: 'mixed', via: 'discord' });
95
+ return '페르소나는 한 번에 하나만 부를 수 있습니다';
96
+ }
72
97
  const body = address.body.trim();
73
98
  if (!body) return '어떤 일을 맡길까요? 좌석 주소 뒤에 요청 내용을 적어 다시 보내 주세요.';
74
99
 
100
+ if (personas.length === 1 && personas[0]?.persona) {
101
+ const { name, persona } = personas[0];
102
+ debug.log('persona.address', 'resolved', { name, personaId: persona.personaId, via: 'discord' });
103
+ const answer = await (deps.personaAnswer ?? answerAsPersona)(persona, body, deps.personaAnswerDeps);
104
+ debug.log('persona.address', 'answered', { personaId: persona.personaId, via: 'discord' });
105
+ return answer;
106
+ }
107
+
75
108
  if (seats.length === 1 && seats[0]?.seat) {
76
109
  const auth = owner();
77
110
  const seatId = canonicalSeatId(seats[0].seat.id);
@@ -0,0 +1,44 @@
1
+ import { fetchLinearIssues } from '../connectors/linear.js';
2
+ import { debug } from '../debug/log.js';
3
+ import { getSecretAsync } from '../nexus/config/secrets/index.js';
4
+ import { CardStore } from '../task-cards/card-store.js';
5
+ import { createWishCard } from './wish-card.js';
6
+
7
+ export const LINEAR_WISH_KEY_MISSING = 'Linear 키가 없습니다 — elanous connector linear 로 먼저 등록하세요';
8
+
9
+ export async function scanLinearWishes({ teamKey, label = 'wish', store, deps = {} }: {
10
+ teamKey: string;
11
+ label?: string;
12
+ store: CardStore;
13
+ deps?: {
14
+ getApiKey?: () => Promise<string | undefined>;
15
+ fetchIssues?: typeof fetchLinearIssues;
16
+ };
17
+ }): Promise<{ added: number; duplicate: number; failed: number }> {
18
+ const apiKey = await (deps.getApiKey ?? (() => getSecretAsync('connector.linear.apiKey')))();
19
+ if (!apiKey?.trim()) throw new Error(LINEAR_WISH_KEY_MISSING);
20
+ let issues: Awaited<ReturnType<typeof fetchLinearIssues>>;
21
+ try {
22
+ issues = await (deps.fetchIssues ?? fetchLinearIssues)({ apiKey, teamKey, labelOrPrefix: label });
23
+ } catch (error) {
24
+ const message = error instanceof Error ? error.message : String(error);
25
+ throw new Error(message.replaceAll(apiKey, '[redacted]'));
26
+ }
27
+ let added = 0;
28
+ let duplicate = 0;
29
+ let failed = 0;
30
+ for (const issue of issues) {
31
+ try {
32
+ const result = createWishCard({
33
+ text: `${issue.title}\n\n${issue.body}\n\n${issue.url}`,
34
+ source: 'linear', ref: issue.identifier ?? '',
35
+ }, store);
36
+ if (result.created) added++;
37
+ else duplicate++;
38
+ } catch {
39
+ failed++;
40
+ }
41
+ }
42
+ debug.log('intake.linear-wish', 'scanned', { teamKey, label, added, duplicate, failed });
43
+ return { added, duplicate, failed };
44
+ }
@@ -1,11 +1,16 @@
1
1
  import { debug } from '../debug/log.js';
2
2
  import { resolveMention, type PersonaSource } from '../persona/mention-parser.js';
3
3
  import type { PersonaProfile } from '../persona/types.js';
4
+ import { resolveSeat, type SeatEntry } from '../seat-address/seat-address.js';
4
5
 
5
6
  export interface PersonaAnswerDeps {
6
7
  complete?: (prompt: string) => Promise<string>;
7
8
  }
8
9
 
10
+ export function seatOfPersona(persona: PersonaProfile, resolve = resolveSeat): SeatEntry | null {
11
+ return persona.seat ? resolve(persona.seat) ?? null : null;
12
+ }
13
+
9
14
  /** Resolve the registry mention first, then an exact display name. */
10
15
  export function resolvePersonaAddress(name: string, source: PersonaSource): PersonaProfile | null {
11
16
  const mention = resolveMention(name, source);
@@ -9,7 +9,7 @@ import { askSeat, parseSeatAsk, type SeatAskDeps } from '../seat-dispatch/seat-a
9
9
  import { parseSeatAddress, resolveSeat } from '../seat-address/seat-address.js';
10
10
  import { getUserConfig, type UserConfig } from '../user-config.js';
11
11
  import { answerAsSeat } from './seat-answer.js';
12
- import { answerAsPersona, resolvePersonaAddress, type PersonaAnswerDeps } from './persona-answer.js';
12
+ import { answerAsPersona, resolvePersonaAddress, seatOfPersona, type PersonaAnswerDeps } from './persona-answer.js';
13
13
  import {
14
14
  submitIntakeWork,
15
15
  type SubmitIntakeWorkDeps,
@@ -66,16 +66,23 @@ export async function handleTelegramSeatWork(
66
66
  const address = parseSeatAddress(text.replace(/^(@[A-Za-z][A-Za-z0-9_-]*(?:,@?[A-Za-z][A-Za-z0-9_-]*)*)/, (prefix) => prefix.replaceAll(',@', ',')));
67
67
  if (!address) return null;
68
68
 
69
- const seats = address.seats.map((name) => ({ name, seat: resolveSeat(name) }));
69
+ const initialSeats = address.seats.map((name) => ({ name, seat: resolveSeat(name) }));
70
70
  const names = address.seats;
71
71
  debug.log('seat-address.telegram', 'parsed', { seats: names });
72
- const hasUnresolved = seats.some(({ seat }) => !seat);
72
+ const hasUnresolved = initialSeats.some(({ seat }) => !seat);
73
73
  const auth = hasUnresolved ? owner() : null;
74
74
  if (hasUnresolved && auth && !deps.personaSource) await awaitGlobalPersonaLoad();
75
75
  const source = hasUnresolved && auth ? (deps.personaSource ?? getGlobalPersonaRegistry()) : null;
76
- const addressed = seats.map(({ name, seat }) => ({
77
- name, seat, persona: !seat && source ? resolvePersonaAddress(name, source) : null,
78
- }));
76
+ const addressed = initialSeats.map(({ name, seat }) => {
77
+ const persona = !seat && source ? resolvePersonaAddress(name, source) : null;
78
+ const personaSeat = persona ? seatOfPersona(persona) : null;
79
+ if (persona && personaSeat) {
80
+ debug.log('persona.address', 'seat-alias', { name, personaId: persona.personaId, seat: personaSeat.id, via: 'telegram' });
81
+ return { name: personaSeat.id, seat: personaSeat, persona: null };
82
+ }
83
+ return { name, seat, persona };
84
+ });
85
+ const seats = addressed;
79
86
  const unknown = addressed.filter(({ seat, persona }) => !seat && (!persona || !auth)).map(({ name }) => `@${name}`);
80
87
  if (unknown.length) {
81
88
  debug.log('seat-address.telegram', 'rejected', { seats: names, reason: 'unknown-seat' });
@@ -0,0 +1,33 @@
1
+ import { debug } from '../debug/log.js';
2
+ import { CardStore } from '../task-cards/card-store.js';
3
+
4
+ export function createWishCard(
5
+ { text, source, ref }: { text: string; source: 'telegram' | 'pwa' | 'tui' | 'linear'; ref: string },
6
+ store: CardStore = new CardStore(),
7
+ ): { cardId: string; title: string; created: boolean } {
8
+ const title = text.trim().split(/\r?\n/, 1)[0]!.trim().slice(0, 80);
9
+ if (!title) throw new Error('소원 글을 입력하세요');
10
+ if (!ref?.trim()) throw new Error('소원 참조가 필요합니다');
11
+ const goalId = `wish:${source}:${ref}`;
12
+ const old = store.listCards().find(card => card.goalId === goalId);
13
+ const card = old ?? store.createCard({ goalId, title });
14
+ const hasIntakeSection = card.sections.some(section => section.key === 'intake:wish:0');
15
+ const created = !old && !hasIntakeSection;
16
+ // A prior attempt may have created the card but stopped before writing its intake section. The card's first
17
+ // title wins: a retry with the same ref but a different request must not put a contradicting body on it
18
+ // (review must-fix) — keep the first title and record only that the retried text did not match.
19
+ if (!hasIntakeSection) {
20
+ const sameRequest = !old || title === card.title;
21
+ store.appendSection(card.id, {
22
+ key: 'intake:wish:0',
23
+ owner: 'steward',
24
+ content: JSON.stringify({
25
+ source, ref, title: card.title, text: sameRequest ? text : null, at: new Date().toISOString(),
26
+ ...(old ? { recovered: true } : {}), ...(sameRequest ? {} : { mismatch: true }),
27
+ }),
28
+ });
29
+ if (!sameRequest) debug.log('intake.wish', 'recovered-mismatch', { source, cardId: card.id });
30
+ }
31
+ debug.log('intake.wish', created ? 'created' : 'duplicate', { source, cardId: card.id });
32
+ return { cardId: card.id, title: card.title, created };
33
+ }
@@ -44,7 +44,7 @@ export function classifyGoalDifficulty(signals: DifficultySignals): DifficultyLe
44
44
  /** Only the baseline role default is adjustable; explicit roleLlm and older config/env layers win. */
45
45
  export function implementDifficultyTier(level: DifficultyLevel, role: RoleLlmResolution): ModelTier | undefined {
46
46
  if (role.source !== 'default') return undefined;
47
- return level === 'small' ? 'budget' : level === 'large' ? 'better' : ROLE_MODEL_DEFAULTS.implement.tier;
47
+ return level === 'small' ? 'budget' : level === 'large' ? 'best' : ROLE_MODEL_DEFAULTS.implement.tier;
48
48
  }
49
49
 
50
50
  export function resolveImplementDifficulty(role: RoleLlmResolution, level: DifficultyLevel): RoleLlmResolution {