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,175 @@
1
+ import { debug } from '../debug/log.js';
2
+ import { parentPort, Worker } from 'node:worker_threads';
3
+ import { collectOverview, type CollectOptions } from './overview-collect.js';
4
+ import { computeVerdict, unmeasured, okCell, type OverviewSnapshot, type PodMemberRow } from './overview.js';
5
+ import { effectiveInstanceRoot } from '../instance/resolve.js';
6
+ import { getElanousConfigDirOverride, setElanousConfigDir } from '../elanous-config-dir.js';
7
+
8
+ const SNAPSHOT_MS = 10_000;
9
+ const POD_MS = 60_000;
10
+ const POD_STALE_MS = 2 * POD_MS;
11
+ /** Bounds for distinct (version, owner, since) keys: memo entries kept and collections in flight. */
12
+ const MAX_MEMO = 32;
13
+ const MAX_PENDING = 2;
14
+
15
+ type PodReading = { context: string; occupied: number | null; reason: string | null; observedAt: number };
16
+ type OverviewData = OverviewSnapshot & ReturnType<typeof computeVerdict> & { cacheAgeMs: number; durationMs: number; sources: string[]; refreshFailed: string | null };
17
+ type WorkerMessage = ({ kind: 'pods' } | { kind: 'snapshot'; options: CollectOptions }) & { configDir?: string };
18
+
19
+ /** The kubectl-capable grid reader lives in a worker, never on the HTTP event loop. */
20
+ const port = parentPort;
21
+ if (port) {
22
+ port.on('message', async (message: WorkerMessage) => {
23
+ try {
24
+ // A worker does not share the main thread's module state: carry the daemon's universe in.
25
+ if (message.configDir) setElanousConfigDir(message.configDir);
26
+ if (message.kind === 'pods') {
27
+ const { buildGridData } = await import('../nexus/api/grid.js');
28
+ const rows = buildGridData().members.map((m) => ({ context: m.context, occupied: m.occupied, reason: m.reason, observedAt: Date.now() }));
29
+ port.postMessage({ rows });
30
+ } else port.postMessage({ snapshot: await collectOverview(message.options) });
31
+ } catch (error) {
32
+ port.postMessage({ error: error instanceof Error ? error.message : String(error) });
33
+ }
34
+ });
35
+ }
36
+
37
+ function workerRead<T>(message: WorkerMessage, timeoutMs: number, field: 'rows' | 'snapshot'): Promise<T> {
38
+ return new Promise((resolve, reject) => {
39
+ const worker = new Worker(new URL('./overview-cache.ts', import.meta.url));
40
+ let settled = false;
41
+ const finish = (error?: Error, result?: T) => {
42
+ if (settled) return;
43
+ settled = true;
44
+ clearTimeout(timeout);
45
+ void worker.terminate();
46
+ if (error) reject(error);
47
+ else resolve(result!);
48
+ };
49
+ const timeout = setTimeout(() => finish(new Error(`${field} 측정 시간 초과`)), timeoutMs);
50
+ worker.on('message', (result: { rows?: PodReading[]; snapshot?: OverviewSnapshot; error?: string }) => {
51
+ if (result.error) finish(new Error(result.error));
52
+ else finish(undefined, result[field] as T);
53
+ });
54
+ worker.on('error', (error) => finish(error instanceof Error ? error : new Error(String(error))));
55
+ const configDir = getElanousConfigDirOverride();
56
+ worker.postMessage({ ...message, ...(configDir ? { configDir } : {}) });
57
+ });
58
+ }
59
+
60
+ const measurePodsInWorker = () => workerRead<PodReading[]>({ kind: 'pods' }, 55_000, 'rows');
61
+ const collectInWorker = (options: CollectOptions) => workerRead<OverviewSnapshot>({ kind: 'snapshot', options: { ...options, root: options.root ?? effectiveInstanceRoot() } }, 30_000, 'snapshot');
62
+
63
+ function pendingSnapshot(at: number, since: string, reason: string): OverviewSnapshot {
64
+ const missing = <T>(name: string) => unmeasured<T>(name, reason);
65
+ return {
66
+ // Same resolution the collector uses; never the daemon's cwd.
67
+ generatedAt: new Date(at).toISOString(), instanceRoot: effectiveInstanceRoot(), since,
68
+ release: { version: missing('release.version'), run: missing('release.run'), schedule: missing('release.schedule'), checklist: missing('release.checklist') },
69
+ tasks: { byOwner: missing('tasks.byOwner'), done: missing('tasks.done'), launchable: missing('tasks.launchable'), capacity: missing('tasks.capacity') },
70
+ machines: { hosts: [], pods: [], parents: missing('machines.parents') },
71
+ landings: missing('landings'), loops: [],
72
+ };
73
+ }
74
+
75
+ export function createOverviewCache(deps: {
76
+ collect?: (opts: CollectOptions) => Promise<OverviewSnapshot>;
77
+ measurePods?: () => Promise<PodReading[]>;
78
+ now?: () => number;
79
+ schedule?: (tick: () => void, ms: number) => NodeJS.Timeout;
80
+ } = {}) {
81
+ const now = deps.now ?? Date.now;
82
+ const collect = deps.collect ?? collectInWorker;
83
+ const backgroundSnapshot = !deps.collect;
84
+ const measurePods = deps.measurePods ?? measurePodsInWorker;
85
+ const schedule = deps.schedule ?? ((tick: () => void, ms: number): NodeJS.Timeout => setInterval(tick, ms));
86
+ // The memo holds the collected snapshot only; Pod occupancy and the verdict are
87
+ // overlaid at response time so a failed or stale (120 s) measurement shows at once.
88
+ type Entry = { at: number; snapshot: OverviewSnapshot; durationMs: number; failure?: string; retryAt?: number };
89
+ const memo = new Map<string, Entry>();
90
+ const pending = new Map<string, Promise<void>>();
91
+ let podRows: PodReading[] = [];
92
+ let podFailure: string | null = null;
93
+ let podTimer: ReturnType<typeof setInterval> | null = null;
94
+ let podPending = false;
95
+
96
+ const refreshPods = async () => {
97
+ if (podPending) return;
98
+ podPending = true;
99
+ try { podRows = await measurePods(); podFailure = null; }
100
+ catch (error) { podRows = []; podFailure = error instanceof Error ? error.message : String(error); }
101
+ finally { podPending = false; }
102
+ };
103
+ const start = () => {
104
+ if (podTimer) return;
105
+ // The timer starts on the first authorized read; measurement never runs on the request stack.
106
+ podTimer = schedule(() => { void refreshPods(); }, POD_MS);
107
+ podTimer.unref();
108
+ };
109
+ const present = (entry: Entry): OverviewData => {
110
+ const at = now();
111
+ const byContext = new Map(podRows.map((row) => [row.context, row]));
112
+ const pods: PodMemberRow[] = entry.snapshot.machines.pods.map((pod) => {
113
+ const row = byContext.get(pod.context);
114
+ const stale = row && at - row.observedAt >= POD_STALE_MS;
115
+ const occupied = !podFailure && row && !stale && row.occupied !== null
116
+ ? okCell(row.occupied, 'pod pool background measure', new Date(row.observedAt).toISOString())
117
+ : unmeasured<number>('pod pool background measure', podFailure ?? (stale ? '낡음(120초)' : row?.reason ?? '측정 대기'), row ? new Date(row.observedAt).toISOString() : null);
118
+ return { ...pod, occupied };
119
+ });
120
+ const full = { ...entry.snapshot, machines: { ...entry.snapshot.machines, pods } };
121
+ return { ...full, ...computeVerdict(full), durationMs: entry.durationMs,
122
+ sources: [full.release.run.source, full.tasks.launchable.source, full.tasks.capacity.source],
123
+ cacheAgeMs: Math.max(0, at - entry.at), refreshFailed: entry.failure ?? null };
124
+ };
125
+ const get = async (options: Pick<CollectOptions, 'version' | 'owner'> & { sinceMs?: number; sinceLabel?: string } = {}): Promise<OverviewData> => {
126
+ start();
127
+ const key = JSON.stringify([options.version ?? null, options.owner ?? null, options.sinceMs ?? 3_600_000]);
128
+ const at = now();
129
+ // Drop expired entries of other keys, then cap the memo (oldest first).
130
+ for (const [other, entry] of memo) if (other !== key && !pending.has(other) && at - entry.at >= SNAPSHOT_MS && (entry.retryAt ?? 0) <= at) memo.delete(other);
131
+ const cached = memo.get(key);
132
+ // A failed refresh keeps the original collection time (true age) and only delays the retry.
133
+ if (cached && (at - cached.at < SNAPSHOT_MS || backgroundSnapshot && pending.has(key) || (cached.retryAt ?? 0) > at)) return present(cached);
134
+ let current = pending.get(key);
135
+ if (!current && pending.size >= MAX_PENDING) {
136
+ // Collections in flight are capped; answer from what we have instead of starting another worker.
137
+ const entry = cached ?? { at, snapshot: pendingSnapshot(at, options.sinceLabel ?? '최근 1h', '동시 수집 상한'), durationMs: 0 };
138
+ return present({ ...entry, failure: entry.failure ?? `동시 수집 상한(${MAX_PENDING})` });
139
+ }
140
+ if (!current) {
141
+ // Evict only when a new key is about to be stored, never on a hit for an existing key.
142
+ if (!memo.has(key)) while (memo.size >= MAX_MEMO) { const oldest = [...memo.keys()].find((other) => !pending.has(other)); if (!oldest) break; memo.delete(oldest); }
143
+ if (backgroundSnapshot && !cached) {
144
+ // Placeholder until the worker answers; it is not a collection.
145
+ memo.set(key, { at, snapshot: pendingSnapshot(at, options.sinceLabel ?? '최근 1h', '백그라운드 스냅숏 수집 대기'), durationMs: 0 });
146
+ }
147
+ current = (async () => {
148
+ const startAt = now();
149
+ const snapshot = await collect({ sinceMs: options.sinceMs ?? 3_600_000, sinceLabel: options.sinceLabel ?? '최근 1h',
150
+ ...(options.version ? { version: options.version } : {}), ...(options.owner ? { owner: options.owner } : {}), now: startAt });
151
+ // Age counts from when collection started (= snapshot.generatedAt), not when it finished.
152
+ const entry = { at: startAt, snapshot, durationMs: now() - startAt };
153
+ memo.set(key, entry);
154
+ const verdict = present(entry);
155
+ debug.log('ops.overview', 'verdict', { verdict: verdict.verdict, reasons: verdict.reasons, cacheAgeMs: verdict.cacheAgeMs, durationMs: entry.durationMs });
156
+ })();
157
+ pending.set(key, current);
158
+ void current.then(
159
+ () => { pending.delete(key); },
160
+ (error) => {
161
+ pending.delete(key);
162
+ debug.log('ops.overview', 'refresh-failed', { reason: error instanceof Error ? error.message : String(error) });
163
+ const prior = memo.get(key);
164
+ if (prior) memo.set(key, { ...prior, failure: error instanceof Error ? error.message : String(error), retryAt: now() + SNAPSHOT_MS });
165
+ },
166
+ );
167
+ }
168
+ if (!backgroundSnapshot) {
169
+ try { await current; }
170
+ catch (error) { if (!memo.has(key)) throw error; }
171
+ }
172
+ return present(memo.get(key)!);
173
+ };
174
+ return { get, memoSize: () => memo.size, stop: () => { if (podTimer) clearInterval(podTimer); podTimer = null; memo.clear(); } };
175
+ }
@@ -6,6 +6,8 @@
6
6
  * ⛔ «못 잼»은 `value:null` ⊕ `reason` 이다 — 절대 0 으로 읽지 않는다(판정도 그렇다).
7
7
  */
8
8
 
9
+ import { formatLandingThroughput, type LandingThroughput } from '../task-agent/cover.js';
10
+
9
11
  export type CellStatus = 'ok' | 'unmeasured' | 'partial';
10
12
  export interface Cell<T> {
11
13
  value: T | null;
@@ -84,6 +86,8 @@ export interface OverviewSnapshot {
84
86
  parents: Cell<ParentsInfo>;
85
87
  };
86
88
  landings: Cell<{ count: number; recent: string[] }>;
89
+ /** Optional until launch/PR/age sources are wired; existing snapshots keep their exact output. */
90
+ throughput?: Cell<LandingThroughput>;
87
91
  loops: LoopRow[];
88
92
  }
89
93
 
@@ -176,6 +180,7 @@ export function unmeasuredCells(s: OverviewSnapshot): string[] {
176
180
  for (const h of s.machines.hosts) visit(`machines.${h.name}`, h.load);
177
181
  for (const p of s.machines.pods) visit(`pods.${p.context}`, p.occupied);
178
182
  visit('machines.parents', s.machines.parents); visit('landings', s.landings);
183
+ if (s.throughput) visit('throughput', s.throughput);
179
184
  for (const l of s.loops) visit(`loops.${l.name}`, l.beat);
180
185
  return out;
181
186
  }
@@ -215,6 +220,9 @@ export function renderOverview(s: OverviewSnapshot, v: VerdictResult, meta: { el
215
220
  lines.push(` Pod ${s.machines.pods.map((p) => `${p.context} ${show(p.occupied, String)}/${p.capacity}`).join(' · ') || '못 잼(풀 설정 없음)'}`);
216
221
  lines.push(` 하니스 부모(이 기계) ${show(s.machines.parents, (p) => `${p.count} · 6h 넘음 ${p.staleOver6h}${p.oldestHours !== null ? ` · 최고 ${p.oldestHours}h` : ''}`)}`);
217
222
  lines.push(`착지 ${s.since} ${show(s.landings, (l) => `${l.count}`)} [${s.landings.source}]`);
223
+ if (s.throughput) {
224
+ lines.push(`${show(s.throughput, formatLandingThroughput)} [${s.throughput.source}]`);
225
+ }
218
226
  for (const l of s.loops) {
219
227
  lines.push(`루프 ${l.name}${l.era === 'seat-era' ? ' [자리 시대 원천]' : ''} ${show(l.beat, (b) => `${kst(b.lastAt)} (${Math.round(b.ageSeconds / 60)}분 전)${b.alive === null ? '' : b.alive ? ' 살아 있음' : ' 프로세스 없음'}${l.staleAfterSeconds === null ? ' · 주기 미상(판정 제외)' : ''}`)}`);
220
228
  }
@@ -23,6 +23,7 @@
23
23
  // event with cacheReadInputTokens absent (treated as 0 downstream).
24
24
 
25
25
  import type { LLMUsage } from './types.js';
26
+ import { readOpenRouterByokUsage } from '../llm/openrouter-byok-cost.js';
26
27
 
27
28
  /** Extract LLMUsage from an OpenAI streaming chunk. Returns null when
28
29
  * the chunk has no usage object (every chunk except the final one
@@ -71,6 +72,8 @@ export function parseOpenAIUsage(event: unknown): LLMUsage | null {
71
72
  out.reasoningOutputTokens = r.completion_tokens_details.reasoning_tokens;
72
73
  }
73
74
  if (typeof r.cost === 'number' && Number.isFinite(r.cost) && r.cost >= 0) out.reportedCostUsd = r.cost;
75
+ // OR-BYOK-COST — BYOK 호출이면 `byok` ⊕ `upstreamCostUsd`(비-BYOK 이면 아무 칸도 안 더한다 · 종전 바이트).
76
+ Object.assign(out, readOpenRouterByokUsage(event));
74
77
  const keys = Object.keys(out).filter(k => k !== 'provider');
75
78
  return keys.length === 0 ? null : out;
76
79
  }
@@ -32,6 +32,11 @@ export interface LLMUsage {
32
32
  /** provider 가 «응답에 실어 보낸» 실제 청구액(USD) — OpenRouter `usage.cost`(요청에 `usage:{include:true}`).
33
33
  * ⭐ 추정(카탈로그 단가)이 아니라 청구 사실이다 — 비용 판정이 이것을 먼저 쓴다(BACKLOG C7). */
34
34
  reportedCostUsd?: number;
35
+ /** OR-BYOK-COST — OpenRouter `usage.is_byok === true`(상류 키로 청구된 호출 · OR `cost` 는 0). */
36
+ byok?: true;
37
+ /** OR-BYOK-COST — OpenRouter `usage.cost_details.upstream_inference_cost`(상류 키로 실제 청구된 USD).
38
+ * ⛔ 칸이 없거나 숫자가 아니면 «없다»(=미상) — 0 으로 꾸미지 않는다. */
39
+ upstreamCostUsd?: number;
35
40
  }
36
41
 
37
42
  /** Back-compat alias for callers that imported `AnthropicUsage` from
@@ -158,6 +158,29 @@ export function selectEnhanceLlm(
158
158
  return { model, effort: opts.reasoningEffort ?? validEnvEffort ?? planning?.effort ?? 'high', modelSource, effortSource };
159
159
  }
160
160
 
161
+ /**
162
+ * ⭐ AUTHOR-DECOMPOSE-KNOB(0.2.24 · G7 #25848) — 골 쪼개기·적대 비평의 `streamLLM` 두 호출이 따를 노브.
163
+ * `selectEnhanceLlm` 과 «같은 규칙»으로 planning 역할을 푼다: ELANOUS_PROMPT_ENHANCE_MODEL/EFFORT →
164
+ * `roleLlm.planning`(`resolveRoleLlm('planning', …)`). 규칙의 «유일한» 차이는 model 폴백이다 —
165
+ * 인핸서는 자기 기본(`tierModel('better')`)으로 떨어지지만, 이 길은 아무것도 설정되지 않으면
166
+ * `undefined` 를 돌려 호출자가 `model` 을 아예 전달하지 않게 한다(프로바이더 기본 모델 = 종전 동작).
167
+ * effort 는 `selectEnhanceLlm` 과 같은 `high` 폴백이며, 두 노브는 따로 풀린다 —
168
+ * effort 만 세팅돼도 모델은 종전 기본 그대로다.
169
+ */
170
+ export function selectPlanningLlm(
171
+ env: NodeJS.ProcessEnv = process.env,
172
+ config: UserConfig = getUserConfig(),
173
+ ): { model: string | undefined; effort: 'low' | 'medium' | 'high' } {
174
+ const planning = config.roleLlm?.planning;
175
+ const model = env.ELANOUS_PROMPT_ENHANCE_MODEL
176
+ || (planning && (planning.model || planning.tier || planning.provider)
177
+ ? resolveRoleLlm('planning', { config, overrides: {} }).model
178
+ : undefined);
179
+ const envEffort = env.ELANOUS_PROMPT_ENHANCE_EFFORT;
180
+ const validEnvEffort = envEffort === 'low' || envEffort === 'medium' || envEffort === 'high' ? envEffort : undefined;
181
+ return { model, effort: validEnvEffort ?? planning?.effort ?? 'high' };
182
+ }
183
+
161
184
  /** 스캐폴드 생성 LLM 시스템 프롬프트 — 가산·원문불가침 규율. */
162
185
  const enhanceSystem = (deliverableHint?: string, wantsScqa = false, checklistUse: ChecklistUse = 'coverage-gate'): string =>
163
186
  [
@@ -6,7 +6,7 @@ import { resolveRepositoryName } from '../harness/repository-name.js';
6
6
  import { getElanousConfigDirOverride } from '../elanous-config-dir.js';
7
7
  import { effectiveInstanceRoot } from '../instance/resolve.js';
8
8
  import { userConfigPath } from '../user-config.js';
9
- import { addItem, listChecklist, ownerMatches, parseOwner, setItem, validateCeoLoad, type ChecklistItem } from './checklist.js';
9
+ import { addItem, checklistHistory, listChecklist, ownerMatches, parseOwner, setItem, validateCeoLoad, type ChecklistItem } from './checklist.js';
10
10
  import { assignedVersions, move, releasedVersion } from './feature-store.js';
11
11
  import { debug } from '../debug/log.js';
12
12
  import { kstDay, listSchedules, type ReleaseSchedule } from './release-schedule.js';
@@ -406,9 +406,24 @@ export function rebalance(version: string, deps: PlacementDeps = {}): RebalanceR
406
406
  return result;
407
407
  }
408
408
 
409
+ /** Reasons that justify a second forward rollover of the same cell (대표 10-10 «계속 이월되지 않게»). */
410
+ const SECOND_ROLLOVER_REASON_PREFIXES: readonly string[] = ['쪼개기:', '닫기:', '\u{1F451}:'];
411
+
412
+ /** Forward moves (to > from) already recorded for this cell across every release. */
413
+ function priorForwardMoves(id: string): number {
414
+ return checklistHistory(id).filter((row) => row.field === 'move'
415
+ && typeof row.from === 'string' && typeof row.to === 'string'
416
+ && versionOrder(row.to, row.from) > 0).length;
417
+ }
418
+
409
419
  /** A seat may defer its own cell one release, never pull another owner's cell forward. */
410
420
  export function seatMove(id: string, from: string, to: string, by: string, reason: string, deps: PlacementDeps = {}): void {
411
421
  if (!reason.trim()) throw new CliUserError('이동 이유가 필요하다');
422
+ const trimmed = reason.trim();
423
+ // A second rollover is only deliberate: split, closure, or a king's order.
424
+ if (versionOrder(to, from) > 0 && priorForwardMoves(id) >= 1
425
+ && !SECOND_ROLLOVER_REASON_PREFIXES.some((prefix) => trimmed.startsWith(prefix)))
426
+ throw new CliUserError(`${id}: 두 번째 이월은 «쪼개기:»·«닫기:»·«\u{1F451}:» 로 시작하는 이유만 허용한다 — 첫 이월 이후 계속 이월되지 않게`);
412
427
  const rows = [...(deps.schedules ?? listSchedules())].sort((a, b) => versionOrder(a.version, b.version));
413
428
  const backlog = deps.backlog ?? BACKLOG_VERSIONS;
414
429
  const next = rows.find((row) => versionOrder(row.version, from) > 0 && !backlog.includes(row.version));