@wjarka/cezarion 0.14.8 → 0.14.9-dev.1009

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 (280) hide show
  1. package/README.md +131 -2
  2. package/dist/application-update/bin-recovery.d.ts +5 -0
  3. package/dist/application-update/bin-recovery.js +96 -0
  4. package/dist/application-update/bin-recovery.js.map +1 -0
  5. package/dist/application-update/discovery.d.ts +22 -0
  6. package/dist/application-update/discovery.js +91 -0
  7. package/dist/application-update/discovery.js.map +1 -0
  8. package/dist/application-update/helper.d.ts +57 -0
  9. package/dist/application-update/helper.js +374 -0
  10. package/dist/application-update/helper.js.map +1 -0
  11. package/dist/application-update/launcher.d.ts +8 -0
  12. package/dist/application-update/launcher.js +53 -0
  13. package/dist/application-update/launcher.js.map +1 -0
  14. package/dist/application-update/lock.d.ts +4 -0
  15. package/dist/application-update/lock.js +70 -0
  16. package/dist/application-update/lock.js.map +1 -0
  17. package/dist/application-update/npm-process.d.ts +17 -0
  18. package/dist/application-update/npm-process.js +133 -0
  19. package/dist/application-update/npm-process.js.map +1 -0
  20. package/dist/application-update/service.d.ts +69 -0
  21. package/dist/application-update/service.js +456 -0
  22. package/dist/application-update/service.js.map +1 -0
  23. package/dist/artifacts/cli.d.ts +1 -0
  24. package/dist/artifacts/cli.js +32 -0
  25. package/dist/artifacts/cli.js.map +1 -0
  26. package/dist/artifacts/lifecycle.d.ts +4 -0
  27. package/dist/artifacts/lifecycle.js +42 -0
  28. package/dist/artifacts/lifecycle.js.map +1 -0
  29. package/dist/artifacts/resolve.d.ts +18 -0
  30. package/dist/artifacts/resolve.js +102 -0
  31. package/dist/artifacts/resolve.js.map +1 -0
  32. package/dist/artifacts/store.d.ts +8 -0
  33. package/dist/artifacts/store.js +323 -0
  34. package/dist/artifacts/store.js.map +1 -0
  35. package/dist/automations/scheduler.d.ts +6 -0
  36. package/dist/automations/scheduler.js +45 -17
  37. package/dist/automations/scheduler.js.map +1 -1
  38. package/dist/automations/store.d.ts +12 -4
  39. package/dist/automations/store.js +199 -22
  40. package/dist/automations/store.js.map +1 -1
  41. package/dist/automations/types.d.ts +2 -0
  42. package/dist/automations/types.js +1 -0
  43. package/dist/automations/types.js.map +1 -1
  44. package/dist/ci-wait/mcp.d.ts +3 -0
  45. package/dist/ci-wait/mcp.js +14 -3
  46. package/dist/ci-wait/mcp.js.map +1 -1
  47. package/dist/contract/application-update.d.ts +32 -0
  48. package/dist/contract/artifacts.d.ts +83 -0
  49. package/dist/contract/ask-schema.d.ts +32 -0
  50. package/dist/contract/ask.d.ts +2 -32
  51. package/dist/contract/automations.d.ts +6 -0
  52. package/dist/contract/conversations.d.ts +165 -0
  53. package/dist/contract/delegation.d.ts +68 -0
  54. package/dist/contract/discovery.d.ts +134 -0
  55. package/dist/contract/events.d.ts +49 -0
  56. package/dist/contract/health.d.ts +12 -0
  57. package/dist/contract/index.d.ts +3 -0
  58. package/dist/contract/index.js +1654 -1454
  59. package/dist/contract/projects.d.ts +40 -0
  60. package/dist/contract/runs.d.ts +186 -0
  61. package/dist/contract/skills.d.ts +33 -0
  62. package/dist/core/agent-runner.d.ts +33 -4
  63. package/dist/core/agent-runner.js +5 -0
  64. package/dist/core/agent-runner.js.map +1 -1
  65. package/dist/core/claude-cli-runner.d.ts +6 -12
  66. package/dist/core/claude-cli-runner.js +90 -20
  67. package/dist/core/claude-cli-runner.js.map +1 -1
  68. package/dist/core/codex-app-server-runner.d.ts +2 -1
  69. package/dist/core/codex-app-server-runner.js +327 -92
  70. package/dist/core/codex-app-server-runner.js.map +1 -1
  71. package/dist/core/codex-app-server-transport.d.ts +13 -1
  72. package/dist/core/codex-app-server-transport.js +58 -17
  73. package/dist/core/codex-app-server-transport.js.map +1 -1
  74. package/dist/core/cursor-acp-runner.d.ts +6 -1
  75. package/dist/core/cursor-acp-runner.js +162 -26
  76. package/dist/core/cursor-acp-runner.js.map +1 -1
  77. package/dist/core/cursor-provider-error.d.ts +3 -0
  78. package/dist/core/cursor-provider-error.js +16 -1
  79. package/dist/core/cursor-provider-error.js.map +1 -1
  80. package/dist/core/input-submissions.d.ts +14 -0
  81. package/dist/core/input-submissions.js +26 -0
  82. package/dist/core/input-submissions.js.map +1 -0
  83. package/dist/core/opencode-server-runner.d.ts +4 -1
  84. package/dist/core/opencode-server-runner.js +156 -26
  85. package/dist/core/opencode-server-runner.js.map +1 -1
  86. package/dist/core/pi-runner.d.ts +2 -1
  87. package/dist/core/pi-runner.js +88 -10
  88. package/dist/core/pi-runner.js.map +1 -1
  89. package/dist/core/provider-auth.d.ts +99 -1
  90. package/dist/core/provider-auth.js +203 -2
  91. package/dist/core/provider-auth.js.map +1 -1
  92. package/dist/delegation/cli.d.ts +1 -0
  93. package/dist/delegation/cli.js +103 -7
  94. package/dist/delegation/cli.js.map +1 -1
  95. package/dist/delegation/conversations.js +13 -5
  96. package/dist/delegation/conversations.js.map +1 -1
  97. package/dist/delegation/input.d.ts +2 -1
  98. package/dist/delegation/input.js +6 -1
  99. package/dist/delegation/input.js.map +1 -1
  100. package/dist/delegation/policy.d.ts +3 -2
  101. package/dist/delegation/policy.js +3 -1
  102. package/dist/delegation/policy.js.map +1 -1
  103. package/dist/delegation/process-liveness.d.ts +66 -0
  104. package/dist/delegation/process-liveness.js +215 -0
  105. package/dist/delegation/process-liveness.js.map +1 -0
  106. package/dist/delegation/provision.d.ts +1 -1
  107. package/dist/delegation/provision.js +10 -5
  108. package/dist/delegation/provision.js.map +1 -1
  109. package/dist/delegation/questions.d.ts +19 -0
  110. package/dist/delegation/questions.js +45 -0
  111. package/dist/delegation/questions.js.map +1 -0
  112. package/dist/delegation/results.d.ts +1 -1
  113. package/dist/delegation/results.js +33 -19
  114. package/dist/delegation/results.js.map +1 -1
  115. package/dist/delegation/routes.d.ts +185 -11
  116. package/dist/delegation/routes.js +6 -2
  117. package/dist/delegation/routes.js.map +1 -1
  118. package/dist/delegation/service.d.ts +23 -5
  119. package/dist/delegation/service.js +240 -17
  120. package/dist/delegation/service.js.map +1 -1
  121. package/dist/delegation/transport.d.ts +178 -15
  122. package/dist/delegation/workspace.d.ts +1 -1
  123. package/dist/delegation/workspace.js +13 -6
  124. package/dist/delegation/workspace.js.map +1 -1
  125. package/dist/discovery/catalog.d.ts +14 -0
  126. package/dist/discovery/catalog.js +31 -0
  127. package/dist/discovery/catalog.js.map +1 -0
  128. package/dist/discovery/cli.d.ts +8 -0
  129. package/dist/discovery/cli.js +104 -0
  130. package/dist/discovery/cli.js.map +1 -0
  131. package/dist/git-worktree.d.ts +3 -1
  132. package/dist/git-worktree.js +26 -4
  133. package/dist/git-worktree.js.map +1 -1
  134. package/dist/index.js +93 -26
  135. package/dist/index.js.map +1 -1
  136. package/dist/open-url.d.ts +2 -0
  137. package/dist/open-url.js +20 -0
  138. package/dist/open-url.js.map +1 -0
  139. package/dist/release/snapshot.d.ts +3 -3
  140. package/dist/release/snapshot.js +6 -6
  141. package/dist/release/snapshot.js.map +1 -1
  142. package/dist/runs/agent-tmpdir.d.ts +2 -0
  143. package/dist/runs/agent-tmpdir.js +5 -2
  144. package/dist/runs/agent-tmpdir.js.map +1 -1
  145. package/dist/runs/client-request.d.ts +11 -0
  146. package/dist/runs/client-request.js +26 -0
  147. package/dist/runs/client-request.js.map +1 -0
  148. package/dist/runs/delegation-state.d.ts +17 -0
  149. package/dist/runs/event-history.js +6 -0
  150. package/dist/runs/event-history.js.map +1 -1
  151. package/dist/runs/retention.d.ts +7 -2
  152. package/dist/runs/retention.js +39 -7
  153. package/dist/runs/retention.js.map +1 -1
  154. package/dist/runs/store.d.ts +100 -6
  155. package/dist/runs/store.js +245 -2
  156. package/dist/runs/store.js.map +1 -1
  157. package/dist/runs/webhook.d.ts +125 -0
  158. package/dist/runs/webhook.js +256 -0
  159. package/dist/runs/webhook.js.map +1 -0
  160. package/dist/server/project-context.d.ts +2 -0
  161. package/dist/server/project-context.js +1 -0
  162. package/dist/server/project-context.js.map +1 -1
  163. package/dist/server/provider-auth-runtime.d.ts +30 -4
  164. package/dist/server/provider-auth-runtime.js +71 -6
  165. package/dist/server/provider-auth-runtime.js.map +1 -1
  166. package/dist/server/server.d.ts +8226 -4860
  167. package/dist/server/server.js +311 -15
  168. package/dist/server/server.js.map +1 -1
  169. package/dist/server-install/platforms/ubuntu-vps.d.ts +5 -1
  170. package/dist/server-install/platforms/ubuntu-vps.js +160 -18
  171. package/dist/server-install/platforms/ubuntu-vps.js.map +1 -1
  172. package/dist/server-install/steps.d.ts +4 -2
  173. package/dist/server-install/steps.js +5 -4
  174. package/dist/server-install/steps.js.map +1 -1
  175. package/dist/skills-remote.d.ts +30 -5
  176. package/dist/skills-remote.js +122 -32
  177. package/dist/skills-remote.js.map +1 -1
  178. package/dist/skills.js +15 -6
  179. package/dist/skills.js.map +1 -1
  180. package/dist/task-cli/cli.d.ts +38 -0
  181. package/dist/task-cli/cli.js +580 -0
  182. package/dist/task-cli/cli.js.map +1 -0
  183. package/dist/task-cli/discovery.d.ts +22 -0
  184. package/dist/task-cli/discovery.js +96 -0
  185. package/dist/task-cli/discovery.js.map +1 -0
  186. package/dist/task-cli/http.d.ts +46 -0
  187. package/dist/task-cli/http.js +101 -0
  188. package/dist/task-cli/http.js.map +1 -0
  189. package/dist/task-cli/projections.d.ts +40 -0
  190. package/dist/task-cli/projections.js +45 -0
  191. package/dist/task-cli/projections.js.map +1 -0
  192. package/dist/task-cli/watch.d.ts +78 -0
  193. package/dist/task-cli/watch.js +336 -0
  194. package/dist/task-cli/watch.js.map +1 -0
  195. package/dist/workflows/run.d.ts +160 -2
  196. package/dist/workflows/run.js +1141 -108
  197. package/dist/workflows/run.js.map +1 -1
  198. package/dist/workspace/agent-profiles.d.ts +22 -0
  199. package/dist/workspace/agent-profiles.js +15 -0
  200. package/dist/workspace/agent-profiles.js.map +1 -1
  201. package/dist/workspace/config.d.ts +9 -0
  202. package/dist/workspace/config.js +13 -1
  203. package/dist/workspace/config.js.map +1 -1
  204. package/dist/workspace/projects-cli.js +1 -1
  205. package/dist/workspace/projects.d.ts +24 -1
  206. package/dist/workspace/projects.js +14 -1
  207. package/dist/workspace/projects.js.map +1 -1
  208. package/package.json +5 -5
  209. package/scripts/mock-claude.mjs +64 -2
  210. package/scripts/mock-codex-app-server.mjs +124 -1
  211. package/scripts/mock-cursor-acp.mjs +54 -3
  212. package/scripts/mock-cursor-hang-stdin-late.mjs +35 -0
  213. package/scripts/mock-cursor-hang-stdin.mjs +30 -0
  214. package/scripts/mock-opencode-serve.mjs +102 -2
  215. package/scripts/mock-pi-rpc.mjs +82 -3
  216. package/web/dist/assets/agent-accounts-CerclGiO.js +1 -0
  217. package/web/dist/assets/centered-state-DH8qtCz-.js +43 -0
  218. package/web/dist/assets/collapsible-De2G0xqQ.js +1 -0
  219. package/web/dist/assets/commit-list-s6JPY3qv.js +1 -0
  220. package/web/dist/assets/compare-variants-BQu_t-_g.js +1 -0
  221. package/web/dist/assets/{diff-tRE59UNb2.js → diff-8M5HlDxq2.js} +2 -2
  222. package/web/dist/assets/diff-controls-B6ok0TtQ.js +1 -0
  223. package/web/dist/assets/{diff-stat-Cgnl_hYF.js → diff-stat-D1AODN2a.js} +1 -1
  224. package/web/dist/assets/{diff-view-DN1ort7Q.js → diff-view-Bs5Gh7UF.js} +2 -2
  225. package/web/dist/assets/dropdown-menu-7KGQMoFj.js +1 -0
  226. package/web/dist/assets/git-toolbar-DqbapMXr.js +1 -0
  227. package/web/dist/assets/github-DCkk6sBm.js +1 -0
  228. package/web/dist/assets/{image-preview-C2JGcBlC.js → image-preview-jzHoEyLM.js} +1 -1
  229. package/web/dist/assets/index-Bnva-ttR.js +7 -0
  230. package/web/dist/assets/index-D5NkwqY7.css +1 -0
  231. package/web/dist/assets/markdown-DvbymqXz.js +2 -0
  232. package/web/dist/assets/pill-BL6ugwXo.js +1 -0
  233. package/web/dist/assets/project-router-ChC5Rb-Y.js +1 -0
  234. package/web/dist/assets/prompt-templates-j8qlK7c-.js +15 -0
  235. package/web/dist/assets/repo-git-_h1NjOVt.js +1 -0
  236. package/web/dist/assets/{run-diff-CyG0Nciv.js → run-diff-BuATgZpx.js} +2 -2
  237. package/web/dist/assets/run-header-DuAWswYM.js +1 -0
  238. package/web/dist/assets/skills-C-fq5BrZ.js +1 -0
  239. package/web/dist/assets/{tab-link-BWNp3mLY.js → tab-link-BmGKj9Rt.js} +1 -1
  240. package/web/dist/assets/task-changes-CHa6JWOS.js +1 -0
  241. package/web/dist/assets/task-commits-BqCqyWm1.js +1 -0
  242. package/web/dist/assets/task-files-BSA0l-YP.js +2 -0
  243. package/web/dist/assets/task-thread-Bw_nQuTx.css +1 -0
  244. package/web/dist/assets/task-thread-Q7Nj33DH.js +9 -0
  245. package/web/dist/assets/textarea-ByZ-txS7.js +1 -0
  246. package/web/dist/assets/thread-loading-DHT9p5o5.js +1 -0
  247. package/web/dist/assets/token-metrics-k4XoovDS.js +1 -0
  248. package/web/dist/assets/utils-BTFBhZ55.js +64 -0
  249. package/web/dist/assets/workflows-HJOd3lVi.js +11 -0
  250. package/web/dist/assets/{zoomable-image-Z3JDnm0b.js → zoomable-image-Ba6y6Wyl.js} +1 -1
  251. package/web/dist/index.html +14 -14
  252. package/web/dist/assets/agent-accounts-BZm_2WAv.js +0 -1
  253. package/web/dist/assets/alert-dialog-B422RI-k.js +0 -43
  254. package/web/dist/assets/collapsible-Cy_Fmkrd.js +0 -1
  255. package/web/dist/assets/commit-list-Dcnk7TnI.js +0 -1
  256. package/web/dist/assets/compare-variants-Ciqwvd_f.js +0 -1
  257. package/web/dist/assets/diff-controls-BGrXGi7O.js +0 -1
  258. package/web/dist/assets/dropdown-menu-dwDGT8MM.js +0 -1
  259. package/web/dist/assets/git-toolbar-UiHnGvE0.js +0 -1
  260. package/web/dist/assets/github-D-MrkbJ7.js +0 -1
  261. package/web/dist/assets/ibm-plex-mono-regular-BF3vfe7_.ttf +0 -0
  262. package/web/dist/assets/index-BszfYvBq.js +0 -7
  263. package/web/dist/assets/index-CWh4pKP3.css +0 -1
  264. package/web/dist/assets/markdown-C_IcKVQd.js +0 -2
  265. package/web/dist/assets/pill-BsbO6G7y.js +0 -1
  266. package/web/dist/assets/project-router-DbLkll25.js +0 -1
  267. package/web/dist/assets/prompt-templates-BlTGJB5W.js +0 -15
  268. package/web/dist/assets/repo-git-Bm3s6Why.js +0 -1
  269. package/web/dist/assets/run-header-IFyIGCey.js +0 -1
  270. package/web/dist/assets/skills-C-C4DVFY.js +0 -1
  271. package/web/dist/assets/task-changes-D2PHtWUW.js +0 -1
  272. package/web/dist/assets/task-commits-N5u9U0_R.js +0 -1
  273. package/web/dist/assets/task-files-BS7c7CFm.js +0 -2
  274. package/web/dist/assets/task-thread-BQFdz1dJ.css +0 -1
  275. package/web/dist/assets/task-thread-DGM2dZV-.js +0 -9
  276. package/web/dist/assets/textarea-FjaU370i.js +0 -1
  277. package/web/dist/assets/thread-loading-77sJ8Yu_.js +0 -1
  278. package/web/dist/assets/token-metrics-Clxh03bE.js +0 -1
  279. package/web/dist/assets/utils-PYYcpCFZ.js +0 -64
  280. package/web/dist/assets/workflows-Czl0ZfXH.js +0 -11
@@ -1,7 +1,8 @@
1
1
  import { EventEmitter } from 'node:events';
2
2
  import { z } from 'zod';
3
- import type { CiWait, ConversationState, AgentInput, DelegationState, WorkerCollectedResult } from '../contract/index.js';
3
+ import type { CiWait, ConversationState, AgentInput, InboxClaim, DelegationState, WorkerCollectedResult } from '../contract/index.js';
4
4
  import { type WorkerExecutionIdentity } from '../delegation/execution-identity.ts';
5
+ import { type WorkerProcessRecord } from '../delegation/process-liveness.ts';
5
6
  import { workflowDefSchema } from '../workflows/types.ts';
6
7
  import type { RunnerId } from '../core/agent-runner.ts';
7
8
  export type RunStatus = 'queued' | 'running' | 'waiting' | 'review' | 'done' | 'failed' | 'cancelled';
@@ -125,6 +126,18 @@ export declare const runRecordSchema: z.ZodObject<{
125
126
  }>;
126
127
  resumed: z.ZodOptional<z.ZodLiteral<true>>;
127
128
  instruction: z.ZodOptional<z.ZodString>;
129
+ question: z.ZodOptional<z.ZodObject<{
130
+ questions: z.ZodArray<z.ZodObject<{
131
+ id: z.ZodOptional<z.ZodString>;
132
+ header: z.ZodString;
133
+ question: z.ZodString;
134
+ options: z.ZodArray<z.ZodObject<{
135
+ label: z.ZodString;
136
+ description: z.ZodOptional<z.ZodString>;
137
+ }, z.core.$strict>>;
138
+ multiSelect: z.ZodOptional<z.ZodBoolean>;
139
+ }, z.core.$strict>>;
140
+ }, z.core.$strict>>;
128
141
  }, z.core.$strict>>;
129
142
  outcomes: z.ZodArray<z.ZodObject<{
130
143
  requestId: z.ZodUUID;
@@ -133,6 +146,7 @@ export declare const runRecordSchema: z.ZodObject<{
133
146
  "completed-without-reply": "completed-without-reply";
134
147
  destroyed: "destroyed";
135
148
  failed: "failed";
149
+ "human-fallback": "human-fallback";
136
150
  replied: "replied";
137
151
  "sender-closed": "sender-closed";
138
152
  "timed-out": "timed-out";
@@ -175,6 +189,7 @@ export declare const runRecordSchema: z.ZodObject<{
175
189
  "completed-without-reply": "completed-without-reply";
176
190
  destroyed: "destroyed";
177
191
  failed: "failed";
192
+ "human-fallback": "human-fallback";
178
193
  replied: "replied";
179
194
  "sender-closed": "sender-closed";
180
195
  "timed-out": "timed-out";
@@ -228,6 +243,7 @@ export declare const runRecordSchema: z.ZodObject<{
228
243
  "completed-without-reply": "completed-without-reply";
229
244
  destroyed: "destroyed";
230
245
  failed: "failed";
246
+ "human-fallback": "human-fallback";
231
247
  replied: "replied";
232
248
  "sender-closed": "sender-closed";
233
249
  "timed-out": "timed-out";
@@ -316,6 +332,7 @@ export declare const runRecordSchema: z.ZodObject<{
316
332
  "completed-without-reply": "completed-without-reply";
317
333
  destroyed: "destroyed";
318
334
  failed: "failed";
335
+ "human-fallback": "human-fallback";
319
336
  replied: "replied";
320
337
  "sender-closed": "sender-closed";
321
338
  "timed-out": "timed-out";
@@ -369,6 +386,7 @@ export declare const runRecordSchema: z.ZodObject<{
369
386
  "completed-without-reply": "completed-without-reply";
370
387
  destroyed: "destroyed";
371
388
  failed: "failed";
389
+ "human-fallback": "human-fallback";
372
390
  replied: "replied";
373
391
  "sender-closed": "sender-closed";
374
392
  "timed-out": "timed-out";
@@ -486,6 +504,15 @@ export declare const runRecordSchema: z.ZodObject<{
486
504
  text: z.ZodString;
487
505
  createdAt: z.ZodISODateTime;
488
506
  deliveredAt: z.ZodOptional<z.ZodISODateTime>;
507
+ consumedAt: z.ZodOptional<z.ZodISODateTime>;
508
+ awaitingRead: z.ZodOptional<z.ZodLiteral<true>>;
509
+ inboxClaim: z.ZodOptional<z.ZodObject<{
510
+ receiptId: z.ZodUUID;
511
+ generation: z.ZodUUID;
512
+ expiresAt: z.ZodISODateTime;
513
+ acknowledgedAt: z.ZodOptional<z.ZodISODateTime>;
514
+ memberIds: z.ZodOptional<z.ZodArray<z.ZodUUID>>;
515
+ }, z.core.$strict>>;
489
516
  }, z.core.$strict>>>;
490
517
  ciWait: z.ZodOptional<z.ZodObject<{
491
518
  prUrl: z.ZodString;
@@ -592,6 +619,11 @@ export declare const runRecordSchema: z.ZodObject<{
592
619
  systemPrompt: z.ZodOptional<z.ZodString>;
593
620
  generateFollowups: z.ZodOptional<z.ZodBoolean>;
594
621
  autonomous: z.ZodOptional<z.ZodBoolean>;
622
+ notify: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
623
+ webhook: z.ZodCatch<z.ZodOptional<z.ZodObject<{
624
+ lastDeliveredAt: z.ZodOptional<z.ZodString>;
625
+ lastError: z.ZodOptional<z.ZodString>;
626
+ }, z.core.$strip>>>;
595
627
  automation: z.ZodOptional<z.ZodObject<{
596
628
  automationId: z.ZodString;
597
629
  automationRevision: z.ZodNumber;
@@ -648,6 +680,8 @@ export declare const runRecordSchema: z.ZodObject<{
648
680
  worktreeReclaimedAt: z.ZodOptional<z.ZodString>;
649
681
  groupId: z.ZodOptional<z.ZodString>;
650
682
  variant: z.ZodOptional<z.ZodString>;
683
+ clientRequestId: z.ZodOptional<z.ZodString>;
684
+ clientRequestHash: z.ZodOptional<z.ZodString>;
651
685
  peakRssBytes: z.ZodOptional<z.ZodNumber>;
652
686
  peakProcCount: z.ZodOptional<z.ZodNumber>;
653
687
  archived: z.ZodDefault<z.ZodBoolean>;
@@ -777,10 +811,22 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
777
811
  state: "accepted" | "continuation-required" | "destroyed" | "late";
778
812
  resumed?: true | undefined;
779
813
  instruction?: string | undefined;
814
+ question?: {
815
+ questions: {
816
+ id?: string | undefined;
817
+ header: string;
818
+ question: string;
819
+ options: {
820
+ label: string;
821
+ description?: string | undefined;
822
+ }[];
823
+ multiSelect?: boolean | undefined;
824
+ }[];
825
+ } | undefined;
780
826
  }[];
781
827
  outcomes: {
782
828
  requestId: string;
783
- status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "replied" | "sender-closed" | "timed-out";
829
+ status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "human-fallback" | "replied" | "sender-closed" | "timed-out";
784
830
  observedAt: string;
785
831
  replyId?: string | undefined;
786
832
  }[];
@@ -803,7 +849,7 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
803
849
  requestIds?: string[] | undefined;
804
850
  requestOutcomes?: {
805
851
  requestId: string;
806
- status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "replied" | "sender-closed" | "timed-out";
852
+ status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "human-fallback" | "replied" | "sender-closed" | "timed-out";
807
853
  observedAt: string;
808
854
  replyId?: string | undefined;
809
855
  }[] | undefined;
@@ -830,7 +876,7 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
830
876
  requestIds?: string[] | undefined;
831
877
  requestOutcomes?: {
832
878
  requestId: string;
833
- status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "replied" | "sender-closed" | "timed-out";
879
+ status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "human-fallback" | "replied" | "sender-closed" | "timed-out";
834
880
  observedAt: string;
835
881
  replyId?: string | undefined;
836
882
  }[] | undefined;
@@ -877,7 +923,7 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
877
923
  requestIds?: string[] | undefined;
878
924
  requestOutcomes?: {
879
925
  requestId: string;
880
- status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "replied" | "sender-closed" | "timed-out";
926
+ status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "human-fallback" | "replied" | "sender-closed" | "timed-out";
881
927
  observedAt: string;
882
928
  replyId?: string | undefined;
883
929
  }[] | undefined;
@@ -904,7 +950,7 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
904
950
  requestIds?: string[] | undefined;
905
951
  requestOutcomes?: {
906
952
  requestId: string;
907
- status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "replied" | "sender-closed" | "timed-out";
953
+ status: "cancelled" | "completed-without-reply" | "destroyed" | "failed" | "human-fallback" | "replied" | "sender-closed" | "timed-out";
908
954
  observedAt: string;
909
955
  replyId?: string | undefined;
910
956
  }[] | undefined;
@@ -974,6 +1020,15 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
974
1020
  text: string;
975
1021
  createdAt: string;
976
1022
  deliveredAt?: string | undefined;
1023
+ consumedAt?: string | undefined;
1024
+ awaitingRead?: true | undefined;
1025
+ inboxClaim?: {
1026
+ receiptId: string;
1027
+ generation: string;
1028
+ expiresAt: string;
1029
+ acknowledgedAt?: string | undefined;
1030
+ memberIds?: string[] | undefined;
1031
+ } | undefined;
977
1032
  }[] | undefined;
978
1033
  ciWait?: {
979
1034
  prUrl: string;
@@ -1043,6 +1098,11 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
1043
1098
  systemPrompt?: string | undefined;
1044
1099
  generateFollowups?: boolean | undefined;
1045
1100
  autonomous?: boolean | undefined;
1101
+ notify?: boolean | undefined;
1102
+ webhook?: {
1103
+ lastDeliveredAt?: string | undefined;
1104
+ lastError?: string | undefined;
1105
+ } | undefined;
1046
1106
  automation?: {
1047
1107
  automationId: string;
1048
1108
  automationRevision: number;
@@ -1085,6 +1145,8 @@ export declare function parseRunRecords(raw: unknown): z.ZodSafeParseResult<{
1085
1145
  worktreeReclaimedAt?: string | undefined;
1086
1146
  groupId?: string | undefined;
1087
1147
  variant?: string | undefined;
1148
+ clientRequestId?: string | undefined;
1149
+ clientRequestHash?: string | undefined;
1088
1150
  peakRssBytes?: number | undefined;
1089
1151
  peakProcCount?: number | undefined;
1090
1152
  archived: boolean;
@@ -1220,6 +1282,9 @@ export declare class RunStore extends EventEmitter {
1220
1282
  setRepoHandle(handle: RepoHandle | null): void;
1221
1283
  listRuns(): RunRecord[];
1222
1284
  getRun(id: string): RunRecord | undefined;
1285
+ /** The run an idempotent start already created (#504), archived or not. A deleted run is gone
1286
+ * with its record, so a retry after deletion honestly creates a fresh one. */
1287
+ findRunByClientRequestId(clientRequestId: string): RunRecord | undefined;
1223
1288
  createRun(input: {
1224
1289
  title: string;
1225
1290
  systemPrompt?: string;
@@ -1234,9 +1299,13 @@ export declare class RunStore extends EventEmitter {
1234
1299
  agentProfile?: string;
1235
1300
  generateFollowups?: boolean;
1236
1301
  autonomous?: boolean;
1302
+ /** Task webhook opt-in (#589), set at creation so the first transition is already covered. */
1303
+ notify?: boolean;
1237
1304
  worktree?: false;
1238
1305
  groupId?: string;
1239
1306
  variant?: string;
1307
+ clientRequestId?: string;
1308
+ clientRequestHash?: string;
1240
1309
  steps: Array<Pick<StepState, 'id' | 'name' | 'kind'>>;
1241
1310
  }): RunRecord;
1242
1311
  /** Build without publishing: owned creation must commit its receipt and run together. */
@@ -1247,7 +1316,23 @@ export declare class RunStore extends EventEmitter {
1247
1316
  input: AgentInput;
1248
1317
  }): void;
1249
1318
  /** Observable atomic input checkpoint: a failed write publishes nothing. */
1319
+ /** Accepted input the harness will never read returns to the queue (#505): only its
1320
+ * delivery and consumption receipts are cleared; order and identity are kept. */
1321
+ requeueUnconsumedAgentInputs(id: string, ids: readonly string[]): void;
1322
+ /** Model consumption observed by the current session (#505). Never un-sets deliveredAt. */
1323
+ commitAgentInputsConsumed(id: string, ids: readonly string[], at: string): void;
1324
+ /** Delivered, but the harness never confirmed reading it (#505): stop awaiting a read. */
1325
+ commitAgentInputsUnconfirmed(id: string, ids: readonly string[]): void;
1326
+ /** Crash recovery (#505): input a harness accepted but was never seen reading goes
1327
+ * back to the queue. Returns the requeued IDs. */
1328
+ requeueAwaitingReadInputs(id: string): string[];
1250
1329
  commitAgentInputs(id: string, inputs: readonly AgentInput[], openingContinuationInputId?: string): void;
1330
+ /** Reserve only unread conversation inputs in one durable index replacement. */
1331
+ claimInboxInputs(runId: string, ids: readonly string[], claim: InboxClaim): void;
1332
+ /** A receipt owns every matching input or none; exact ACK retries preserve the first timestamp. */
1333
+ ackInboxInputs(runId: string, receiptId: string, generation: string, at: string): 'acknowledged' | 'already-acknowledged';
1334
+ releaseInboxInputs(runId: string, receiptId: string, generation: string): 'released';
1335
+ clearExpiredInboxClaims(runId: string, now: string): void;
1251
1336
  /** CI intent and its wake entry share one atomic index replacement. */
1252
1337
  commitCiWait(id: string, wait: CiWait, input?: AgentInput): void;
1253
1338
  /** Retire the wait and precisely its pending wake, optionally accepting human input. */
@@ -1458,6 +1543,15 @@ export declare class RunStore extends EventEmitter {
1458
1543
  } | undefined;
1459
1544
  private writeWorkerExecution;
1460
1545
  commitWorkerExecutionStart(id: string): string;
1546
+ /** Private process record (#469): which processes a generation spawned, never part of run JSON or SSE. */
1547
+ private processesPath;
1548
+ /** Tri-state: only ENOENT is `absent` (legacy, scan-only). A present record that is unreadable,
1549
+ * wrongly permissioned, malformed or of another generation is `unknown`: never finalized or reaped. */
1550
+ readWorkerProcesses(id: string, generation: string): WorkerProcessRecord | 'absent' | 'unknown';
1551
+ private writeWorkerProcesses;
1552
+ /** Bound to the current generation. Past the cap the record never drops an entry: the
1553
+ * working-directory scan is the remaining evidence. */
1554
+ appendWorkerProcess(id: string, generation: string, pid: number): boolean;
1461
1555
  commitWorkerExecutionComplete(id: string, generation: string): boolean;
1462
1556
  /** Cancellation must hit disk before queued no-start evidence authorizes cleanup. */
1463
1557
  commitWorkerCancellation(id: string): void;
@@ -1,13 +1,16 @@
1
1
  import { EventEmitter } from 'node:events';
2
+ import { removeArtifacts } from '../artifacts/lifecycle.js';
2
3
  import { randomUUID } from 'node:crypto';
4
+ import { isDeepStrictEqual } from 'node:util';
3
5
  import { appendFileSync, closeSync, constants, fstatSync, fsyncSync, lstatSync, openSync, readSync, realpathSync, readdirSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
4
6
  import { join, resolve } from 'node:path';
5
7
  import { z } from 'zod';
6
- import { ciWaitSchema, agentInputSchema, delegationStateSchema, workerCreationReceiptSchema, workerCollectedResultSchema, workerResultFileSchema, continuationMessageSchema, runRecordSchema as contractRunRecordSchema, } from '../contract/index.js';
8
+ import { ciWaitSchema, agentInputSchema, inboxClaimSchema, delegationStateSchema, workerCreationReceiptSchema, workerCollectedResultSchema, workerResultFileSchema, continuationMessageSchema, runRecordSchema as contractRunRecordSchema, } from '../contract/index.js';
7
9
  import { storedDelegationStateSchema } from './delegation-state.js';
8
10
  import { refreshHumanAskSummary } from './human-ask-summary.js';
9
11
  import { workerExecutionIdentitySchema } from '../delegation/execution-identity.js';
10
12
  import { reconcileWorkerWait } from '../delegation/wait.js';
13
+ import { processStartToken } from '../delegation/process-liveness.js';
11
14
  import { collectSecretValues, redactDeep, redactSecrets } from '../core/secret-redaction.js';
12
15
  // Pure, dependency-free reference helpers — the same sanity bound the marker parser applies.
13
16
  import { MAX_REF } from './task-refs.js';
@@ -167,6 +170,10 @@ export const runRecordSchema = z.object({
167
170
  * group-pick winner-park read it). Additive-safe: absent = falsy = not
168
171
  * autonomous. Set at creation from `WorkflowInput.autonomous`. */
169
172
  autonomous: z.boolean().optional(),
173
+ /** Task webhook opt-in (#589). Absent = off; `POST /runs/:id/notify` flips it at any time. */
174
+ notify: z.boolean().optional().catch(undefined),
175
+ /** The last task-webhook delivery's outcome (#589), written by `runs/webhook.ts`. */
176
+ webhook: contractRunRecordSchema.shape.webhook.catch(undefined),
170
177
  /** Optional provenance for tasks launched by a project GitHub automation. */
171
178
  automation: z
172
179
  .object({
@@ -262,6 +269,10 @@ export const runRecordSchema = z.object({
262
269
  groupId: z.string().optional(),
263
270
  /** Variant letter within the group — 'A' | 'B' | 'C' (kept as a string). */
264
271
  variant: z.string().optional(),
272
+ /** Idempotent start (#504): the caller's request id and the hash of the start payload it
273
+ * named (`src/runs/client-request.ts`). Absent on every run started without one. */
274
+ clientRequestId: z.string().optional(),
275
+ clientRequestHash: z.string().optional(),
265
276
  /** Peak resident memory (bytes) / process count observed across the run's
266
277
  * agent process trees (#348) — written when a session's telemetry ends.
267
278
  * Optional: old runs.json files and `ps`-less platforms have neither. */
@@ -695,6 +706,11 @@ export function rescopeRun(run, handle) {
695
706
  }
696
707
  return changed;
697
708
  }
709
+ const WORKER_PROCESS_CAP = 32;
710
+ const recordedProcessSchema = z.object({ pid: z.number().int().positive(), startToken: z.string().min(1).max(128).optional() }).strict();
711
+ const workerProcessRecordSchema = z.object({ generation: z.string().uuid(), controller: recordedProcessSchema,
712
+ processes: z.array(recordedProcessSchema).max(WORKER_PROCESS_CAP) }).strict();
713
+ const startToken = (pid) => { const token = processStartToken(pid); return token === undefined ? {} : { startToken: token }; };
698
714
  /**
699
715
  * File-backed run store: `runs.json` index (atomic tmp+rename writes, the
700
716
  * pattern from @cezar/core's IssueStore) plus one append-only NDJSON event
@@ -776,6 +792,14 @@ export class RunStore extends EventEmitter {
776
792
  getRun(id) {
777
793
  return this.runs.get(id);
778
794
  }
795
+ /** The run an idempotent start already created (#504), archived or not. A deleted run is gone
796
+ * with its record, so a retry after deletion honestly creates a fresh one. */
797
+ findRunByClientRequestId(clientRequestId) {
798
+ for (const run of this.runs.values())
799
+ if (run.clientRequestId === clientRequestId)
800
+ return run;
801
+ return undefined;
802
+ }
779
803
  createRun(input) {
780
804
  const run = this.buildRun(input, randomUUID());
781
805
  this.runs.set(run.id, run);
@@ -804,9 +828,11 @@ export class RunStore extends EventEmitter {
804
828
  agentProfile: input.agentProfile,
805
829
  generateFollowups: input.generateFollowups,
806
830
  autonomous: input.autonomous,
831
+ ...(input.notify ? { notify: true } : {}),
807
832
  worktree: input.worktree,
808
833
  groupId: input.groupId,
809
834
  variant: input.variant,
835
+ ...(input.clientRequestId === undefined ? {} : { clientRequestId: input.clientRequestId, clientRequestHash: input.clientRequestHash }),
810
836
  status: 'queued',
811
837
  createdAt: new Date().toISOString(),
812
838
  tokensUsed: 0,
@@ -846,11 +872,65 @@ export class RunStore extends EventEmitter {
846
872
  this.commitIndex(proposed, changed);
847
873
  }
848
874
  /** Observable atomic input checkpoint: a failed write publishes nothing. */
875
+ /** Accepted input the harness will never read returns to the queue (#505): only its
876
+ * delivery and consumption receipts are cleared; order and identity are kept. */
877
+ requeueUnconsumedAgentInputs(id, ids) {
878
+ const run = this.runs.get(id);
879
+ if (!run?.agentInputs || !ids.length)
880
+ return;
881
+ this.commitAgentInputs(id, run.agentInputs.map(input => {
882
+ if (!ids.includes(input.id))
883
+ return input;
884
+ const { deliveredAt: _delivered, consumedAt: _consumed, awaitingRead: _awaiting, ...queued } = input;
885
+ return queued;
886
+ }));
887
+ }
888
+ /** Model consumption observed by the current session (#505). Never un-sets deliveredAt. */
889
+ commitAgentInputsConsumed(id, ids, at) {
890
+ const run = this.runs.get(id);
891
+ if (!run?.agentInputs || !ids.length)
892
+ return;
893
+ this.commitAgentInputs(id, run.agentInputs.map(input => {
894
+ if (!ids.includes(input.id) || !input.deliveredAt || input.consumedAt)
895
+ return input;
896
+ const { awaitingRead: _awaiting, ...read } = input;
897
+ return { ...read, consumedAt: at };
898
+ }));
899
+ }
900
+ /** Delivered, but the harness never confirmed reading it (#505): stop awaiting a read. */
901
+ commitAgentInputsUnconfirmed(id, ids) {
902
+ const run = this.runs.get(id);
903
+ if (!run?.agentInputs || !ids.length)
904
+ return;
905
+ this.commitAgentInputs(id, run.agentInputs.map(input => {
906
+ if (!ids.includes(input.id) || !input.awaitingRead)
907
+ return input;
908
+ const { awaitingRead: _awaiting, ...unconfirmed } = input;
909
+ return unconfirmed;
910
+ }));
911
+ }
912
+ /** Crash recovery (#505): input a harness accepted but was never seen reading goes
913
+ * back to the queue. Returns the requeued IDs. */
914
+ requeueAwaitingReadInputs(id) {
915
+ const ids = (this.runs.get(id)?.agentInputs ?? []).filter(input => input.awaitingRead && input.deliveredAt && !input.consumedAt).map(input => input.id);
916
+ this.requeueUnconsumedAgentInputs(id, ids);
917
+ return ids;
918
+ }
849
919
  commitAgentInputs(id, inputs, openingContinuationInputId) {
850
920
  const run = this.runs.get(id);
851
921
  if (!run)
852
922
  throw new Error('missing agent input target');
853
923
  const agentInputs = inputs.map(input => agentInputSchema.parse(input));
924
+ const now = new Date().toISOString();
925
+ for (const input of run.agentInputs ?? []) {
926
+ const claim = input.inboxClaim;
927
+ if (!claim || claim.acknowledgedAt || claim.expiresAt <= now)
928
+ continue;
929
+ const matching = agentInputs.filter(next => next.id === input.id);
930
+ if (matching.length !== 1 || !isDeepStrictEqual(matching[0].inboxClaim, claim)) {
931
+ throw new Error('live inbox receipt changed');
932
+ }
933
+ }
854
934
  if (openingContinuationInputId && (run.continuationMessage?.agentInputId !== openingContinuationInputId ||
855
935
  !agentInputs.some(input => input.id === openingContinuationInputId && input.deliveredAt)))
856
936
  throw new Error('opening agent input checkpoint changed');
@@ -858,6 +938,96 @@ export class RunStore extends EventEmitter {
858
938
  proposed.set(id, { ...run, agentInputs, ...(openingContinuationInputId ? { continuationMessage: undefined } : {}) });
859
939
  this.commitIndex(proposed, new Set([id]));
860
940
  }
941
+ /** Reserve only unread conversation inputs in one durable index replacement. */
942
+ claimInboxInputs(runId, ids, claim) {
943
+ const run = this.runs.get(runId);
944
+ if (!run)
945
+ throw new Error('missing inbox recipient');
946
+ const selected = z.array(z.uuid()).min(1).max(32).refine(values => new Set(values).size === values.length).parse(ids);
947
+ const receipt = inboxClaimSchema.parse({ ...claim, memberIds: selected });
948
+ if (receipt.acknowledgedAt || receipt.expiresAt <= new Date().toISOString())
949
+ throw new Error('inbox claim is not live');
950
+ const inputs = run.agentInputs ?? [];
951
+ if (inputs.some(input => input.inboxClaim?.receiptId === receipt.receiptId))
952
+ throw new Error('inbox receipt already exists');
953
+ for (const id of selected) {
954
+ const matching = inputs.filter(input => input.id === id);
955
+ if (matching.length !== 1 || matching[0]?.source !== 'agent' || !matching[0].conversation ||
956
+ matching[0].deliveredAt || (matching[0].inboxClaim && matching[0].inboxClaim.expiresAt > new Date().toISOString())) {
957
+ throw new Error('inbox input is not available');
958
+ }
959
+ }
960
+ const selectedIds = new Set(selected);
961
+ const proposed = new Map(this.runs);
962
+ proposed.set(runId, { ...run, agentInputs: inputs.map(input => selectedIds.has(input.id)
963
+ ? agentInputSchema.parse({ ...input, inboxClaim: receipt }) : input) });
964
+ this.commitIndex(proposed, new Set([runId]));
965
+ }
966
+ /** A receipt owns every matching input or none; exact ACK retries preserve the first timestamp. */
967
+ ackInboxInputs(runId, receiptId, generation, at) {
968
+ const run = this.runs.get(runId);
969
+ if (!run)
970
+ throw new Error('missing inbox recipient');
971
+ inboxClaimSchema.shape.receiptId.parse(receiptId);
972
+ inboxClaimSchema.shape.generation.parse(generation);
973
+ inboxClaimSchema.shape.expiresAt.parse(at);
974
+ const matching = (run.agentInputs ?? []).filter(input => input.inboxClaim?.receiptId === receiptId);
975
+ const receipt = matching[0]?.inboxClaim;
976
+ const memberIds = receipt?.memberIds;
977
+ if (!memberIds || matching.length !== memberIds.length ||
978
+ !memberIds.every(id => matching.some(input => input.id === id)) ||
979
+ matching.some(input => !isDeepStrictEqual(input.inboxClaim, receipt)) ||
980
+ matching.some(input => input.inboxClaim?.generation !== generation ||
981
+ input.source !== 'agent' || !input.conversation))
982
+ throw new Error('inbox receipt changed');
983
+ if (matching.every(input => input.inboxClaim?.acknowledgedAt && input.deliveredAt === input.inboxClaim.acknowledgedAt)) {
984
+ return 'already-acknowledged';
985
+ }
986
+ if (matching.some(input => input.deliveredAt || input.inboxClaim?.acknowledgedAt || input.inboxClaim.expiresAt <= at)) {
987
+ throw new Error('inbox receipt expired or displaced');
988
+ }
989
+ const proposed = new Map(this.runs);
990
+ proposed.set(runId, { ...run, agentInputs: run.agentInputs.map(input => input.inboxClaim?.receiptId === receiptId
991
+ ? agentInputSchema.parse({ ...input, deliveredAt: at, inboxClaim: { ...input.inboxClaim, acknowledgedAt: at } }) : input) });
992
+ this.commitIndex(proposed, new Set([runId]));
993
+ return 'acknowledged';
994
+ }
995
+ releaseInboxInputs(runId, receiptId, generation) {
996
+ const run = this.runs.get(runId);
997
+ if (!run)
998
+ throw new Error('missing inbox recipient');
999
+ inboxClaimSchema.shape.receiptId.parse(receiptId);
1000
+ inboxClaimSchema.shape.generation.parse(generation);
1001
+ const matching = (run.agentInputs ?? []).filter(input => input.inboxClaim?.receiptId === receiptId);
1002
+ if (!matching.length || matching.some(input => input.inboxClaim?.generation !== generation ||
1003
+ input.inboxClaim?.acknowledgedAt || input.deliveredAt))
1004
+ throw new Error('inbox receipt changed');
1005
+ const proposed = new Map(this.runs);
1006
+ proposed.set(runId, { ...run, agentInputs: run.agentInputs.map(input => {
1007
+ if (input.inboxClaim?.receiptId !== receiptId)
1008
+ return input;
1009
+ const { inboxClaim: _claim, ...unclaimed } = input;
1010
+ return agentInputSchema.parse(unclaimed);
1011
+ }) });
1012
+ this.commitIndex(proposed, new Set([runId]));
1013
+ return 'released';
1014
+ }
1015
+ clearExpiredInboxClaims(runId, now) {
1016
+ const run = this.runs.get(runId);
1017
+ if (!run)
1018
+ throw new Error('missing inbox recipient');
1019
+ inboxClaimSchema.shape.expiresAt.parse(now);
1020
+ if (!run.agentInputs?.some(input => input.inboxClaim && !input.inboxClaim.acknowledgedAt && input.inboxClaim.expiresAt <= now))
1021
+ return;
1022
+ const proposed = new Map(this.runs);
1023
+ proposed.set(runId, { ...run, agentInputs: run.agentInputs.map(input => {
1024
+ if (!input.inboxClaim || input.inboxClaim.acknowledgedAt || input.inboxClaim.expiresAt > now)
1025
+ return input;
1026
+ const { inboxClaim: _claim, ...unclaimed } = input;
1027
+ return agentInputSchema.parse(unclaimed);
1028
+ }) });
1029
+ this.commitIndex(proposed, new Set([runId]));
1030
+ }
861
1031
  /** CI intent and its wake entry share one atomic index replacement. */
862
1032
  commitCiWait(id, wait, input) {
863
1033
  const run = this.runs.get(id);
@@ -1337,7 +1507,7 @@ export class RunStore extends EventEmitter {
1337
1507
  ? startedAgentSteps.reduce((sum, candidate) => sum + (candidate.outputTokens ?? 0), 0)
1338
1508
  : undefined;
1339
1509
  const cost = run.steps.reduce((sum, s) => sum + (s.costUsd ?? 0), 0);
1340
- run.costUsd = cost > 0 ? cost : undefined;
1510
+ run.costUsd = run.steps.some((step) => step.costUsd !== undefined) ? cost : undefined;
1341
1511
  this.touch(run);
1342
1512
  }
1343
1513
  setArchived(id, archived) {
@@ -1808,9 +1978,78 @@ export class RunStore extends EventEmitter {
1808
1978
  throw new Error('Worker execution checkpoint does not prove safe admission');
1809
1979
  }
1810
1980
  const generation = randomUUID();
1981
+ // #469: the controller is durable before `starting`, so a crash leaves a provable generation.
1982
+ this.writeWorkerProcesses(id, { generation, controller: { pid: process.pid, ...startToken(process.pid) }, processes: [] });
1811
1983
  this.writeWorkerExecution(id, { generation, phase: 'starting' });
1812
1984
  return generation;
1813
1985
  }
1986
+ /** Private process record (#469): which processes a generation spawned, never part of run JSON or SSE. */
1987
+ processesPath(id) {
1988
+ return this.executionPath(id).replace(/\.execution\.json$/, '.processes.json');
1989
+ }
1990
+ /** Tri-state: only ENOENT is `absent` (legacy, scan-only). A present record that is unreadable,
1991
+ * wrongly permissioned, malformed or of another generation is `unknown`: never finalized or reaped. */
1992
+ readWorkerProcesses(id, generation) {
1993
+ try {
1994
+ const fd = openSync(this.processesPath(id), constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK);
1995
+ try {
1996
+ const info = fstatSync(fd);
1997
+ if (!info.isFile() || info.size > 8192 || (info.mode & 0o077))
1998
+ return 'unknown';
1999
+ const buffer = Buffer.alloc(8193);
2000
+ const count = readSync(fd, buffer, 0, buffer.length, 0);
2001
+ const record = workerProcessRecordSchema.parse(JSON.parse(buffer.subarray(0, count).toString('utf8')));
2002
+ return record.generation === generation ? record : 'unknown';
2003
+ }
2004
+ finally {
2005
+ closeSync(fd);
2006
+ }
2007
+ }
2008
+ catch (error) {
2009
+ return error.code === 'ENOENT' ? 'absent' : 'unknown';
2010
+ }
2011
+ }
2012
+ writeWorkerProcesses(id, record) {
2013
+ const path = this.processesPath(id);
2014
+ try {
2015
+ const existing = lstatSync(path);
2016
+ if (!existing.isFile() || existing.isSymbolicLink())
2017
+ throw new Error('Unsafe process record');
2018
+ }
2019
+ catch (error) {
2020
+ if (error.code !== 'ENOENT')
2021
+ throw error;
2022
+ }
2023
+ const temporary = `${path}.${randomUUID()}.tmp`;
2024
+ const fd = openSync(temporary, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600);
2025
+ try {
2026
+ writeFileSync(fd, JSON.stringify(workerProcessRecordSchema.parse(record)));
2027
+ fsyncSync(fd);
2028
+ }
2029
+ finally {
2030
+ closeSync(fd);
2031
+ }
2032
+ try {
2033
+ renameSync(temporary, path);
2034
+ }
2035
+ finally {
2036
+ rmSync(temporary, { force: true });
2037
+ }
2038
+ }
2039
+ /** Bound to the current generation. Past the cap the record never drops an entry: the
2040
+ * working-directory scan is the remaining evidence. */
2041
+ appendWorkerProcess(id, generation, pid) {
2042
+ const record = this.readWorkerProcesses(id, generation);
2043
+ if (typeof record === 'string' || this.readWorkerExecution(id)?.generation !== generation)
2044
+ return false;
2045
+ const entry = { pid, ...startToken(pid) };
2046
+ if (record.processes.some(known => known.pid === entry.pid && known.startToken === entry.startToken))
2047
+ return true;
2048
+ if (record.processes.length >= WORKER_PROCESS_CAP)
2049
+ return false;
2050
+ this.writeWorkerProcesses(id, { ...record, processes: [...record.processes, entry] });
2051
+ return true;
2052
+ }
1814
2053
  commitWorkerExecutionComplete(id, generation) {
1815
2054
  const proof = this.readWorkerExecution(id);
1816
2055
  if (!proof || proof.generation !== generation)
@@ -1933,6 +2172,7 @@ export class RunStore extends EventEmitter {
1933
2172
  this.removeRunHistoryBytes(id);
1934
2173
  // Private process/account evidence is now replaced by the exact parent receipt.
1935
2174
  rmSync(this.identityPath(id), { force: true });
2175
+ rmSync(this.processesPath(id), { force: true });
1936
2176
  rmSync(this.executionPath(id), { force: true });
1937
2177
  const retained = this.readWorkerResult(parent.id, id);
1938
2178
  this.commitWorkerResult(parent.id, { ...retained, observedAt: new Date().toISOString(),
@@ -1974,6 +2214,7 @@ export class RunStore extends EventEmitter {
1974
2214
  rmSync(this.eventsPath(id), { force: true });
1975
2215
  rmSync(this.handoffPath(id), { force: true });
1976
2216
  rmSync(this.imagesDir(id), { recursive: true, force: true });
2217
+ removeArtifacts(this.dataDir, id);
1977
2218
  }
1978
2219
  deleteRun(id) {
1979
2220
  if (!this.canDeleteRun(id))
@@ -1987,6 +2228,7 @@ export class RunStore extends EventEmitter {
1987
2228
  rmSync(this.eventsPath(id), { force: true });
1988
2229
  rmSync(this.handoffPath(id), { force: true });
1989
2230
  rmSync(this.imagesDir(id), { recursive: true, force: true });
2231
+ removeArtifacts(this.dataDir, id);
1990
2232
  }
1991
2233
  catch { /* Ordinary run deletion preserves its existing best-effort behavior. */ }
1992
2234
  this.seqs.delete(id);
@@ -2054,6 +2296,7 @@ export class RunStore extends EventEmitter {
2054
2296
  rmSync(this.eventsPath(stale.id), { force: true });
2055
2297
  rmSync(this.handoffPath(stale.id), { force: true });
2056
2298
  rmSync(this.imagesDir(stale.id), { recursive: true, force: true });
2299
+ removeArtifacts(this.dataDir, stale.id);
2057
2300
  }
2058
2301
  catch {
2059
2302
  // best effort