@wjarka/cezarion 0.15.1 → 0.16.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 (462) hide show
  1. package/README.md +167 -46
  2. package/dist/agent-config/catalog.d.ts +6 -2
  3. package/dist/agent-config/catalog.js +78 -3
  4. package/dist/agent-config/catalog.js.map +1 -1
  5. package/dist/agent-config/model-settings/shared.js +6 -3
  6. package/dist/agent-config/model-settings/shared.js.map +1 -1
  7. package/dist/agent-config/models.js +4 -0
  8. package/dist/agent-config/models.js.map +1 -1
  9. package/dist/agent-config/validate.js +6 -0
  10. package/dist/agent-config/validate.js.map +1 -1
  11. package/dist/artifacts/cli.js +10 -2
  12. package/dist/artifacts/cli.js.map +1 -1
  13. package/dist/automations/coordinator.d.ts +7 -0
  14. package/dist/automations/coordinator.js +22 -2
  15. package/dist/automations/coordinator.js.map +1 -1
  16. package/dist/automations/github-poller.d.ts +4 -4
  17. package/dist/automations/github-poller.js.map +1 -1
  18. package/dist/automations/schedule-runner.d.ts +130 -0
  19. package/dist/automations/schedule-runner.js +326 -0
  20. package/dist/automations/schedule-runner.js.map +1 -0
  21. package/dist/automations/scheduler.d.ts +38 -7
  22. package/dist/automations/scheduler.js +90 -22
  23. package/dist/automations/scheduler.js.map +1 -1
  24. package/dist/automations/store.d.ts +80 -3
  25. package/dist/automations/store.js +208 -16
  26. package/dist/automations/store.js.map +1 -1
  27. package/dist/automations/task-template.d.ts +42 -6
  28. package/dist/automations/task-template.js +152 -24
  29. package/dist/automations/task-template.js.map +1 -1
  30. package/dist/automations/types.d.ts +142 -6
  31. package/dist/automations/types.js +64 -6
  32. package/dist/automations/types.js.map +1 -1
  33. package/dist/autosave-holders.js +8 -12
  34. package/dist/autosave-holders.js.map +1 -1
  35. package/dist/ci-wait/client.d.ts +8 -1
  36. package/dist/ci-wait/client.js +43 -1
  37. package/dist/ci-wait/client.js.map +1 -1
  38. package/dist/ci-wait/controller.d.ts +83 -4
  39. package/dist/ci-wait/controller.js +76 -11
  40. package/dist/ci-wait/controller.js.map +1 -1
  41. package/dist/ci-wait/errors.d.ts +7 -1
  42. package/dist/ci-wait/errors.js +36 -0
  43. package/dist/ci-wait/errors.js.map +1 -1
  44. package/dist/ci-wait/github.d.ts +2 -0
  45. package/dist/ci-wait/github.js +3 -1
  46. package/dist/ci-wait/github.js.map +1 -1
  47. package/dist/ci-wait/mcp.d.ts +2 -98
  48. package/dist/ci-wait/mcp.js +11 -25
  49. package/dist/ci-wait/mcp.js.map +1 -1
  50. package/dist/ci-wait/supervisor.d.ts +2 -1
  51. package/dist/ci-wait/supervisor.js +2 -1
  52. package/dist/ci-wait/supervisor.js.map +1 -1
  53. package/dist/ci-wait/tools.d.ts +307 -0
  54. package/dist/ci-wait/tools.js +63 -0
  55. package/dist/ci-wait/tools.js.map +1 -0
  56. package/dist/config.d.ts +3 -0
  57. package/dist/config.js +7 -0
  58. package/dist/config.js.map +1 -1
  59. package/dist/contract/agent-config.d.ts +5 -0
  60. package/dist/contract/agent-profiles.d.ts +15 -0
  61. package/dist/contract/automation-schedule.d.ts +60 -0
  62. package/dist/contract/automations.d.ts +208 -36
  63. package/dist/contract/delegation.d.ts +96 -0
  64. package/dist/contract/discovery.d.ts +7 -0
  65. package/dist/contract/github.d.ts +1 -0
  66. package/dist/contract/health.d.ts +6 -0
  67. package/dist/contract/index.d.ts +3 -0
  68. package/dist/contract/index.js +1445 -745
  69. package/dist/contract/preview.d.ts +508 -0
  70. package/dist/contract/runs.d.ts +716 -15
  71. package/dist/contract/skills.d.ts +39 -0
  72. package/dist/contract/workflows.d.ts +6 -0
  73. package/dist/contract/workspace.d.ts +65 -0
  74. package/dist/contract/zoned-time.d.ts +35 -0
  75. package/dist/core/agent-env.d.ts +11 -0
  76. package/dist/core/agent-env.js +27 -5
  77. package/dist/core/agent-env.js.map +1 -1
  78. package/dist/core/agent-profiles.js +7 -0
  79. package/dist/core/agent-profiles.js.map +1 -1
  80. package/dist/core/agent-runner.d.ts +49 -1
  81. package/dist/core/agent-runner.js +2 -1
  82. package/dist/core/agent-runner.js.map +1 -1
  83. package/dist/core/backend-detect.d.ts +3 -2
  84. package/dist/core/backend-detect.js +27 -1
  85. package/dist/core/backend-detect.js.map +1 -1
  86. package/dist/core/claude-cli-runner.d.ts +1 -0
  87. package/dist/core/claude-cli-runner.js +69 -19
  88. package/dist/core/claude-cli-runner.js.map +1 -1
  89. package/dist/core/codex-app-server-runner.d.ts +1 -0
  90. package/dist/core/codex-app-server-runner.js +200 -25
  91. package/dist/core/codex-app-server-runner.js.map +1 -1
  92. package/dist/core/codex-app-server-transport.d.ts +7 -3
  93. package/dist/core/codex-app-server-transport.js +11 -9
  94. package/dist/core/codex-app-server-transport.js.map +1 -1
  95. package/dist/core/cursor-acp-runner.d.ts +1 -0
  96. package/dist/core/cursor-acp-runner.js +24 -9
  97. package/dist/core/cursor-acp-runner.js.map +1 -1
  98. package/dist/core/host-model-catalog.js +2 -0
  99. package/dist/core/host-model-catalog.js.map +1 -1
  100. package/dist/core/managed-session.js +2 -0
  101. package/dist/core/managed-session.js.map +1 -1
  102. package/dist/core/model-identity.js +2 -0
  103. package/dist/core/model-identity.js.map +1 -1
  104. package/dist/core/model-presets.js +3 -0
  105. package/dist/core/model-presets.js.map +1 -1
  106. package/dist/core/omp-model-catalog.d.ts +43 -0
  107. package/dist/core/omp-model-catalog.js +205 -0
  108. package/dist/core/omp-model-catalog.js.map +1 -0
  109. package/dist/core/omp-runner.d.ts +73 -0
  110. package/dist/core/omp-runner.js +990 -0
  111. package/dist/core/omp-runner.js.map +1 -0
  112. package/dist/core/omp-ui-mapper.d.ts +106 -0
  113. package/dist/core/omp-ui-mapper.js +882 -0
  114. package/dist/core/omp-ui-mapper.js.map +1 -0
  115. package/dist/core/opencode-server-runner.d.ts +18 -4
  116. package/dist/core/opencode-server-runner.js +337 -96
  117. package/dist/core/opencode-server-runner.js.map +1 -1
  118. package/dist/core/opencode-session-error.d.ts +11 -0
  119. package/dist/core/opencode-session-error.js +26 -0
  120. package/dist/core/opencode-session-error.js.map +1 -0
  121. package/dist/core/opencode-ui-mapper.js +4 -36
  122. package/dist/core/opencode-ui-mapper.js.map +1 -1
  123. package/dist/core/pi-runner.d.ts +1 -0
  124. package/dist/core/pi-runner.js +75 -12
  125. package/dist/core/pi-runner.js.map +1 -1
  126. package/dist/core/provider-auth.d.ts +9 -2
  127. package/dist/core/provider-auth.js +43 -6
  128. package/dist/core/provider-auth.js.map +1 -1
  129. package/dist/core/runner-factory.js +3 -0
  130. package/dist/core/runner-factory.js.map +1 -1
  131. package/dist/core/runner-model-catalog.js +1 -0
  132. package/dist/core/runner-model-catalog.js.map +1 -1
  133. package/dist/core/runner-stderr.d.ts +2 -0
  134. package/dist/core/runner-stderr.js +20 -0
  135. package/dist/core/runner-stderr.js.map +1 -0
  136. package/dist/core/session-process.d.ts +22 -0
  137. package/dist/core/session-process.js +202 -0
  138. package/dist/core/session-process.js.map +1 -0
  139. package/dist/core/ui-events.d.ts +1 -1
  140. package/dist/core/usage-limit.js +3 -60
  141. package/dist/core/usage-limit.js.map +1 -1
  142. package/dist/data-gitignore.d.ts +6 -0
  143. package/dist/data-gitignore.js +57 -0
  144. package/dist/data-gitignore.js.map +1 -0
  145. package/dist/delegation/capacity.d.ts +19 -0
  146. package/dist/delegation/capacity.js +34 -0
  147. package/dist/delegation/capacity.js.map +1 -0
  148. package/dist/delegation/cli.js +13 -4
  149. package/dist/delegation/cli.js.map +1 -1
  150. package/dist/delegation/conversations.d.ts +4 -2
  151. package/dist/delegation/conversations.js +8 -12
  152. package/dist/delegation/conversations.js.map +1 -1
  153. package/dist/delegation/credentials.d.ts +2 -0
  154. package/dist/delegation/credentials.js +10 -0
  155. package/dist/delegation/credentials.js.map +1 -1
  156. package/dist/delegation/destroy-observation.d.ts +42 -0
  157. package/dist/delegation/destroy-observation.js +115 -0
  158. package/dist/delegation/destroy-observation.js.map +1 -0
  159. package/dist/delegation/execution-identity.d.ts +6 -0
  160. package/dist/delegation/execution-identity.js +1 -1
  161. package/dist/delegation/execution-identity.js.map +1 -1
  162. package/dist/delegation/policy.d.ts +3 -2
  163. package/dist/delegation/policy.js +7 -5
  164. package/dist/delegation/policy.js.map +1 -1
  165. package/dist/delegation/process-liveness.d.ts +57 -20
  166. package/dist/delegation/process-liveness.js +114 -92
  167. package/dist/delegation/process-liveness.js.map +1 -1
  168. package/dist/delegation/provision.js +6 -6
  169. package/dist/delegation/provision.js.map +1 -1
  170. package/dist/delegation/results.js +4 -2
  171. package/dist/delegation/results.js.map +1 -1
  172. package/dist/delegation/retry-backoff.d.ts +23 -0
  173. package/dist/delegation/retry-backoff.js +18 -0
  174. package/dist/delegation/retry-backoff.js.map +1 -0
  175. package/dist/delegation/routes.d.ts +64 -58
  176. package/dist/delegation/scratch-cleanup.d.ts +29 -0
  177. package/dist/delegation/scratch-cleanup.js +143 -0
  178. package/dist/delegation/scratch-cleanup.js.map +1 -0
  179. package/dist/delegation/service.d.ts +20 -1
  180. package/dist/delegation/service.js +233 -70
  181. package/dist/delegation/service.js.map +1 -1
  182. package/dist/delegation/transport.d.ts +12 -6
  183. package/dist/delegation/workspace.d.ts +23 -4
  184. package/dist/delegation/workspace.js +147 -31
  185. package/dist/delegation/workspace.js.map +1 -1
  186. package/dist/git-worktree-lock-helper.js +8 -3
  187. package/dist/git-worktree-lock-helper.js.map +1 -1
  188. package/dist/git-worktree-lock.d.ts +6 -1
  189. package/dist/git-worktree-lock.js +10 -4
  190. package/dist/git-worktree-lock.js.map +1 -1
  191. package/dist/git-worktree-release.d.ts +20 -0
  192. package/dist/git-worktree-release.js +27 -0
  193. package/dist/git-worktree-release.js.map +1 -0
  194. package/dist/git-worktree.d.ts +8 -2
  195. package/dist/git-worktree.js +19 -2
  196. package/dist/git-worktree.js.map +1 -1
  197. package/dist/handoff.d.ts +2 -2
  198. package/dist/handoff.js +1 -1
  199. package/dist/index.js +109 -76
  200. package/dist/index.js.map +1 -1
  201. package/dist/paths.d.ts +1 -1
  202. package/dist/paths.js +5 -1
  203. package/dist/paths.js.map +1 -1
  204. package/dist/preview/cdp.d.ts +13 -0
  205. package/dist/preview/cdp.js +69 -0
  206. package/dist/preview/cdp.js.map +1 -0
  207. package/dist/preview/chromium.d.ts +75 -0
  208. package/dist/preview/chromium.js +394 -0
  209. package/dist/preview/chromium.js.map +1 -0
  210. package/dist/preview/dev-server.d.ts +68 -0
  211. package/dist/preview/dev-server.js +341 -0
  212. package/dist/preview/dev-server.js.map +1 -0
  213. package/dist/preview/host.d.ts +154 -0
  214. package/dist/preview/host.js +681 -0
  215. package/dist/preview/host.js.map +1 -0
  216. package/dist/preview/registration.d.ts +63 -0
  217. package/dist/preview/registration.js +109 -0
  218. package/dist/preview/registration.js.map +1 -0
  219. package/dist/preview/session.d.ts +73 -0
  220. package/dist/preview/session.js +347 -0
  221. package/dist/preview/session.js.map +1 -0
  222. package/dist/preview/url.d.ts +6 -0
  223. package/dist/preview/url.js +17 -0
  224. package/dist/preview/url.js.map +1 -0
  225. package/dist/runs/agent-tmpdir-removal.d.ts +15 -0
  226. package/dist/runs/agent-tmpdir-removal.js +60 -0
  227. package/dist/runs/agent-tmpdir-removal.js.map +1 -0
  228. package/dist/runs/agent-tmpdir.d.ts +12 -1
  229. package/dist/runs/agent-tmpdir.js +123 -34
  230. package/dist/runs/agent-tmpdir.js.map +1 -1
  231. package/dist/runs/delegation-state.d.ts +5 -1
  232. package/dist/runs/delegation-state.js +9 -0
  233. package/dist/runs/delegation-state.js.map +1 -1
  234. package/dist/runs/event-history.d.ts +4 -4
  235. package/dist/runs/event-history.js +97 -117
  236. package/dist/runs/event-history.js.map +1 -1
  237. package/dist/runs/history-compressor.d.ts +29 -0
  238. package/dist/runs/history-compressor.js +116 -0
  239. package/dist/runs/history-compressor.js.map +1 -0
  240. package/dist/runs/history-file.d.ts +36 -0
  241. package/dist/runs/history-file.js +463 -0
  242. package/dist/runs/history-file.js.map +1 -0
  243. package/dist/runs/human-ask-summary.js +18 -13
  244. package/dist/runs/human-ask-summary.js.map +1 -1
  245. package/dist/runs/legacy-index.d.ts +62 -0
  246. package/dist/runs/legacy-index.js +168 -0
  247. package/dist/runs/legacy-index.js.map +1 -0
  248. package/dist/runs/raw-record.d.ts +52 -0
  249. package/dist/runs/raw-record.js +145 -0
  250. package/dist/runs/raw-record.js.map +1 -0
  251. package/dist/runs/retention.d.ts +13 -6
  252. package/dist/runs/retention.js +42 -13
  253. package/dist/runs/retention.js.map +1 -1
  254. package/dist/runs/run-claims.d.ts +23 -0
  255. package/dist/runs/run-claims.js +24 -0
  256. package/dist/runs/run-claims.js.map +1 -0
  257. package/dist/runs/run-database.d.ts +385 -0
  258. package/dist/runs/run-database.js +930 -0
  259. package/dist/runs/run-database.js.map +1 -0
  260. package/dist/runs/run-index.d.ts +35 -9
  261. package/dist/runs/run-index.js +200 -25
  262. package/dist/runs/run-index.js.map +1 -1
  263. package/dist/runs/run-row.d.ts +44 -0
  264. package/dist/runs/run-row.js +84 -0
  265. package/dist/runs/run-row.js.map +1 -0
  266. package/dist/runs/run-search.d.ts +29 -0
  267. package/dist/runs/run-search.js +63 -0
  268. package/dist/runs/run-search.js.map +1 -0
  269. package/dist/runs/store-open-error.d.ts +24 -0
  270. package/dist/runs/store-open-error.js +61 -0
  271. package/dist/runs/store-open-error.js.map +1 -0
  272. package/dist/runs/store.d.ts +667 -32
  273. package/dist/runs/store.js +2153 -324
  274. package/dist/runs/store.js.map +1 -1
  275. package/dist/runs/transcript-facts.d.ts +77 -0
  276. package/dist/runs/transcript-facts.js +302 -0
  277. package/dist/runs/transcript-facts.js.map +1 -0
  278. package/dist/runs/webhook.js +5 -4
  279. package/dist/runs/webhook.js.map +1 -1
  280. package/dist/runs/worker-execution.d.ts +22 -0
  281. package/dist/runs/worker-execution.js +30 -0
  282. package/dist/runs/worker-execution.js.map +1 -0
  283. package/dist/server/capabilities.d.ts +1 -0
  284. package/dist/server/capabilities.js +2 -0
  285. package/dist/server/capabilities.js.map +1 -1
  286. package/dist/server/cockpit-ownership.d.ts +8 -0
  287. package/dist/server/cockpit-ownership.js +19 -0
  288. package/dist/server/cockpit-ownership.js.map +1 -1
  289. package/dist/server/forge/github.d.ts +1 -4
  290. package/dist/server/forge/github.js +47 -23
  291. package/dist/server/forge/github.js.map +1 -1
  292. package/dist/server/git.js +1 -1
  293. package/dist/server/git.js.map +1 -1
  294. package/dist/server/open-in-app.js +1 -0
  295. package/dist/server/open-in-app.js.map +1 -1
  296. package/dist/server/preview-socket.d.ts +36 -0
  297. package/dist/server/preview-socket.js +126 -0
  298. package/dist/server/preview-socket.js.map +1 -0
  299. package/dist/server/project-context.d.ts +31 -4
  300. package/dist/server/project-context.js +73 -9
  301. package/dist/server/project-context.js.map +1 -1
  302. package/dist/server/provider-action-gate.js +1 -0
  303. package/dist/server/provider-action-gate.js.map +1 -1
  304. package/dist/server/repo-branches.d.ts +4 -3
  305. package/dist/server/repo-branches.js +5 -4
  306. package/dist/server/repo-branches.js.map +1 -1
  307. package/dist/server/server.d.ts +4500 -2727
  308. package/dist/server/server.js +651 -202
  309. package/dist/server/server.js.map +1 -1
  310. package/dist/server/upgrade-router.d.ts +23 -0
  311. package/dist/server/upgrade-router.js +53 -0
  312. package/dist/server/upgrade-router.js.map +1 -0
  313. package/dist/server/validators.d.ts +6 -1
  314. package/dist/server/validators.js +4 -4
  315. package/dist/server/validators.js.map +1 -1
  316. package/dist/server/ws.d.ts +16 -16
  317. package/dist/server/ws.js +56 -54
  318. package/dist/server/ws.js.map +1 -1
  319. package/dist/skills-remote.d.ts +14 -0
  320. package/dist/skills-remote.js +91 -15
  321. package/dist/skills-remote.js.map +1 -1
  322. package/dist/task-cli/cli.js +40 -16
  323. package/dist/task-cli/cli.js.map +1 -1
  324. package/dist/task-cli/discovery.d.ts +4 -3
  325. package/dist/task-cli/discovery.js +4 -3
  326. package/dist/task-cli/discovery.js.map +1 -1
  327. package/dist/task-cli/http.d.ts +2 -0
  328. package/dist/task-cli/http.js +10 -4
  329. package/dist/task-cli/http.js.map +1 -1
  330. package/dist/task-cli/projections.d.ts +5 -2
  331. package/dist/task-cli/projections.js +13 -0
  332. package/dist/task-cli/projections.js.map +1 -1
  333. package/dist/task-cli/watch.d.ts +15 -2
  334. package/dist/task-cli/watch.js +31 -9
  335. package/dist/task-cli/watch.js.map +1 -1
  336. package/dist/ui-state.d.ts +4 -3
  337. package/dist/ui-state.js +8 -3
  338. package/dist/ui-state.js.map +1 -1
  339. package/dist/workflows/grouped-command.d.ts +31 -0
  340. package/dist/workflows/grouped-command.js +143 -0
  341. package/dist/workflows/grouped-command.js.map +1 -0
  342. package/dist/workflows/run.d.ts +223 -42
  343. package/dist/workflows/run.js +1316 -398
  344. package/dist/workflows/run.js.map +1 -1
  345. package/dist/workflows/types.d.ts +12 -0
  346. package/dist/workflows/types.js +20 -0
  347. package/dist/workflows/types.js.map +1 -1
  348. package/dist/workspace/agent-accounts.d.ts +5 -1
  349. package/dist/workspace/agent-accounts.js +1 -0
  350. package/dist/workspace/agent-accounts.js.map +1 -1
  351. package/dist/workspace/agent-profiles.js +1 -1
  352. package/dist/workspace/agent-profiles.js.map +1 -1
  353. package/dist/workspace/config.d.ts +3 -1
  354. package/dist/workspace/config.js +1 -0
  355. package/dist/workspace/config.js.map +1 -1
  356. package/dist/worktree-setup.d.ts +85 -0
  357. package/dist/worktree-setup.js +164 -0
  358. package/dist/worktree-setup.js.map +1 -0
  359. package/package.json +7 -5
  360. package/scripts/benchmark-run-store-fixture.ts +428 -0
  361. package/scripts/benchmark-run-store.ts +743 -0
  362. package/scripts/benchmark-stuck-destroys.ts +143 -0
  363. package/scripts/mock-autonomous.mjs +11 -0
  364. package/scripts/mock-ci-tool.mjs +20 -5
  365. package/scripts/mock-claude.mjs +110 -1
  366. package/scripts/mock-codex-app-server.mjs +117 -2
  367. package/scripts/mock-codex-coalesce-steer.mjs +59 -0
  368. package/scripts/mock-codex-reverse-steer.mjs +130 -0
  369. package/scripts/mock-cursor-acp.mjs +75 -1
  370. package/scripts/mock-omp-rpc.mjs +605 -0
  371. package/scripts/mock-opencode-serve.mjs +312 -22
  372. package/scripts/mock-pi-rpc.mjs +109 -3
  373. package/scripts/mock-runner-crash.mjs +42 -0
  374. package/scripts/mock-silent-tail.mjs +78 -0
  375. package/scripts/mock-turn-messages.mjs +5 -0
  376. package/scripts/omp-ci-wait.mjs +29 -0
  377. package/scripts/omp-restrict-delegation.yml +4 -0
  378. package/scripts/pi-ci-wait.mjs +12 -9
  379. package/scripts/seed-archive-profile.ts +3 -3
  380. package/web/dist/assets/agent-accounts-CFOBMVHc.js +1 -0
  381. package/web/dist/assets/alert-dialog-BrpNUwMt.js +1 -0
  382. package/web/dist/assets/app-window-CSHwDX9N.js +1 -0
  383. package/web/dist/assets/button-C_rd7CPp.js +43 -0
  384. package/web/dist/assets/compare-variants-D6WXhvXA.js +1 -0
  385. package/web/dist/assets/diff-DrEARXTV.js +3 -0
  386. package/web/dist/assets/diff-controls-B1-j7nFg.js +1 -0
  387. package/web/dist/assets/{diff-view-DPn6bQ5W.js → diff-view-DvGLjdTk.js} +3 -3
  388. package/web/dist/assets/dropdown-menu-BegOZFrj.js +1 -0
  389. package/web/dist/assets/format-DpNe3S0A.js +1 -0
  390. package/web/dist/assets/git-toolbar-Cw4ljmhB.js +1 -0
  391. package/web/dist/assets/github-CfV5sqL0.js +1 -0
  392. package/web/dist/assets/{image-preview-DJ8cgb1Z.js → image-preview-FOEfqpZP.js} +1 -1
  393. package/web/dist/assets/index-DgWWlOLy.js +13 -0
  394. package/web/dist/assets/index-fBFnuvxu.css +1 -0
  395. package/web/dist/assets/{nav-row-styles-3j64kH5s.js → nav-row-styles-vp2JbhuJ.js} +1 -1
  396. package/web/dist/assets/play-CmVAj_fu.js +1 -0
  397. package/web/dist/assets/popover-BohP6WTx.js +1 -0
  398. package/web/dist/assets/preview-pane-connected-qTA5KNoJ.js +3 -0
  399. package/web/dist/assets/preview-state-vuMviq6q.js +1 -0
  400. package/web/dist/assets/prompt-templates-Cubdut_C.js +15 -0
  401. package/web/dist/assets/repo-git-Cf4t5PD0.js +1 -0
  402. package/web/dist/assets/{run-diff-5-RRdcVz.js → run-diff-BpkUW37Z.js} +3 -3
  403. package/web/dist/assets/run-header-68WHf9wV.js +1 -0
  404. package/web/dist/assets/skills-DiVIgKeU.js +1 -0
  405. package/web/dist/assets/square-terminal-B6RYYieR.js +1 -0
  406. package/web/dist/assets/status-dot-CqTJHSP9.js +1 -0
  407. package/web/dist/assets/{tab-link-K0hjzEXR.js → tab-link-B07Hu4QX.js} +1 -1
  408. package/web/dist/assets/task-changes--lQF6EYf.js +1 -0
  409. package/web/dist/assets/task-commits-Cx0qy4DN.js +1 -0
  410. package/web/dist/assets/task-files-D50s-gKW.js +2 -0
  411. package/web/dist/assets/task-github-item-DaqmSwYT.js +1 -0
  412. package/web/dist/assets/task-thread-BPQ64lui.js +7 -0
  413. package/web/dist/assets/{task-thread-Bw_nQuTx.css → task-thread-zNE2Q9E-.css} +1 -1
  414. package/web/dist/assets/textarea-D20O4Pw0.js +1 -0
  415. package/web/dist/assets/thread-state-DWcFMrvb.js +4 -0
  416. package/web/dist/assets/use-hand-to-agent-state-BLIGmwbd.css +1 -0
  417. package/web/dist/assets/use-hand-to-agent-state-Cy9eyyfL.js +1 -0
  418. package/web/dist/assets/{utils-fzNfbPzA.js → utils-JsL3ZsJf.js} +1 -1
  419. package/web/dist/assets/workflows-jwG7uxqi.js +11 -0
  420. package/web/dist/assets/{zoomable-image-CDmZ_Rtl.js → zoomable-image-BJJ64KGc.js} +1 -1
  421. package/web/dist/cezarion-lockup-dark.svg +1 -13
  422. package/web/dist/cezarion-lockup-light.svg +1 -13
  423. package/web/dist/cezarion-mark-dark.svg +1 -12
  424. package/web/dist/cezarion-mark-light.svg +1 -12
  425. package/web/dist/index.html +16 -21
  426. package/web/dist/assets/agent-accounts-CU2ZDv24.js +0 -1
  427. package/web/dist/assets/alert-dialog-C-xYE_dg.js +0 -43
  428. package/web/dist/assets/chevron-right-BENZ-1ZN.js +0 -1
  429. package/web/dist/assets/collapsible-DD9J91Iv.js +0 -1
  430. package/web/dist/assets/compare-variants-5FmqpFSN.js +0 -1
  431. package/web/dist/assets/diff-controls-Bj0YrioE.js +0 -1
  432. package/web/dist/assets/diff-rE8rm75J2.js +0 -3
  433. package/web/dist/assets/diff-stat-BKDCSVfJ.js +0 -1
  434. package/web/dist/assets/dropdown-menu-Ch5BYQh_.js +0 -1
  435. package/web/dist/assets/format-dBiNDNVe.js +0 -1
  436. package/web/dist/assets/git-toolbar-ClVvf5iF.js +0 -1
  437. package/web/dist/assets/github-BkNOyour.css +0 -1
  438. package/web/dist/assets/github-DeZMeMwJ.js +0 -1
  439. package/web/dist/assets/github-item-detail-W5F6GVjF.js +0 -1
  440. package/web/dist/assets/github-sidebar-model-DcMu-IK2.js +0 -1
  441. package/web/dist/assets/highlighter-CdHqIwFr.js +0 -3
  442. package/web/dist/assets/index-CatemBEL.js +0 -8
  443. package/web/dist/assets/index-MvfD8bZJ.css +0 -1
  444. package/web/dist/assets/markdown-zdKeLIYA.js +0 -2
  445. package/web/dist/assets/mobile-run-bar-BCfmFi8V.js +0 -1
  446. package/web/dist/assets/prompt-templates-CBPp39IU.js +0 -15
  447. package/web/dist/assets/reference-chip-D6owncJT.js +0 -1
  448. package/web/dist/assets/repo-git-CtdkZFey.js +0 -1
  449. package/web/dist/assets/run-header-BcjoDi0W.js +0 -1
  450. package/web/dist/assets/sheet-ChnYVrp1.js +0 -1
  451. package/web/dist/assets/skills-C8YKp2vr.js +0 -1
  452. package/web/dist/assets/task-changes-BW8YdWG8.js +0 -1
  453. package/web/dist/assets/task-commits-q2kB2y6L.js +0 -1
  454. package/web/dist/assets/task-files-pPL6_3Cr.js +0 -2
  455. package/web/dist/assets/task-github-item-BXF5R52U.js +0 -1
  456. package/web/dist/assets/task-thread-DtkFL3EC.js +0 -10
  457. package/web/dist/assets/textarea-DxlkE-Yd.js +0 -1
  458. package/web/dist/assets/token-metrics-DIAjrPlM.js +0 -1
  459. package/web/dist/assets/use-desktop-CrDny2uG.js +0 -1
  460. package/web/dist/assets/use-submit-shortcut-KWJsC906.js +0 -1
  461. package/web/dist/assets/workflows-TmcPEj2K.js +0 -11
  462. /package/web/dist/assets/{lib-BQXq3kEf2.js → lib-BQXq3kEf.js} +0 -0
@@ -7,20 +7,30 @@ import { prepareWorkerContext, workerContextTask } from './context.js';
7
7
  import { acceptedWorkerIdentitySchema, workerContextHash, workerWorkflowHash } from './execution-identity.js';
8
8
  import { createHash, randomUUID } from 'node:crypto';
9
9
  import { discoveryRequestSchema, conversationSendRequestSchema, conversationInspectRequestSchema, conversationCancelRequestSchema, inboxReceiptRequestSchema, requestWaitRequestSchema, workerSpawnRequestSchema, workerSteerRequestSchema, workerWaitRequestSchema, workerParamsSchema, workerCancelWaitRequestSchema, } from '../contract/index.js';
10
- import { workerOutcome } from '../runs/delegation-state.js';
11
- import { QUICK_TASK_WORKFLOW, allowedToolsForStep, stepKind } from '../workflows/types.js';
10
+ import { workerDestroyView, workerOutcome } from '../runs/delegation-state.js';
11
+ import { QUICK_TASK_WORKFLOW, skillTaskSteps, plannedWorkflow, allowedToolsForStep, stepKind } from '../workflows/types.js';
12
+ import { discoverSkills } from '../skills.js';
12
13
  import { findWorkflow } from '../workflows/load.js';
13
14
  import { isAuthenticatedCaller } from './credentials.js';
14
15
  import { parseDelegationEffort } from './effort.js';
15
16
  import { authorizeSpawn, authorizeSpawnReplay, authorizeWorker, authorizeCancelWait, authorizeRetainedResult, DelegationPolicyError } from './policy.js';
16
- import { planOwnedWorkspace, readOwnedDiff, removeOwnedWorkspace, resolveWorkerBaseline } from './workspace.js';
17
+ import { releaseThenRemoveOwnedWorkspace } from '../git-worktree-release.js';
18
+ import { gitCommonDir, planOwnedWorkspace, readOwnedDiff, resolveWorkerBaseline, WorkspaceHeldError } from './workspace.js';
19
+ import { holdersStillHold, observeDestroy, recordHolders } from './destroy-observation.js';
20
+ import { DESTROY_ATTENTION_ATTEMPTS, DESTROY_BACKOFF, retryDelayMs } from './retry-backoff.js';
17
21
  /**
18
22
  * The catalog definition a spawn runs (#451): the built-in quick-task constant when the request
19
- * names none — never the catalog's `quick-task` entry, so a repo file cannot change the default
20
- * path — else the named entry. Every agent step resolves its own runner and account (#452), so a
23
+ * names neither workflow nor skill — never the catalog's `quick-task` entry, so a repo file cannot change the default
24
+ * path — else the named workflow or a one-step discovered skill task. Every agent step resolves its own runner and account (#452), so a
21
25
  * chain may mix providers; a chain with no agent step has nothing for a worker to run.
22
26
  */
23
27
  async function resolveSpawnWorkflow(root, request) {
28
+ if (request.skill !== undefined) {
29
+ const skills = await discoverSkills(root);
30
+ if (!skills.some(skill => skill.name === request.skill))
31
+ throw new DelegationPolicyError('invalid_input', `Unknown skill "${request.skill}"; available: ${skills.map(skill => skill.name).join(', ')}`);
32
+ return plannedWorkflow(skillTaskSteps(request.skill));
33
+ }
24
34
  if (request.workflow === undefined)
25
35
  return QUICK_TASK_WORKFLOW;
26
36
  const workflow = await findWorkflow(root, request.workflow);
@@ -119,7 +129,13 @@ export class DelegationService {
119
129
  serial = new Map();
120
130
  destroyRetryTimers = new Map();
121
131
  destroyRetryInFlight = new Map();
122
- destroyRetryDelayMs = 60_000;
132
+ /** What each pending destroy's last full attempt observed (hearsay-tools/cezarion#879). */
133
+ destroyObservations = new Map();
134
+ /** Each project's common Git dir, resolved once per registration for the change check. */
135
+ commonDirs = new Map();
136
+ // Private and overridable so tests need not wait out production cadence.
137
+ destroyBackoff = DESTROY_BACKOFF;
138
+ destroyAttentionAttempts = DESTROY_ATTENTION_ATTEMPTS;
123
139
  /** How long destroy waits for proven termination; private and overridable so tests need not wait it out. */
124
140
  terminationTimeoutMs = 30_000;
125
141
  registerProject(project) {
@@ -127,9 +143,11 @@ export class DelegationService {
127
143
  if (existing?.store === project.store && existing.manager === project.manager)
128
144
  return () => { };
129
145
  this.clearDestroyRetries(project.id);
146
+ existing?.manager.pauseWorkerCleanup();
130
147
  this.projects.set(project.id, project);
131
148
  return () => { if (this.projects.get(project.id) === project) {
132
149
  this.clearDestroyRetries(project.id);
150
+ project.manager.pauseWorkerCleanup();
133
151
  this.projects.delete(project.id);
134
152
  } };
135
153
  }
@@ -138,15 +156,22 @@ export class DelegationService {
138
156
  clearTimeout(timer);
139
157
  this.destroyRetryTimers.delete(projectId);
140
158
  this.destroyRetryInFlight.delete(projectId);
159
+ this.destroyObservations.delete(projectId);
160
+ this.commonDirs.delete(projectId);
141
161
  }
142
162
  /** Called only after manager recovery, when persisted destroy intents may be retried. */
143
163
  armDestroyRetries(projectId) {
144
164
  const project = this.projects.get(projectId);
145
165
  if (!project)
146
166
  return;
167
+ project.manager.recoverWorkerCleanup();
168
+ // A pending destroy keeps its worker in the live set (`isLiveRecord`), so this is every one.
147
169
  for (const run of project.store.listRuns())
148
170
  this.scheduleDestroyRetry(project, run.id);
149
171
  }
172
+ /** Arms the next automatic attempt of a pending destroy. A retry state written before a restart
173
+ * resumes where it was; the fast cadence is the floor, and a little over the cap the ceiling, so
174
+ * a clock that jumped cannot park a destroy for days (hearsay-tools/cezarion#879). */
150
175
  scheduleDestroyRetry(project, workerId) {
151
176
  if (this.projects.get(project.id) !== project)
152
177
  return;
@@ -160,6 +185,9 @@ export class DelegationService {
160
185
  }
161
186
  if (timers.has(workerId) || this.destroyRetryInFlight.get(project.id)?.has(workerId))
162
187
  return;
188
+ const { fastMs, capMs } = this.destroyBackoff;
189
+ const nextAt = run.destroyRetry ? Date.parse(run.destroyRetry.nextAt) - Date.now() : 0;
190
+ const delay = Math.min(Math.max(fastMs, nextAt), capMs * 1.1);
163
191
  const timer = setTimeout(() => {
164
192
  timers.delete(workerId);
165
193
  if (this.projects.get(project.id) !== project)
@@ -170,6 +198,19 @@ export class DelegationService {
170
198
  this.destroyRetryInFlight.set(project.id, inFlight);
171
199
  }
172
200
  inFlight.add(workerId);
201
+ void this.destroyTick(project, workerId).catch(() => undefined).finally(() => {
202
+ inFlight.delete(workerId);
203
+ this.scheduleDestroyRetry(project, workerId);
204
+ });
205
+ }, delay);
206
+ timer.unref?.();
207
+ timers.set(workerId, timer);
208
+ }
209
+ /** One automatic attempt. It runs in full only when something its last full attempt observed has
210
+ * changed, or a holder it named has exited; either way it records the attempt and when the next
211
+ * is due, and from the attention threshold on the cockpit says the cleanup needs attention. */
212
+ destroyTick(project, workerId) {
213
+ return this.serialized(`worker:${project.id}:${workerId}`, async () => {
173
214
  const check = () => {
174
215
  if (this.projects.get(project.id) !== project)
175
216
  throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
@@ -179,13 +220,48 @@ export class DelegationService {
179
220
  }
180
221
  return current;
181
222
  };
182
- void this.destroySerialized(project, workerId, check).catch(() => undefined).finally(() => {
183
- inFlight.delete(workerId);
184
- this.scheduleDestroyRetry(project, workerId);
185
- });
186
- }, this.destroyRetryDelayMs);
187
- timer.unref?.();
188
- timers.set(workerId, timer);
223
+ check();
224
+ const seen = this.destroyObservations.get(project.id)?.get(workerId);
225
+ const dataDir = join(project.root, '.ai/cezar');
226
+ const unchanged = !!seen && holdersStillHold({ store: project.store, dataDir, workerId, holders: seen.holders }) &&
227
+ seen.key === observeDestroy({ store: project.store, dataDir, commonDir: await this.commonDir(project), workerId });
228
+ // One automatic full attempt at a time per project: attempts that coincide (every pending destroy after a
229
+ // restart) would otherwise contend for the worktree mutation lock and come back lock-busy, never skipped.
230
+ if (!unchanged)
231
+ await this.serialized(`destroy-retries:${project.id}`, () => this.destroyAttempt(project, workerId, check, 'scheduled')).catch(() => undefined);
232
+ if (this.projects.get(project.id) !== project)
233
+ return;
234
+ const current = project.store.getRun(workerId);
235
+ if (current?.delegation?.role !== 'worker' || !current.delegation.destroy || current.delegation.destroy.phase === 'complete')
236
+ return;
237
+ const attempts = (current.destroyRetry?.attempts ?? 0) + 1;
238
+ const retry = { attempts, nextAt: new Date(Date.now() + retryDelayMs(attempts, this.destroyBackoff)).toISOString(),
239
+ ...(attempts >= this.destroyAttentionAttempts ? { needsAttention: true } : {}) };
240
+ project.store.commitDestroyRetry(workerId, retry);
241
+ });
242
+ }
243
+ commonDir(project) {
244
+ let dir = this.commonDirs.get(project.id);
245
+ if (!dir) {
246
+ // A failure is not cached: the next attempt asks again, and until then the check is record-only.
247
+ dir = gitCommonDir(project.root).catch(() => { this.commonDirs.delete(project.id); return undefined; });
248
+ this.commonDirs.set(project.id, dir);
249
+ }
250
+ return dir;
251
+ }
252
+ observeAttempt(project, workerId, seen) {
253
+ if (this.projects.get(project.id) !== project)
254
+ return;
255
+ let observations = this.destroyObservations.get(project.id);
256
+ if (!seen) {
257
+ observations?.delete(workerId);
258
+ return;
259
+ }
260
+ if (!observations) {
261
+ observations = new Map();
262
+ this.destroyObservations.set(project.id, observations);
263
+ }
264
+ observations.set(workerId, seen);
189
265
  }
190
266
  context(caller) {
191
267
  if (!delegationEnabled())
@@ -234,7 +310,7 @@ export class DelegationService {
234
310
  return { project, sender, recipient, root, worker };
235
311
  }
236
312
  currentConversation(project, root) {
237
- const state = reconcileConversationState(root, project.store.listRuns(), new Date().toISOString(), run => run.delegation?.role === 'worker'
313
+ const state = reconcileConversationState(root, (id) => project.store.getRun(id), new Date().toISOString(), run => run.delegation?.role === 'worker'
238
314
  ? project.store.readWorkerExecution(run.id)?.phase === 'complete' : !project.manager.isActive(run.id));
239
315
  if (root.delegation?.role === 'root' && state && state !== root.delegation.conversation)
240
316
  project.store.commitConversation(root.id, state);
@@ -482,6 +558,7 @@ export class DelegationService {
482
558
  ...(request.model === undefined ? {} : { model: request.model }),
483
559
  ...(effortPin === undefined ? {} : { effort: effortPin }),
484
560
  ...(request.workflow === undefined ? {} : { workflow: request.workflow }),
561
+ ...(request.skill === undefined ? {} : { skill: request.skill }),
485
562
  })).digest('hex');
486
563
  const receipt = parent.delegation.receipts.find(r => r.requestId === request.requestId);
487
564
  if (receipt) {
@@ -492,7 +569,7 @@ export class DelegationService {
492
569
  throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
493
570
  return { workerId: worker.id, baselineSha: worker.delegation.workspace.baselineSha };
494
571
  }
495
- authorizeSpawn(caller, parent, project.id);
572
+ authorizeSpawn(caller, parent, project.id, id => project.store.getRun(id));
496
573
  const catalog = await resolveSpawnWorkflow(project.root, request);
497
574
  const resolvedSteps = await resolveSpawnSteps(project.manager, parent.id, request, catalog, (step, settings) => this.validateModels(request, [{ step, settings }]));
498
575
  // Run-level fields hold the first agent step (cockpit columns, pre-#452 evidence readers);
@@ -514,7 +591,7 @@ export class DelegationService {
514
591
  const current = this.context(caller);
515
592
  if (current !== project)
516
593
  throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
517
- authorizeSpawn(caller, project.store.getRun(parent.id), project.id);
594
+ authorizeSpawn(caller, project.store.getRun(parent.id), project.id, id => project.store.getRun(id));
518
595
  // The public definition carries what each agent step RESOLVED to — runner, account,
519
596
  // model, effort and the narrowed grants — so the bound `workflowDef` and the private
520
597
  // per-step identity describe the same launch and the manager can hold them against each
@@ -537,8 +614,8 @@ export class DelegationService {
537
614
  steps: workflowDef.steps.map(step => ({ id: step.id, name: step.name ?? step.id, kind: stepKind(step) })),
538
615
  }, parent.id, request.requestId, { role: 'worker', permissions: [], parentRunId: parent.id, workspace, ...(context ? { context } : {}) }, requestHash, {
539
616
  ...identity, ...(context ? { contextHash: workerContextHash(context) } : {}),
540
- // A named catalog chain is bound like the context recipe; the default quick-task leaves the identity as before.
541
- ...(request.workflow === undefined ? {} : { workflowHash: workerWorkflowHash(workflowDef) }),
617
+ // A named catalog chain or skill is bound like the context recipe; the default quick-task leaves the identity as before.
618
+ ...(request.workflow === undefined && request.skill === undefined ? {} : { workflowHash: workerWorkflowHash(workflowDef) }),
542
619
  });
543
620
  accepted = true;
544
621
  project.manager.enqueueOwnedRun(worker.id);
@@ -562,7 +639,7 @@ export class DelegationService {
562
639
  ...(worker.delegation.context === undefined ? {} : { inputs: worker.delegation.context.inputs }),
563
640
  ...(worker.currentStepId === undefined ? {} : { currentStepId: worker.currentStepId }),
564
641
  ...(worker.activity === undefined ? {} : { activity: worker.activity }),
565
- ...(wait ? { wait } : {}), ...(worker.delegation.destroy ? { destroy: worker.delegation.destroy } : {}), ...(outcome ? { outcome } : {}) };
642
+ ...(wait ? { wait } : {}), ...(worker.delegation.destroy ? { destroy: workerDestroyView(worker) } : {}), ...(outcome ? { outcome } : {}) };
566
643
  }
567
644
  async collect(caller, params) {
568
645
  const project = this.context(caller);
@@ -655,36 +732,74 @@ export class DelegationService {
655
732
  check();
656
733
  return this.destroySerialized(project, workerId, check);
657
734
  }
735
+ /** An explicit destroy (Clean up, `cez worker destroy`): always a full attempt, and it restarts the
736
+ * backoff, so a timer armed for a later automatic attempt is dropped and re-armed at the fast cadence. */
658
737
  destroySerialized(project, workerId, check) {
659
- return this.serialized(`worker:${project.id}:${workerId}`, async () => {
660
- const assertAttached = () => {
661
- if (this.projects.get(project.id) !== project)
662
- throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
663
- };
738
+ return this.serialized(`worker:${project.id}:${workerId}`, () => this.destroyAttempt(project, workerId, check, 'manual')).finally(() => {
739
+ // A replaced registration owns its own timers under the same project id: leave them alone.
740
+ if (this.projects.get(project.id) !== project)
741
+ return;
742
+ const timers = this.destroyRetryTimers.get(project.id);
743
+ clearTimeout(timers?.get(workerId));
744
+ timers?.delete(workerId);
745
+ this.scheduleDestroyRetry(project, workerId);
746
+ });
747
+ }
748
+ /** One full destroy attempt; the caller holds the worker's serialization key. A manual attempt
749
+ * clears the run's `destroyRetry`, which resets the backoff; a completed destroy drops it. */
750
+ async destroyAttempt(project, workerId, check, mode) {
751
+ const assertAttached = () => {
752
+ if (this.projects.get(project.id) !== project)
753
+ throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
754
+ };
755
+ assertAttached();
756
+ let worker = check();
757
+ if (worker.delegation?.role !== 'worker')
758
+ throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
759
+ const parent = project.store.getRun(worker.delegation.parentRunId);
760
+ if (parent?.delegation?.role === 'root' && parent.delegation.receipts.some(receipt => receipt.workerId === workerId && receipt.deletion)) {
761
+ throw new DelegationPolicyError('incompatible_state', 'Worker history deletion has begun; retry history deletion');
762
+ }
763
+ const commonDir = await this.commonDir(project);
764
+ assertAttached();
765
+ worker = check();
766
+ if (worker.delegation?.role !== 'worker')
767
+ throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
768
+ // What this attempt decided on (hearsay-tools/cezarion#879): a later tick that would see the same skips its
769
+ // work. Retaken where the attempt decides (after termination, at each holder proof), so its own writes up
770
+ // to there (stopping the worker, the removal's checkpoint) are not a change, and anything later is.
771
+ const observe = () => observeDestroy({ store: project.store, dataDir: join(project.root, '.ai/cezar'), commonDir, workerId });
772
+ let key = observe();
773
+ const workspace = worker.delegation.workspace;
774
+ const requestedAt = worker.delegation.destroy?.requestedAt ?? new Date().toISOString();
775
+ const resources = (worker.delegation.destroy?.remaining ?? ['worktree', 'branch']).filter((resource) => resource !== 'process');
776
+ const persist = (phase, remaining, error) => {
664
777
  assertAttached();
665
- let worker = check();
778
+ worker = project.store.getRun(workerId);
666
779
  if (worker.delegation?.role !== 'worker')
667
780
  throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
668
- const parent = project.store.getRun(worker.delegation.parentRunId);
669
- if (parent?.delegation?.role === 'root' && parent.delegation.receipts.some(receipt => receipt.workerId === workerId && receipt.deletion)) {
670
- throw new DelegationPolicyError('incompatible_state', 'Worker history deletion has begun; retry history deletion');
671
- }
672
- const workspace = worker.delegation.workspace;
673
- const requestedAt = worker.delegation.destroy?.requestedAt ?? new Date().toISOString();
674
- const resources = (worker.delegation.destroy?.remaining ?? ['worktree', 'branch']).filter((resource) => resource !== 'process');
675
- const persist = (phase, remaining, error) => {
676
- assertAttached();
677
- worker = project.store.getRun(workerId);
678
- if (worker.delegation?.role !== 'worker')
679
- throw new DelegationPolicyError('denied_scope', 'Worker scope denied');
680
- project.store.commitDelegation([{ id: workerId, delegation: { ...worker.delegation, destroy: { requestedAt, phase, remaining, ...(error ? { error: error.slice(0, 2_000) } : {}) } } }]);
681
- };
682
- persist('requested', worker.delegation.destroy?.remaining ?? ['process', 'worktree', 'branch']);
781
+ project.store.commitDelegation([{ id: workerId, delegation: { ...worker.delegation, destroy: { requestedAt, phase, remaining,
782
+ ...(error ? { error: error.slice(0, 2_000) } : {}) } } }]);
783
+ // The retry state lives beside the delegation: an explicit destroy and a completed one both drop it.
784
+ if (worker.destroyRetry && (phase === 'complete' || (mode === 'manual' && phase === 'requested')))
785
+ project.store.commitDestroyRetry(workerId, undefined);
786
+ };
787
+ /** What the attempt decided on, and the holders it ran into, kept only when that explains the
788
+ * failure. A complete destroy keeps nothing, and neither does a failure no key input or named
789
+ * holder accounts for (lock contention, a failed Git step, an unnamed blocker): it can clear
790
+ * without changing anything a tick could see, so the next tick attempts in full. */
791
+ const observed = (result, holders, explained) => {
792
+ this.observeAttempt(project, workerId, result.state === 'complete' || !explained ? undefined : { key, holders: recordHolders(holders) });
793
+ return result;
794
+ };
795
+ persist('requested', worker.delegation.destroy?.remaining ?? ['process', 'worktree', 'branch']);
796
+ try {
683
797
  persist('terminating', ['process', ...resources]);
684
798
  project.manager.requestWorkerStop(workerId);
685
799
  let result;
686
800
  const terminated = await project.manager.awaitRunTermination(workerId, this.terminationTimeoutMs, { reapOrphans: true });
687
801
  assertAttached();
802
+ key = observe();
688
803
  if (!terminated) {
689
804
  // #469: name what blocks a crashed generation; other causes keep the generic message.
690
805
  const taken = project.manager.takeWorkerTerminationBlocker(workerId);
@@ -695,41 +810,89 @@ export class DelegationService {
695
810
  project.store.appendEvent(workerId, { type: 'lifecycle', message: `destroy blocked: ${reason}` });
696
811
  result = { workerId, state: 'incomplete', remaining: ['process', ...resources], error: !taken ? 'Worker termination is not proven; retry cleanup later'
697
812
  : taken.blocker.kind === 'unreadable' ? reason : `Worker termination is not proven: ${reason}; retry cleanup later` };
813
+ persist(result.state, result.remaining, result.error);
814
+ return observed(result, taken?.blocker.kind === 'processes' ? taken.blocker.pids : taken?.blocker.kind === 'controller' ? [taken.blocker.pid] : [], !!taken);
698
815
  }
699
- else {
700
- persist('cleaning', resources);
701
- const snapshot = structuredClone(check());
702
- const proof = project.store.readWorkerExecution(workerId);
703
- const evidence = await collectWorkerEvidence(project.root, project.store, snapshot);
816
+ persist('cleaning', resources);
817
+ const snapshot = structuredClone(check());
818
+ const proof = project.store.readWorkerExecution(workerId);
819
+ const evidence = await collectWorkerEvidence(project.root, project.store, snapshot);
820
+ assertAttached();
821
+ const current = check();
822
+ if (proof?.phase !== 'complete' || project.store.readWorkerExecution(workerId)?.generation !== proof.generation ||
823
+ project.store.readWorkerExecution(workerId)?.phase !== 'complete' || current.status !== snapshot.status ||
824
+ JSON.stringify(current.delegation) !== JSON.stringify(snapshot.delegation))
825
+ throw new Error('Worker changed before cleanup checkpoint');
826
+ // This immutable parent payload must be durable before the first destructive operation. A
827
+ // payload already on disk is durable, whatever cleanup phase it names (hearsay-tools/cezarion#879).
828
+ project.store.commitWorkerResult(evidence.result.parentRunId, evidence.result, evidence.diffSnapshot, { ignoreCleanup: true });
829
+ assertAttached();
830
+ check();
831
+ const release = project.manager.claimWorkerCleanup(workerId);
832
+ const assertCurrent = () => {
704
833
  assertAttached();
705
834
  const current = check();
706
- if (proof?.phase !== 'complete' || project.store.readWorkerExecution(workerId)?.generation !== proof.generation ||
707
- project.store.readWorkerExecution(workerId)?.phase !== 'complete' || current.status !== snapshot.status ||
708
- JSON.stringify(current.delegation) !== JSON.stringify(snapshot.delegation))
709
- throw new Error('Worker changed before cleanup checkpoint');
710
- // This immutable parent payload must be durable before the first destructive operation.
711
- project.store.commitWorkerResult(evidence.result.parentRunId, evidence.result, evidence.diffSnapshot);
712
- assertAttached();
713
- check();
714
- result = await removeOwnedWorkspace(project.root, workspace, project.manager.getWorkerNoMaterializationProof(workerId), assertAttached);
715
- // An already-started checked Git operation may finish after detach. Its
716
- // checkpoint makes a new controller's retry safe; the old store must not
717
- // publish a result after ownership of the project has moved.
718
- assertAttached();
719
- persist(result.state, result.remaining, result.error);
720
- // Preserve the captured bytes even if Git removal was only partially successful.
721
- const removed = !result.remaining.includes('worktree');
722
- project.store.commitWorkerResult(evidence.result.parentRunId, { ...evidence.result, observedAt: new Date().toISOString(),
723
- cleanup: result.state, outcome: result.state === 'complete' ? 'destroyed' : evidence.result.lastExecutionOutcome,
724
- workspace: { ...workspace, state: removed ? 'deleted' : evidence.result.workspace.state },
725
- head: removed ? { state: 'deleted', reason: 'missing', ...('sha' in evidence.result.head ? { sha: evidence.result.head.sha } : {}) } : evidence.result.head,
726
- diff: evidence.result.diff.state === 'available' ? { ...evidence.result.diff, snapshotId: randomUUID() } : evidence.result.diff,
727
- }, evidence.diffSnapshot);
728
- return result;
835
+ if (!release || current.delegation?.role !== 'worker' ||
836
+ JSON.stringify(current.delegation.workspace) !== JSON.stringify(workspace) ||
837
+ project.store.readWorkerExecution(workerId)?.generation !== proof.generation ||
838
+ project.store.readWorkerExecution(workerId)?.phase !== 'complete') {
839
+ throw new Error('Worker resource ownership changed');
840
+ }
841
+ };
842
+ const assertSafe = () => {
843
+ assertCurrent();
844
+ key = observe();
845
+ const holders = project.store.workerResourceHolders(workerId, proof.generation, workspace.resourceId);
846
+ if (holders === 'safe')
847
+ return;
848
+ throw holders.length ? new WorkspaceHeldError(holders) : new Error('Worker resources may still be held; cleanup will retry');
849
+ };
850
+ // The removal turns being held into an incomplete result; it reports the holders either of its
851
+ // checks named, or that it stopped for a reason no key input explains, an unnamed holder
852
+ // included (hearsay-tools/cezarion#879).
853
+ let holders = [];
854
+ let explained = true;
855
+ try {
856
+ assertCurrent();
857
+ // #781: release preview before the final fresh proof immediately preceding removal.
858
+ result = await releaseThenRemoveOwnedWorkspace({ previewHost: project.manager.previewHost }, project.root, workspace, assertCurrent, assertSafe, { held: pids => { holders = pids; }, unexplained: () => { explained = false; } });
729
859
  }
860
+ catch {
861
+ explained = false;
862
+ result = { workerId, state: 'incomplete', remaining: resources, error: 'Worker resources may still be held; cleanup will retry' };
863
+ }
864
+ finally {
865
+ release?.();
866
+ }
867
+ // An already-started checked Git operation may finish after detach. Its
868
+ // checkpoint makes a new controller's retry safe; the old store must not
869
+ // publish a result after ownership of the project has moved.
870
+ assertAttached();
730
871
  persist(result.state, result.remaining, result.error);
731
- return result;
732
- }).finally(() => this.scheduleDestroyRetry(project, workerId));
872
+ // Preserve the captured bytes even if Git removal was only partially successful.
873
+ const removed = !result.remaining.includes('worktree');
874
+ project.store.commitWorkerResult(evidence.result.parentRunId, { ...evidence.result, observedAt: new Date().toISOString(),
875
+ cleanup: result.state, outcome: result.state === 'complete' ? 'destroyed' : evidence.result.lastExecutionOutcome,
876
+ workspace: { ...workspace, state: removed ? 'deleted' : evidence.result.workspace.state },
877
+ head: removed ? { state: 'deleted', reason: 'missing', ...('sha' in evidence.result.head ? { sha: evidence.result.head.sha } : {}) } : evidence.result.head,
878
+ diff: evidence.result.diff.state === 'available' ? { ...evidence.result.diff, snapshotId: randomUUID() } : evidence.result.diff,
879
+ }, evidence.diffSnapshot);
880
+ return observed(result, holders, explained);
881
+ }
882
+ catch (error) {
883
+ // hearsay-tools/cezarion#879: an attempt that throws says why, instead of leaving an
884
+ // in-progress phase with no error, as long as this is still the same pending destroy. A
885
+ // throw explains nothing a tick could see clear, so the next tick attempts in full.
886
+ this.observeAttempt(project, workerId, undefined);
887
+ try {
888
+ const current = this.projects.get(project.id) === project ? project.store.getRun(workerId) : undefined;
889
+ if (current?.delegation?.role === 'worker' && current.delegation.destroy?.requestedAt === requestedAt && current.delegation.destroy.phase !== 'complete') {
890
+ persist('incomplete', current.delegation.destroy.remaining, error instanceof Error ? error.message : String(error));
891
+ }
892
+ }
893
+ catch { /* the attempt's own error is the one to report */ }
894
+ throw error;
895
+ }
733
896
  }
734
897
  }
735
898
  //# sourceMappingURL=service.js.map