elanous 0.2.21 → 0.2.22

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 (315) 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-08b716476f1d50af.js +1 -0
  4. package/apps/pwa/out/_next/static/chunks/{2489-ff55a35b202c0f94.js → 2489-d238d3e2eb5b2bf5.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/3857-5985236d9307f676.js +1 -0
  6. package/apps/pwa/out/_next/static/chunks/5633-d6852385000738ef.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/5977-5b9e8a45a5b0c823.js +1 -0
  8. package/apps/pwa/out/_next/static/chunks/8857-aa1cc4ac90af4d40.js +1 -0
  9. package/apps/pwa/out/_next/static/chunks/9247-55bcfbb0e8057f59.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/approvals/page-88459e0903da2803.js +1 -0
  11. package/apps/pwa/out/_next/static/chunks/app/autopilot/{page-d781be3e3a9cfd74.js → page-d85c43f46f58716b.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/board/{page-b0a80997903852d8.js → page-4e645600579ae952.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-2b1263165630d9c8.js → page-87edbfd19117823b.js} +1 -1
  14. package/apps/pwa/out/_next/static/chunks/app/bots/page-7f0c2b7801ac6a54.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/ceo/page-bf1f030a9d1adffd.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/chat/{page-e2d6bed48ac20120.js → page-cbaf66b1e1804adf.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/app/consult/{page-111cabe4d1fd69ae.js → page-80ed511e02c81412.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/control/{page-544dd4bd4847eeb7.js → page-9f5610caaa06178c.js} +1 -1
  19. package/apps/pwa/out/_next/static/chunks/app/decisions/{page-3ee191319bf97d87.js → page-ec8f3575e339de74.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/design-check/page-86d753e460f1a989.js +1 -0
  21. package/apps/pwa/out/_next/static/chunks/app/editor/page-4166a7d51cf0a57d.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/exec/{page-a449f1ed472b81c2.js → page-2a19c5cd3b562580.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/field/{page-9045094d6d3b5cd0.js → page-b6a09cde2e07521a.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/inside/{page-c06673596849ef45.js → page-f7eae1518ca0c353.js} +1 -1
  25. package/apps/pwa/out/_next/static/chunks/app/intake/page-456a8573ad811381.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/{layout-9f81233f29dcd409.js → layout-8462d6a543cbb2d9.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/live/page-2974cdfb87c0f9c9.js +1 -0
  28. package/apps/pwa/out/_next/static/chunks/app/live-run/page-f927ffa25f3c86d1.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/loops/page-abd86cd832aaa011.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/market/page-79daf68f6256adaa.js +1 -0
  31. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-8c3f168fc3f91939.js +1 -0
  32. package/apps/pwa/out/_next/static/chunks/app/morning/{page-7a12f61be4a579d1.js → page-786fb4504d0f2fa9.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/{not-found-c85b0589591a99c9.js → not-found-75209f14597dcb8f.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/observatory/{page-725a4da02dd129eb.js → page-bf30affe3a85af7e.js} +1 -1
  35. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-1187a44b7515434b.js +1 -0
  36. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-3d0910185a66ed54.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-f401ee1dd5a26e25.js +1 -0
  38. package/apps/pwa/out/_next/static/chunks/app/outputs/page-5dfe7604fd842784.js +1 -0
  39. package/apps/pwa/out/_next/static/chunks/app/{page-21bec4afeb2f0917.js → page-76cf838050a8d681.js} +1 -1
  40. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-dc39ee7a36d26385.js → page-7ec8e289b4c14df6.js} +1 -1
  41. package/apps/pwa/out/_next/static/chunks/app/scheduler/{page-84ae759f76053672.js → page-8c8ac17d52be2a79.js} +1 -1
  42. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-3db58ddb61fe2be9.js → page-d359ab048d63a9d9.js} +1 -1
  43. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-c5315e30f3528dc4.js → page-b8b345c880bf78d1.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/settings/{page-2fe368efb6de3b6a.js → page-f151cc10ad026940.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-9875f103057da433.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/setup/page-9789328af9a9aa62.js +1 -0
  47. package/apps/pwa/out/_next/static/chunks/app/share/{page-c69762987b751174.js → page-bf495a1e284f4e00.js} +1 -1
  48. package/apps/pwa/out/_next/static/chunks/app/showroom/page-a263d94bca4be22a.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/tasks/page-3962eab8ec58d16e.js +1 -0
  50. package/apps/pwa/out/_next/static/chunks/app/term/page-9abcfac07e02a542.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/today/page-bee49f927c333abb.js +1 -0
  52. package/apps/pwa/out/_next/static/chunks/app/trace/page-bac2d56dcb02d955.js +1 -0
  53. package/apps/pwa/out/_next/static/chunks/app/vault/page-937cbcc13176295c.js +1 -0
  54. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-d09495594f7f480d.js +1 -0
  55. package/apps/pwa/out/_next/static/chunks/app/workflows/{page-7f8d7fcdf058a845.js → page-69cb93d1a2e57e0e.js} +1 -1
  56. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-22163556cd38bc88.js → page-72ec543928d67c3e.js} +1 -1
  57. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-82906eeda0186a7b.js +1 -0
  58. package/apps/pwa/out/_next/static/css/f2bd5257e6af88d8.css +1 -0
  59. package/apps/pwa/out/approvals/index.html +1 -1
  60. package/apps/pwa/out/approvals/index.txt +14 -14
  61. package/apps/pwa/out/autopilot/index.html +1 -1
  62. package/apps/pwa/out/autopilot/index.txt +14 -14
  63. package/apps/pwa/out/board/index.html +1 -1
  64. package/apps/pwa/out/board/index.txt +14 -14
  65. package/apps/pwa/out/botlab/index.html +1 -1
  66. package/apps/pwa/out/botlab/index.txt +14 -14
  67. package/apps/pwa/out/bots/index.html +1 -1
  68. package/apps/pwa/out/bots/index.txt +14 -14
  69. package/apps/pwa/out/ceo/index.html +1 -1
  70. package/apps/pwa/out/ceo/index.txt +14 -14
  71. package/apps/pwa/out/chat/index.html +1 -1
  72. package/apps/pwa/out/chat/index.txt +14 -14
  73. package/apps/pwa/out/consult/index.html +1 -1
  74. package/apps/pwa/out/consult/index.txt +14 -14
  75. package/apps/pwa/out/control/index.html +1 -1
  76. package/apps/pwa/out/control/index.txt +14 -14
  77. package/apps/pwa/out/decisions/index.html +1 -1
  78. package/apps/pwa/out/decisions/index.txt +14 -14
  79. package/apps/pwa/out/design-check/index.html +1 -1
  80. package/apps/pwa/out/design-check/index.txt +14 -14
  81. package/apps/pwa/out/editor/index.html +1 -1
  82. package/apps/pwa/out/editor/index.txt +14 -14
  83. package/apps/pwa/out/exec/index.html +1 -1
  84. package/apps/pwa/out/exec/index.txt +14 -14
  85. package/apps/pwa/out/field/index.html +1 -1
  86. package/apps/pwa/out/field/index.txt +14 -14
  87. package/apps/pwa/out/healthz +1 -1
  88. package/apps/pwa/out/index.html +1 -1
  89. package/apps/pwa/out/index.txt +14 -14
  90. package/apps/pwa/out/inside/index.html +1 -1
  91. package/apps/pwa/out/inside/index.txt +14 -14
  92. package/apps/pwa/out/intake/index.html +1 -1
  93. package/apps/pwa/out/intake/index.txt +14 -14
  94. package/apps/pwa/out/live/index.html +1 -1
  95. package/apps/pwa/out/live/index.txt +14 -14
  96. package/apps/pwa/out/live-run/index.html +1 -1
  97. package/apps/pwa/out/live-run/index.txt +14 -14
  98. package/apps/pwa/out/loops/index.html +1 -1
  99. package/apps/pwa/out/loops/index.txt +14 -14
  100. package/apps/pwa/out/market/index.html +1 -1
  101. package/apps/pwa/out/market/index.txt +14 -14
  102. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  103. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  104. package/apps/pwa/out/morning/index.html +1 -1
  105. package/apps/pwa/out/morning/index.txt +14 -14
  106. package/apps/pwa/out/observatory/index.html +1 -1
  107. package/apps/pwa/out/observatory/index.txt +14 -14
  108. package/apps/pwa/out/ops/checklist/index.html +1 -1
  109. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  110. package/apps/pwa/out/ops/release/index.html +1 -1
  111. package/apps/pwa/out/ops/release/index.txt +14 -14
  112. package/apps/pwa/out/ops/seats/index.html +1 -1
  113. package/apps/pwa/out/ops/seats/index.txt +14 -14
  114. package/apps/pwa/out/outputs/index.html +1 -1
  115. package/apps/pwa/out/outputs/index.txt +14 -14
  116. package/apps/pwa/out/reflection/index.html +1 -1
  117. package/apps/pwa/out/reflection/index.txt +15 -15
  118. package/apps/pwa/out/scheduler/index.html +1 -1
  119. package/apps/pwa/out/scheduler/index.txt +14 -14
  120. package/apps/pwa/out/sessions/index.html +1 -1
  121. package/apps/pwa/out/sessions/index.txt +14 -14
  122. package/apps/pwa/out/settings/devices/index.html +1 -1
  123. package/apps/pwa/out/settings/devices/index.txt +14 -14
  124. package/apps/pwa/out/settings/index.html +1 -1
  125. package/apps/pwa/out/settings/index.txt +14 -14
  126. package/apps/pwa/out/setup/done/index.html +1 -1
  127. package/apps/pwa/out/setup/done/index.txt +14 -14
  128. package/apps/pwa/out/setup/index.html +1 -1
  129. package/apps/pwa/out/setup/index.txt +14 -14
  130. package/apps/pwa/out/share/index.html +1 -1
  131. package/apps/pwa/out/share/index.txt +14 -14
  132. package/apps/pwa/out/showroom/index.html +1 -1
  133. package/apps/pwa/out/showroom/index.txt +14 -14
  134. package/apps/pwa/out/tasks/index.html +1 -1
  135. package/apps/pwa/out/tasks/index.txt +14 -14
  136. package/apps/pwa/out/term/index.html +1 -1
  137. package/apps/pwa/out/term/index.txt +14 -14
  138. package/apps/pwa/out/today/index.html +1 -1
  139. package/apps/pwa/out/today/index.txt +14 -14
  140. package/apps/pwa/out/trace/index.html +1 -1
  141. package/apps/pwa/out/trace/index.txt +14 -14
  142. package/apps/pwa/out/vault/index.html +1 -1
  143. package/apps/pwa/out/vault/index.txt +14 -14
  144. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  145. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  146. package/apps/pwa/out/workflows/index.html +1 -1
  147. package/apps/pwa/out/workflows/index.txt +14 -14
  148. package/apps/pwa/out/workspace/index.html +1 -1
  149. package/apps/pwa/out/workspace/index.txt +14 -14
  150. package/apps/pwa/out/worktrees/index.html +1 -1
  151. package/apps/pwa/out/worktrees/index.txt +14 -14
  152. package/graphs/catalog/catalog.yaml +1 -1
  153. package/graphs/drafts/recipes.yaml +2 -2
  154. package/graphs/test-diet/recipes.yaml +3 -0
  155. package/graphs/test-diet/test-diet.yaml +3 -1
  156. package/package.json +1 -1
  157. package/scripts/f-session-monitor.ts +69 -0
  158. package/scripts/hq/standby-snapshot.ts +7 -3
  159. package/scripts/knowledge-compress.ts +11 -0
  160. package/scripts/nightly-docops.ts +90 -3
  161. package/scripts/research-coo-admin-dates.ts +39 -0
  162. package/scripts/skills/public-check.ts +7 -2
  163. package/scripts/smart-merge/resync-shadow.ts +5 -3
  164. package/scripts/test-diet/lib.ts +81 -1
  165. package/scripts/test-diet/node.ts +48 -2
  166. package/scripts/verify-coo-admin-evidence.ts +83 -0
  167. package/src/acp/client.ts +4 -2
  168. package/src/agent/run-context.ts +4 -0
  169. package/src/agent-env/profile.ts +25 -5
  170. package/src/agent-mission/driver.ts +26 -12
  171. package/src/agent-mission/supervisor-metrics.ts +17 -2
  172. package/src/agent-substrate/acp-reviewer.ts +12 -5
  173. package/src/agent-substrate/pr-reviewer.ts +11 -4
  174. package/src/agent-substrate/review-intent.ts +102 -3
  175. package/src/autopilot/build/llm-conflict-merge.ts +22 -8
  176. package/src/autopilot/pr-manager.ts +18 -8
  177. package/src/away/release-watch.ts +47 -8
  178. package/src/briefing/brief-items.ts +104 -11
  179. package/src/chat/index.ts +1 -0
  180. package/src/cli/brief-cli.ts +20 -2
  181. package/src/cli/config-classify.ts +83 -0
  182. package/src/cli/control-cli.ts +14 -0
  183. package/src/cli/decisions-cli.ts +26 -16
  184. package/src/cli/logs-model-input.ts +1 -1
  185. package/src/cli/pr-cli.ts +65 -8
  186. package/src/cli/release-cli.ts +21 -4
  187. package/src/cli/tasks-cli.ts +108 -1
  188. package/src/connectors/types.ts +6 -6
  189. package/src/context-bus/context-now-surfaces.ts +18 -0
  190. package/src/context-bus/coord-events.ts +32 -4
  191. package/src/context-bus/long-term-memory.ts +23 -6
  192. package/src/context-bus/nightly-condense.ts +169 -21
  193. package/src/control/control-status.ts +87 -0
  194. package/src/control-plane/light-member.ts +5 -3
  195. package/src/control-plane/member.ts +172 -7
  196. package/src/core-turn/run-goal-loop.ts +22 -1
  197. package/src/dashboard/slash-runtime/dashboard-handlers.ts +9 -0
  198. package/src/decisions/decision-cards.ts +20 -1
  199. package/src/decisions/decision-ledger.ts +11 -8
  200. package/src/decisions/web-push-decision-cards.ts +19 -3
  201. package/src/discord-self-message.ts +2 -2
  202. package/src/domains/coo-admin-dates.evidence.json +876 -0
  203. package/src/domains/coo-admin-tool.ts +48 -14
  204. package/src/domains/ops-status.ts +53 -0
  205. package/src/domains/outbound-alert.ts +24 -1
  206. package/src/exec-requests/planner.ts +6 -0
  207. package/src/graph-kinds/registry.ts +6 -6
  208. package/src/graph-runner/graph-cli.ts +34 -2
  209. package/src/graph-runner/graph-run-api.ts +7 -0
  210. package/src/graph-runner/graph-tick.ts +73 -12
  211. package/src/graph-runner/runner.ts +43 -11
  212. package/src/graph-unify/wf-to-graph.ts +79 -0
  213. package/src/graph-wizard/generate.ts +34 -2
  214. package/src/graph-wizard/steps.ts +13 -0
  215. package/src/harness/harness-cli-command.ts +121 -23
  216. package/src/harness/harness-queue.ts +58 -14
  217. package/src/harness/launch-advice.ts +23 -0
  218. package/src/index.ts +36 -7
  219. package/src/knowledge/conclusion-compress.ts +93 -0
  220. package/src/knowledge/know-find.ts +9 -2
  221. package/src/knowledge/query.ts +20 -0
  222. package/src/llm.ts +26 -16
  223. package/src/loops/neighbor-runtime.ts +88 -0
  224. package/src/loops/orchestrator/author-depth.ts +29 -11
  225. package/src/loops/orchestrator/overlap.ts +7 -1
  226. package/src/loops/orchestrator/resync-shadow.ts +1 -1
  227. package/src/loops/orchestrator/tick.ts +35 -2
  228. package/src/loops/orchestrator/traffic.ts +2 -0
  229. package/src/loops/registry.ts +5 -0
  230. package/src/market/publish.ts +17 -1
  231. package/src/maturity/feature-maturity.ts +2 -1
  232. package/src/mss/logging/redaction.ts +17 -0
  233. package/src/nexus/api/draft-metrics.ts +22 -8
  234. package/src/nexus/api/graph-peer-edit.ts +77 -0
  235. package/src/nexus/api/graph-wizard-api.ts +9 -1
  236. package/src/nexus/api/graphs-api.ts +145 -7
  237. package/src/nexus/api/http-server.ts +93 -8
  238. package/src/nexus/api/plugins-market.ts +24 -0
  239. package/src/nexus/index.ts +3 -2
  240. package/src/oauth/claude-pty-maturity.ts +24 -0
  241. package/src/oauth/fallback-chain.ts +27 -14
  242. package/src/plugins/maker/market-bundle.ts +9 -0
  243. package/src/plugins/maker/plugin-maker.ts +9 -3
  244. package/src/plugins/maker/wizard-generate.ts +73 -9
  245. package/src/policy/llm-policy.ts +11 -6
  246. package/src/release-loop/checklist-board.ts +27 -0
  247. package/src/release-loop/checklist.ts +38 -0
  248. package/src/seat-address/seat-address.ts +1 -0
  249. package/src/seat-loop/seat-loop.ts +174 -20
  250. package/src/seat-loop/seat-report.ts +29 -1
  251. package/src/self-dev/dev-cli.ts +90 -8
  252. package/src/self-dev/dev-pipeline.ts +20 -1
  253. package/src/self-dev/draft-sweep.ts +45 -8
  254. package/src/self-dev/orchestrate.ts +3 -0
  255. package/src/self-dev/review-provider-fallback.ts +83 -0
  256. package/src/self-dev/run-supervisor.ts +5 -1
  257. package/src/self-implement/harness-policy-gates.ts +7 -5
  258. package/src/self-implement/host-regate.ts +28 -14
  259. package/src/self-implement/l8-merge-queue.ts +80 -8
  260. package/src/self-implement/orchestrator.ts +68 -27
  261. package/src/self-implement/seams.ts +5 -3
  262. package/src/self-implement/self-implement-cli.ts +4 -0
  263. package/src/shell-env-bootstrap.ts +19 -4
  264. package/src/steward/triage.ts +25 -8
  265. package/src/storage/file-lock.ts +33 -4
  266. package/src/task-agent/completion-evidence.ts +31 -3
  267. package/src/task-agent/cover.ts +61 -0
  268. package/src/task-agent/live-moves.ts +126 -3
  269. package/src/task-agent/shadow.ts +19 -4
  270. package/src/task-agent/task-hand.ts +2 -0
  271. package/src/task-orchestrator/surfaces/pod-pool.ts +138 -15
  272. package/src/task-orchestrator/surfaces/self-implement.ts +7 -0
  273. package/src/telegram-commands.ts +8 -0
  274. package/src/telegram-project-command.ts +6 -0
  275. package/src/telegram.ts +2 -2
  276. package/src/typecheck-ratchet.ts +2 -1
  277. package/src/user-config.ts +35 -4
  278. package/src/version/packed-revision.json +1 -1
  279. package/src/workflow-runtime/executor.ts +24 -2
  280. package/src/workflow-runtime/triggers/registry.ts +27 -0
  281. package/src/workflow-runtime/triggers/schedule-source.ts +30 -3
  282. package/apps/pwa/out/_next/static/chunks/1307-c97eba9fb72a9a5e.js +0 -1
  283. package/apps/pwa/out/_next/static/chunks/3857-995ea9f5ef33d71e.js +0 -1
  284. package/apps/pwa/out/_next/static/chunks/5977-59fedea50d13c762.js +0 -1
  285. package/apps/pwa/out/_next/static/chunks/8857-f1762140ef9a96b1.js +0 -1
  286. package/apps/pwa/out/_next/static/chunks/9247-7581825d511a64c0.js +0 -1
  287. package/apps/pwa/out/_next/static/chunks/9497-b0e758d1cd4ae8d7.js +0 -1
  288. package/apps/pwa/out/_next/static/chunks/app/approvals/page-937c92292a4ab4f0.js +0 -1
  289. package/apps/pwa/out/_next/static/chunks/app/bots/page-ea998f7f5d156bea.js +0 -1
  290. package/apps/pwa/out/_next/static/chunks/app/ceo/page-33c328b64ca508f8.js +0 -1
  291. package/apps/pwa/out/_next/static/chunks/app/design-check/page-e4bcb2c503dab04b.js +0 -1
  292. package/apps/pwa/out/_next/static/chunks/app/editor/page-300897dcbbd5ed76.js +0 -1
  293. package/apps/pwa/out/_next/static/chunks/app/intake/page-103fa3191c1b2e08.js +0 -1
  294. package/apps/pwa/out/_next/static/chunks/app/live/page-be8b4615c919e36c.js +0 -1
  295. package/apps/pwa/out/_next/static/chunks/app/live-run/page-c685b104e0dfa609.js +0 -1
  296. package/apps/pwa/out/_next/static/chunks/app/loops/page-8d693331cfbb6add.js +0 -1
  297. package/apps/pwa/out/_next/static/chunks/app/market/page-cef4f30a4c22fae1.js +0 -1
  298. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/page-66ea1b54fc83fa15.js +0 -1
  299. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/page-8cc707bd09a497d9.js +0 -1
  300. package/apps/pwa/out/_next/static/chunks/app/ops/release/page-90b55be1f15ce0e8.js +0 -1
  301. package/apps/pwa/out/_next/static/chunks/app/ops/seats/page-cb16606e08be65db.js +0 -1
  302. package/apps/pwa/out/_next/static/chunks/app/outputs/page-16dc64b74ed6a40a.js +0 -1
  303. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-8d04132dd0ac5e10.js +0 -1
  304. package/apps/pwa/out/_next/static/chunks/app/setup/page-f23484de467c9ea3.js +0 -1
  305. package/apps/pwa/out/_next/static/chunks/app/showroom/page-5456692bda39aafb.js +0 -1
  306. package/apps/pwa/out/_next/static/chunks/app/tasks/page-d75f7cd65845ce98.js +0 -1
  307. package/apps/pwa/out/_next/static/chunks/app/term/page-5603f5e568b25ce3.js +0 -1
  308. package/apps/pwa/out/_next/static/chunks/app/today/page-d2cc0f4e89f42028.js +0 -1
  309. package/apps/pwa/out/_next/static/chunks/app/trace/page-a6eb73b8a4840f1c.js +0 -1
  310. package/apps/pwa/out/_next/static/chunks/app/vault/page-c0a3f4d10edb83fa.js +0 -1
  311. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/page-6c0b88068c143f15.js +0 -1
  312. package/apps/pwa/out/_next/static/chunks/app/worktrees/page-141436fd26dd56fc.js +0 -1
  313. package/apps/pwa/out/_next/static/css/e81df11490b4413b.css +0 -1
  314. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_buildManifest.js +0 -0
  315. /package/apps/pwa/out/_next/static/{MObU8ujkMciAKr6ljAV05 → aBi6EyMtLsrl7J8eH4kF7}/_ssgManifest.js +0 -0
@@ -4,6 +4,7 @@ import { appendFileSync, closeSync, existsSync, mkdirSync, openSync, readdirSync
4
4
  import { join, resolve } from 'node:path';
5
5
  import { Database } from 'bun:sqlite';
6
6
  import { debug } from '../debug/log.js';
7
+ import { stateDirSourceForChild } from '../agent/identity-env.js';
7
8
  import { findTreeRoot } from '../cli/test-flag.js';
8
9
  import { readLogInstances, type LogInstanceView } from '../mss/logging/instance-registry.js';
9
10
  import { effectiveInstanceRoot, prodInstanceRoot, releaseLedgerRoot } from '../instance/resolve.js';
@@ -20,6 +21,7 @@ import { hostLeaseCounts, leaseHasPendingPod } from '../pod-lease/host-lease.js'
20
21
  import { leaseKubectl, measurePoolLease } from '../task-orchestrator/surfaces/pod-lease.js';
21
22
  import { parsePodPool, podPoolHostLease, resolvePodPoolSpec } from '../task-orchestrator/surfaces/pod-pool.js';
22
23
  import { writeHarnessQueueReceipt } from './harness-queue-child.js';
24
+ import { adviseLaunch, compareLaunchAdvice, type LaunchAdvice } from './launch-advice.js';
23
25
  import { finishAdvice, measureFinish, recordFinishHistory, type FinishMetrics } from '../loops/orchestrator/finish-rate.js';
24
26
 
25
27
  export type QueueSeat = 'OP' | 'TC' | 'MK' | 'UX';
@@ -73,6 +75,8 @@ export interface HarnessQueueDeps {
73
75
  configPath?: string;
74
76
  cap?: (seat: QueueSeat) => number;
75
77
  pool?: () => QueuePool;
78
+ /** Shadow-only advice seam; exceptions must not affect the queue decision. */
79
+ advice?: typeof adviseLaunch;
76
80
  launch?: (item: QueueItem, args: string[], root: string) => Promise<number>;
77
81
  /** Test seam: runs after a direct CLI call enqueued its row and before that call's own tick (race tests). */
78
82
  afterEnqueue?: (item: QueueItem) => void | Promise<void>;
@@ -385,24 +389,25 @@ export function listHarnessQueue(deps: HarnessQueueDeps = {}): QueueItem[] {
385
389
  return read(harnessQueuePath(deps.root ?? effectiveInstanceRoot()));
386
390
  }
387
391
 
388
- /** A missing row is not proof of failure unless the queue left a cancellation marker. */
389
- export function harnessQueueOutcome(id: string, deps: HarnessQueueDeps = {}): 'pending' | 'succeeded' | 'retryable' | 'unknown' {
392
+ /** A missing row is not proof of failure; explicit removal and terminal receipts have distinct markers. */
393
+ export function harnessQueueOutcome(id: string, deps: HarnessQueueDeps = {}): 'pending' | 'succeeded' | 'retryable' | 'removed' | 'unknown' {
390
394
  const root = deps.root ?? effectiveInstanceRoot();
391
395
  const row = listHarnessQueue(deps).find((item) => item.id === id);
392
396
  if (!row) {
393
397
  const marker = join(root, 'harness', `${id}.outcome`);
394
398
  if (!existsSync(marker)) return 'unknown';
395
399
  const value = readFileSync(marker, 'utf8');
396
- return value === 'succeeded' || value === 'retryable' ? value : 'unknown';
400
+ return value === 'succeeded' || value === 'retryable' || value === 'removed' ? value : 'unknown';
397
401
  }
398
402
  return queueRowOutcome(row, root);
399
403
  }
400
404
 
401
405
  const keyMarkerPath = (root: string, key: string) => join(root, 'harness', 'by-key', `${createHash('sha256').update(key).digest('hex')}.id`);
402
406
 
403
- function recordQueueOutcome(root: string, id: string, outcome: 'succeeded' | 'retryable', key?: string): void {
407
+ function recordQueueOutcome(root: string, id: string, outcome: 'succeeded' | 'retryable' | 'removed', key?: string): void {
404
408
  const path = join(root, 'harness', `${id}.outcome`);
405
- if (!existsSync(path)) writeFileSync(path, outcome, { flag: 'wx', mode: 0o600 });
409
+ if (outcome === 'removed') writeFileSync(path, outcome, { mode: 0o600 });
410
+ else if (!existsSync(path)) writeFileSync(path, outcome, { flag: 'wx', mode: 0o600 });
406
411
  // The row is about to leave the queue; keep «which id last held this key» so a caller that crashed before
407
412
  // recording the id can still find the outcome (LOOP-LIVE1 · seat loop recovery).
408
413
  if (key) {
@@ -491,10 +496,8 @@ export async function removeHarnessQueue(id: string, deps: HarnessQueueDeps = {}
491
496
  if (state !== 'finished' && state !== 'not-started'
492
497
  || (deps.processes ?? readHarnessQueueProcesses)().some((row) => row.launchId === item.launchId)) return false;
493
498
  }
494
- const outcome = item.status === 'queued' ? 'retryable' : queueRowOutcome(item, root);
495
- if (item.idempotencyKey && (outcome === 'retryable' || outcome === 'succeeded')) {
496
- recordQueueOutcome(root, id, outcome, item.idempotencyKey);
497
- }
499
+ const outcome = queueRowOutcome(item, root) === 'succeeded' ? 'succeeded' : 'removed';
500
+ if (item.idempotencyKey) recordQueueOutcome(root, id, outcome, item.idempotencyKey);
498
501
  save(path, rows.filter((row) => row.id !== id));
499
502
  observe('skipped', { id, seat: item.seat, reason: 'removed' }, deps);
500
503
  return true;
@@ -699,6 +702,26 @@ export function readHarnessQueueInventory(probe: ProcessProbe = {}): QueueInvent
699
702
  if (prior && prior !== record.seat) { ledgerSeats.delete(pid); ambiguousPids.add(pid); }
700
703
  else ledgerSeats.set(pid, record.seat!);
701
704
  }
705
+ // One lsof for every candidate: a per-pid lsof (~0.1–0.3 s each) made a tick on a busy host take 10 s+ (10-08). lsof exits 1
706
+ // when some pid is gone yet still prints the others; a pid it does not print stays unattributed (charged to all seats).
707
+ let batchCwds: Map<number, string> | undefined;
708
+ const readBatchCwds = (): Map<number, string> => {
709
+ const cwds = new Map<number, string>();
710
+ const started = Date.now();
711
+ const lsof = run('lsof', ['-a', '-d', 'cwd', '-Fpn', '-p', candidates.map(({ pid }) => pid).join(',')],
712
+ { encoding: 'utf8', timeout: 5_000, maxBuffer: 16 * 1024 * 1024 });
713
+ if (!lsof.error && typeof lsof.stdout === 'string') {
714
+ let current: number | undefined;
715
+ for (const line of lsof.stdout.split('\n')) {
716
+ if (line.startsWith('p')) current = Number(line.slice(1));
717
+ else if (line.startsWith('n/') && current !== undefined) cwds.set(current, line.slice(1));
718
+ }
719
+ }
720
+ try { debug.log('harness.queue', 'cwd-batch', { pids: candidates.length, read: cwds.size, ms: Date.now() - started,
721
+ ...(lsof.error ? { error: String(lsof.error).slice(0, 240) } : {}) }); }
722
+ catch { /* Observation cannot affect dispatch. */ }
723
+ return cwds;
724
+ };
702
725
  const rows: QueueProcess[] = [];
703
726
  // Attribute a run after grouping: the orchestrator may switch working trees while its launcher stays in HQ.
704
727
  for (const root of candidates.filter((row) => !byPid.has(row.ppid))) {
@@ -736,9 +759,9 @@ export function readHarnessQueueInventory(probe: ProcessProbe = {}): QueueInvent
736
759
  if (probe.cwd) cwd = probe.cwd(pid);
737
760
  else if (platform === 'linux') cwd = readlinkSync(`/proc/${pid}/cwd`);
738
761
  else {
739
- const lsof = run('lsof', ['-a', '-p', String(pid), '-d', 'cwd', '-Fn'], { encoding: 'utf8', timeout: 5_000 });
740
- if (lsof.error || lsof.status !== 0 || typeof lsof.stdout !== 'string') throw new Error('cwd unavailable');
741
- cwd = lsof.stdout.split('\n').find((line) => line.startsWith('n/'))?.slice(1) ?? '';
762
+ const found = (batchCwds ??= readBatchCwds()).get(pid);
763
+ if (found === undefined) throw new Error('cwd unavailable');
764
+ cwd = found;
742
765
  }
743
766
  if (!cwd.startsWith('/')) continue;
744
767
  const found = queueSeatForCwd(cwd, trees);
@@ -816,7 +839,8 @@ async function launch(item: QueueItem, args: string[], root: string, command?: s
816
839
  harnessQueueReceiptPath(root, item.launchId!), command ?? join(repo, 'bin', 'elanous.mjs'),
817
840
  ...(resolve(root) === prodInstanceRoot() ? [] : [`--test=${root}`]), ...args], {
818
841
  cwd: item.launchCwd ?? repo, detached: true, stdio: ['ignore', fd, fd],
819
- env: { ...process.env, ELANOUS_STATE_DIR: root, ELANOUS_HARNESS_SEAT: item.seat,
842
+ env: { ...process.env, ELANOUS_STATE_DIR: root,
843
+ ELANOUS_STATE_DIR_SOURCE: stateDirSourceForChild(root), ELANOUS_HARNESS_SEAT: item.seat,
820
844
  ELANOUS_HARNESS_QUEUE_LAUNCH: item.launchId! },
821
845
  });
822
846
  const pid = await new Promise<number>((done, reject) => {
@@ -1187,7 +1211,13 @@ export async function tickHarnessQueue(deps: HarnessQueueDeps = {}, requestedId?
1187
1211
 
1188
1212
  async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string | undefined, followUp: boolean): Promise<QueueTick> {
1189
1213
  const root = deps.root ?? effectiveInstanceRoot();
1190
- return locked(root, async (path) => {
1214
+ const caller = 'harness-queue-tick' as const;
1215
+ let advice: LaunchAdvice = { verdict: 'unknown', why: 'pool not measured before queue decision' };
1216
+ const shadow = (pool: QueuePool | undefined): void => {
1217
+ try { advice = (deps.advice ?? adviseLaunch)({ pool, caller }); }
1218
+ catch { advice = { verdict: 'unknown', why: 'launch advice unavailable' }; }
1219
+ };
1220
+ const actual = await locked<QueueTick>(root, async (path): Promise<QueueTick> => {
1191
1221
  // A burst follow-up already ran the author shadow and the idle request in this tick's first pass.
1192
1222
  if (!followUp) try {
1193
1223
  const now = (deps.now ?? (() => new Date()))();
@@ -1196,7 +1226,11 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
1196
1226
  debug.log('loops.author-depth', 'shadow-skipped-test', {});
1197
1227
  } else {
1198
1228
  const shadow = deps.authorShadow ?? (await import('../loops/orchestrator/author-depth.js')).runAuthorDepthShadow;
1229
+ const started = Date.now();
1199
1230
  await shadow(root, now);
1231
+ // The shadow runs inside the queue lock; its duration is what other ticks wait on.
1232
+ try { debug.log('harness.queue', 'author-shadow', { ms: Date.now() - started }); }
1233
+ catch { /* Observation cannot affect dispatch. */ }
1200
1234
  }
1201
1235
  } catch (error) {
1202
1236
  try { debug.log('loops.author-depth', 'shadow-failed', { reason: String(error) }); }
@@ -1463,6 +1497,7 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
1463
1497
  if (pool && (![pool.running, pool.pending, pool.reserved, pool.limit].every((n) => Number.isSafeInteger(n) && n >= 0) || pool.limit < 1)) {
1464
1498
  poolReason = 'pod lease status incomplete';
1465
1499
  }
1500
+ shadow(poolReason ? undefined : pool);
1466
1501
  if (pool && !poolReason) {
1467
1502
  const queueLaunching = current.filter((row) => row.status === 'launching' || row.status === 'launched').length;
1468
1503
  // CHILD-UNIV-HOLD: each unreadable child universe is charged one pool slot (conservative), never a seat slot.
@@ -1545,4 +1580,13 @@ async function tickHarnessQueueOnce(deps: HarnessQueueDeps, requestedId: string
1545
1580
  }
1546
1581
  return waiting();
1547
1582
  });
1583
+ // An empty queue tick has nothing to advise on; logging it would flood the advice-unknown sample.
1584
+ if (actual.outcome === 'skipped' && !actual.item) return actual;
1585
+ try {
1586
+ const { kind } = compareLaunchAdvice(advice, actual.outcome);
1587
+ debug.log('resource.advice', 'launch-advice', {
1588
+ caller, verdict: advice.verdict, why: advice.why, actual: actual.outcome, kind, itemId: actual.item?.id ?? null,
1589
+ });
1590
+ } catch { /* Shadow observation cannot affect the queue result. */ }
1591
+ return actual;
1548
1592
  }
@@ -0,0 +1,23 @@
1
+ import type { QueuePool, QueueTick } from './harness-queue.js';
2
+
3
+ export type LaunchAdvice = { verdict: 'launch-now' | 'wait' | 'unknown'; why: string; retryAfterHint?: string };
4
+ export type LaunchAdviceInput = { pool: QueuePool | null | undefined; caller: 'harness-queue-tick' | 'hand-task' | 'release-run' };
5
+
6
+ /** A pool-only suggestion; queue, seat, freeze and finish gates remain the dispatcher's decisions. */
7
+ export function adviseLaunch(input: LaunchAdviceInput): LaunchAdvice {
8
+ const pool = input.pool;
9
+ if (!pool || ![pool.running, pool.pending, pool.reserved, pool.limit].every(n => Number.isSafeInteger(n) && n >= 0)
10
+ || pool.limit < 1) return { verdict: 'unknown', why: 'pool unavailable or incomplete' };
11
+ const used = pool.running + pool.pending + pool.reserved;
12
+ if (used >= pool.limit) return { verdict: 'wait', why: `pool ${used}/${pool.limit} occupied`, retryAfterHint: 'when a pool slot is released' };
13
+ return { verdict: 'launch-now', why: `pool ${used}/${pool.limit} occupied` };
14
+ }
15
+
16
+ export function compareLaunchAdvice(advice: LaunchAdvice, actualOutcome: QueueTick['outcome']): {
17
+ agree: boolean; kind: 'advice-launch-actual-wait' | 'advice-wait-actual-launch' | 'advice-unknown' | 'agree';
18
+ } {
19
+ if (advice.verdict === 'unknown' || actualOutcome === 'skipped') return { agree: false, kind: 'advice-unknown' };
20
+ if (advice.verdict === 'launch-now' && actualOutcome === 'waiting') return { agree: false, kind: 'advice-launch-actual-wait' };
21
+ if (advice.verdict === 'wait' && actualOutcome === 'launched') return { agree: false, kind: 'advice-wait-actual-launch' };
22
+ return { agree: true, kind: 'agree' };
23
+ }
package/src/index.ts CHANGED
@@ -613,9 +613,10 @@ function buildHarnessAskSayDevCliSpec(
613
613
  input: { file: string },
614
614
  cliOpts: ReturnType<typeof harnessAskSayOptionsToDevCliOpts>,
615
615
  entranceId: EntranceId,
616
+ runId: string,
616
617
  ) {
617
618
  return {
618
- ...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId),
619
+ ...buildDevCliSpec(input, { kind: 'self' }, cliOpts, Object.keys(cliOpts), entranceId, undefined, undefined, runId),
619
620
  ...(cliOpts.json ? { humanReadableOutput: false } : {}),
620
621
  };
621
622
  }
@@ -661,6 +662,7 @@ async function runDevAskFromAskFile(askPath: string, opts: HarnessAskSayChildLlm
661
662
  { file: flowResult.goalFile },
662
663
  cliOpts,
663
664
  CLI_HARNESS_ASK_ENTRANCE.id,
665
+ identity.runId,
664
666
  ),
665
667
  ...(pieceFeature === undefined ? {} : { input: { text: pieceFeature } }),
666
668
  ...(base === undefined ? {} : { base }),
@@ -765,6 +767,7 @@ async function runDevAskFromGoalFile(goalPath: string, opts: HarnessAskSayChildL
765
767
  { file: goalPath },
766
768
  cliOpts,
767
769
  CLI_HARNESS_ASK_ENTRANCE.id,
770
+ identity.runId,
768
771
  ),
769
772
  runId: identity.runId,
770
773
  runIdSource: identity.source,
@@ -868,6 +871,7 @@ async function runDevSayFromWords(
868
871
  { file: flowResult.goalFile },
869
872
  cliOpts,
870
873
  entrance,
874
+ identity.runId,
871
875
  ),
872
876
  runId: identity.runId,
873
877
  runIdSource: identity.source,
@@ -1271,9 +1275,10 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
1271
1275
  .option('--source-sha <sha>', 'Git source commit')
1272
1276
  .option('--source-base <path>', 'Git source plugin base directory')
1273
1277
  .option('--bundle-root <dir>', 'Root that extensions["ai.elanous"].bundle paths resolve against (usually the repository root)')
1278
+ .option('--keep-existing', 'Carry over previous index entries whose name has no folder under --dir (unchanged entry and archive)')
1274
1279
  .option('--json', 'JSON result')
1275
1280
  .action(async (opts: { dir: string; out: string; key: string; keyId: string; marketName: string; displayName: string;
1276
- sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; json?: boolean }) => {
1281
+ sourceRepo?: string; sourceSha?: string; sourceBase?: string; bundleRoot?: string; keepExisting?: boolean; json?: boolean }) => {
1277
1282
  try {
1278
1283
  const sourceOptions = [opts.sourceRepo, opts.sourceSha, opts.sourceBase];
1279
1284
  if (sourceOptions.some(Boolean) && !sourceOptions.every(Boolean)) throw new Error('source-repo, source-sha and source-base must be provided together');
@@ -1282,12 +1287,14 @@ marketCmd.command('publish').description('Publish a local signed marketplace ind
1282
1287
  market: { name: opts.marketName, displayName: opts.displayName },
1283
1288
  key: { keyId: opts.keyId, privateKeyPem: readFileSync(opts.key, 'utf8') },
1284
1289
  ...(opts.bundleRoot ? { bundleRoot: opts.bundleRoot } : {}),
1290
+ ...(opts.keepExisting ? { keepExisting: true } : {}),
1285
1291
  ...(opts.sourceRepo && opts.sourceSha && opts.sourceBase
1286
1292
  ? { source: { repoUrl: opts.sourceRepo, sha: opts.sourceSha, basePath: opts.sourceBase } } : {}),
1287
1293
  });
1288
1294
  if (opts.json) await writeStdoutJson(JSON.stringify(result) + '\n');
1289
1295
  else {
1290
- console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}`);
1296
+ console.log(`Published ${result.published.length} plugins (sequence ${result.sequence}); skipped ${result.skipped.length}` +
1297
+ (opts.keepExisting ? `; kept ${result.kept.length}` : ''));
1291
1298
  for (const warning of result.warnings) console.warn(`⚠ ${warning.dir}: ${warning.graph} uses recipes it does not ship (${warning.recipes.join(', ')}) — third-party graphs should use their own recipes.yaml or custom nodes`);
1292
1299
  }
1293
1300
  for (const item of result.skipped) {
@@ -3477,8 +3484,10 @@ coordCmd.command('event').command('record')
3477
3484
  .requiredOption('--header <line>', '채널 글 첫 줄')
3478
3485
  .option('--url <url>', '채널 코멘트 URL')
3479
3486
  .action(async (opts: { seat: string; header: string; url?: string }) => {
3480
- const { parseCoordHeader, recordCoordEvent } = await import('./context-bus/coord-events.js');
3481
- recordCoordEvent({ ...parseCoordHeader(opts.seat, opts.header), url: opts.url || null });
3487
+ const { parseCoordHeader, recordCoordEvent, mirrorCoordDecision } = await import('./context-bus/coord-events.js');
3488
+ const input = { ...parseCoordHeader(opts.seat, opts.header), url: opts.url || null, at: new Date().toISOString() };
3489
+ try { recordCoordEvent(input); }
3490
+ finally { mirrorCoordDecision(input); }
3482
3491
  });
3483
3492
  coordCmd.command('events')
3484
3493
  .option('--since <ISO|2h>', '조회 시작', '2h')
@@ -7317,8 +7326,8 @@ const selfDevCmd = program
7317
7326
  console.error(`[dev] 외부 backend 미션의 자기 명령은 \`elanous agent-mission mission\` 입니다; 대응 명령: elanous agent-mission mission --backend ${executor.backend}`);
7318
7327
  }
7319
7328
  let spec = invokedAsDrive
7320
- ? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command))
7321
- : buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command));
7329
+ ? devCli.buildDriveAliasDevSpec(hasText ? textParts.join(' ') : undefined, devOpts, devCli.explicitDevOptionNames(command), devRunId)
7330
+ : buildDevCliSpec(input, executor, devOpts, devCli.explicitDevOptionNames(command), 'cli-dev-ask', undefined, undefined, devRunId);
7322
7331
  if (!opts.json && spec.notice) console.error(spec.notice);
7323
7332
  // 역할별 LLM은 현재 프로세스 설정이다. self-mission 구현 자식에 전달되지 않는 조합은 위 검증이 먼저 거부한다.
7324
7333
  if (Array.isArray(opts.roleLlm) && opts.roleLlm.length > 0) {
@@ -7819,6 +7828,26 @@ configCmd
7819
7828
  if (report.prodStatus !== 'ok') process.exit(1);
7820
7829
  });
7821
7830
 
7831
+ configCmd
7832
+ .command('classify')
7833
+ .description('읽기 전용 — 운영 config 잎의 자리(기본값·기계 사실·비밀·운영 정책)와 덮어쓰기 수 (CFG-CLASSIFY)')
7834
+ .option('--json', '잎 전체를 JSON 으로 출력 (비밀 값 제외)')
7835
+ .action(async (o: { json?: boolean }) => {
7836
+ const { getElanousConfigDir } = await import('./elanous-config-dir.js');
7837
+ const { readUniverseConfig } = await import('./cli/config-drift.js');
7838
+ const { classifyConfig, renderClassify } = await import('./cli/config-classify.js');
7839
+ const { buildUserConfig } = await import('./user-config.js');
7840
+ const prod = readUniverseConfig(getElanousConfigDir(), []);
7841
+ if (prod.status !== 'ok' || !prod.raw) {
7842
+ console.error(`config classify — config 못 읽음(${prod.status}${prod.error ? `: ${prod.error}` : ''})`);
7843
+ process.exitCode = 1;
7844
+ return;
7845
+ }
7846
+ const report = classifyConfig(prod.raw, buildUserConfig('/nonexistent/elanous-config-drift/config.json') as unknown as Record<string, unknown>);
7847
+ if (o.json) await (await import('./cli/stdout-flush.js')).writeStdoutFully(JSON.stringify(report, null, 2));
7848
+ else console.log(renderClassify(report));
7849
+ });
7850
+
7822
7851
  configCmd
7823
7852
  .command('explain <key>')
7824
7853
  .description('읽기 전용 — 운영 값 ⊕ 출처 층 ⊕ 파생 시험 우주별 값 (CFG-STORE1)')
@@ -0,0 +1,93 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { releaseLedgerRoot } from '../instance/resolve.js';
5
+ import { listChecklist, setItem } from '../release-loop/checklist.js';
6
+ import { withFileLockSync } from '../storage/file-lock.js';
7
+
8
+ export type ConclusionKind = 'evidence' | 'decision' | 'lesson';
9
+ export type ConclusionSource = 'goal' | 'handoff' | 'run-log' | 'channel' | 'pr-discussion';
10
+ export interface ConclusionRecord {
11
+ source: ConclusionSource;
12
+ cell: string;
13
+ kind: ConclusionKind;
14
+ conclusion: string;
15
+ /** Original URL, or a relative path to the original for non-channel sources. */
16
+ ref: string;
17
+ body: string;
18
+ /** The original post's UTC timestamp; required when compressing a channel day. */
19
+ createdAt?: string;
20
+ /** Terminal status is required before a run can be archived. */
21
+ status?: string;
22
+ }
23
+ export interface ConclusionBatch {
24
+ version: string;
25
+ day: string;
26
+ records: ConclusionRecord[];
27
+ }
28
+ export interface ConclusionReceipt {
29
+ archive: string;
30
+ lines: string[];
31
+ originalBytes: number;
32
+ conclusionBytes: number;
33
+ }
34
+
35
+ const kinds = new Set<ConclusionKind>(['evidence', 'decision', 'lesson']);
36
+ const sources = new Set<ConclusionSource>(['goal', 'handoff', 'run-log', 'channel', 'pr-discussion']);
37
+ const closed = new Set(['done', 'failed', 'cancelled', 'superseded', 'completed', 'abandoned']);
38
+ const validDay = (day: string) => /^\d{4}-\d{2}-\d{2}$/.test(day) && new Date(`${day}T00:00:00Z`).toISOString().slice(0, 10) === day;
39
+
40
+ /** Archive a complete input batch; publish only source-linked, explicitly supplied conclusions to the checklist ledger. */
41
+ export function compressConclusions(batch: ConclusionBatch, root = releaseLedgerRoot()): ConclusionReceipt {
42
+ if (!batch || !Array.isArray(batch.records) || !batch.records.length || !validDay(batch.day)) throw new Error('invalid day or empty records');
43
+ if (!/^\d+\.\d+\.\d+(?:-(?:rc|alpha|beta)\.\d+)?$/.test(batch.version)) throw new Error('invalid checklist version');
44
+ if (resolve(root) !== resolve(releaseLedgerRoot())) throw new Error('archive and checklist must share a ledger root');
45
+ const checklist = listChecklist(batch.version);
46
+ const digest = createHash('sha256').update(JSON.stringify(batch)).digest('hex');
47
+ const archiveRef = `knowledge-compress/archive/${digest}.json`;
48
+ const byCell = new Map<string, string[]>();
49
+ for (const [index, record] of batch.records.entries()) {
50
+ if (!record || !sources.has(record.source) || !kinds.has(record.kind) ||
51
+ !checklist.items.some(item => item.id === record.cell) || !record.body?.trim() ||
52
+ !record.conclusion?.trim() || /[\r\n]/.test(record.conclusion) || !record.ref?.trim()) {
53
+ throw new Error('unverified source, conclusion or checklist cell');
54
+ }
55
+ if (record.source === 'channel') {
56
+ if (!/^https:\/\/github\.com\/[^\s]+#issuecomment-\d+$/.test(record.ref) ||
57
+ !record.createdAt || !Number.isFinite(Date.parse(record.createdAt)) ||
58
+ new Date(record.createdAt).toISOString().slice(0, 10) !== batch.day) throw new Error('channel comment must link to the selected UTC day');
59
+ }
60
+ if (record.source === 'run-log' && !closed.has(record.status ?? '')) throw new Error('run is not finished');
61
+ const conclusion = record.conclusion.trim().replace(/\s+/g, ' ');
62
+ if (conclusion.length > 160 || !conclusion) throw new Error('conclusion exceeds one line');
63
+ const ref = record.ref.trim();
64
+ if (/\s|[\r\n]/.test(ref) || (record.source !== 'channel' && !/^(https:\/\/|[^/][^\s]*$)/.test(ref))) throw new Error('invalid source pointer');
65
+ const line = `${record.kind}: ${conclusion} (${record.source === 'channel' ? ref : `${archiveRef}#record-${index + 1}`})`;
66
+ byCell.set(record.cell, [...(byCell.get(record.cell) ?? []), line]);
67
+ }
68
+ const lines = [...byCell].flatMap(([cell, entries]) => [...new Set(entries)].map(line => `${cell} · ${line}`));
69
+ const originalBytes = Buffer.byteLength(JSON.stringify(batch.records));
70
+ const conclusionBytes = Buffer.byteLength(lines.join('\n'));
71
+ if (conclusionBytes * 10 > originalBytes) throw new Error(`conclusions exceed 1/10 of original (${conclusionBytes}/${originalBytes} bytes)`);
72
+
73
+ const dir = join(root, 'knowledge-compress');
74
+ const archive = join(root, archiveRef);
75
+ const lock = join(dir, 'conclusions.lock');
76
+ mkdirSync(dirname(archive), { recursive: true });
77
+ return withFileLockSync(lock, () => {
78
+ if (existsSync(archive)) {
79
+ if (readFileSync(archive, 'utf8') !== JSON.stringify(batch)) throw new Error('archive digest collision');
80
+ } else {
81
+ const temp = `${archive}.${randomUUID()}.tmp`;
82
+ writeFileSync(temp, JSON.stringify(batch), { flag: 'wx', mode: 0o600 });
83
+ renameSync(temp, archive);
84
+ }
85
+ for (const [cell, entries] of byCell) {
86
+ const existing = listChecklist(batch.version).items.find(item => item.id === cell);
87
+ if (!existing) throw new Error(`checklist cell disappeared: ${cell}`);
88
+ const additions = [...new Set(entries)].filter(line => !existing.evidence?.split('\n').includes(line));
89
+ if (additions.length) setItem(batch.version, cell, { evidence: [existing.evidence, ...additions].filter(Boolean).join('\n') }, 'knowledge-compress');
90
+ }
91
+ return { archive: resolve(archive), lines, originalBytes, conclusionBytes };
92
+ });
93
+ }
@@ -3,6 +3,7 @@ import { LessonLedger, type LessonRow } from '../lessons/lesson-ledger.js';
3
3
  import { DecisionLedger, type DecisionEntry, type SeatDecisionRecord } from '../decisions/decision-ledger.js';
4
4
  import { devVersion, listChecklist, type Checklist } from '../release-loop/checklist.js';
5
5
  import { debug } from '../debug/log.js';
6
+ import { queryInstalledPack } from './query.js';
6
7
 
7
8
  /** Directive row fields this search reads — kept local because `src/directives/directive-index` is excluded from the public export. */
8
9
  export interface DirectiveRow { ts: string; text: string; source_file: string; line_no: number }
@@ -14,7 +15,7 @@ function openDirectiveIndex(): { search(q: string): DirectiveRow[]; close(): voi
14
15
  return new mod.DirectiveIndex();
15
16
  }
16
17
 
17
- export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson';
18
+ export type KnowSource = 'directive' | 'decision' | 'checklist' | 'lesson' | 'pack';
18
19
  export interface KnowHit {
19
20
  source: KnowSource;
20
21
  id: string;
@@ -36,6 +37,7 @@ export interface KnowFindDeps {
36
37
  lessons: (query: string) => LessonRow[];
37
38
  versions: () => string[];
38
39
  now: () => Date;
40
+ pack?: typeof queryInstalledPack;
39
41
  }
40
42
 
41
43
  function versions(): string[] {
@@ -55,6 +57,7 @@ const defaults: KnowFindDeps = {
55
57
  lessons: (query) => new LessonLedger().find(query),
56
58
  versions,
57
59
  now: () => new Date(),
60
+ pack: queryInstalledPack,
58
61
  };
59
62
 
60
63
  function matches(text: string, words: string[]): boolean {
@@ -62,7 +65,7 @@ function matches(text: string, words: string[]): boolean {
62
65
  return words.every((word) => normalized.includes(word));
63
66
  }
64
67
 
65
- export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowResult {
68
+ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}, packId?: string): KnowResult {
66
69
  const read = { ...defaults, ...deps };
67
70
  const words = query.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
68
71
  if (!words.length) throw new Error('know: 질문이 필요합니다');
@@ -106,6 +109,10 @@ export function knowFind(query: string, deps: Partial<KnowFindDeps> = {}): KnowR
106
109
  for (const row of read.lessons(query)) rows.push({ source: 'lesson', id: row.id, title: row.incident,
107
110
  status: row.status, at: row.updated_at, current: row.occurrence_count > 1, ref: `lesson show ${row.id}` });
108
111
  });
112
+ if (packId) attempt('pack', () => {
113
+ for (const card of (read.pack ?? queryInstalledPack)(packId, query)) rows.push({ source: 'pack', id: card.id, title: card.title,
114
+ status: 'installed', at: card.updatedAt, current: true, ref: card.ref });
115
+ });
109
116
  rows.sort((a, b) => Number(b.current) - Number(a.current) || (Date.parse(b.at) || 0) - (Date.parse(a.at) || 0)
110
117
  || a.source.localeCompare(b.source) || a.id.localeCompare(b.id));
111
118
  debug.log('knowledge.know', 'query', { query, counts: Object.fromEntries((['directive', 'decision', 'checklist', 'lesson'] as const)
@@ -10,6 +10,26 @@ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
10
10
  import { join, relative, sep } from 'node:path';
11
11
  import { parseFrontmatter, type ObsidianVault } from '../auto-research/obsidian-bridge.js';
12
12
  import type { KnowledgeKind, KnowledgeNote, KnowledgeQueryInput, KnowledgeQueryResult } from './types.js';
13
+ import { PACK_KINDS, parsePackIdString, packIdString } from './kgs/pack.js';
14
+ import { kgsStoreSingleton, type KgsSqliteStore } from './kgs/sqlite-store.js';
15
+
16
+ /** Search only cards belonging to a pack present in the local KGS index. No vault fallback. */
17
+ export function listInstalledPacks(store: Pick<KgsSqliteStore, 'listPacksByKind'> = kgsStoreSingleton()): Array<{ id: string; title: string }> {
18
+ return PACK_KINDS.flatMap(kind => store.listPacksByKind(kind).map(pack => ({ id: packIdString(pack.metadata.id), title: pack.metadata.title })));
19
+ }
20
+
21
+ export function queryInstalledPack(packId: string, question: string, store: Pick<KgsSqliteStore, 'readPack'> = kgsStoreSingleton()): Array<{ id: string; title: string; body: string; ref: string; updatedAt: string }> {
22
+ const id = parsePackIdString(packId);
23
+ if (!id) throw new Error(`invalid pack id: ${packId}`);
24
+ const pack = store.readPack(id.slug, id.version);
25
+ if (!pack) throw new Error(`pack not installed: ${packId}`);
26
+ const words = question.trim().toLocaleLowerCase().split(/\s+/).filter(Boolean);
27
+ return pack.cards.map(card => ({ card, score: words.filter(word => `${card.title} ${card.body}`.toLocaleLowerCase().includes(word)).length }))
28
+ .filter(hit => hit.score > 0 || words.length === 0)
29
+ .sort((a, b) => b.score - a.score || a.card.id.localeCompare(b.card.id))
30
+ .map(({ card }) => ({ id: card.id, title: card.title, body: card.body,
31
+ ref: `${packIdString(pack.metadata.id)}#${card.id}`, updatedAt: card.updatedAt }));
32
+ }
13
33
 
14
34
  const DEFAULT_LIMIT = 20;
15
35
  const MAX_LIMIT = 100;
package/src/llm.ts CHANGED
@@ -1746,7 +1746,7 @@ export const REASONING_CYCLE: readonly import('./user-config.js').ReasoningLevel
1746
1746
 
1747
1747
  /** Does the (provider, model) pair support a "think before answering"
1748
1748
  * reasoning surface? Capability-based: gpt-5 family on Codex /
1749
- * Responses API + Anthropic claude-4 / claude-3.7 (extended thinking)
1749
+ * Responses API + Anthropic Claude 3.7+ (extended thinking)
1750
1750
  * + OpenAI o-series. Other providers / older models return false so
1751
1751
  * the HUD pill auto-hides and the wire body skips the reasoning
1752
1752
  * field — no user toggle required. */
@@ -1768,9 +1768,7 @@ export function modelSupportsReasoning(
1768
1768
  return m.startsWith('o1') || m.startsWith('o3') || m.startsWith('o4');
1769
1769
  }
1770
1770
  if (provider === 'anthropic') {
1771
- // Extended thinking is GA on claude-3.7 onward — anything 4-family
1772
- // (opus-4, sonnet-4, haiku-4) plus the 3.7 line.
1773
- return m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
1771
+ return claudeSupportsThinking(model);
1774
1772
  }
1775
1773
  if (provider === 'gemini') {
1776
1774
  // Gemini 2.5+ family supports thinking budget (thinkingConfig).
@@ -1926,16 +1924,32 @@ function mapReasoningLevelToAnthropicEffort(
1926
1924
  * 🩹 그래서 규칙을 이름 목록이 아니라 «수»로 둔다 — 목록은 새 모델마다 늙지만 세대 비교는 안 늙는다.
1927
1925
  * ⚠️ 알 수 없는 형태는 legacy 로 둔다(무회귀). 판정 못 하면 종전 동작이 정답이다. */
1928
1926
  function usesAdaptiveThinking(model: string | undefined): boolean {
1927
+ const version = claudeVersion(model, true);
1928
+ if (!version) return false;
1929
+ if (version.generation >= 5) return true;
1930
+ return version.generation === 4 && version.minor >= 7;
1931
+ }
1932
+
1933
+ /** Shared numeric version parser for adaptive API shape and extended-thinking support. */
1934
+ function claudeVersion(model: string | undefined, requireTerminal = false): { generation: number; minor: number } | undefined {
1929
1935
  // 끝에 붙는 릴리스 날짜(`-20251101`)는 버전이 아니다 — 떼고 본다.
1930
1936
  const m = (model ?? '').toLowerCase().replace(/-\d{8}$/, '');
1931
- const v = /-(\d+)(?:[-.](\d+))?$/.exec(m);
1932
- if (!v) return false;
1933
- const generation = Number(v[1]);
1934
- const minor = v[2] === undefined ? 0 : Number(v[2]);
1935
- if (generation >= 5) return true;
1936
- return generation === 4 && minor >= 7;
1937
+ for (const v of m.matchAll(/-(\d+)(?:[-.](\d+))?(?=-|$)/g)) {
1938
+ if (requireTerminal && v.index + v[0].length !== m.length) continue;
1939
+ return { generation: Number(v[1]), minor: v[2] === undefined ? 0 : Number(v[2]) };
1940
+ }
1941
+ return undefined;
1937
1942
  }
1938
1943
 
1944
+ function claudeSupportsThinking(model: string | undefined): boolean {
1945
+ // includes, not startsWith: prefixed ids (`us.anthropic.claude-…`, `anthropic/claude-…`) passed the old substring check.
1946
+ if (!model?.toLowerCase().includes('claude-')) return false;
1947
+ const version = claudeVersion(model);
1948
+ return version !== undefined && (version.generation > 3 || version.generation === 3 && version.minor >= 7);
1949
+ }
1950
+
1951
+ export const _claudeSupportsThinkingForContractTest = claudeSupportsThinking;
1952
+
1939
1953
  /** ⛔ 테스트 전용 노출이 아니다 — 모델 계열 계약은 «이름을 가진 판정»이라 회귀를 테스트로 물어야 한다.
1940
1954
  * 이 파일 안 두 조립부(:3102·:9591)가 그 값을 쓰고, 그 계약이 틀리면 provider 가 통째로 막힌다. */
1941
1955
  export const _usesAdaptiveThinkingForContractTest = usesAdaptiveThinking;
@@ -3134,13 +3148,9 @@ async function* streamAnthropicEvents(
3134
3148
  // forcing a single thinking block before all tools). Improves
3135
3149
  // multi-tool turn quality — the model can decide what to call
3136
3150
  // next based on the prior tool's output thinking. Only supported
3137
- // on extended-thinking-capable models (claude-3-7 / 4 family).
3138
- const model: string = (body?.model ?? '') as string;
3139
- const m = model.toLowerCase();
3140
- const supportsThinking =
3141
- m.includes('-4-') || m.includes('-3-7') || m.includes('3.7') || m.includes('claude-4');
3151
+ // on extended-thinking-capable models (Claude 3.7+).
3142
3152
  const betas: string[] = ['fine-grained-tool-streaming-2025-05-14'];
3143
- if (supportsThinking) betas.push('interleaved-thinking-2025-05-14');
3153
+ if (claudeSupportsThinking(body?.model)) betas.push('interleaved-thinking-2025-05-14');
3144
3154
 
3145
3155
  const response = await fetchApiWithRetry(
3146
3156
  ANTHROPIC_API_URL,