@iowarp/clio-coder 0.3.6 → 0.3.7

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 (258) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/README.md +16 -5
  3. package/dist/{acp-2BEHC4DL.js → acp-SK4MD6MM.js} +10 -10
  4. package/dist/{agents-LNNFTM53.js → agents-2FN2K6ME.js} +30 -25
  5. package/dist/assets/codewiki.json +1 -1
  6. package/dist/{auth-KXXFI2VS.js → auth-QIYZWM5I.js} +13 -13
  7. package/dist/{chunk-KHSFENX2.js → chunk-3HAPLH5M.js} +10 -10
  8. package/dist/{chunk-24I7BN55.js → chunk-465YSENW.js} +2 -2
  9. package/dist/{chunk-4OC57DA6.js → chunk-4DGYLA73.js} +53 -2
  10. package/dist/{chunk-CYQKWTG3.js → chunk-4DWFMQDR.js} +4 -4
  11. package/dist/{chunk-E2ER4LJF.js → chunk-5C3AQNDW.js} +25 -1
  12. package/dist/{chunk-22NAGB7X.js → chunk-5C77SEEY.js} +5 -94
  13. package/dist/{chunk-43AOLP7E.js → chunk-5FR74PWO.js} +2 -1
  14. package/dist/{chunk-K7T3E2SR.js → chunk-5UJ6ECTS.js} +10 -9
  15. package/dist/{chunk-6US73PDB.js → chunk-6M7VS3J3.js} +5 -5
  16. package/dist/{chunk-CJUB2JJ2.js → chunk-6TUKSZVF.js} +5 -5
  17. package/dist/{chunk-5JGRAMKL.js → chunk-AB4XIIVB.js} +8 -6
  18. package/dist/{chunk-R46L2BIR.js → chunk-BMWK7ZIZ.js} +14 -20
  19. package/dist/{chunk-4BPJXDWC.js → chunk-C4JBQ5SR.js} +30 -14
  20. package/dist/{chunk-XXQNGV4M.js → chunk-CEYBNUGC.js} +243 -63
  21. package/dist/{chunk-VEZEGCGW.js → chunk-D4MDIG46.js} +20 -18
  22. package/dist/chunk-DJNLUABN.js +843 -0
  23. package/dist/{chunk-RY3LY4J5.js → chunk-DMD2AGVS.js} +5 -4
  24. package/dist/{chunk-KOHPCX4K.js → chunk-DOOEX22V.js} +2 -2
  25. package/dist/chunk-DQA7QLMD.js +123 -0
  26. package/dist/chunk-DR52UMZW.js +21 -0
  27. package/dist/{chunk-XF5N4U5A.js → chunk-EBEFWSGL.js} +6 -5
  28. package/dist/{chunk-EYPA3EGJ.js → chunk-EELBMBT6.js} +120 -13
  29. package/dist/{chunk-CKXWIANG.js → chunk-EOOQZZDE.js} +16 -14
  30. package/dist/{chunk-WR67VIZY.js → chunk-FOT2FX5J.js} +63 -5
  31. package/dist/{chunk-FYYLNIL5.js → chunk-GH5622CP.js} +2 -2
  32. package/dist/chunk-GWS3VEIW.js +195 -0
  33. package/dist/{chunk-LYF7OHWH.js → chunk-J7PIKKWC.js} +8 -463
  34. package/dist/{chunk-NILBFAPG.js → chunk-JNXPYBB4.js} +2 -2
  35. package/dist/{chunk-4VP4KH3K.js → chunk-JRIO5UD2.js} +4 -4
  36. package/dist/{chunk-6XXKFVSN.js → chunk-JTSEDYVQ.js} +7 -7
  37. package/dist/{chunk-QKMUKYO7.js → chunk-KCMKRQX4.js} +236 -84
  38. package/dist/chunk-KZ2H5X4G.js +1026 -0
  39. package/dist/{chunk-QNQHSOLF.js → chunk-LADCF22A.js} +12 -12
  40. package/dist/chunk-M4AKACEO.js +382 -0
  41. package/dist/{chunk-XYDYPRZI.js → chunk-MXI6J5JF.js} +7 -7
  42. package/dist/{chunk-G7MUEIGA.js → chunk-OB5HIGJY.js} +1 -1
  43. package/dist/{chunk-EKY57CSP.js → chunk-OBMAI2DP.js} +61 -767
  44. package/dist/chunk-PD3MESLB.js +242 -0
  45. package/dist/{chunk-ZRGEBJ4T.js → chunk-QCTRSGHQ.js} +21 -21
  46. package/dist/chunk-RVG5JXAL.js +41 -0
  47. package/dist/{chunk-RD5U66HV.js → chunk-SROCI7ZU.js} +7 -7
  48. package/dist/{chunk-MFFY33HR.js → chunk-THKY7CD7.js} +466 -205
  49. package/dist/{chunk-PCZJO5TI.js → chunk-UFQ3F4FW.js} +13 -178
  50. package/dist/{chunk-AD2SYQYC.js → chunk-UHXRNZ2J.js} +121 -3
  51. package/dist/chunk-UND3GU2L.js +103 -0
  52. package/dist/{chunk-QM3F2GKX.js → chunk-UUANF5CR.js} +2247 -2096
  53. package/dist/chunk-UVDSQ6LW.js +472 -0
  54. package/dist/{chunk-DJVECN66.js → chunk-VQNODYQ4.js} +14 -14
  55. package/dist/{chunk-3BPUFZDL.js → chunk-VREKEFLL.js} +3 -3
  56. package/dist/{chunk-PBTHKCPN.js → chunk-WJHBC77E.js} +6 -6
  57. package/dist/{chunk-XE2VEJHX.js → chunk-X2KV5FXT.js} +2 -2
  58. package/dist/{chunk-ZXF4XRKW.js → chunk-XEGB6BCN.js} +157 -7
  59. package/dist/{chunk-E25LMLRW.js → chunk-YD734TPH.js} +2 -2
  60. package/dist/{verifiers-NCBTHHN2.js → chunk-YTYFXUI3.js} +65 -322
  61. package/dist/{chunk-OH3TOQTB.js → chunk-ZGH7FGS5.js} +13 -7
  62. package/dist/cli/index.js +32 -30
  63. package/dist/{clio-M2KGYUFZ.js → clio-WBVQEBKO.js} +7 -7
  64. package/dist/{code-nav-GQNL7XA6.js → code-nav-FGGFIE7L.js} +3 -3
  65. package/dist/{components-5TTYYX6G.js → components-F7OEATSO.js} +4 -4
  66. package/dist/{config-XUUYQIWO.js → config-TRBL3RCF.js} +34 -29
  67. package/dist/{configure-IHJ7YOMV.js → configure-OLCVPHNM.js} +15 -15
  68. package/dist/{context-74JLXAWD.js → context-MJIJ6GOX.js} +11 -11
  69. package/dist/{context-ZQ7SIFJV.js → context-WFPKQSM6.js} +19 -3
  70. package/dist/{context-75MIWW3U.js → context-XEWE3MOJ.js} +31 -26
  71. package/dist/{context-clear-GYKWNUML.js → context-clear-KNOS2JPB.js} +31 -26
  72. package/dist/{context-working-set-UX5KEP4J.js → context-working-set-EUXAZI6N.js} +8 -8
  73. package/dist/{dispatch-runner-GIJBHNFL.js → dispatch-runner-B7MTOVKL.js} +313 -53
  74. package/dist/{docs-6FZSCG5B.js → docs-FLJTIDSE.js} +4 -4
  75. package/dist/{doctor-SVJ5BZCW.js → doctor-RN4YKO2X.js} +14 -14
  76. package/dist/{eval-CG6LLBLD.js → eval-RUBJVSNQ.js} +8 -7
  77. package/dist/{evidence-ZYFIEN42.js → evidence-JZNBUOQZ.js} +30 -25
  78. package/dist/{evolve-QGEXEMDW.js → evolve-FJVC4KKI.js} +30 -25
  79. package/dist/{extensions-ADGNCJJD.js → extensions-IQL36S7K.js} +4 -4
  80. package/dist/{fleet-S5R4ZOQY.js → fleet-BDKYJFCP.js} +214 -360
  81. package/dist/fleet-commands-ZFIWZSB3.js +70 -0
  82. package/dist/fleet-graph-Y6HPXIVF.js +125 -0
  83. package/dist/fleet-new-RDVJLHHH.js +48 -0
  84. package/dist/fleet-validate-BIYREGIK.js +79 -0
  85. package/dist/{init-5DRU55YR.js → init-LQUB5COQ.js} +44 -37
  86. package/dist/library-NJAHIGG4.js +217 -0
  87. package/dist/{memory-7YKKR6UC.js → memory-OG6HOYKM.js} +31 -26
  88. package/dist/{models-ZPOLRU2C.js → models-5ZG5XY7J.js} +21 -20
  89. package/dist/{monitor-US5F5YGZ.js → monitor-TJ7AMTGB.js} +49 -30
  90. package/dist/{orchestrator-E2AL4T5N.js → orchestrator-WZYB54DM.js} +3827 -581
  91. package/dist/{paths-E7KYAQWE.js → paths-XUC7GS6E.js} +4 -4
  92. package/dist/{reset-KZ652EK6.js → reset-PXQT45IY.js} +7 -7
  93. package/dist/{run-SRNBKDWD.js → run-FQ74YF62.js} +53 -45
  94. package/dist/{share-CGZE33UP.js → share-FW7SVCL3.js} +33 -9
  95. package/dist/{skills-S2X4DLY5.js → skills-7E7IRB3R.js} +24 -8
  96. package/dist/{skills-eval-W2GGIC4R.js → skills-eval-LI75W6OK.js} +34 -27
  97. package/dist/{targets-54SWINWB.js → targets-4CIFKCTW.js} +23 -22
  98. package/dist/{terminal-lease-SAIF2OGY.js → terminal-lease-WUZY7ZV5.js} +4 -4
  99. package/dist/{uninstall-BVLWXKBT.js → uninstall-7FV7IP4E.js} +4 -4
  100. package/dist/{upgrade-JKAR27XC.js → upgrade-K2HVIVMQ.js} +20 -19
  101. package/dist/{usage-MSAWCLX4.js → usage-GTZELZQX.js} +116 -49
  102. package/dist/verifiers-RLAHT27O.js +336 -0
  103. package/dist/{verify-X5HDROLA.js → verify-BX3BRKH5.js} +7 -6
  104. package/dist/{wiki-generate-GUSOQ6ZP.js → wiki-generate-ASIFASCN.js} +45 -37
  105. package/dist/worker/entry.js +38 -35
  106. package/docs/README.md +3 -2
  107. package/docs/acp.md +1 -1
  108. package/docs/alcf-provider.md +1 -1
  109. package/docs/architecture.md +2 -2
  110. package/docs/artifact-versions.md +10 -6
  111. package/docs/built-in-agents.md +26 -2
  112. package/docs/capacity-and-scheduling.md +1 -1
  113. package/docs/commands-and-modes.md +82 -2
  114. package/docs/configuration-and-targets.md +79 -2
  115. package/docs/context-engine.md +1 -1
  116. package/docs/development-pipeline.md +1 -1
  117. package/docs/dispatch-architecture-rationale.md +1 -1
  118. package/docs/documentation-coverage.md +3 -3
  119. package/docs/documentation-guide.md +3 -3
  120. package/docs/eval-runner.md +1 -1
  121. package/docs/evals-internal.md +1 -1
  122. package/docs/evidence-and-memory.md +5 -5
  123. package/docs/evolution.md +1 -1
  124. package/docs/exit-codes-and-output.md +4 -1
  125. package/docs/extensions-and-sharing.md +6 -2
  126. package/docs/fleet-demo-runbook.md +2 -2
  127. package/docs/fleet-dispatch.md +197 -10
  128. package/docs/git-commit-provenance.md +2 -2
  129. package/docs/glossary.md +1 -1
  130. package/docs/installation-and-lifecycle.md +2 -2
  131. package/docs/middleware-and-components.md +2 -1
  132. package/docs/model-catalog.md +1 -1
  133. package/docs/observability.md +55 -8
  134. package/docs/proactive-memory.md +1 -1
  135. package/docs/prompt-envelope-and-tools.md +1 -1
  136. package/docs/provider-adapter-cookbook.md +1 -1
  137. package/docs/release-cut-checklist.md +79 -64
  138. package/docs/resource-library.md +59 -0
  139. package/docs/safety-model.md +2 -2
  140. package/docs/scientific-validation.md +3 -3
  141. package/docs/session-lifecycle.md +37 -1
  142. package/docs/skills-marketplace.md +16 -3
  143. package/docs/tool-usage.md +14 -7
  144. package/docs/trace-store.md +1 -1
  145. package/docs/troubleshooting.md +1 -1
  146. package/docs/tui-design.md +1 -1
  147. package/docs/worker-dispatch-mechanics.md +3 -3
  148. package/package.json +1 -1
  149. package/src/cli/fleet-commands.ts +37 -0
  150. package/src/cli/fleet-graph.ts +102 -0
  151. package/src/cli/fleet-new.ts +36 -0
  152. package/src/cli/fleet-preflight.ts +121 -0
  153. package/src/cli/fleet-validate.ts +30 -0
  154. package/src/cli/fleet.ts +173 -335
  155. package/src/cli/index.ts +3 -1
  156. package/src/cli/library.ts +190 -0
  157. package/src/cli/share.ts +13 -1
  158. package/src/cli/usage.ts +111 -19
  159. package/src/core/bus-events.ts +4 -0
  160. package/src/core/commit-attribution.ts +4 -4
  161. package/src/core/config.ts +130 -0
  162. package/src/core/defaults.ts +81 -0
  163. package/src/domains/agents/builtins/architect.md +1 -0
  164. package/src/domains/agents/builtins/oracle.md +33 -0
  165. package/src/domains/agents/catalog.ts +13 -1
  166. package/src/domains/agents/fleet-contract.ts +278 -16
  167. package/src/domains/agents/index.ts +14 -0
  168. package/src/domains/agents/result-contract.ts +235 -1
  169. package/src/domains/config/classify.ts +4 -0
  170. package/src/domains/dispatch/active-route-planner.ts +14 -0
  171. package/src/domains/dispatch/backoff.ts +2 -1
  172. package/src/domains/dispatch/capability-match.ts +1 -0
  173. package/src/domains/dispatch/checkout-writer-lease.ts +175 -0
  174. package/src/domains/dispatch/contract.ts +34 -0
  175. package/src/domains/dispatch/delegation-plan.ts +167 -0
  176. package/src/domains/dispatch/execution-plan.ts +76 -5
  177. package/src/domains/dispatch/execution-role.ts +3 -1
  178. package/src/domains/dispatch/execution-scheduler.ts +183 -67
  179. package/src/domains/dispatch/extension.ts +258 -9
  180. package/src/domains/dispatch/fleet-gate.ts +14 -0
  181. package/src/domains/dispatch/fleet-plan.ts +63 -3
  182. package/src/domains/dispatch/fleet-run.ts +737 -0
  183. package/src/domains/dispatch/gate-role-prompts.ts +9 -0
  184. package/src/domains/dispatch/host-verification.ts +178 -0
  185. package/src/domains/dispatch/index.ts +38 -0
  186. package/src/domains/dispatch/intent.ts +159 -0
  187. package/src/domains/dispatch/receipt-integrity.ts +8 -4
  188. package/src/domains/dispatch/state.ts +36 -3
  189. package/src/domains/dispatch/types.ts +51 -6
  190. package/src/domains/dispatch/validation.ts +66 -6
  191. package/src/domains/evidence/trust-status.ts +10 -1
  192. package/src/domains/middleware/index.ts +15 -0
  193. package/src/domains/middleware/watchdog.ts +281 -0
  194. package/src/domains/observability/contract.ts +3 -1
  195. package/src/domains/observability/cost.ts +12 -1
  196. package/src/domains/observability/extension.ts +2 -2
  197. package/src/domains/observability/index.ts +10 -0
  198. package/src/domains/observability/out-of-turn-usage.ts +223 -0
  199. package/src/domains/resources/index.ts +20 -0
  200. package/src/domains/resources/library.ts +326 -0
  201. package/src/domains/resources/skills/marketplace.ts +37 -12
  202. package/src/domains/session/handoff.ts +629 -0
  203. package/src/domains/share/archive.ts +67 -2
  204. package/src/entry/orchestrator.ts +37 -0
  205. package/src/interactive/bus-notices.ts +26 -0
  206. package/src/interactive/chat-loop.ts +235 -1
  207. package/src/interactive/chat-renderer.ts +22 -0
  208. package/src/interactive/cost-overlay.ts +31 -3
  209. package/src/interactive/council-dispatch.ts +30 -0
  210. package/src/interactive/council-grid.ts +213 -0
  211. package/src/interactive/council.ts +99 -0
  212. package/src/interactive/dispatch-board.ts +260 -16
  213. package/src/interactive/fleet-run-preview.ts +307 -0
  214. package/src/interactive/footer/notifications.ts +219 -0
  215. package/src/interactive/handoff-round.ts +56 -0
  216. package/src/interactive/interactive-application.ts +43 -1
  217. package/src/interactive/interactive-event-projection.ts +9 -1
  218. package/src/interactive/interactive-slash-runtime.ts +52 -2
  219. package/src/interactive/interactive-subscriptions.ts +14 -2
  220. package/src/interactive/oracle.ts +179 -0
  221. package/src/interactive/overlay-ask-user-lifecycle.ts +6 -0
  222. package/src/interactive/overlay-general-openers.ts +190 -1
  223. package/src/interactive/overlay-key-routing.ts +17 -1
  224. package/src/interactive/overlay-lifecycle.ts +41 -1
  225. package/src/interactive/overlay-permission-lifecycle.ts +10 -0
  226. package/src/interactive/overlay-resource-openers.ts +11 -3
  227. package/src/interactive/overlay-session-lifecycle.ts +234 -2
  228. package/src/interactive/overlays/fleet-run-approval.ts +208 -0
  229. package/src/interactive/overlays/handoff-review.ts +185 -0
  230. package/src/interactive/overlays/library-install-confirm.ts +151 -0
  231. package/src/interactive/overlays/list-overlay.ts +168 -2
  232. package/src/interactive/overlays/settings.ts +101 -4
  233. package/src/interactive/overlays/side-question.ts +139 -0
  234. package/src/interactive/overlays/skills-hub.ts +401 -15
  235. package/src/interactive/side-question.ts +171 -0
  236. package/src/interactive/slash-commands.ts +432 -5
  237. package/src/interactive/slash-spec.ts +19 -6
  238. package/src/interactive/theme/tokens.ts +30 -0
  239. package/src/interactive/turn-middleware.ts +15 -1
  240. package/src/interactive/watchdog-run.ts +75 -0
  241. package/src/interactive/worker-share.ts +56 -1
  242. package/src/interactive/worker-stream.ts +7 -0
  243. package/src/tools/bootstrap.ts +3 -0
  244. package/src/tools/compete-worktrees.ts +13 -79
  245. package/src/tools/dispatch-admission.ts +242 -8
  246. package/src/tools/dispatch-arguments.ts +57 -1
  247. package/src/tools/dispatch-plan.ts +136 -6
  248. package/src/tools/dispatch-runner.ts +319 -13
  249. package/src/tools/dispatch-types.ts +20 -1
  250. package/src/tools/dispatch.ts +72 -2
  251. package/src/tools/monitor.ts +16 -0
  252. package/src/tools/profiles.ts +18 -4
  253. package/src/tools/task-worktree.ts +238 -0
  254. package/src/tools/verify/authoring.ts +61 -1
  255. package/src/tools/verify/scripts.ts +62 -0
  256. package/src/tools/worker-evidence.ts +2 -1
  257. package/src/worker/spec-contract.ts +1 -0
  258. package/dist/chunk-HC4CLZ2Y.js +0 -68
@@ -1,4 +1,5 @@
1
1
  import { closeAgentLedger, openAgentLedger } from "./agent-ledger-store.js";
2
+ import type { DelegationPlan } from "./delegation-plan.js";
2
3
  import { type ExecutionHandoff, projectExecutionHandoffs } from "./execution-handoff.js";
3
4
  import {
4
5
  type ExecutionPlan,
@@ -18,6 +19,11 @@ export interface ExecutionStepResult extends ExecutionHandoff {
18
19
  * its own writes is not a witness.
19
20
  */
20
21
  boundaryViolated?: boolean;
22
+ failureReason?: string;
23
+ gatePathHash?: string;
24
+ delegationPlanHash?: string;
25
+ /** Coordinator-validated tasks that the scheduler may splice after this step settles. */
26
+ delegationPlan?: DelegationPlan;
21
27
  }
22
28
 
23
29
  /**
@@ -83,6 +89,8 @@ export interface ExecutionLoopDecisionInput {
83
89
  }
84
90
 
85
91
  export interface ExecutionSchedulerAdapter {
92
+ /** Successful prefix results that retain their original receipt references. */
93
+ replayed?: ReadonlyMap<string, ExecutionStepResult>;
86
94
  preflight(step: ExecutionPlanAgentStep): ExecutionPlanAdmission;
87
95
  reserve(plan: ExecutionPlan, admissions: ReadonlyArray<ExecutionPlanAdmission>): { ownerId: string };
88
96
  run(
@@ -131,6 +139,14 @@ export interface ExecutionSchedulerAdapter {
131
139
  cancel(assignmentId: string): void;
132
140
  release(ownerId: string): void;
133
141
  releaseUnconsumed(ownerId: string): void;
142
+ /** Persist a settled result after boundary enforcement and before a dependent starts. */
143
+ onStepSettled?(step: ExecutionPlanStep, result: ExecutionStepResult): void | Promise<void>;
144
+ /** Recompile the live plan after a successful dynamic plan step settles. */
145
+ spliceAfter?(
146
+ plan: ExecutionPlan,
147
+ step: ExecutionPlanAgentStep,
148
+ result: ExecutionStepResult,
149
+ ): ExecutionPlan | null | Promise<ExecutionPlan | null>;
134
150
  }
135
151
  export interface ExecutionPlanResult {
136
152
  planHash: string;
@@ -164,11 +180,19 @@ function isWorkspaceMutator(step: ExecutionPlanStep): boolean {
164
180
  return step.verification !== true && step.commitFrom === undefined;
165
181
  }
166
182
 
183
+ /** A token writer either declares a non-empty agent boundary or may mutate workspace state. */
184
+ function isExecutionPlanWriter(step: ExecutionPlanStep): boolean {
185
+ if (step.kind === "agent" && (step.writes?.length ?? 0) > 0) return true;
186
+ return isWorkspaceMutator(step);
187
+ }
188
+
167
189
  export async function executePlan(
168
- plan: ExecutionPlan,
190
+ initialPlan: ExecutionPlan,
169
191
  adapter: ExecutionSchedulerAdapter,
170
192
  signal?: AbortSignal,
171
193
  ): Promise<ExecutionPlanResult> {
194
+ let plan = initialPlan;
195
+ const replayed = adapter.replayed ?? new Map<string, ExecutionStepResult>();
172
196
  const stepsById = new Map(plan.steps.map((step) => [step.id, step]));
173
197
  const loopsById = new Map(plan.loops.map((loop) => [loop.id, loop]));
174
198
  const agentSteps: ExecutionPlanAgentStep[] = [];
@@ -182,7 +206,7 @@ export async function executePlan(
182
206
  if (step.approvedAuthority === null || step.approvedAuthority !== step.requestedAuthority) {
183
207
  throw new Error(`execution plan: step '${step.id}' lacks its requested authority grant`);
184
208
  }
185
- agentSteps.push(step);
209
+ if (!replayed.has(step.id)) agentSteps.push(step);
186
210
  }
187
211
  for (const loop of plan.loops) {
188
212
  if (loop.checkKind === "agent" && adapter.decideLoop === undefined) {
@@ -205,6 +229,8 @@ export async function executePlan(
205
229
  if (ledgerId !== null) await openAgentLedger(ledgerId);
206
230
  const admissions = agentSteps.map((step) => adapter.preflight(step));
207
231
  const reservation = adapter.reserve(plan, admissions);
232
+ const reservationOwners = new Set([reservation.ownerId]);
233
+ const reservationByStep = new Map(agentSteps.map((step) => [step.id, reservation.ownerId]));
208
234
  const results = new Map<string, ExecutionStepResult>();
209
235
  const skipped = new Set<string>();
210
236
  const unneeded = new Set<string>();
@@ -232,7 +258,7 @@ export async function executePlan(
232
258
  stopped = true;
233
259
  for (const owned of running.values()) if (owned.assignmentId !== null) adapter.cancel(owned.assignmentId);
234
260
  codeAbort.abort();
235
- adapter.releaseUnconsumed(reservation.ownerId);
261
+ for (const ownerId of reservationOwners) adapter.releaseUnconsumed(ownerId);
236
262
  };
237
263
  signal?.addEventListener("abort", cancelOwned, { once: true });
238
264
 
@@ -247,6 +273,42 @@ export async function executePlan(
247
273
  results.set(step.id, result);
248
274
  if (isWorkspaceMutator(step)) lastMutationSeq = sequence;
249
275
  };
276
+ for (const step of plan.steps) {
277
+ const result = replayed.get(step.id);
278
+ if (result === undefined) continue;
279
+ if (!result.succeeded || !result.integrityValid) {
280
+ throw new Error(`execution plan: replayed step '${step.id}' is not a completed successful step`);
281
+ }
282
+ recordCompletion(step, result);
283
+ if (step.loop?.role === "check" && step.kind === "code") {
284
+ checkPassed.set(step.id, result.succeeded);
285
+ attemptsRun.set(step.loop.loopId, (attemptsRun.get(step.loop.loopId) ?? 0) + 1);
286
+ }
287
+ if (step.loop?.role === "check" && step.kind === "agent") {
288
+ const loop = loopsById.get(step.loop.loopId);
289
+ const decide = adapter.decideLoop;
290
+ if (loop === undefined || decide === undefined) {
291
+ throw new Error(`execution plan: replayed loop check '${step.id}' has no decider`);
292
+ }
293
+ const decision = await decide({
294
+ loop,
295
+ step,
296
+ attempt: step.loop.attempt,
297
+ terminalAttempt: step.loop.attempt >= loop.maxAttempts,
298
+ result,
299
+ priorResults: results,
300
+ });
301
+ checkPassed.set(step.id, decision.resolved);
302
+ attemptsRun.set(step.loop.loopId, (attemptsRun.get(step.loop.loopId) ?? 0) + 1);
303
+ if (decision.findings !== null) loopFindings.set(`${loop.id}:${step.loop.attempt}`, decision.findings);
304
+ if (decision.needsDecision !== undefined && decision.needsDecision !== null) {
305
+ needsDecision.push(decision.needsDecision);
306
+ }
307
+ }
308
+ if (step.loop?.role === "repair") {
309
+ repairsRun.set(step.loop.loopId, (repairsRun.get(step.loop.loopId) ?? 0) + 1);
310
+ }
311
+ }
250
312
 
251
313
  /**
252
314
  * A failed predecessor normally disqualifies its dependents. Two deliberate
@@ -408,7 +470,7 @@ export async function executePlan(
408
470
  if (plan.onFailure === "stop") {
409
471
  stopped = true;
410
472
  codeAbort.abort();
411
- adapter.releaseUnconsumed(reservation.ownerId);
473
+ for (const ownerId of reservationOwners) adapter.releaseUnconsumed(ownerId);
412
474
  halt = true;
413
475
  }
414
476
  }
@@ -479,12 +541,16 @@ export async function executePlan(
479
541
  });
480
542
 
481
543
  try {
482
- for (const [waveIndex, wave] of plan.waves.entries()) {
544
+ let waveIndex = 0;
545
+ while (waveIndex < plan.waves.length) {
546
+ const wave = plan.waves[waveIndex];
547
+ if (wave === undefined) break;
483
548
  if (stopped || signal?.aborted) break;
484
549
  if (await revalidateFor(wave)) break;
485
550
  const admitted = wave.flatMap((id) => {
486
551
  const step = stepsById.get(id);
487
552
  if (!step) throw new Error(`execution plan: unknown scheduled step '${id}'`);
553
+ if (results.has(id)) return [];
488
554
  if (isUnneeded(step)) {
489
555
  unneeded.add(step.id);
490
556
  return [];
@@ -495,72 +561,122 @@ export async function executePlan(
495
561
  }
496
562
  return [{ step, handoffs: handoffsFor(step) }];
497
563
  });
498
- const launch = admitted.filter(
499
- (entry): entry is { step: ExecutionPlanAgentStep; handoffs: ExecutionHandoff[] } => entry.step.kind === "agent",
500
- );
501
- const codeWork = admitted.filter(
502
- (entry): entry is { step: ExecutionPlanCodeStep; handoffs: ExecutionHandoff[] } => entry.step.kind === "code",
503
- );
504
- const boundaryWindow = `wave-${waveIndex}`;
505
- const enforced = openBoundary(
506
- boundaryWindow,
507
- admitted.map((entry) => entry.step),
508
- );
509
- const started = await Promise.all(
510
- launch.map(async ({ step, handoffs }) => ({
511
- step,
512
- handle: await adapter.run(step, handoffs, { ownerId: reservation.ownerId, memberId: step.id }, ledger),
513
- })),
514
- );
515
- for (const { step, handle } of started) running.set(step.id, { assignmentId: handle.assignmentId });
516
- const onSettled = (step: ExecutionPlanStep, result: ExecutionStepResult): void => {
517
- if (isPlanFailure(step, result) && plan.onFailure === "stop") {
518
- stopped = true;
519
- for (const [runningStepId, owned] of running) {
520
- if (runningStepId !== step.id && owned.assignmentId !== null) adapter.cancel(owned.assignmentId);
564
+ const admissionGroups: Array<typeof admitted> = [];
565
+ if (plan.writers === 1) {
566
+ const readers = admitted.filter(({ step }) => !isExecutionPlanWriter(step));
567
+ const writers = admitted.filter(({ step }) => isExecutionPlanWriter(step));
568
+ if (writers.length === 0) admissionGroups.push(readers);
569
+ else {
570
+ admissionGroups.push([...readers, ...(writers[0] === undefined ? [] : [writers[0]])]);
571
+ for (const writer of writers.slice(1)) admissionGroups.push([writer]);
572
+ }
573
+ } else admissionGroups.push(admitted);
574
+ for (const [groupIndex, group] of admissionGroups.entries()) {
575
+ if (stopped || signal?.aborted) break;
576
+ const launch = group.filter(
577
+ (entry): entry is { step: ExecutionPlanAgentStep; handoffs: ExecutionHandoff[] } => entry.step.kind === "agent",
578
+ );
579
+ const codeWork = group.filter(
580
+ (entry): entry is { step: ExecutionPlanCodeStep; handoffs: ExecutionHandoff[] } => entry.step.kind === "code",
581
+ );
582
+ const boundaryWindow = groupIndex === 0 ? `wave-${waveIndex}` : `wave-${waveIndex}-writer-${groupIndex + 1}`;
583
+ const enforced = openBoundary(
584
+ boundaryWindow,
585
+ group.map((entry) => entry.step),
586
+ );
587
+ const started = await Promise.all(
588
+ launch.map(async ({ step, handoffs }) => ({
589
+ step,
590
+ handle: await adapter.run(
591
+ step,
592
+ handoffs,
593
+ { ownerId: reservationByStep.get(step.id) ?? reservation.ownerId, memberId: step.id },
594
+ ledger,
595
+ ),
596
+ })),
597
+ );
598
+ for (const { step, handle } of started) running.set(step.id, { assignmentId: handle.assignmentId });
599
+ const onSettled = (step: ExecutionPlanStep, result: ExecutionStepResult): void => {
600
+ if (isPlanFailure(step, result) && plan.onFailure === "stop") {
601
+ stopped = true;
602
+ for (const [runningStepId, owned] of running) {
603
+ if (runningStepId !== step.id && owned.assignmentId !== null) adapter.cancel(owned.assignmentId);
604
+ }
605
+ codeAbort.abort();
606
+ for (const ownerId of reservationOwners) adapter.releaseUnconsumed(ownerId);
521
607
  }
522
- codeAbort.abort();
523
- adapter.releaseUnconsumed(reservation.ownerId);
608
+ };
609
+ const ran = await Promise.all([
610
+ ...started.map(async ({ step, handle }) => {
611
+ const result = await handle.result;
612
+ onSettled(step, result);
613
+ return { step: step as ExecutionPlanStep, result };
614
+ }),
615
+ ...codeWork.map(async ({ step, handoffs }) => {
616
+ const result = await runCodeStepNode(step, handoffs);
617
+ onSettled(step, result);
618
+ return { step: step as ExecutionPlanStep, result };
619
+ }),
620
+ ]);
621
+ // The boundary is settled before anything is recorded or handed on, so
622
+ // a rolled-back step never appears upstream of the work it would have
623
+ // contaminated.
624
+ const settled = await closeBoundary(boundaryWindow, enforced, ran);
625
+ for (const { step, result } of settled) {
626
+ running.set(step.id, { assignmentId: result.assignmentId });
627
+ recordCompletion(step, result);
628
+ await adapter.onStepSettled?.(step, result);
629
+ if (step.loop?.role === "repair") {
630
+ repairsRun.set(step.loop.loopId, (repairsRun.get(step.loop.loopId) ?? 0) + 1);
631
+ }
632
+ running.delete(step.id);
524
633
  }
525
- };
526
- const ran = await Promise.all([
527
- ...started.map(async ({ step, handle }) => {
528
- const result = await handle.result;
529
- onSettled(step, result);
530
- return { step: step as ExecutionPlanStep, result };
531
- }),
532
- ...codeWork.map(async ({ step, handoffs }) => {
533
- const result = await runCodeStepNode(step, handoffs);
534
- onSettled(step, result);
535
- return { step: step as ExecutionPlanStep, result };
536
- }),
537
- ]);
538
- // The boundary is settled before anything is recorded or handed on, so
539
- // a rolled-back step never appears upstream of the work it would have
540
- // contaminated.
541
- const settled = await closeBoundary(boundaryWindow, enforced, ran);
542
- for (const { step, result } of settled) {
543
- running.set(step.id, { assignmentId: result.assignmentId });
544
- recordCompletion(step, result);
545
- if (step.loop?.role === "repair") {
546
- repairsRun.set(step.loop.loopId, (repairsRun.get(step.loop.loopId) ?? 0) + 1);
634
+ // Loop continuation is decided after the wave settles, so a gate's
635
+ // verdict is read from a sealed result rather than from a live stream.
636
+ for (const { step, result } of settled) await settleCheck(step, result);
637
+ // A loop that spent its last attempt without converging ends the run
638
+ // under a stop policy, whether the verdict came from an exit code or
639
+ // from a gate that answered "no" perfectly well.
640
+ const exhausted = settled.some(({ step }) => {
641
+ const loop = step.loop?.role === "check" ? loopOf(step) : null;
642
+ return loop !== null && step.loop !== undefined && step.loop.attempt >= loop.maxAttempts && !loopResolved(loop);
643
+ });
644
+ if ((exhausted || settled.some(({ step, result }) => isPlanFailure(step, result))) && plan.onFailure === "stop") {
645
+ stopped = true;
646
+ for (const ownerId of reservationOwners) adapter.releaseUnconsumed(ownerId);
547
647
  }
548
- running.delete(step.id);
549
648
  }
550
- // Loop continuation is decided after the wave settles, so a gate's
551
- // verdict is read from a sealed result rather than from a live stream.
552
- for (const { step, result } of settled) await settleCheck(step, result);
553
- // A loop that spent its last attempt without converging ends the run
554
- // under a stop policy, whether the verdict came from an exit code or
555
- // from a gate that answered "no" perfectly well.
556
- const exhausted = settled.some(({ step }) => {
557
- const loop = step.loop?.role === "check" ? loopOf(step) : null;
558
- return loop !== null && step.loop !== undefined && step.loop.attempt >= loop.maxAttempts && !loopResolved(loop);
559
- });
560
- if ((exhausted || settled.some(({ step, result }) => isPlanFailure(step, result))) && plan.onFailure === "stop") {
561
- stopped = true;
562
- adapter.releaseUnconsumed(reservation.ownerId);
649
+ if (!stopped && adapter.spliceAfter !== undefined) {
650
+ for (const stepId of wave) {
651
+ const step = stepsById.get(stepId);
652
+ const settled = results.get(stepId);
653
+ if (
654
+ step?.kind !== "agent" ||
655
+ step.plan === undefined ||
656
+ settled === undefined ||
657
+ !settled.succeeded ||
658
+ !settled.integrityValid
659
+ ) {
660
+ continue;
661
+ }
662
+ const spliced = await adapter.spliceAfter(plan, step, settled);
663
+ if (spliced === null) continue;
664
+ const previousIds = new Set(plan.steps.map((entry) => entry.id));
665
+ const additions = spliced.steps.filter((entry) => !previousIds.has(entry.id));
666
+ for (const addition of additions) stepsById.set(addition.id, addition);
667
+ const newAgentSteps = additions.filter(
668
+ (addition): addition is ExecutionPlanAgentStep => addition.kind === "agent",
669
+ );
670
+ if (newAgentSteps.length > 0) {
671
+ const newAdmissions = newAgentSteps.map((addition) => adapter.preflight(addition));
672
+ const additionalReservation = adapter.reserve(spliced, newAdmissions);
673
+ reservationOwners.add(additionalReservation.ownerId);
674
+ for (const addition of newAgentSteps) reservationByStep.set(addition.id, additionalReservation.ownerId);
675
+ }
676
+ plan = spliced;
677
+ }
563
678
  }
679
+ waveIndex += 1;
564
680
  }
565
681
  return {
566
682
  planHash: plan.hash,
@@ -577,7 +693,7 @@ export async function executePlan(
577
693
  throw error;
578
694
  } finally {
579
695
  signal?.removeEventListener("abort", cancelOwned);
580
- adapter.release(reservation.ownerId);
696
+ for (const ownerId of reservationOwners) adapter.release(ownerId);
581
697
  if (ledgerId !== null) await closeAgentLedger(ledgerId);
582
698
  }
583
699
  }