@the-open-engine/zeroshot 6.38.1 → 6.39.1

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 (383) hide show
  1. package/README.md +6 -0
  2. package/cli/event-copy.js +8 -7
  3. package/cli/event-copy.ts +12 -0
  4. package/cli/index.js +67 -48
  5. package/cli/json-export.js +61 -64
  6. package/cli/json-export.ts +112 -0
  7. package/cli/message-formatter-utils.js +68 -56
  8. package/cli/message-formatter-utils.ts +121 -0
  9. package/cli/message-formatters-normal.js +124 -259
  10. package/cli/message-formatters-normal.ts +250 -0
  11. package/cli/message-formatters-watch.js +81 -84
  12. package/cli/message-formatters-watch.ts +189 -0
  13. package/lib/cluster/generated/protocol-schema.cjs +1 -1
  14. package/lib/cluster/generated/protocol-schema.mjs +1 -1
  15. package/lib/cluster/generated/protocol.d.cts +8 -0
  16. package/lib/cluster/generated/protocol.d.mts +8 -0
  17. package/lib/cluster/generated/protocol.d.ts +8 -0
  18. package/lib/clusters-registry.d.ts +23 -0
  19. package/lib/clusters-registry.js +17 -29
  20. package/lib/completion.d.ts +12 -0
  21. package/lib/completion.js +84 -94
  22. package/lib/compose-utils.d.ts +32 -0
  23. package/lib/compose-utils.js +57 -72
  24. package/lib/detached-startup.d.ts +84 -0
  25. package/lib/detached-startup.js +232 -302
  26. package/lib/docker-config.d.ts +43 -0
  27. package/lib/docker-config.js +150 -325
  28. package/lib/docker-env-auth.d.ts +17 -0
  29. package/lib/docker-env-auth.js +75 -0
  30. package/lib/git-remote-utils.d.ts +48 -0
  31. package/lib/git-remote-utils.js +147 -213
  32. package/lib/id-detector.d.ts +15 -0
  33. package/lib/id-detector.js +37 -41
  34. package/lib/path-check.d.ts +30 -0
  35. package/lib/path-check.js +42 -45
  36. package/lib/process-liveness.d.ts +16 -0
  37. package/lib/process-liveness.js +19 -12
  38. package/lib/provider-credential-path.d.ts +16 -0
  39. package/lib/provider-credential-path.js +31 -29
  40. package/lib/provider-defaults.d.ts +30 -0
  41. package/lib/provider-defaults.js +47 -42
  42. package/lib/provider-detection.d.ts +11 -0
  43. package/lib/provider-detection.js +91 -54
  44. package/lib/provider-names.d.ts +38 -0
  45. package/lib/provider-names.js +56 -64
  46. package/lib/repo-settings.d.ts +28 -0
  47. package/lib/repo-settings.js +83 -59
  48. package/lib/run-mode.d.ts +21 -0
  49. package/lib/run-mode.js +21 -20
  50. package/lib/run-plan.d.ts +28 -0
  51. package/lib/run-plan.js +17 -16
  52. package/lib/settings/claude-auth.d.ts +35 -0
  53. package/lib/settings/claude-auth.js +30 -47
  54. package/lib/settings-defaults.d.ts +20 -0
  55. package/lib/settings-defaults.js +149 -0
  56. package/lib/settings-error.d.ts +7 -0
  57. package/lib/settings-error.js +8 -0
  58. package/lib/settings-issue-providers.d.ts +10 -0
  59. package/lib/settings-issue-providers.js +12 -0
  60. package/lib/settings-models.d.ts +22 -0
  61. package/lib/settings-models.js +93 -0
  62. package/lib/settings-storage.d.ts +23 -0
  63. package/lib/settings-storage.js +205 -0
  64. package/lib/settings-validation.d.ts +7 -0
  65. package/lib/settings-validation.js +182 -0
  66. package/lib/settings.d.ts +29 -0
  67. package/lib/settings.js +41 -736
  68. package/lib/setup-apply-types.d.ts +66 -0
  69. package/lib/setup-apply-types.js +2 -0
  70. package/lib/setup-apply.d.ts +39 -0
  71. package/lib/setup-apply.js +267 -264
  72. package/lib/setup-journal.d.ts +41 -0
  73. package/lib/setup-journal.js +119 -76
  74. package/lib/setup-plan-types.d.ts +102 -0
  75. package/lib/setup-plan-types.js +2 -0
  76. package/lib/setup-plan.d.ts +30 -0
  77. package/lib/setup-plan.js +300 -324
  78. package/lib/setup-undo.d.ts +44 -0
  79. package/lib/setup-undo.js +61 -74
  80. package/lib/start-cluster-config.d.ts +26 -0
  81. package/lib/start-cluster-config.js +56 -0
  82. package/lib/start-cluster-environment.d.ts +41 -0
  83. package/lib/start-cluster-environment.js +134 -0
  84. package/lib/start-cluster-run-options.d.ts +54 -0
  85. package/lib/start-cluster-run-options.js +148 -0
  86. package/lib/start-cluster.d.ts +64 -0
  87. package/lib/start-cluster.js +98 -454
  88. package/lib/stream-json-parser.d.ts +17 -0
  89. package/lib/stream-json-parser.js +51 -52
  90. package/npm-shrinkwrap.json +6 -8
  91. package/package.json +14 -8
  92. package/scripts/postinstall.js +27 -0
  93. package/scripts/rust-distribution.js +36 -45
  94. package/src/agent/agent-command-proofs-context.js +35 -45
  95. package/src/agent/agent-command-proofs-context.ts +87 -0
  96. package/src/agent/agent-config-settings.js +19 -0
  97. package/src/agent/agent-config-settings.ts +41 -0
  98. package/src/agent/agent-config.js +122 -231
  99. package/src/agent/agent-config.ts +285 -0
  100. package/src/agent/agent-context-builder.js +38 -273
  101. package/src/agent/agent-context-builder.ts +51 -0
  102. package/src/agent/agent-context-environment-sections.js +105 -0
  103. package/src/agent/agent-context-environment-sections.ts +134 -0
  104. package/src/agent/agent-context-packs.js +165 -0
  105. package/src/agent/agent-context-packs.ts +296 -0
  106. package/src/agent/agent-context-prompt-sections.js +100 -0
  107. package/src/agent/agent-context-prompt-sections.ts +148 -0
  108. package/src/agent/agent-context-sections.js +25 -334
  109. package/src/agent/agent-context-sections.ts +36 -0
  110. package/src/agent/agent-context-sources.js +96 -149
  111. package/src/agent/agent-context-sources.ts +253 -0
  112. package/src/agent/agent-context-types.js +2 -0
  113. package/src/agent/agent-context-types.ts +132 -0
  114. package/src/agent/agent-context-validation-sections.js +73 -0
  115. package/src/agent/agent-context-validation-sections.ts +129 -0
  116. package/src/agent/agent-input-injector.js +99 -118
  117. package/src/agent/agent-input-injector.ts +195 -0
  118. package/src/agent/agent-liveness-poll.js +74 -78
  119. package/src/agent/agent-liveness-poll.ts +163 -0
  120. package/src/agent/agent-quality-gate-schema.js +77 -86
  121. package/src/agent/agent-quality-gate-schema.ts +135 -0
  122. package/src/agent/agent-quality-gates-context.js +44 -50
  123. package/src/agent/agent-quality-gates-context.ts +91 -0
  124. package/src/agent/agent-stuck-detector.js +171 -239
  125. package/src/agent/agent-stuck-detector.ts +288 -0
  126. package/src/agent/agent-trigger-evaluator.js +29 -46
  127. package/src/agent/agent-trigger-evaluator.ts +98 -0
  128. package/src/agent/context-metrics.js +110 -141
  129. package/src/agent/context-metrics.ts +261 -0
  130. package/src/agent/context-pack-builder.js +72 -358
  131. package/src/agent/context-pack-builder.ts +105 -0
  132. package/src/agent/context-pack-core.js +70 -0
  133. package/src/agent/context-pack-core.ts +96 -0
  134. package/src/agent/context-pack-selection.js +119 -0
  135. package/src/agent/context-pack-selection.ts +187 -0
  136. package/src/agent/context-pack-truncation.js +139 -0
  137. package/src/agent/context-pack-truncation.ts +208 -0
  138. package/src/agent/context-pack-types.js +2 -0
  139. package/src/agent/context-pack-types.ts +98 -0
  140. package/src/agent/context-replay-policy.js +30 -39
  141. package/src/agent/context-replay-policy.ts +71 -0
  142. package/src/agent/critical-agent-policy.js +6 -10
  143. package/src/agent/critical-agent-policy.ts +20 -0
  144. package/src/agent/guidance-queue.js +74 -87
  145. package/src/agent/guidance-queue.ts +166 -0
  146. package/src/agent/output-extraction-error-detail.js +45 -0
  147. package/src/agent/output-extraction-error-detail.ts +49 -0
  148. package/src/agent/output-extraction-failures.js +125 -0
  149. package/src/agent/output-extraction-failures.ts +149 -0
  150. package/src/agent/output-extraction-json.js +234 -0
  151. package/src/agent/output-extraction-json.ts +235 -0
  152. package/src/agent/output-extraction-pi.js +124 -0
  153. package/src/agent/output-extraction-pi.ts +137 -0
  154. package/src/agent/output-extraction-types.js +2 -0
  155. package/src/agent/output-extraction-types.ts +49 -0
  156. package/src/agent/output-extraction.js +17 -617
  157. package/src/agent/output-extraction.ts +30 -0
  158. package/src/agent/output-reformatter-errors.js +83 -0
  159. package/src/agent/output-reformatter-errors.ts +106 -0
  160. package/src/agent/output-reformatter-types.js +2 -0
  161. package/src/agent/output-reformatter-types.ts +86 -0
  162. package/src/agent/output-reformatter.js +140 -185
  163. package/src/agent/output-reformatter.ts +238 -0
  164. package/src/agent/pi-terminal-lifecycle-types.js +2 -0
  165. package/src/agent/pi-terminal-lifecycle-types.ts +41 -0
  166. package/src/agent/pi-terminal-lifecycle.js +186 -206
  167. package/src/agent/pi-terminal-lifecycle.ts +297 -0
  168. package/src/agent/provider-control-plane.js +22 -21
  169. package/src/agent/provider-control-plane.ts +52 -0
  170. package/src/agent/provider-terminal-failure-types.js +2 -0
  171. package/src/agent/provider-terminal-failure-types.ts +60 -0
  172. package/src/agent/provider-terminal-failure.js +194 -191
  173. package/src/agent/provider-terminal-failure.ts +262 -0
  174. package/src/agent/rate-limit-backoff.js +31 -74
  175. package/src/agent/rate-limit-backoff.ts +71 -0
  176. package/src/agent/schema-utils.js +85 -125
  177. package/src/agent/schema-utils.ts +148 -0
  178. package/src/agent/structured-output-error.js +34 -33
  179. package/src/agent/structured-output-error.ts +105 -0
  180. package/src/agent/validation-platform.js +27 -27
  181. package/src/agent/validation-platform.ts +49 -0
  182. package/src/agents/git-pusher-template.js +17 -34
  183. package/src/attach/attach-client.js +232 -405
  184. package/src/attach/attach-client.ts +300 -0
  185. package/src/attach/attach-server-cleanup.js +43 -0
  186. package/src/attach/attach-server-cleanup.ts +39 -0
  187. package/src/attach/attach-server-clients.js +117 -0
  188. package/src/attach/attach-server-clients.ts +122 -0
  189. package/src/attach/attach-server-events.js +38 -0
  190. package/src/attach/attach-server-events.ts +36 -0
  191. package/src/attach/attach-server-pty.js +45 -0
  192. package/src/attach/attach-server-pty.ts +64 -0
  193. package/src/attach/attach-server-runtime.js +73 -0
  194. package/src/attach/attach-server-runtime.ts +62 -0
  195. package/src/attach/attach-server-socket.js +28 -0
  196. package/src/attach/attach-server-socket.ts +23 -0
  197. package/src/attach/attach-server-types.js +2 -0
  198. package/src/attach/attach-server-types.ts +82 -0
  199. package/src/attach/attach-server.js +106 -523
  200. package/src/attach/attach-server.ts +158 -0
  201. package/src/attach/index.js +18 -35
  202. package/src/attach/index.ts +18 -0
  203. package/src/attach/protocol.js +95 -184
  204. package/src/attach/protocol.ts +181 -0
  205. package/src/attach/ring-buffer.js +76 -102
  206. package/src/attach/ring-buffer.ts +109 -0
  207. package/src/attach/send-input.js +64 -75
  208. package/src/attach/send-input.ts +94 -0
  209. package/src/attach/socket-discovery.js +164 -218
  210. package/src/attach/socket-discovery.ts +230 -0
  211. package/src/attach/socket-paths.js +42 -61
  212. package/src/attach/socket-paths.ts +81 -0
  213. package/src/claude-credentials.js +51 -54
  214. package/src/claude-credentials.ts +89 -0
  215. package/src/cluster/generated/protocol-schema.ts +1 -1
  216. package/src/cluster/generated/protocol.ts +2 -1
  217. package/src/command-proofs.js +110 -141
  218. package/src/command-proofs.ts +216 -0
  219. package/src/config-router.js +81 -73
  220. package/src/config-router.ts +131 -0
  221. package/src/copy-containment.js +191 -0
  222. package/src/copy-containment.ts +259 -0
  223. package/src/copy-worker.js +53 -30
  224. package/src/copy-worker.ts +88 -0
  225. package/src/darwin-keychain-boundary.js +80 -111
  226. package/src/darwin-keychain-boundary.ts +148 -0
  227. package/src/guidance-topics.js +4 -5
  228. package/src/guidance-topics.ts +10 -0
  229. package/src/input-helpers.js +33 -54
  230. package/src/input-helpers.ts +58 -0
  231. package/src/isolation-manager.js +59 -20
  232. package/src/ledger-sequence.js +45 -48
  233. package/src/ledger-sequence.ts +63 -0
  234. package/src/legacy-lib/clusters-registry.ts +40 -0
  235. package/src/legacy-lib/completion.ts +148 -0
  236. package/src/legacy-lib/compose-utils.ts +102 -0
  237. package/src/legacy-lib/detached-startup.ts +451 -0
  238. package/src/legacy-lib/docker-config.ts +282 -0
  239. package/src/legacy-lib/docker-env-auth.ts +116 -0
  240. package/src/legacy-lib/git-remote-utils.ts +244 -0
  241. package/src/legacy-lib/id-detector.ts +57 -0
  242. package/src/legacy-lib/js-yaml.d.ts +3 -0
  243. package/src/legacy-lib/omelette.d.ts +15 -0
  244. package/src/legacy-lib/path-check.ts +89 -0
  245. package/src/legacy-lib/process-liveness.ts +36 -0
  246. package/src/legacy-lib/provider-credential-path.ts +57 -0
  247. package/src/legacy-lib/provider-defaults.ts +114 -0
  248. package/src/legacy-lib/provider-detection.ts +67 -0
  249. package/src/legacy-lib/provider-names.ts +126 -0
  250. package/src/legacy-lib/repo-settings.ts +88 -0
  251. package/src/legacy-lib/run-mode.ts +58 -0
  252. package/src/legacy-lib/run-plan.ts +49 -0
  253. package/src/legacy-lib/settings/claude-auth.ts +80 -0
  254. package/src/legacy-lib/settings-defaults.ts +212 -0
  255. package/src/legacy-lib/settings-error.ts +8 -0
  256. package/src/legacy-lib/settings-issue-providers.ts +18 -0
  257. package/src/legacy-lib/settings-models.ts +124 -0
  258. package/src/legacy-lib/settings-storage.ts +277 -0
  259. package/src/legacy-lib/settings-validation.ts +220 -0
  260. package/src/legacy-lib/settings.ts +87 -0
  261. package/src/legacy-lib/setup-apply-types.ts +70 -0
  262. package/src/legacy-lib/setup-apply.ts +456 -0
  263. package/src/legacy-lib/setup-journal.ts +185 -0
  264. package/src/legacy-lib/setup-plan-types.ts +96 -0
  265. package/src/legacy-lib/setup-plan.ts +500 -0
  266. package/src/legacy-lib/setup-undo.ts +142 -0
  267. package/src/legacy-lib/start-cluster-config.ts +124 -0
  268. package/src/legacy-lib/start-cluster-environment.ts +166 -0
  269. package/src/legacy-lib/start-cluster-run-options.ts +281 -0
  270. package/src/legacy-lib/start-cluster.ts +237 -0
  271. package/src/legacy-lib/stream-json-parser.ts +88 -0
  272. package/src/message-buffer.js +55 -71
  273. package/src/message-buffer.ts +107 -0
  274. package/src/message-bus-bridge.js +92 -133
  275. package/src/message-bus-bridge.ts +149 -0
  276. package/src/name-generator-shared.js +220 -0
  277. package/src/name-generator-shared.ts +231 -0
  278. package/src/name-generator.js +8 -217
  279. package/src/name-generator.ts +27 -0
  280. package/src/omp-blob-root.js +83 -64
  281. package/src/omp-blob-root.ts +116 -0
  282. package/src/omp-config-overlay.js +65 -38
  283. package/src/omp-config-overlay.ts +110 -0
  284. package/src/omp-execution-fingerprint.js +38 -43
  285. package/src/omp-execution-fingerprint.ts +92 -0
  286. package/src/omp-session-limits.js +10 -15
  287. package/src/omp-session-limits.ts +53 -0
  288. package/src/omp-session-partition.js +2 -2
  289. package/src/omp-session-verifier.js +1 -1
  290. package/src/orchestrator.js +32 -7
  291. package/src/pr-body-template.js +71 -0
  292. package/src/providers/anthropic/index.js +3 -1
  293. package/src/providers/anthropic/index.ts +13 -0
  294. package/src/providers/capabilities.js +19 -27
  295. package/src/providers/capabilities.ts +44 -0
  296. package/src/providers/google/index.js +3 -1
  297. package/src/providers/google/index.ts +13 -0
  298. package/src/providers/openai/index.js +3 -1
  299. package/src/providers/openai/index.ts +13 -0
  300. package/src/providers/opencode/index.js +3 -1
  301. package/src/providers/opencode/index.ts +13 -0
  302. package/src/quality-gates.js +67 -127
  303. package/src/quality-gates.ts +149 -0
  304. package/src/repo-settings-access.js +40 -0
  305. package/src/repo-settings-access.ts +64 -0
  306. package/src/schemas/sub-cluster.js +127 -183
  307. package/src/schemas/sub-cluster.ts +238 -0
  308. package/src/state-snapshot-normalization.js +224 -0
  309. package/src/state-snapshot-normalization.ts +222 -0
  310. package/src/state-snapshot.js +192 -359
  311. package/src/state-snapshot.ts +291 -0
  312. package/src/state-snapshotter.js +118 -130
  313. package/src/state-snapshotter.ts +167 -0
  314. package/src/sub-cluster-wrapper.js +4 -13
  315. package/src/task-run-model-args.js +87 -124
  316. package/src/task-run-model-args.ts +204 -0
  317. package/src/task-runner.js +5 -25
  318. package/src/task-runner.ts +28 -0
  319. package/src/task-startup-error.js +73 -58
  320. package/src/task-startup-error.ts +126 -0
  321. package/src/template-validation/consensus-gate-contracts.js +2 -0
  322. package/src/template-validation/consensus-gate-contracts.ts +70 -0
  323. package/src/template-validation/consensus-gate-scenarios.js +76 -0
  324. package/src/template-validation/consensus-gate-scenarios.ts +130 -0
  325. package/src/template-validation/consensus-gate-stage.js +34 -0
  326. package/src/template-validation/consensus-gate-stage.ts +56 -0
  327. package/src/template-validation/random-topology-contracts.js +2 -0
  328. package/src/template-validation/random-topology-contracts.ts +54 -0
  329. package/src/template-validation/random-topology-dispatch.js +25 -0
  330. package/src/template-validation/random-topology-dispatch.ts +33 -0
  331. package/src/template-validation/random-topology-message-loop.js +53 -0
  332. package/src/template-validation/random-topology-message-loop.ts +73 -0
  333. package/src/template-validation/random-topology-operations.js +79 -0
  334. package/src/template-validation/random-topology-operations.ts +101 -0
  335. package/src/template-validation/random-topology-runtime.js +25 -0
  336. package/src/template-validation/random-topology-runtime.ts +43 -0
  337. package/src/template-validation/random-topology-scenario.js +52 -0
  338. package/src/template-validation/random-topology-scenario.ts +57 -0
  339. package/src/template-validation/random-topology-schema.js +159 -0
  340. package/src/template-validation/random-topology-schema.ts +190 -0
  341. package/src/template-validation/random-topology-state.js +28 -0
  342. package/src/template-validation/random-topology-state.ts +30 -0
  343. package/src/template-validation/random-topology-trigger-evaluation.js +45 -0
  344. package/src/template-validation/random-topology-trigger-evaluation.ts +65 -0
  345. package/src/template-validation/random-topology-trigger.js +75 -0
  346. package/src/template-validation/random-topology-trigger.ts +88 -0
  347. package/src/template-validation/report-formatter.js +40 -46
  348. package/src/template-validation/report-formatter.ts +101 -0
  349. package/src/template-validation/simulate-consensus-gates.js +111 -302
  350. package/src/template-validation/simulate-consensus-gates.ts +196 -0
  351. package/src/template-validation/simulate-random-topology.js +26 -519
  352. package/src/template-validation/simulate-random-topology.ts +57 -0
  353. package/src/template-validation/simulate-two-stage-validation.js +41 -265
  354. package/src/template-validation/simulate-two-stage-validation.ts +55 -0
  355. package/src/template-validation/simulation-agent-runtime.js +25 -0
  356. package/src/template-validation/simulation-agent-runtime.ts +40 -0
  357. package/src/template-validation/simulation-agent.js +29 -0
  358. package/src/template-validation/simulation-agent.ts +73 -0
  359. package/src/template-validation/simulation-runtime.js +34 -0
  360. package/src/template-validation/simulation-runtime.ts +72 -0
  361. package/src/template-validation/two-stage-contracts.js +2 -0
  362. package/src/template-validation/two-stage-contracts.ts +41 -0
  363. package/src/template-validation/two-stage-inputs.js +21 -0
  364. package/src/template-validation/two-stage-inputs.ts +29 -0
  365. package/src/template-validation/two-stage-results.js +30 -0
  366. package/src/template-validation/two-stage-results.ts +40 -0
  367. package/src/template-validation/two-stage-scenario.js +86 -0
  368. package/src/template-validation/two-stage-scenario.ts +120 -0
  369. package/src/worktree-claude-config.js +182 -220
  370. package/src/worktree-claude-config.ts +272 -0
  371. package/src/worktree-tooling-env.js +103 -125
  372. package/src/worktree-tooling-env.ts +142 -0
  373. package/task-lib/completion.js +2 -3
  374. package/task-lib/completion.ts +9 -0
  375. package/task-lib/config.js +1 -4
  376. package/task-lib/config.ts +11 -0
  377. package/task-lib/name-generator.js +4 -226
  378. package/task-lib/name-generator.ts +11 -0
  379. package/task-lib/omp-session-cleanup.js +1 -1
  380. package/task-lib/omp-storage-root.js +8 -12
  381. package/task-lib/omp-storage-root.ts +62 -0
  382. package/task-lib/process-termination.js +143 -166
  383. package/task-lib/process-termination.ts +268 -0
@@ -0,0 +1,451 @@
1
+ import fs = require('fs');
2
+ import os = require('os');
3
+ import path = require('path');
4
+
5
+ interface LockOptions {
6
+ lockfilePath: string;
7
+ stale: number;
8
+ retries: {
9
+ retries: number;
10
+ minTimeout: number;
11
+ maxTimeout: number;
12
+ randomize: boolean;
13
+ };
14
+ }
15
+
16
+ type ReleaseLock = () => Promise<void>;
17
+
18
+ interface LockfileFacade {
19
+ lock(filePath: string, options: LockOptions): Promise<ReleaseLock>;
20
+ }
21
+
22
+ interface RunOptions extends Record<string, unknown> {
23
+ autoMerge?: unknown;
24
+ closeIssue?: unknown;
25
+ docker?: unknown;
26
+ mergeQueue?: unknown;
27
+ pr?: unknown;
28
+ prBase?: unknown;
29
+ prBody?: unknown;
30
+ ship?: unknown;
31
+ worktree?: unknown;
32
+ }
33
+
34
+ interface RunPlan {
35
+ isolation: 'none' | 'worktree' | 'docker';
36
+ delivery: 'none' | 'pr' | 'ship';
37
+ autoMerge: boolean;
38
+ }
39
+
40
+ interface RunPlanFacade {
41
+ resolveRunPlan(options?: RunOptions): Readonly<RunPlan>;
42
+ }
43
+
44
+ interface ProcessLivenessFacade {
45
+ isProcessRunning(pid: unknown): boolean;
46
+ }
47
+
48
+ interface IsolationManagerFacade {
49
+ getDetachedSetupResources(clusterId: string): unknown;
50
+ }
51
+
52
+ interface ClusterRecord extends Record<string, unknown> {
53
+ createdAt?: number;
54
+ id?: string;
55
+ pid?: number | null;
56
+ resumeDaemonPid?: number | null;
57
+ setupLogPath?: string | null;
58
+ state?: string;
59
+ }
60
+
61
+ type ClusterRegistry = Record<string, ClusterRecord>;
62
+ type ClusterUpdater = (clusters: ClusterRegistry) => ClusterRegistry | void;
63
+
64
+ interface DetachedSetupParams {
65
+ clusterId: string;
66
+ pid?: number;
67
+ storageDir?: string;
68
+ logPath?: string | null;
69
+ worktree?: unknown;
70
+ runOptions?: RunOptions;
71
+ cwd?: string;
72
+ }
73
+
74
+ interface ClusterIdentityParams {
75
+ clusterId: string;
76
+ storageDir?: string;
77
+ }
78
+
79
+ interface SetupFailureParams extends ClusterIdentityParams {
80
+ error: unknown;
81
+ logPath?: string | null;
82
+ }
83
+
84
+ interface ResumePatchParams extends ClusterIdentityParams {
85
+ daemonPid: number;
86
+ }
87
+
88
+ interface RegistrationWaitParams extends ClusterIdentityParams {
89
+ timeoutMs?: number;
90
+ pollMs?: number;
91
+ daemonPid?: number;
92
+ logPath?: string | null;
93
+ }
94
+
95
+ interface OwnershipWaitParams extends ResumePatchParams {
96
+ timeoutMs?: number;
97
+ pollMs?: number;
98
+ }
99
+
100
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
101
+ const lockfile: LockfileFacade = require('proper-lockfile');
102
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
103
+ const runPlan: RunPlanFacade = require('./run-plan');
104
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
105
+ const processLiveness: ProcessLivenessFacade = require('./process-liveness');
106
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
107
+ const IsolationManager: IsolationManagerFacade = require('../src/isolation-manager');
108
+
109
+ const { resolveRunPlan } = runPlan;
110
+ const { isProcessRunning } = processLiveness;
111
+
112
+ const DEFAULT_WAIT_TIMEOUT_SECONDS = 180;
113
+ const DEFAULT_WAIT_POLL_MS = 1000;
114
+ const CLUSTERS_LOCK_STALE_MS = 5000;
115
+ const DEFAULT_OWNERSHIP_TIMEOUT_MS = 10000;
116
+
117
+ function isClusterRegistry(value: unknown): value is ClusterRegistry {
118
+ return value !== null && typeof value === 'object';
119
+ }
120
+
121
+ function hasErrorCode(error: unknown, code: string): boolean {
122
+ return error !== null && typeof error === 'object' && 'code' in error && error.code === code;
123
+ }
124
+
125
+ function getStorageDir(storageDir?: string | null): string {
126
+ return storageDir || path.join(os.homedir(), '.zeroshot');
127
+ }
128
+
129
+ function getClustersFilePath(storageDir?: string | null): string {
130
+ return path.join(getStorageDir(storageDir), 'clusters.json');
131
+ }
132
+
133
+ function resolveWaitTimeoutMs(waitTimeoutSeconds: unknown): number {
134
+ const parsed = Number(waitTimeoutSeconds);
135
+ if (!Number.isFinite(parsed) || parsed <= 0) {
136
+ return DEFAULT_WAIT_TIMEOUT_SECONDS * 1000;
137
+ }
138
+ return Math.floor(parsed * 1000);
139
+ }
140
+
141
+ // Kept as an alias: this module used to carry its own copy of this check.
142
+ const isProcessAlive = isProcessRunning;
143
+
144
+ function isClusterRegistered(clusterId: string, storageDir?: string): boolean {
145
+ const clustersFile = getClustersFilePath(storageDir);
146
+ if (!fs.existsSync(clustersFile)) {
147
+ return false;
148
+ }
149
+
150
+ try {
151
+ const raw = fs.readFileSync(clustersFile, 'utf8');
152
+ const parsed: unknown = JSON.parse(raw);
153
+ return isClusterRegistry(parsed) && Boolean(parsed[clusterId]);
154
+ } catch {
155
+ return false;
156
+ }
157
+ }
158
+
159
+ function ensureStorageDir(storageDir?: string): string {
160
+ const resolvedStorageDir = getStorageDir(storageDir);
161
+ fs.mkdirSync(resolvedStorageDir, { recursive: true });
162
+ return resolvedStorageDir;
163
+ }
164
+
165
+ function ensureClustersFile(storageDir?: string): string {
166
+ const resolvedStorageDir = ensureStorageDir(storageDir);
167
+ const clustersFile = getClustersFilePath(resolvedStorageDir);
168
+ try {
169
+ fs.writeFileSync(clustersFile, '{}', { flag: 'wx', mode: 0o600 });
170
+ } catch (error: unknown) {
171
+ if (!hasErrorCode(error, 'EEXIST')) {
172
+ throw error;
173
+ }
174
+ }
175
+ return clustersFile;
176
+ }
177
+
178
+ function readClustersFile(clustersFile: string): ClusterRegistry {
179
+ try {
180
+ const parsed: unknown = JSON.parse(fs.readFileSync(clustersFile, 'utf8'));
181
+ return isClusterRegistry(parsed) ? parsed : {};
182
+ } catch {
183
+ return {};
184
+ }
185
+ }
186
+
187
+ async function updateClustersFile(
188
+ storageDir: string | undefined,
189
+ updater: ClusterUpdater
190
+ ): Promise<ClusterRegistry> {
191
+ const clustersFile = ensureClustersFile(storageDir);
192
+ const lockfilePath = path.join(path.dirname(clustersFile), 'clusters.json.lock');
193
+ let release: ReleaseLock | undefined;
194
+
195
+ try {
196
+ release = await lockfile.lock(clustersFile, {
197
+ lockfilePath,
198
+ stale: CLUSTERS_LOCK_STALE_MS,
199
+ retries: {
200
+ retries: 20,
201
+ minTimeout: 100,
202
+ maxTimeout: 250,
203
+ randomize: true,
204
+ },
205
+ });
206
+
207
+ const clusters = readClustersFile(clustersFile);
208
+ const updated = updater(clusters) || clusters;
209
+ fs.writeFileSync(clustersFile, JSON.stringify(updated, null, 2));
210
+ return updated;
211
+ } finally {
212
+ if (release) {
213
+ await release();
214
+ }
215
+ }
216
+ }
217
+
218
+ async function registerDetachedSetupCluster({
219
+ clusterId,
220
+ pid,
221
+ storageDir,
222
+ logPath,
223
+ worktree,
224
+ runOptions = {},
225
+ cwd,
226
+ }: DetachedSetupParams): Promise<void> {
227
+ const plan = resolveRunPlan(runOptions);
228
+ const setupResources =
229
+ plan.isolation === 'docker' ? IsolationManager.getDetachedSetupResources(clusterId) : null;
230
+ await updateClustersFile(storageDir, (clusters) => {
231
+ clusters[clusterId] = {
232
+ id: clusterId,
233
+ state: 'setup',
234
+ createdAt: Date.now(),
235
+ pid: typeof pid === 'number' && Number.isInteger(pid) ? pid : null,
236
+ setupLogPath: logPath || null,
237
+ setupStartedAt: Date.now(),
238
+ setupStage: 'starting',
239
+ autoPr: plan.delivery !== 'none',
240
+ prOptions: plan.delivery !== 'none'
241
+ ? {
242
+ prBase: runOptions.prBase,
243
+ prBody: typeof runOptions.prBody === 'string' ? runOptions.prBody : null,
244
+ mergeQueue: runOptions.mergeQueue || false,
245
+ closeIssue: runOptions.closeIssue || null,
246
+ autoMerge: plan.autoMerge,
247
+ cwd: cwd || null,
248
+ }
249
+ : null,
250
+ worktree: worktree || null,
251
+ config: null,
252
+ issue: null,
253
+ isolation: null,
254
+ agentStates: [],
255
+ failureInfo: null,
256
+ setupResources,
257
+ provisional: true,
258
+ };
259
+ return clusters;
260
+ });
261
+ }
262
+
263
+ async function removeDetachedSetupCluster({
264
+ clusterId,
265
+ storageDir,
266
+ }: ClusterIdentityParams): Promise<void> {
267
+ await updateClustersFile(storageDir, (clusters) => {
268
+ delete clusters[clusterId];
269
+ return clusters;
270
+ });
271
+ }
272
+
273
+ async function markDetachedSetupFailed({
274
+ clusterId,
275
+ storageDir,
276
+ error,
277
+ logPath,
278
+ }: SetupFailureParams): Promise<void> {
279
+ await updateClustersFile(storageDir, (clusters) => {
280
+ const existing = clusters[clusterId] || { id: clusterId, createdAt: Date.now() };
281
+ clusters[clusterId] = {
282
+ ...existing,
283
+ state: 'failed',
284
+ pid: null,
285
+ setupLogPath: existing.setupLogPath || logPath || null,
286
+ setupFinishedAt: Date.now(),
287
+ failureInfo: {
288
+ type: 'setup',
289
+ error: error instanceof Error ? error.message : String(error),
290
+ timestamp: Date.now(),
291
+ },
292
+ provisional: true,
293
+ };
294
+ return clusters;
295
+ });
296
+ }
297
+
298
+ function sleep(ms: number): Promise<void> {
299
+ return new Promise((resolve) => setTimeout(resolve, ms));
300
+ }
301
+
302
+ function formatLogHint(logPath?: string | null): string {
303
+ return logPath ? ` Check log: ${logPath}` : '';
304
+ }
305
+
306
+ async function waitForClusterRegistration({
307
+ clusterId,
308
+ timeoutMs,
309
+ pollMs = DEFAULT_WAIT_POLL_MS,
310
+ storageDir,
311
+ daemonPid,
312
+ logPath,
313
+ }: RegistrationWaitParams): Promise<{ ready: true; elapsedMs: number }> {
314
+ const effectiveTimeoutMs =
315
+ typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0
316
+ ? timeoutMs
317
+ : DEFAULT_WAIT_TIMEOUT_SECONDS * 1000;
318
+ const startedAt = Date.now();
319
+ const deadline = startedAt + effectiveTimeoutMs;
320
+ const logHint = formatLogHint(logPath);
321
+
322
+ while (Date.now() < deadline) {
323
+ if (isClusterRegistered(clusterId, storageDir)) {
324
+ return { ready: true, elapsedMs: Date.now() - startedAt };
325
+ }
326
+
327
+ if (daemonPid && !isProcessAlive(daemonPid)) {
328
+ throw new Error(
329
+ `Detached daemon exited before cluster "${clusterId}" registered in storage.${logHint}`
330
+ );
331
+ }
332
+
333
+ await sleep(pollMs);
334
+ }
335
+
336
+ const timeoutSeconds = Math.ceil(effectiveTimeoutMs / 1000);
337
+ throw new Error(
338
+ `Timed out after ${timeoutSeconds}s waiting for cluster "${clusterId}" to appear in status/list.${logHint}`
339
+ );
340
+ }
341
+
342
+ /**
343
+ * Atomically claim the right to resume-daemon an existing cluster record.
344
+ * Ownership for the handoff is tracked separately from cluster.pid/state so
345
+ * orchestrator.resume() can still perform its own eligibility check.
346
+ */
347
+ async function patchDetachedResumeCluster({
348
+ clusterId,
349
+ daemonPid,
350
+ storageDir,
351
+ }: ResumePatchParams): Promise<void> {
352
+ await updateClustersFile(storageDir, (clusters) => {
353
+ const existing = clusters[clusterId];
354
+ if (!existing) {
355
+ throw new Error(`Cannot start resume daemon: cluster "${clusterId}" not found in registry`);
356
+ }
357
+ if (existing.resumeDaemonPid && isProcessRunning(existing.resumeDaemonPid)) {
358
+ const ownership = `Cluster "${clusterId}" already has a live resume daemon`;
359
+ throw new Error(
360
+ `${ownership} (PID ${existing.resumeDaemonPid}); refusing to start a second one`
361
+ );
362
+ }
363
+ if (existing.state === 'running' && existing.pid && isProcessRunning(existing.pid)) {
364
+ throw new Error(
365
+ `Cluster "${clusterId}" is already running (PID ${existing.pid}); refusing to start a second resume daemon`
366
+ );
367
+ }
368
+ clusters[clusterId] = { ...existing, resumeDaemonPid: daemonPid };
369
+ return clusters;
370
+ });
371
+ }
372
+
373
+ /**
374
+ * Undo a resume handoff that did not complete so a dead daemon cannot leave
375
+ * the cluster stuck in a running state.
376
+ */
377
+ async function revertDetachedResumeCluster({
378
+ clusterId,
379
+ storageDir,
380
+ error,
381
+ }: SetupFailureParams): Promise<void> {
382
+ await updateClustersFile(storageDir, (clusters) => {
383
+ const existing = clusters[clusterId];
384
+ if (!existing) return clusters;
385
+ clusters[clusterId] = {
386
+ ...existing,
387
+ state: 'failed',
388
+ pid: null,
389
+ resumeDaemonPid: null,
390
+ failureInfo: {
391
+ type: 'resume-daemon',
392
+ error: error instanceof Error ? error.message : String(error),
393
+ timestamp: Date.now(),
394
+ },
395
+ };
396
+ return clusters;
397
+ });
398
+ }
399
+
400
+ function getRegisteredResumeDaemonPid(clusterId: string, storageDir?: string): number | null {
401
+ const clustersFile = getClustersFilePath(storageDir);
402
+ if (!fs.existsSync(clustersFile)) {
403
+ return null;
404
+ }
405
+ try {
406
+ const parsed: unknown = JSON.parse(fs.readFileSync(clustersFile, 'utf8'));
407
+ if (!isClusterRegistry(parsed)) {
408
+ return null;
409
+ }
410
+ return parsed[clusterId]?.resumeDaemonPid ?? null;
411
+ } catch {
412
+ return null;
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Daemon-side half of the resume handoff: wait until the registry shows this
418
+ * process's PID as resumeDaemonPid before touching the cluster.
419
+ */
420
+ async function waitForResumeOwnership({
421
+ clusterId,
422
+ daemonPid,
423
+ storageDir,
424
+ timeoutMs = DEFAULT_OWNERSHIP_TIMEOUT_MS,
425
+ pollMs = DEFAULT_WAIT_POLL_MS,
426
+ }: OwnershipWaitParams): Promise<boolean> {
427
+ const deadline = Date.now() + timeoutMs;
428
+ while (Date.now() < deadline) {
429
+ if (getRegisteredResumeDaemonPid(clusterId, storageDir) === daemonPid) {
430
+ return true;
431
+ }
432
+ await sleep(pollMs);
433
+ }
434
+ return getRegisteredResumeDaemonPid(clusterId, storageDir) === daemonPid;
435
+ }
436
+
437
+ export = {
438
+ DEFAULT_WAIT_TIMEOUT_SECONDS,
439
+ getClustersFilePath,
440
+ getRegisteredResumeDaemonPid,
441
+ isClusterRegistered,
442
+ isProcessAlive,
443
+ markDetachedSetupFailed,
444
+ patchDetachedResumeCluster,
445
+ registerDetachedSetupCluster,
446
+ removeDetachedSetupCluster,
447
+ resolveWaitTimeoutMs,
448
+ revertDetachedResumeCluster,
449
+ waitForClusterRegistration,
450
+ waitForResumeOwnership,
451
+ };