elanous 0.2.14 → 0.2.16

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 (350) 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/{1307-6e7e0377c1db0bdc.js → 1307-b46d45b705e8c9ae.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/5977-c952712c87a1f757.js +1 -0
  5. package/apps/pwa/out/_next/static/chunks/899-46946fe8363e1262.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/{9103-7a3ae7f60af3b99f.js → 9103-60fbef33e66f0a46.js} +1 -1
  7. package/apps/pwa/out/_next/static/chunks/app/approvals/page-79c3ed84baaedd03.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-7b2b4f378354869a.js → page-3cb52e5ea8fc2fa5.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/board/page-a424ef80885d1743.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/botlab/page-c1e20d7d338713f1.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/bots/{page-1512d91c7491ed62.js → page-482c5dbde699947c.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/ceo/page-1e962b31d1f11081.js +1 -0
  13. package/apps/pwa/out/_next/static/chunks/app/chat/{page-a627db94ec6904a0.js → page-8c2d9526164f5d09.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/consult/page-ffff6d819fbc6e0f.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/control/{page-e6b84ab07d3567d5.js → page-a0e2a6d1bfb52fc5.js} +1 -1
  16. package/apps/pwa/out/_next/static/chunks/app/design-check/page-c53598cfce6156f2.js +1 -0
  17. package/apps/pwa/out/_next/static/chunks/app/editor/{page-fed69f9759d248d0.js → page-13a63953f53c6a23.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/exec/{page-15374909d8f1aebd.js → page-5d3c8429d04f3831.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/field/{page-012a742f81218b01.js → page-3193bdc0bbab047a.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/inside/page-76270efaee638f17.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/intake/{page-99effd9d893a43dc.js → page-6107ca8ddd5e3bdd.js} +1 -1
  22. package/apps/pwa/out/_next/static/chunks/app/{layout-938de57ae0315e46.js → layout-dce686e582c23937.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live/page-d3d53b146416e422.js +1 -0
  24. package/apps/pwa/out/_next/static/chunks/app/loops/{page-160089123a40dd91.js → page-1a61c7ca1ceb3ae7.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/market/{page-629e1ede482ec6eb.js → page-51f811fa119308b6.js} +1 -1
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-dacff46049e1557e.js +1 -0
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-27e99b4e54a849d4.js → page-4167348049166852.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/not-found-de52e8fdf9095f7a.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/page-36a7d994f4ca0299.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-32cde713be47cddb.js → page-ca3952cd78a83ca2.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-e7e10cd3386222e5.js → page-5b042736aa113756.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-acb749f1e432bcf3.js +1 -0
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/page-cd78862707999911.js +1 -0
  34. package/apps/pwa/out/_next/static/chunks/app/page-54b9ef8afe6278fd.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-e64caff084f7ce21.js → page-1ebfe049c640e171.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-db07eaffba0df7e3.js → page-57924393e3e4e539.js} +1 -1
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/page-9e0ce6c8129fa641.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-31fa1ce4c4aff9ae.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/settings/{page-86f91034bd5cabe5.js → page-0f5cd57ca928f42b.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-abe8e3b74bea1ffc.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/app/setup/page-4364ef9f4e7c7f0f.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/share/{page-ce4d8a1215ce53a3.js → page-d28573d6792b4275.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-58043db78ba519ae.js → page-59f03fda76c03d22.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-357750dfb91bf960.js → page-d751de5f8062ff5e.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/term/{page-274703bc41b71333.js → page-fe1759f94003b32b.js} +1 -1
  46. package/apps/pwa/out/_next/static/chunks/app/today/page-d64c984e855c8c68.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-86914b0dee8ab7f8.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/{page-3b7e821dc492bdfd.js → page-9afdee66b820c76e.js} +1 -1
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d4aef67abf264874.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-f8da5dc6f3752276.js → page-1ea64acadd704ace.js} +1 -1
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-2f810b18d89ecba6.js → page-3544f154c87b43ee.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-30c2073bfc21c004.js +1 -0
  53. package/apps/pwa/out/_next/static/css/001d5fbaee388a25.css +1 -0
  54. package/apps/pwa/out/_next/static/css/{04dabcbcc4bfa6db.css → 085673d826b98b62.css} +1 -1
  55. package/apps/pwa/out/_next/static/rv9cierV0jSOvDBm1g0XB/_buildManifest.js +1 -0
  56. package/apps/pwa/out/approvals/index.html +1 -1
  57. package/apps/pwa/out/approvals/index.txt +15 -15
  58. package/apps/pwa/out/autopilot/index.html +1 -1
  59. package/apps/pwa/out/autopilot/index.txt +15 -15
  60. package/apps/pwa/out/board/index.html +1 -1
  61. package/apps/pwa/out/board/index.txt +15 -15
  62. package/apps/pwa/out/botlab/index.html +1 -1
  63. package/apps/pwa/out/botlab/index.txt +15 -15
  64. package/apps/pwa/out/bots/index.html +1 -1
  65. package/apps/pwa/out/bots/index.txt +15 -15
  66. package/apps/pwa/out/ceo/index.html +1 -0
  67. package/apps/pwa/out/ceo/index.txt +33 -0
  68. package/apps/pwa/out/chat/index.html +1 -1
  69. package/apps/pwa/out/chat/index.txt +15 -15
  70. package/apps/pwa/out/consult/index.html +1 -1
  71. package/apps/pwa/out/consult/index.txt +15 -15
  72. package/apps/pwa/out/control/index.html +1 -1
  73. package/apps/pwa/out/control/index.txt +15 -15
  74. package/apps/pwa/out/design-check/index.html +1 -1
  75. package/apps/pwa/out/design-check/index.txt +15 -15
  76. package/apps/pwa/out/editor/index.html +1 -1
  77. package/apps/pwa/out/editor/index.txt +15 -15
  78. package/apps/pwa/out/exec/index.html +1 -1
  79. package/apps/pwa/out/exec/index.txt +15 -15
  80. package/apps/pwa/out/field/index.html +1 -1
  81. package/apps/pwa/out/field/index.txt +15 -15
  82. package/apps/pwa/out/healthz +1 -1
  83. package/apps/pwa/out/index.html +1 -1
  84. package/apps/pwa/out/index.txt +15 -15
  85. package/apps/pwa/out/inside/index.html +1 -1
  86. package/apps/pwa/out/inside/index.txt +15 -15
  87. package/apps/pwa/out/intake/index.html +1 -1
  88. package/apps/pwa/out/intake/index.txt +15 -15
  89. package/apps/pwa/out/live/index.html +1 -1
  90. package/apps/pwa/out/live/index.txt +15 -15
  91. package/apps/pwa/out/loops/index.html +1 -1
  92. package/apps/pwa/out/loops/index.txt +15 -15
  93. package/apps/pwa/out/market/index.html +1 -1
  94. package/apps/pwa/out/market/index.txt +15 -15
  95. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  96. package/apps/pwa/out/missions/placeholder/index.txt +15 -15
  97. package/apps/pwa/out/morning/index.html +1 -1
  98. package/apps/pwa/out/morning/index.txt +15 -15
  99. package/apps/pwa/out/observatory/index.html +1 -1
  100. package/apps/pwa/out/observatory/index.txt +15 -15
  101. package/apps/pwa/out/ops/checklist/index.html +1 -1
  102. package/apps/pwa/out/ops/checklist/index.txt +15 -15
  103. package/apps/pwa/out/ops/release/index.html +1 -1
  104. package/apps/pwa/out/ops/release/index.txt +15 -15
  105. package/apps/pwa/out/ops/seats/index.html +1 -1
  106. package/apps/pwa/out/ops/seats/index.txt +15 -15
  107. package/apps/pwa/out/outputs/index.html +1 -1
  108. package/apps/pwa/out/outputs/index.txt +15 -15
  109. package/apps/pwa/out/reflection/index.html +1 -1
  110. package/apps/pwa/out/reflection/index.txt +16 -16
  111. package/apps/pwa/out/scheduler/index.html +1 -1
  112. package/apps/pwa/out/scheduler/index.txt +15 -15
  113. package/apps/pwa/out/sessions/index.html +1 -1
  114. package/apps/pwa/out/sessions/index.txt +15 -15
  115. package/apps/pwa/out/settings/devices/index.html +1 -1
  116. package/apps/pwa/out/settings/devices/index.txt +15 -15
  117. package/apps/pwa/out/settings/index.html +1 -1
  118. package/apps/pwa/out/settings/index.txt +15 -15
  119. package/apps/pwa/out/setup/done/index.html +1 -1
  120. package/apps/pwa/out/setup/done/index.txt +15 -15
  121. package/apps/pwa/out/setup/index.html +1 -1
  122. package/apps/pwa/out/setup/index.txt +15 -15
  123. package/apps/pwa/out/share/index.html +1 -1
  124. package/apps/pwa/out/share/index.txt +15 -15
  125. package/apps/pwa/out/showroom/index.html +1 -1
  126. package/apps/pwa/out/showroom/index.txt +15 -15
  127. package/apps/pwa/out/tasks/index.html +1 -1
  128. package/apps/pwa/out/tasks/index.txt +15 -15
  129. package/apps/pwa/out/term/index.html +1 -1
  130. package/apps/pwa/out/term/index.txt +15 -15
  131. package/apps/pwa/out/today/index.html +1 -1
  132. package/apps/pwa/out/today/index.txt +15 -15
  133. package/apps/pwa/out/trace/index.html +1 -1
  134. package/apps/pwa/out/trace/index.txt +15 -15
  135. package/apps/pwa/out/vault/index.html +1 -1
  136. package/apps/pwa/out/vault/index.txt +15 -15
  137. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  138. package/apps/pwa/out/workflows/chat-ui/index.txt +15 -15
  139. package/apps/pwa/out/workflows/index.html +1 -1
  140. package/apps/pwa/out/workflows/index.txt +15 -15
  141. package/apps/pwa/out/workspace/index.html +1 -1
  142. package/apps/pwa/out/workspace/index.txt +15 -15
  143. package/apps/pwa/out/worktrees/index.html +1 -1
  144. package/apps/pwa/out/worktrees/index.txt +15 -15
  145. package/graphs/doc-gen/doc-gen.yaml +24 -0
  146. package/graphs/doc-gen/recipes.yaml +3 -0
  147. package/graphs/heal/heal-loop.yaml +1 -1
  148. package/graphs/implement-loop.yaml +1 -1
  149. package/graphs/orchestrator/orchestrator.yaml +7 -0
  150. package/graphs/orchestrator/recipes.yaml +3 -0
  151. package/graphs/release/recipes.yaml +3 -0
  152. package/graphs/release/release-loop.yaml +4 -2
  153. package/graphs/steward/recipes.yaml +2 -1
  154. package/package.json +1 -1
  155. package/scripts/brand/check.ts +7 -5
  156. package/scripts/ci-public-export-import-gate.ts +154 -0
  157. package/scripts/ci-test-interference-gate.ts +5 -1
  158. package/scripts/ci-typecheck-changed.ts +82 -31
  159. package/scripts/docs/doc-rot.ts +164 -0
  160. package/scripts/docs/generate-graph-anatomy.ts +437 -0
  161. package/scripts/docs-cli-check.ts +4 -4
  162. package/scripts/expose-rubric-suggest.ts +18 -12
  163. package/scripts/goals/goal-census.ts +343 -0
  164. package/scripts/lecture-note/node.ts +31 -11
  165. package/scripts/lessons/handbook-page.ts +30 -3
  166. package/scripts/marketing/generate-ax-brief.ts +140 -0
  167. package/scripts/marketing/generate-feature-map.ts +118 -0
  168. package/scripts/measure/author-section-contribution.ts +84 -0
  169. package/scripts/release-loop/checklist-gate-node.ts +53 -14
  170. package/scripts/release-loop/cut-branch.ts +43 -13
  171. package/scripts/release-loop/gate-node.ts +82 -14
  172. package/scripts/release-loop/vault-note-node.ts +257 -8
  173. package/scripts/release-story/draft.ts +57 -9
  174. package/scripts/rhythm/daily.ts +263 -76
  175. package/scripts/rhythm/monthly.ts +97 -0
  176. package/scripts/rhythm/weekly.ts +30 -9
  177. package/scripts/smart-merge/resync-shadow.ts +147 -0
  178. package/src/acp/client.ts +17 -2
  179. package/src/agent-env/profile.ts +161 -0
  180. package/src/agent-mission/driver.ts +51 -1
  181. package/src/agent-mission/supervisor-metrics-cli.ts +40 -0
  182. package/src/agent-mission/supervisor-metrics.ts +218 -0
  183. package/src/agent-substrate/pr-reviewer.ts +2 -0
  184. package/src/ask-user-question/pending-questions.ts +5 -0
  185. package/src/auth/github-app-token.ts +112 -13
  186. package/src/autopilot/build/llm-conflict-merge.ts +32 -6
  187. package/src/autopilot/track-agent-shadow.ts +2 -1
  188. package/src/briefing/brief-items.ts +276 -0
  189. package/src/budget/usage-outcomes.ts +204 -0
  190. package/src/claims/claims-render.ts +14 -5
  191. package/src/cli/agent-env-cli.ts +21 -0
  192. package/src/cli/brief-cli.ts +73 -0
  193. package/src/cli/decisions-cli.ts +9 -0
  194. package/src/cli/doctor-cli.ts +23 -1
  195. package/src/cli/doctor-node-modules.ts +147 -0
  196. package/src/cli/intake-cli.ts +46 -17
  197. package/src/cli/know-cli.ts +20 -0
  198. package/src/cli/logs-cli.ts +43 -1
  199. package/src/cli/pod-cli.ts +2 -0
  200. package/src/cli/pr-cli.ts +80 -12
  201. package/src/cli/release-cli.ts +66 -29
  202. package/src/cli/seat-requests-cli.ts +52 -0
  203. package/src/cli/self-update.ts +200 -1
  204. package/src/cli/usage-cli.ts +26 -0
  205. package/src/context-bus/context-now-public.ts +16 -6
  206. package/src/context-bus/context-now.ts +3 -1
  207. package/src/context-bus/coord-events.ts +6 -4
  208. package/src/decisions/decision-cards.ts +33 -7
  209. package/src/decisions/proact-meter.ts +286 -0
  210. package/src/decisions/proact1-lite.ts +86 -0
  211. package/src/decisions/web-push-decision-cards.ts +1 -1
  212. package/src/discord-self-message.ts +9 -0
  213. package/src/domains/alert-audit.ts +271 -0
  214. package/src/domains/core-tools.ts +4 -3
  215. package/src/domains/ops-facts-tool.ts +31 -1
  216. package/src/flow-loop/split.ts +24 -0
  217. package/src/harness/harness-cli-command.ts +147 -17
  218. package/src/harness/harness-incidents.ts +2 -0
  219. package/src/harness/harness-pod-dispatch.ts +67 -8
  220. package/src/harness/harness-queue.ts +380 -50
  221. package/src/harness/harness-seams.ts +13 -3
  222. package/src/harness/helper-repair.ts +218 -0
  223. package/src/harness/helper-scan.ts +27 -4
  224. package/src/harness/launch-stamp.ts +236 -0
  225. package/src/harness/ledger-write-guard-child.fixture.ts +12 -0
  226. package/src/harness/ledger-write-guard.ts +35 -0
  227. package/src/harness/nested-elanous-policy.ts +114 -0
  228. package/src/harness/pod-memory-policy.ts +15 -0
  229. package/src/harness/repository-name.ts +55 -0
  230. package/src/hq/fence-audit.ts +90 -0
  231. package/src/hq/hq.ts +70 -6
  232. package/src/hq/lease.ts +9 -7
  233. package/src/hq/move-plan.ts +126 -0
  234. package/src/hq/promote.ts +31 -0
  235. package/src/hq/transfer.ts +185 -0
  236. package/src/index.ts +189 -13
  237. package/src/intake-plane/check.ts +63 -0
  238. package/src/intake-plane/digest.ts +65 -21
  239. package/src/intake-plane/front-classifier.ts +6 -0
  240. package/src/intake-plane/items.ts +3 -2
  241. package/src/intake-plane/lens.ts +177 -0
  242. package/src/intake-plane/route.ts +145 -1
  243. package/src/knowledge/know-find.ts +105 -0
  244. package/src/llm.ts +75 -19
  245. package/src/loops/budget.ts +36 -5
  246. package/src/loops/checker.ts +2 -2
  247. package/src/loops/orchestrator/author-depth.ts +65 -9
  248. package/src/loops/orchestrator/author-input-check.ts +8 -3
  249. package/src/loops/orchestrator/author-ledger.ts +3 -1
  250. package/src/loops/orchestrator/finish-rate.ts +14 -3
  251. package/src/loops/orchestrator/seat-share.ts +56 -0
  252. package/src/loops/orchestrator/tick.ts +107 -29
  253. package/src/loops/orchestrator/traffic.ts +117 -11
  254. package/src/loops/registry.ts +142 -61
  255. package/src/maturity/bot-slash-catalog.ts +11 -2
  256. package/src/maturity/feature-maturity.ts +4 -3
  257. package/src/maturity/tui-slash-availability.ts +40 -0
  258. package/src/mss/logging/log-store.ts +35 -4
  259. package/src/nexus/api/http-server.ts +28 -14
  260. package/src/nexus/api/loop-edges.ts +173 -0
  261. package/src/nexus/api/schedules-read.ts +13 -0
  262. package/src/nexus/api/seat-requests.ts +79 -56
  263. package/src/oauth/overload-failover-launch.ts +75 -0
  264. package/src/oauth/overload-failover.ts +137 -0
  265. package/src/prompt-enhance/enhance.ts +6 -1
  266. package/src/release-loop/auto-start.ts +6 -3
  267. package/src/release-loop/checklist.ts +19 -5
  268. package/src/release-loop/feature-store.ts +59 -31
  269. package/src/release-loop/merged-pr-checklist.ts +125 -0
  270. package/src/release-loop/placement.ts +98 -29
  271. package/src/seat-dispatch/seat-ask.ts +33 -6
  272. package/src/seat-dispatch/seat-request-eligibility.ts +19 -0
  273. package/src/seat-dispatch/seat-request-ledger.ts +133 -0
  274. package/src/seat-loop/seat-loop.ts +215 -12
  275. package/src/self-dev/ask-launch-flow.ts +22 -1
  276. package/src/self-dev/authored-goal-trace.ts +64 -0
  277. package/src/self-dev/dev-cli.ts +19 -1
  278. package/src/self-dev/dev-pipeline.ts +14 -9
  279. package/src/self-dev/draft-sweep.ts +90 -10
  280. package/src/self-dev/draft-triage-rules.ts +25 -8
  281. package/src/self-dev/entrance-registry.ts +10 -6
  282. package/src/self-dev/old-door.ts +73 -0
  283. package/src/self-dev/orchestrate.ts +15 -1
  284. package/src/self-dev/review-provider-fallback.ts +6 -1
  285. package/src/self-dev/run-supervisor.ts +49 -2
  286. package/src/self-implement/app-api-budget.ts +166 -0
  287. package/src/self-implement/budget-gate.ts +7 -5
  288. package/src/self-implement/follow-up-goals.ts +289 -0
  289. package/src/self-implement/frozen-merges.ts +3 -2
  290. package/src/self-implement/goal-author.ts +50 -6
  291. package/src/self-implement/goal-clarification-escalation.ts +5 -0
  292. package/src/self-implement/heal-intake.ts +120 -2
  293. package/src/self-implement/heal-triage.ts +44 -0
  294. package/src/self-implement/host-regate.ts +9 -2
  295. package/src/self-implement/l8-merge-queue.ts +132 -0
  296. package/src/self-implement/orchestrator.ts +325 -47
  297. package/src/self-implement/pipeline-shape.ts +1 -1
  298. package/src/self-implement/revert-guard.ts +42 -7
  299. package/src/self-implement/run-ledger.ts +6 -0
  300. package/src/self-implement/run-status-mapping.ts +2 -0
  301. package/src/self-implement/seams.ts +45 -16
  302. package/src/self-implement/self-implement-cli.ts +1 -0
  303. package/src/self-implement/speed-claims.ts +338 -0
  304. package/src/session-runtime/index.ts +2 -0
  305. package/src/session-runtime/retry-api.ts +87 -0
  306. package/src/session-runtime/retry-policy.ts +107 -6
  307. package/src/steward/triage.ts +74 -3
  308. package/src/steward/working-backwards.ts +20 -2
  309. package/src/task-cards/card-cli.ts +25 -0
  310. package/src/task-cards/card-store.ts +5 -3
  311. package/src/task-orchestrator/surfaces/pod-artifact-return.ts +63 -48
  312. package/src/task-orchestrator/surfaces/pod-command-job.ts +84 -9
  313. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +48 -0
  314. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +4 -3
  315. package/src/task-orchestrator/surfaces/pod-ledger-prod-sink.ts +75 -0
  316. package/src/task-orchestrator/surfaces/pod-pool.ts +70 -9
  317. package/src/task-orchestrator/surfaces/pod-source-receive.ts +37 -1
  318. package/src/task-orchestrator/surfaces/self-implement-pod.ts +166 -46
  319. package/src/task-orchestrator/surfaces/self-implement.ts +7 -1
  320. package/src/telegram-commands.ts +5 -0
  321. package/src/telegram.ts +72 -4
  322. package/src/user-config.ts +64 -7
  323. package/src/version/packed-revision.json +1 -1
  324. package/src/workflow-runtime/nodes/bash.ts +9 -1
  325. package/apps/pwa/out/_next/static/chunks/4562-071d8bac44a01069.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/5977-2df489622a52a33a.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/approvals/page-74383f890f075511.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/board/page-c496bb16da85385f.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/botlab/page-1abfe3aec3be6bdc.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/consult/page-fb260b9283dbf646.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/design-check/page-61ec1d2e92bdd50a.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/inside/page-fd7e200d4d83fbd7.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/live/page-d47266766766d227.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-2ce5471926761fdd.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/not-found-e995d30f6800b6bf.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/observatory/page-39f89c53038a51d4.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-5a753bb4ec0cc511.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/outputs/page-04250f0e2c0d03df.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/page-341480ed8e3c1ea8.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/sessions/page-8ac8d71c2adf9c8a.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/settings/devices/page-e1e47d8d940aba3d.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-94dc383d3d54db1a.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/setup/page-b3759cc14da80908.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/today/page-194dce4a83143fce.js +0 -1
  345. package/apps/pwa/out/_next/static/chunks/app/trace/page-35bdeae2e2c17147.js +0 -1
  346. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-16bef3da05a900c3.js +0 -1
  347. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-134c1bc762aee62c.js +0 -1
  348. package/apps/pwa/out/_next/static/css/2e33713c444d5b39.css +0 -1
  349. package/apps/pwa/out/_next/static/x6mjI6vqOkVVu0jFuDkz1/_buildManifest.js +0 -1
  350. /package/apps/pwa/out/_next/static/{x6mjI6vqOkVVu0jFuDkz1 → rv9cierV0jSOvDBm1g0XB}/_ssgManifest.js +0 -0
@@ -0,0 +1,185 @@
1
+ import { appendFileSync, mkdirSync, readSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ import { getElanousConfigDir } from '../elanous-config-dir.js';
5
+ import { getUserConfig } from '../user-config.js';
6
+ import { defaultSshRunner, localShellRunner, parseLease, sshLeaseStore, type LeaseStore, type SshRunner } from './lease.js';
7
+ import { promoteHq, type PromoteResult } from './promote.js';
8
+
9
+ type Direction = 'promote' | 'handback';
10
+ export interface TransferStep { step: string; status: 'done' | 'failed' | 'ready'; measurement: string }
11
+ export interface TransferResult { ok: boolean; direction: Direction; from: string; to: string; runId: string; journal: string | null; steps: TransferStep[] }
12
+ export interface TransferDeps {
13
+ store: LeaseStore;
14
+ run: SshRunner;
15
+ confirm: (question: string) => boolean;
16
+ promote: (host: string) => PromoteResult;
17
+ journalDir: string;
18
+ }
19
+
20
+ const aliases: Record<string, string[]> = { mbp: ['mbp', 'MacBookProM5'], node-b: ['node-b', 'MacStudioB1'] };
21
+ const valid = (alias: string, holder: string) => aliases[alias]?.includes(holder) ?? false;
22
+ const leaseCommand = (action: 'release' | 'acquire', expected?: { holder: string; generation: number }) =>
23
+ `elanous hq lease ${action}${expected ? ` --expected-holder '${expected.holder}' --expected-generation ${expected.generation}` : ''} --json`;
24
+ const identityCommand = `python3 - <<'PY'
25
+ import json, pathlib
26
+ home=pathlib.Path.home()
27
+ local=home/'.elanous-hq/host'
28
+ config=home/'.elanous-hqcfg/config.json'
29
+ name=local.read_text().strip() if local.exists() else json.loads(config.read_text())['hq']['hostName']
30
+ print(name)
31
+ PY`;
32
+ const cronCommand = `crontab -l && printf '\\n---HQ-AUDIT---\\n' && elanous hq fence-audit --json`;
33
+ const fenceCommand = `elanous hq fence --role cron -- sh -c 'printf "HQ_FENCE_ALLOWED:%s\\n" "$ELANOUS_HQ_GENERATION"'`;
34
+
35
+ export function defaultTransferDeps(): TransferDeps {
36
+ const arbiter = getUserConfig().hq?.arbiter ?? 'cloud-vm';
37
+ return {
38
+ store: sshLeaseStore(arbiter, arbiter === 'local' ? localShellRunner : defaultSshRunner),
39
+ run: defaultSshRunner,
40
+ confirm: (question) => {
41
+ if (!process.stdin.isTTY) return false;
42
+ process.stderr.write(`${question}\nType TRANSFER to continue: `);
43
+ const buffer = Buffer.alloc(128);
44
+ const count = requireRead(buffer);
45
+ return buffer.toString('utf8', 0, count).trim() === 'TRANSFER';
46
+ },
47
+ promote: (host) => promoteHq(host, true),
48
+ journalDir: join(getElanousConfigDir(), 'hq', 'transfers'),
49
+ };
50
+ }
51
+
52
+ function requireRead(buffer: Buffer): number {
53
+ // Read one terminal line without treating EOF or a non-interactive caller as consent.
54
+ let size = 0;
55
+ while (size < buffer.length) {
56
+ const n = requireReadByte(buffer, size);
57
+ if (n === 0) break;
58
+ size += n;
59
+ if (buffer[size - 1] === 10) break;
60
+ }
61
+ return size;
62
+ }
63
+
64
+ function requireReadByte(buffer: Buffer, offset: number): number {
65
+ return readSync(0, buffer, offset, 1, null);
66
+ }
67
+
68
+ export function transferHq(direction: Direction, apply = false, deps: TransferDeps = defaultTransferDeps()): TransferResult {
69
+ const from = direction === 'promote' ? 'mbp' : 'node-b';
70
+ const to = direction === 'promote' ? 'node-b' : 'mbp';
71
+ const runId = randomUUID();
72
+ const journal = apply ? join(deps.journalDir, `${runId}.jsonl`) : null;
73
+ const steps: TransferStep[] = [];
74
+ const result = (ok: boolean): TransferResult => ({ ok, direction, from, to, runId, journal, steps });
75
+ const record = (step: string, status: TransferStep['status'], measurement: string) => {
76
+ const row = { step, status, measurement };
77
+ steps.push(row);
78
+ if (journal) {
79
+ mkdirSync(deps.journalDir, { recursive: true, mode: 0o700 });
80
+ appendFileSync(journal, `${JSON.stringify({ at: new Date().toISOString(), runId, direction, from, to, ...row })}\n`, { mode: 0o600 });
81
+ }
82
+ return status !== 'failed';
83
+ };
84
+ const lease = () => {
85
+ const { now, raw } = deps.store.read();
86
+ const current = parseLease(raw);
87
+ return { current, now };
88
+ };
89
+ const remote = (host: string, command: string) => {
90
+ const r = deps.run(host, command);
91
+ if (r.status !== 0) throw new Error(`${host}: ${r.stderr.trim() || r.stdout.trim() || `exit=${r.status}`}`);
92
+ return r.stdout.trim();
93
+ };
94
+ try {
95
+ const before = lease();
96
+ if (!before.current || !valid(from, before.current.holder) || before.current.renewedAt + before.current.ttlSeconds < before.now) {
97
+ record('① current lease', 'failed', `holder=${before.current?.holder ?? 'none'} generation=${before.current?.generation ?? 'none'} expired=${before.current ? before.current.renewedAt + before.current.ttlSeconds < before.now : true}`);
98
+ return result(false);
99
+ }
100
+ const source = remote(from, identityCommand);
101
+ const target = remote(to, identityCommand);
102
+ if (source !== before.current.holder || !valid(to, target) || source === target) {
103
+ record('② host identities', 'failed', `source=${source} holder=${before.current.holder} target=${target}`);
104
+ return result(false);
105
+ }
106
+ record('① current lease', 'done', `holder=${source} generation=${before.current.generation}`);
107
+ record('② host identities', 'done', `source=${source} target=${target}`);
108
+ for (const host of [from, to]) {
109
+ const inventory = remote(host, cronCommand);
110
+ const marker = '\n---HQ-AUDIT---\n';
111
+ const index = inventory.lastIndexOf(marker);
112
+ if (index < 0) throw new Error(`${host}: missing cron audit`);
113
+ const cron = inventory.slice(0, index);
114
+ const audit = JSON.parse(inventory.slice(index + marker.length)) as unknown;
115
+ const live = cron.split('\n').filter(line => line.trim() && !line.trimStart().startsWith('#'));
116
+ if (!Array.isArray(audit) || audit.length > 0 || !live.some(line => /hq heartbeat\b/.test(line)) || !live.some(line => /hq fence --role cron\b/.test(line))) {
117
+ record(`③ cron fence ${host}`, 'failed', `unfenced=${Array.isArray(audit) ? audit.length : 'invalid'} heartbeat=${live.some(line => /hq heartbeat\b/.test(line))} fenced=${live.some(line => /hq fence --role cron\b/.test(line))}`);
118
+ return result(false);
119
+ }
120
+ record(`③ cron fence ${host}`, 'done', `unfenced=0 heartbeat=true fenced=true`);
121
+ }
122
+ const oldFence = remote(from, fenceCommand);
123
+ const newFence = remote(to, fenceCommand);
124
+ if (oldFence !== `HQ_FENCE_ALLOWED:${before.current.generation}` || newFence.includes('HQ_FENCE_ALLOWED:')) {
125
+ record('④ pre-transfer fence', 'failed', `source=${oldFence} target=${newFence}`);
126
+ return result(false);
127
+ }
128
+ record('④ pre-transfer fence', 'done', `source=allowed generation=${before.current.generation} target=skipped`);
129
+ if (!apply) {
130
+ record('⑤ human confirmation', 'ready', 'dry-run: no writes; --apply requires interactive TRANSFER confirmation');
131
+ return result(true);
132
+ }
133
+ if (!deps.confirm(`Release ${source} generation ${before.current.generation}, acquire ${target}, and switch cron fence? This cannot undo work already sent.`)) {
134
+ record('⑤ human confirmation', 'failed', 'confirmation declined');
135
+ return result(false);
136
+ }
137
+ record('⑤ human confirmation', 'done', 'operator typed TRANSFER');
138
+ const still = lease().current;
139
+ if (!still || still.holder !== source || still.generation !== before.current.generation || still.renewedAt + still.ttlSeconds < deps.store.read().now) {
140
+ record('⑥ release old lease', 'failed', 'lease changed before release');
141
+ return result(false);
142
+ }
143
+ let released: { ok?: boolean; record?: { holder?: string; generation?: number } };
144
+ try { released = JSON.parse(remote(from, leaseCommand('release', { holder: source, generation: before.current.generation }))); }
145
+ catch (error) {
146
+ record('⑥ release old lease', 'failed', error instanceof Error ? error.message : String(error));
147
+ return result(false);
148
+ }
149
+ if (released.ok !== true || released.record?.holder !== source || released.record.generation !== before.current.generation) {
150
+ record('⑥ release old lease', 'failed', 'release not confirmed');
151
+ return result(false);
152
+ }
153
+ record('⑥ release old lease', 'done', `holder=${source} generation=${released.record.generation} released=true`);
154
+ const acquired = JSON.parse(remote(to, leaseCommand('acquire'))) as { ok?: boolean; record?: { holder?: string; generation?: number } };
155
+ const current = lease().current;
156
+ if (acquired.ok !== true || acquired.record?.holder !== target || acquired.record.generation !== before.current.generation + 1 || current?.holder !== target || current.generation !== acquired.record.generation) {
157
+ record('⑦ acquire new lease', 'failed', `expected=${target} generation=${before.current.generation + 1} observed=${current?.holder ?? 'none'} generation=${current?.generation ?? 'none'}; keep old host fenced, recover manually`);
158
+ return result(false);
159
+ }
160
+ record('⑦ acquire new lease', 'done', `holder=${target} generation=${current.generation}`);
161
+ if (direction === 'promote') {
162
+ const promoted = deps.promote(to);
163
+ for (const line of promoted.lines) record(`⑧ promotion ${line.step}`, line.status, line.measurement);
164
+ if (!promoted.ok) return result(false);
165
+ }
166
+ const sourceAfter = remote(from, fenceCommand);
167
+ const targetAfter = remote(to, fenceCommand);
168
+ const observed = lease().current;
169
+ if (sourceAfter.includes('HQ_FENCE_ALLOWED:') || targetAfter !== `HQ_FENCE_ALLOWED:${current.generation}` || observed?.holder !== target || observed.generation !== current.generation) {
170
+ record('⑨ final fence', 'failed', `source=${sourceAfter} target=${targetAfter} lease=${observed?.holder ?? 'none'} generation=${observed?.generation ?? 'none'}`);
171
+ return result(false);
172
+ }
173
+ record('⑨ final fence', 'done', `source=skipped target=allowed generation=${current.generation}`);
174
+ return result(true);
175
+ } catch (error) {
176
+ record('halt', 'failed', error instanceof Error ? error.message : String(error));
177
+ return result(false);
178
+ }
179
+ }
180
+
181
+ export function formatTransfer(result: TransferResult): string {
182
+ return [`hq ${result.direction}: ${result.ok ? 'ok' : 'failed'} · ${result.from} → ${result.to} · run=${result.runId}`,
183
+ ...result.steps.map(s => `${s.step}: ${s.status} · ${s.measurement}`),
184
+ ...(result.journal ? [`journal=${result.journal}`] : [])].join('\n');
185
+ }
package/src/index.ts CHANGED
@@ -27,10 +27,13 @@ import { installDevCompletionGuard } from './self-dev/dev-completion-guard.js';
27
27
  import { buildOrchestrateDecomposePrepareArgs, normalizeOrchestrateRequest, splitOrchestrateGoalTexts } from './self-dev/self-orchestrate-runtime.js';
28
28
  import { CLI_ENTRANCE_BASELINE, evaluateCommandEntranceBaseline } from './self-dev/entrance-baseline.js';
29
29
  import { CLI_HARNESS_DOGFOOD_ENTRANCE, CLI_HARNESS_ORCHESTRATE_ENTRANCE, describeEntranceCommand, listEntrancesWithModelExposure, renderLaunchEntrances, summarizeEntrances, type EntranceId } from './self-dev/entrance-registry.js';
30
+ import { isOldDoorInternalCall, refuseOldDoor } from './self-dev/old-door.js';
30
31
  import { collectCommandEntrances, renderCommandEntrances } from './self-dev/entrance-inventory.js';
31
32
  import { isGoalAuthorFileName } from './self-implement/goal-document.js';
32
33
  import { DEV_PIPELINE_SINK_SURFACE } from './self-implement/self-cli-sink-surface.js';
33
34
  import { registerPrivateLedgerCommands } from './cli/private-ledger-commands.js';
35
+ import { registerKnowCommand } from './cli/know-cli.js';
36
+ import { registerSeatRequestsCommands } from './cli/seat-requests-cli.js';
34
37
  import { applyTestStateDirFlagFromArgv } from './cli/test-state-dir-flag.js';
35
38
  import { applyTestFlagFromArgv, observeTestFlagOwnership, uncoveredTestFlagPaths, staleTestFlagPaths } from './cli/test-flag.js';
36
39
  import { registerPtyTakeoverCommands } from './cli/pty-takeover-cli.js';
@@ -46,11 +49,13 @@ import { registerPublishCommands } from './cli/publish-cli.js';
46
49
  import { registerAutopilotCommands } from './cli/autopilot-cli.js';
47
50
  import { registerRoleCommands } from './cli/role-cli.js';
48
51
  import { registerMachineCommands } from './cli/machine-cli.js';
52
+ import { registerAgentEnvCommands } from './cli/agent-env-cli.js';
49
53
  import { registerScheduleCommands } from './cli/schedule-cli.js';
50
54
  export { runSchedule, scheduleCreatePlan } from './cli/schedule-cli.js';
51
55
  export type { ScheduleDispatch } from './cli/schedule-cli.js';
52
56
  import { registerReviewLoopOptions, buildReviewLoopOpts } from './agent-mission/review-loop-cli.js';
53
57
  import { registerCapabilitiesCommand } from './agent-mission/capabilities-cli.js';
58
+ import { registerSupervisorMetricsCommand } from './agent-mission/supervisor-metrics-cli.js';
54
59
  import { agentBackendNames } from './agent-mission/driver.js';
55
60
  import { registerWhereCommand } from './cli/where-cli.js';
56
61
  import { registerShadowCommand } from './cli/shadow-cli.js';
@@ -68,6 +73,7 @@ import { registerFlowCommands } from './cli/flow-cli.js';
68
73
  import { registerModelWatchCommand } from './cli/model-watch-cli.js';
69
74
  import { registerIntakeCommands } from './cli/intake-cli.js';
70
75
  import { registerTasksCommands } from './cli/tasks-cli.js';
76
+ import { registerBriefCommands } from './cli/brief-cli.js';
71
77
  import { registerLabelsCommands } from './cli/labels-cli.js';
72
78
  import { registerMsgCommands } from './cli/msg-cli.js';
73
79
  import { registerDoctorCommand } from './cli/doctor-cli.js';
@@ -80,6 +86,7 @@ import { registerStartCommand } from './cli/start-cli.js';
80
86
  import { registerGroundingSourcesCli } from './grounding/sources-cli.js';
81
87
  import { registerGraphCommands } from './graph-runner/graph-cli.js';
82
88
  import { registerLoopCommands } from './loops/loop-cli.js';
89
+ import { registerHqMovePlanCommand } from './hq/hq.js';
83
90
  import { registerCardCommand } from './task-cards/card-cli.js';
84
91
  import { registerLaunchHeadCommands } from './launch-head/launch-head-cli.js';
85
92
  import { acpServerHelpText } from './boot/acp-server-help.js';
@@ -856,6 +863,7 @@ registerPrCommands(program);
856
863
  registerRepoCommands(program);
857
864
  registerRoleCommands(program);
858
865
  registerMachineCommands(program);
866
+ registerAgentEnvCommands(program);
859
867
  // CLI entry: where action → resolveCurrentInstance → resolveInstance (instance universe).
860
868
  registerWhereCommand(program);
861
869
  registerShadowCommand(program);
@@ -871,6 +879,7 @@ registerUsageCommand(program);
871
879
  registerLiveCommands(program);
872
880
  registerResearchCommand(program);
873
881
  registerReleaseCommands(program);
882
+ registerKnowCommand(program);
874
883
  registerFreezeCommands(program);
875
884
  registerFlowCommands(program);
876
885
  // HQ-HB · HQ-FENCE (10-04): one writer across mbp · node-b · cloud-vm — lease on the arbiter, 2-of-3 quorum, generation fencing.
@@ -889,24 +898,42 @@ hqCmd.command('host').description('이 머신의 HQ 호스트 이름 설정')
889
898
  });
890
899
  hqCmd.command('lease').argument('<action>', 'acquire|renew|status|release')
891
900
  .option('--host <name>', '이 호스트 이름(기본 ~/.elanous-hq/host · hq.hostName · os.hostname)')
901
+ .option('--expected-holder <name>', 'release CAS: 해제할 임대의 예상 소유자')
902
+ .option('--expected-generation <number>', 'release CAS: 해제할 임대의 예상 세대')
892
903
  .option('--json', 'JSON 출력')
893
- .action(async (action: string, opts: { host?: string; json?: boolean }) => {
904
+ .action(async (action: string, opts: { host?: string; expectedHolder?: string; expectedGeneration?: string; json?: boolean }) => {
894
905
  try {
895
906
  if (!['acquire', 'renew', 'status', 'release'].includes(action)) throw new CliUserError(`hq lease: unknown action ${action}`, 'acquire|renew|status|release');
907
+ if ((opts.expectedHolder !== undefined || opts.expectedGeneration !== undefined) && action !== 'release')
908
+ throw new CliUserError('hq lease: expected holder/generation are only valid for release');
909
+ if ((opts.expectedHolder === undefined) !== (opts.expectedGeneration === undefined)
910
+ || (opts.expectedGeneration !== undefined && (!/^[1-9]\d*$/.test(opts.expectedGeneration) || !Number.isSafeInteger(Number(opts.expectedGeneration)))))
911
+ throw new CliUserError('hq lease release: supply both --expected-holder and --expected-generation (positive integer)');
896
912
  await hqSink();
897
913
  const { hqLease } = await import('./hq/hq.js');
898
- const result = hqLease(action as 'acquire', {}, opts.host ? { host: opts.host } : {});
914
+ const result = hqLease(action as 'acquire', {}, {
915
+ ...(opts.host ? { host: opts.host } : {}),
916
+ ...(opts.expectedHolder !== undefined ? { expectedHolder: opts.expectedHolder, expectedGeneration: Number(opts.expectedGeneration) } : {}),
917
+ });
899
918
  if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
900
919
  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 : ''}`);
901
920
  if (!result.ok) process.exitCode = 2;
902
921
  } catch (error) { if (error instanceof CliUserError) throw error; console.error(`hq lease: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
903
922
  });
904
923
  hqCmd.command('promote').description('HQ 승격 런북 (기본 dry-run · --apply 일 때만 변경)')
905
- .requiredOption('--to <host>', '승격 대상 호스트')
906
- .option('--apply', '대기 사본을 승격 우주에 반영하고 넥서스·rescue 실행')
924
+ .option('--to <host>', '기존 임대 취득 후 승격 대상 호스트 (생략하면 mbp → node-b 전환 포함)')
925
+ .option('--apply', '임대 이전 및 승격 실행 · 터미널 확인 필수 (기존 --to 경로는 임대 보유 필요)')
907
926
  .option('--json', 'JSON 출력')
908
927
  .action(async (opts: { to: string; apply?: boolean; json?: boolean }) => {
909
928
  try {
929
+ if (!opts.to) {
930
+ const { transferHq, formatTransfer } = await import('./hq/transfer.js');
931
+ const result = transferHq('promote', opts.apply === true);
932
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
933
+ else console.log(formatTransfer(result));
934
+ if (!result.ok) process.exitCode = 1;
935
+ return;
936
+ }
910
937
  const { promoteHq, formatPromote } = await import('./hq/promote.js');
911
938
  const result = promoteHq(opts.to, opts.apply === true);
912
939
  if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
@@ -914,6 +941,18 @@ hqCmd.command('promote').description('HQ 승격 런북 (기본 dry-run · --appl
914
941
  if (!result.ok) process.exitCode = 1;
915
942
  } catch (error) { console.error(`hq promote: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
916
943
  });
944
+ hqCmd.command('handback').description('node-b → mbp 임대·크론 울타리 복귀 (기본 읽기 전용)')
945
+ .option('--apply', '각 검사를 통과한 뒤 터미널 확인을 받고 이전 실행')
946
+ .option('--json', '단계별 결과·저널 경로 JSON 출력')
947
+ .action(async (opts: { apply?: boolean; json?: boolean }) => {
948
+ try {
949
+ const { transferHq, formatTransfer } = await import('./hq/transfer.js');
950
+ const result = transferHq('handback', opts.apply === true);
951
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
952
+ else console.log(formatTransfer(result));
953
+ if (!result.ok) process.exitCode = 1;
954
+ } catch (error) { console.error(`hq handback: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
955
+ });
917
956
  hqCmd.command('heartbeat').description('보유자는 갱신 · 아니면 대기(보유자에 닿는지 중재자에 보고)').option('--json', 'JSON 출력')
918
957
  .action(async (opts: { json?: boolean }) => {
919
958
  try {
@@ -937,6 +976,18 @@ hqCmd.command('seen').description('이 호스트가 본 가장 높은 임대 세
937
976
  else console.log(result.seen ? `hq seen: generation ${result.generation}` : 'hq seen: never');
938
977
  process.exitCode = result.seen ? 0 : 3;
939
978
  });
979
+ registerHqMovePlanCommand(hqCmd);
980
+ hqCmd.command('fence-audit').description('운영 crontab 의 HQ 울타리 밖 쓰기 잡 목록과 감싼 줄 제안(읽기 전용)')
981
+ .option('--json', 'JSON 출력')
982
+ .action(async (opts: { json?: boolean }) => {
983
+ try {
984
+ await hqSink();
985
+ const rows = (await import('./hq/fence-audit.js')).hqFenceAudit();
986
+ if (opts.json) await writeStdoutJson(`${JSON.stringify(rows)}\n`);
987
+ else if (!rows.length) console.log('hq fence-audit: unfenced 0');
988
+ else for (const row of rows) console.log(`${row.raw}\n role: ${row.recommendedRole}\n suggested: ${row.suggestedLine}`);
989
+ } catch (error) { console.error(`hq fence-audit: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
990
+ });
940
991
  hqCmd.command('fence').description('이 호스트가 임대를 쥐었을 때만 명령 실행(아니면 건너뜀 · exit 0)')
941
992
  .requiredOption('--role <role>', 'telegram-poller|cron|seat-loop|release-run|conatus|git-push')
942
993
  .argument('<command...>', '-- 뒤의 명령')
@@ -947,12 +998,63 @@ hqCmd.command('fence').description('이 호스트가 임대를 쥐었을 때만
947
998
  process.exitCode = hqFenceRun(opts.role as 'cron', command);
948
999
  });
949
1000
  const seatCmd = program.command('seat').description('분배된 자리의 하루 루프와 보고');
1001
+ registerSeatRequestsCommands(seatCmd);
950
1002
  seatCmd.command('answer <askId> <answer>').description('자리 질문에 답을 남기고 원래 표면으로 회신 예약')
951
1003
  .action(async (askId: string, answer: string) => {
952
1004
  try {
953
- const { answerSeatAsk } = await import('./seat-dispatch/seat-ask.js');
1005
+ try { await (await import('./domains/standalone-log-sink.js')).registerStandaloneLogSink('seat-answer'); } catch { /* file trail remains available */ }
1006
+ const { debug } = await import('./debug/log.js');
1007
+ const { answerSeatAsk, deliverSeatAnswers, seatAskDeliveryInProgress, seatAskReplyInfo } = await import('./seat-dispatch/seat-ask.js');
954
1008
  answerSeatAsk(askId, answer);
1009
+ const { origin, askedAt, answeredAt } = seatAskReplyInfo(askId);
955
1010
  console.log(`자리 답변 기록: ${askId}`);
1011
+ if (origin.channel !== 'telegram') {
1012
+ debug.log('seat.ask', 'reply-no-target', { id: askId, via: origin.channel });
1013
+ console.log(`전달 대상 없음: ${askId} (${origin.channel})`);
1014
+ return;
1015
+ }
1016
+ const token = process.env.TELEGRAM_BOT_TOKEN?.trim();
1017
+ const elapsedMs = askedAt === null ? null : Math.max(0, answeredAt - askedAt);
1018
+ if (!token) {
1019
+ debug.log('seat.ask', 'reply-failed', { id: askId, via: 'telegram', reason: 'token-missing', elapsedMs });
1020
+ console.log(`텔레그램 토큰 없음 (TELEGRAM_BOT_TOKEN): ${askId} · 회신 대기`);
1021
+ return;
1022
+ }
1023
+ const botId = token.split(':')[0];
1024
+ if (origin.botId && origin.botId !== botId) {
1025
+ debug.log('seat.ask', 'reply-no-target', { id: askId, via: 'telegram', reason: 'bot-mismatch' });
1026
+ console.log(`전달 대상 없음: ${askId} (수신 봇 불일치) · 회신 대기`);
1027
+ return;
1028
+ }
1029
+ try {
1030
+ let sent = false;
1031
+ await deliverSeatAnswers({ askId, channel: 'telegram', botId, send: async (target, text) => {
1032
+ if (target.channel !== 'telegram') throw new Error('seat ask belongs to another surface');
1033
+ const response = await fetch(`https://api.telegram.org/bot${token}/sendMessage`, {
1034
+ method: 'POST', headers: { 'content-type': 'application/json' },
1035
+ body: JSON.stringify({ chat_id: target.chatId, text, reply_to_message_id: target.messageId,
1036
+ ...(target.threadId === undefined ? {} : { message_thread_id: target.threadId }) }),
1037
+ });
1038
+ const result = await response.json() as { ok?: boolean; description?: string };
1039
+ if (!response.ok || result.ok !== true) throw new Error(result.description ?? `Telegram HTTP ${response.status}`);
1040
+ sent = true;
1041
+ } });
1042
+ if (!sent) {
1043
+ if (seatAskDeliveryInProgress(askId)) {
1044
+ debug.log('seat.ask', 'reply-in-progress', { id: askId, via: 'telegram', reason: 'claimed-by-other-worker' });
1045
+ console.log(`다른 작업자가 전달 중: ${askId}`);
1046
+ } else {
1047
+ debug.log('seat.ask', 'reply-no-target', { id: askId, via: 'telegram', reason: 'not-claimed' });
1048
+ console.log(`전달 대상 없음: ${askId} (회신 대기)`);
1049
+ }
1050
+ return;
1051
+ }
1052
+ debug.log('seat.ask', 'reply-sent', { id: askId, via: 'telegram', elapsedMs });
1053
+ console.log(`텔레그램 회신 전달: ${askId} · 지연 ${elapsedMs === null ? '미상' : `${elapsedMs}ms`}`);
1054
+ } catch (error) {
1055
+ debug.log('seat.ask', 'reply-failed', { id: askId, via: 'telegram', reason: error instanceof Error ? error.message : String(error), elapsedMs });
1056
+ console.log(`텔레그램 회신 실패: ${askId} · 회신 대기`);
1057
+ }
956
1058
  } catch (error) { console.error(`seat answer: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
957
1059
  });
958
1060
  seatCmd.command('loop').requiredOption('--seat <SEAT>', 'MK|OP|TC|UX')
@@ -962,10 +1064,10 @@ seatCmd.command('loop').requiredOption('--seat <SEAT>', 'MK|OP|TC|UX')
962
1064
  try {
963
1065
  // V3 F5: without the sink, the seat loop's judgment lines (seat.loop) never reach the log store when cron runs it.
964
1066
  await (await import('./domains/standalone-log-sink.js')).registerStandaloneLogSink('seat-loop');
965
- const { runSeatLoopTurn } = await import('./seat-loop/seat-loop.js');
1067
+ const { runSeatLoopTurn, seatLoopTickLine } = await import('./seat-loop/seat-loop.js');
966
1068
  const result = await runSeatLoopTurn(opts.seat);
967
1069
  if (opts.json) await writeStdoutJson(`${JSON.stringify(result)}\n`);
968
- else console.log(`seat loop ${opts.seat}: ${result.status}${'runId' in result && result.runId ? ` ${result.runId}` : ''}`);
1070
+ else console.log(seatLoopTickLine(result));
969
1071
  } catch (error) { console.error(`seat loop: ${error instanceof Error ? error.message : String(error)}`); process.exitCode = 1; }
970
1072
  });
971
1073
  seatCmd.command('report').requiredOption('--seat <SEAT>', 'MK|OP|TC|UX')
@@ -1161,8 +1263,10 @@ program.command('self-update')
1161
1263
  .option('--keep <n>', '설치 뒤 남길 최근 판 수(설치본은 current·직전 판, 체크아웃은 current·데몬 판 보호 · 0 이면 정리 안 함)', '3')
1162
1264
  .option('--alert', '실패(exit≠0)를 알림으로도 보냄 — 크론(무인) 실행용')
1163
1265
  .option('--skip-pwa-build', '체크아웃 설치 전 PWA 빌드를 명시적으로 건너뜀')
1266
+ .option('--dev', '판 사이 dev 빌드 설치 — versions/<판>-dev.<sha12> · install.json channel=dev · 이전 versionDir 보존 (사람 승인 · 자동 아님)')
1267
+ .option('--rollback', 'install.json 의 이전 versionDir 로 current 를 되돌린다 (이전이 없으면 거부)')
1164
1268
  .option('--auto <on|off|status>', '자동 갱신 — macOS launchd · Linux systemd 타이머가 매일 04:17 에 `self-update --restart --alert` (크론이 이미 부르면 켜지 않는다)')
1165
- .action(async (opts: { from?: string; version?: string; restart?: boolean; json?: boolean; keep?: string; alert?: boolean; skipPwaBuild?: boolean; auto?: string }) => {
1269
+ .action(async (opts: { from?: string; version?: string; restart?: boolean; json?: boolean; keep?: string; alert?: boolean; skipPwaBuild?: boolean; dev?: boolean; rollback?: boolean; auto?: string }) => {
1166
1270
  if (opts.auto !== undefined) {
1167
1271
  if (!['on', 'off', 'status'].includes(opts.auto)) { console.error(`--auto 는 on · off · status 중 하나: ${opts.auto}`); process.exitCode = 2; return; }
1168
1272
  const { runAutoUpdate } = await import('./cli/update-auto.js');
@@ -3457,6 +3561,22 @@ selfCmd
3457
3561
  }
3458
3562
  });
3459
3563
 
3564
+ selfCmd
3565
+ .command('speed-claims')
3566
+ .description('런 원장 발사→종결 시간의 자리별·결과별 분포를 읽기 전용으로 출력한다')
3567
+ .option('--json', '구조화된 JSON으로 출력한다')
3568
+ .action(async (opts: { json?: boolean }) => {
3569
+ try {
3570
+ const { measureSpeedClaims, renderSpeedClaims } = await import('./self-implement/speed-claims.js');
3571
+ const result = measureSpeedClaims();
3572
+ if (opts.json) await writeStdoutJson(JSON.stringify(result) + '\n');
3573
+ else console.log(renderSpeedClaims(result));
3574
+ } catch (error) {
3575
+ console.error(`speed-claims measurement failed: ${error instanceof Error ? error.message : String(error)}`);
3576
+ process.exitCode = 1;
3577
+ }
3578
+ });
3579
+
3460
3580
  selfCmd
3461
3581
  .command('goal-source-distribution')
3462
3582
  .description('self-implement 런 원장의 start goalSource 분포를 읽기 전용으로 출력한다')
@@ -4226,6 +4346,12 @@ selfCmd
4226
4346
  .option('--supervise-rounds <n>', '슈퍼바이저 재개 라운드 상한 (기본 3 · 끄려면 --no-supervise)')
4227
4347
  .option('--json', '구조화 출력 {stage, ok, branch, worktree, prUrl?, merged?}')
4228
4348
  .action(async (parts: string[], opts: { featureFile?: string; base?: string; draft?: boolean; openPr?: boolean; autoMerge?: boolean; autoReview?: boolean; maxWait?: string; json?: boolean; plan?: boolean; enhance?: boolean; ground?: boolean; observeOnly?: boolean }) => {
4349
+ if (!isOldDoorInternalCall('self-implement')) {
4350
+ const refused = refuseOldDoor('self-implement');
4351
+ debug.log('harness.entrance', 'old-door-refused', { door: refused.door, caller: refused.caller });
4352
+ ui.error(refused.message);
4353
+ process.exit(refused.exitCode);
4354
+ }
4229
4355
  if (opts.featureFile && parts.length > 0) { ui.error('--feature-file 과 위치 인자 feature 는 함께 줄 수 없다'); process.exit(2); }
4230
4356
  let feature: string;
4231
4357
  try { feature = (opts.featureFile ? readFileSync(opts.featureFile, 'utf8') : parts.join(' ')).trim(); }
@@ -4379,9 +4505,18 @@ const selfOrchestrateCmd = selfCmd
4379
4505
  .option('--no-pod-rebuild', 'pod: 이미지 판(elanous.commit)이 HEAD 와 달라도 다시 굽지 않는다 — 측정은 «이미지 판»을 잰다')
4380
4506
  .option('--pod-pass-env <keys>', 'pod: 호스트 env 에서 Pod 로 넘길 키(쉼표) — 예 OPENROUTER_API_KEY,ANTHROPIC_API_KEY(벤치마크 과금 경로)')
4381
4507
  .option('--bench-arms <spec>', 'pod 벤치마크: 골 1개를 팔마다 «라벨 한 줄만 다르게» 복제해 동시에 — "id=provider[:model][@KEY+KEY];…" (예 codex=openai-codex;or-kimi=openrouter:openrouter/moonshotai/kimi-k3@OPENROUTER_API_KEY) · --auto-merge 거부 · RFC fleet 슈퍼바이저 §A3')
4508
+ .addOption(new Option('--child-llm-provider <id>', 'pod: 구현 자식 LLM provider(openai-codex|grok) — 하니스 --child-llm-provider 가 이 자리로 온다(10-05)').hideHelp())
4509
+ .addOption(new Option('--child-llm-model <id>', 'pod: 구현 자식 LLM model(--child-llm-provider 와 함께)').hideHelp())
4510
+ .addOption(new Option('--child-llm-effort <level>', 'pod: 구현 자식 추론 노력(--child-llm-provider 와 함께)').hideHelp())
4382
4511
  .option('--help-all', '모든 orchestrate 옵션 표시')
4383
4512
  .action(function (this: Command, parts: string[], opts: { goalFile?: string[]; concurrency?: string; autoMerge?: boolean; autoReview?: boolean; openPr?: boolean; base?: string; teardown?: boolean; resume?: string; board?: boolean; decompose?: boolean; fabricDecompose?: boolean; maxTasks?: string; supervise?: boolean; superviseRounds?: string; json?: boolean }) {
4384
4513
  return (async () => {
4514
+ if (!isOldDoorInternalCall('self-orchestrate')) {
4515
+ const refused = refuseOldDoor('self-orchestrate');
4516
+ debug.log('harness.entrance', 'old-door-refused', { door: refused.door, caller: refused.caller });
4517
+ ui.error(refused.message);
4518
+ process.exit(refused.exitCode);
4519
+ }
4385
4520
  // 위치 인자는 종전처럼 분리하고 파일 입력은 파일당 한 골이다.
4386
4521
  let inputGoals: ReturnType<typeof readSelfOrchestrateGoals>;
4387
4522
  try { inputGoals = readSelfOrchestrateGoals(parts, opts.goalFile); }
@@ -4613,6 +4748,19 @@ const selfOrchestrateCmd = selfCmd
4613
4748
  let rotationAccounts: readonly string[] | undefined;
4614
4749
  let podProvider: 'openai-codex' | 'grok' = 'openai-codex';
4615
4750
  let grokApiKeyOptIn = false;
4751
+ {
4752
+ // 10-05 PODPROVIDER: refuse child choices this Pod launch cannot honour instead of dropping them.
4753
+ const child = opts as { childLlmProvider?: string; childLlmModel?: string; childLlmEffort?: string };
4754
+ const named = child.childLlmProvider?.trim();
4755
+ const refuse = !named && (child.childLlmModel?.trim() || child.childLlmEffort?.trim())
4756
+ ? 'pod: --child-llm-model/--child-llm-effort 는 --child-llm-provider 와 함께'
4757
+ : named && benchArms
4758
+ ? 'pod: --child-llm-provider 는 --bench-arms 와 함께 못 쓴다(팔마다 모델이 하나다)'
4759
+ : named === 'grok' && explicitPodAccount
4760
+ ? 'pod: --child-llm-provider grok 은 --pod-account 와 함께 못 쓴다(그것은 codex 계정을 고정한다)'
4761
+ : undefined;
4762
+ if (refuse) { ui.error(refuse); debug.log('self-implement.pod', 'child-provider-refused', { provider: named ?? null, reason: refuse }); process.exit(2); }
4763
+ }
4616
4764
  if (!explicitPodAccount && !benchArms) {
4617
4765
  const { inspectCodexRotation } = await import('./oauth/codex-account-store.js');
4618
4766
  const { planPodProvider, makePodAccountBroker } = await import('./task-orchestrator/surfaces/pod-account-broker.js');
@@ -4651,17 +4799,31 @@ const selfOrchestrateCmd = selfCmd
4651
4799
  if (!opts.json) ui.warn(podGrokSkippedLine(grokCheck));
4652
4800
  }
4653
4801
  }
4654
- const plan = planPodProvider({
4802
+ // 10-05 PODPROVIDER: a launch that named the child provider gets it, or is refused — never a silent codex.
4803
+ const namedChild = (opts as { childLlmProvider?: string }).childLlmProvider?.trim();
4804
+ const { podNamedChildProvider, podFallbackCredentials, podGrokSubscriptionUsable: namedGrokUsable } = await import('./task-orchestrator/surfaces/self-implement-pod.js');
4805
+ const namedChildDecision = podNamedChildProvider(namedChild, {
4806
+ grokSubscription: namedChild === 'grok' && credential?.kind === 'subscription' && (grokSubscriptionUsable || namedGrokUsable().usable),
4807
+ grokApiKey: credential?.kind === 'api_key' && grokApiKeyOptIn,
4808
+ });
4809
+ if (namedChildDecision.refuse) { ui.error(namedChildDecision.refuse); debug.log('self-implement.pod', 'child-provider-refused', { provider: namedChild, reason: namedChildDecision.refuse }); process.exit(2); }
4810
+ // A named codex child must stay codex — the plan may not fall back to grok (post-review must-fix).
4811
+ const namedCodex = namedChildDecision.provider === 'openai-codex';
4812
+ const plan: import('./task-orchestrator/surfaces/pod-account-broker.js').PodProviderPlan = namedChildDecision.provider === 'grok'
4813
+ ? { provider: 'grok', excluded: [], grokSubscriptionEligible: credential?.kind === 'subscription' }
4814
+ : planPodProvider({
4655
4815
  codexCandidates: inspectCodexRotation().candidates,
4656
4816
  // Per-account caps (대표 default:97) override the Pod default cap — without them default was dropped at 60%.
4657
4817
  thresholdPercentByAccount: getUserConfig().llm?.codexAccountRotationThresholdPercentByAccount,
4658
4818
  // 대표 한도 정책 한 값(`llm.codexQuotaPolicy`) — 크레딧·폴백을 같이 정한다.
4659
4819
  creditsAllowed: codexPolicyAllowsCredits(quotaPolicy),
4660
- grokSubscription: codexPolicyAllowsFallback(quotaPolicy) && grokSubscriptionUsable,
4661
- grokApiKey: codexPolicyAllowsFallback(quotaPolicy) && credential?.kind === 'api_key',
4820
+ ...podFallbackCredentials(namedChildDecision.provider, {
4821
+ grokSubscription: codexPolicyAllowsFallback(quotaPolicy) && grokSubscriptionUsable,
4822
+ grokApiKey: codexPolicyAllowsFallback(quotaPolicy) && credential?.kind === 'api_key',
4823
+ }),
4662
4824
  grokApiKeyOptIn,
4663
4825
  });
4664
- if (plan.provider === null) { ui.error(`pod: 쓸 codex 계정이 없다 — ${plan.reasons.join(' · ')} · 계정을 명시하려면 --pod-account <이름>`); process.exit(2); }
4826
+ if (plan.provider === null) { ui.error(`pod: 쓸 codex 계정이 없다 — ${plan.reasons.join(' · ')}${namedCodex ? ' · child provider 를 openai-codex 로 지정해 grok 으로 넘기지 않는다' : ''} · 계정을 명시하려면 --pod-account <이름>`); process.exit(2); }
4665
4827
  podProvider = plan.provider;
4666
4828
  if (plan.provider === 'openai-codex') {
4667
4829
  accountBroker = makePodAccountBroker({ usable: plan.accounts, excluded: plan.excluded });
@@ -4670,6 +4832,10 @@ const selfOrchestrateCmd = selfCmd
4670
4832
  { const { emitDecision } = await import('./live/detail-switch.js');
4671
4833
  emitDecision({ kind: 'ROUTE', what: `Pod 계정 배분 ${plan.accounts.join(' → ')}`, reason: plan.excluded.length ? `뺌 ${plan.excluded.map((x) => `${x.name}(${x.why})`).join(', ')}` : '모든 계정 여유', purpose: plan.creditAccounts ? '구독 잔량 0 — 정책상 크레딧으로 병렬 조각을 돌린다' : '병렬 조각이 한 계정에 몰리지 않게 잔량 순으로 돌려 준다', phase: 'dispatch', target: `Pod ${plan.accounts.length}계정`, paths: plan.accounts.length + plan.excluded.length }); }
4672
4834
  if (!opts.json) ui.info(`[pod] 계정 배분(잔량 순 · 돌려 가며): ${plan.accounts.join(' → ')}${plan.creditAccounts ? ' · 💳 크레딧(구독 잔량 0 · 허가됨)' : ''}${plan.excluded.length ? ` · 뺌 ${plan.excluded.map((x) => `${x.name}(${x.why})`).join(', ')}` : ''}`);
4835
+ } else if (namedChildDecision.provider === 'grok') {
4836
+ const model = (opts as { childLlmModel?: string }).childLlmModel?.trim() || defaultGrokModel().id;
4837
+ debug.log('self-implement.pod', 'provider-named', { provider: 'grok', model, credential: credential?.kind ?? null });
4838
+ if (!opts.json) ui.info(`[pod] child provider = grok(모델 ${model}) — 발사 인자로 지정됨`);
4673
4839
  } else {
4674
4840
  const model = defaultGrokModel().id;
4675
4841
  debug.log('self-implement.pod', 'provider-fallback', { from: 'openai-codex', to: 'grok', excluded: plan.excluded, model });
@@ -4697,7 +4863,10 @@ const selfOrchestrateCmd = selfCmd
4697
4863
  ...(podSource.kind === 'bundle' ? { headCommit: podSource.headCommit, sizeBytes: podSource.sizeBytes } : {}),
4698
4864
  });
4699
4865
  }
4700
- const podBase = { hostSupervised: opts.supervise === true, ...(remoteOnlyPool ? { imageCommit: image.imageCommit } : {}), account: podProvider === 'grok' ? 'grok' : explicitPodAccount ?? 'team', ...(accountBroker ? { accountBroker, rotationAccounts } : {}), ...(podProvider === 'grok' ? { provider: 'grok' as const, grokApiKeyOptIn } : {}), passEnv, ...(pool ? { pool } : {}), ...((opts as { podSkillEnv?: boolean }).podSkillEnv ? { skillEnv: true } : {}), ...(podSource ? { source: podSource } : {}) };
4866
+ const childFlags = opts as { childLlmProvider?: string; childLlmModel?: string; childLlmEffort?: string };
4867
+ const podChildLlm = childFlags.childLlmProvider?.trim() ? { childProviderExplicit: true, ...(childFlags.childLlmModel?.trim() ? { childModel: childFlags.childLlmModel.trim() } : {}), ...(childFlags.childLlmEffort?.trim() ? { childEffort: childFlags.childLlmEffort.trim() } : {}) } : {};
4868
+ if (childFlags.childLlmProvider?.trim()) debug.log('self-implement.pod', 'child-provider-named', { provider: childFlags.childLlmProvider.trim(), model: childFlags.childLlmModel ?? null, podProvider });
4869
+ const podBase = { hostSupervised: opts.supervise === true, ...podChildLlm, ...(remoteOnlyPool ? { imageCommit: image.imageCommit } : {}), account: podProvider === 'grok' ? 'grok' : explicitPodAccount ?? 'team', ...(accountBroker ? { accountBroker, rotationAccounts } : {}), ...(podProvider === 'grok' ? { provider: 'grok' as const, grokApiKeyOptIn } : {}), passEnv, ...(pool ? { pool } : {}), ...((opts as { podSkillEnv?: boolean }).podSkillEnv ? { skillEnv: true } : {}), ...(podSource ? { source: podSource } : {}) };
4701
4870
  if (benchArms) {
4702
4871
  const { benchPodSpawn } = await import('./task-orchestrator/surfaces/self-implement-pod.js');
4703
4872
  podSpawn = benchPodSpawn(benchArms, podBase);
@@ -5873,6 +6042,7 @@ agentCmd.hook('preAction', async (_thisCommand, actionCommand) => {
5873
6042
  });
5874
6043
 
5875
6044
  registerCapabilitiesCommand(agentCmd);
6045
+ registerSupervisorMetricsCommand(agentCmd);
5876
6046
 
5877
6047
  agentCmd.command('install-from-docs <tool>')
5878
6048
  .description('공식 설치 문서를 읽고 안전한 설치 줄을 선택한다 — sudo는 사람에게 넘긴다')
@@ -6744,6 +6914,11 @@ const selfDevCmd = program
6744
6914
  // ⭐ `--ask`/`--say` — 발사 절차 넷을 «한 로직»으로. 저작 → 전제 검사 → 발사.
6745
6915
  // ⛔ 여기서 저작한 골 파일을 아래 `--file` 자리에 그대로 넘긴다(새 실행 경로를 만들지 않는다).
6746
6916
  let askAuthoredFile: string | undefined;
6917
+ if (opts.ask !== undefined && !isOldDoorInternalCall('dev-ask')) {
6918
+ const refused = refuseOldDoor('dev-ask');
6919
+ debug.log('harness.entrance', 'old-door-refused', { door: refused.door, caller: refused.caller });
6920
+ throw new DevPipelineError(refused.message);
6921
+ }
6747
6922
  const retirementInput = opts.ask !== undefined
6748
6923
  ? { kind: 'ask' as const, value: opts.ask }
6749
6924
  : opts.say !== undefined
@@ -7468,6 +7643,7 @@ missionCmd
7468
7643
  });
7469
7644
 
7470
7645
  registerTasksCommands(program);
7646
+ registerBriefCommands(program);
7471
7647
  registerLabelsCommands(program);
7472
7648
  registerMsgCommands(program);
7473
7649