elanous 0.2.23 → 0.2.24

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 (347) 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/{272-b23d2e92161d11fd.js → 272-0eed0fb0aab716b6.js} +1 -1
  4. package/apps/pwa/out/_next/static/chunks/{3961-fbef064c213b2b18.js → 3961-aaae04ed1d32622f.js} +1 -1
  5. package/apps/pwa/out/_next/static/chunks/app/approvals/{page-d5341ac96203a983.js → page-3b9fc39362473ae7.js} +1 -1
  6. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-f2b62c19ca68fef8.js +1 -0
  7. package/apps/pwa/out/_next/static/chunks/app/board/{page-55c631c032dcc47e.js → page-d7602d489edf5f37.js} +1 -1
  8. package/apps/pwa/out/_next/static/chunks/app/botlab/{page-ad3bd8084048bee4.js → page-24282bf9f90031a5.js} +1 -1
  9. package/apps/pwa/out/_next/static/chunks/app/bots/page-8b7c5ab6b5bc8d64.js +1 -0
  10. package/apps/pwa/out/_next/static/chunks/app/ceo/{page-5c5d0e370f495ef6.js → page-4eb84d454f8c3415.js} +1 -1
  11. package/apps/pwa/out/_next/static/chunks/app/chat/{page-c325e21543cd48a8.js → page-b2e494bdad57fcb5.js} +1 -1
  12. package/apps/pwa/out/_next/static/chunks/app/consult/{page-6168a7c43e6ef8f5.js → page-c0ab229673a8b0eb.js} +1 -1
  13. package/apps/pwa/out/_next/static/chunks/app/control/page-3e2b45f3c36dbac4.js +1 -0
  14. package/apps/pwa/out/_next/static/chunks/app/decisions/page-c22aa511b28743ad.js +1 -0
  15. package/apps/pwa/out/_next/static/chunks/app/design-check/page-26345242e4049f55.js +1 -0
  16. package/apps/pwa/out/_next/static/chunks/app/editor/{page-25bb425afdb27541.js → page-bf88c0f558d8ef54.js} +1 -1
  17. package/apps/pwa/out/_next/static/chunks/app/exec/{page-32d72fdef1afe11a.js → page-52fec523e5b5d3ad.js} +1 -1
  18. package/apps/pwa/out/_next/static/chunks/app/field/page-67723dfdcde92b31.js +1 -0
  19. package/apps/pwa/out/_next/static/chunks/app/inside/{page-2bd9ce9c020d02d4.js → page-22725e2570f1126b.js} +1 -1
  20. package/apps/pwa/out/_next/static/chunks/app/intake/{page-a34096245e63b9f2.js → page-1891335950d68b70.js} +1 -1
  21. package/apps/pwa/out/_next/static/chunks/app/layout-3267af1ebdc73cf5.js +1 -0
  22. package/apps/pwa/out/_next/static/chunks/app/live/{page-4cc66d22ca4ec502.js → page-c314b75c11234998.js} +1 -1
  23. package/apps/pwa/out/_next/static/chunks/app/live-run/{page-83b14c729fcea837.js → page-9f9c85415f61782b.js} +1 -1
  24. package/apps/pwa/out/_next/static/chunks/app/loops/page-c1d4276059085587.js +1 -0
  25. package/apps/pwa/out/_next/static/chunks/app/market/page-833aab587f26ad4c.js +1 -0
  26. package/apps/pwa/out/_next/static/chunks/app/missions/[id]/{page-ccdb83ed40c8d724.js → page-8526c17ae38e6a50.js} +1 -1
  27. package/apps/pwa/out/_next/static/chunks/app/morning/{page-7ad3de8fa06fdea9.js → page-f998637a8402115c.js} +1 -1
  28. package/apps/pwa/out/_next/static/chunks/app/not-found-b7bb53828b17b335.js +1 -0
  29. package/apps/pwa/out/_next/static/chunks/app/observatory/page-1b1850de0257e46d.js +1 -0
  30. package/apps/pwa/out/_next/static/chunks/app/ops/checklist/{page-786111f30b6f0969.js → page-6f23159fedb41f67.js} +1 -1
  31. package/apps/pwa/out/_next/static/chunks/app/ops/release/{page-48fb31810693c357.js → page-1d809a87139dd024.js} +1 -1
  32. package/apps/pwa/out/_next/static/chunks/app/ops/seats/{page-4e17cce5e2ed0fee.js → page-da9e570234d481fd.js} +1 -1
  33. package/apps/pwa/out/_next/static/chunks/app/outputs/{page-cb154acadb025414.js → page-b92caddb5c715f30.js} +1 -1
  34. package/apps/pwa/out/_next/static/chunks/app/page-8ce3a38e210b810a.js +1 -0
  35. package/apps/pwa/out/_next/static/chunks/app/reflection/{page-c2dd2b19c93f90ab.js → page-57ffeeb6019440fd.js} +1 -1
  36. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-abbb417bf514630b.js +1 -0
  37. package/apps/pwa/out/_next/static/chunks/app/sessions/{page-d3c007c8a0ddd7ee.js → page-2c2b3f29cec1efa4.js} +1 -1
  38. package/apps/pwa/out/_next/static/chunks/app/settings/devices/{page-7837ce2aacb97b04.js → page-9369279f72fc7b51.js} +1 -1
  39. package/apps/pwa/out/_next/static/chunks/app/settings/page-64861f14039499c3.js +1 -0
  40. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-ddc4c93efd8ab94b.js +1 -0
  41. package/apps/pwa/out/_next/static/chunks/app/setup/page-901a1391b81053bc.js +1 -0
  42. package/apps/pwa/out/_next/static/chunks/app/share/page-68d9bbaac9841a58.js +1 -0
  43. package/apps/pwa/out/_next/static/chunks/app/showroom/{page-119be9a0444d2482.js → page-b5c0c996f329371e.js} +1 -1
  44. package/apps/pwa/out/_next/static/chunks/app/tasks/{page-fa806c018833e332.js → page-89881ccbcf728978.js} +1 -1
  45. package/apps/pwa/out/_next/static/chunks/app/term/page-0e87a803b3d7165e.js +1 -0
  46. package/apps/pwa/out/_next/static/chunks/app/today/{page-d431fbdea6ad93dd.js → page-58d4e5cb30ef4871.js} +1 -1
  47. package/apps/pwa/out/_next/static/chunks/app/trace/page-02ce4c3b5f39e4d2.js +1 -0
  48. package/apps/pwa/out/_next/static/chunks/app/vault/page-e620d1bb8eadcec2.js +1 -0
  49. package/apps/pwa/out/_next/static/chunks/app/workflows/chat-ui/{page-847c3de6ca8a4416.js → page-acf868c64d77f950.js} +1 -1
  50. package/apps/pwa/out/_next/static/chunks/app/workflows/page-54fa605fa6dba457.js +1 -0
  51. package/apps/pwa/out/_next/static/chunks/app/workspace/{page-74e69ba2c8e970d4.js → page-bc79f0833533fa84.js} +1 -1
  52. package/apps/pwa/out/_next/static/chunks/app/worktrees/{page-70038f41ae7cf931.js → page-d5c4398eea03d8f3.js} +1 -1
  53. package/apps/pwa/out/_next/static/css/72becfd42807b50d.css +1 -0
  54. package/apps/pwa/out/approvals/index.html +1 -1
  55. package/apps/pwa/out/approvals/index.txt +14 -14
  56. package/apps/pwa/out/autopilot/index.html +1 -1
  57. package/apps/pwa/out/autopilot/index.txt +14 -14
  58. package/apps/pwa/out/board/index.html +1 -1
  59. package/apps/pwa/out/board/index.txt +14 -14
  60. package/apps/pwa/out/botlab/index.html +1 -1
  61. package/apps/pwa/out/botlab/index.txt +14 -14
  62. package/apps/pwa/out/bots/index.html +1 -1
  63. package/apps/pwa/out/bots/index.txt +14 -14
  64. package/apps/pwa/out/ceo/index.html +1 -1
  65. package/apps/pwa/out/ceo/index.txt +14 -14
  66. package/apps/pwa/out/chat/index.html +1 -1
  67. package/apps/pwa/out/chat/index.txt +14 -14
  68. package/apps/pwa/out/consult/index.html +1 -1
  69. package/apps/pwa/out/consult/index.txt +14 -14
  70. package/apps/pwa/out/control/index.html +1 -1
  71. package/apps/pwa/out/control/index.txt +14 -14
  72. package/apps/pwa/out/decisions/index.html +1 -1
  73. package/apps/pwa/out/decisions/index.txt +14 -14
  74. package/apps/pwa/out/design-check/index.html +1 -1
  75. package/apps/pwa/out/design-check/index.txt +14 -14
  76. package/apps/pwa/out/editor/index.html +1 -1
  77. package/apps/pwa/out/editor/index.txt +14 -14
  78. package/apps/pwa/out/exec/index.html +1 -1
  79. package/apps/pwa/out/exec/index.txt +14 -14
  80. package/apps/pwa/out/field/index.html +1 -1
  81. package/apps/pwa/out/field/index.txt +14 -14
  82. package/apps/pwa/out/healthz +1 -1
  83. package/apps/pwa/out/index.html +1 -1
  84. package/apps/pwa/out/index.txt +14 -14
  85. package/apps/pwa/out/inside/index.html +1 -1
  86. package/apps/pwa/out/inside/index.txt +14 -14
  87. package/apps/pwa/out/intake/index.html +1 -1
  88. package/apps/pwa/out/intake/index.txt +14 -14
  89. package/apps/pwa/out/live/index.html +1 -1
  90. package/apps/pwa/out/live/index.txt +14 -14
  91. package/apps/pwa/out/live-run/index.html +1 -1
  92. package/apps/pwa/out/live-run/index.txt +14 -14
  93. package/apps/pwa/out/loops/index.html +1 -1
  94. package/apps/pwa/out/loops/index.txt +14 -14
  95. package/apps/pwa/out/market/index.html +1 -1
  96. package/apps/pwa/out/market/index.txt +14 -14
  97. package/apps/pwa/out/missions/placeholder/index.html +1 -1
  98. package/apps/pwa/out/missions/placeholder/index.txt +14 -14
  99. package/apps/pwa/out/morning/index.html +1 -1
  100. package/apps/pwa/out/morning/index.txt +14 -14
  101. package/apps/pwa/out/observatory/index.html +1 -1
  102. package/apps/pwa/out/observatory/index.txt +14 -14
  103. package/apps/pwa/out/ops/checklist/index.html +1 -1
  104. package/apps/pwa/out/ops/checklist/index.txt +14 -14
  105. package/apps/pwa/out/ops/release/index.html +1 -1
  106. package/apps/pwa/out/ops/release/index.txt +14 -14
  107. package/apps/pwa/out/ops/seats/index.html +1 -1
  108. package/apps/pwa/out/ops/seats/index.txt +14 -14
  109. package/apps/pwa/out/outputs/index.html +1 -1
  110. package/apps/pwa/out/outputs/index.txt +14 -14
  111. package/apps/pwa/out/reflection/index.html +1 -1
  112. package/apps/pwa/out/reflection/index.txt +15 -15
  113. package/apps/pwa/out/scheduler/index.html +1 -1
  114. package/apps/pwa/out/scheduler/index.txt +14 -14
  115. package/apps/pwa/out/sessions/index.html +1 -1
  116. package/apps/pwa/out/sessions/index.txt +14 -14
  117. package/apps/pwa/out/settings/devices/index.html +1 -1
  118. package/apps/pwa/out/settings/devices/index.txt +14 -14
  119. package/apps/pwa/out/settings/index.html +1 -1
  120. package/apps/pwa/out/settings/index.txt +14 -14
  121. package/apps/pwa/out/setup/done/index.html +1 -1
  122. package/apps/pwa/out/setup/done/index.txt +14 -14
  123. package/apps/pwa/out/setup/index.html +1 -1
  124. package/apps/pwa/out/setup/index.txt +14 -14
  125. package/apps/pwa/out/share/index.html +1 -1
  126. package/apps/pwa/out/share/index.txt +14 -14
  127. package/apps/pwa/out/showroom/index.html +1 -1
  128. package/apps/pwa/out/showroom/index.txt +14 -14
  129. package/apps/pwa/out/tasks/index.html +1 -1
  130. package/apps/pwa/out/tasks/index.txt +14 -14
  131. package/apps/pwa/out/term/index.html +1 -1
  132. package/apps/pwa/out/term/index.txt +14 -14
  133. package/apps/pwa/out/today/index.html +1 -1
  134. package/apps/pwa/out/today/index.txt +14 -14
  135. package/apps/pwa/out/trace/index.html +1 -1
  136. package/apps/pwa/out/trace/index.txt +14 -14
  137. package/apps/pwa/out/vault/index.html +1 -1
  138. package/apps/pwa/out/vault/index.txt +14 -14
  139. package/apps/pwa/out/workflows/chat-ui/index.html +1 -1
  140. package/apps/pwa/out/workflows/chat-ui/index.txt +14 -14
  141. package/apps/pwa/out/workflows/index.html +1 -1
  142. package/apps/pwa/out/workflows/index.txt +14 -14
  143. package/apps/pwa/out/workspace/index.html +1 -1
  144. package/apps/pwa/out/workspace/index.txt +14 -14
  145. package/apps/pwa/out/worktrees/index.html +1 -1
  146. package/apps/pwa/out/worktrees/index.txt +14 -14
  147. package/catalog/models/openrouter/deepseek-v4-pro-0813.yaml +39 -0
  148. package/catalog/models/openrouter/deepseek-v4.1-flash.yaml +38 -0
  149. package/catalog/models/openrouter/glm-5.3-flash.yaml +35 -0
  150. package/catalog/providers/openrouter.yaml +2 -0
  151. package/graphs/implement-loop.yaml +31 -0
  152. package/graphs/ops/harness-gc.yaml +17 -0
  153. package/graphs/ops/recipes.yaml +3 -0
  154. package/graphs/overlays/README.md +1 -1
  155. package/graphs/overlays/stop-heal-ladder.yaml +105 -0
  156. package/package.json +1 -1
  157. package/scripts/add-api-key.sh +1 -1
  158. package/scripts/ci-typecheck-changed.ts +71 -14
  159. package/scripts/fleet/bedrock-pod-secret.ts +122 -0
  160. package/scripts/fleet/node-setup-linux.sh +97 -0
  161. package/scripts/fleet/node-setup.sh +11 -0
  162. package/scripts/fleet/zai-pod-secret.ts +86 -0
  163. package/scripts/land-global-checks.ts +3 -0
  164. package/scripts/measure-log-diet-2.ts +213 -0
  165. package/scripts/measure-tsc-affected-roots.ts +289 -0
  166. package/scripts/release-loop/gate-executor.ts +28 -0
  167. package/scripts/release-loop/gate-node.ts +262 -26
  168. package/scripts/release-loop/resume-release.ts +9 -3
  169. package/scripts/release-loop/resume.ts +41 -0
  170. package/scripts/release-loop/unattended-release.ts +22 -7
  171. package/scripts/tsc-baseline-cache.ts +147 -0
  172. package/scripts/usage-rollup.ts +58 -6
  173. package/src/agent/run-context.ts +10 -0
  174. package/src/agent-mission/driver.ts +18 -3
  175. package/src/agent-mission/resource-ladder.ts +8 -3
  176. package/src/agent-mission/review-loop.ts +8 -2
  177. package/src/agent-substrate/acp-implementer.ts +139 -0
  178. package/src/agent-substrate/pr-reviewer.ts +31 -4
  179. package/src/agent-substrate/self-review-cli.ts +48 -9
  180. package/src/autopilot/mission-critique.ts +18 -5
  181. package/src/autopilot/mission-feasibility-negotiate.ts +11 -10
  182. package/src/autopilot/mission-trace.ts +12 -1
  183. package/src/autopilot/system-lookback-run.ts +16 -10
  184. package/src/bench/bench.ts +183 -0
  185. package/src/budget/fetchers/grok.ts +15 -5
  186. package/src/budget/llm-cost.ts +37 -5
  187. package/src/budget/types.ts +8 -2
  188. package/src/budget/unified-usage.ts +88 -6
  189. package/src/cli/bench-cli.ts +34 -0
  190. package/src/cli/guardian-cli.ts +26 -1
  191. package/src/cli/image-cli.ts +101 -0
  192. package/src/cli/log-event-names.ts +4 -0
  193. package/src/cli/pr-cli.ts +62 -5
  194. package/src/cli/pr-land-overlap-evidence.ts +160 -12
  195. package/src/cli/release-cli.ts +18 -9
  196. package/src/cli/rfc-status.ts +52 -2
  197. package/src/cli/self-update.ts +104 -0
  198. package/src/cli/tasks-cli.ts +109 -4
  199. package/src/cli/usage-cli.ts +91 -8
  200. package/src/core-turn/run-goal-loop.ts +5 -4
  201. package/src/dashboard/ad-run-setup-bindings.ts +279 -0
  202. package/src/dashboard/index.ts +18 -276
  203. package/src/dashboard/stale-install-check.ts +11 -0
  204. package/src/debug/log-diet.ts +38 -7
  205. package/src/debug/log.ts +19 -1
  206. package/src/domains/release-tool.ts +39 -0
  207. package/src/error-report/report.ts +21 -4
  208. package/src/github/pr-labels.ts +1 -0
  209. package/src/graph-runner/graph-cli.ts +42 -1
  210. package/src/graph-runner/runner.ts +167 -6
  211. package/src/grok/usage.ts +23 -2
  212. package/src/grounding/ref-index.ts +253 -0
  213. package/src/harness/dispatch-backpressure.ts +10 -2
  214. package/src/harness/harness-clean.ts +199 -78
  215. package/src/harness/harness-cli-command.ts +122 -7
  216. package/src/harness/harness-gc.ts +331 -0
  217. package/src/harness/harness-stop.ts +4 -2
  218. package/src/harness/harness-substrate-default.ts +142 -7
  219. package/src/harness/launch-registry.ts +6 -0
  220. package/src/harness/worktree-branch-prefix.ts +9 -0
  221. package/src/hq/fence.ts +187 -0
  222. package/src/hq/seats.ts +38 -22
  223. package/src/images/image-ledger.ts +179 -0
  224. package/src/index.ts +93 -11
  225. package/src/instance/child-scope.ts +32 -1
  226. package/src/instance/dev-project.ts +133 -0
  227. package/src/instance/resolve.ts +24 -0
  228. package/src/instance/spawn-elanous.ts +45 -0
  229. package/src/intake-plane/ref-prefetch.ts +110 -0
  230. package/src/intelligence-map/model-catalog.ts +72 -0
  231. package/src/llm/bedrock-eventstream.ts +215 -0
  232. package/src/llm/bedrock-provider.ts +114 -41
  233. package/src/llm/bedrock.ts +174 -28
  234. package/src/llm/openrouter-byok-cost.ts +96 -0
  235. package/src/llm/openrouter-deepseek.ts +151 -0
  236. package/src/llm/openrouter-upstream-pins.config.ts +25 -0
  237. package/src/llm/zai-coding-plan.ts +700 -0
  238. package/src/llm.ts +87 -16
  239. package/src/loops/checker.ts +148 -9
  240. package/src/loops/orchestrator/resync-shadow.ts +34 -7
  241. package/src/maturity/feature-maturity.ts +2 -2
  242. package/src/model-tier/llm-tier-map.ts +9 -5
  243. package/src/models/prompts.ts +2 -1
  244. package/src/mss/logging/log-store.ts +58 -11
  245. package/src/nexus/api/missions.ts +73 -2
  246. package/src/nexus/api/ops-api.ts +18 -0
  247. package/src/nexus/index.ts +7 -75
  248. package/src/nexus/tools-kind.ts +78 -0
  249. package/src/oauth/codex-account-rotation.ts +39 -2
  250. package/src/oauth/codex-account-store.ts +323 -24
  251. package/src/oauth/fallback-chain.ts +53 -15
  252. package/src/onboarding/first-run.ts +37 -18
  253. package/src/onboarding/web-first.ts +20 -2
  254. package/src/ops/machine-heartbeat.ts +185 -0
  255. package/src/ops/overview-cache.ts +175 -0
  256. package/src/ops/overview.ts +8 -0
  257. package/src/prompt-cache/openai.ts +3 -0
  258. package/src/prompt-cache/types.ts +5 -0
  259. package/src/prompt-enhance/enhance.ts +23 -0
  260. package/src/release-loop/placement.ts +16 -1
  261. package/src/requirements/requirement-ledger.ts +536 -0
  262. package/src/retro/daily.ts +5 -5
  263. package/src/seat-loop/seat-loop.ts +32 -10
  264. package/src/self-dev/dev-cli.ts +44 -10
  265. package/src/self-dev/dev-pipeline.ts +7 -2
  266. package/src/self-dev/draft-residue.ts +14 -2
  267. package/src/self-dev/draft-sweep.ts +103 -0
  268. package/src/self-dev/graph-journey-route.ts +6 -0
  269. package/src/self-dev/orchestrate-cli.ts +25 -3
  270. package/src/self-dev/orchestrate.ts +41 -6
  271. package/src/self-dev/run-supervisor.ts +188 -15
  272. package/src/self-dev/run-types.ts +13 -0
  273. package/src/self-dev/supervisor-verdict-edges.ts +1 -0
  274. package/src/self-implement/budget-gate.ts +53 -13
  275. package/src/self-implement/child-context-composition.ts +139 -0
  276. package/src/self-implement/child-llm-preference.ts +13 -5
  277. package/src/self-implement/claude-acp-implement.ts +67 -0
  278. package/src/self-implement/flex-signals.ts +59 -0
  279. package/src/self-implement/frozen-merges.ts +8 -3
  280. package/src/self-implement/goal-author-decompose.ts +10 -2
  281. package/src/self-implement/goal-author.ts +24 -9
  282. package/src/self-implement/graph-overlay-condition.ts +28 -0
  283. package/src/self-implement/graph-overlay-yaml.ts +41 -7
  284. package/src/self-implement/graph-templates.ts +33 -1
  285. package/src/self-implement/graph-yaml.ts +56 -0
  286. package/src/self-implement/heal-intake.ts +5 -1
  287. package/src/self-implement/orchestrator.ts +144 -15
  288. package/src/self-implement/running-runs.ts +75 -5
  289. package/src/self-implement/seams.ts +47 -1
  290. package/src/self-implement/self-implement-cli.ts +9 -4
  291. package/src/self-implement/stop-autoheal.ts +69 -2
  292. package/src/session-runtime/retry-policy.ts +2 -1
  293. package/src/steward/launch.ts +5 -4
  294. package/src/steward/triage-budget.ts +62 -0
  295. package/src/steward/triage.ts +83 -22
  296. package/src/task-agent/actions.ts +62 -5
  297. package/src/task-agent/completion-evidence.ts +5 -0
  298. package/src/task-agent/cover.ts +101 -3
  299. package/src/task-agent/judge.ts +16 -0
  300. package/src/task-agent/land-order-ledger.ts +95 -0
  301. package/src/task-agent/live-move-history.ts +87 -1
  302. package/src/task-agent/live-moves.ts +350 -20
  303. package/src/task-agent/rejudge-on-head.ts +75 -20
  304. package/src/task-agent/review-prior-fix.ts +146 -0
  305. package/src/task-agent/run-adopt.ts +136 -0
  306. package/src/task-agent/shadow.ts +53 -9
  307. package/src/task-agent/task-hand.ts +54 -7
  308. package/src/task-orchestrator/surfaces/pod-account-broker.ts +38 -5
  309. package/src/task-orchestrator/surfaces/pod-bedrock-child.ts +74 -0
  310. package/src/task-orchestrator/surfaces/pod-child-default.ts +50 -0
  311. package/src/task-orchestrator/surfaces/pod-child-providers.ts +14 -1
  312. package/src/task-orchestrator/surfaces/pod-failure-reason.ts +79 -6
  313. package/src/task-orchestrator/surfaces/pod-lease.ts +17 -5
  314. package/src/task-orchestrator/surfaces/pod-ledger-collect.ts +11 -5
  315. package/src/task-orchestrator/surfaces/pod-pool.ts +20 -4
  316. package/src/task-orchestrator/surfaces/pod-zai-child.ts +101 -0
  317. package/src/task-orchestrator/surfaces/self-implement-pod.ts +213 -23
  318. package/src/task-orchestrator/surfaces/self-implement.ts +19 -1
  319. package/src/typecheck-ratchet.ts +1 -0
  320. package/src/user-config.ts +51 -11
  321. package/src/version/packed-revision.json +1 -1
  322. package/src/workflow-runtime/triggers/registry.ts +59 -0
  323. package/src/workflow-runtime/triggers/webhook-source.ts +35 -5
  324. package/apps/pwa/out/_next/static/chunks/app/autopilot/page-0160ab83c8685a12.js +0 -1
  325. package/apps/pwa/out/_next/static/chunks/app/bots/page-e6784759a2531031.js +0 -1
  326. package/apps/pwa/out/_next/static/chunks/app/control/page-31417d3604154a6d.js +0 -1
  327. package/apps/pwa/out/_next/static/chunks/app/decisions/page-e9606dc0bdec9305.js +0 -1
  328. package/apps/pwa/out/_next/static/chunks/app/design-check/page-a648bc464c9dc754.js +0 -1
  329. package/apps/pwa/out/_next/static/chunks/app/field/page-d27b794c8ba76bbb.js +0 -1
  330. package/apps/pwa/out/_next/static/chunks/app/layout-22170f4f00a15496.js +0 -1
  331. package/apps/pwa/out/_next/static/chunks/app/loops/page-6e2781c523dad761.js +0 -1
  332. package/apps/pwa/out/_next/static/chunks/app/market/page-26dd415c8f13f341.js +0 -1
  333. package/apps/pwa/out/_next/static/chunks/app/not-found-015c02c4fec4023e.js +0 -1
  334. package/apps/pwa/out/_next/static/chunks/app/observatory/page-6b0ae5b5c42f6d2f.js +0 -1
  335. package/apps/pwa/out/_next/static/chunks/app/page-16258e956ea93fc2.js +0 -1
  336. package/apps/pwa/out/_next/static/chunks/app/scheduler/page-85f35dd1dd96ae87.js +0 -1
  337. package/apps/pwa/out/_next/static/chunks/app/settings/page-7350ee93f4fe1033.js +0 -1
  338. package/apps/pwa/out/_next/static/chunks/app/setup/done/page-00723458575f2b19.js +0 -1
  339. package/apps/pwa/out/_next/static/chunks/app/setup/page-745d9b6c015076f8.js +0 -1
  340. package/apps/pwa/out/_next/static/chunks/app/share/page-736d33c7414a8a1a.js +0 -1
  341. package/apps/pwa/out/_next/static/chunks/app/term/page-c782041b5f5199ba.js +0 -1
  342. package/apps/pwa/out/_next/static/chunks/app/trace/page-0c056abff82b9891.js +0 -1
  343. package/apps/pwa/out/_next/static/chunks/app/vault/page-242d668e4fbf72c4.js +0 -1
  344. package/apps/pwa/out/_next/static/chunks/app/workflows/page-81dd0d8ae923ed72.js +0 -1
  345. package/apps/pwa/out/_next/static/css/5619d59e87eddf66.css +0 -1
  346. /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_buildManifest.js +0 -0
  347. /package/apps/pwa/out/_next/static/{rfkMm4movqkqMebBrCXiR → Q-7iofoxOEv1WX18S35of}/_ssgManifest.js +0 -0
@@ -0,0 +1,139 @@
1
+ import { debug } from '../debug/log.js';
2
+
3
+ export const SMALL_CHILD_CONTEXT_TOKEN_BUDGET = 32_000;
4
+
5
+ export const CHILD_CONTEXT_COMPONENT_NAMES = [
6
+ 'systemPrompt',
7
+ 'agentsMd',
8
+ 'claudeMd',
9
+ 'toolSchemas',
10
+ 'goal',
11
+ 'ledger',
12
+ ] as const;
13
+
14
+ export type ChildContextComponentName = typeof CHILD_CONTEXT_COMPONENT_NAMES[number];
15
+ export type ChildContextProfile = 'default' | 'small';
16
+
17
+ export interface ChildContextComponents {
18
+ readonly systemPrompt?: string;
19
+ readonly agentsMd?: string;
20
+ readonly claudeMd?: string;
21
+ readonly toolSchemas?: string;
22
+ readonly goal?: string;
23
+ readonly ledger?: string;
24
+ }
25
+
26
+ export interface ChildContextComponentMeasurement {
27
+ readonly inputBytes: number;
28
+ readonly includedBytes: number;
29
+ readonly truncated: boolean;
30
+ }
31
+
32
+ export interface ChildContextComposition {
33
+ readonly text: string;
34
+ readonly profile: ChildContextProfile;
35
+ readonly budgetTokens: number | null;
36
+ /** UTF-8 bytes are a conservative upper bound on tokenizer tokens. */
37
+ readonly tokenUpperBound: number;
38
+ readonly components: Readonly<Record<ChildContextComponentName, ChildContextComponentMeasurement>>;
39
+ }
40
+
41
+ export interface ComposeChildContextOptions {
42
+ readonly profile?: ChildContextProfile;
43
+ readonly log?: (event: string, data: Record<string, unknown>) => void;
44
+ }
45
+
46
+ const JOINER = '\n\n';
47
+
48
+ function utf8Bytes(value: string): number {
49
+ return Buffer.byteLength(value, 'utf8');
50
+ }
51
+
52
+ function prefixWithinUtf8Budget(value: string, byteBudget: number): string {
53
+ if (byteBudget <= 0) return '';
54
+ let included = '';
55
+ let bytes = 0;
56
+ for (const character of value) {
57
+ const characterBytes = utf8Bytes(character);
58
+ if (bytes + characterBytes > byteBudget) break;
59
+ included += character;
60
+ bytes += characterBytes;
61
+ }
62
+ return included;
63
+ }
64
+
65
+ function emptyMeasurements(): Record<ChildContextComponentName, ChildContextComponentMeasurement> {
66
+ return {
67
+ systemPrompt: { inputBytes: 0, includedBytes: 0, truncated: false },
68
+ agentsMd: { inputBytes: 0, includedBytes: 0, truncated: false },
69
+ claudeMd: { inputBytes: 0, includedBytes: 0, truncated: false },
70
+ toolSchemas: { inputBytes: 0, includedBytes: 0, truncated: false },
71
+ goal: { inputBytes: 0, includedBytes: 0, truncated: false },
72
+ ledger: { inputBytes: 0, includedBytes: 0, truncated: false },
73
+ };
74
+ }
75
+
76
+ function emitCompositionEvent(
77
+ log: ((event: string, data: Record<string, unknown>) => void) | undefined,
78
+ data: Record<string, unknown>,
79
+ ): void {
80
+ try {
81
+ if (log) log('child.context-composition', data);
82
+ else debug.log('self-implement', 'child.context-composition', data);
83
+ } catch {
84
+ // Context composition must not fail because an observation sink is unavailable.
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Composes the implementation child's first-turn context and records the byte
90
+ * contribution of every input component. The small profile spends its hard cap
91
+ * on executable context first and trims repository instruction files last.
92
+ */
93
+ export function composeChildContext(
94
+ components: ChildContextComponents,
95
+ options: ComposeChildContextOptions = {},
96
+ ): ChildContextComposition {
97
+ const profile = options.profile ?? 'default';
98
+ const budgetBytes = profile === 'small' ? SMALL_CHILD_CONTEXT_TOKEN_BUDGET : null;
99
+ const measurements = emptyMeasurements();
100
+ const included: Partial<Record<ChildContextComponentName, string>> = {};
101
+ const order: readonly ChildContextComponentName[] = profile === 'small'
102
+ ? ['systemPrompt', 'toolSchemas', 'goal', 'ledger', 'agentsMd', 'claudeMd']
103
+ : CHILD_CONTEXT_COMPONENT_NAMES;
104
+ let usedBytes = 0;
105
+ let includedCount = 0;
106
+
107
+ for (const name of order) {
108
+ const value = components[name] ?? '';
109
+ const inputBytes = utf8Bytes(value);
110
+ const separatorBytes = includedCount > 0 && value.length > 0 ? utf8Bytes(JOINER) : 0;
111
+ const availableBytes = budgetBytes === null ? Number.POSITIVE_INFINITY : Math.max(0, budgetBytes - usedBytes - separatorBytes);
112
+ const includedValue = inputBytes <= availableBytes ? value : prefixWithinUtf8Budget(value, availableBytes);
113
+ const includedBytes = utf8Bytes(includedValue);
114
+ measurements[name] = { inputBytes, includedBytes, truncated: includedBytes < inputBytes };
115
+ if (includedValue.length === 0) continue;
116
+ included[name] = includedValue;
117
+ usedBytes += separatorBytes + includedBytes;
118
+ includedCount += 1;
119
+ }
120
+
121
+ const text = CHILD_CONTEXT_COMPONENT_NAMES
122
+ .flatMap((name) => included[name] === undefined ? [] : [included[name]!])
123
+ .join(JOINER);
124
+ const tokenUpperBound = utf8Bytes(text);
125
+ const result: ChildContextComposition = {
126
+ text,
127
+ profile,
128
+ budgetTokens: budgetBytes === null ? null : SMALL_CHILD_CONTEXT_TOKEN_BUDGET,
129
+ tokenUpperBound,
130
+ components: measurements,
131
+ };
132
+ emitCompositionEvent(options.log, {
133
+ profile,
134
+ budgetTokens: result.budgetTokens,
135
+ tokenUpperBound,
136
+ components: measurements,
137
+ });
138
+ return result;
139
+ }
@@ -11,9 +11,12 @@ import type {
11
11
  import { DEFAULT_BUDGET_GATE } from '../user-config.js';
12
12
  import { debug } from '../debug/log.js';
13
13
 
14
+ /** 체인 한 칸. `codexCredits` = `llm.fallbackChain` 의 `codex-credits` 칸(codex 를 선불 크레딧으로 · 마지막 수단). */
15
+ export type ChildLlmChainSlot = ChildLlmChainEntry & { readonly codexCredits?: true };
16
+
14
17
  export interface ResolvedChildLlmPreference {
15
18
  mode: ChildLlmPreferenceMode;
16
- chain: ChildLlmChainEntry[];
19
+ chain: ChildLlmChainSlot[];
17
20
  budgetGate: {
18
21
  minHeadroomPercent: number;
19
22
  onShortfall: BudgetGateOnShortfall;
@@ -27,6 +30,8 @@ export type ChildLlmPreferenceInput = Pick<UserConfig, 'tools' | 'llm'> & Partia
27
30
  const FALLBACK_NAME_TO_PROVIDER: Readonly<Record<string, string>> = {
28
31
  'codex-rotate': 'openai-codex',
29
32
  grok: 'grok',
33
+ // CODEX-CREDITS-STEP — provider 는 같은 codex 지만 «크레딧 칸»이라 따로 센다(아래 중복 판정).
34
+ 'codex-credits': 'openai-codex',
30
35
  };
31
36
 
32
37
  function warnPreference(detail: string): void {
@@ -39,15 +44,18 @@ function warnPreference(detail: string): void {
39
44
  }
40
45
 
41
46
  /** `llm.fallbackChain` 이름을 자식 프로바이더 칸으로 옮긴다. 모르는 이름은 경고하고 건너뛴다. */
42
- export function chainFromFallbackNames(names: readonly string[] | undefined): ChildLlmChainEntry[] {
43
- const chain: ChildLlmChainEntry[] = [];
47
+ export function chainFromFallbackNames(names: readonly string[] | undefined): ChildLlmChainSlot[] {
48
+ const chain: ChildLlmChainSlot[] = [];
44
49
  for (const name of names ?? []) {
45
50
  const provider = FALLBACK_NAME_TO_PROVIDER[name];
46
51
  if (!provider) {
47
52
  warnPreference(`llm.fallbackChain 의 모르는 이름 '${name}' 을 건너뜀`);
48
53
  continue;
49
54
  }
50
- if (!chain.some((entry) => entry.provider === provider)) chain.push({ provider });
55
+ const codexCredits = name === 'codex-credits';
56
+ if (!chain.some((entry) => entry.provider === provider && (entry.codexCredits === true) === codexCredits)) {
57
+ chain.push(codexCredits ? { provider, codexCredits: true } : { provider });
58
+ }
51
59
  }
52
60
  return chain;
53
61
  }
@@ -60,7 +68,7 @@ export function resolveChildLlmPreference(config: ChildLlmPreferenceInput): Reso
60
68
  const mode: ChildLlmPreferenceMode = explicitMode ?? (provider ? 'pinned' : 'auto');
61
69
  const modeSource: ResolvedChildLlmPreference['source']['mode'] = explicitMode ? 'explicit' : 'inferred';
62
70
 
63
- let chain: ChildLlmChainEntry[];
71
+ let chain: ChildLlmChainSlot[];
64
72
  let chainSource: ResolvedChildLlmPreference['source']['chain'];
65
73
  if (mode === 'pinned') {
66
74
  // 계정 회전은 여기 없다. provider 없는 model 은 pinned 가 아니다.
@@ -0,0 +1,67 @@
1
+ // ── CLAUDE-IMPL-LANE — `--child-llm-provider claude-acp` 의 구현 실행 (호스트 레인 · 구독 ACP) ──
2
+ //
3
+ // claude-acp 는 «LLM provider» 가 아니라 구현 실행기 선택이다. 이 갈래는 implement seam 이 PTY 자식
4
+ // (`elanous chat` · getProvider) 대신 부른다 — PTY 자식으로 보내면 getProviderForConfig 가 모델 이름(claude-…)을
5
+ // 보고 anthropic(API 키 provider)으로 풀어 «구독» 런이 API 로 과금된다(10-11 #26277 보류 판독).
6
+ //
7
+ // fail-closed: 구독 확인 실패 · 사용률 미측정 · 상한(기본 85%) 이상 · 프록시 주소 없음이면 거부하고
8
+ // 다른 공급자로 조용히 새지 않는다(거부 사유는 관측 `self-implement.claude-acp` `refused`).
9
+ import { debug } from '../debug/log.js';
10
+ import { makeAcpImplementer, type AcpImplementerOpts, type AcpImplementerResult } from '../agent-substrate/acp-implementer.js';
11
+ import { subscriptionReviewerAvailability, type SubscriptionReviewerDeps } from '../self-dev/review-provider-fallback.js';
12
+
13
+ /** 구현 레인의 구독 사용률 상한(%) — 대표 10-11 «Claude 구독(ACP) = 계정 둘 · 85 까지». 리뷰어 상한(95)과 별개 값. */
14
+ export const CLAUDE_ACP_IMPLEMENT_CAP_DEFAULT = 85;
15
+ export const CLAUDE_ACP_PROVIDER = 'claude-acp';
16
+
17
+ export function isClaudeAcpChild(childLlm: { provider?: string } | undefined): boolean {
18
+ return childLlm?.provider?.trim().toLowerCase() === CLAUDE_ACP_PROVIDER;
19
+ }
20
+
21
+ /** 상한은 `ELANOUS_CLAUDE_ACP_CAP`(0~100)로만 바꾼다 — 잘못된 값은 기본 85(올려 잡지 않는다). */
22
+ export function claudeAcpImplementCap(env: NodeJS.ProcessEnv = process.env): number {
23
+ const raw = env.ELANOUS_CLAUDE_ACP_CAP?.trim();
24
+ const n = raw ? Number(raw) : Number.NaN;
25
+ return Number.isFinite(n) && n >= 0 && n <= 100 ? n : CLAUDE_ACP_IMPLEMENT_CAP_DEFAULT;
26
+ }
27
+
28
+ export type ClaudeAcpImplementOutcome =
29
+ | { kind: 'refused'; reason: string }
30
+ | { kind: 'ran'; result: AcpImplementerResult };
31
+
32
+ export interface ClaudeAcpImplementInput {
33
+ cwd: string;
34
+ prompt: string;
35
+ model?: string;
36
+ runId?: string;
37
+ env?: NodeJS.ProcessEnv;
38
+ /** 시험 주입 — 구독 확인·상태 읽기. */
39
+ availabilityDeps?: SubscriptionReviewerDeps;
40
+ /** 시험 주입 — ACP 에이전트 생성기(makeAcpImplementer 로 그대로 넘긴다). */
41
+ createAgent?: AcpImplementerOpts['createAgent'];
42
+ timeoutMs?: number;
43
+ }
44
+
45
+ export async function runClaudeAcpImplement(input: ClaudeAcpImplementInput): Promise<ClaudeAcpImplementOutcome> {
46
+ const env = input.env ?? process.env;
47
+ const cap = claudeAcpImplementCap(env);
48
+ const availability = subscriptionReviewerAvailability({ provider: CLAUDE_ACP_PROVIDER }, cap, env, input.availabilityDeps ?? {});
49
+ if (!availability.spawn) {
50
+ const reason = availability.reason ?? 'Claude subscription unavailable';
51
+ debug.log('self-implement.claude-acp', 'refused', { reason, cap, ...(input.runId ? { runId: input.runId } : {}) }, { level: 'warn' });
52
+ return { kind: 'refused', reason };
53
+ }
54
+ const proxyBaseUrl = availability.spawn.env.ANTHROPIC_BASE_URL;
55
+ debug.log('self-implement.claude-acp', 'start', { cap, proxy: proxyBaseUrl !== undefined, model: input.model ?? '(default)', ...(input.runId ? { runId: input.runId } : {}) });
56
+ const implement = makeAcpImplementer({
57
+ worktree: input.cwd,
58
+ env,
59
+ ...(proxyBaseUrl ? { proxyBaseUrl } : {}),
60
+ ...(input.model ? { model: input.model } : {}),
61
+ ...(input.timeoutMs !== undefined ? { timeoutMs: input.timeoutMs } : {}),
62
+ ...(input.createAgent ? { createAgent: input.createAgent } : {}),
63
+ });
64
+ const result = await implement(input.prompt);
65
+ debug.log('self-implement.claude-acp', 'done', { chars: result.text.length, allowed: result.allowed, denied: result.denied, ...(input.runId ? { runId: input.runId } : {}) });
66
+ return { kind: 'ran', result };
67
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 재작업 라운드 관측 신호 넷 — 관측 전용 순수 계산(FLEX-RFC §A-①).
3
+ *
4
+ * ⛔ 판정·분기·오버레이 상태를 만들지 않는다. 호출자는 결과를 원장에 «남기기만» 한다.
5
+ * ⛔ 모르는 값은 0 으로 지어내지 않고 키를 뺀다(key-absent) — 0 은 «측정했더니 없었다»의 값이다.
6
+ */
7
+
8
+ export interface FlexSignalsInput {
9
+ /** 오래된 라운드 → 최신 라운드 순의 게이트 실패 id 목록. `undefined`·빈 목록은 «실패 id 를 못 쟀다». */
10
+ gateFailedIdsByRound: readonly (readonly string[] | undefined)[];
11
+ /** 최신 게이트 실패들의 base 귀속(`preexisting`·`introduced`·`unknown` …). 못 쟀으면 `undefined`. */
12
+ failureAttributions?: readonly string[] | undefined;
13
+ /** 지금 orchestrator 가 알고 있는 review_repeat 값 그대로. */
14
+ reviewRepeat?: number | undefined;
15
+ /** 지금까지 쓴 라운드 수. */
16
+ round: number;
17
+ /** 계획된 라운드 상한(effectiveMax). 모르면 `undefined`. */
18
+ plannedRounds?: number | undefined;
19
+ }
20
+
21
+ export interface FlexSignals {
22
+ fail_signature_repeat?: number;
23
+ base_reproduce?: number;
24
+ review_repeat?: number;
25
+ budget_burn?: number;
26
+ }
27
+
28
+ function failureKey(ids: readonly string[] | undefined): string | undefined {
29
+ return ids?.length ? JSON.stringify([...new Set(ids)].sort()) : undefined;
30
+ }
31
+
32
+ /** 최신 라운드의 실패 id 집합이 그 이전 라운드들에서 «같은 집합»으로 나온 횟수. 최신 집합을 모르면 `undefined`. */
33
+ function failSignatureRepeat(rounds: FlexSignalsInput['gateFailedIdsByRound']): number | undefined {
34
+ if (rounds.length === 0) return undefined;
35
+ const latest = failureKey(rounds[rounds.length - 1]);
36
+ if (latest === undefined) return undefined;
37
+ return rounds.slice(0, -1).filter((ids) => failureKey(ids) === latest).length;
38
+ }
39
+
40
+ /** base 에서도 재현된(`preexisting`) 실패 수. 귀속이 비었거나 하나라도 `unknown` 이면 `undefined`. */
41
+ function baseReproduce(attributions: FlexSignalsInput['failureAttributions']): number | undefined {
42
+ if (attributions === undefined || attributions.length === 0) return undefined;
43
+ if (attributions.includes('unknown')) return undefined;
44
+ return attributions.filter((attribution) => attribution === 'preexisting').length;
45
+ }
46
+
47
+ export function computeFlexSignals(input: FlexSignalsInput): FlexSignals {
48
+ const repeat = failSignatureRepeat(input.gateFailedIdsByRound);
49
+ const reproduce = baseReproduce(input.failureAttributions);
50
+ const burn = input.plannedRounds !== undefined && input.plannedRounds > 0
51
+ ? input.round / input.plannedRounds
52
+ : undefined;
53
+ return {
54
+ ...(repeat === undefined ? {} : { fail_signature_repeat: repeat }),
55
+ ...(reproduce === undefined ? {} : { base_reproduce: reproduce }),
56
+ ...(input.reviewRepeat === undefined ? {} : { review_repeat: input.reviewRepeat }),
57
+ ...(burn === undefined ? {} : { budget_burn: burn }),
58
+ };
59
+ }
@@ -5,7 +5,7 @@ import { dirname, join, resolve } from 'node:path';
5
5
  import { effectiveInstanceRoot, prodInstanceRoot } from '../instance/resolve.js';
6
6
  import { debug } from '../debug/log.js';
7
7
  import { enterJourneyNode, exitJourneyNode, passJourneyNode, withJourneyNodeSync } from '../self-dev/graph-journey-nodes.js';
8
- import { routeJourneyEdge } from '../self-dev/graph-journey-route.js';
8
+ import { freezeCheckHolds, routeJourneyEdge } from '../self-dev/graph-journey-route.js';
9
9
  import { beginLandingMerge, type LandingFreeze } from '../release-loop/landing-freeze.js';
10
10
 
11
11
  /** `manual`: the owning repository could not be determined — a sweep never resumes it; a person does. */
@@ -194,8 +194,13 @@ export function admitLandingMerge(
194
194
  () => admitLandingMergeUnobserved(heldEntry, root, hooks, claimKey, opts));
195
195
  const freezeLabel = admission.kind === 'merge' ? 'open' : admission.kind === 'held' ? 'frozen' : 'taken';
196
196
  exitJourneyNode('freeze-check', { provenance: 'admit-landing-merge', outcome: freezeLabel, ...(pr === undefined ? {} : { data: { pr } }) });
197
- // 2판: 간선 판독을 남긴다(관측). 승인 요구(open→hold) 강제는 0.2.22 — 보류 사유 값이 먼저다(RFC §3).
198
- routeJourneyEdge('freeze-check', freezeLabel, { freeze: freezeLabel });
197
+ const route = routeJourneyEdge('freeze-check', freezeLabel, { freeze: freezeLabel });
198
+ // 그래프가 열린 병합을 hold 로 보내면(launch 오버레이의 «승인 요구») 병합하지 않고 보류한다. 답이 없으면 오늘 판정 그대로.
199
+ if (admission.kind === 'merge' && freezeCheckHolds(freezeLabel, route)) {
200
+ admission.end();
201
+ passJourneyNode('hold', { provenance: 'admit-landing-merge', outcome: 'graph-hold', ...(pr === undefined ? {} : { data: { pr } }) });
202
+ return { kind: 'held', freeze: { reason: 'graph freeze-check → hold', startedAt: new Date().toISOString(), until: null, by: 'graph' } };
203
+ }
199
204
  return admission;
200
205
  }
201
206
 
@@ -5,6 +5,7 @@ import { llmDecomposeSteps } from '../harness/llm-decompose.js';
5
5
  import { GOAL_AUTHOR_COARSE_MAX_SLICES } from '../task-orchestrator/generator-prompt.js';
6
6
  import { getDefaultLogStore } from '../mss/logging/log-store.js';
7
7
  import { emitDecision } from '../live/detail-switch.js';
8
+ import { selectPlanningLlm } from '../prompt-enhance/enhance.js';
8
9
  import type { GoalAuthorDeps } from './goal-author.js';
9
10
 
10
11
  const GOAL_STEPS_DECOMPOSED_EVENT = 'goal-steps-decomposed';
@@ -64,10 +65,17 @@ export function createGoalAuthorDecomposeSteps(
64
65
  ): NonNullable<GoalAuthorDeps['decomposeSteps']> {
65
66
  return async (objective, options) => {
66
67
  const { streamLLM } = await import('../llm.js');
68
+ // ⭐ AUTHOR-DECOMPOSE-KNOB(0.2.24 · G7 #25848) — 쪼개기·적대 비평의 두 streamLLM 호출이 인핸서와
69
+ // «같은 규칙»으로 planning 노브(roleLlm.planning ⊕ ELANOUS_PROMPT_ENHANCE_MODEL/EFFORT)를 따른다.
70
+ // 미설정이면 종전 그대로 — model 은 아예 전달하지 않는다(streamLLM 기본 모델) ⊕ effort 는 high.
71
+ const { model, effort } = selectPlanningLlm();
72
+ const llmOpts: { model?: string; reasoningEffort: 'low' | 'medium' | 'high' } = model === undefined
73
+ ? { reasoningEffort: effort }
74
+ : { model, reasoningEffort: effort };
67
75
  let decompositionRationale: string | undefined;
68
76
  const steps = await llmDecomposeSteps(
69
77
  objective,
70
- (input) => streamLLM([{ role: 'user', content: input.prompt }], () => {}, { reasoningEffort: 'high' }).then((text) => ({ text })),
78
+ (input) => streamLLM([{ role: 'user', content: input.prompt }], () => {}, llmOpts).then((text) => ({ text })),
71
79
  { ...(options?.context ? { context: options.context } : {}), maxTasks: GOAL_AUTHOR_COARSE_MAX_SLICES, promptProfile: 'goal-author-coarse', ...(signal ? { signal } : {}), onRationale: (rationale) => { decompositionRationale = rationale; } },
72
80
  );
73
81
  const heft = classifyHeft(objective);
@@ -120,7 +128,7 @@ export function createGoalAuthorDecomposeSteps(
120
128
  return finalSteps;
121
129
  };
122
130
  if (!reviewExecuted) return finalize(steps, decompositionRationale);
123
- const critic = (prompt: string) => streamLLM([{ role: 'user', content: prompt }], () => {}, { reasoningEffort: 'high' });
131
+ const critic = (prompt: string) => streamLLM([{ role: 'user', content: prompt }], () => {}, llmOpts);
124
132
 
125
133
  try {
126
134
  const critique = await adversarialPlanCritique(objective, steps, critic, options?.context);
@@ -1233,6 +1233,21 @@ function countAddressableLines(contents: string): number {
1233
1233
  }
1234
1234
 
1235
1235
  const TRACED_PATH_LIST_ITEM = /^\s*(?:[-*+]|\d+[.)])\s+(.+)$/;
1236
+ const TRACED_PATH_LEADING_LIST_MARKERS = /^(?:\s*(?:[-*+]|\d+[.)])\s+)+/;
1237
+ const TRACED_PATH_EVIDENCE_TAG = /^\[(?:code|skill|memory|doc|pty|unknown)\]\s+/;
1238
+ // 2026-10-11 — 저작기가 «목록 기호 두 겹»(`- - src/x.ts` · `- 1. src/x.ts` · `- [code] - src/x.ts`)을 쓰면
1239
+ // 한 겹만 벗긴 남은 «- src/…» 를 경로로 읽어 `traced path does not exist: - src/…` ERROR 를 냈다
1240
+ // (Pod 저작에서 자식이 결과 줄 없이 죽음 · 오늘 no-result-line 18건의 공용 원인). 기호·태그를 반복해서 벗긴다.
1241
+ function tracedPathListItem(line: string): string | undefined {
1242
+ const item = TRACED_PATH_LIST_ITEM.exec(line)?.[1];
1243
+ if (item === undefined) return undefined;
1244
+ let evidence = item;
1245
+ for (;;) {
1246
+ const next = evidence.replace(TRACED_PATH_LEADING_LIST_MARKERS, '').replace(TRACED_PATH_EVIDENCE_TAG, '');
1247
+ if (next === evidence) return evidence;
1248
+ evidence = next;
1249
+ }
1250
+ }
1236
1251
  // ⛔⭐⭐⭐ 2026-08-11 — 이 문면은 «통하지 않는 처방»을 첫 번째로 권하고 있었다.
1237
1252
  // 옛 문면: *"Re-authoring the same input may produce different evidence; **retry before changing the ask**."*
1238
1253
  // 📏 실측(두 트랙 합산): 그 말대로 «재저작»만 반복했더니 ***0/4***. ask 에 «식별자 앵커»를 넣자 ***2/2***.
@@ -1355,9 +1370,8 @@ export function tracedPathReferences(document: string): TracedPathReference[] {
1355
1370
  if (!section) return [];
1356
1371
  const references: TracedPathReference[] = [];
1357
1372
  for (const line of section.split(/\r?\n/)) {
1358
- const item = TRACED_PATH_LIST_ITEM.exec(line)?.[1];
1359
- if (!item) continue;
1360
- const evidence = item.replace(/^\[(?:code|skill|memory|doc|pty|unknown)\]\s+/, '');
1373
+ const evidence = tracedPathListItem(line);
1374
+ if (!evidence) continue;
1361
1375
  if (isTracedPathsEvidenceUnavailable(evidence) || isTracedPathExclusionDescription(evidence)) continue;
1362
1376
  // 설명 구분자는 **공백으로 둘러싼** em dash 또는 hyphen 뿐이다 — 파일명 안의 dash 를 설명 구분자로 오인하지 않는다.
1363
1377
  // 경로 칸 안의 복수 경로는 **공백으로 둘러싼** 가운뎃점만 본다 — 파일명 안의 `·` 를 가르지 않는다.
@@ -1367,10 +1381,12 @@ export function tracedPathReferences(document: string): TracedPathReference[] {
1367
1381
  for (const fragment of pathField.split(' · ')) {
1368
1382
  const trimmed = fragment.trim();
1369
1383
  if (!trimmed) continue;
1370
- const unwrapped = stripInlineCode(trimmed);
1384
+ // 10-11 재발 — 저작기가 «`- src/…`» 처럼 목록 기호를 백틱 «안»에 쓰면 바깥 기호만 벗긴 뒤 백틱을 벗겨
1385
+ // «- src/…» 가 경로로 남았다(AUTHOR-DECOMPOSE-KNOB · MODEL-BENCH-SCORES 부검). 백틱을 벗긴 뒤 한 번 더 벗긴다.
1386
+ const unwrapped = stripInlineCode(trimmed).replace(TRACED_PATH_LEADING_LIST_MARKERS, '');
1371
1387
  const match = TRACED_PATH_LINE.exec(unwrapped);
1372
1388
  // 두 번 벗긴다 — 줄번호가 백틱 **안**(`p:42`)일 수도 **밖**(`p`:42)일 수도 있다.
1373
- const path = match ? stripInlineCode(match[1].trim()) : undefined;
1389
+ const path = match ? stripInlineCode(match[1].trim()).replace(TRACED_PATH_LEADING_LIST_MARKERS, '') : undefined;
1374
1390
  if (!path) continue;
1375
1391
  references.push({ path, line: match?.[2] ? Number(match[2]) : null, endLine: match?.[3] ? Number(match[3]) : null });
1376
1392
  }
@@ -1461,9 +1477,8 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
1461
1477
  const { prohibitionSymbolStartingLineCount, permissionSymbolStartingLineCount, mixedSymbolLineCount } = symbolStartingLineCounts(document);
1462
1478
  const { exhaustiveRequestWordingCount, blanketBehaviorPreservationCount, namedPreservationTargetCount, removalFormDecisionConditionCount } = goalAuthorKnownShapeCounts(ask);
1463
1479
  const tracedPathExclusionCount = (markdownSection(document, 'TRACED PATHS')?.split(/\r?\n/) ?? [])
1464
- .map((line) => TRACED_PATH_LIST_ITEM.exec(line)?.[1])
1465
- .filter((item): item is string => item !== undefined
1466
- && isTracedPathExclusionDescription(item.replace(/^\[(?:code|skill|memory|doc|pty|unknown)\]\s+/, ''))).length;
1480
+ .map((line) => tracedPathListItem(line))
1481
+ .filter((item): item is string => item !== undefined && isTracedPathExclusionDescription(item)).length;
1467
1482
  const selfQuestionSubjectViolations = detectSelfQuestionSubjectViolations(document);
1468
1483
  const launchDeclarationClassification = artifactLaunchDeclarationClassification(document);
1469
1484
  const headings = canonicalBlocks(document);
@@ -1694,7 +1709,7 @@ export function lintGoalFile(document: string, branch: string, deps: GoalFileLin
1694
1709
  });
1695
1710
  }
1696
1711
  const unavailableTracedPathEvidence = markdownSection(document, 'TRACED PATHS')?.split(/\r?\n/)
1697
- .map((line) => TRACED_PATH_LIST_ITEM.exec(line)?.[1])
1712
+ .map((line) => tracedPathListItem(line))
1698
1713
  .find((item): item is string => item !== undefined && isTracedPathsEvidenceUnavailable(item));
1699
1714
  if (unavailableTracedPathEvidence) {
1700
1715
  const requiresTracedPaths = goalType === null || goalType === 'implement';
@@ -1,3 +1,5 @@
1
+ import type { RunStopClass } from './run-stop.js';
2
+
1
3
  /** ⭐ RFC §5 «4단계» — `applies_when` 을 «코드»가 판정한다.
2
4
  *
3
5
  * ⛔⭐ **표현식 엔진을 만들지 않는다.** 문법이 넓어지면 오버레이가 «작은 프로그램»이 되고,
@@ -31,6 +33,32 @@ export function parseOverlayCondition(source: string): OverlayConditionParse {
31
33
  return { ok: true, condition: { key: key!, operator: operator as OverlayConditionOperator, literal } };
32
34
  }
33
35
 
36
+ /** Inputs known at a runtime overlay decision. Every field except `attempts` may be unknown. */
37
+ export interface RuntimeOverlayStateInput {
38
+ readonly attempts: number;
39
+ readonly goalId?: string;
40
+ readonly stopClass?: RunStopClass;
41
+ readonly reviewRepeat?: number;
42
+ /** Only a real boolean is a measurement. */
43
+ readonly siblingOverlap?: unknown;
44
+ readonly mainSyncConflict?: unknown;
45
+ /** Only a non-empty string is a measurement. */
46
+ readonly authorGrade?: unknown;
47
+ }
48
+
49
+ /** ⛔ 아는 값만 싣는다 — 모르면 키를 «뺀다». 0·false·빈 문자열을 지어내지 않는다. */
50
+ export function buildRuntimeOverlayState(input: RuntimeOverlayStateInput): Record<string, unknown> {
51
+ return {
52
+ attempts: input.attempts,
53
+ ...(input.goalId === undefined ? {} : { goal_id: input.goalId }),
54
+ ...(input.stopClass === undefined ? {} : { stop_class: input.stopClass }),
55
+ ...(typeof input.reviewRepeat === 'number' && Number.isFinite(input.reviewRepeat) ? { review_repeat: input.reviewRepeat } : {}),
56
+ ...(typeof input.siblingOverlap === 'boolean' ? { sibling_overlap: input.siblingOverlap } : {}),
57
+ ...(typeof input.mainSyncConflict === 'boolean' ? { main_sync_conflict: input.mainSyncConflict } : {}),
58
+ ...(typeof input.authorGrade === 'string' && input.authorGrade.length > 0 ? { author_grade: input.authorGrade } : {}),
59
+ };
60
+ }
61
+
34
62
  export type OverlayConditionVerdict =
35
63
  /** 조건이 참이다 — 얹는다. */
36
64
  | { readonly kind: 'applies' }
@@ -1,7 +1,7 @@
1
1
  import { readFileSync, readdirSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
- import { parse as parseYaml } from 'yaml';
4
+ import { parse as parseYaml, parseAllDocuments } from 'yaml';
5
5
  import { inspectPipelineGraph, type PipelineGraphDefect } from './pipeline-shape.js';
6
6
  import { edgeMapOf, type GraphParseIssue, type GraphTemplateSpec } from './graph-yaml.js';
7
7
  import { evaluateOverlayCondition } from './graph-overlay-condition.js';
@@ -15,7 +15,8 @@ import type { OverlayPatch } from './graph-overlay.js';
15
15
  * ⑴ 얹은 «결과»에 위상 검사를 다시 건다 — 노드 집합이 그대로여도 도달 가능성은 바뀐다
16
16
  * ⑵ 계약이 빠진 노드를 «거부»한다 — 권한 없는 노드가 조용히 들어오는 것을 막는다
17
17
  * ⑶ 무엇을 얹었는지를 «값»으로 낸다 — 사후에 「왜 이 걸음인가」를 답하려면 필요하다 */
18
- export type OverlayStage = 'launch' | 'runtime';
18
+ /** `heal` = 멈춤 갈래 사다리 전용 — `stop-autoheal.ts` 만 고른다. 실행기의 launch·runtime 선택에는 «안» 잡힌다. */
19
+ export type OverlayStage = 'launch' | 'runtime' | 'heal';
19
20
 
20
21
  export interface OverlayPatchOp {
21
22
  readonly op: 'add' | 'replace' | 'remove';
@@ -33,19 +34,52 @@ export interface GraphOverlaySpec {
33
34
  }
34
35
 
35
36
  export interface OverlayParseResult {
37
+ /** 첫 오버레이 — 한 파일에 하나만 둔 기존 선언의 읽기 자리. */
36
38
  readonly overlay?: GraphOverlaySpec;
39
+ /** 파일이 선언한 오버레이 전부. `---` 로 문서를 나누면 한 파일에 여럿이다. 오류가 하나라도 있으면 비어 있다. */
40
+ readonly overlays: readonly GraphOverlaySpec[];
37
41
  readonly errors: readonly GraphParseIssue[];
38
42
  }
39
43
 
40
- const STAGES = new Set<OverlayStage>(['launch', 'runtime']);
44
+ const STAGES = new Set<OverlayStage>(['launch', 'runtime', 'heal']);
41
45
  const OPS = new Set(['add', 'replace', 'remove']);
42
46
 
43
- /** ⛔ 던지지 않는다 — 파서 계약은 `graph-yaml.ts` 와 같다. */
47
+ /** ⛔ 던지지 않는다 — 파서 계약은 `graph-yaml.ts` 와 같다.
48
+ * 한 파일 = 문서 하나가 기본이다. `---` 로 나눈 여러 문서는 문서마다 오버레이 하나로 읽는다
49
+ * (오류 경로는 `<label>#<문서 번호>`). 문서가 하나면 읽는 법도 오류 문면도 예전과 같다. */
44
50
  export function parseGraphOverlayYaml(source: string, label = '<inline>'): OverlayParseResult {
45
- const errors: GraphParseIssue[] = [];
46
51
  let raw: unknown;
47
52
  try { raw = parseYaml(source); }
48
- catch (error) { return { errors: [{ path: label, message: `YAML 파싱 실패: ${error instanceof Error ? error.message : String(error)}` }] }; }
53
+ catch (error) {
54
+ const documents = parseAllDocuments(source);
55
+ if (documents.length > 1 && documents.every((document) => document.errors.length === 0)) {
56
+ return overlaysFromDocuments(documents.map((document) => document.toJS() as unknown), label);
57
+ }
58
+ return { overlays: [], errors: [{ path: label, message: `YAML 파싱 실패: ${error instanceof Error ? error.message : String(error)}` }] };
59
+ }
60
+ const parsed = overlayFromRaw(raw, label);
61
+ return { ...(parsed.overlay ? { overlay: parsed.overlay } : {}), overlays: parsed.overlay ? [parsed.overlay] : [], errors: parsed.errors };
62
+ }
63
+
64
+ function overlaysFromDocuments(raws: readonly unknown[], label: string): OverlayParseResult {
65
+ const errors: GraphParseIssue[] = [];
66
+ const overlays: GraphOverlaySpec[] = [];
67
+ for (const [index, raw] of raws.entries()) {
68
+ const parsed = overlayFromRaw(raw, `${label}#${index}`);
69
+ errors.push(...parsed.errors);
70
+ if (parsed.overlay) overlays.push(parsed.overlay);
71
+ }
72
+ const seen = new Set<string>();
73
+ for (const overlay of overlays) {
74
+ if (seen.has(overlay.overlayId)) errors.push({ path: `${label}/overlay_id`, message: `overlay_id '${overlay.overlayId}' 가 한 파일에서 중복이다` });
75
+ seen.add(overlay.overlayId);
76
+ }
77
+ if (errors.length > 0) return { overlays: [], errors };
78
+ return { overlay: overlays[0]!, overlays, errors };
79
+ }
80
+
81
+ function overlayFromRaw(raw: unknown, label: string): { overlay?: GraphOverlaySpec; errors: GraphParseIssue[] } {
82
+ const errors: GraphParseIssue[] = [];
49
83
  if (raw === null || typeof raw !== 'object') return { errors: [{ path: label, message: 'YAML 최상위가 맵이 아니다' }] };
50
84
  const doc = raw as Record<string, unknown>;
51
85
 
@@ -198,7 +232,7 @@ export function loadGraphOverlays(dir: string): OverlayLoadResult {
198
232
  catch (error) { errors.push({ path: file, message: `읽기 실패: ${error instanceof Error ? error.message : String(error)}` }); continue; }
199
233
  const result = parseGraphOverlayYaml(source, file);
200
234
  errors.push(...result.errors);
201
- if (result.overlay) overlays.push(result.overlay);
235
+ overlays.push(...result.overlays);
202
236
  }
203
237
  return { overlays, errors, scannedFiles: files.length };
204
238
  }
@@ -23,6 +23,7 @@ export interface GraphTemplateNode {
23
23
  readonly kind: GraphNodeSpec['kind'];
24
24
  /** 그 노드의 재방문 상한. ⛔ 지금의 rework 상한이 «여기로 올라올» 자리다(RFC §4.1). */
25
25
  readonly maxVisits: number;
26
+ readonly journeyStage?: string;
26
27
  /** YAML recipe identifier; existing orchestrator does not execute it. */
27
28
  readonly recipe?: string;
28
29
  /** YAML이 선언한 선택 계약. 관측용이며 실행 권한을 강제하지 않는다. */
@@ -34,6 +35,7 @@ export interface GraphTemplate {
34
35
  readonly version: string;
35
36
  readonly entryNode: string;
36
37
  readonly terminalNodes: readonly string[];
38
+ readonly journeyStages?: readonly string[];
37
39
  /** YAML 템플릿이 선언한 문서 전용 변경의 gate 건너뛰기 정책. */
38
40
  readonly docsOnlyGateSkip?: boolean;
39
41
  /** 선언된 런 계약(선택) — 런 시작 때 발사 인자·부모 계약과 함께 한 번 해석된다. */
@@ -104,7 +106,7 @@ export function defaultGraphsDir(): string {
104
106
  return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'graphs');
105
107
  }
106
108
 
107
- export type DiscardedGraphNodeField = Exclude<keyof GraphNodeSpec, 'nodeId' | 'kind' | 'maxVisits' | 'recipe' | 'contract'>;
109
+ export type DiscardedGraphNodeField = Exclude<keyof GraphNodeSpec, 'nodeId' | 'kind' | 'maxVisits' | 'journeyStage' | 'recipe' | 'contract'>;
108
110
 
109
111
  export interface GraphTemplateCompilation {
110
112
  readonly template: GraphTemplate;
@@ -129,12 +131,14 @@ export function compileGraphTemplate(spec: GraphTemplateSpec): GraphTemplateComp
129
131
  version: String(spec.version),
130
132
  entryNode: spec.entryNode,
131
133
  terminalNodes: spec.terminalNodes,
134
+ ...(spec.journeyStages === undefined ? {} : { journeyStages: spec.journeyStages }),
132
135
  ...(spec.docsOnlyGateSkip !== undefined ? { docsOnlyGateSkip: spec.docsOnlyGateSkip } : {}),
133
136
  ...(spec.runContract !== undefined ? { runContract: spec.runContract } : {}),
134
137
  nodes: spec.nodes.map((n) => ({
135
138
  nodeId: n.nodeId,
136
139
  kind: n.kind,
137
140
  maxVisits: n.maxVisits,
141
+ ...(n.journeyStage === undefined ? {} : { journeyStage: n.journeyStage }),
138
142
  ...(n.recipe === undefined ? {} : { recipe: n.recipe }),
139
143
  ...(n.contract === undefined ? {} : { contract: n.contract }),
140
144
  })),
@@ -324,6 +328,34 @@ export function inspectAllTemplates(): Array<{ graphId: string; defects: Pipelin
324
328
  .map((t) => ({ graphId: t.graphId, defects: inspectPipelineGraph(t.edges, t.entryNode) }));
325
329
  }
326
330
 
331
+ /** L0 ratios are distinct: stages represented by nodes, and nodes accounted for by this run. */
332
+ export function measureL0Coverage(
333
+ template: GraphTemplate,
334
+ enteredNodes: Iterable<string>,
335
+ skippedNodes: Iterable<string>,
336
+ ): {
337
+ declarationCoverage: number | null;
338
+ traversalCoverage: number | null;
339
+ declaredStageCount: number;
340
+ listedStageCount: number;
341
+ accountedNodeCount: number;
342
+ declaredNodeCount: number;
343
+ } {
344
+ const stages = template.journeyStages ?? [];
345
+ const declaredStages = new Set(template.nodes.map((node) => node.journeyStage).filter((stage): stage is string => stage !== undefined && stages.includes(stage)));
346
+ const accounted = new Set([...enteredNodes, ...skippedNodes]);
347
+ const declaredNodeCount = template.nodes.length;
348
+ const accountedNodeCount = template.nodes.filter((node) => accounted.has(node.nodeId)).length;
349
+ return {
350
+ declarationCoverage: stages.length ? declaredStages.size / stages.length : null,
351
+ traversalCoverage: declaredNodeCount ? accountedNodeCount / declaredNodeCount : null,
352
+ declaredStageCount: declaredStages.size,
353
+ listedStageCount: stages.length,
354
+ accountedNodeCount,
355
+ declaredNodeCount,
356
+ };
357
+ }
358
+
327
359
  /** ⭐ 원장에 실을 «그래프 신원» — ⛔ 옛 TS 상수가 아니라 «지금 도는 템플릿»을 따른다.
328
360
  *
329
361
  * 🩸 계기(2026-09-08): 승격을 켠 research 런이 `investigate` 를 밟았는데 원장의