@indigoai-us/hq-cli 5.313.0 → 5.314.0

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 (1174) hide show
  1. package/CHANGELOG.md +1 -4300
  2. package/assets/scaffold/core/scripts/checkpoint-stop-gate.sh +43 -281
  3. package/assets/scaffold/core/scripts/hook-lib.sh +50 -345
  4. package/assets/scaffold/core/scripts/hq-session.sh +23 -184
  5. package/assets/scaffold/core/scripts/lib/session-id.sh +2 -3
  6. package/assets/scaffold/core/scripts/lib/session-scope-capability.sh +1 -1
  7. package/assets/scaffold/core/scripts/lib/work-mesh-enqueue.sh +1 -2
  8. package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +87 -10
  9. package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +3 -36
  10. package/assets/scaffold/core/scripts/rebuild-projects-index.sh +12 -99
  11. package/assets/scaffold/core/scripts/token-usage-report.sh +20 -81
  12. package/dist/bin/hq-auth-refresh.d.ts +0 -2
  13. package/dist/bin/hq-auth-refresh.js +6 -12
  14. package/dist/commands/__fixtures__/access-vault.d.ts +0 -2
  15. package/dist/commands/__fixtures__/access-vault.js +1 -1
  16. package/dist/commands/_patterns.d.ts +0 -16
  17. package/dist/commands/_patterns.js +3 -20
  18. package/dist/commands/access.d.ts +1 -2
  19. package/dist/commands/access.js +49 -58
  20. package/dist/commands/agents.d.ts +4 -106
  21. package/dist/commands/agents.js +45 -924
  22. package/dist/commands/auth.d.ts +0 -1
  23. package/dist/commands/auth.js +19 -189
  24. package/dist/commands/billing.d.ts +0 -3
  25. package/dist/commands/billing.js +1 -6
  26. package/dist/commands/channels.js +1 -56
  27. package/dist/commands/cloud-demote.js +2 -3
  28. package/dist/commands/cloud-provision.d.ts +0 -14
  29. package/dist/commands/cloud-provision.js +58 -232
  30. package/dist/commands/cloud.d.ts +15 -49
  31. package/dist/commands/cloud.js +122 -357
  32. package/dist/commands/company-transfer.d.ts +28 -4
  33. package/dist/commands/company-transfer.js +49 -5
  34. package/dist/commands/company.js +3 -68
  35. package/dist/commands/core-checkpoint.d.ts +9 -19
  36. package/dist/commands/core-checkpoint.js +37 -301
  37. package/dist/commands/core.d.ts +0 -4
  38. package/dist/commands/core.js +80 -277
  39. package/dist/commands/crm.js +3 -52
  40. package/dist/commands/db-provision.js +4 -4
  41. package/dist/commands/db-sql.js +21 -7
  42. package/dist/commands/dm.d.ts +12 -119
  43. package/dist/commands/dm.js +38 -388
  44. package/dist/commands/doctor.d.ts +0 -12
  45. package/dist/commands/doctor.js +12 -158
  46. package/dist/commands/feedback.d.ts +0 -6
  47. package/dist/commands/feedback.js +37 -86
  48. package/dist/commands/files-browse.d.ts +12 -32
  49. package/dist/commands/files-browse.js +60 -176
  50. package/dist/commands/files.d.ts +0 -17
  51. package/dist/commands/files.js +47 -300
  52. package/dist/commands/group-grants.js +2 -3
  53. package/dist/commands/groups.js +5 -6
  54. package/dist/commands/index-cmd.d.ts +1 -19
  55. package/dist/commands/index-cmd.js +7 -74
  56. package/dist/commands/integrations-api.d.ts +10 -15
  57. package/dist/commands/integrations-api.js +22 -27
  58. package/dist/commands/integrations-connect.d.ts +13 -51
  59. package/dist/commands/integrations-connect.js +71 -86
  60. package/dist/commands/integrations-core.d.ts +6 -6
  61. package/dist/commands/integrations-core.js +11 -25
  62. package/dist/commands/integrations-import.d.ts +1 -1
  63. package/dist/commands/integrations-import.js +24 -43
  64. package/dist/commands/integrations-manage.js +13 -20
  65. package/dist/commands/integrations.d.ts +0 -8
  66. package/dist/commands/integrations.js +7 -94
  67. package/dist/commands/login.js +6 -49
  68. package/dist/commands/mcp-registration.d.ts +25 -32
  69. package/dist/commands/mcp-registration.js +23 -256
  70. package/dist/commands/mcp-status.js +0 -1
  71. package/dist/commands/meetings.js +34 -55
  72. package/dist/commands/members.js +1 -5
  73. package/dist/commands/mesh.d.ts +0 -11
  74. package/dist/commands/mesh.js +76 -1086
  75. package/dist/commands/onboard-identity-guard.d.ts +1 -1
  76. package/dist/commands/onboard.js +5 -24
  77. package/dist/commands/pack-install.js +1 -3
  78. package/dist/commands/packs.js +1 -11
  79. package/dist/commands/people.d.ts +0 -4
  80. package/dist/commands/people.js +3 -37
  81. package/dist/commands/publish.d.ts +1 -8
  82. package/dist/commands/publish.js +4 -17
  83. package/dist/commands/reindex.js +6 -56
  84. package/dist/commands/rescue.d.ts +0 -4
  85. package/dist/commands/rescue.js +17 -27
  86. package/dist/commands/run.js +4 -18
  87. package/dist/commands/safe-extract.d.ts +2 -11
  88. package/dist/commands/safe-extract.js +8 -36
  89. package/dist/commands/scaffold-fast.d.ts +41 -0
  90. package/dist/commands/scaffold-fast.js +57 -0
  91. package/dist/commands/secrets.d.ts +2 -20
  92. package/dist/commands/secrets.js +71 -175
  93. package/dist/commands/skill.d.ts +4 -35
  94. package/dist/commands/skill.js +18 -357
  95. package/dist/commands/sync-mode.d.ts +0 -11
  96. package/dist/commands/sync-mode.js +0 -30
  97. package/dist/commands/sync-narrow.js +5 -36
  98. package/dist/commands/team-sync.js +10 -71
  99. package/dist/commands/update.js +0 -6
  100. package/dist/commands/whoami.d.ts +0 -22
  101. package/dist/commands/whoami.js +4 -137
  102. package/dist/commands/workers.d.ts +14 -2
  103. package/dist/commands/workers.js +10 -7
  104. package/dist/fast-core.d.ts +7 -6
  105. package/dist/fast-core.js +24 -87
  106. package/dist/index.d.ts +4 -3
  107. package/dist/index.js +9 -59
  108. package/dist/lazy-commands.d.ts +44 -20
  109. package/dist/lazy-commands.js +58 -60
  110. package/dist/lib/company-skill-wrapper.d.ts +0 -1
  111. package/dist/lib/company-skill-wrapper.js +29 -121
  112. package/dist/lib/core-utils/codex-skill-bridge.d.ts +1 -2
  113. package/dist/lib/core-utils/qmd-reindex-after-sync.d.ts +1 -14
  114. package/dist/lib/core-utils/qmd-reindex-after-sync.js +17 -101
  115. package/dist/lib/core-utils/soft-timeout.js +1 -1
  116. package/dist/lib/core-utils/timeout-guard.d.ts +3 -3
  117. package/dist/lib/core-utils/timeout-guard.js +13 -28
  118. package/dist/lib/core-utils/token-usage-report.js +3 -17
  119. package/dist/lib/db/control-plane.js +0 -6
  120. package/dist/lib/db/local.d.ts +5 -0
  121. package/dist/lib/db/local.js +1 -1
  122. package/dist/lib/db/migrate.d.ts +12 -0
  123. package/dist/lib/db/migrate.js +5 -5
  124. package/dist/lib/db/remote-sql.d.ts +2 -2
  125. package/dist/lib/db/remote-sql.js +3 -3
  126. package/dist/lib/db/sql.d.ts +10 -0
  127. package/dist/lib/db/sql.js +8 -4
  128. package/dist/lib/doctor/__testing__/fake-hq-tree.d.ts +1 -1
  129. package/dist/lib/doctor/__testing__/fake-hq-tree.js +1 -1
  130. package/dist/lib/doctor/checks/claude-wiring.js +1 -70
  131. package/dist/lib/doctor/checks/codex-wiring.d.ts +4 -0
  132. package/dist/lib/doctor/checks/codex-wiring.js +35 -3
  133. package/dist/lib/doctor/checks/grok-wiring.js +34 -1
  134. package/dist/lib/doctor/checks/integrations.js +0 -11
  135. package/dist/lib/doctor/checks/runtime-health.d.ts +17 -4
  136. package/dist/lib/doctor/checks/runtime-health.js +7 -20
  137. package/dist/lib/doctor/checks/sync-health.d.ts +0 -21
  138. package/dist/lib/doctor/checks/sync-health.js +2 -72
  139. package/dist/lib/doctor/checks/work-context.js +6 -20
  140. package/dist/lib/doctor/compat.d.ts +16 -0
  141. package/dist/lib/doctor/compat.js +3 -4
  142. package/dist/lib/doctor/deep/effects.js +3 -1
  143. package/dist/lib/doctor/deep/executor.d.ts +10 -4
  144. package/dist/lib/doctor/deep/executor.js +1 -1
  145. package/dist/lib/doctor/deep/parity.d.ts +29 -2
  146. package/dist/lib/doctor/deep/parity.js +7 -7
  147. package/dist/lib/doctor/deep/sandbox.d.ts +25 -7
  148. package/dist/lib/doctor/deep/sandbox.js +7 -5
  149. package/dist/lib/doctor/fix/apply.d.ts +8 -79
  150. package/dist/lib/doctor/fix/apply.js +80 -292
  151. package/dist/lib/doctor/fix/remediation.d.ts +6 -14
  152. package/dist/lib/doctor/fix/remediation.js +9 -49
  153. package/dist/lib/doctor/fixtures/schema.js +3 -1
  154. package/dist/lib/doctor/json-output.d.ts +1 -17
  155. package/dist/lib/doctor/json-output.js +2 -7
  156. package/dist/lib/doctor/platform.d.ts +26 -0
  157. package/dist/lib/doctor/platform.js +5 -5
  158. package/dist/lib/doctor/registry.d.ts +0 -14
  159. package/dist/lib/doctor/registry.js +0 -71
  160. package/dist/lib/doctor/report.js +1 -1
  161. package/dist/lib/doctor/types.d.ts +0 -5
  162. package/dist/lib/doctor/types.js +0 -20
  163. package/dist/lib/flag-registry-worker.js +1 -12
  164. package/dist/lib/flag-registry.d.ts +6 -26
  165. package/dist/lib/flag-registry.js +16 -133
  166. package/dist/lib/index-render/companies.d.ts +1 -4
  167. package/dist/lib/index-render/companies.js +3 -66
  168. package/dist/lib/index-render/company-knowledge.js +17 -80
  169. package/dist/lib/index-render/index.d.ts +0 -1
  170. package/dist/lib/index-render/index.js +1 -2
  171. package/dist/lib/index-render/projects.js +5 -82
  172. package/dist/lib/index-render/shared.d.ts +1 -13
  173. package/dist/lib/index-render/shared.js +2 -41
  174. package/dist/lib/mesh/api.d.ts +10 -103
  175. package/dist/lib/mesh/api.js +64 -334
  176. package/dist/lib/mesh/client.d.ts +2 -3
  177. package/dist/lib/mesh/client.js +7 -26
  178. package/dist/lib/mesh/live/backfill-held.d.ts +16 -67
  179. package/dist/lib/mesh/live/backfill-held.js +61 -415
  180. package/dist/lib/mesh/live/backoff.d.ts +2 -2
  181. package/dist/lib/mesh/live/backoff.js +3 -16
  182. package/dist/lib/mesh/live/daemon/board-refresh.d.ts +1 -7
  183. package/dist/lib/mesh/live/daemon/board-refresh.js +30 -78
  184. package/dist/lib/mesh/live/daemon/credentials.d.ts +10 -64
  185. package/dist/lib/mesh/live/daemon/credentials.js +20 -270
  186. package/dist/lib/mesh/live/daemon/doctor.d.ts +0 -53
  187. package/dist/lib/mesh/live/daemon/doctor.js +6 -190
  188. package/dist/lib/mesh/live/daemon/index.d.ts +10 -13
  189. package/dist/lib/mesh/live/daemon/index.js +6 -7
  190. package/dist/lib/mesh/live/daemon/install.d.ts +1 -255
  191. package/dist/lib/mesh/live/daemon/install.js +13 -811
  192. package/dist/lib/mesh/live/daemon/paths.d.ts +0 -2
  193. package/dist/lib/mesh/live/daemon/paths.js +0 -2
  194. package/dist/lib/mesh/live/daemon/presence.d.ts +5 -24
  195. package/dist/lib/mesh/live/daemon/presence.js +10 -79
  196. package/dist/lib/mesh/live/daemon/run.d.ts +2 -48
  197. package/dist/lib/mesh/live/daemon/run.js +33 -485
  198. package/dist/lib/mesh/live/daemon/state.d.ts +2 -19
  199. package/dist/lib/mesh/live/daemon/state.js +2 -34
  200. package/dist/lib/mesh/live/daemon/transcript-watch.d.ts +9 -45
  201. package/dist/lib/mesh/live/daemon/transcript-watch.js +31 -227
  202. package/dist/lib/mesh/live/emit-client.d.ts +0 -8
  203. package/dist/lib/mesh/live/emit-client.js +8 -23
  204. package/dist/lib/mesh/live/emit-evidence.d.ts +0 -19
  205. package/dist/lib/mesh/live/emit-evidence.js +2 -20
  206. package/dist/lib/mesh/live/emit.d.ts +0 -9
  207. package/dist/lib/mesh/live/emit.js +2 -28
  208. package/dist/lib/mesh/live/enqueue.d.ts +0 -5
  209. package/dist/lib/mesh/live/enqueue.js +0 -19
  210. package/dist/lib/mesh/live/flush.d.ts +5 -17
  211. package/dist/lib/mesh/live/flush.js +38 -391
  212. package/dist/lib/mesh/live/format-spool-line.d.ts +2 -5
  213. package/dist/lib/mesh/live/format-spool-line.js +0 -10
  214. package/dist/lib/mesh/live/paths.d.ts +0 -2
  215. package/dist/lib/mesh/live/paths.js +0 -4
  216. package/dist/lib/mesh/live/session-event.schema.json +1 -24
  217. package/dist/lib/mesh/live/session-events-client.d.ts +0 -2
  218. package/dist/lib/mesh/live/session-events-client.js +0 -26
  219. package/dist/lib/mesh/live/spool.d.ts +0 -19
  220. package/dist/lib/mesh/live/spool.js +3 -269
  221. package/dist/lib/mesh/live/validate-session-event.d.ts +1 -1
  222. package/dist/lib/mesh/live/validate-session-event.js +1 -1
  223. package/dist/lib/plan-limit-nag.d.ts +6 -102
  224. package/dist/lib/plan-limit-nag.js +43 -348
  225. package/dist/lib/scan-packages/index.d.ts +0 -2
  226. package/dist/lib/scan-packages/index.js +6 -168
  227. package/dist/lib/search-index/background.d.ts +1 -8
  228. package/dist/lib/search-index/background.js +200 -55
  229. package/dist/lib/search-index/index.d.ts +0 -12
  230. package/dist/lib/search-index/index.js +7 -153
  231. package/dist/lib/work-context/company.d.ts +20 -22
  232. package/dist/lib/work-context/company.js +40 -124
  233. package/dist/lib/work-context/config.d.ts +11 -58
  234. package/dist/lib/work-context/config.js +83 -334
  235. package/dist/lib/work-context/contract.d.ts +0 -13
  236. package/dist/lib/work-context/contract.schema.json +1 -3
  237. package/dist/lib/work-context/index.d.ts +25 -0
  238. package/dist/lib/work-context/index.js +21 -0
  239. package/dist/lib/work-context/outbox.d.ts +5 -27
  240. package/dist/lib/work-context/outbox.js +6 -135
  241. package/dist/lib/work-context/reconcile.d.ts +0 -15
  242. package/dist/lib/work-context/reconcile.js +29 -405
  243. package/dist/lib/work-context/state.d.ts +10 -93
  244. package/dist/lib/work-context/state.js +29 -342
  245. package/dist/lib/workers-registry/index.js +8 -62
  246. package/dist/main.d.ts +0 -32
  247. package/dist/main.js +73 -273
  248. package/dist/node-network-compat.d.ts +30 -2
  249. package/dist/node-network-compat.js +51 -4
  250. package/dist/node-preflight.d.ts +1 -7
  251. package/dist/node-preflight.js +5 -5
  252. package/dist/register-all.d.ts +29 -4
  253. package/dist/register-all.js +233 -4
  254. package/dist/run/discover-schemas.d.ts +1 -2
  255. package/dist/run/env-graph-guard.d.ts +2 -2
  256. package/dist/run/env-graph-guard.js +2 -5
  257. package/dist/run/hq-plugin.d.ts +0 -4
  258. package/dist/run/hq-plugin.js +8 -36
  259. package/dist/sentry-before-send.js +0 -80
  260. package/dist/sentry.d.ts +2 -8
  261. package/dist/sentry.js +13 -117
  262. package/dist/startup-registration.d.ts +2 -26
  263. package/dist/startup-registration.js +15 -116
  264. package/dist/utils/cli-telemetry.d.ts +1 -74
  265. package/dist/utils/cli-telemetry.js +7 -434
  266. package/dist/utils/client-health.d.ts +4 -0
  267. package/dist/utils/client-health.js +2 -2
  268. package/dist/utils/cognito-session.d.ts +0 -71
  269. package/dist/utils/cognito-session.js +27 -481
  270. package/dist/utils/contribution-table.d.ts +1 -1
  271. package/dist/utils/environmental-error.d.ts +0 -18
  272. package/dist/utils/environmental-error.js +7 -240
  273. package/dist/utils/expected-cli-error.d.ts +0 -2
  274. package/dist/utils/expected-cli-error.js +0 -4
  275. package/dist/utils/feedback-diagnostics.d.ts +1 -2
  276. package/dist/utils/feedback-log-bundle.d.ts +26 -51
  277. package/dist/utils/feedback-log-bundle.js +31 -211
  278. package/dist/utils/feedback-logs.d.ts +47 -18
  279. package/dist/utils/feedback-logs.js +21 -50
  280. package/dist/utils/feedback-screenshots.d.ts +1 -0
  281. package/dist/utils/feedback-screenshots.js +1 -1
  282. package/dist/utils/hq-roots.d.ts +2 -25
  283. package/dist/utils/hq-roots.js +7 -67
  284. package/dist/utils/incomplete-install-error.d.ts +19 -87
  285. package/dist/utils/incomplete-install-error.js +68 -319
  286. package/dist/utils/install-tree-torn.d.ts +24 -260
  287. package/dist/utils/install-tree-torn.js +51 -998
  288. package/dist/utils/large-file-guard.d.ts +10 -0
  289. package/dist/utils/large-file-guard.js +1 -1
  290. package/dist/utils/local-files-overview.d.ts +8 -4
  291. package/dist/utils/local-files-overview.js +7 -48
  292. package/dist/utils/login-provider.d.ts +0 -7
  293. package/dist/utils/login-provider.js +0 -34
  294. package/dist/utils/pack-contributions.d.ts +14 -2
  295. package/dist/utils/pack-contributions.js +44 -7
  296. package/dist/utils/package-root-diagnostics.d.ts +1 -1
  297. package/dist/utils/people.d.ts +11 -7
  298. package/dist/utils/people.js +20 -5
  299. package/dist/utils/plan-gate-error.d.ts +10 -52
  300. package/dist/utils/plan-gate-error.js +41 -364
  301. package/dist/utils/qmd-model-download-error.d.ts +8 -0
  302. package/dist/utils/qmd-model-download-error.js +1 -1
  303. package/dist/utils/qmd-query-document-error.d.ts +6 -0
  304. package/dist/utils/qmd-query-document-error.js +1 -1
  305. package/dist/utils/redact-error-text.d.ts +2 -0
  306. package/dist/utils/redact-error-text.js +2 -3
  307. package/dist/utils/run-bundled-script.d.ts +0 -6
  308. package/dist/utils/run-bundled-script.js +4 -117
  309. package/dist/utils/sandbox-runner-client.d.ts +1 -2
  310. package/dist/utils/secrets-cache.d.ts +3 -7
  311. package/dist/utils/secrets-cache.js +14 -162
  312. package/dist/utils/self-update.d.ts +1 -34
  313. package/dist/utils/self-update.js +42 -660
  314. package/dist/utils/sentry-fingerprint.d.ts +1 -23
  315. package/dist/utils/sentry-fingerprint.js +31 -223
  316. package/dist/utils/team-upgrade.d.ts +1 -1
  317. package/dist/utils/team-upgrade.js +3 -4
  318. package/dist/utils/vault-api.d.ts +1 -21
  319. package/dist/utils/vault-api.js +84 -251
  320. package/dist/utils/version-check.d.ts +1 -11
  321. package/dist/utils/version-check.js +3 -32
  322. package/dist/utils/version-gate.d.ts +17 -101
  323. package/dist/utils/version-gate.js +70 -645
  324. package/dist/utils/windows-spawn.d.ts +0 -6
  325. package/dist/utils/windows-spawn.js +0 -37
  326. package/package.json +8 -20
  327. package/README.md +0 -113
  328. package/assets/bot-workers/setup/context/USER-GUIDE.md +0 -404
  329. package/assets/bot-workers/setup/context/quick-reference.md +0 -199
  330. package/assets/bot-workers/setup/skills/agents-and-team.md +0 -167
  331. package/assets/bot-workers/setup/skills/explain-hq.md +0 -35
  332. package/assets/bot-workers/setup/skills/first-company.md +0 -74
  333. package/assets/bot-workers/setup/skills/joining-a-team.md +0 -125
  334. package/assets/bot-workers/setup/skills/pricing.md +0 -92
  335. package/assets/bot-workers/setup/skills/standing-help.md +0 -81
  336. package/assets/bot-workers/setup/skills/your-business.md +0 -281
  337. package/assets/bot-workers/setup/worker.yaml +0 -791
  338. package/assets/lanes-hooks/hq-lanes-drain.json +0 -15
  339. package/assets/lanes-hooks/hq-lanes-drain.mjs +0 -275
  340. package/assets/lanes-hooks/hq-lanes-drain.sh +0 -21
  341. package/assets/lanes-reconcile/hq-lanes-reconcile.service +0 -11
  342. package/assets/lanes-reconcile/hq-lanes-reconcile.timer +0 -12
  343. package/assets/scaffold/.claude/hooks/validate-policy-frontmatter.sh +0 -485
  344. package/assets/scaffold/core/scripts/audit-log.sh +0 -384
  345. package/assets/scaffold/core/scripts/clean-worktree-reconcile-handoff.sh +0 -171
  346. package/assets/scaffold/core/scripts/eval-trigger.sh +0 -193
  347. package/assets/scaffold/core/scripts/git-pack-extension-check.sh +0 -164
  348. package/assets/scaffold/core/scripts/handoff-finalize.sh +0 -688
  349. package/assets/scaffold/core/scripts/handoff-open-steps.sh +0 -81
  350. package/assets/scaffold/core/scripts/handoff-post.sh +0 -252
  351. package/assets/scaffold/core/scripts/hq-delegate-bundle.sh +0 -307
  352. package/assets/scaffold/core/scripts/hq-delegate-grant.sh +0 -252
  353. package/assets/scaffold/core/scripts/hq-delegate-pickup.sh +0 -99
  354. package/assets/scaffold/core/scripts/hq-delegate-publish.sh +0 -91
  355. package/assets/scaffold/core/scripts/hq-delegate-repo.sh +0 -158
  356. package/assets/scaffold/core/scripts/hq-delegate-resolve.sh +0 -145
  357. package/assets/scaffold/core/scripts/hq-delegate-secrets.sh +0 -129
  358. package/assets/scaffold/core/scripts/hq-delegate-send.sh +0 -259
  359. package/assets/scaffold/core/scripts/hq-delegate-transfer.sh +0 -175
  360. package/assets/scaffold/core/scripts/hq-delegate-verify.sh +0 -238
  361. package/assets/scaffold/core/scripts/hq-dm-bind.sh +0 -585
  362. package/assets/scaffold/core/scripts/hq-job-notify.sh +0 -389
  363. package/assets/scaffold/core/scripts/hq-job-probe.sh +0 -835
  364. package/assets/scaffold/core/scripts/hq-job-remediate.sh +0 -359
  365. package/assets/scaffold/core/scripts/hq-job-run.sh +0 -1025
  366. package/assets/scaffold/core/scripts/hq-pin.sh +0 -118
  367. package/assets/scaffold/core/scripts/jobs-schedule-parse.sh +0 -310
  368. package/assets/scaffold/core/scripts/jobs-validate.sh +0 -451
  369. package/assets/scaffold/core/scripts/knowledge-prefs.sh +0 -55
  370. package/assets/scaffold/core/scripts/lib/portable.sh +0 -211
  371. package/assets/scaffold/core/scripts/lib/secret-patterns.sh +0 -58
  372. package/assets/scaffold/core/scripts/lib/work-mesh-live-hook.sh +0 -378
  373. package/assets/scaffold/core/scripts/lib/work-mesh-live-rebind.sh +0 -141
  374. package/assets/scaffold/core/scripts/migrate-policy-triggers.sh +0 -373
  375. package/assets/scaffold/core/scripts/ontology-audience-grant.sh +0 -81
  376. package/assets/scaffold/core/scripts/ontology-brief.mjs +0 -93
  377. package/assets/scaffold/core/scripts/ontology-candidate.sh +0 -100
  378. package/assets/scaffold/core/scripts/ontology-garden.mjs +0 -238
  379. package/assets/scaffold/core/scripts/ontology-source-worklist.sh +0 -79
  380. package/assets/scaffold/core/scripts/outpost-jobs-reconcile.sh +0 -834
  381. package/assets/scaffold/core/scripts/policy-age-report.sh +0 -156
  382. package/assets/scaffold/core/scripts/policy-retire.sh +0 -199
  383. package/assets/scaffold/core/scripts/qmd-reindex-bg.sh +0 -40
  384. package/assets/scaffold/core/scripts/read-policy-frontmatter.sh +0 -32
  385. package/assets/scaffold/core/scripts/refresh-vault-access.sh +0 -188
  386. package/assets/scaffold/core/scripts/register-project.sh +0 -262
  387. package/assets/scaffold/core/scripts/resolve-company.sh +0 -199
  388. package/assets/scaffold/core/scripts/resume-thread-lock.sh +0 -285
  389. package/assets/scaffold/core/scripts/source-yaml-validate.sh +0 -36
  390. package/assets/scaffold/core/scripts/verify-story-deliverables.sh +0 -148
  391. package/assets/scaffold/core/scripts/work-mesh-live-bind-trusted.sh +0 -152
  392. package/assets/scaffold/core/scripts/work-mesh-live-rebind.sh +0 -59
  393. package/assets/scaffold/core/scripts/worktree-gc.sh +0 -362
  394. package/contracts/preflight/v1/fixtures.json +0 -87
  395. package/contracts/preflight/v1/schema.json +0 -58
  396. package/dist/command-catalog.generated.d.ts +0 -10735
  397. package/dist/command-catalog.generated.js +0 -13836
  398. package/dist/command-registration-plan.d.ts +0 -474
  399. package/dist/command-registration-plan.js +0 -136
  400. package/dist/commands/agent-enroll.d.ts +0 -146
  401. package/dist/commands/agent-enroll.js +0 -401
  402. package/dist/commands/agent-inbox.d.ts +0 -9
  403. package/dist/commands/agent-inbox.js +0 -102
  404. package/dist/commands/agent-kit.d.ts +0 -87
  405. package/dist/commands/agent-kit.js +0 -480
  406. package/dist/commands/agent-mcp.d.ts +0 -24
  407. package/dist/commands/agent-mcp.js +0 -127
  408. package/dist/commands/agent-probe.d.ts +0 -77
  409. package/dist/commands/agent-probe.js +0 -335
  410. package/dist/commands/agent.d.ts +0 -13
  411. package/dist/commands/agent.js +0 -40
  412. package/dist/commands/billing-sponsor.d.ts +0 -83
  413. package/dist/commands/billing-sponsor.js +0 -344
  414. package/dist/commands/bot-companies.d.ts +0 -40
  415. package/dist/commands/bot-companies.js +0 -64
  416. package/dist/commands/bot-continuity.d.ts +0 -28
  417. package/dist/commands/bot-continuity.js +0 -68
  418. package/dist/commands/bot-jobs.d.ts +0 -127
  419. package/dist/commands/bot-jobs.js +0 -454
  420. package/dist/commands/bot.d.ts +0 -278
  421. package/dist/commands/bot.js +0 -1982
  422. package/dist/commands/cloud-retire.d.ts +0 -51
  423. package/dist/commands/cloud-retire.js +0 -154
  424. package/dist/commands/company-rename-flag.d.ts +0 -4
  425. package/dist/commands/company-rename-flag.js +0 -7
  426. package/dist/commands/company-rename.d.ts +0 -4
  427. package/dist/commands/company-rename.js +0 -90
  428. package/dist/commands/core-audit-log-native.d.ts +0 -9
  429. package/dist/commands/core-audit-log-native.js +0 -257
  430. package/dist/commands/core-checkpoint-gate-setting.d.ts +0 -59
  431. package/dist/commands/core-checkpoint-gate-setting.js +0 -163
  432. package/dist/commands/core-checkpoint-stop-gate-native.d.ts +0 -34
  433. package/dist/commands/core-checkpoint-stop-gate-native.js +0 -725
  434. package/dist/commands/core-command-catalog.d.ts +0 -34
  435. package/dist/commands/core-command-catalog.js +0 -62
  436. package/dist/commands/core-company-index-scope-flag.d.ts +0 -3
  437. package/dist/commands/core-company-index-scope-flag.js +0 -6
  438. package/dist/commands/core-delegate-w1b.d.ts +0 -10
  439. package/dist/commands/core-delegate-w1b.js +0 -1219
  440. package/dist/commands/core-delegate.d.ts +0 -46
  441. package/dist/commands/core-delegate.js +0 -1040
  442. package/dist/commands/core-dm-bind-native.d.ts +0 -32
  443. package/dist/commands/core-dm-bind-native.js +0 -668
  444. package/dist/commands/core-eval-trigger-native.d.ts +0 -4
  445. package/dist/commands/core-eval-trigger-native.js +0 -12
  446. package/dist/commands/core-git-pack-extension-check-native.d.ts +0 -22
  447. package/dist/commands/core-git-pack-extension-check-native.js +0 -190
  448. package/dist/commands/core-handoff-fast.d.ts +0 -18
  449. package/dist/commands/core-handoff-fast.js +0 -104
  450. package/dist/commands/core-handoff-open-steps.d.ts +0 -21
  451. package/dist/commands/core-handoff-open-steps.js +0 -264
  452. package/dist/commands/core-handoff.d.ts +0 -24
  453. package/dist/commands/core-handoff.js +0 -1144
  454. package/dist/commands/core-hq-session-native.d.ts +0 -39
  455. package/dist/commands/core-hq-session-native.js +0 -579
  456. package/dist/commands/core-job-native-utils.d.ts +0 -6
  457. package/dist/commands/core-job-native-utils.js +0 -80
  458. package/dist/commands/core-job-native.d.ts +0 -5
  459. package/dist/commands/core-job-native.js +0 -16
  460. package/dist/commands/core-job-notify-native.d.ts +0 -36
  461. package/dist/commands/core-job-notify-native.js +0 -359
  462. package/dist/commands/core-job-probe-native.d.ts +0 -45
  463. package/dist/commands/core-job-probe-native.js +0 -564
  464. package/dist/commands/core-job-remediate-native.d.ts +0 -26
  465. package/dist/commands/core-job-remediate-native.js +0 -374
  466. package/dist/commands/core-job-remote-launcher.d.ts +0 -7
  467. package/dist/commands/core-job-remote-launcher.js +0 -51
  468. package/dist/commands/core-job-run-native.d.ts +0 -47
  469. package/dist/commands/core-job-run-native.js +0 -1215
  470. package/dist/commands/core-jobs-native.d.ts +0 -25
  471. package/dist/commands/core-jobs-native.js +0 -1339
  472. package/dist/commands/core-knowledge-prefs.d.ts +0 -11
  473. package/dist/commands/core-knowledge-prefs.js +0 -137
  474. package/dist/commands/core-migrate-policy-triggers-native.d.ts +0 -21
  475. package/dist/commands/core-migrate-policy-triggers-native.js +0 -378
  476. package/dist/commands/core-native-handoff-flag.d.ts +0 -4
  477. package/dist/commands/core-native-handoff-flag.js +0 -7
  478. package/dist/commands/core-native-hook-helper-commands.d.ts +0 -4
  479. package/dist/commands/core-native-hook-helper-commands.js +0 -56
  480. package/dist/commands/core-native-hook-helpers-flag.d.ts +0 -4
  481. package/dist/commands/core-native-hook-helpers-flag.js +0 -7
  482. package/dist/commands/core-native-jobs-flag.d.ts +0 -4
  483. package/dist/commands/core-native-jobs-flag.js +0 -7
  484. package/dist/commands/core-native-policy-flag.d.ts +0 -9
  485. package/dist/commands/core-native-policy-flag.js +0 -12
  486. package/dist/commands/core-native-session-flag.d.ts +0 -4
  487. package/dist/commands/core-native-session-flag.js +0 -7
  488. package/dist/commands/core-native-utilities-flag.d.ts +0 -4
  489. package/dist/commands/core-native-utilities-flag.js +0 -7
  490. package/dist/commands/core-native-utilities.d.ts +0 -29
  491. package/dist/commands/core-native-utilities.js +0 -54
  492. package/dist/commands/core-native-worker-skills-flag.d.ts +0 -4
  493. package/dist/commands/core-native-worker-skills-flag.js +0 -7
  494. package/dist/commands/core-ontology-native.d.ts +0 -17
  495. package/dist/commands/core-ontology-native.js +0 -916
  496. package/dist/commands/core-ontology.d.ts +0 -66
  497. package/dist/commands/core-ontology.js +0 -430
  498. package/dist/commands/core-pin-native.d.ts +0 -14
  499. package/dist/commands/core-pin-native.js +0 -236
  500. package/dist/commands/core-policy-frontmatter.d.ts +0 -8
  501. package/dist/commands/core-policy-frontmatter.js +0 -52
  502. package/dist/commands/core-policy.d.ts +0 -14
  503. package/dist/commands/core-policy.js +0 -1018
  504. package/dist/commands/core-refresh-vault-access-native.d.ts +0 -20
  505. package/dist/commands/core-refresh-vault-access-native.js +0 -229
  506. package/dist/commands/core-register-project-native.d.ts +0 -30
  507. package/dist/commands/core-register-project-native.js +0 -347
  508. package/dist/commands/core-resolve-company-command.d.ts +0 -4
  509. package/dist/commands/core-resolve-company-command.js +0 -42
  510. package/dist/commands/core-resolve-company-native.d.ts +0 -28
  511. package/dist/commands/core-resolve-company-native.js +0 -408
  512. package/dist/commands/core-work-mesh-bind-trusted-native.d.ts +0 -32
  513. package/dist/commands/core-work-mesh-bind-trusted-native.js +0 -190
  514. package/dist/commands/core-work-mesh-live-rebind-native.d.ts +0 -18
  515. package/dist/commands/core-work-mesh-live-rebind-native.js +0 -262
  516. package/dist/commands/core-worker-lint-native.d.ts +0 -4
  517. package/dist/commands/core-worker-lint-native.js +0 -160
  518. package/dist/commands/core-worker-share-native.d.ts +0 -4
  519. package/dist/commands/core-worker-share-native.js +0 -260
  520. package/dist/commands/core-worker-skills-command.d.ts +0 -8
  521. package/dist/commands/core-worker-skills-command.js +0 -33
  522. package/dist/commands/core-worker-skills-runtime.d.ts +0 -33
  523. package/dist/commands/core-worker-skills-runtime.js +0 -86
  524. package/dist/commands/daemon.d.ts +0 -63
  525. package/dist/commands/daemon.js +0 -877
  526. package/dist/commands/deploy-comments.d.ts +0 -3
  527. package/dist/commands/deploy-comments.js +0 -162
  528. package/dist/commands/lanes-accounts.d.ts +0 -28
  529. package/dist/commands/lanes-accounts.js +0 -595
  530. package/dist/commands/lanes-autopilot.d.ts +0 -11
  531. package/dist/commands/lanes-autopilot.js +0 -336
  532. package/dist/commands/lanes-checks.d.ts +0 -51
  533. package/dist/commands/lanes-checks.js +0 -941
  534. package/dist/commands/lanes-hold.d.ts +0 -43
  535. package/dist/commands/lanes-hold.js +0 -655
  536. package/dist/commands/lanes-kpis.d.ts +0 -11
  537. package/dist/commands/lanes-kpis.js +0 -34
  538. package/dist/commands/lanes-message.d.ts +0 -9
  539. package/dist/commands/lanes-message.js +0 -937
  540. package/dist/commands/lanes-move.d.ts +0 -12
  541. package/dist/commands/lanes-move.js +0 -397
  542. package/dist/commands/lanes-questions.d.ts +0 -36
  543. package/dist/commands/lanes-questions.js +0 -1005
  544. package/dist/commands/lanes-report.d.ts +0 -11
  545. package/dist/commands/lanes-report.js +0 -333
  546. package/dist/commands/lanes-resume.d.ts +0 -6
  547. package/dist/commands/lanes-resume.js +0 -564
  548. package/dist/commands/lanes-review.d.ts +0 -10
  549. package/dist/commands/lanes-review.js +0 -56
  550. package/dist/commands/lanes-shared.d.ts +0 -448
  551. package/dist/commands/lanes-shared.js +0 -170
  552. package/dist/commands/lanes-stories.d.ts +0 -15
  553. package/dist/commands/lanes-stories.js +0 -347
  554. package/dist/commands/lanes-watch.d.ts +0 -7
  555. package/dist/commands/lanes-watch.js +0 -216
  556. package/dist/commands/lanes.d.ts +0 -149
  557. package/dist/commands/lanes.js +0 -8973
  558. package/dist/commands/monitor.d.ts +0 -58
  559. package/dist/commands/monitor.js +0 -980
  560. package/dist/commands/onboard-bot-membership.d.ts +0 -32
  561. package/dist/commands/onboard-bot-membership.js +0 -40
  562. package/dist/commands/onboard-create-identity.d.ts +0 -26
  563. package/dist/commands/onboard-create-identity.js +0 -23
  564. package/dist/commands/outpost-pruner.d.ts +0 -3
  565. package/dist/commands/outpost-pruner.js +0 -125
  566. package/dist/commands/personal-connector-settings-flag.d.ts +0 -4
  567. package/dist/commands/personal-connector-settings-flag.js +0 -7
  568. package/dist/commands/personal-targeted-push-flag.d.ts +0 -4
  569. package/dist/commands/personal-targeted-push-flag.js +0 -7
  570. package/dist/commands/personal-targeted-push-paths.d.ts +0 -3
  571. package/dist/commands/personal-targeted-push-paths.js +0 -67
  572. package/dist/commands/report.d.ts +0 -19
  573. package/dist/commands/report.js +0 -537
  574. package/dist/commands/run-feature-telemetry.d.ts +0 -3
  575. package/dist/commands/run-feature-telemetry.js +0 -6
  576. package/dist/commands/staff/reset-test-account.d.ts +0 -110
  577. package/dist/commands/staff/reset-test-account.js +0 -268
  578. package/dist/commands/staff/test-account.d.ts +0 -51
  579. package/dist/commands/staff/test-account.js +0 -196
  580. package/dist/commands/vault-retention-flag.d.ts +0 -3
  581. package/dist/commands/vault-retention-flag.js +0 -9
  582. package/dist/commands/vault-retention.d.ts +0 -11
  583. package/dist/commands/vault-retention.js +0 -69
  584. package/dist/commands/version.d.ts +0 -10
  585. package/dist/commands/version.js +0 -11
  586. package/dist/fast-scaffold.d.ts +0 -86
  587. package/dist/fast-scaffold.js +0 -364
  588. package/dist/lanes-drain-hook.d.ts +0 -14
  589. package/dist/lanes-drain-hook.js +0 -203
  590. package/dist/lib/agent-kit/adopt-identity.d.ts +0 -27
  591. package/dist/lib/agent-kit/adopt-identity.js +0 -60
  592. package/dist/lib/agent-kit/attachment-download.d.ts +0 -24
  593. package/dist/lib/agent-kit/attachment-download.js +0 -47
  594. package/dist/lib/agent-kit/creds.d.ts +0 -106
  595. package/dist/lib/agent-kit/creds.js +0 -163
  596. package/dist/lib/agent-kit/fallback.d.ts +0 -78
  597. package/dist/lib/agent-kit/fallback.js +0 -210
  598. package/dist/lib/agent-kit/inbox-state.d.ts +0 -83
  599. package/dist/lib/agent-kit/inbox-state.js +0 -159
  600. package/dist/lib/agent-kit/kit-config.d.ts +0 -56
  601. package/dist/lib/agent-kit/kit-config.js +0 -65
  602. package/dist/lib/agent-kit/log.d.ts +0 -16
  603. package/dist/lib/agent-kit/log.js +0 -46
  604. package/dist/lib/agent-kit/mcp/attachments.d.ts +0 -126
  605. package/dist/lib/agent-kit/mcp/attachments.js +0 -222
  606. package/dist/lib/agent-kit/mcp/jsonrpc.d.ts +0 -75
  607. package/dist/lib/agent-kit/mcp/jsonrpc.js +0 -164
  608. package/dist/lib/agent-kit/mcp/tools.d.ts +0 -64
  609. package/dist/lib/agent-kit/mcp/tools.js +0 -397
  610. package/dist/lib/agent-kit/paths.d.ts +0 -65
  611. package/dist/lib/agent-kit/paths.js +0 -118
  612. package/dist/lib/agent-kit/private-file.d.ts +0 -29
  613. package/dist/lib/agent-kit/private-file.js +0 -91
  614. package/dist/lib/agent-kit/run/heartbeat.d.ts +0 -72
  615. package/dist/lib/agent-kit/run/heartbeat.js +0 -128
  616. package/dist/lib/agent-kit/run/inbox.d.ts +0 -76
  617. package/dist/lib/agent-kit/run/inbox.js +0 -205
  618. package/dist/lib/agent-kit/run/supervisor.d.ts +0 -35
  619. package/dist/lib/agent-kit/run/supervisor.js +0 -85
  620. package/dist/lib/agent-kit/run/sync.d.ts +0 -33
  621. package/dist/lib/agent-kit/run/sync.js +0 -58
  622. package/dist/lib/agent-kit/services.d.ts +0 -31
  623. package/dist/lib/agent-kit/services.js +0 -55
  624. package/dist/lib/agent-kit/skills.d.ts +0 -18
  625. package/dist/lib/agent-kit/skills.js +0 -151
  626. package/dist/lib/agent-kit/wake.d.ts +0 -83
  627. package/dist/lib/agent-kit/wake.js +0 -243
  628. package/dist/lib/agents/model-catalog.d.ts +0 -62
  629. package/dist/lib/agents/model-catalog.js +0 -275
  630. package/dist/lib/auth-login-provider-state.d.ts +0 -8
  631. package/dist/lib/auth-login-provider-state.js +0 -63
  632. package/dist/lib/aws-sdk-node-warning-output.d.ts +0 -12
  633. package/dist/lib/aws-sdk-node-warning-output.js +0 -22
  634. package/dist/lib/billing/free-month.d.ts +0 -18
  635. package/dist/lib/billing/free-month.js +0 -31
  636. package/dist/lib/billing/plan-limit-refusals.d.ts +0 -69
  637. package/dist/lib/billing/plan-limit-refusals.js +0 -187
  638. package/dist/lib/billing/plan-limit-upgrade-url.d.ts +0 -11
  639. package/dist/lib/billing/plan-limit-upgrade-url.js +0 -66
  640. package/dist/lib/billing/plan-lock.d.ts +0 -143
  641. package/dist/lib/billing/plan-lock.js +0 -439
  642. package/dist/lib/bot/api.d.ts +0 -349
  643. package/dist/lib/bot/api.js +0 -316
  644. package/dist/lib/bot/attachments.d.ts +0 -45
  645. package/dist/lib/bot/attachments.js +0 -89
  646. package/dist/lib/bot/cli-update.d.ts +0 -138
  647. package/dist/lib/bot/cli-update.js +0 -278
  648. package/dist/lib/bot/company-bind.d.ts +0 -27
  649. package/dist/lib/bot/company-bind.js +0 -62
  650. package/dist/lib/bot/config.d.ts +0 -153
  651. package/dist/lib/bot/config.js +0 -233
  652. package/dist/lib/bot/continuity-download.d.ts +0 -28
  653. package/dist/lib/bot/continuity-download.js +0 -75
  654. package/dist/lib/bot/continuity-install.d.ts +0 -14
  655. package/dist/lib/bot/continuity-install.js +0 -101
  656. package/dist/lib/bot/continuity.d.ts +0 -67
  657. package/dist/lib/bot/continuity.js +0 -301
  658. package/dist/lib/bot/creds.d.ts +0 -24
  659. package/dist/lib/bot/creds.js +0 -51
  660. package/dist/lib/bot/daemon.d.ts +0 -173
  661. package/dist/lib/bot/daemon.js +0 -552
  662. package/dist/lib/bot/inbox-poll-backoff-flag.d.ts +0 -24
  663. package/dist/lib/bot/inbox-poll-backoff-flag.js +0 -135
  664. package/dist/lib/bot/inbox-state.d.ts +0 -18
  665. package/dist/lib/bot/inbox-state.js +0 -51
  666. package/dist/lib/bot/index.d.ts +0 -23
  667. package/dist/lib/bot/index.js +0 -23
  668. package/dist/lib/bot/inflight.d.ts +0 -119
  669. package/dist/lib/bot/inflight.js +0 -83
  670. package/dist/lib/bot/job-completions.d.ts +0 -89
  671. package/dist/lib/bot/job-completions.js +0 -132
  672. package/dist/lib/bot/job-run.d.ts +0 -109
  673. package/dist/lib/bot/job-run.js +0 -246
  674. package/dist/lib/bot/local-config.d.ts +0 -61
  675. package/dist/lib/bot/local-config.js +0 -145
  676. package/dist/lib/bot/local-jobs.d.ts +0 -89
  677. package/dist/lib/bot/local-jobs.js +0 -459
  678. package/dist/lib/bot/local-name.d.ts +0 -54
  679. package/dist/lib/bot/local-name.js +0 -114
  680. package/dist/lib/bot/log.d.ts +0 -13
  681. package/dist/lib/bot/log.js +0 -59
  682. package/dist/lib/bot/owner-context.d.ts +0 -79
  683. package/dist/lib/bot/owner-context.js +0 -175
  684. package/dist/lib/bot/paths.d.ts +0 -76
  685. package/dist/lib/bot/paths.js +0 -131
  686. package/dist/lib/bot/progress.d.ts +0 -132
  687. package/dist/lib/bot/progress.js +0 -289
  688. package/dist/lib/bot/promote.d.ts +0 -16
  689. package/dist/lib/bot/promote.js +0 -106
  690. package/dist/lib/bot/promotion-hold.d.ts +0 -24
  691. package/dist/lib/bot/promotion-hold.js +0 -103
  692. package/dist/lib/bot/promotion-receipt.d.ts +0 -9
  693. package/dist/lib/bot/promotion-receipt.js +0 -56
  694. package/dist/lib/bot/promotion-upload.d.ts +0 -16
  695. package/dist/lib/bot/promotion-upload.js +0 -80
  696. package/dist/lib/bot/prompt.d.ts +0 -136
  697. package/dist/lib/bot/prompt.js +0 -433
  698. package/dist/lib/bot/room-policy.d.ts +0 -63
  699. package/dist/lib/bot/room-policy.js +0 -91
  700. package/dist/lib/bot/run.d.ts +0 -217
  701. package/dist/lib/bot/run.js +0 -1869
  702. package/dist/lib/bot/runnable.d.ts +0 -51
  703. package/dist/lib/bot/runnable.js +0 -65
  704. package/dist/lib/bot/runtime/claude.d.ts +0 -50
  705. package/dist/lib/bot/runtime/claude.js +0 -170
  706. package/dist/lib/bot/runtime/codex.d.ts +0 -28
  707. package/dist/lib/bot/runtime/codex.js +0 -153
  708. package/dist/lib/bot/runtime/grok-acp.d.ts +0 -40
  709. package/dist/lib/bot/runtime/grok-acp.js +0 -265
  710. package/dist/lib/bot/runtime/grok.d.ts +0 -41
  711. package/dist/lib/bot/runtime/grok.js +0 -169
  712. package/dist/lib/bot/runtime/index.d.ts +0 -36
  713. package/dist/lib/bot/runtime/index.js +0 -417
  714. package/dist/lib/bot/runtime/messages-stream.d.ts +0 -56
  715. package/dist/lib/bot/runtime/messages-stream.js +0 -194
  716. package/dist/lib/bot/runtime/sanitize.d.ts +0 -18
  717. package/dist/lib/bot/runtime/sanitize.js +0 -28
  718. package/dist/lib/bot/runtime/types.d.ts +0 -159
  719. package/dist/lib/bot/runtime/types.js +0 -51
  720. package/dist/lib/bot/runtime/windows.d.ts +0 -10
  721. package/dist/lib/bot/runtime/windows.js +0 -53
  722. package/dist/lib/bot/runtime-sign-in.d.ts +0 -24
  723. package/dist/lib/bot/runtime-sign-in.js +0 -31
  724. package/dist/lib/bot/scaffold.d.ts +0 -38
  725. package/dist/lib/bot/scaffold.js +0 -94
  726. package/dist/lib/bot/self-heal.d.ts +0 -52
  727. package/dist/lib/bot/self-heal.js +0 -79
  728. package/dist/lib/bot/self-process.d.ts +0 -19
  729. package/dist/lib/bot/self-process.js +0 -35
  730. package/dist/lib/bot/session.d.ts +0 -19
  731. package/dist/lib/bot/session.js +0 -39
  732. package/dist/lib/bot/setup-explain.d.ts +0 -64
  733. package/dist/lib/bot/setup-explain.js +0 -141
  734. package/dist/lib/bot/split.d.ts +0 -32
  735. package/dist/lib/bot/split.js +0 -241
  736. package/dist/lib/bot/status.d.ts +0 -57
  737. package/dist/lib/bot/status.js +0 -66
  738. package/dist/lib/bot/worker-source.d.ts +0 -66
  739. package/dist/lib/bot/worker-source.js +0 -283
  740. package/dist/lib/checkpoint-ontology/aliases.d.ts +0 -17
  741. package/dist/lib/checkpoint-ontology/aliases.js +0 -44
  742. package/dist/lib/checkpoint-ontology/apply.d.ts +0 -110
  743. package/dist/lib/checkpoint-ontology/apply.js +0 -777
  744. package/dist/lib/checkpoint-ontology/company-brief.d.ts +0 -18
  745. package/dist/lib/checkpoint-ontology/company-brief.js +0 -337
  746. package/dist/lib/checkpoint-ontology/company.d.ts +0 -44
  747. package/dist/lib/checkpoint-ontology/company.js +0 -118
  748. package/dist/lib/checkpoint-ontology/drop-aliases.d.ts +0 -41
  749. package/dist/lib/checkpoint-ontology/drop-aliases.js +0 -76
  750. package/dist/lib/checkpoint-ontology/entity-file.d.ts +0 -109
  751. package/dist/lib/checkpoint-ontology/entity-file.js +0 -371
  752. package/dist/lib/checkpoint-ontology/identity.d.ts +0 -38
  753. package/dist/lib/checkpoint-ontology/identity.js +0 -95
  754. package/dist/lib/checkpoint-ontology/merge-copies.d.ts +0 -199
  755. package/dist/lib/checkpoint-ontology/merge-copies.js +0 -1086
  756. package/dist/lib/checkpoint-ontology/multi-writer.d.ts +0 -101
  757. package/dist/lib/checkpoint-ontology/multi-writer.js +0 -394
  758. package/dist/lib/checkpoint-ontology/plan.d.ts +0 -193
  759. package/dist/lib/checkpoint-ontology/plan.js +0 -504
  760. package/dist/lib/checkpoint-ontology/queued-touches.d.ts +0 -29
  761. package/dist/lib/checkpoint-ontology/queued-touches.js +0 -193
  762. package/dist/lib/checkpoint-ontology/sync-client.d.ts +0 -38
  763. package/dist/lib/checkpoint-ontology/sync-client.js +0 -84
  764. package/dist/lib/checkpoint-ontology/tree.d.ts +0 -55
  765. package/dist/lib/checkpoint-ontology/tree.js +0 -198
  766. package/dist/lib/company-errors-sdk-warning-flag.d.ts +0 -5
  767. package/dist/lib/company-errors-sdk-warning-flag.js +0 -8
  768. package/dist/lib/core-utils/hook-timeout-debug-context-flag.d.ts +0 -20
  769. package/dist/lib/core-utils/hook-timeout-debug-context-flag.js +0 -81
  770. package/dist/lib/core-utils/jq-json-serializer.d.ts +0 -5
  771. package/dist/lib/core-utils/jq-json-serializer.js +0 -10
  772. package/dist/lib/core-utils/secret-patterns.d.ts +0 -43
  773. package/dist/lib/core-utils/secret-patterns.js +0 -34
  774. package/dist/lib/core-utils/sentry-report.d.ts +0 -114
  775. package/dist/lib/core-utils/sentry-report.js +0 -1034
  776. package/dist/lib/core-utils/timeout-guard-native.d.ts +0 -7
  777. package/dist/lib/core-utils/timeout-guard-native.js +0 -40
  778. package/dist/lib/core-utils/worktree-resume.d.ts +0 -20
  779. package/dist/lib/core-utils/worktree-resume.js +0 -1198
  780. package/dist/lib/daemon/catalog.d.ts +0 -134
  781. package/dist/lib/daemon/catalog.js +0 -358
  782. package/dist/lib/daemon/config.d.ts +0 -35
  783. package/dist/lib/daemon/config.js +0 -108
  784. package/dist/lib/daemon/flag.d.ts +0 -25
  785. package/dist/lib/daemon/flag.js +0 -59
  786. package/dist/lib/daemon/handoff.d.ts +0 -56
  787. package/dist/lib/daemon/handoff.js +0 -161
  788. package/dist/lib/daemon/hq-cloud.d.ts +0 -79
  789. package/dist/lib/daemon/hq-cloud.js +0 -183
  790. package/dist/lib/daemon/install.d.ts +0 -85
  791. package/dist/lib/daemon/install.js +0 -320
  792. package/dist/lib/daemon/legacy-sync-env.d.ts +0 -10
  793. package/dist/lib/daemon/legacy-sync-env.js +0 -58
  794. package/dist/lib/daemon/legacy.d.ts +0 -45
  795. package/dist/lib/daemon/legacy.js +0 -133
  796. package/dist/lib/daemon/lock.d.ts +0 -47
  797. package/dist/lib/daemon/lock.js +0 -138
  798. package/dist/lib/daemon/machine-type.d.ts +0 -50
  799. package/dist/lib/daemon/machine-type.js +0 -159
  800. package/dist/lib/daemon/migrate.d.ts +0 -77
  801. package/dist/lib/daemon/migrate.js +0 -265
  802. package/dist/lib/daemon/paths.d.ts +0 -24
  803. package/dist/lib/daemon/paths.js +0 -43
  804. package/dist/lib/daemon/protocol-tail.d.ts +0 -33
  805. package/dist/lib/daemon/protocol-tail.js +0 -131
  806. package/dist/lib/daemon/remote-control.d.ts +0 -74
  807. package/dist/lib/daemon/remote-control.js +0 -153
  808. package/dist/lib/daemon/requests.d.ts +0 -28
  809. package/dist/lib/daemon/requests.js +0 -65
  810. package/dist/lib/daemon/run.d.ts +0 -79
  811. package/dist/lib/daemon/run.js +0 -755
  812. package/dist/lib/daemon/succession.d.ts +0 -118
  813. package/dist/lib/daemon/succession.js +0 -263
  814. package/dist/lib/daemon/supervisor.d.ts +0 -132
  815. package/dist/lib/daemon/supervisor.js +0 -836
  816. package/dist/lib/daemon/sync-runner.d.ts +0 -58
  817. package/dist/lib/daemon/sync-runner.js +0 -277
  818. package/dist/lib/daemon/tools.d.ts +0 -52
  819. package/dist/lib/daemon/tools.js +0 -95
  820. package/dist/lib/daemon/updater.d.ts +0 -57
  821. package/dist/lib/daemon/updater.js +0 -163
  822. package/dist/lib/deploy-api.d.ts +0 -5
  823. package/dist/lib/deploy-api.js +0 -21
  824. package/dist/lib/deploy-apps.d.ts +0 -13
  825. package/dist/lib/deploy-apps.js +0 -29
  826. package/dist/lib/doctor/__testing__/run-doctor.d.ts +0 -8
  827. package/dist/lib/doctor/__testing__/run-doctor.js +0 -52
  828. package/dist/lib/doctor/checks/bots.d.ts +0 -13
  829. package/dist/lib/doctor/checks/bots.js +0 -189
  830. package/dist/lib/doctor/checks/scaffold-cli-floor.d.ts +0 -6
  831. package/dist/lib/doctor/checks/scaffold-cli-floor.js +0 -73
  832. package/dist/lib/doctor/checks/sync-runtime.d.ts +0 -11
  833. package/dist/lib/doctor/checks/sync-runtime.js +0 -216
  834. package/dist/lib/doctor/client-health-flag-token.d.ts +0 -15
  835. package/dist/lib/doctor/client-health-flag-token.js +0 -66
  836. package/dist/lib/doctor/client-health-reason-codes-flag.d.ts +0 -27
  837. package/dist/lib/doctor/client-health-reason-codes-flag.js +0 -106
  838. package/dist/lib/doctor/fix/json-output.d.ts +0 -28
  839. package/dist/lib/doctor/fix/json-output.js +0 -33
  840. package/dist/lib/doctor/fix/refresh-sync-outcome.d.ts +0 -12
  841. package/dist/lib/doctor/fix/refresh-sync-outcome.js +0 -25
  842. package/dist/lib/doctor/shared.d.ts +0 -8
  843. package/dist/lib/doctor/shared.js +0 -38
  844. package/dist/lib/doctor/stray-gate-entries.d.ts +0 -35
  845. package/dist/lib/doctor/stray-gate-entries.js +0 -83
  846. package/dist/lib/first-sync-next-step.d.ts +0 -37
  847. package/dist/lib/first-sync-next-step.js +0 -189
  848. package/dist/lib/flag-registry-endpoint.d.ts +0 -5
  849. package/dist/lib/flag-registry-endpoint.js +0 -9
  850. package/dist/lib/flag-registry-worker-source.generated.d.ts +0 -2
  851. package/dist/lib/flag-registry-worker-source.generated.js +0 -3
  852. package/dist/lib/flag-snapshot-file.d.ts +0 -20
  853. package/dist/lib/flag-snapshot-file.js +0 -91
  854. package/dist/lib/heartbeat-identity.d.ts +0 -30
  855. package/dist/lib/heartbeat-identity.js +0 -76
  856. package/dist/lib/lanes/account-home.d.ts +0 -8
  857. package/dist/lib/lanes/account-home.js +0 -10
  858. package/dist/lib/lanes/accounts.d.ts +0 -117
  859. package/dist/lib/lanes/accounts.js +0 -366
  860. package/dist/lib/lanes/admission-host.d.ts +0 -22
  861. package/dist/lib/lanes/admission-host.js +0 -37
  862. package/dist/lib/lanes/admission.d.ts +0 -26
  863. package/dist/lib/lanes/admission.js +0 -210
  864. package/dist/lib/lanes/approval-gate.d.ts +0 -8
  865. package/dist/lib/lanes/approval-gate.js +0 -90
  866. package/dist/lib/lanes/autopilot-metrics.d.ts +0 -99
  867. package/dist/lib/lanes/autopilot-metrics.js +0 -301
  868. package/dist/lib/lanes/autopilot-registry.d.ts +0 -62
  869. package/dist/lib/lanes/autopilot-registry.js +0 -192
  870. package/dist/lib/lanes/autopilot.d.ts +0 -120
  871. package/dist/lib/lanes/autopilot.js +0 -1061
  872. package/dist/lib/lanes/blocker-freshness.d.ts +0 -31
  873. package/dist/lib/lanes/blocker-freshness.js +0 -168
  874. package/dist/lib/lanes/checked-company-path.d.ts +0 -3
  875. package/dist/lib/lanes/checked-company-path.js +0 -39
  876. package/dist/lib/lanes/claude-balance.d.ts +0 -97
  877. package/dist/lib/lanes/claude-balance.js +0 -319
  878. package/dist/lib/lanes/compaction.d.ts +0 -110
  879. package/dist/lib/lanes/compaction.js +0 -293
  880. package/dist/lib/lanes/config.d.ts +0 -94
  881. package/dist/lib/lanes/config.js +0 -606
  882. package/dist/lib/lanes/constants.d.ts +0 -3
  883. package/dist/lib/lanes/constants.js +0 -3
  884. package/dist/lib/lanes/delivery-core.d.ts +0 -15
  885. package/dist/lib/lanes/delivery-core.js +0 -21
  886. package/dist/lib/lanes/delivery.d.ts +0 -180
  887. package/dist/lib/lanes/delivery.js +0 -443
  888. package/dist/lib/lanes/disk-guard.d.ts +0 -66
  889. package/dist/lib/lanes/disk-guard.js +0 -172
  890. package/dist/lib/lanes/dropbox.d.ts +0 -129
  891. package/dist/lib/lanes/dropbox.js +0 -601
  892. package/dist/lib/lanes/effort.d.ts +0 -51
  893. package/dist/lib/lanes/effort.js +0 -113
  894. package/dist/lib/lanes/envelope-repair-dispatch.d.ts +0 -26
  895. package/dist/lib/lanes/envelope-repair-dispatch.js +0 -74
  896. package/dist/lib/lanes/errors.d.ts +0 -39
  897. package/dist/lib/lanes/errors.js +0 -72
  898. package/dist/lib/lanes/flag-keys.d.ts +0 -15
  899. package/dist/lib/lanes/flag-keys.js +0 -15
  900. package/dist/lib/lanes/grants.d.ts +0 -30
  901. package/dist/lib/lanes/grants.js +0 -171
  902. package/dist/lib/lanes/hire-grant-dispatch.d.ts +0 -29
  903. package/dist/lib/lanes/hire-grant-dispatch.js +0 -315
  904. package/dist/lib/lanes/hooks-install.d.ts +0 -82
  905. package/dist/lib/lanes/hooks-install.js +0 -479
  906. package/dist/lib/lanes/host-capacity.d.ts +0 -69
  907. package/dist/lib/lanes/host-capacity.js +0 -254
  908. package/dist/lib/lanes/hq-root.d.ts +0 -19
  909. package/dist/lib/lanes/hq-root.js +0 -28
  910. package/dist/lib/lanes/index.d.ts +0 -76
  911. package/dist/lib/lanes/index.js +0 -42
  912. package/dist/lib/lanes/keep-alive.d.ts +0 -68
  913. package/dist/lib/lanes/keep-alive.js +0 -343
  914. package/dist/lib/lanes/kr-freshness.d.ts +0 -76
  915. package/dist/lib/lanes/kr-freshness.js +0 -413
  916. package/dist/lib/lanes/lane-company.d.ts +0 -10
  917. package/dist/lib/lanes/lane-company.js +0 -41
  918. package/dist/lib/lanes/liveness.d.ts +0 -108
  919. package/dist/lib/lanes/liveness.js +0 -396
  920. package/dist/lib/lanes/machine-identity.d.ts +0 -34
  921. package/dist/lib/lanes/machine-identity.js +0 -92
  922. package/dist/lib/lanes/manager-status-gate.d.ts +0 -17
  923. package/dist/lib/lanes/manager-status-gate.js +0 -133
  924. package/dist/lib/lanes/merge-command.d.ts +0 -15
  925. package/dist/lib/lanes/merge-command.js +0 -629
  926. package/dist/lib/lanes/mesh-events.d.ts +0 -127
  927. package/dist/lib/lanes/mesh-events.js +0 -564
  928. package/dist/lib/lanes/message-replay.d.ts +0 -21
  929. package/dist/lib/lanes/message-replay.js +0 -122
  930. package/dist/lib/lanes/models.d.ts +0 -86
  931. package/dist/lib/lanes/models.js +0 -234
  932. package/dist/lib/lanes/monitor.d.ts +0 -48
  933. package/dist/lib/lanes/monitor.js +0 -428
  934. package/dist/lib/lanes/move-account.d.ts +0 -56
  935. package/dist/lib/lanes/move-account.js +0 -193
  936. package/dist/lib/lanes/orphans.d.ts +0 -25
  937. package/dist/lib/lanes/orphans.js +0 -102
  938. package/dist/lib/lanes/outpost-dispatch.d.ts +0 -147
  939. package/dist/lib/lanes/outpost-dispatch.js +0 -271
  940. package/dist/lib/lanes/outpost-hire-cache.d.ts +0 -22
  941. package/dist/lib/lanes/outpost-hire-cache.js +0 -153
  942. package/dist/lib/lanes/outpost-identity.d.ts +0 -33
  943. package/dist/lib/lanes/outpost-identity.js +0 -59
  944. package/dist/lib/lanes/outpost-profile-repin.d.ts +0 -17
  945. package/dist/lib/lanes/outpost-profile-repin.js +0 -84
  946. package/dist/lib/lanes/outpost-pruner-install.d.ts +0 -37
  947. package/dist/lib/lanes/outpost-pruner-install.js +0 -238
  948. package/dist/lib/lanes/outpost-registry-worker-approval.d.ts +0 -18
  949. package/dist/lib/lanes/outpost-registry-worker-approval.js +0 -109
  950. package/dist/lib/lanes/outpost-snapshot-memo.d.ts +0 -6
  951. package/dist/lib/lanes/outpost-snapshot-memo.js +0 -14
  952. package/dist/lib/lanes/outpost-worktree-pruner-flag.d.ts +0 -19
  953. package/dist/lib/lanes/outpost-worktree-pruner-flag.js +0 -86
  954. package/dist/lib/lanes/outpost-worktree-pruner.d.ts +0 -62
  955. package/dist/lib/lanes/outpost-worktree-pruner.js +0 -1201
  956. package/dist/lib/lanes/profile.d.ts +0 -69
  957. package/dist/lib/lanes/profile.js +0 -552
  958. package/dist/lib/lanes/provider-rejection.d.ts +0 -86
  959. package/dist/lib/lanes/provider-rejection.js +0 -286
  960. package/dist/lib/lanes/questions.d.ts +0 -90
  961. package/dist/lib/lanes/questions.js +0 -384
  962. package/dist/lib/lanes/quota.d.ts +0 -134
  963. package/dist/lib/lanes/quota.js +0 -471
  964. package/dist/lib/lanes/reconcile-install.d.ts +0 -90
  965. package/dist/lib/lanes/reconcile-install.js +0 -257
  966. package/dist/lib/lanes/reconcile.d.ts +0 -161
  967. package/dist/lib/lanes/reconcile.js +0 -3324
  968. package/dist/lib/lanes/registry-flag.d.ts +0 -35
  969. package/dist/lib/lanes/registry-flag.js +0 -164
  970. package/dist/lib/lanes/registry-persona.d.ts +0 -20
  971. package/dist/lib/lanes/registry-persona.js +0 -329
  972. package/dist/lib/lanes/registry.d.ts +0 -122
  973. package/dist/lib/lanes/registry.js +0 -570
  974. package/dist/lib/lanes/reviews.d.ts +0 -38
  975. package/dist/lib/lanes/reviews.js +0 -395
  976. package/dist/lib/lanes/runaway-tool-guard.d.ts +0 -38
  977. package/dist/lib/lanes/runaway-tool-guard.js +0 -284
  978. package/dist/lib/lanes/session-relink.d.ts +0 -25
  979. package/dist/lib/lanes/session-relink.js +0 -216
  980. package/dist/lib/lanes/spawn.d.ts +0 -355
  981. package/dist/lib/lanes/spawn.js +0 -2668
  982. package/dist/lib/lanes/ssm-command-context.d.ts +0 -12
  983. package/dist/lib/lanes/ssm-command-context.js +0 -52
  984. package/dist/lib/lanes/store.d.ts +0 -91
  985. package/dist/lib/lanes/store.js +0 -1822
  986. package/dist/lib/lanes/stories.d.ts +0 -23
  987. package/dist/lib/lanes/stories.js +0 -92
  988. package/dist/lib/lanes/story-ledger.d.ts +0 -80
  989. package/dist/lib/lanes/story-ledger.js +0 -815
  990. package/dist/lib/lanes/story-switch.d.ts +0 -21
  991. package/dist/lib/lanes/story-switch.js +0 -75
  992. package/dist/lib/lanes/text-scan.d.ts +0 -11
  993. package/dist/lib/lanes/text-scan.js +0 -103
  994. package/dist/lib/lanes/throttle.d.ts +0 -116
  995. package/dist/lib/lanes/throttle.js +0 -437
  996. package/dist/lib/lanes/timestamp.d.ts +0 -3
  997. package/dist/lib/lanes/timestamp.js +0 -26
  998. package/dist/lib/lanes/tracker-checks.d.ts +0 -77
  999. package/dist/lib/lanes/tracker-checks.js +0 -134
  1000. package/dist/lib/lanes/tracker-report-state.d.ts +0 -10
  1001. package/dist/lib/lanes/tracker-report-state.js +0 -124
  1002. package/dist/lib/lanes/tracker-report.d.ts +0 -203
  1003. package/dist/lib/lanes/tracker-report.js +0 -522
  1004. package/dist/lib/lanes/tracker-store.d.ts +0 -18
  1005. package/dist/lib/lanes/tracker-store.js +0 -106
  1006. package/dist/lib/lanes/types.d.ts +0 -1030
  1007. package/dist/lib/lanes/types.js +0 -418
  1008. package/dist/lib/lanes/wait.d.ts +0 -39
  1009. package/dist/lib/lanes/wait.js +0 -159
  1010. package/dist/lib/lanes/watch.d.ts +0 -39
  1011. package/dist/lib/lanes/watch.js +0 -249
  1012. package/dist/lib/lanes/watcher-main.d.ts +0 -11
  1013. package/dist/lib/lanes/watcher-main.js +0 -108
  1014. package/dist/lib/lanes/watcher.d.ts +0 -287
  1015. package/dist/lib/lanes/watcher.js +0 -1972
  1016. package/dist/lib/mesh/denial-codes.d.ts +0 -5
  1017. package/dist/lib/mesh/denial-codes.js +0 -14
  1018. package/dist/lib/mesh/live/daemon/config.d.ts +0 -36
  1019. package/dist/lib/mesh/live/daemon/config.js +0 -120
  1020. package/dist/lib/mesh/live/daemon/legacy-listen.d.ts +0 -43
  1021. package/dist/lib/mesh/live/daemon/legacy-listen.js +0 -89
  1022. package/dist/lib/mesh/live/daemon/live-cache.d.ts +0 -26
  1023. package/dist/lib/mesh/live/daemon/live-cache.js +0 -116
  1024. package/dist/lib/mesh/live/daemon/mqtt-events.d.ts +0 -7
  1025. package/dist/lib/mesh/live/daemon/mqtt-events.js +0 -2
  1026. package/dist/lib/mesh/live/daemon/mqtt-events.test-d.d.ts +0 -2
  1027. package/dist/lib/mesh/live/daemon/mqtt-events.test-d.js +0 -10
  1028. package/dist/lib/mesh/live/daemon/version-drift.d.ts +0 -5
  1029. package/dist/lib/mesh/live/daemon/version-drift.js +0 -47
  1030. package/dist/lib/mesh/live/daemon/work-subscribe.d.ts +0 -32
  1031. package/dist/lib/mesh/live/daemon/work-subscribe.js +0 -262
  1032. package/dist/lib/mesh/live/report-summary.d.ts +0 -45
  1033. package/dist/lib/mesh/live/report-summary.js +0 -234
  1034. package/dist/lib/mesh/live/session-seq.d.ts +0 -4
  1035. package/dist/lib/mesh/live/session-seq.js +0 -33
  1036. package/dist/lib/mesh/project-board.d.ts +0 -65
  1037. package/dist/lib/mesh/project-board.js +0 -181
  1038. package/dist/lib/mesh/story-status.d.ts +0 -22
  1039. package/dist/lib/mesh/story-status.js +0 -49
  1040. package/dist/lib/monitor.d.ts +0 -149
  1041. package/dist/lib/monitor.js +0 -519
  1042. package/dist/lib/node-binary.d.ts +0 -50
  1043. package/dist/lib/node-binary.js +0 -133
  1044. package/dist/lib/notify-recipient.d.ts +0 -7
  1045. package/dist/lib/notify-recipient.js +0 -11
  1046. package/dist/lib/onboarding/checkpoint.d.ts +0 -13
  1047. package/dist/lib/onboarding/checkpoint.js +0 -44
  1048. package/dist/lib/onboarding/cli/onboard.d.ts +0 -53
  1049. package/dist/lib/onboarding/cli/onboard.js +0 -135
  1050. package/dist/lib/onboarding/cli/prompts.d.ts +0 -28
  1051. package/dist/lib/onboarding/cli/prompts.js +0 -83
  1052. package/dist/lib/onboarding/errors.d.ts +0 -34
  1053. package/dist/lib/onboarding/errors.js +0 -59
  1054. package/dist/lib/onboarding/index.d.ts +0 -13
  1055. package/dist/lib/onboarding/index.js +0 -13
  1056. package/dist/lib/onboarding/orchestrator.d.ts +0 -47
  1057. package/dist/lib/onboarding/orchestrator.js +0 -569
  1058. package/dist/lib/onboarding/types.d.ts +0 -79
  1059. package/dist/lib/onboarding/types.js +0 -8
  1060. package/dist/lib/policy-validator.d.ts +0 -7
  1061. package/dist/lib/policy-validator.js +0 -187
  1062. package/dist/lib/process-flag-company-context.d.ts +0 -8
  1063. package/dist/lib/process-flag-company-context.js +0 -67
  1064. package/dist/lib/report-publish.d.ts +0 -175
  1065. package/dist/lib/report-publish.js +0 -421
  1066. package/dist/lib/report-run.d.ts +0 -62
  1067. package/dist/lib/report-run.js +0 -123
  1068. package/dist/lib/report-snapshot.d.ts +0 -12
  1069. package/dist/lib/report-snapshot.js +0 -135
  1070. package/dist/lib/report-templates.d.ts +0 -9
  1071. package/dist/lib/report-templates.js +0 -21
  1072. package/dist/lib/scaffold-cli-floor.d.ts +0 -8
  1073. package/dist/lib/scaffold-cli-floor.js +0 -50
  1074. package/dist/lib/search-index/collection-path-repair-flag.d.ts +0 -4
  1075. package/dist/lib/search-index/collection-path-repair-flag.js +0 -7
  1076. package/dist/lib/search-index/embed-lock.d.ts +0 -34
  1077. package/dist/lib/search-index/embed-lock.js +0 -295
  1078. package/dist/lib/search-index/load-gate.d.ts +0 -57
  1079. package/dist/lib/search-index/load-gate.js +0 -270
  1080. package/dist/lib/search-index/max-doc-bytes.d.ts +0 -219
  1081. package/dist/lib/search-index/max-doc-bytes.js +0 -463
  1082. package/dist/lib/search-index/qmd-exit-diagnostics.d.ts +0 -5
  1083. package/dist/lib/search-index/qmd-exit-diagnostics.js +0 -40
  1084. package/dist/lib/search-index/reindex-single-flight-flag.d.ts +0 -5
  1085. package/dist/lib/search-index/reindex-single-flight-flag.js +0 -8
  1086. package/dist/lib/service-manager/index.d.ts +0 -42
  1087. package/dist/lib/service-manager/index.js +0 -105
  1088. package/dist/lib/service-manager/launchd.d.ts +0 -81
  1089. package/dist/lib/service-manager/launchd.js +0 -212
  1090. package/dist/lib/service-manager/systemd.d.ts +0 -19
  1091. package/dist/lib/service-manager/systemd.js +0 -72
  1092. package/dist/lib/service-manager/types.d.ts +0 -34
  1093. package/dist/lib/service-manager/types.js +0 -26
  1094. package/dist/lib/test-utils/isolated-flag-company-context.d.ts +0 -3
  1095. package/dist/lib/test-utils/isolated-flag-company-context.js +0 -18
  1096. package/dist/lib/work-context/bind-best.d.ts +0 -66
  1097. package/dist/lib/work-context/bind-best.js +0 -344
  1098. package/dist/lib/work-context/bind-calling-session.d.ts +0 -26
  1099. package/dist/lib/work-context/bind-calling-session.js +0 -63
  1100. package/dist/lib/work-context/company-rebind.d.ts +0 -23
  1101. package/dist/lib/work-context/company-rebind.js +0 -59
  1102. package/dist/lib/work-context/default-company.d.ts +0 -43
  1103. package/dist/lib/work-context/default-company.js +0 -83
  1104. package/dist/lib/work-context/outbox-quarantine-status-flag.d.ts +0 -4
  1105. package/dist/lib/work-context/outbox-quarantine-status-flag.js +0 -7
  1106. package/dist/lib/work-context/prd-sync.d.ts +0 -56
  1107. package/dist/lib/work-context/prd-sync.js +0 -197
  1108. package/dist/lib/work-context/preflight-contract.d.ts +0 -28
  1109. package/dist/lib/work-context/preflight-contract.js +0 -85
  1110. package/dist/lib/work-context/prompt-rebind.d.ts +0 -25
  1111. package/dist/lib/work-context/prompt-rebind.js +0 -85
  1112. package/dist/lib/work-context/windows-acl.d.ts +0 -123
  1113. package/dist/lib/work-context/windows-acl.js +0 -425
  1114. package/dist/lib/workers-registry/read.d.ts +0 -15
  1115. package/dist/lib/workers-registry/read.js +0 -17
  1116. package/dist/native-fast-route.d.ts +0 -7
  1117. package/dist/native-fast-route.js +0 -45
  1118. package/dist/node-network-compat-core.d.ts +0 -12
  1119. package/dist/node-network-compat-core.js +0 -41
  1120. package/dist/node-network-compat-fast.d.ts +0 -7
  1121. package/dist/node-network-compat-fast.js +0 -23
  1122. package/dist/secrets-env-fast-path.d.ts +0 -5
  1123. package/dist/secrets-env-fast-path.js +0 -16
  1124. package/dist/secrets-env-fast-route.d.ts +0 -18
  1125. package/dist/secrets-env-fast-route.js +0 -54
  1126. package/dist/utils/checkpoint-stamp-write-error.d.ts +0 -28
  1127. package/dist/utils/checkpoint-stamp-write-error.js +0 -99
  1128. package/dist/utils/cli-network-lifecycle.d.ts +0 -4
  1129. package/dist/utils/cli-network-lifecycle.js +0 -11
  1130. package/dist/utils/cognito-refresh-sentry.d.ts +0 -3
  1131. package/dist/utils/cognito-refresh-sentry.js +0 -49
  1132. package/dist/utils/console-origin.d.ts +0 -9
  1133. package/dist/utils/console-origin.js +0 -28
  1134. package/dist/utils/core-eval-trigger.d.ts +0 -4
  1135. package/dist/utils/core-eval-trigger.js +0 -88
  1136. package/dist/utils/exclusive-lock.d.ts +0 -32
  1137. package/dist/utils/exclusive-lock.js +0 -75
  1138. package/dist/utils/fast-stream-errors.d.ts +0 -12
  1139. package/dist/utils/fast-stream-errors.js +0 -18
  1140. package/dist/utils/files-browse-acl.d.ts +0 -5
  1141. package/dist/utils/files-browse-acl.js +0 -11
  1142. package/dist/utils/hq-core-yaml.d.ts +0 -10
  1143. package/dist/utils/hq-core-yaml.js +0 -40
  1144. package/dist/utils/hq-root-resolver.d.ts +0 -13
  1145. package/dist/utils/hq-root-resolver.js +0 -103
  1146. package/dist/utils/redact-url.d.ts +0 -14
  1147. package/dist/utils/redact-url.js +0 -218
  1148. package/dist/utils/scaffold-fast-import-guard.d.ts +0 -2
  1149. package/dist/utils/scaffold-fast-import-guard.js +0 -40
  1150. package/dist/utils/scaffold-packlist-guard.d.ts +0 -7
  1151. package/dist/utils/scaffold-packlist-guard.js +0 -23
  1152. package/dist/utils/scaffold-sibling-references.d.ts +0 -22
  1153. package/dist/utils/scaffold-sibling-references.js +0 -156
  1154. package/dist/utils/secret-load-availability.d.ts +0 -8
  1155. package/dist/utils/secret-load-availability.js +0 -54
  1156. package/dist/utils/staged-install.d.ts +0 -34
  1157. package/dist/utils/staged-install.js +0 -346
  1158. package/dist/utils/staged-npm-install.d.ts +0 -16
  1159. package/dist/utils/staged-npm-install.js +0 -264
  1160. package/dist/utils/sync-log-sources.d.ts +0 -15
  1161. package/dist/utils/sync-log-sources.js +0 -36
  1162. package/dist/utils/table-width.d.ts +0 -7
  1163. package/dist/utils/table-width.js +0 -15
  1164. package/dist/utils/update-command-supervisor.cjs +0 -247
  1165. package/dist/utils/upload-author.d.ts +0 -15
  1166. package/dist/utils/upload-author.js +0 -39
  1167. package/dist/utils/version-report.d.ts +0 -27
  1168. package/dist/utils/version-report.js +0 -105
  1169. package/dist/version-request.d.ts +0 -14
  1170. package/dist/version-request.js +0 -36
  1171. package/scripts/mesh-daemon-if-stale.mjs +0 -20
  1172. package/templates/reports/blank.md +0 -3
  1173. package/templates/reports/daily-digest.md +0 -11
  1174. package/templates/runbooks/update-report.md +0 -21
@@ -1,3324 +0,0 @@
1
- /**
2
- * Periodic reconcile pass (spec §5.7, §6.8, §9).
3
- *
4
- * Owned by the machine, not by any lane. Reads never mutate; every write is a
5
- * compare-and-set. The per-lane watcher owns progress/terminal/silence of a
6
- * live process — this pass owns the transitions no single lane can own.
7
- */
8
- import * as fs from "node:fs";
9
- import * as path from "node:path";
10
- import { parseUserYaml } from "../../utils/user-yaml-error.js";
11
- import { compareLaneQueueOrder, hasActiveAdmissionReservation, hasActivePendingResume, laneAdmissionCapReason, laneRunsOnHost, withAllLaneAdmissionLocks, } from "./admission.js";
12
- import { accountStatus, isCredentialProven, resolveAccount } from "./accounts.js";
13
- import { effectiveAccountHome } from "./account-home.js";
14
- import { enforceRegistryApprovalGate } from "./approval-gate.js";
15
- import { loadLanesConfig, providerQuotaDefaultHoldMs } from "./config.js";
16
- import { MAX_LANE_DEPTH } from "./constants.js";
17
- import { AccountResolutionError, LanesError, isLaneVersionConflict } from "./errors.js";
18
- import { loadWorkerProfile } from "./profile.js";
19
- import { terminalChildResumeHireGrantError, withHireGrantAdmission, } from "./grants.js";
20
- import { classifySessionLiveness, defaultLivenessDeps, loadLivenessIndex, readDaemonPresenceConnected, unindexedClaudeSessionLiveness, } from "./liveness.js";
21
- import { ANSWERED_QUESTIONS_FILE, armLaneWatcher, envelopeRepairMode, isPidRecordAlive, laneRunDir, readPidRecord, readProcStarttime, readSpawnManifest, markSpawnFailure, stopLaneProcesses, spawnEnvelopeRepair, spawnLaneWorker, retryLaneOnAlternateAccount, } from "./spawn.js";
22
- import { appendCappedHistory, lanesStoreRoot, parseLaneRecord, parseQuestionRecord, readLane, readQuestion, listQuestionIds, updateLane, updateQuestion, } from "./store.js";
23
- import { applyThrottleDecision, decideAdmission, defaultThrottleDeps, emptyThrottleState, evaluateThrottle, isCpuCheckEnabled, readAvailableMemMb, readThrottleState, resolveCpuBusyFraction, sampleCpuBusyObservation, recordCpuBusyObservation, throttleStatePath, writeThrottleState, } from "./throttle.js";
24
- import { readLanesRegistryFlag, readLanesRegistryFlagFailOff } from "./registry-flag.js";
25
- import { LANE_HIRE_GRANT_FINISHED_LANES_FLAG } from "./flag-keys.js";
26
- import { checkLaneDiskSpace, configuredCodexRolloutHomes, lowDiskGuardEnabledForLane, retainCodexRollouts } from "./disk-guard.js";
27
- import { laneCompanyUidSource, resolveLaneCompany } from "./lane-company.js";
28
- import { appendDropboxMessage } from "./dropbox.js";
29
- import { DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG, listHostProcesses, runRunawayToolGuard, RUNAWAY_TOOL_GUARD_FLAG, } from "./runaway-tool-guard.js";
30
- import { LEAD_LANE_KEEP_ALIVE_FLAG, LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS, LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS, LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, queueLaneTerminalResume, LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT, LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, LEAD_LANE_KEEP_ALIVE_RESUME_TEXT, LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, doneKeepAliveVerdict, hasPendingKeepAliveWork, keepAliveExclusion, loadRetiredLaneIds, leadLaneKeepAliveBackoffMs, undeliveredAnsweredQuestions, } from "./keep-alive.js";
31
- import { providerSupportsSessionResume } from "./delivery.js";
32
- import { applyDefaultQuotaHold, clearAccountQuotaAfterSuccessfulTurnBestEffort, readQuotaSignal, recordAccountQuota, } from "./quota.js";
33
- import { answeredQuestionEntries, questionRecordRefusalCode, raiseEnvelopeQuestions, resolveQuestionCompanyWithFallback, } from "./questions.js";
34
- import { SILENCE_MS, appendWatcherEvent, classifyLaneExit, isPidAlive, missingExpectedEnvelopeFile, readLatestEnvelopeFile, readLatestEnvelopeRecord, watcherEventsPath, watcherOutLogPath, writeWatcherDecision, } from "./watcher.js";
35
- import { createLaneMesh, emitLaneDecisionEvents, emitLaneTerminalStateEvent, laneMeshCompany, } from "./mesh-events.js";
36
- import { activeLaneStories, laneStories, terminalEnvelopeStoryStatus } from "./stories.js";
37
- import { applyStorySwitchEnvelope } from "./story-switch.js";
38
- import { readProjectStoryStates } from "./story-ledger.js";
39
- import { decideEnvelopeRepair, envelopeQuestionIds, routeProviderFailure } from "./watcher.js";
40
- import { PROVIDER_REJECTED_REASON, providerRejectionDetails, readProviderRejection, } from "./provider-rejection.js";
41
- /** Spec §9: unanswered at the current hop for 60 minutes → one hop. */
42
- export const QUESTION_AGE_MS = 60 * 60 * 1000;
43
- /** Spec §6.6 + §9: a depth-3 worker has two hops to age toward the user. */
44
- export const MAX_AGED_ADVANCES = MAX_LANE_DEPTH - 1;
45
- /** Aligns with watcher silence: a host that has not reported for this long is dead. */
46
- export const DEAD_HOST_MS = SILENCE_MS;
47
- export const RECONCILE_LOCK_STALE_MS = 30_000;
48
- export const RECONCILE_LOCK_TIMEOUT_MS = 5_000;
49
- /** A pickup probe that has not cleared its marker within this window is abandoned. */
50
- export const PICKUP_VALIDATION_MAX_AGE_MS = 15 * 60 * 1000;
51
- export const PICKUP_VALIDATION_TIMEOUT_REASON = "pickup_validation_timeout";
52
- export const CLOSE_INTERRUPTED_DONE_STORY_WORKERS_FLAG = "lanes.close-interrupted-done-story-workers";
53
- const INTERRUPTED_DONE_STORY_MIN_AGE_MS = 24 * 60 * 60 * 1000;
54
- export const RECONCILE_UNIT_NAME = "hq-lanes-reconcile.service";
55
- export const RECONCILE_TIMER_NAME = "hq-lanes-reconcile.timer";
56
- function spawnDepsForHost(deps) {
57
- return {
58
- ...(deps.spawnLaneDeps ?? {}),
59
- env: deps.spawnLaneDeps?.env ?? deps.env ?? process.env,
60
- hostname: deps.host.hostname,
61
- admissionHost: deps.host,
62
- };
63
- }
64
- function logSkip(skip) {
65
- process.stderr.write(`hq lanes reconcile: skipped ${skip.kind}${skip.id ? ` ${skip.id}` : ""}: ${skip.why}\n`);
66
- }
67
- function emptySummary(dryRun) {
68
- return {
69
- ok: true,
70
- action: "reconcile",
71
- dry_run: dryRun,
72
- admitted: [],
73
- overdue: [],
74
- aged: [],
75
- advanced: [],
76
- resumed: [],
77
- dead_host: [],
78
- heartbeats: [],
79
- skipped: [],
80
- keep_alive_blocked: [],
81
- errors: [],
82
- heartbeat_at: null,
83
- };
84
- }
85
- function markDirty(summary, skip) {
86
- summary.skipped.push(skip);
87
- summary.ok = false;
88
- logSkip(skip);
89
- }
90
- function terminalizeModelUnsupported(hqRoot, lane, err) {
91
- if (!(err instanceof LanesError) || err.error !== "model_unsupported")
92
- return;
93
- try {
94
- markSpawnFailure(hqRoot, lane.lane_id, err.error, {
95
- ...err.details,
96
- message: err.message,
97
- });
98
- }
99
- catch (writeErr) {
100
- process.stderr.write(`hq lanes reconcile: failed to terminalize ${lane.lane_id} after model validation: ${writeErr instanceof Error ? writeErr.message : String(writeErr)}\n`);
101
- }
102
- }
103
- function markError(summary, err) {
104
- const message = err instanceof Error ? err.message : String(err);
105
- const code = err instanceof LanesError ? err.error : undefined;
106
- summary.errors.push(code ? { message, code } : { message });
107
- summary.ok = false;
108
- process.stderr.write(`hq lanes reconcile: ${message}${err instanceof Error && err.stack ? `\n${err.stack}` : ""}\n`);
109
- }
110
- export function reconcileHeartbeatPath(hqRoot) {
111
- return path.join(hqRoot, "workspace", "lanes-runs", "reconcile-heartbeat.json");
112
- }
113
- export function timeoutPath(hqRoot, laneId) {
114
- return path.join(laneRunDir(hqRoot, laneId), "timeout.json");
115
- }
116
- export function laneRunsHere(lane, host) {
117
- return laneRunsOnHost(lane, host);
118
- }
119
- function hasFreshAdmissionReservation(lane, nowMs) {
120
- return hasActiveAdmissionReservation(lane, nowMs);
121
- }
122
- export function agedAdvanceCount(question) {
123
- return question.routing_history.filter((entry) => entry.reason === "unanswered_timeout").length;
124
- }
125
- export function currentHopStartedAtMs(question) {
126
- const atCurrent = [...question.routing_history]
127
- .reverse()
128
- .find((entry) => entry.hop === question.hop);
129
- const raw = atCurrent?.at ?? question.asked_at;
130
- const ms = Date.parse(raw);
131
- return Number.isFinite(ms) ? ms : Number.NEGATIVE_INFINITY;
132
- }
133
- export function shouldAgeQuestion(question, audience, nowMs, ageWindowMs = QUESTION_AGE_MS) {
134
- if (question.status !== "pending")
135
- return false;
136
- if (audience.kind === "user")
137
- return false;
138
- if (agedAdvanceCount(question) >= MAX_AGED_ADVANCES)
139
- return false;
140
- const started = currentHopStartedAtMs(question);
141
- if (started === Number.NEGATIVE_INFINITY)
142
- return false;
143
- return nowMs - started >= ageWindowMs;
144
- }
145
- export function shouldAdvanceAudienceEnded(audience, liveness) {
146
- if (audience.kind === "user")
147
- return false;
148
- if (audience.kind !== "session")
149
- return false;
150
- return liveness === "ended";
151
- }
152
- export function readTimeoutDeadline(hqRoot, laneId) {
153
- const filePath = timeoutPath(hqRoot, laneId);
154
- let text;
155
- try {
156
- text = fs.readFileSync(filePath, "utf8");
157
- }
158
- catch (err) {
159
- const code = err.code;
160
- if (code === "ENOENT")
161
- return null;
162
- throw err;
163
- }
164
- let parsed;
165
- try {
166
- parsed = JSON.parse(text);
167
- }
168
- catch (err) {
169
- throw new LanesError("timeout_unreadable", `Timeout file for ${laneId} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`, { lane_id: laneId, path: filePath });
170
- }
171
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
172
- throw new LanesError("timeout_unreadable", `Timeout file for ${laneId} is not an object`, { lane_id: laneId, path: filePath });
173
- }
174
- const deadline = parsed.deadline_at;
175
- if (typeof deadline !== "string" || !Number.isFinite(Date.parse(deadline))) {
176
- return null;
177
- }
178
- const timeoutSecs = parsed.timeout_secs;
179
- return {
180
- deadline_at: deadline,
181
- timeout_secs: typeof timeoutSecs === "number" && Number.isInteger(timeoutSecs)
182
- ? timeoutSecs
183
- : undefined,
184
- };
185
- }
186
- export function isDeadlinePassed(deadlineAt, nowMs) {
187
- const at = Date.parse(deadlineAt);
188
- if (!Number.isFinite(at))
189
- return false;
190
- return nowMs > at;
191
- }
192
- function readDirJsonIds(dir, label) {
193
- let names;
194
- try {
195
- names = fs.readdirSync(dir);
196
- }
197
- catch (err) {
198
- const code = err.code;
199
- if (code === "ENOENT")
200
- return [];
201
- throw new LanesError("store_unreadable", `Failed to read ${label} directory ${dir}: ${err instanceof Error ? err.message : String(err)}`, { path: dir });
202
- }
203
- return names.filter((name) => typeof name === "string" &&
204
- name.endsWith(".json") &&
205
- !name.startsWith(".") &&
206
- !name.includes(".conflict-"));
207
- }
208
- function loadLaneRecords(hqRoot, summary) {
209
- const dir = path.join(lanesStoreRoot(hqRoot), "lanes");
210
- const names = readDirJsonIds(dir, "lane");
211
- const lanes = [];
212
- for (const name of names) {
213
- const filePath = path.join(dir, name);
214
- const id = name.slice(0, -".json".length);
215
- try {
216
- const text = fs.readFileSync(filePath, "utf8");
217
- const parsed = JSON.parse(text);
218
- lanes.push(parseLaneRecord(parsed, filePath));
219
- }
220
- catch (err) {
221
- markDirty(summary, {
222
- kind: "lane",
223
- id,
224
- why: `unreadable: ${err instanceof Error ? err.message : String(err)}`,
225
- });
226
- }
227
- }
228
- return lanes;
229
- }
230
- function loadQuestionRecords(hqRoot, summary) {
231
- const dir = path.join(lanesStoreRoot(hqRoot), "questions");
232
- const names = readDirJsonIds(dir, "question");
233
- const questions = [];
234
- for (const name of names) {
235
- const filePath = path.join(dir, name);
236
- const id = name.slice(0, -".json".length);
237
- try {
238
- const text = fs.readFileSync(filePath, "utf8");
239
- const parsed = JSON.parse(text);
240
- questions.push(parseQuestionRecord(parsed, filePath));
241
- }
242
- catch (err) {
243
- markDirty(summary, {
244
- kind: "question",
245
- id,
246
- why: `unreadable: ${err instanceof Error ? err.message : String(err)}`,
247
- });
248
- }
249
- }
250
- return questions;
251
- }
252
- function defaultReadSessionSenior(hqRoot, sessionId) {
253
- const filePath = path.join(hqRoot, "workspace", "sessions", sessionId, "meta.yaml");
254
- let text;
255
- try {
256
- text = fs.readFileSync(filePath, "utf8");
257
- }
258
- catch (err) {
259
- const code = err.code;
260
- if (code === "ENOENT")
261
- return { kind: "user" };
262
- throw err;
263
- }
264
- const doc = parseUserYaml(text, filePath);
265
- if (doc === null || typeof doc !== "object" || Array.isArray(doc)) {
266
- return { kind: "user" };
267
- }
268
- const senior = doc.senior;
269
- if (senior === "user" || senior === undefined || senior === null) {
270
- return { kind: "user" };
271
- }
272
- if (typeof senior === "string") {
273
- const colon = senior.indexOf(":");
274
- if (colon <= 0)
275
- return { kind: "user" };
276
- const kind = senior.slice(0, colon);
277
- const id = senior.slice(colon + 1);
278
- if (kind === "session" || kind === "lane")
279
- return { kind, id };
280
- if (kind === "user")
281
- return { kind: "user" };
282
- }
283
- if (typeof senior === "object" && !Array.isArray(senior)) {
284
- const rec = senior;
285
- if (rec.kind === "user")
286
- return { kind: "user" };
287
- if ((rec.kind === "session" || rec.kind === "lane") &&
288
- typeof rec.id === "string" &&
289
- rec.id.length > 0) {
290
- return { kind: rec.kind, id: rec.id };
291
- }
292
- }
293
- return { kind: "user" };
294
- }
295
- export function resolveQuestionAudience(question, readLaneSenior, readSessionSenior) {
296
- let current = { kind: "lane", id: question.origin.id };
297
- for (let hop = 0; hop < question.hop; hop += 1) {
298
- if (current.kind === "user")
299
- return current;
300
- if (current.kind === "lane") {
301
- if (!current.id)
302
- return current;
303
- current = readLaneSenior(current.id);
304
- continue;
305
- }
306
- if (!current.id)
307
- return current;
308
- current = readSessionSenior(current.id);
309
- }
310
- return current;
311
- }
312
- function casLaneIf(hqRoot, laneId, shouldWrite, updater, dryRun) {
313
- for (let i = 0; i < 5; i += 1) {
314
- const current = readLane(hqRoot, laneId);
315
- if (!shouldWrite(current))
316
- return { changed: false, record: current };
317
- if (dryRun)
318
- return { changed: true, record: updater(current) };
319
- try {
320
- return {
321
- changed: true,
322
- record: updateLane(hqRoot, laneId, current.version, "reconcile", updater),
323
- };
324
- }
325
- catch (err) {
326
- if (!isLaneVersionConflict(err) || i === 4)
327
- throw err;
328
- }
329
- }
330
- throw new LanesError("version_conflict", `Exhausted CAS retries for lane ${laneId}`, { lane_id: laneId });
331
- }
332
- function laneStoryLedgerIsClosed(hqRoot, lane) {
333
- const stories = laneStories(lane);
334
- if (stories.length === 0)
335
- return { closed: false, states: [] };
336
- const states = [];
337
- for (const projectId of new Set(stories.map((story) => story.project_id))) {
338
- const projectStories = stories.filter((story) => story.project_id === projectId);
339
- const ledgerStates = readProjectStoryStates(hqRoot, projectId, projectStories.map((story) => story.story_id), lane.company?.slug);
340
- for (const story of projectStories) {
341
- const state = ledgerStates.get(story.story_id);
342
- if (state === undefined)
343
- return { closed: false, states: [...states, "missing"] };
344
- states.push(state);
345
- }
346
- }
347
- return { closed: states.length > 0 && states.every((state) => state === "done" || state === "dropped"), states };
348
- }
349
- async function closeInterruptedDoneStoryWorkers(hqRoot, lanes, questions, summary, deps, now, enabledByCompany) {
350
- if (deps.dryRun)
351
- return;
352
- const nowMs = now.getTime();
353
- const at = now.toISOString();
354
- for (const lane of lanes) {
355
- if (lane.depth < 1 || lane.state !== "interrupted" || lane.keep_alive === true ||
356
- !laneRunsHere(lane, deps.host) || !lane.interrupted_at ||
357
- !Number.isFinite(Date.parse(lane.interrupted_at)) ||
358
- nowMs - Date.parse(lane.interrupted_at) < INTERRUPTED_DONE_STORY_MIN_AGE_MS ||
359
- hasPendingKeepAliveWork(hqRoot, lane, questions))
360
- continue;
361
- let company;
362
- try {
363
- company = resolveLaneCompany(hqRoot, lane).company;
364
- }
365
- catch (err) {
366
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `story close company lookup failed: ${err instanceof Error ? err.message : String(err)}` });
367
- continue;
368
- }
369
- if (!company || enabledByCompany.get(keepAliveCompanyKey(company)) !== true)
370
- continue;
371
- let ledger;
372
- try {
373
- ledger = laneStoryLedgerIsClosed(hqRoot, lane);
374
- }
375
- catch (err) {
376
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `story close ledger read failed: ${err instanceof Error ? err.message : String(err)}` });
377
- continue;
378
- }
379
- if (!ledger.closed)
380
- continue;
381
- try {
382
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => {
383
- const currentCompany = resolveLaneCompany(hqRoot, current).company;
384
- if (current.state !== "interrupted" || current.depth < 1 || current.keep_alive === true ||
385
- !currentCompany || enabledByCompany.get(keepAliveCompanyKey(currentCompany)) !== true ||
386
- !laneRunsHere(current, deps.host) || !current.interrupted_at ||
387
- !Number.isFinite(Date.parse(current.interrupted_at)) ||
388
- nowMs - Date.parse(current.interrupted_at) < INTERRUPTED_DONE_STORY_MIN_AGE_MS ||
389
- hasPendingKeepAliveWork(hqRoot, current, questions))
390
- return false;
391
- return laneStoryLedgerIsClosed(hqRoot, current).closed;
392
- }, (current) => ({
393
- ...current,
394
- state: "killed",
395
- queued_reason: undefined,
396
- queued_details: undefined,
397
- updated_at: at,
398
- terminal_reason: "story_closed",
399
- terminal_details: { ...(current.terminal_details ?? {}), operator_reason: "story_closed" },
400
- stop_history: [...(current.stop_history ?? []), { at, reason: "story_closed" }],
401
- }), false);
402
- if (!result.changed)
403
- continue;
404
- stopLaneProcesses(hqRoot, lane.lane_id, {
405
- killProcess: deps.spawnLaneDeps?.killProcess,
406
- isAlive: deps.isAlive,
407
- });
408
- for (const question of questions) {
409
- if (question.origin.id !== lane.lane_id || question.status !== "pending")
410
- continue;
411
- casQuestionIf(hqRoot, question.question_id, (current) => current.status === "pending", (current) => ({
412
- ...current,
413
- status: "cancelled",
414
- routing_history: appendCappedHistory(current.routing_history, {
415
- hop: current.hop,
416
- audience: { kind: "user" },
417
- at,
418
- reason: "cancelled",
419
- }),
420
- }), false);
421
- }
422
- appendWatcherEvent(watcherEventsPath(hqRoot, lane.lane_id), {
423
- event: "story_closed",
424
- at,
425
- lane_id: lane.lane_id,
426
- story_state: [...new Set(ledger.states)].join(","),
427
- terminal_reason: "story_closed",
428
- });
429
- const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
430
- if (mesh) {
431
- const timeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
432
- let timer;
433
- try {
434
- await Promise.race([
435
- emitLaneTerminalStateEvent(mesh, result.record, "killed", "story_closed"),
436
- new Promise((_resolve, reject) => {
437
- timer = setTimeout(() => reject(new Error(`terminal mesh emit timed out after ${timeoutMs}ms`)), timeoutMs);
438
- }),
439
- ]);
440
- }
441
- catch (err) {
442
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `story close mesh emit failed: ${err instanceof Error ? err.message : String(err)}` });
443
- }
444
- finally {
445
- if (timer)
446
- clearTimeout(timer);
447
- }
448
- }
449
- }
450
- catch (err) {
451
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `story close failed: ${err instanceof Error ? err.message : String(err)}` });
452
- }
453
- }
454
- }
455
- function writeQueueReason(hqRoot, lane, reason, dryRun) {
456
- if (lane.queued_reason === reason)
457
- return;
458
- casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "queued" && current.queued_reason !== reason, (current) => ({ ...current, queued_reason: reason, queued_details: undefined, admission_reserved_at: undefined }), dryRun);
459
- }
460
- function availableBalancedAccount(hqRoot, lane, occupying, config, host, profileMaxConcurrent, nowMs) {
461
- if (!lane.account || !lane.admission_accounts?.includes(lane.account))
462
- return undefined;
463
- if (laneAdmissionCapReason(occupying, lane, config, host, profileMaxConcurrent, nowMs) !== "account_cap") {
464
- return undefined;
465
- }
466
- const pinnedModel = lane.model_source === undefined || lane.model_source === "cli" || lane.model_source === "preserved"
467
- ? lane.model
468
- : lane.model_source === "profile"
469
- ? lane.model
470
- : undefined;
471
- for (const account of lane.admission_accounts) {
472
- if (account === lane.account)
473
- continue;
474
- if (config.providers[lane.provider].accounts[account]?.enabled !== true)
475
- continue;
476
- const status = accountStatus(lane.provider, account, config, {
477
- hostname: host.hostname,
478
- existsSync: (file) => fs.existsSync(file),
479
- });
480
- if (!isCredentialProven(status))
481
- continue;
482
- try {
483
- resolveAccount(lane.provider, account, config, {
484
- hqRoot,
485
- hostname: host.hostname,
486
- ...(pinnedModel !== undefined ? { model: pinnedModel } : {}),
487
- });
488
- }
489
- catch (err) {
490
- if (err instanceof AccountResolutionError)
491
- continue;
492
- throw err;
493
- }
494
- const candidate = { ...lane, account };
495
- if (laneAdmissionCapReason(occupying, candidate, config, host, profileMaxConcurrent, nowMs) === undefined) {
496
- return account;
497
- }
498
- }
499
- return undefined;
500
- }
501
- function writeAdmissionAccount(hqRoot, lane, account, dryRun) {
502
- return casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "queued" &&
503
- current.account === lane.account &&
504
- current.admission_accounts?.includes(account) === true, (current) => ({ ...current, account }), dryRun).record;
505
- }
506
- function casQuestionIf(hqRoot, questionId, shouldWrite, updater, dryRun) {
507
- for (let i = 0; i < 5; i += 1) {
508
- const current = readQuestion(hqRoot, questionId);
509
- if (!shouldWrite(current))
510
- return { changed: false, record: current };
511
- if (dryRun)
512
- return { changed: true, record: updater(current) };
513
- try {
514
- return {
515
- changed: true,
516
- record: updateQuestion(hqRoot, questionId, current.version, updater),
517
- };
518
- }
519
- catch (err) {
520
- if (!isLaneVersionConflict(err) || i === 4)
521
- throw err;
522
- }
523
- }
524
- throw new LanesError("version_conflict", `Exhausted CAS retries for question ${questionId}`, { question_id: questionId });
525
- }
526
- async function emitAgedQuestionAtUser(summary, deps, lane, question) {
527
- if (deps.dryRun)
528
- return;
529
- const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
530
- const company = laneMeshCompany(lane);
531
- if (!mesh || !company || !lane.thread_id)
532
- return;
533
- const timeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
534
- let timer;
535
- try {
536
- await Promise.race([
537
- mesh.appendThreadEvent(company, lane.thread_id, "lane_question_at_user", { lane_id: lane.lane_id, question_id: question.question_id }),
538
- new Promise((_resolve, reject) => {
539
- timer = setTimeout(() => reject(new Error(`question-at-user mesh emit timed out after ${timeoutMs}ms`)), timeoutMs);
540
- }),
541
- ]);
542
- }
543
- catch (err) {
544
- markDirty(summary, {
545
- kind: "question",
546
- id: question.question_id,
547
- why: `question-at-user mesh emit failed: ${err instanceof Error ? err.message : String(err)}`,
548
- });
549
- }
550
- finally {
551
- if (timer)
552
- clearTimeout(timer);
553
- }
554
- }
555
- function pickupValidationPendingAtMs(lane) {
556
- const raw = lane.pickup_validation_pending_since ?? lane.updated_at;
557
- const at = Date.parse(raw);
558
- return Number.isFinite(at) ? at : null;
559
- }
560
- function pickupValidationExpired(lane, nowMs) {
561
- const pendingAtMs = pickupValidationPendingAtMs(lane);
562
- return pendingAtMs === null || nowMs - pendingAtMs > PICKUP_VALIDATION_MAX_AGE_MS;
563
- }
564
- function expireAbandonedPickupValidation(hqRoot, lanes, summary, deps, now) {
565
- const nowMs = now.getTime();
566
- for (const lane of lanes) {
567
- if (lane.state !== "queued" ||
568
- lane.pickup_validation_pending !== true ||
569
- !laneRunsHere(lane, deps.host) ||
570
- !pickupValidationExpired(lane, nowMs)) {
571
- continue;
572
- }
573
- try {
574
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "queued" &&
575
- current.pickup_validation_pending === true &&
576
- pickupValidationExpired(current, nowMs), (current) => {
577
- const pendingAtMs = pickupValidationPendingAtMs(current);
578
- return {
579
- ...current,
580
- state: "failed",
581
- queued_reason: undefined,
582
- queued_details: undefined,
583
- pickup_validation_pending: undefined,
584
- pickup_validation_pending_since: undefined,
585
- terminal_reason: PICKUP_VALIDATION_TIMEOUT_REASON,
586
- terminal_details: {
587
- pickup_validation_pending_since: current.pickup_validation_pending_since ?? current.updated_at,
588
- age_ms: pendingAtMs === null ? null : Math.max(0, nowMs - pendingAtMs),
589
- max_age_ms: PICKUP_VALIDATION_MAX_AGE_MS,
590
- },
591
- };
592
- }, deps.dryRun);
593
- if (result.changed) {
594
- summary.aged.push({
595
- id: lane.lane_id,
596
- from: "queued",
597
- to: "failed",
598
- reason: PICKUP_VALIDATION_TIMEOUT_REASON,
599
- });
600
- }
601
- }
602
- catch (err) {
603
- markDirty(summary, {
604
- kind: "lane",
605
- id: lane.lane_id,
606
- why: `pickup validation expiry failed: ${err instanceof Error ? err.message : String(err)}`,
607
- });
608
- }
609
- }
610
- }
611
- function fileNonEmpty(filePath) {
612
- try {
613
- return fs.statSync(filePath).size > 0;
614
- }
615
- catch (err) {
616
- const code = err.code;
617
- if (code === "ENOENT")
618
- return false;
619
- throw err;
620
- }
621
- }
622
- function lastHostReportMs(lane) {
623
- const raw = lane.last_host_report_at ?? lane.updated_at;
624
- const ms = Date.parse(raw);
625
- return Number.isFinite(ms) ? ms : 0;
626
- }
627
- function writeHeartbeatFile(hqRoot, summary, at) {
628
- const filePath = reconcileHeartbeatPath(hqRoot);
629
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
630
- const tmp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
631
- const body = `${JSON.stringify({ at, ok: summary.ok, action: "reconcile" }, null, 2)}\n`;
632
- try {
633
- fs.writeFileSync(tmp, body, { encoding: "utf8" });
634
- fs.renameSync(tmp, filePath);
635
- }
636
- catch (err) {
637
- try {
638
- fs.unlinkSync(tmp);
639
- }
640
- catch (unlinkErr) {
641
- const code = unlinkErr.code;
642
- if (code !== "ENOENT") {
643
- process.stderr.write(`hq lanes reconcile: unlink heartbeat tmp failed: ${unlinkErr instanceof Error ? unlinkErr.message : String(unlinkErr)}\n`);
644
- }
645
- }
646
- throw err;
647
- }
648
- }
649
- function writeAnsweredQuestions(hqRoot, lane, questions) {
650
- const filePath = path.join(laneRunDir(hqRoot, lane.lane_id), ANSWERED_QUESTIONS_FILE);
651
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
652
- fs.writeFileSync(filePath, `${JSON.stringify(answeredQuestionEntries(hqRoot, lane, questions), null, 2)}\n`, "utf8");
653
- }
654
- async function withReconcileLock(hqRoot, fn) {
655
- const lockPath = path.join(hqRoot, "workspace", "lanes-runs", ".admission-locks", "reconcile.lock");
656
- fs.mkdirSync(path.dirname(lockPath), { recursive: true });
657
- const started = Date.now();
658
- while (true) {
659
- let fd;
660
- try {
661
- fd = fs.openSync(lockPath, "wx");
662
- try {
663
- fs.writeSync(fd, Buffer.from(`${process.pid}\n${Date.now()}\n`, "utf8"));
664
- }
665
- catch (err) {
666
- fs.closeSync(fd);
667
- fd = undefined;
668
- throw err;
669
- }
670
- try {
671
- return await fn();
672
- }
673
- finally {
674
- try {
675
- fs.closeSync(fd);
676
- }
677
- catch (err) {
678
- process.stderr.write(`hq lanes reconcile: lock close failed: ${err instanceof Error ? err.message : String(err)}\n`);
679
- }
680
- try {
681
- fs.unlinkSync(lockPath);
682
- }
683
- catch (err) {
684
- const code = err.code;
685
- if (code !== "ENOENT") {
686
- process.stderr.write(`hq lanes reconcile: lock unlink failed: ${err instanceof Error ? err.message : String(err)}\n`);
687
- }
688
- }
689
- }
690
- }
691
- catch (err) {
692
- const code = err.code;
693
- if (code !== "EEXIST")
694
- throw err;
695
- if (Date.now() - started > RECONCILE_LOCK_TIMEOUT_MS) {
696
- throw new LanesError("store_lock_timeout", `Timed out waiting for reconcile lock ${lockPath}`, { path: lockPath });
697
- }
698
- try {
699
- const raw = fs.readFileSync(lockPath, "utf8");
700
- const tsLine = raw.split("\n")[1];
701
- const ts = tsLine ? Number(tsLine) : NaN;
702
- if (Number.isFinite(ts) && Date.now() - ts > RECONCILE_LOCK_STALE_MS) {
703
- try {
704
- fs.unlinkSync(lockPath);
705
- }
706
- catch (unlinkErr) {
707
- const unlinkCode = unlinkErr.code;
708
- if (unlinkCode !== "ENOENT")
709
- throw unlinkErr;
710
- }
711
- continue;
712
- }
713
- }
714
- catch (statErr) {
715
- const statCode = statErr.code;
716
- if (statCode !== "ENOENT") {
717
- process.stderr.write(`hq lanes reconcile: stale-lock probe failed: ${statErr instanceof Error ? statErr.message : String(statErr)}\n`);
718
- }
719
- }
720
- Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
721
- }
722
- }
723
- }
724
- function classifyDeadLocalLane(hqRoot, laneId, now) {
725
- const runDir = laneRunDir(hqRoot, laneId);
726
- const manifest = readSpawnManifest(runDir);
727
- const minEnvelopeName = manifest.envelope_path ? path.basename(manifest.envelope_path) : undefined;
728
- const latest = readLatestEnvelopeRecord(hqRoot, laneId, minEnvelopeName);
729
- const envelope = latest?.envelope ?? null;
730
- const hasPartialOutput = [
731
- path.join(runDir, "logs", "worker.out"),
732
- path.join(runDir, "logs", "worker.err"),
733
- path.join(runDir, "logs", "last-message.txt"),
734
- ].some((file) => fileNonEmpty(file));
735
- const lane = readLane(hqRoot, laneId);
736
- // The same routing as the watcher's exit path (watcher.ts): a provider
737
- // failure on the stream is read first, a 429 or quota-worded one becomes
738
- // the quota signal, any other becomes provider_rejected, and only a stream
739
- // with no failure object falls back to the quota reader.
740
- const workerOutOffset = readSpawnManifest(runDir).worker_out_offset ?? 0;
741
- const routed = envelope
742
- ? { quota: null, rejection: null }
743
- : routeProviderFailure(readProviderRejection(runDir, lane.provider, workerOutOffset), () => readQuotaSignal(runDir, lane.model, now, workerOutOffset), lane.model, now);
744
- // As in the watcher: a reset-less 429 gets the provider's default hold.
745
- const quota = routed.quota
746
- ? applyDefaultQuotaHold(routed.quota, providerQuotaDefaultHoldMs(loadLanesConfig(hqRoot).providers[lane.provider]), now)
747
- : null;
748
- return {
749
- classification: classifyLaneExit({
750
- envelope,
751
- hasPartialOutput,
752
- quota,
753
- providerRejection: routed.rejection,
754
- }),
755
- latest,
756
- quota,
757
- providerRejection: routed.rejection,
758
- lane,
759
- };
760
- }
761
- function runawayGuardCompanyKey(company) {
762
- return company
763
- ? `${company.uid ?? ""}\u0000${company.slug ?? ""}`
764
- : "unresolved-company";
765
- }
766
- function reportRunawayGuardError(summary, stage, error) {
767
- summary.ok = false;
768
- summary.errors.push({
769
- code: `runaway_tool_guard_${stage}_failed`,
770
- message: `runaway search guard ${stage} failed; no additional search processes were stopped`,
771
- });
772
- process.stderr.write(`${JSON.stringify({
773
- event: "lanes.runaway_tool_guard.failed",
774
- stage,
775
- error: error instanceof Error ? error.name : "UnknownError",
776
- fallback: "continue_without_argv",
777
- })}\n`);
778
- }
779
- async function runRunawayToolGuardPass(hqRoot, lanes, config, deps, now, summary) {
780
- if (deps.dryRun)
781
- return;
782
- const localRunningLanes = lanes.filter((lane) => lane.state === "running" && laneRunsHere(lane, deps.host));
783
- if (localRunningLanes.length === 0)
784
- return;
785
- const groups = new Map();
786
- for (const lane of localRunningLanes) {
787
- let worker;
788
- try {
789
- worker = readPidRecord(path.join(laneRunDir(hqRoot, lane.lane_id), "worker.pid"));
790
- if (!worker ||
791
- worker.starttime === undefined ||
792
- !isPidRecordAlive(worker, deps.isAlive))
793
- continue;
794
- }
795
- catch (error) {
796
- reportRunawayGuardError(summary, "worker_pid_read", error);
797
- continue;
798
- }
799
- let company;
800
- try {
801
- company = resolveLaneCompany(hqRoot, lane).company;
802
- }
803
- catch (error) {
804
- reportRunawayGuardError(summary, "company_resolution", error);
805
- continue;
806
- }
807
- const key = runawayGuardCompanyKey(company);
808
- const group = groups.get(key) ?? { company, lanes: [] };
809
- group.lanes.push({ laneId: lane.lane_id, agentPid: worker.pid, agentStarttime: worker.starttime });
810
- groups.set(key, group);
811
- }
812
- if (groups.size === 0)
813
- return;
814
- const readRegistryFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
815
- const enabledGroups = new Set();
816
- const env = deps.env ?? process.env;
817
- await Promise.all([...groups.entries()].map(async ([key, group]) => {
818
- try {
819
- const enabled = await readRegistryFlag(RUNAWAY_TOOL_GUARD_FLAG, {
820
- company: group.company ?? undefined,
821
- env,
822
- });
823
- if (enabled)
824
- enabledGroups.add(key);
825
- }
826
- catch (error) {
827
- reportRunawayGuardError(summary, "flag_read", error);
828
- }
829
- }));
830
- if (enabledGroups.size === 0)
831
- return;
832
- const laneProcesses = [...groups.entries()]
833
- .filter(([key]) => enabledGroups.has(key))
834
- .flatMap(([, group]) => group.lanes);
835
- if (laneProcesses.length === 0)
836
- return;
837
- let processes;
838
- try {
839
- processes = (deps.runawayToolProcessLister ?? listHostProcesses)();
840
- }
841
- catch (error) {
842
- reportRunawayGuardError(summary, "process_table_read", error);
843
- return;
844
- }
845
- const runawayConfig = config.runaway_search_guard ?? DEFAULT_RUNAWAY_SEARCH_GUARD_CONFIG;
846
- try {
847
- runRunawayToolGuard({
848
- hqRoot,
849
- lanes: laneProcesses,
850
- processes,
851
- config: runawayConfig,
852
- now,
853
- currentUid: process.getuid?.() ?? -1,
854
- isCurrentProcess: deps.runawayToolProcessIsCurrent ??
855
- ((pid, expectedStarttime) => readProcStarttime(pid) === expectedStarttime),
856
- signal: deps.runawayToolProcessSignal ?? ((pid, signal) => process.kill(pid, signal)),
857
- recordEvent: (laneId, event) => appendWatcherEvent(watcherEventsPath(hqRoot, laneId), event, deps.runawayToolEventAppend),
858
- notifyLane: (laneId, text) => appendDropboxMessage(hqRoot, laneId, {
859
- at: now.toISOString(),
860
- message_id: `runaway-search-guard:${laneId}:${now.getTime()}`,
861
- kind: "system_notice",
862
- text,
863
- }),
864
- reportError: (stage, errorName) => {
865
- summary.ok = false;
866
- summary.errors.push({
867
- code: `runaway_tool_guard_${stage}_failed`,
868
- message: `runaway search guard could not ${stage} a stopped process (${errorName})`,
869
- });
870
- process.stderr.write(`${JSON.stringify({
871
- event: "lanes.runaway_tool_guard.failed",
872
- stage,
873
- error: errorName,
874
- fallback: "continue_without_argv",
875
- })}\n`);
876
- },
877
- });
878
- }
879
- catch (error) {
880
- reportRunawayGuardError(summary, "execution", error);
881
- }
882
- }
883
- /** Unlocked pass body. `runReconcilePass` takes the process lock around this. */
884
- export async function runPassBody(hqRoot, deps) {
885
- const now = deps.now();
886
- const nowMs = now.getTime();
887
- const nowIso = now.toISOString();
888
- const summary = emptySummary(deps.dryRun);
889
- const livenessDeps = deps.livenessDeps ?? defaultLivenessDeps();
890
- let lanes;
891
- let questions;
892
- try {
893
- lanes = loadLaneRecords(hqRoot, summary);
894
- questions = loadQuestionRecords(hqRoot, summary);
895
- }
896
- catch (err) {
897
- markError(summary, err);
898
- return summary;
899
- }
900
- if (deps.terminalKeepAliveCandidate) {
901
- return runTerminalKeepAliveCandidatePass(hqRoot, lanes, summary, deps, now, deps.terminalKeepAliveCandidate);
902
- }
903
- let presenceConnected;
904
- try {
905
- presenceConnected =
906
- deps.presenceConnected ?? readDaemonPresenceConnected(livenessDeps);
907
- }
908
- catch (err) {
909
- markError(summary, err);
910
- return summary;
911
- }
912
- let livenessIndex;
913
- try {
914
- livenessIndex = loadLivenessIndex(hqRoot, undefined, livenessDeps, !deps.dryRun);
915
- }
916
- catch (err) {
917
- markError(summary, err);
918
- return summary;
919
- }
920
- const config = loadLanesConfig(hqRoot);
921
- try {
922
- const companyRefs = new Map();
923
- for (const lane of lanes) {
924
- const company = resolveLaneCompany(hqRoot, lane).company;
925
- if (company)
926
- companyRefs.set(company.uid ?? company.slug, company);
927
- }
928
- const readFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
929
- const homes = configuredCodexRolloutHomes(hqRoot, config, deps.host.hostname);
930
- let retentionEnabled = false;
931
- for (const company of homes.length > 0 ? companyRefs.values() : []) {
932
- retentionEnabled ||= await readFlag("lanes.codex-rollout-retention", { company });
933
- if (retentionEnabled)
934
- break;
935
- }
936
- if (retentionEnabled) {
937
- const statePath = path.join(hqRoot, "workspace", "lanes-runs", `.rollout-retention-${deps.host.hostname.replaceAll(/[^a-zA-Z0-9_-]/g, "_")}.json`);
938
- let lastRunAt;
939
- try {
940
- const state = JSON.parse(fs.readFileSync(statePath, "utf8"));
941
- if (typeof state.last_run_at !== "string" || !Number.isFinite(Date.parse(state.last_run_at))) {
942
- throw new LanesError("rollout_retention_state_invalid", `Invalid rollout-retention state at ${statePath}`, { path: statePath });
943
- }
944
- lastRunAt = Date.parse(state.last_run_at);
945
- }
946
- catch (err) {
947
- if (err.code !== "ENOENT")
948
- throw err;
949
- }
950
- const lowDisk = homes.some((home) => checkLaneDiskSpace(hqRoot, home, deps.spawnLaneDeps?.statfs, config.low_disk_min_free_bytes).low);
951
- if (lowDisk || lastRunAt === undefined || nowMs - lastRunAt >= 60 * 60 * 1000) {
952
- const result = await retainCodexRollouts({
953
- homes,
954
- lanes,
955
- nowMs,
956
- compressAfterMs: config.codex_rollout_compress_after_hours * 60 * 60 * 1000,
957
- deleteAfterMs: config.codex_rollout_delete_after_days * 24 * 60 * 60 * 1000,
958
- });
959
- summary.rollout_retention = result;
960
- fs.mkdirSync(path.dirname(statePath), { recursive: true });
961
- const tempPath = `${statePath}.${process.pid}.tmp`;
962
- fs.writeFileSync(tempPath, `${JSON.stringify({ last_run_at: nowIso }, null, 2)}\n`, { flag: "wx" });
963
- fs.renameSync(tempPath, statePath);
964
- }
965
- }
966
- }
967
- catch (err) {
968
- markDirty(summary, { kind: "store", why: `Codex rollout retention failed: ${err instanceof Error ? err.message : String(err)}` });
969
- }
970
- const laneById = new Map(lanes.map((lane) => [lane.lane_id, lane]));
971
- const readLaneSenior = (laneId) => {
972
- const lane = laneById.get(laneId);
973
- if (lane)
974
- return lane.senior;
975
- try {
976
- return readLane(hqRoot, laneId).senior;
977
- }
978
- catch (err) {
979
- throw new LanesError("lane_not_found", `Cannot resolve senior for missing lane ${laneId}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: laneId });
980
- }
981
- };
982
- const readSessionSenior = deps.readSessionSenior ??
983
- ((sessionId) => defaultReadSessionSenior(hqRoot, sessionId));
984
- const configuredClaudeHomes = Object.values(config.providers.claude.accounts).map((account) => effectiveAccountHome(account, deps.host.hostname).home);
985
- const sessionStateCache = new Map();
986
- const sessionState = (sessionId) => {
987
- if (sessionStateCache.has(sessionId)) {
988
- return sessionStateCache.get(sessionId) ?? null;
989
- }
990
- const entry = livenessIndex.sessions[sessionId];
991
- if (!entry) {
992
- const state = unindexedClaudeSessionLiveness(sessionId, nowMs, config.liveness, livenessDeps, configuredClaudeHomes);
993
- sessionStateCache.set(sessionId, state);
994
- return state;
995
- }
996
- const state = classifySessionLiveness(entry, nowMs, presenceConnected, config.liveness);
997
- sessionStateCache.set(sessionId, state);
998
- return state;
999
- };
1000
- const questionAgeWindowMs = (audience) => audience.kind === "session"
1001
- ? config.session_question_timeout_secs * 1000
1002
- : QUESTION_AGE_MS;
1003
- if (deps.afterLoad)
1004
- await deps.afterLoad();
1005
- await runRunawayToolGuardPass(hqRoot, lanes, config, deps, now, summary);
1006
- const startDeps = deps;
1007
- markOverdueLanes(hqRoot, lanes, summary, deps, now);
1008
- await heartbeatAndDeadHosts(hqRoot, lanes, summary, startDeps, now);
1009
- for (const question of questions) {
1010
- if (question.status !== "pending")
1011
- continue;
1012
- let audience;
1013
- try {
1014
- audience = resolveQuestionAudience(question, readLaneSenior, readSessionSenior);
1015
- }
1016
- catch (err) {
1017
- markDirty(summary, {
1018
- kind: "question",
1019
- id: question.question_id,
1020
- why: `audience unreadable: ${err instanceof Error ? err.message : String(err)}`,
1021
- });
1022
- continue;
1023
- }
1024
- try {
1025
- let liveness;
1026
- try {
1027
- liveness =
1028
- audience.kind === "session" && audience.id
1029
- ? sessionState(audience.id)
1030
- : audience.kind === "user"
1031
- ? "live"
1032
- : null;
1033
- }
1034
- catch (err) {
1035
- // A failed metadata probe cannot prove that the session ended, but it
1036
- // must not suppress the independent unanswered-timeout rule below.
1037
- markError(summary, err);
1038
- liveness = null;
1039
- }
1040
- if (shouldAdvanceAudienceEnded(audience, liveness)) {
1041
- const nextHop = question.hop + 1;
1042
- const nextAudience = resolveQuestionAudience({ ...question, hop: nextHop }, readLaneSenior, readSessionSenior);
1043
- const result = casQuestionIf(hqRoot, question.question_id, (current) => current.status === "pending" && current.hop === question.hop, (current) => {
1044
- const entry = {
1045
- hop: nextHop,
1046
- audience: nextAudience,
1047
- at: nowIso,
1048
- reason: "audience_ended",
1049
- };
1050
- return {
1051
- ...current,
1052
- hop: nextHop,
1053
- routing_history: appendCappedHistory(current.routing_history, entry),
1054
- };
1055
- }, deps.dryRun);
1056
- if (result.changed) {
1057
- summary.advanced.push({
1058
- id: question.question_id,
1059
- from: String(question.hop),
1060
- to: String(nextHop),
1061
- reason: "audience_ended",
1062
- });
1063
- }
1064
- continue;
1065
- }
1066
- if (shouldAgeQuestion(question, audience, nowMs, questionAgeWindowMs(audience))) {
1067
- const nextHop = question.hop + 1;
1068
- const nextAudience = resolveQuestionAudience({ ...question, hop: nextHop }, readLaneSenior, readSessionSenior);
1069
- const result = casQuestionIf(hqRoot, question.question_id, (current) => {
1070
- if (current.status !== "pending" ||
1071
- agedAdvanceCount(current) >= MAX_AGED_ADVANCES) {
1072
- return false;
1073
- }
1074
- const currentAudience = resolveQuestionAudience(current, readLaneSenior, readSessionSenior);
1075
- return shouldAgeQuestion(current, currentAudience, nowMs, questionAgeWindowMs(currentAudience));
1076
- }, (current) => {
1077
- const entry = {
1078
- hop: current.hop + 1,
1079
- audience: nextAudience,
1080
- at: nowIso,
1081
- reason: "unanswered_timeout",
1082
- };
1083
- return {
1084
- ...current,
1085
- hop: current.hop + 1,
1086
- routing_history: appendCappedHistory(current.routing_history, entry),
1087
- };
1088
- }, deps.dryRun);
1089
- if (result.changed) {
1090
- summary.aged.push({
1091
- id: question.question_id,
1092
- from: String(question.hop),
1093
- to: String(nextHop),
1094
- reason: "unanswered_timeout",
1095
- });
1096
- if (nextAudience.kind === "user") {
1097
- const originLane = laneById.get(question.lane_id) ?? readLane(hqRoot, question.lane_id);
1098
- await emitAgedQuestionAtUser(summary, deps, originLane, result.record);
1099
- }
1100
- }
1101
- }
1102
- }
1103
- catch (err) {
1104
- markDirty(summary, {
1105
- kind: "question",
1106
- id: question.question_id,
1107
- why: `advance failed: ${err instanceof LanesError ? `${err.error}: ` : ""}${err instanceof Error ? err.message : String(err)}`,
1108
- });
1109
- }
1110
- }
1111
- const readRegistryFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
1112
- const storyCloseCompanies = new Map();
1113
- for (const lane of lanes) {
1114
- if (lane.depth < 1 || lane.state !== "interrupted" || lane.keep_alive === true ||
1115
- !laneRunsHere(lane, deps.host) || !lane.interrupted_at ||
1116
- !Number.isFinite(Date.parse(lane.interrupted_at)) ||
1117
- nowMs - Date.parse(lane.interrupted_at) < INTERRUPTED_DONE_STORY_MIN_AGE_MS ||
1118
- hasPendingKeepAliveWork(hqRoot, lane, questions) || laneStories(lane).length === 0)
1119
- continue;
1120
- try {
1121
- const company = resolveLaneCompany(hqRoot, lane).company;
1122
- if (company)
1123
- storyCloseCompanies.set(keepAliveCompanyKey(company), company);
1124
- }
1125
- catch (err) {
1126
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `story close company lookup failed: ${err instanceof Error ? err.message : String(err)}` });
1127
- }
1128
- }
1129
- const storyCloseEnabledByCompany = new Map();
1130
- const env = deps.env ?? process.env;
1131
- await Promise.all([...storyCloseCompanies.entries()].map(async ([key, company]) => {
1132
- try {
1133
- storyCloseEnabledByCompany.set(key, await readRegistryFlag(CLOSE_INTERRUPTED_DONE_STORY_WORKERS_FLAG, {
1134
- company: company ?? undefined,
1135
- env,
1136
- }));
1137
- }
1138
- catch (err) {
1139
- storyCloseEnabledByCompany.set(key, false);
1140
- markDirty(summary, { kind: "lane", why: `story close flag read failed for ${company?.slug ?? "unresolved company"}: ${err instanceof Error ? err.message : String(err)}` });
1141
- }
1142
- }));
1143
- const keepAliveFlagCompanies = new Map();
1144
- for (const lane of lanes) {
1145
- if (!(lane.state === "done" || lane.state === "interrupted" || lane.state === "blocked" || lane.state === "killed" || lane.state === "cancelled" || lane.state === "failed") ||
1146
- (lane.keep_alive !== true && lane.worker_profile === undefined) ||
1147
- (lane.state === "blocked" && lane.keep_alive !== true) ||
1148
- !laneRunsHere(lane, deps.host) ||
1149
- (lane.state === "done" && (lane.terminal_reason === PROVIDER_REJECTED_REASON ||
1150
- lane.terminal_reason === "account_not_logged_in" ||
1151
- !providerSupportsSessionResume(lane.provider) ||
1152
- !(lane.provider_session_id ?? lane.last_resume?.session_id))))
1153
- continue;
1154
- if (lane.state === "done") {
1155
- const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
1156
- if (!envelope || envelope.decision !== "done")
1157
- continue;
1158
- const hasQueuedWork = lane.keep_alive_standing === true && hasPendingKeepAliveWork(hqRoot, lane, questions);
1159
- if (doneKeepAliveVerdict(lane, envelope, hasQueuedWork, {
1160
- hasPendingWork: hasPendingKeepAliveWork(hqRoot, lane, questions),
1161
- nowMs: now.getTime(),
1162
- nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
1163
- }) !== "eligible")
1164
- continue;
1165
- }
1166
- const company = resolveLaneCompany(hqRoot, lane).company;
1167
- if (company === null)
1168
- continue;
1169
- const key = keepAliveCompanyKey(company);
1170
- keepAliveFlagCompanies.set(key, company);
1171
- }
1172
- const keepAliveFlagByCompany = new Map();
1173
- const blockedResumeFlagByCompany = new Map();
1174
- const immediateFirstResumeFlagByCompany = new Map();
1175
- const supersessionLineageAwareByCompany = new Map();
1176
- await Promise.all([...keepAliveFlagCompanies.entries()].map(async ([key, company]) => {
1177
- const options = {
1178
- company: company ?? undefined,
1179
- env,
1180
- };
1181
- const hasBlockedKeepAliveLane = lanes.some((lane) => lane.state === "blocked" &&
1182
- lane.keep_alive === true &&
1183
- laneRunsHere(lane, startDeps.host) &&
1184
- keepAliveCompanyKey(resolveLaneCompany(hqRoot, lane).company) === key);
1185
- const [keepAliveEnabled, lineageAware, blockedResumeEnabled, immediateFirstResumeEnabled] = await Promise.all([
1186
- readRegistryFlag(LEAD_LANE_KEEP_ALIVE_FLAG, options),
1187
- readRegistryFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, options),
1188
- hasBlockedKeepAliveLane
1189
- ? readRegistryFlag(LEAD_LANE_KEEP_ALIVE_RESUME_BLOCKED_FLAG, options)
1190
- : Promise.resolve(false),
1191
- readRegistryFlag(LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, options),
1192
- ]);
1193
- keepAliveFlagByCompany.set(key, keepAliveEnabled);
1194
- blockedResumeFlagByCompany.set(key, blockedResumeEnabled);
1195
- immediateFirstResumeFlagByCompany.set(key, immediateFirstResumeEnabled);
1196
- supersessionLineageAwareByCompany.set(key, lineageAware);
1197
- }));
1198
- const admissionThrottleDeps = startDeps.throttleDeps ?? defaultThrottleDeps(startDeps.env ?? process.env);
1199
- const hasAdmissionCandidates = lanes.some((lane) => laneRunsHere(lane, startDeps.host) &&
1200
- (lane.state === "queued" || lane.state === "awaiting_input")) || [...keepAliveFlagByCompany.values()].some(Boolean);
1201
- const priorThrottleState = readThrottleState(throttleStatePath(hqRoot), admissionThrottleDeps);
1202
- const priorThrottleSnapshot = priorThrottleState === "missing" || priorThrottleState === "unreadable"
1203
- ? undefined
1204
- : priorThrottleState.cpu_snapshot;
1205
- const cpuObservation = await sampleCpuBusyObservation(admissionThrottleDeps, priorThrottleSnapshot, { measureBusy: hasAdmissionCandidates });
1206
- const admissionDeps = {
1207
- ...startDeps,
1208
- cpuBusyFraction: cpuObservation.busy,
1209
- cpuObservation,
1210
- skipAdmissionWithoutCpuSample: !hasAdmissionCandidates,
1211
- };
1212
- await withAllLaneAdmissionLocks(hqRoot, async () => {
1213
- const freshLanes = loadLaneRecords(hqRoot, summary);
1214
- const freshQuestions = loadQuestionRecords(hqRoot, summary);
1215
- await closeInterruptedDoneStoryWorkers(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, storyCloseEnabledByCompany);
1216
- await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, admissionDeps, now, keepAliveFlagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany, immediateFirstResumeFlagByCompany, deps.terminalKeepAliveCandidate);
1217
- const drainLanes = loadLaneRecords(hqRoot, summary);
1218
- await admitQueued(hqRoot, drainLanes, summary, admissionDeps, now);
1219
- await resumeAwaiting(hqRoot, drainLanes, freshQuestions, summary, admissionDeps);
1220
- });
1221
- if (!deps.dryRun && cpuObservation.snapshot !== undefined) {
1222
- const latestThrottleState = readThrottleState(throttleStatePath(hqRoot), admissionThrottleDeps);
1223
- if (latestThrottleState === "unreadable") {
1224
- markError(summary, new LanesError("throttle_state_unreadable", "Throttle state became unreadable while reconciling"));
1225
- }
1226
- else {
1227
- const currentThrottleState = latestThrottleState === "missing"
1228
- ? emptyThrottleState()
1229
- : latestThrottleState;
1230
- writeThrottleState(throttleStatePath(hqRoot), recordCpuBusyObservation(currentThrottleState, cpuObservation), admissionThrottleDeps);
1231
- }
1232
- }
1233
- if (!deps.dryRun && summary.errors.length === 0) {
1234
- try {
1235
- writeHeartbeatFile(hqRoot, summary, nowIso);
1236
- summary.heartbeat_at = nowIso;
1237
- }
1238
- catch (err) {
1239
- markError(summary, err);
1240
- }
1241
- }
1242
- return summary;
1243
- }
1244
- function markOverdueLanes(hqRoot, lanes, summary, deps, now) {
1245
- const nowMs = now.getTime();
1246
- const nowIso = now.toISOString();
1247
- for (const lane of lanes) {
1248
- if (lane.overdue === true)
1249
- continue;
1250
- if (!laneRunsHere(lane, deps.host))
1251
- continue;
1252
- let deadline;
1253
- try {
1254
- deadline = readTimeoutDeadline(hqRoot, lane.lane_id);
1255
- }
1256
- catch (err) {
1257
- markDirty(summary, {
1258
- kind: "lane",
1259
- id: lane.lane_id,
1260
- why: `timeout unreadable: ${err instanceof Error ? err.message : String(err)}`,
1261
- });
1262
- continue;
1263
- }
1264
- if (!deadline || !isDeadlinePassed(deadline.deadline_at, nowMs))
1265
- continue;
1266
- try {
1267
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.overdue !== true, (current) => ({
1268
- ...current,
1269
- overdue: true,
1270
- overdue_at: nowIso,
1271
- }), deps.dryRun);
1272
- if (result.changed) {
1273
- summary.overdue.push({
1274
- id: lane.lane_id,
1275
- from: "ok",
1276
- to: "overdue",
1277
- });
1278
- }
1279
- }
1280
- catch (err) {
1281
- markDirty(summary, {
1282
- kind: "lane",
1283
- id: lane.lane_id,
1284
- why: `overdue CAS failed: ${err instanceof Error ? err.message : String(err)}`,
1285
- });
1286
- }
1287
- }
1288
- }
1289
- async function heartbeatAndDeadHosts(hqRoot, lanes, summary, deps, now) {
1290
- const nowMs = now.getTime();
1291
- const nowIso = now.toISOString();
1292
- const isAlive = deps.isAlive ?? isPidAlive;
1293
- for (const lane of lanes) {
1294
- if (lane.state !== "running")
1295
- continue;
1296
- const local = laneRunsHere(lane, deps.host);
1297
- if (local) {
1298
- const runDir = laneRunDir(hqRoot, lane.lane_id);
1299
- let workerPid;
1300
- let watcherPid;
1301
- let workerAlive;
1302
- let watcherAlive;
1303
- try {
1304
- const workerRec = readPidRecord(path.join(runDir, "worker.pid"));
1305
- const watcherRec = readPidRecord(path.join(runDir, "watcher.pid"));
1306
- workerPid = workerRec?.pid ?? null;
1307
- watcherPid = watcherRec?.pid ?? null;
1308
- workerAlive = workerRec !== null && isPidRecordAlive(workerRec, isAlive);
1309
- watcherAlive = watcherRec !== null && isPidRecordAlive(watcherRec, isAlive);
1310
- }
1311
- catch (err) {
1312
- markDirty(summary, {
1313
- kind: "lane",
1314
- id: lane.lane_id,
1315
- why: `pid file unreadable: ${err instanceof Error ? err.message : String(err)}`,
1316
- });
1317
- continue;
1318
- }
1319
- if (workerAlive) {
1320
- try {
1321
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.last_host_report_at !== nowIso, (current) => ({ ...current, last_host_report_at: nowIso }), deps.dryRun);
1322
- if (result.changed)
1323
- summary.heartbeats.push(lane.lane_id);
1324
- }
1325
- catch (err) {
1326
- markDirty(summary, {
1327
- kind: "lane",
1328
- id: lane.lane_id,
1329
- why: `heartbeat CAS failed: ${err instanceof Error ? err.message : String(err)}`,
1330
- });
1331
- }
1332
- if (!watcherAlive && !deps.dryRun) {
1333
- try {
1334
- armLaneWatcher(hqRoot, lane.lane_id, spawnDepsForHost(deps));
1335
- summary.heartbeats.push(`${lane.lane_id}:watcher-rearmed`);
1336
- }
1337
- catch (err) {
1338
- markDirty(summary, {
1339
- kind: "lane",
1340
- id: lane.lane_id,
1341
- why: `watcher re-arm failed: ${err instanceof Error ? err.message : String(err)}`,
1342
- });
1343
- }
1344
- }
1345
- continue;
1346
- }
1347
- if (watcherAlive) {
1348
- // Watcher owns terminal classification of a process that has just exited.
1349
- continue;
1350
- }
1351
- const hadPids = workerPid !== null || watcherPid !== null;
1352
- if (!hadPids && nowMs - lastHostReportMs(lane) < DEAD_HOST_MS) {
1353
- try {
1354
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.last_host_report_at !== nowIso, (current) => ({ ...current, last_host_report_at: nowIso }), deps.dryRun);
1355
- if (result.changed)
1356
- summary.heartbeats.push(lane.lane_id);
1357
- }
1358
- catch (err) {
1359
- markDirty(summary, {
1360
- kind: "lane",
1361
- id: lane.lane_id,
1362
- why: `heartbeat CAS failed: ${err instanceof Error ? err.message : String(err)}`,
1363
- });
1364
- }
1365
- continue;
1366
- }
1367
- try {
1368
- const { classification, latest, quota, providerRejection, lane: classified } = classifyDeadLocalLane(hqRoot, lane.lane_id, now);
1369
- // Match the watcher's one-repair decision before classifying an
1370
- // unparseable envelope after both processes have died. As in the
1371
- // watcher, a provider rejection or a quota signal skips the repair.
1372
- const runDir = laneRunDir(hqRoot, lane.lane_id);
1373
- const manifest = readSpawnManifest(runDir);
1374
- const minEnvelopeName = manifest.envelope_path ? path.basename(manifest.envelope_path) : undefined;
1375
- const rawLatest = readLatestEnvelopeFile(hqRoot, lane.lane_id, minEnvelopeName);
1376
- const missingExpected = rawLatest === null
1377
- ? missingExpectedEnvelopeFile(hqRoot, lane.lane_id, manifest.envelope_path)
1378
- : null;
1379
- const repairCandidate = rawLatest ?? missingExpected;
1380
- let invalidEnvelope = null;
1381
- if (!deps.dryRun &&
1382
- !providerRejection &&
1383
- !quota &&
1384
- repairCandidate &&
1385
- repairCandidate.envelope === null &&
1386
- (!repairCandidate.missing || envelopeRepairMode(classified.provider) === "resume")) {
1387
- if (await lowDiskGuardEnabledForLane(hqRoot, classified, deps.readRegistryFlag)) {
1388
- const repairConfig = loadLanesConfig(hqRoot);
1389
- const repairAccount = classified.account
1390
- ? repairConfig.providers[classified.provider].accounts[classified.account]
1391
- : undefined;
1392
- if (!repairAccount) {
1393
- throw new LanesError("low_disk_account_unavailable", `Cannot determine provider home for envelope repair of lane ${classified.lane_id}`, { lane_id: classified.lane_id });
1394
- }
1395
- const providerHome = effectiveAccountHome(repairAccount, deps.host.hostname).home;
1396
- const free = checkLaneDiskSpace(hqRoot, providerHome, deps.spawnLaneDeps?.statfs, repairConfig.low_disk_min_free_bytes);
1397
- if (free.low) {
1398
- const measuredAt = now.toISOString();
1399
- updateLane(hqRoot, classified.lane_id, classified.version, "reconcile", (current) => ({
1400
- ...current,
1401
- state: "queued",
1402
- queued_reason: "low_disk",
1403
- queued_details: { reason: "low_disk", hq_free_bytes: free.hq.free_bytes, provider_free_bytes: free.provider.free_bytes, measured_at: measuredAt },
1404
- queued_at: current.queued_at ?? measuredAt,
1405
- admission_reserved_at: undefined,
1406
- terminal_reason: undefined,
1407
- terminal_details: undefined,
1408
- }));
1409
- summary.skipped.push({ kind: "lane", id: classified.lane_id, why: `low_disk: hq=${free.hq.free_bytes} provider=${free.provider.free_bytes}` });
1410
- continue;
1411
- }
1412
- }
1413
- const spawnRepair = deps.spawnRepair ??
1414
- ((root, repairLane, input) => spawnEnvelopeRepair(root, repairLane, input, spawnDepsForHost(deps)));
1415
- const repair = decideEnvelopeRepair(hqRoot, lane.lane_id, repairCandidate, nowMs, spawnRepair, () => undefined);
1416
- if (repair.action === "spawned") {
1417
- armLaneWatcher(hqRoot, lane.lane_id, spawnDepsForHost(deps));
1418
- summary.heartbeats.push(`${lane.lane_id}:envelope-repair`);
1419
- continue;
1420
- }
1421
- if (!repairCandidate.missing)
1422
- invalidEnvelope = { errors: repair.errors };
1423
- }
1424
- let repairedClassification = invalidEnvelope
1425
- ? classifyLaneExit({ envelope: null, hasPartialOutput: false, invalidEnvelope })
1426
- : classification;
1427
- let questionRefusalCode = null;
1428
- const accountRetryReason = quota
1429
- ? "usage_limit"
1430
- : providerRejection && classification.reason === "account_not_logged_in"
1431
- ? "account_unavailable"
1432
- : null;
1433
- if (!deps.dryRun &&
1434
- quota &&
1435
- classified.account &&
1436
- (classification.reason === "account_quota_exhausted" ||
1437
- classification.reason === "account_model_unavailable")) {
1438
- recordAccountQuota(hqRoot, classified.provider, classified.account, quota);
1439
- }
1440
- if (!deps.dryRun &&
1441
- accountRetryReason &&
1442
- classified.account) {
1443
- try {
1444
- const recovered = await retryLaneOnAlternateAccount(hqRoot, classified, accountRetryReason, "reconcile", {
1445
- ...spawnDepsForHost(deps),
1446
- });
1447
- summary.dead_host.push({
1448
- id: lane.lane_id,
1449
- from: "running",
1450
- to: recovered.state,
1451
- reason: accountRetryReason,
1452
- });
1453
- summary.heartbeats.push(`${lane.lane_id}:account-retry`);
1454
- continue;
1455
- }
1456
- catch (err) {
1457
- const message = err instanceof Error ? err.message : String(err);
1458
- process.stderr.write(`hq lanes reconcile: account failover failed for ${lane.lane_id}: ${message}\n`);
1459
- if (readLane(hqRoot, lane.lane_id).state !== "running") {
1460
- summary.errors.push({ message: `${lane.lane_id}: account failover left a non-running record after ${message}` });
1461
- continue;
1462
- }
1463
- }
1464
- }
1465
- if (!deps.dryRun &&
1466
- classification.state === "awaiting_input" &&
1467
- latest &&
1468
- (latest.envelope.questions?.length ?? 0) > 0) {
1469
- const questionLane = readLane(hqRoot, lane.lane_id);
1470
- try {
1471
- const activeCompany = await resolveQuestionCompanyWithFallback(hqRoot, questionLane, deps.resolveQuestionActiveCompany);
1472
- raiseEnvelopeQuestions(hqRoot, questionLane, latest.envelope, latest.ref, nowIso, undefined, activeCompany);
1473
- }
1474
- catch (err) {
1475
- questionRefusalCode = questionRecordRefusalCode(err);
1476
- if (questionRefusalCode === null)
1477
- throw err;
1478
- repairedClassification = {
1479
- state: "failed",
1480
- reason: `question_record_refused:${questionRefusalCode}`,
1481
- };
1482
- process.stderr.write(`hq lanes reconcile: terminal ask refused for ${lane.lane_id}: ${questionRefusalCode}\n`);
1483
- }
1484
- }
1485
- let terminalEnvelope = latest?.envelope;
1486
- let terminalClassification = repairedClassification;
1487
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "running", (current) => {
1488
- const approvalGate = latest?.envelope
1489
- ? enforceRegistryApprovalGate(current, latest.envelope)
1490
- : undefined;
1491
- terminalEnvelope = approvalGate?.envelope ?? latest?.envelope;
1492
- terminalClassification = approvalGate?.violation
1493
- ? { state: "blocked", reason: "approval_gate_violated" }
1494
- : repairedClassification;
1495
- const next = {
1496
- ...current,
1497
- state: terminalClassification.state,
1498
- queued_reason: undefined,
1499
- queued_details: undefined,
1500
- terminal_reason: terminalClassification.reason ?? "host_lost",
1501
- interrupted_at: terminalClassification.state === "interrupted" ? nowIso : undefined,
1502
- ...(providerRejection
1503
- ? { terminal_details: providerRejectionDetails(providerRejection, current) }
1504
- : {}),
1505
- ...(quota?.quota_reset_at ? { quota_reset_at: quota.quota_reset_at } : {}),
1506
- ...(quota?.marker ? { model_quota_marker: quota.marker } : {}),
1507
- last_host_report_at: nowIso,
1508
- };
1509
- if (approvalGate && !approvalGate.violation && approvalGate.approvedIdsToConsume.length > 0) {
1510
- next.registry_worker_approval_history = approvalGate.approvedIdsToConsume.reduce((history, approvalId) => appendCappedHistory(history, {
1511
- event: "consumed",
1512
- approval_id: approvalId,
1513
- envelope_ref: latest.ref,
1514
- at: nowIso,
1515
- }), current.registry_worker_approval_history ?? []);
1516
- }
1517
- if (terminalEnvelope && latest) {
1518
- const envelope = terminalEnvelope;
1519
- next.last_envelope = {
1520
- ref: latest.ref,
1521
- decision: envelope.decision,
1522
- at: nowIso,
1523
- artifacts: envelope.artifacts,
1524
- ...(envelope.blocked_reason !== undefined ? { blocked_reason: envelope.blocked_reason } : {}),
1525
- };
1526
- const storySwitch = applyStorySwitchEnvelope(current, envelope, nowIso, latest.ref);
1527
- if (storySwitch.clearPending) {
1528
- next.story_switch_pending = undefined;
1529
- }
1530
- if (storySwitch.storySwitchUnacknowledged !== undefined) {
1531
- next.story_switch_unacknowledged = storySwitch.storySwitchUnacknowledged;
1532
- next.attribution_history = storySwitch.attributionHistory ?? current.attribution_history;
1533
- }
1534
- const storyStatus = storySwitch.suppressCompletion
1535
- ? "in_progress"
1536
- : terminalEnvelopeStoryStatus(current, envelope.decision, envelope.story_status);
1537
- if (storyStatus !== undefined) {
1538
- next.story_status = storySwitch.suppressCompletion ? "in_progress" : envelope.story_status;
1539
- next.story_complete = storySwitch.suppressCompletion ? false : envelope.story_complete;
1540
- next.story_complete_reason = storySwitch.suppressCompletion ? undefined : envelope.story_complete_reason;
1541
- next.stories = laneStories(current).map((story) => story.project_id === current.project_id && story.story_id === current.story_id
1542
- ? {
1543
- ...story,
1544
- status: storyStatus,
1545
- updated_at: nowIso,
1546
- ...(envelope.decision === "blocked"
1547
- ? { blocked_reason: envelope.blocked_reason ?? "" }
1548
- : { blocked_reason: undefined }),
1549
- }
1550
- : story);
1551
- }
1552
- if (approvalGate?.violation) {
1553
- next.last_envelope = {
1554
- ref: latest.ref,
1555
- decision: envelope.decision,
1556
- at: nowIso,
1557
- artifacts: envelope.artifacts,
1558
- blocked_reason: envelope.blocked_reason,
1559
- };
1560
- }
1561
- }
1562
- return next;
1563
- }, deps.dryRun);
1564
- if (!deps.dryRun &&
1565
- result.changed &&
1566
- result.record.state === "done" &&
1567
- terminalClassification.state === "done" &&
1568
- terminalEnvelope?.decision === "done" &&
1569
- !quota &&
1570
- !providerRejection &&
1571
- result.record.account &&
1572
- result.record.model) {
1573
- clearAccountQuotaAfterSuccessfulTurnBestEffort(hqRoot, result.record.provider, result.record.account, result.record.model, new Date(nowIso));
1574
- }
1575
- if (result.changed) {
1576
- summary.dead_host.push({
1577
- id: lane.lane_id,
1578
- from: "running",
1579
- to: terminalClassification.state,
1580
- reason: terminalClassification.reason ?? "host_lost",
1581
- });
1582
- if (terminalEnvelope && latest && questionRefusalCode === null) {
1583
- const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
1584
- if (mesh) {
1585
- const meshEmitTimeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
1586
- let timer;
1587
- try {
1588
- const questionIds = terminalEnvelope.decision === "ask"
1589
- ? envelopeQuestionIds(lane.lane_id, latest.ref, terminalEnvelope)
1590
- : undefined;
1591
- await Promise.race([
1592
- emitLaneDecisionEvents(mesh, result.record, terminalEnvelope, { questionIds }),
1593
- new Promise((_resolve, reject) => {
1594
- timer = setTimeout(() => reject(new Error(`terminal mesh emit timed out after ${meshEmitTimeoutMs}ms`)), meshEmitTimeoutMs);
1595
- }),
1596
- ]);
1597
- }
1598
- catch (err) {
1599
- markDirty(summary, {
1600
- kind: "lane",
1601
- id: lane.lane_id,
1602
- why: `terminal mesh emit failed: ${err instanceof Error ? err.message : String(err)}`,
1603
- });
1604
- }
1605
- finally {
1606
- if (timer)
1607
- clearTimeout(timer);
1608
- }
1609
- }
1610
- }
1611
- else if (!deps.dryRun &&
1612
- (result.record.state === "failed" || result.record.state === "interrupted" ||
1613
- result.record.state === "cancelled" || result.record.state === "killed")) {
1614
- const mesh = deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh;
1615
- if (mesh) {
1616
- const meshEmitTimeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
1617
- let timer;
1618
- try {
1619
- await Promise.race([
1620
- emitLaneTerminalStateEvent(mesh, result.record, result.record.state, result.record.terminal_reason),
1621
- new Promise((_resolve, reject) => {
1622
- timer = setTimeout(() => reject(new Error(`terminal mesh emit timed out after ${meshEmitTimeoutMs}ms`)), meshEmitTimeoutMs);
1623
- }),
1624
- ]);
1625
- }
1626
- catch (err) {
1627
- markDirty(summary, {
1628
- kind: "lane",
1629
- id: lane.lane_id,
1630
- why: `terminal mesh emit failed: ${err instanceof Error ? err.message : String(err)}`,
1631
- });
1632
- }
1633
- finally {
1634
- if (timer)
1635
- clearTimeout(timer);
1636
- }
1637
- }
1638
- }
1639
- if (!deps.dryRun && deps.recoverQueuedMessages) {
1640
- try {
1641
- await deps.recoverQueuedMessages(result.record);
1642
- }
1643
- catch (err) {
1644
- markDirty(summary, {
1645
- kind: "lane",
1646
- id: lane.lane_id,
1647
- why: `queued message recovery failed: ${err instanceof Error ? err.message : String(err)}`,
1648
- });
1649
- }
1650
- }
1651
- }
1652
- }
1653
- catch (err) {
1654
- markDirty(summary, {
1655
- kind: "lane",
1656
- id: lane.lane_id,
1657
- why: `dead-host classify failed: ${err instanceof Error ? err.message : String(err)}`,
1658
- });
1659
- }
1660
- continue;
1661
- }
1662
- if (nowMs - lastHostReportMs(lane) < DEAD_HOST_MS)
1663
- continue;
1664
- try {
1665
- const result = casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "running" &&
1666
- nowMs - lastHostReportMs(current) >= DEAD_HOST_MS, (current) => ({
1667
- ...current,
1668
- state: "interrupted",
1669
- queued_reason: undefined,
1670
- queued_details: undefined,
1671
- terminal_reason: "host_lost",
1672
- interrupted_at: nowIso,
1673
- }), deps.dryRun);
1674
- if (result.changed) {
1675
- summary.dead_host.push({
1676
- id: lane.lane_id,
1677
- from: "running",
1678
- to: "interrupted",
1679
- reason: "host_lost",
1680
- });
1681
- const mesh = !deps.dryRun
1682
- ? deps.mesh === undefined ? createLaneMesh({ interactive: false }) : deps.mesh
1683
- : null;
1684
- if (mesh) {
1685
- const meshEmitTimeoutMs = deps.meshEmitTimeoutMs ?? 10_000;
1686
- let timer;
1687
- try {
1688
- await Promise.race([
1689
- emitLaneTerminalStateEvent(mesh, result.record, "interrupted", "host_lost"),
1690
- new Promise((_resolve, reject) => {
1691
- timer = setTimeout(() => reject(new Error(`terminal mesh emit timed out after ${meshEmitTimeoutMs}ms`)), meshEmitTimeoutMs);
1692
- }),
1693
- ]);
1694
- }
1695
- catch (err) {
1696
- markDirty(summary, {
1697
- kind: "lane",
1698
- id: lane.lane_id,
1699
- why: `terminal mesh emit failed: ${err instanceof Error ? err.message : String(err)}`,
1700
- });
1701
- }
1702
- finally {
1703
- if (timer)
1704
- clearTimeout(timer);
1705
- }
1706
- }
1707
- }
1708
- }
1709
- catch (err) {
1710
- markDirty(summary, {
1711
- kind: "lane",
1712
- id: lane.lane_id,
1713
- why: `dead-host CAS failed: ${err instanceof Error ? err.message : String(err)}`,
1714
- });
1715
- }
1716
- }
1717
- }
1718
- async function admitQueued(hqRoot, lanes, summary, deps, now, onlyLaneIds) {
1719
- if (onlyLaneIds === undefined)
1720
- expireAbandonedPickupValidation(hqRoot, lanes, summary, deps, now);
1721
- // A lane whose pickup probe is still out is not admitted: its record is
1722
- // about to be bound or failed by the pickup, and a worker spawned now
1723
- // would keep running behind that write.
1724
- const localQueued = lanes
1725
- .filter((lane) => lane.state === "queued" &&
1726
- (onlyLaneIds === undefined || onlyLaneIds.has(lane.lane_id)) &&
1727
- lane.pickup_validation_pending !== true &&
1728
- laneRunsHere(lane, deps.host))
1729
- .filter((lane) => !hasFreshAdmissionReservation(lane, now.getTime()))
1730
- .sort(compareLaneQueueOrder);
1731
- if (localQueued.length === 0 || deps.skipAdmissionWithoutCpuSample === true)
1732
- return;
1733
- const config = loadLanesConfig(hqRoot);
1734
- const throttleDeps = deps.throttleDeps ?? defaultThrottleDeps(deps.env ?? process.env);
1735
- const filePath = throttleStatePath(hqRoot);
1736
- const loaded = readThrottleState(filePath, throttleDeps);
1737
- const throttleState = loaded === "unreadable"
1738
- ? "unreadable"
1739
- : loaded === "missing"
1740
- ? emptyThrottleState()
1741
- : loaded;
1742
- const busy = deps.cpuBusyFraction !== undefined
1743
- ? deps.cpuBusyFraction
1744
- : await resolveCpuBusyFraction(throttleDeps);
1745
- const availableMemMb = readAvailableMemMb(throttleDeps);
1746
- const nowMs = now.getTime();
1747
- const decision = evaluateThrottle({
1748
- busy,
1749
- availableMemMb,
1750
- cpuCheckEnabled: isCpuCheckEnabled(throttleDeps.env),
1751
- state: throttleState,
1752
- throttle: config.throttle,
1753
- nowMs,
1754
- });
1755
- const spawnFn = deps.spawn ??
1756
- (async (root, lane) => spawnLaneWorker(root, lane, {
1757
- ...spawnDepsForHost(deps),
1758
- lowDiskGuardEnabled: await lowDiskGuardEnabledForLane(root, lane, deps.readRegistryFlag),
1759
- }));
1760
- const running = lanes.filter((lane) => (lane.state === "running" ||
1761
- hasFreshAdmissionReservation(lane, now.getTime()) ||
1762
- hasActivePendingResume(lane, now.getTime())) &&
1763
- laneRunsHere(lane, deps.host));
1764
- const spawned = [];
1765
- if (!decision.admit) {
1766
- for (const lane of localQueued) {
1767
- let profileMaxConcurrent;
1768
- if (lane.worker_profile !== undefined) {
1769
- try {
1770
- profileMaxConcurrent = loadWorkerProfile(hqRoot, lane.worker_profile).meta.max_concurrent;
1771
- }
1772
- catch (err) {
1773
- markDirty(summary, {
1774
- kind: "lane",
1775
- id: lane.lane_id,
1776
- why: `profile admission failed: ${err instanceof Error ? err.message : String(err)}`,
1777
- });
1778
- continue;
1779
- }
1780
- }
1781
- const capReason = laneAdmissionCapReason(running, lane, config, deps.host, profileMaxConcurrent, now.getTime());
1782
- writeQueueReason(hqRoot, lane, capReason ?? "throttle", deps.dryRun);
1783
- }
1784
- if (!deps.dryRun && throttleState !== "unreadable") {
1785
- writeThrottleState(filePath, applyThrottleDecision(throttleState, decision, nowMs), throttleDeps);
1786
- }
1787
- return;
1788
- }
1789
- for (const lane of localQueued) {
1790
- let profileMaxConcurrent;
1791
- if (lane.worker_profile !== undefined) {
1792
- try {
1793
- profileMaxConcurrent = loadWorkerProfile(hqRoot, lane.worker_profile).meta.max_concurrent;
1794
- }
1795
- catch (err) {
1796
- markDirty(summary, {
1797
- kind: "lane",
1798
- id: lane.lane_id,
1799
- why: `profile admission failed: ${err instanceof Error ? err.message : String(err)}`,
1800
- });
1801
- continue;
1802
- }
1803
- }
1804
- const projected = [...running, ...spawned];
1805
- const alternateAccount = availableBalancedAccount(hqRoot, lane, projected, config, deps.host, profileMaxConcurrent, now.getTime());
1806
- const admittedLane = alternateAccount !== undefined
1807
- ? writeAdmissionAccount(hqRoot, lane, alternateAccount, deps.dryRun)
1808
- : lane;
1809
- const baseCap = laneAdmissionCapReason(running, admittedLane, config, deps.host, profileMaxConcurrent, now.getTime());
1810
- const currentCap = laneAdmissionCapReason([...running, ...spawned], admittedLane, config, deps.host, profileMaxConcurrent, now.getTime());
1811
- let blockedBy;
1812
- if (currentCap !== undefined) {
1813
- blockedBy = baseCap ?? "queue_position";
1814
- }
1815
- if (!blockedBy && !decision.admit)
1816
- blockedBy = "throttle";
1817
- if (blockedBy) {
1818
- writeQueueReason(hqRoot, admittedLane, blockedBy, deps.dryRun);
1819
- continue;
1820
- }
1821
- try {
1822
- if (deps.dryRun) {
1823
- summary.admitted.push({
1824
- id: lane.lane_id,
1825
- from: "queued",
1826
- to: "running",
1827
- reason: decision.reason,
1828
- });
1829
- spawned.push({ ...admittedLane, state: "running" });
1830
- continue;
1831
- }
1832
- const started = await spawnFn(hqRoot, admittedLane);
1833
- if (decision.reason === "starvation_bound") {
1834
- casLaneIf(hqRoot, lane.lane_id, (current) => current.admitted_reason !== "starvation_bound", (current) => ({ ...current, admitted_reason: "starvation_bound" }), false);
1835
- }
1836
- summary.admitted.push({
1837
- id: started.lane_id,
1838
- from: "queued",
1839
- to: started.state,
1840
- reason: started.queued_reason ?? decision.reason,
1841
- ...(started.queued_reason === "provider_binary_missing" &&
1842
- typeof started.terminal_details?.searched_path === "string"
1843
- ? {
1844
- searched_path: started.terminal_details.searched_path,
1845
- }
1846
- : {}),
1847
- });
1848
- spawned.push(started);
1849
- }
1850
- catch (err) {
1851
- if (err instanceof LanesError && err.error === "worker_profile_hash_mismatch") {
1852
- const expectedHash = typeof err.details.expected_hash === "string"
1853
- ? err.details.expected_hash
1854
- : null;
1855
- const actualHash = typeof err.details.actual_hash === "string"
1856
- ? err.details.actual_hash
1857
- : undefined;
1858
- const workerProfile = typeof err.details.worker === "string"
1859
- ? err.details.worker
1860
- : lane.worker_profile;
1861
- if (actualHash !== undefined && workerProfile !== undefined) {
1862
- casLaneIf(hqRoot, lane.lane_id, (current) => current.state === "queued", (current) => ({
1863
- ...current,
1864
- queued_reason: "worker_profile_changed",
1865
- queued_details: {
1866
- reason: "worker_profile_changed",
1867
- expected_hash: expectedHash,
1868
- actual_hash: actualHash,
1869
- recovery_command: `hq lanes resume ${lane.lane_id} --worker ${workerProfile}`,
1870
- },
1871
- admission_reserved_at: undefined,
1872
- }), false);
1873
- summary.skipped.push({
1874
- kind: "lane",
1875
- id: lane.lane_id,
1876
- why: "worker_profile_changed",
1877
- });
1878
- continue;
1879
- }
1880
- }
1881
- terminalizeModelUnsupported(hqRoot, lane, err);
1882
- markDirty(summary, {
1883
- kind: "lane",
1884
- id: lane.lane_id,
1885
- why: `admit failed: ${err instanceof Error ? err.message : String(err)}`,
1886
- });
1887
- }
1888
- }
1889
- if (!deps.dryRun) {
1890
- const admittedAny = spawned.length > 0;
1891
- const applied = admittedAny
1892
- ? { admit: true, reason: decision.reason }
1893
- : decision;
1894
- const base = throttleState === "unreadable" ? emptyThrottleState() : throttleState;
1895
- writeThrottleState(filePath, applyThrottleDecision(base, applied, nowMs), throttleDeps);
1896
- }
1897
- }
1898
- function keepAliveCompanyKey(company) {
1899
- return JSON.stringify([company?.uid ?? null, company?.slug ?? null]);
1900
- }
1901
- function hasKeepAliveProgress(lane) {
1902
- return (lane.keep_alive_resume_count ?? 0) !== 0 ||
1903
- lane.keep_alive_last_resume_at !== undefined ||
1904
- lane.keep_alive_next_eligible_at !== undefined ||
1905
- lane.keep_alive_exhausted_at !== undefined;
1906
- }
1907
- function clearedKeepAliveProgress(lane) {
1908
- return {
1909
- ...lane,
1910
- keep_alive_resume_count: 0,
1911
- keep_alive_last_resume_at: undefined,
1912
- keep_alive_next_eligible_at: undefined,
1913
- keep_alive_exhausted_at: undefined,
1914
- };
1915
- }
1916
- function keepAliveProgressResetByHuman(lane) {
1917
- const count = lane.keep_alive_resume_count ?? 0;
1918
- const lastAutoMs = Date.parse(lane.keep_alive_last_resume_at ?? "");
1919
- if (count > 0 && !Number.isFinite(lastAutoMs))
1920
- return true;
1921
- const messageMs = Date.parse(lane.last_message_at ?? "");
1922
- const resumeMs = lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_RESUME_TEXT ||
1923
- lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT ||
1924
- lane.last_resume?.text === LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT
1925
- ? Number.NaN
1926
- : Date.parse(lane.last_resume?.at ?? "");
1927
- return Number.isFinite(lastAutoMs) &&
1928
- ((Number.isFinite(messageMs) && messageMs > lastAutoMs) ||
1929
- (Number.isFinite(resumeMs) && resumeMs > lastAutoMs));
1930
- }
1931
- function recordKeepAliveEvent(hqRoot, laneId, summary, event) {
1932
- try {
1933
- writeWatcherDecision(watcherEventsPath(hqRoot, laneId), {
1934
- lane_id: laneId,
1935
- ...event,
1936
- });
1937
- }
1938
- catch (err) {
1939
- markDirty(summary, {
1940
- kind: "lane",
1941
- id: laneId,
1942
- why: `keep-alive event write failed: ${err instanceof Error ? err.message : String(err)}`,
1943
- });
1944
- }
1945
- }
1946
- function keepAliveAccountSelectionEvidence(selection) {
1947
- if (selection === undefined)
1948
- return undefined;
1949
- return {
1950
- account: selection.account,
1951
- selected_at: selection.selected_at,
1952
- reason: selection.reason,
1953
- considered: selection.considered.map((row) => ({
1954
- account: row.account,
1955
- live_lanes: row.live_lanes,
1956
- eligible: row.eligible,
1957
- reason: row.ineligible_reason ?? null,
1958
- })),
1959
- };
1960
- }
1961
- function keepAliveSkipGuidance(laneId, reason) {
1962
- switch (reason) {
1963
- case "missing_opt_in_time":
1964
- return `if appropriate, run hq lanes keep-alive ${laneId} --on`;
1965
- case "not_opted_in":
1966
- return "explicit opt-in is required before keep-alive can resume this lane";
1967
- case "envelope_before_opt_in":
1968
- return `the latest envelope predates opt-in; review the lane with hq lanes show ${laneId}`;
1969
- case "interruption_before_opt_in":
1970
- return `the interruption predates opt-in; review the lane with hq lanes show ${laneId}`;
1971
- case "missing_interruption_time":
1972
- return "the interruption time is unavailable; inspect the watcher classification before resuming";
1973
- case "retired":
1974
- return "the lane is retired; resolve its retired status before resuming";
1975
- case "superseded":
1976
- return "a newer lead exists for this project; continue on that lane";
1977
- case "retired_list_invalid":
1978
- return "the retired lane list is invalid; repair workspace/lane-briefs/juniors.json before retrying";
1979
- case "terminal_state":
1980
- return `the lane is in a terminal state; review it with hq lanes show ${laneId} before starting replacement work`;
1981
- }
1982
- }
1983
- function recordKeepAliveSkip(hqRoot, lane, reason, summary, deps, detail) {
1984
- summary.skipped.push({
1985
- kind: "lane",
1986
- id: lane.lane_id,
1987
- why: `keep_alive_${reason}${detail ? `: ${detail}` : ""}`,
1988
- });
1989
- if (lane.keep_alive === true) {
1990
- summary.keep_alive_blocked.push({ id: lane.lane_id, reason });
1991
- process.stderr.write(`hq lanes reconcile: keep-alive blocked for lane ${lane.lane_id} (${reason}); ${keepAliveSkipGuidance(lane.lane_id, reason)}\n`);
1992
- }
1993
- if (deps.dryRun)
1994
- return;
1995
- let laneVersion = lane.version;
1996
- if (lane.keep_alive_blocked_reason !== reason) {
1997
- try {
1998
- const update = casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === lane.state, (current) => ({ ...current, keep_alive_blocked_reason: reason }), false);
1999
- laneVersion = update.record.version;
2000
- }
2001
- catch (err) {
2002
- markDirty(summary, {
2003
- kind: "lane",
2004
- id: lane.lane_id,
2005
- why: `keep-alive skip state write failed: ${err instanceof Error ? err.message : String(err)}`,
2006
- });
2007
- }
2008
- }
2009
- if (lane.keep_alive === true) {
2010
- recordKeepAliveSkipEvent(hqRoot, lane, reason, laneVersion, summary, deps, detail);
2011
- }
2012
- }
2013
- function recordDoneKeepAlivePolicySkip(hqRoot, summary, lane, verdict, deps) {
2014
- summary.skipped.push({
2015
- kind: "lane",
2016
- id: lane.lane_id,
2017
- why: `keep_alive_${verdict}`,
2018
- });
2019
- if (lane.keep_alive === true && !deps.dryRun) {
2020
- recordKeepAliveSkipEvent(hqRoot, lane, verdict, lane.version, summary, deps);
2021
- }
2022
- }
2023
- function recordKeepAliveSkipEvent(hqRoot, lane, reason, laneVersion, summary, deps, detail) {
2024
- if (lane.keep_alive !== true || deps.dryRun)
2025
- return;
2026
- const eventsPath = watcherEventsPath(hqRoot, lane.lane_id);
2027
- try {
2028
- let lastSkip;
2029
- try {
2030
- const scanBackwards = (body, targetReason, targetVersion) => {
2031
- const lines = body.split(/\r?\n/);
2032
- for (let index = lines.length - 1; index >= 0; index -= 1) {
2033
- if (!lines[index])
2034
- continue;
2035
- try {
2036
- const value = JSON.parse(lines[index]);
2037
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
2038
- const event = value;
2039
- if (event.event === "keep_alive_skip" &&
2040
- (targetReason === undefined || (event.reason === targetReason && event.lane_version === targetVersion))) {
2041
- return event;
2042
- }
2043
- }
2044
- }
2045
- catch {
2046
- // A partial concurrent append must not prevent future skip events.
2047
- }
2048
- }
2049
- return undefined;
2050
- };
2051
- const maxTailBytes = 64 * 1024;
2052
- const fd = fs.openSync(eventsPath, "r");
2053
- let fileSize;
2054
- let tail;
2055
- let tailOffset;
2056
- try {
2057
- fileSize = fs.fstatSync(fd).size;
2058
- tailOffset = Math.max(0, fileSize - maxTailBytes);
2059
- const buffer = Buffer.alloc(fileSize - tailOffset);
2060
- let bytesRead = 0;
2061
- while (bytesRead < buffer.length) {
2062
- const count = fs.readSync(fd, buffer, bytesRead, buffer.length - bytesRead, tailOffset + bytesRead);
2063
- if (count === 0)
2064
- break;
2065
- bytesRead += count;
2066
- }
2067
- tail = buffer.subarray(0, bytesRead).toString("utf8");
2068
- }
2069
- finally {
2070
- fs.closeSync(fd);
2071
- }
2072
- // When the bounded read starts mid-line, ignore that incomplete prefix.
2073
- if (tailOffset > 0) {
2074
- const firstNewline = tail.indexOf("\n");
2075
- tail = firstNewline < 0 ? "" : tail.slice(firstNewline + 1);
2076
- }
2077
- lastSkip = scanBackwards(tail, reason === "next_check_at" ? reason : undefined, laneVersion);
2078
- // For small logs the tail is not a complete record boundary if a single
2079
- // record exceeded the bound, so allow one full-file scan as a fallback.
2080
- if (!lastSkip && fileSize < maxTailBytes) {
2081
- lastSkip = scanBackwards(fs.readFileSync(eventsPath, "utf8"), reason === "next_check_at" ? reason : undefined, laneVersion);
2082
- }
2083
- }
2084
- catch (err) {
2085
- if (err.code !== "ENOENT")
2086
- throw err;
2087
- }
2088
- if (lastSkip?.reason === reason && lastSkip.lane_version === laneVersion)
2089
- return;
2090
- recordKeepAliveEvent(hqRoot, lane.lane_id, summary, {
2091
- event: "keep_alive_skip",
2092
- at: deps.now().toISOString(),
2093
- reason,
2094
- ...(detail === undefined ? {} : { detail }),
2095
- lane_version: laneVersion,
2096
- lane_state: lane.state,
2097
- });
2098
- }
2099
- catch (err) {
2100
- markDirty(summary, {
2101
- kind: "lane",
2102
- id: lane.lane_id,
2103
- why: `keep-alive skip event dedupe failed: ${err instanceof Error ? err.message : String(err)}`,
2104
- });
2105
- }
2106
- }
2107
- function readLatestInterruptedClassificationAt(hqRoot, lane) {
2108
- const logPath = watcherOutLogPath(hqRoot, lane.lane_id);
2109
- let body;
2110
- try {
2111
- body = fs.readFileSync(logPath, "utf8");
2112
- }
2113
- catch (err) {
2114
- if (err.code === "ENOENT")
2115
- return undefined;
2116
- throw err;
2117
- }
2118
- let latestClassification;
2119
- for (const line of body.split(/\r?\n/).filter(Boolean)) {
2120
- let parsed;
2121
- try {
2122
- parsed = JSON.parse(line);
2123
- }
2124
- catch {
2125
- continue;
2126
- }
2127
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
2128
- continue;
2129
- const record = parsed;
2130
- if (record.event === "classification")
2131
- latestClassification = record;
2132
- }
2133
- const at = latestClassification?.at;
2134
- if (latestClassification?.state !== "interrupted" ||
2135
- latestClassification.reason !== lane.terminal_reason ||
2136
- typeof at !== "string" ||
2137
- !Number.isFinite(Date.parse(at)) ||
2138
- new Date(Date.parse(at)).toISOString() !== at)
2139
- return undefined;
2140
- return at;
2141
- }
2142
- function latestEnvelopeMtimeMs(hqRoot, lane, ref) {
2143
- const filePath = path.join(laneRunDir(hqRoot, lane.lane_id), ref);
2144
- try {
2145
- return fs.statSync(filePath).mtimeMs;
2146
- }
2147
- catch (err) {
2148
- throw new LanesError("keep_alive_envelope_timestamp_unavailable", `Cannot stat latest envelope for lane ${lane.lane_id}: ${err instanceof Error ? err.message : String(err)}`, { lane_id: lane.lane_id, path: filePath });
2149
- }
2150
- }
2151
- async function resumeBlockedKeepAlive(hqRoot, lane, questions, summary, deps, now, keepAliveFlags, blockedFlags, companyForLane, lanes, retiredLaneIds, lineageAwareByCompany) {
2152
- const company = companyForLane(lane);
2153
- if (company === null) {
2154
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_company_unresolved" });
2155
- return;
2156
- }
2157
- const key = keepAliveCompanyKey(company);
2158
- if (keepAliveFlags.get(key) !== true || blockedFlags.get(key) !== true) {
2159
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked" });
2160
- return;
2161
- }
2162
- if (lane.terminal_reason === "approval_gate_violated" || lane.last_envelope?.blocked_reason === "approval_gate_violated") {
2163
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_approval_gate_violated" });
2164
- return;
2165
- }
2166
- if (lane.last_envelope?.decision !== "blocked") {
2167
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_missing_blocked_envelope" });
2168
- return;
2169
- }
2170
- if (retiredLaneIds === null) {
2171
- recordKeepAliveSkip(hqRoot, lane, "retired_list_invalid", summary, deps);
2172
- return;
2173
- }
2174
- const exclusion = keepAliveExclusion(lane, lanes, retiredLaneIds, lineageAwareByCompany.get(key) === true, companyForLane);
2175
- if (exclusion !== undefined) {
2176
- recordKeepAliveSkip(hqRoot, lane, exclusion, summary, deps);
2177
- return;
2178
- }
2179
- const terminalAt = lane.last_envelope.at;
2180
- const terminalMs = Date.parse(terminalAt ?? "");
2181
- if (!Number.isFinite(terminalMs)) {
2182
- recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, "blocked terminal time is invalid");
2183
- return;
2184
- }
2185
- if (activeLaneStories(lane).length === 0) {
2186
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_no_open_stories" });
2187
- return;
2188
- }
2189
- let pendingQuestion = questions.some((question) => question.lane_id === lane.lane_id && question.status === "pending");
2190
- try {
2191
- for (const id of listQuestionIds(hqRoot)) {
2192
- const question = readQuestion(hqRoot, id);
2193
- if (question.lane_id === lane.lane_id && question.status === "pending")
2194
- pendingQuestion = true;
2195
- }
2196
- }
2197
- catch (error) {
2198
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: "blocked keep-alive question read failed: " + (error instanceof Error ? error.message : String(error)) });
2199
- return;
2200
- }
2201
- const blockedHistory = lane.status_gate_history ?? [];
2202
- const priorResumes = lane.keep_alive_blocked_resume_streak ?? 0;
2203
- const alreadyEscalated = blockedHistory.some((entry) => entry.event === "keep_alive_blocked_escalated" && entry.terminal_at === terminalAt);
2204
- if (pendingQuestion) {
2205
- appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "open_question", deps);
2206
- return;
2207
- }
2208
- if (priorResumes >= 2) {
2209
- if (!alreadyEscalated) {
2210
- casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
2211
- ...current,
2212
- status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
2213
- event: "keep_alive_blocked_escalated",
2214
- at: now.toISOString(),
2215
- terminal_at: terminalAt,
2216
- }),
2217
- }), deps.dryRun);
2218
- }
2219
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_blocked_escalated" });
2220
- return;
2221
- }
2222
- if ((lane.keep_alive_resume_count ?? 0) >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES || lane.keep_alive_exhausted_at !== undefined) {
2223
- if (lane.keep_alive_exhausted_at === undefined && !deps.dryRun) {
2224
- casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" &&
2225
- (current.keep_alive_resume_count ?? 0) >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES, (current) => ({ ...current, keep_alive_exhausted_at: now.toISOString() }), false);
2226
- }
2227
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_exhausted" });
2228
- return;
2229
- }
2230
- if (now.getTime() - terminalMs < LEAD_LANE_KEEP_ALIVE_BLOCKED_GRACE_MS) {
2231
- appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, now.toISOString(), "blocked_grace", deps);
2232
- return;
2233
- }
2234
- const latest = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
2235
- if (latest.version !== lane.version || latest.state !== "blocked" || latest.last_envelope?.decision !== "blocked" || latest.last_envelope.at !== terminalAt)
2236
- return;
2237
- let hasOpenQuestion;
2238
- try {
2239
- hasOpenQuestion = listQuestionIds(hqRoot).some((id) => {
2240
- const question = readQuestion(hqRoot, id);
2241
- return question.lane_id === lane.lane_id && question.status === "pending";
2242
- });
2243
- }
2244
- catch (error) {
2245
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: "blocked keep-alive question recheck failed: " + (error instanceof Error ? error.message : String(error)) });
2246
- return;
2247
- }
2248
- if (hasOpenQuestion) {
2249
- appendBlockedKeepAliveHistory(hqRoot, latest, terminalAt, now.toISOString(), "open_question", deps);
2250
- return;
2251
- }
2252
- let current = latest;
2253
- let accountSelection;
2254
- if (current.provider === "claude" && deps.prepareKeepAliveClaudeResume !== undefined) {
2255
- try {
2256
- const prepared = await deps.prepareKeepAliveClaudeResume(current);
2257
- current = prepared.lane;
2258
- accountSelection = prepared.accountSelection;
2259
- }
2260
- catch (error) {
2261
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `blocked keep-alive account preparation failed: ${error instanceof Error ? error.message : String(error)}` });
2262
- return;
2263
- }
2264
- const afterPreparation = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
2265
- if (afterPreparation.state !== "blocked" || afterPreparation.last_envelope?.decision !== "blocked" || afterPreparation.last_envelope.at !== terminalAt)
2266
- return;
2267
- current = afterPreparation;
2268
- }
2269
- const expectedVersion = current.version;
2270
- const result = queueLaneTerminalResume(current, now.toISOString(), LEAD_LANE_KEEP_ALIVE_BLOCKED_RESUME_TEXT, { allowBlockedResume: true, trigger: "resume" });
2271
- if (!result.resumeQueued) {
2272
- if (result.resumeSkippedReason !== undefined) {
2273
- summary.skipped.push({ kind: "lane", id: lane.lane_id, why: "keep_alive_" + result.resumeSkippedReason });
2274
- }
2275
- return;
2276
- }
2277
- const queued = casLaneIf(hqRoot, latest.lane_id, (latestRecord) => latestRecord.version === expectedVersion && latestRecord.state === "blocked" && latestRecord.last_envelope?.decision === "blocked" && latestRecord.last_envelope.at === terminalAt, (current) => ({
2278
- ...result.lane,
2279
- version: current.version,
2280
- pending_resume: result.lane.pending_resume === undefined
2281
- ? undefined
2282
- : { ...result.lane.pending_resume, keep_alive: true },
2283
- keep_alive_blocked_resume_streak: priorResumes + 1,
2284
- keep_alive_resume_count: (current.keep_alive_resume_count ?? 0) + 1,
2285
- keep_alive_history: appendCappedHistory(current.keep_alive_history ?? [], {
2286
- at: now.toISOString(),
2287
- resumed_from: "blocked",
2288
- account: current.account ?? null,
2289
- }),
2290
- keep_alive_last_resume_at: now.toISOString(),
2291
- status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
2292
- event: "keep_alive_blocked_resumed",
2293
- at: now.toISOString(),
2294
- terminal_at: terminalAt,
2295
- resume_queued: true,
2296
- }),
2297
- }), deps.dryRun);
2298
- if (queued.changed) {
2299
- summary.resumed.push({ id: lane.lane_id, from: "blocked", to: "queued", reason: "keep_alive_blocked" });
2300
- if (!deps.dryRun) {
2301
- recordKeepAliveEvent(hqRoot, lane.lane_id, summary, {
2302
- event: "keep_alive_resume",
2303
- at: now.toISOString(),
2304
- attempt: (lane.keep_alive_resume_count ?? 0) + 1,
2305
- resumed_from: "blocked",
2306
- ...(accountSelection !== undefined
2307
- ? { account_selection: keepAliveAccountSelectionEvidence(accountSelection) }
2308
- : {}),
2309
- });
2310
- }
2311
- }
2312
- }
2313
- function appendBlockedKeepAliveHistory(hqRoot, lane, terminalAt, at, reason, deps) {
2314
- if (deps.dryRun || lane.status_gate_history?.some((entry) => entry.event === "keep_alive_blocked_grace" && entry.terminal_at === terminalAt && entry.resume_skipped_reason === reason))
2315
- return;
2316
- casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "blocked" && current.last_envelope?.at === terminalAt, (current) => ({
2317
- ...current,
2318
- status_gate_history: appendCappedHistory(current.status_gate_history ?? [], {
2319
- event: "keep_alive_blocked_grace",
2320
- at,
2321
- terminal_at: terminalAt,
2322
- resume_skipped_reason: reason,
2323
- }),
2324
- }), false);
2325
- }
2326
- async function resumeKeepAlive(hqRoot, lanes, questions, summary, deps, now, flagByCompany, supersessionLineageAwareByCompany, blockedResumeFlagByCompany, immediateFirstResumeFlagByCompany, terminalKeepAliveCandidate) {
2327
- const nowMs = now.getTime();
2328
- const nowIso = now.toISOString();
2329
- const config = loadLanesConfig(hqRoot);
2330
- const resolvedCompanyByLaneVersion = new Map();
2331
- const companyForLane = (candidate) => {
2332
- const key = `${candidate.lane_id}\u0000${candidate.version}`;
2333
- if (resolvedCompanyByLaneVersion.has(key))
2334
- return resolvedCompanyByLaneVersion.get(key) ?? null;
2335
- const company = resolveLaneCompany(hqRoot, candidate).company;
2336
- resolvedCompanyByLaneVersion.set(key, company);
2337
- return company;
2338
- };
2339
- const keepAliveEnabled = [...flagByCompany.values()].some((enabled) => enabled) || [...blockedResumeFlagByCompany.values()].some(Boolean);
2340
- let retiredLaneIds = null;
2341
- if (keepAliveEnabled) {
2342
- try {
2343
- retiredLaneIds = loadRetiredLaneIds(hqRoot);
2344
- }
2345
- catch (err) {
2346
- markError(summary, err);
2347
- }
2348
- }
2349
- for (const originalLane of lanes) {
2350
- if (terminalKeepAliveCandidate && (originalLane.lane_id !== terminalKeepAliveCandidate.laneId ||
2351
- originalLane.version !== terminalKeepAliveCandidate.version ||
2352
- originalLane.state !== "done"))
2353
- continue;
2354
- if (!laneRunsHere(originalLane, deps.host))
2355
- continue;
2356
- if (originalLane.state === "killed" ||
2357
- originalLane.state === "cancelled" ||
2358
- originalLane.state === "failed") {
2359
- if (originalLane.keep_alive === true &&
2360
- flagByCompany.get(keepAliveCompanyKey(companyForLane(originalLane))) === true)
2361
- recordKeepAliveSkip(hqRoot, originalLane, "terminal_state", summary, deps);
2362
- continue;
2363
- }
2364
- if (originalLane.state === "blocked" && originalLane.keep_alive === true) {
2365
- await resumeBlockedKeepAlive(hqRoot, originalLane, questions, summary, deps, now, flagByCompany, blockedResumeFlagByCompany, companyForLane, lanes, retiredLaneIds, supersessionLineageAwareByCompany);
2366
- continue;
2367
- }
2368
- if ((originalLane.state !== "done" && originalLane.state !== "interrupted") ||
2369
- (originalLane.keep_alive !== true && originalLane.worker_profile === undefined))
2370
- continue;
2371
- const initialState = originalLane.state;
2372
- let lane = originalLane;
2373
- if (initialState === "done") {
2374
- const envelope = readLatestEnvelopeRecord(hqRoot, lane.lane_id)?.envelope;
2375
- if (!envelope || envelope.decision !== "done")
2376
- continue;
2377
- const hasPendingWork = hasPendingKeepAliveWork(hqRoot, lane, questions);
2378
- const hasQueuedWork = lane.keep_alive_standing === true && hasPendingWork;
2379
- const verdict = doneKeepAliveVerdict(lane, envelope, hasQueuedWork, {
2380
- hasPendingWork,
2381
- nowMs,
2382
- nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
2383
- });
2384
- if (verdict !== "eligible") {
2385
- let skipLane = lane;
2386
- if (envelope.story_complete === true && lane.keep_alive_standing !== true && hasKeepAliveProgress(lane)) {
2387
- skipLane = casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version &&
2388
- current.state === "done" &&
2389
- current.keep_alive === lane.keep_alive, (current) => ({ ...clearedKeepAliveProgress(current), keep_alive_blocked_reason: undefined }), deps.dryRun).record;
2390
- }
2391
- if (verdict !== null)
2392
- recordDoneKeepAlivePolicySkip(hqRoot, summary, skipLane, verdict, deps);
2393
- continue;
2394
- }
2395
- }
2396
- const company = companyForLane(lane);
2397
- if (company === null) {
2398
- if (lane.keep_alive === true) {
2399
- summary.skipped.push({
2400
- kind: "lane",
2401
- id: lane.lane_id,
2402
- why: "keep_alive_company_unresolved",
2403
- });
2404
- recordKeepAliveSkipEvent(hqRoot, lane, "company_unresolved", lane.version, summary, deps);
2405
- }
2406
- continue;
2407
- }
2408
- const companyKey = keepAliveCompanyKey(company);
2409
- if (flagByCompany.get(companyKey) !== true) {
2410
- if (lane.keep_alive === true) {
2411
- summary.skipped.push({
2412
- kind: "lane",
2413
- id: lane.lane_id,
2414
- why: "keep_alive_flag_off",
2415
- });
2416
- recordKeepAliveSkipEvent(hqRoot, lane, "keep_alive_flag_off", lane.version, summary, deps);
2417
- }
2418
- continue;
2419
- }
2420
- // Persist a stable timestamp for legacy interrupted rows before a later
2421
- // keep-alive status write can advance their general updated_at clock. The
2422
- // watcher classification is the interruption time; updated_at is only an
2423
- // upper bound and can postdate explicit opt-in.
2424
- if (initialState === "interrupted" && lane.keep_alive === true && lane.interrupted_at === undefined) {
2425
- let interruptedAt;
2426
- try {
2427
- interruptedAt = readLatestInterruptedClassificationAt(hqRoot, lane);
2428
- }
2429
- catch (err) {
2430
- const detail = `watcher classification read failed: ${err instanceof Error ? err.message : String(err)}`;
2431
- recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, detail);
2432
- const skip = summary.skipped[summary.skipped.length - 1];
2433
- if (skip) {
2434
- summary.ok = false;
2435
- logSkip(skip);
2436
- }
2437
- continue;
2438
- }
2439
- if (interruptedAt === undefined) {
2440
- recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, "no latest matching watcher classification event");
2441
- const skip = summary.skipped[summary.skipped.length - 1];
2442
- if (skip) {
2443
- summary.ok = false;
2444
- logSkip(skip);
2445
- }
2446
- continue;
2447
- }
2448
- try {
2449
- const backfill = casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version &&
2450
- current.state === "interrupted" &&
2451
- current.keep_alive === true &&
2452
- current.terminal_reason === lane.terminal_reason &&
2453
- current.interrupted_at === undefined, (current) => ({ ...current, interrupted_at: interruptedAt }), deps.dryRun);
2454
- lane = backfill.record;
2455
- if (lane.interrupted_at === undefined) {
2456
- recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps, "lane changed before watcher classification timestamp could be recorded");
2457
- const skip = summary.skipped[summary.skipped.length - 1];
2458
- if (skip) {
2459
- summary.ok = false;
2460
- logSkip(skip);
2461
- }
2462
- continue;
2463
- }
2464
- }
2465
- catch (err) {
2466
- markDirty(summary, {
2467
- kind: "lane",
2468
- id: lane.lane_id,
2469
- why: `keep-alive interruption timestamp backfill failed: ${err instanceof Error ? err.message : String(err)}`,
2470
- });
2471
- continue;
2472
- }
2473
- }
2474
- if (initialState === "interrupted" && lane.terminal_reason !== "exited_with_partial_output") {
2475
- recordKeepAliveSkip(hqRoot, lane, "terminal_state", summary, deps, lane.terminal_reason ?? "missing interruption reason");
2476
- continue;
2477
- }
2478
- if (initialState === "done" && (lane.terminal_reason === PROVIDER_REJECTED_REASON ||
2479
- lane.terminal_reason === "account_not_logged_in")) {
2480
- recordKeepAliveSkip(hqRoot, lane, "terminal_state", summary, deps, lane.terminal_reason);
2481
- continue;
2482
- }
2483
- if (lane.keep_alive !== true) {
2484
- recordKeepAliveSkip(hqRoot, lane, "not_opted_in", summary, deps);
2485
- continue;
2486
- }
2487
- const optedInAtMs = Date.parse(lane.keep_alive_opted_in_at ?? "");
2488
- if (!Number.isFinite(optedInAtMs)) {
2489
- recordKeepAliveSkip(hqRoot, lane, "missing_opt_in_time", summary, deps);
2490
- continue;
2491
- }
2492
- let eligibilityAtMs;
2493
- if (initialState === "done") {
2494
- const latest = readLatestEnvelopeRecord(hqRoot, lane.lane_id);
2495
- if (!latest || latest.envelope.decision !== "done")
2496
- continue;
2497
- const hasPendingWork = hasPendingKeepAliveWork(hqRoot, lane, questions);
2498
- const hasQueuedWork = lane.keep_alive_standing === true && hasPendingWork;
2499
- const verdict = doneKeepAliveVerdict(lane, latest.envelope, hasQueuedWork, {
2500
- hasPendingWork,
2501
- nowMs,
2502
- nextCheckAnchorMs: Date.parse(lane.last_envelope?.at ?? lane.updated_at),
2503
- });
2504
- if (verdict !== "eligible") {
2505
- let skipLane = lane;
2506
- if (latest.envelope.story_complete === true && lane.keep_alive_standing !== true && hasKeepAliveProgress(lane)) {
2507
- skipLane = casLaneIf(hqRoot, lane.lane_id, (current) => current.version === lane.version && current.state === "done", (current) => ({ ...clearedKeepAliveProgress(current), keep_alive_blocked_reason: undefined }), deps.dryRun).record;
2508
- }
2509
- if (verdict !== null)
2510
- recordDoneKeepAlivePolicySkip(hqRoot, summary, skipLane, verdict, deps);
2511
- continue;
2512
- }
2513
- try {
2514
- eligibilityAtMs = latestEnvelopeMtimeMs(hqRoot, lane, latest.ref);
2515
- }
2516
- catch (err) {
2517
- markDirty(summary, {
2518
- kind: "lane",
2519
- id: lane.lane_id,
2520
- why: `keep-alive envelope timestamp unavailable: ${err instanceof Error ? err.message : String(err)}`,
2521
- });
2522
- continue;
2523
- }
2524
- }
2525
- else {
2526
- eligibilityAtMs = Date.parse(lane.interrupted_at ?? "");
2527
- if (!Number.isFinite(eligibilityAtMs)) {
2528
- recordKeepAliveSkip(hqRoot, lane, "missing_interruption_time", summary, deps);
2529
- continue;
2530
- }
2531
- }
2532
- const initialTimeBlockReason = initialState === "done"
2533
- ? "envelope_before_opt_in"
2534
- : "interruption_before_opt_in";
2535
- if (eligibilityAtMs <= optedInAtMs) {
2536
- recordKeepAliveSkip(hqRoot, lane, initialTimeBlockReason, summary, deps);
2537
- continue;
2538
- }
2539
- if (retiredLaneIds === null) {
2540
- recordKeepAliveSkip(hqRoot, lane, "retired_list_invalid", summary, deps);
2541
- continue;
2542
- }
2543
- const exclusion = keepAliveExclusion(lane, lanes, retiredLaneIds, supersessionLineageAwareByCompany.get(companyKey) === true, companyForLane);
2544
- if (exclusion !== undefined) {
2545
- recordKeepAliveSkip(hqRoot, lane, exclusion, summary, deps);
2546
- continue;
2547
- }
2548
- const providerSessionId = lane.provider_session_id ?? lane.last_resume?.session_id;
2549
- if (!providerSupportsSessionResume(lane.provider) ||
2550
- typeof providerSessionId !== "string" ||
2551
- providerSessionId.trim().length === 0) {
2552
- if (initialState === "interrupted") {
2553
- recordKeepAliveSkip(hqRoot, lane, "terminal_state", summary, deps, "provider session cannot be resumed");
2554
- }
2555
- continue;
2556
- }
2557
- // This second read is intentionally independent of the pass snapshot. The
2558
- // final timestamp comparison below closes the opt-in race before the CAS.
2559
- const rechecked = deps.readKeepAliveRecheckLane?.(hqRoot, lane.lane_id) ?? readLane(hqRoot, lane.lane_id);
2560
- // Dry-run backfills only in memory. Carry that simulated legacy timestamp
2561
- // through the fresh read when the stored lane version has not changed.
2562
- let current = deps.dryRun &&
2563
- initialState === "interrupted" &&
2564
- originalLane.interrupted_at === undefined &&
2565
- lane.interrupted_at !== undefined &&
2566
- rechecked.version === lane.version &&
2567
- rechecked.interrupted_at === undefined
2568
- ? { ...rechecked, interrupted_at: lane.interrupted_at }
2569
- : rechecked;
2570
- if (current.state !== initialState || !laneRunsHere(current, deps.host))
2571
- continue;
2572
- if (current.keep_alive !== true) {
2573
- recordKeepAliveSkip(hqRoot, current, "not_opted_in", summary, deps);
2574
- continue;
2575
- }
2576
- if (initialState === "interrupted" && current.terminal_reason !== "exited_with_partial_output") {
2577
- recordKeepAliveSkip(hqRoot, current, "terminal_state", summary, deps, current.terminal_reason ?? "missing interruption reason");
2578
- continue;
2579
- }
2580
- let currentEligibilityAtMs;
2581
- if (initialState === "done") {
2582
- const currentEnvelope = readLatestEnvelopeRecord(hqRoot, current.lane_id);
2583
- if (!currentEnvelope || currentEnvelope.envelope.decision !== "done")
2584
- continue;
2585
- const hasPendingWork = hasPendingKeepAliveWork(hqRoot, current, questions);
2586
- const hasQueuedWork = current.keep_alive_standing === true && hasPendingWork;
2587
- const verdict = doneKeepAliveVerdict(current, currentEnvelope.envelope, hasQueuedWork, {
2588
- hasPendingWork,
2589
- nowMs,
2590
- nextCheckAnchorMs: Date.parse(current.last_envelope?.at ?? current.updated_at),
2591
- });
2592
- if (verdict !== "eligible") {
2593
- let skipLane = current;
2594
- if (currentEnvelope.envelope.story_complete === true && current.keep_alive_standing !== true && hasKeepAliveProgress(current)) {
2595
- skipLane = casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
2596
- latestLane.state === "done" &&
2597
- latestLane.keep_alive === true &&
2598
- latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at, (latestLane) => ({ ...clearedKeepAliveProgress(latestLane), keep_alive_blocked_reason: undefined }), deps.dryRun).record;
2599
- }
2600
- if (verdict !== null)
2601
- recordDoneKeepAlivePolicySkip(hqRoot, summary, skipLane, verdict, deps);
2602
- continue;
2603
- }
2604
- try {
2605
- currentEligibilityAtMs = latestEnvelopeMtimeMs(hqRoot, current, currentEnvelope.ref);
2606
- }
2607
- catch (err) {
2608
- markDirty(summary, {
2609
- kind: "lane",
2610
- id: current.lane_id,
2611
- why: `keep-alive envelope timestamp unavailable: ${err instanceof Error ? err.message : String(err)}`,
2612
- });
2613
- continue;
2614
- }
2615
- }
2616
- else {
2617
- currentEligibilityAtMs = Date.parse(current.interrupted_at ?? "");
2618
- if (!Number.isFinite(currentEligibilityAtMs)) {
2619
- recordKeepAliveSkip(hqRoot, current, "missing_interruption_time", summary, deps);
2620
- continue;
2621
- }
2622
- }
2623
- const currentOptedInAtMs = Date.parse(current.keep_alive_opted_in_at ?? "");
2624
- if (!Number.isFinite(currentOptedInAtMs)) {
2625
- recordKeepAliveSkip(hqRoot, current, "missing_opt_in_time", summary, deps);
2626
- continue;
2627
- }
2628
- const currentTimeBlockReason = initialState === "done"
2629
- ? "envelope_before_opt_in"
2630
- : "interruption_before_opt_in";
2631
- if (currentEligibilityAtMs <= currentOptedInAtMs) {
2632
- recordKeepAliveSkip(hqRoot, current, currentTimeBlockReason, summary, deps);
2633
- continue;
2634
- }
2635
- if (retiredLaneIds === null) {
2636
- recordKeepAliveSkip(hqRoot, current, "retired_list_invalid", summary, deps);
2637
- continue;
2638
- }
2639
- const currentLanes = loadLaneRecords(hqRoot, summary);
2640
- const currentCompany = companyForLane(current);
2641
- if (currentCompany === null) {
2642
- summary.skipped.push({
2643
- kind: "lane",
2644
- id: current.lane_id,
2645
- why: "keep_alive_company_unresolved",
2646
- });
2647
- recordKeepAliveSkipEvent(hqRoot, current, "company_unresolved", current.version, summary, deps);
2648
- continue;
2649
- }
2650
- const currentCompanyKey = keepAliveCompanyKey(currentCompany);
2651
- if (flagByCompany.get(currentCompanyKey) !== true) {
2652
- summary.skipped.push({
2653
- kind: "lane",
2654
- id: current.lane_id,
2655
- why: "keep_alive_flag_off",
2656
- });
2657
- recordKeepAliveSkipEvent(hqRoot, current, "keep_alive_flag_off", current.version, summary, deps);
2658
- continue;
2659
- }
2660
- const currentExclusion = keepAliveExclusion(current, currentLanes, retiredLaneIds, supersessionLineageAwareByCompany.get(currentCompanyKey) === true, companyForLane);
2661
- if (currentExclusion !== undefined) {
2662
- recordKeepAliveSkip(hqRoot, current, currentExclusion, summary, deps);
2663
- continue;
2664
- }
2665
- if (current.keep_alive_blocked_reason === "superseded") {
2666
- // The fresh exclusion check found no active successor. Clear the stale
2667
- // reason before any later exit (backoff, queue full, retry cap, hire
2668
- // refusal) so the watcher keeps treating the lane as resumable.
2669
- const cleared = casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
2670
- latestLane.state === current.state &&
2671
- latestLane.keep_alive === true &&
2672
- latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at &&
2673
- latestLane.keep_alive_blocked_reason === "superseded", (latestLane) => ({ ...latestLane, keep_alive_blocked_reason: undefined }), deps.dryRun);
2674
- if (!cleared.changed)
2675
- continue;
2676
- current = cleared.record;
2677
- }
2678
- const currentSessionId = current.provider_session_id ?? current.last_resume?.session_id;
2679
- if (!providerSupportsSessionResume(current.provider) ||
2680
- typeof currentSessionId !== "string" ||
2681
- currentSessionId.trim().length === 0)
2682
- continue;
2683
- const resetByHuman = keepAliveProgressResetByHuman(current);
2684
- const attempts = resetByHuman ? 0 : current.keep_alive_resume_count ?? 0;
2685
- if (attempts >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES) {
2686
- if (current.keep_alive_exhausted_at !== undefined)
2687
- continue;
2688
- const exhausted = casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
2689
- latestLane.state === current.state &&
2690
- latestLane.keep_alive === true &&
2691
- latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at &&
2692
- (latestLane.keep_alive_resume_count ?? 0) >= LEAD_LANE_KEEP_ALIVE_MAX_RESUMES &&
2693
- latestLane.keep_alive_exhausted_at === undefined, (latestLane) => ({ ...latestLane, keep_alive_exhausted_at: nowIso }), deps.dryRun);
2694
- if (exhausted.changed && !deps.dryRun) {
2695
- recordKeepAliveEvent(hqRoot, current.lane_id, summary, {
2696
- event: "keep_alive_exhausted",
2697
- at: nowIso,
2698
- attempt: attempts,
2699
- resumed_from: current.state,
2700
- });
2701
- }
2702
- continue;
2703
- }
2704
- const attempt = attempts + 1;
2705
- // A done turn ends no later than its done envelope. Bookkeeping writes
2706
- // (such as clearing a stale block) move updated_at but not the envelope,
2707
- // so they cannot push the retry clock forward.
2708
- const terminalAtMs = initialState === "done"
2709
- ? Math.min(Date.parse(current.updated_at), currentEligibilityAtMs)
2710
- : currentEligibilityAtMs;
2711
- const immediateFirstResumeEnabled = initialState === "done" &&
2712
- immediateFirstResumeFlagByCompany.get(keepAliveCompanyKey(companyForLane(current))) === true;
2713
- const attemptDelayMs = immediateFirstResumeEnabled && attempt === 1
2714
- ? LEAD_LANE_KEEP_ALIVE_FIRST_RESUME_SETTLE_MS
2715
- : leadLaneKeepAliveBackoffMs(immediateFirstResumeEnabled ? Math.max(1, attempt - 1) : attempt);
2716
- const attemptEligibleMs = terminalAtMs + attemptDelayMs;
2717
- const storedNextMs = resetByHuman
2718
- ? Number.NaN
2719
- : Date.parse(current.keep_alive_next_eligible_at ?? "");
2720
- const nextEligibleMs = Number.isFinite(storedNextMs)
2721
- ? Math.max(attemptEligibleMs, storedNextMs)
2722
- : attemptEligibleMs;
2723
- if (nowMs < nextEligibleMs) {
2724
- let skipLane = current;
2725
- if (resetByHuman && hasKeepAliveProgress(current)) {
2726
- const clearedProgress = casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
2727
- latestLane.state === current.state &&
2728
- latestLane.keep_alive === true &&
2729
- latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at, (latestLane) => clearedKeepAliveProgress(latestLane), deps.dryRun);
2730
- skipLane = clearedProgress.record;
2731
- }
2732
- recordKeepAliveSkipEvent(hqRoot, skipLane, "backoff", skipLane.version, summary, deps, new Date(nextEligibleMs).toISOString());
2733
- continue;
2734
- }
2735
- const nextEligibleAt = new Date(nowMs + leadLaneKeepAliveBackoffMs(immediateFirstResumeEnabled ? attempt : attempt + 1)).toISOString();
2736
- const queuedCount = loadLaneRecords(hqRoot, summary).filter((queuedLane) => queuedLane.state === "queued" &&
2737
- queuedLane.provider === current.provider &&
2738
- laneRunsHere(queuedLane, deps.host) &&
2739
- !hasFreshAdmissionReservation(queuedLane, nowMs)).length;
2740
- const maxQueued = config.providers[current.provider].max_queued;
2741
- if (queuedCount >= maxQueued) {
2742
- summary.skipped.push({
2743
- kind: "lane",
2744
- id: current.lane_id,
2745
- why: `lane_queue_full: ${current.provider} queue has ${queuedCount}/${maxQueued} lanes`,
2746
- });
2747
- recordKeepAliveSkipEvent(hqRoot, current, "lane_queue_full", current.version, summary, deps, `${current.provider} queue has ${queuedCount}/${maxQueued} lanes`);
2748
- continue;
2749
- }
2750
- const seniorId = current.senior.kind === "lane" ? current.senior.id : undefined;
2751
- let finishedChildrenDoNotCount = false;
2752
- if (seniorId !== undefined) {
2753
- let grantPreflight;
2754
- try {
2755
- const companyResolution = resolveLaneCompany(hqRoot, current);
2756
- const company = companyResolution.company ?? undefined;
2757
- finishedChildrenDoNotCount = await readLanesRegistryFlagFailOff(LANE_HIRE_GRANT_FINISHED_LANES_FLAG, {
2758
- company,
2759
- ...(company ? { companyUidSource: laneCompanyUidSource(companyResolution.source) } : {}),
2760
- env: deps.env,
2761
- }, deps.readRegistryFlag ?? readLanesRegistryFlag);
2762
- grantPreflight = withHireGrantAdmission(hqRoot, seniorId, (snapshot) => {
2763
- const latestLane = readLane(hqRoot, current.lane_id);
2764
- if (latestLane.version !== current.version ||
2765
- latestLane.state !== current.state ||
2766
- latestLane.keep_alive !== true ||
2767
- latestLane.keep_alive_opted_in_at !== current.keep_alive_opted_in_at ||
2768
- latestLane.senior.kind !== "lane" ||
2769
- latestLane.senior.id !== seniorId)
2770
- return { kind: "stale" };
2771
- const refusal = terminalChildResumeHireGrantError(latestLane, snapshot, finishedChildrenDoNotCount);
2772
- return refusal === null
2773
- ? { kind: "eligible" }
2774
- : { kind: "refused", error: refusal.error, message: refusal.message };
2775
- }, undefined, new Set(), finishedChildrenDoNotCount);
2776
- }
2777
- catch (err) {
2778
- markDirty(summary, {
2779
- kind: "lane",
2780
- id: current.lane_id,
2781
- why: `keep-alive hire admission preflight failed: ${err instanceof Error ? err.message : String(err)}`,
2782
- });
2783
- continue;
2784
- }
2785
- if (grantPreflight.kind === "stale")
2786
- continue;
2787
- if (grantPreflight.kind === "refused") {
2788
- summary.skipped.push({
2789
- kind: "lane",
2790
- id: current.lane_id,
2791
- why: `keep_alive_hire_admission: ${grantPreflight.error}: ${grantPreflight.message}`,
2792
- });
2793
- recordKeepAliveSkipEvent(hqRoot, current, "hire_admission", current.version, summary, deps, `${grantPreflight.error}: ${grantPreflight.message}`);
2794
- continue;
2795
- }
2796
- }
2797
- const resumedFrom = current.state === "done" ? "done" : "interrupted";
2798
- const resumeText = resumedFrom === "interrupted"
2799
- ? LEAD_LANE_KEEP_ALIVE_INTERRUPTED_RESUME_TEXT
2800
- : LEAD_LANE_KEEP_ALIVE_RESUME_TEXT;
2801
- const carriesAnsweredQuestions = resumedFrom === "done" &&
2802
- current.keep_alive_standing === true &&
2803
- undeliveredAnsweredQuestions(current, questions).length > 0;
2804
- if (carriesAnsweredQuestions && !deps.dryRun) {
2805
- writeAnsweredQuestions(hqRoot, current, questions);
2806
- }
2807
- let accountSelectionFailure;
2808
- let accountSelection;
2809
- if (current.provider === "claude" && deps.prepareKeepAliveClaudeResume !== undefined) {
2810
- const optInBeforePreparation = current.keep_alive_opted_in_at;
2811
- let preparedLane;
2812
- try {
2813
- const prepared = await deps.prepareKeepAliveClaudeResume(current);
2814
- preparedLane = prepared.lane;
2815
- accountSelectionFailure = prepared.selectionFailure;
2816
- accountSelection = prepared.accountSelection;
2817
- }
2818
- catch (err) {
2819
- // Account balancing is best-effort for keep-alive. A failure to read
2820
- // balance inputs must not prevent the existing session from resuming.
2821
- try {
2822
- preparedLane = readLane(hqRoot, current.lane_id);
2823
- }
2824
- catch (readErr) {
2825
- markDirty(summary, {
2826
- kind: "lane",
2827
- id: current.lane_id,
2828
- why: `keep-alive account selection failed and lane could not be re-read: ${readErr instanceof Error ? readErr.message : String(readErr)}`,
2829
- });
2830
- continue;
2831
- }
2832
- accountSelectionFailure = err instanceof Error ? err.message : String(err);
2833
- }
2834
- const preparedSessionId = preparedLane.provider_session_id ?? preparedLane.last_resume?.session_id;
2835
- if (preparedLane.state !== initialState ||
2836
- preparedLane.keep_alive !== true ||
2837
- !laneRunsHere(preparedLane, deps.host) ||
2838
- preparedSessionId !== currentSessionId)
2839
- continue;
2840
- let latest;
2841
- try {
2842
- latest = deps.dryRun
2843
- ? (deps.readKeepAliveRecheckLane?.(hqRoot, current.lane_id) ?? readLane(hqRoot, current.lane_id))
2844
- : readLane(hqRoot, current.lane_id);
2845
- }
2846
- catch (err) {
2847
- markDirty(summary, {
2848
- kind: "lane",
2849
- id: current.lane_id,
2850
- why: `keep-alive eligibility recheck failed after account selection: ${err instanceof Error ? err.message : String(err)}`,
2851
- });
2852
- continue;
2853
- }
2854
- if (latest.state !== initialState || !laneRunsHere(latest, deps.host))
2855
- continue;
2856
- if (latest.keep_alive !== true) {
2857
- recordKeepAliveSkip(hqRoot, latest, "not_opted_in", summary, deps);
2858
- continue;
2859
- }
2860
- if (latest.keep_alive_opted_in_at !== optInBeforePreparation) {
2861
- summary.skipped.push({
2862
- kind: "lane",
2863
- id: latest.lane_id,
2864
- why: "keep_alive_eligibility_changed_during_account_selection",
2865
- });
2866
- continue;
2867
- }
2868
- const latestSessionId = latest.provider_session_id ?? latest.last_resume?.session_id;
2869
- if (latestSessionId !== currentSessionId)
2870
- continue;
2871
- let latestEligibilityAtMs;
2872
- if (initialState === "done") {
2873
- const envelope = readLatestEnvelopeRecord(hqRoot, latest.lane_id);
2874
- if (!envelope || envelope.envelope.decision !== "done")
2875
- continue;
2876
- const hasPendingWork = hasPendingKeepAliveWork(hqRoot, latest, questions);
2877
- const finalDoneVerdict = doneKeepAliveVerdict(latest, envelope.envelope, latest.keep_alive_standing === true && hasPendingWork, {
2878
- hasPendingWork,
2879
- nowMs,
2880
- nextCheckAnchorMs: Date.parse(latest.last_envelope?.at ?? latest.updated_at),
2881
- });
2882
- if (finalDoneVerdict !== "eligible") {
2883
- if (finalDoneVerdict === "next_check_at") {
2884
- recordDoneKeepAlivePolicySkip(hqRoot, summary, latest, finalDoneVerdict, deps);
2885
- }
2886
- continue;
2887
- }
2888
- try {
2889
- latestEligibilityAtMs = latestEnvelopeMtimeMs(hqRoot, latest, envelope.ref);
2890
- }
2891
- catch (err) {
2892
- markDirty(summary, {
2893
- kind: "lane",
2894
- id: latest.lane_id,
2895
- why: `keep-alive envelope timestamp unavailable after account selection: ${err instanceof Error ? err.message : String(err)}`,
2896
- });
2897
- continue;
2898
- }
2899
- }
2900
- else {
2901
- latestEligibilityAtMs = Date.parse(latest.interrupted_at ?? "");
2902
- }
2903
- const latestOptedInAtMs = Date.parse(latest.keep_alive_opted_in_at ?? "");
2904
- if (!Number.isFinite(latestEligibilityAtMs) || !Number.isFinite(latestOptedInAtMs))
2905
- continue;
2906
- if (latestEligibilityAtMs <= latestOptedInAtMs) {
2907
- recordKeepAliveSkip(hqRoot, latest, initialState === "done" ? "envelope_before_opt_in" : "interruption_before_opt_in", summary, deps);
2908
- continue;
2909
- }
2910
- current = deps.dryRun ? preparedLane : latest;
2911
- }
2912
- if (accountSelectionFailure !== undefined) {
2913
- summary.skipped.push({
2914
- kind: "lane",
2915
- id: current.lane_id,
2916
- why: `keep_alive_account_selection_failed: ${accountSelectionFailure}`,
2917
- });
2918
- if (!deps.dryRun) {
2919
- recordKeepAliveSkipEvent(hqRoot, current, "account_selection_failed", current.version, summary, deps, accountSelectionFailure);
2920
- recordKeepAliveEvent(hqRoot, current.lane_id, summary, {
2921
- event: "keep_alive_account_selection_failed",
2922
- at: nowIso,
2923
- account: current.account ?? null,
2924
- reason: accountSelectionFailure,
2925
- });
2926
- }
2927
- }
2928
- const queue = (expectedSeniorId) => casLaneIf(hqRoot, current.lane_id, (latestLane) => latestLane.version === current.version &&
2929
- latestLane.state === current.state &&
2930
- latestLane.keep_alive === true &&
2931
- latestLane.keep_alive_opted_in_at === current.keep_alive_opted_in_at &&
2932
- (expectedSeniorId === undefined ||
2933
- (latestLane.senior.kind === "lane" && latestLane.senior.id === expectedSeniorId)), (latestLane) => ({
2934
- ...latestLane,
2935
- state: "queued",
2936
- queued_reason: undefined,
2937
- queued_details: undefined,
2938
- queued_at: nowIso,
2939
- admission_reserved_at: undefined,
2940
- terminal_reason: undefined,
2941
- terminal_details: undefined,
2942
- pending_resume: {
2943
- session_id: currentSessionId,
2944
- trigger: carriesAnsweredQuestions ? "answered_questions" : "resume",
2945
- keep_alive: true,
2946
- resumed_from: resumedFrom,
2947
- text: resumeText,
2948
- compact: true,
2949
- at: nowIso,
2950
- },
2951
- keep_alive_resume_count: attempt,
2952
- keep_alive_history: appendCappedHistory(latestLane.keep_alive_history ?? [], {
2953
- at: nowIso,
2954
- resumed_from: resumedFrom,
2955
- account: latestLane.account ?? null,
2956
- }),
2957
- keep_alive_last_resume_at: nowIso,
2958
- keep_alive_next_eligible_at: nextEligibleAt,
2959
- keep_alive_exhausted_at: undefined,
2960
- keep_alive_blocked_reason: undefined,
2961
- }), deps.dryRun);
2962
- let queued;
2963
- if (seniorId) {
2964
- try {
2965
- queued = withHireGrantAdmission(hqRoot, seniorId, (snapshot) => {
2966
- const latestLane = readLane(hqRoot, current.lane_id);
2967
- if (latestLane.version !== current.version ||
2968
- latestLane.senior.kind !== "lane" ||
2969
- latestLane.senior.id !== seniorId)
2970
- return { changed: false, record: latestLane };
2971
- const refusal = terminalChildResumeHireGrantError(latestLane, snapshot, finishedChildrenDoNotCount);
2972
- if (refusal !== null) {
2973
- summary.skipped.push({
2974
- kind: "lane",
2975
- id: current.lane_id,
2976
- why: `keep_alive_hire_admission: ${refusal.error}: ${refusal.message}`,
2977
- });
2978
- return { changed: false, record: latestLane };
2979
- }
2980
- return queue(seniorId);
2981
- }, undefined, new Set(), finishedChildrenDoNotCount);
2982
- }
2983
- catch (err) {
2984
- markDirty(summary, {
2985
- kind: "lane",
2986
- id: current.lane_id,
2987
- why: `keep-alive hire admission failed: ${err instanceof Error ? err.message : String(err)}`,
2988
- });
2989
- continue;
2990
- }
2991
- }
2992
- else {
2993
- queued = queue(seniorId);
2994
- }
2995
- if (!queued.changed)
2996
- continue;
2997
- summary.resumed.push({
2998
- id: current.lane_id,
2999
- from: resumedFrom,
3000
- to: "queued",
3001
- reason: "keep_alive",
3002
- });
3003
- if (!deps.dryRun) {
3004
- recordKeepAliveEvent(hqRoot, current.lane_id, summary, {
3005
- event: "keep_alive_resume",
3006
- at: nowIso,
3007
- attempt,
3008
- next_eligible_at: nextEligibleAt,
3009
- resumed_from: resumedFrom,
3010
- ...(accountSelection !== undefined
3011
- ? { account_selection: keepAliveAccountSelectionEvidence(accountSelection) }
3012
- : {}),
3013
- });
3014
- }
3015
- }
3016
- }
3017
- async function resumeAwaiting(hqRoot, lanes, questions, summary, deps, onlyLaneIds) {
3018
- const config = loadLanesConfig(hqRoot);
3019
- const awaiting = lanes.filter((lane) => lane.state === "awaiting_input" &&
3020
- laneRunsHere(lane, deps.host) &&
3021
- (onlyLaneIds === undefined || onlyLaneIds.has(lane.lane_id)));
3022
- if (awaiting.length === 0 || deps.skipAdmissionWithoutCpuSample === true)
3023
- return;
3024
- const throttleDeps = deps.throttleDeps ?? defaultThrottleDeps(deps.env ?? process.env);
3025
- let throttleDecision;
3026
- if (deps.dryRun) {
3027
- const loaded = readThrottleState(throttleStatePath(hqRoot), throttleDeps);
3028
- const state = loaded === "missing" ? emptyThrottleState() : loaded;
3029
- throttleDecision = evaluateThrottle({
3030
- busy: deps.cpuBusyFraction !== undefined
3031
- ? deps.cpuBusyFraction
3032
- : deps.cpuObservation?.busy ?? await resolveCpuBusyFraction(throttleDeps),
3033
- availableMemMb: readAvailableMemMb(throttleDeps),
3034
- cpuCheckEnabled: isCpuCheckEnabled(throttleDeps.env),
3035
- state,
3036
- throttle: config.throttle,
3037
- nowMs: deps.now().getTime(),
3038
- });
3039
- }
3040
- else {
3041
- const throttle = await decideAdmission(hqRoot, config.throttle, throttleDeps, deps.cpuBusyFraction, deps.cpuObservation);
3042
- throttleDecision = throttle.decision;
3043
- }
3044
- const spawnFn = deps.spawn ?? (async (root, lane) => spawnLaneWorker(root, lane, {
3045
- ...spawnDepsForHost(deps),
3046
- lowDiskGuardEnabled: await lowDiskGuardEnabledForLane(root, lane, deps.readRegistryFlag),
3047
- }));
3048
- const nowMs = deps.now().getTime();
3049
- const fallbackAnswerAt = new Date(nowMs).toISOString();
3050
- const answerAtByLane = new Map();
3051
- for (const question of questions) {
3052
- if (question.status !== "answered")
3053
- continue;
3054
- const at = question.answer?.answered_at ?? question.asked_at;
3055
- const previous = answerAtByLane.get(question.origin.id);
3056
- if (previous === undefined || Date.parse(at) > Date.parse(previous)) {
3057
- answerAtByLane.set(question.origin.id, at);
3058
- }
3059
- }
3060
- const latestAnswerAt = (lane) => answerAtByLane.get(lane.lane_id) ?? fallbackAnswerAt;
3061
- awaiting.sort((a, b) => {
3062
- const byAnswerTime = Date.parse(latestAnswerAt(a)) - Date.parse(latestAnswerAt(b));
3063
- return byAnswerTime || compareLaneQueueOrder(a, b);
3064
- });
3065
- const capacityLanes = lanes.map((lane) => {
3066
- try {
3067
- return readLane(hqRoot, lane.lane_id);
3068
- }
3069
- catch {
3070
- return lane;
3071
- }
3072
- });
3073
- const running = capacityLanes.filter((lane) => (lane.state === "running" ||
3074
- hasFreshAdmissionReservation(lane, nowMs) ||
3075
- hasActivePendingResume(lane, nowMs)) &&
3076
- laneRunsHere(lane, deps.host));
3077
- for (const lane of awaiting) {
3078
- const current = readLane(hqRoot, lane.lane_id);
3079
- if (current.state !== "awaiting_input")
3080
- continue;
3081
- const pending = questions.filter((q) => q.origin.id === lane.lane_id && q.status === "pending");
3082
- const answered = questions.filter((q) => q.origin.id === lane.lane_id && q.status === "answered");
3083
- if (pending.length > 0 || answered.length === 0)
3084
- continue;
3085
- try {
3086
- if (current.senior.kind === "lane" && current.senior.id !== undefined) {
3087
- const companyResolution = resolveLaneCompany(hqRoot, current);
3088
- const company = companyResolution.company ?? undefined;
3089
- const finishedChildrenDoNotCount = await readLanesRegistryFlagFailOff(LANE_HIRE_GRANT_FINISHED_LANES_FLAG, {
3090
- company,
3091
- ...(company ? { companyUidSource: laneCompanyUidSource(companyResolution.source) } : {}),
3092
- env: deps.env,
3093
- }, deps.readRegistryFlag ?? readLanesRegistryFlag);
3094
- if (finishedChildrenDoNotCount) {
3095
- const admission = withHireGrantAdmission(hqRoot, current.senior.id, (snapshot) => {
3096
- const latest = readLane(hqRoot, current.lane_id);
3097
- if (latest.version !== current.version || latest.state !== "awaiting_input") {
3098
- return { kind: "stale" };
3099
- }
3100
- const refusal = terminalChildResumeHireGrantError(latest, snapshot, true);
3101
- return refusal === null
3102
- ? { kind: "eligible" }
3103
- : { kind: "refused", error: refusal.error, message: refusal.message };
3104
- }, undefined, new Set(), true);
3105
- if (admission.kind === "stale")
3106
- continue;
3107
- if (admission.kind === "refused") {
3108
- if (!deps.dryRun)
3109
- writeAnsweredQuestions(hqRoot, current, questions);
3110
- summary.skipped.push({
3111
- kind: "lane",
3112
- id: current.lane_id,
3113
- why: `${admission.error}: ${admission.message}`,
3114
- });
3115
- continue;
3116
- }
3117
- }
3118
- }
3119
- let profileMaxConcurrent;
3120
- if (current.worker_profile !== undefined) {
3121
- profileMaxConcurrent = loadWorkerProfile(hqRoot, current.worker_profile).meta.max_concurrent;
3122
- }
3123
- const capReason = laneAdmissionCapReason(running, current, config, deps.host, profileMaxConcurrent, nowMs);
3124
- const answeredAt = latestAnswerAt(current);
3125
- const queueCandidate = { ...current, state: "queued", queued_at: answeredAt };
3126
- const earlierQueued = capacityLanes.some((queued) => queued.lane_id !== current.lane_id &&
3127
- queued.state === "queued" &&
3128
- queued.provider === current.provider &&
3129
- laneRunsHere(queued, deps.host) &&
3130
- compareLaneQueueOrder(queued, queueCandidate) < 0);
3131
- const queueReason = !throttleDecision.admit
3132
- ? "throttle"
3133
- : capReason ?? (earlierQueued ? "queue_position" : undefined);
3134
- if (queueReason !== undefined) {
3135
- const queuedCount = capacityLanes.filter((queued) => queued.state === "queued" &&
3136
- queued.provider === current.provider &&
3137
- laneRunsHere(queued, deps.host) &&
3138
- !hasFreshAdmissionReservation(queued, nowMs)).length;
3139
- const maxQueued = config.providers[current.provider].max_queued;
3140
- if (queuedCount >= maxQueued) {
3141
- summary.skipped.push({
3142
- kind: "lane",
3143
- id: current.lane_id,
3144
- why: `lane_queue_full: ${current.provider} queue has ${queuedCount}/${maxQueued} lanes`,
3145
- });
3146
- continue;
3147
- }
3148
- }
3149
- if (!deps.dryRun)
3150
- writeAnsweredQuestions(hqRoot, current, questions);
3151
- if (queueReason !== undefined) {
3152
- const answerEnv = deps.spawnLaneDeps?.env ?? deps.env ?? process.env;
3153
- const answerCaller = typeof answerEnv.HQ_LANE_ID === "string"
3154
- ? answerEnv.HQ_LANE_ID.trim() || "user"
3155
- : "user";
3156
- const queued = casLaneIf(hqRoot, current.lane_id, (latest) => latest.state === "awaiting_input", (latest) => ({
3157
- ...latest,
3158
- state: "queued",
3159
- queued_reason: queueReason,
3160
- queued_details: undefined,
3161
- queued_at: answeredAt,
3162
- pending_resume: {
3163
- ...(latest.provider_session_id
3164
- ? { session_id: latest.provider_session_id }
3165
- : {}),
3166
- trigger: "answered_questions",
3167
- caller: answerCaller,
3168
- at: answeredAt,
3169
- },
3170
- }), deps.dryRun);
3171
- summary.resumed.push({
3172
- id: queued.record.lane_id,
3173
- from: "awaiting_input",
3174
- to: "queued",
3175
- reason: queueReason,
3176
- });
3177
- capacityLanes.push(queued.record);
3178
- continue;
3179
- }
3180
- if (deps.dryRun) {
3181
- summary.resumed.push({
3182
- id: lane.lane_id,
3183
- from: "awaiting_input",
3184
- to: "running",
3185
- });
3186
- running.push({ ...current, state: "running" });
3187
- continue;
3188
- }
3189
- const started = await spawnFn(hqRoot, lane);
3190
- if (started.state === "running")
3191
- running.push(started);
3192
- summary.resumed.push({
3193
- id: started.lane_id,
3194
- from: "awaiting_input",
3195
- to: started.state,
3196
- });
3197
- }
3198
- catch (err) {
3199
- terminalizeModelUnsupported(hqRoot, lane, err);
3200
- const errorName = err instanceof LanesError ? `${err.error}: ` : "";
3201
- const errorMessage = err instanceof Error ? err.message : String(err);
3202
- markDirty(summary, {
3203
- kind: "lane",
3204
- id: lane.lane_id,
3205
- why: `resume failed: ${errorName}${errorMessage}`,
3206
- });
3207
- }
3208
- }
3209
- }
3210
- /**
3211
- * One idempotent reconcile pass. Throws only when the store directory itself
3212
- * cannot be listed; per-record failures are logged onto the summary and set
3213
- * `ok: false` rather than being swallowed.
3214
- */
3215
- export async function runReconcilePass(hqRoot, deps) {
3216
- return withReconcileLock(hqRoot, async () => runPassBody(hqRoot, deps));
3217
- }
3218
- async function runTerminalKeepAliveCandidatePass(hqRoot, lanes, summary, deps, now, candidate) {
3219
- const lane = lanes.find((row) => row.lane_id === candidate.laneId);
3220
- if (!lane || lane.version !== candidate.version || lane.state !== "done" ||
3221
- lane.keep_alive !== true || !laneRunsHere(lane, deps.host))
3222
- return summary;
3223
- let company;
3224
- try {
3225
- company = resolveLaneCompany(hqRoot, lane).company;
3226
- }
3227
- catch (err) {
3228
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `keep-alive company lookup failed: ${err instanceof Error ? err.message : String(err)}` });
3229
- return summary;
3230
- }
3231
- if (company === null)
3232
- return summary;
3233
- const readFlag = deps.readRegistryFlag ?? readLanesRegistryFlag;
3234
- const options = { company, env: deps.env ?? process.env };
3235
- let keepAliveEnabled;
3236
- let lineageAware;
3237
- let immediateFirstResumeEnabled;
3238
- try {
3239
- [keepAliveEnabled, lineageAware, immediateFirstResumeEnabled] = await Promise.all([
3240
- readFlag(LEAD_LANE_KEEP_ALIVE_FLAG, options),
3241
- readFlag(LANE_SUPERSESSION_LINEAGE_AWARE_FLAG, options),
3242
- readFlag(LEAD_LANE_KEEP_ALIVE_IMMEDIATE_FIRST_RESUME_FLAG, options),
3243
- ]);
3244
- }
3245
- catch (err) {
3246
- markDirty(summary, { kind: "lane", id: lane.lane_id, why: `keep-alive flag read failed: ${err instanceof Error ? err.message : String(err)}` });
3247
- return summary;
3248
- }
3249
- if (!keepAliveEnabled)
3250
- return summary;
3251
- await withAllLaneAdmissionLocks(hqRoot, async () => {
3252
- const freshLanes = loadLaneRecords(hqRoot, summary);
3253
- const freshQuestions = loadQuestionRecords(hqRoot, summary);
3254
- const current = freshLanes.find((row) => row.lane_id === candidate.laneId);
3255
- if (!current || current.version !== candidate.version || current.state !== "done")
3256
- return;
3257
- await resumeKeepAlive(hqRoot, freshLanes, freshQuestions, summary, deps, now, new Map([[keepAliveCompanyKey(company), true]]), new Map([[keepAliveCompanyKey(company), lineageAware]]), new Map(), new Map([[keepAliveCompanyKey(company), immediateFirstResumeEnabled]]), candidate);
3258
- await admitQueued(hqRoot, loadLaneRecords(hqRoot, summary), summary, deps, now, new Set([candidate.laneId]));
3259
- });
3260
- return summary;
3261
- }
3262
- /** Run the reconcile answer-resume path for one lane before questions answer returns. */
3263
- export async function wakeAnsweredLaneNow(hqRoot, laneId, deps) {
3264
- const summary = emptySummary(deps.dryRun);
3265
- const throttleDeps = deps.throttleDeps ?? defaultThrottleDeps(deps.env ?? process.env);
3266
- await withReconcileLock(hqRoot, async () => {
3267
- const previousState = readThrottleState(throttleStatePath(hqRoot), throttleDeps);
3268
- const previousSnapshot = previousState === "missing" || previousState === "unreadable"
3269
- ? undefined
3270
- : previousState.cpu_snapshot;
3271
- const cpuObservation = await sampleCpuBusyObservation(throttleDeps, previousSnapshot);
3272
- const admissionDeps = {
3273
- ...deps,
3274
- cpuBusyFraction: cpuObservation.busy,
3275
- cpuObservation,
3276
- skipAdmissionWithoutCpuSample: false,
3277
- };
3278
- await withAllLaneAdmissionLocks(hqRoot, async () => {
3279
- const lanes = loadLaneRecords(hqRoot, summary);
3280
- const questions = loadQuestionRecords(hqRoot, summary);
3281
- if (!lanes.some((lane) => lane.lane_id === laneId)) {
3282
- throw new LanesError("lane_not_found", `Lane ${laneId} was not found`, { lane_id: laneId });
3283
- }
3284
- await resumeAwaiting(hqRoot, lanes, questions, summary, admissionDeps, new Set([laneId]));
3285
- });
3286
- });
3287
- const latest = readLane(hqRoot, laneId);
3288
- const laneQuestions = loadQuestionRecords(hqRoot, emptySummary(false)).filter((question) => question.origin.id === laneId);
3289
- const remaining = laneQuestions
3290
- .filter((question) => question.status === "pending")
3291
- .map((question) => question.question_id)
3292
- .sort();
3293
- if (latest.state === "running") {
3294
- return { status: "resumed", lane_state: latest.state };
3295
- }
3296
- if (latest.state === "queued" && latest.pending_resume?.trigger === "answered_questions") {
3297
- return {
3298
- status: "queued",
3299
- lane_state: latest.state,
3300
- ...(latest.queued_reason !== undefined ? { queued_reason: latest.queued_reason } : {}),
3301
- };
3302
- }
3303
- if (latest.state === "awaiting_input" && remaining.length > 0) {
3304
- return {
3305
- status: "awaiting_answers",
3306
- lane_state: latest.state,
3307
- remaining_question_ids: remaining,
3308
- };
3309
- }
3310
- const failure = summary.skipped.find((skip) => skip.kind === "lane" && skip.id === laneId);
3311
- if (latest.state === "awaiting_input" && failure?.why.startsWith("hire_grant_exhausted:")) {
3312
- return {
3313
- status: "deferred",
3314
- lane_state: latest.state,
3315
- error: failure.why,
3316
- };
3317
- }
3318
- return {
3319
- status: "failed",
3320
- lane_state: latest.state,
3321
- error: failure?.why ?? `Lane ${laneId} did not leave awaiting_input after its questions were answered`,
3322
- };
3323
- }
3324
- //# sourceMappingURL=reconcile.js.map