@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
@@ -12,7 +12,7 @@
12
12
  import { createHash, randomBytes } from "node:crypto";
13
13
  import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
14
14
  import { isAbsolute, relative, resolve as resolvePath, sep } from "node:path";
15
- import { BusChannels, type DispatchCompletedPayload } from "../../core/bus-events.js";
15
+ import { BusChannels, type DispatchCompletedPayload, type DispatchRunIdentity } from "../../core/bus-events.js";
16
16
  import { DEFAULT_SETTINGS, type DelegationToolGovernance } from "../../core/defaults.js";
17
17
  import type { DomainBundle, DomainContext, DomainExtension } from "../../core/domain-loader.js";
18
18
  import { GUARDRAIL_DEFAULTS } from "../../core/guardrails.js";
@@ -40,6 +40,12 @@ import { WORKER_RUNTIME_MEDIATES_CLIO_DISPATCH } from "../../engine/worker-runti
40
40
  import { toolPromptHintsForNames } from "../../tools/builtin-tool-catalog.js";
41
41
  import { networkToolsDisabled } from "../../tools/network-policy.js";
42
42
  import { applyToolProfile, assertToolProfileEnforceable, type ToolProfileName } from "../../tools/profiles.js";
43
+ import {
44
+ applyTaskWorktree,
45
+ cleanupTaskWorktree,
46
+ createTaskWorktree,
47
+ gitCheckoutRoot,
48
+ } from "../../tools/task-worktree.js";
43
49
  import { truncateUtf8 } from "../../tools/truncate-utf8.js";
44
50
  import {
45
51
  serializeWorkerRuntimeDescriptor,
@@ -138,6 +144,7 @@ import { type BatchState, createBatch, onRunComplete, snapshotBatch } from "./ba
138
144
  import { type RunToolBudgetEnvelope, resolveToolBudgetEnvelope } from "./budget-envelope.js";
139
145
  import { assessCapabilityMismatch, type CapabilityMismatch } from "./capability-match.js";
140
146
  import { capacityLeaseUsage, createNodeLeaseUsageReader } from "./capacity-lease.js";
147
+ import { acquireCheckoutWriterLease, type CheckoutWriterLease } from "./checkout-writer-lease.js";
141
148
  import type {
142
149
  DispatchAdmissionObserver,
143
150
  DispatchContract,
@@ -159,6 +166,7 @@ import {
159
166
  import { routeFactVerdict } from "./fleet-preflight.js";
160
167
  import { competeStanceLiner, isBoundedGateRolePrompt } from "./gate-role-prompts.js";
161
168
  import { classifyHeartbeat, DEFAULT_HEARTBEAT_SPEC, type HeartbeatSpec, type HeartbeatStatus } from "./heartbeat.js";
169
+ import { hostVerificationRejection, runHostVerification } from "./host-verification.js";
162
170
  import { adaptJointRouteInput } from "./joint-route-adapter.js";
163
171
  import {
164
172
  configuredJointNodes,
@@ -209,7 +217,7 @@ import { createRouteObserver, type RouteObservationHandle, type RouteObserver }
209
217
  import { reduceRouteQuality } from "./route-quality.js";
210
218
  import { defaultRoutingIntent } from "./routing-intent.js";
211
219
  import { detectRunIdentity } from "./run-identity.js";
212
- import { type Ledger, openLedger } from "./state.js";
220
+ import { type Ledger, newRunId, openLedger } from "./state.js";
213
221
  import {
214
222
  countToolCalls,
215
223
  hasPotentiallyMutatingAttempt,
@@ -1826,7 +1834,7 @@ function buildDispatchWorkerSpec(input: DispatchWorkerSpecInput, config?: Config
1826
1834
  // A gate role that overrides the recipe contract gets no worker-side repair,
1827
1835
  // for the same reason it gets no recipe validation.
1828
1836
  if (appliesRecipeResultContract(input.req.gate?.role) && input.recipe?.resultContract) {
1829
- spec.resultContract = input.recipe.resultContract;
1837
+ spec.resultContract = input.req.resultContractOverride ?? input.recipe.resultContract;
1830
1838
  }
1831
1839
  const product = input.req.product ?? input.recipe?.product;
1832
1840
  if (product) spec.product = product;
@@ -3610,10 +3618,12 @@ export function createDispatchBundle(
3610
3618
  let identity!: ReturnType<typeof detectRunIdentity>;
3611
3619
  try {
3612
3620
  envelope = ledgerRef.create({
3621
+ ...(req.runIdHint !== undefined ? { id: req.runIdHint } : {}),
3613
3622
  agentId: req.agentId,
3614
3623
  executionRole: withAttemptRole(req.executionRole, req.lineage?.attempt ?? 0),
3615
3624
  requestOrigin: lifecycle.requestOrigin,
3616
3625
  task: req.task,
3626
+ ...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
3617
3627
  targetId,
3618
3628
  wireModelId,
3619
3629
  runtimeId,
@@ -3645,6 +3655,7 @@ export function createDispatchBundle(
3645
3655
  ...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
3646
3656
  ...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
3647
3657
  ...(req.gate !== undefined ? { gate: req.gate } : {}),
3658
+ ...(req.council !== undefined ? { council: req.council } : {}),
3648
3659
  ...(req.plan !== undefined ? { plan: req.plan } : {}),
3649
3660
  ...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
3650
3661
  });
@@ -3787,6 +3798,7 @@ export function createDispatchBundle(
3787
3798
  executionRole: envelope.executionRole,
3788
3799
  requestOrigin: lifecycle.requestOrigin,
3789
3800
  task: req.task,
3801
+ ...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
3790
3802
  targetId,
3791
3803
  wireModelId,
3792
3804
  runtimeId,
@@ -3801,6 +3813,7 @@ export function createDispatchBundle(
3801
3813
  ...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
3802
3814
  ...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
3803
3815
  ...(req.gate !== undefined ? { gate: req.gate } : {}),
3816
+ ...(req.council !== undefined ? { council: req.council } : {}),
3804
3817
  ...(req.plan !== undefined ? { plan: req.plan } : {}),
3805
3818
  ...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
3806
3819
  projectContext: lifecycle.projectContext,
@@ -3913,6 +3926,7 @@ export function createDispatchBundle(
3913
3926
  durationMs,
3914
3927
  exitCode: receipt.exitCode,
3915
3928
  toolActivity: receipt.toolActivity ?? null,
3929
+ ...(receipt.hostVerification !== undefined ? { hostVerification: receipt.hostVerification.status } : {}),
3916
3930
  ...(receipt.skillActivations && receipt.skillActivations.length > 0
3917
3931
  ? { skillActivations: [...receipt.skillActivations] }
3918
3932
  : {}),
@@ -4444,7 +4458,8 @@ export function createDispatchBundle(
4444
4458
  if (
4445
4459
  event.type === "clio_run_outcome" &&
4446
4460
  isRunOutcomeCode(event.payload?.outcomeCode) &&
4447
- event.payload.outcomeCode !== "worker_final_output_missing"
4461
+ event.payload.outcomeCode !== "worker_final_output_missing" &&
4462
+ event.payload.outcomeCode !== "host_verification_rejected"
4448
4463
  ) {
4449
4464
  if (acceptsOutcomeCodeEvents) {
4450
4465
  trustedOutcomeCodes.add(event.payload.outcomeCode);
@@ -4604,11 +4619,13 @@ export function createDispatchBundle(
4604
4619
  let identity!: ReturnType<typeof detectRunIdentity>;
4605
4620
  try {
4606
4621
  envelope = ledgerRef.create({
4622
+ ...(req.runIdHint !== undefined ? { id: req.runIdHint } : {}),
4607
4623
  agentId: req.agentId,
4608
4624
  executionRole: withAttemptRole(req.executionRole, req.lineage?.attempt ?? 0),
4609
4625
  agentAudience: lifecycle.agentAudience,
4610
4626
  requestOrigin: lifecycle.requestOrigin,
4611
4627
  task: req.task,
4628
+ ...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
4612
4629
  budget: lifecycle.budgetEnvelope,
4613
4630
  targetId: lifecycle.target.target.id,
4614
4631
  wireModelId: lifecycle.target.wireModelId,
@@ -4658,6 +4675,7 @@ export function createDispatchBundle(
4658
4675
  ...(lifecycle.pipeline ? { pipeline: lifecycle.pipeline } : {}),
4659
4676
  ...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
4660
4677
  ...(req.gate !== undefined ? { gate: req.gate } : {}),
4678
+ ...(req.council !== undefined ? { council: req.council } : {}),
4661
4679
  ...(req.plan !== undefined ? { plan: req.plan } : {}),
4662
4680
  ...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
4663
4681
  ...(heartbeatAt ? { heartbeatAt: heartbeatIso(heartbeatAt.current) } : {}),
@@ -4678,6 +4696,7 @@ export function createDispatchBundle(
4678
4696
  budget: lifecycle.budgetEnvelope,
4679
4697
  ...(placement !== undefined && placement !== null ? { node: placement.node.id } : {}),
4680
4698
  ...(req.gate !== undefined ? { gate: { role: req.gate.role, cycle: req.gate.cycle } } : {}),
4699
+ ...(req.council !== undefined ? { council: req.council } : {}),
4681
4700
  ...(req.reroutes !== undefined && req.reroutes.length > 0 ? { rerouteCount: req.reroutes.length } : {}),
4682
4701
  ...(lifecycle.target.modelCapabilities !== undefined &&
4683
4702
  lifecycle.target.modelCapabilities !== null &&
@@ -4829,6 +4848,15 @@ export function createDispatchBundle(
4829
4848
  const tokenCount =
4830
4849
  tokenMeter.inputTokens + tokenMeter.outputTokens + tokenMeter.cacheReadTokens + tokenMeter.cacheWriteTokens;
4831
4850
  const protectedArtifacts = protectedArtifactReceiptSummary(spec.protectedArtifactState);
4851
+ const fleetGate = (() => {
4852
+ if (req.fleetGateReceipt === undefined) return null;
4853
+ try {
4854
+ const content = readFileSync(resolvePath(lifecycle.cwd, req.fleetGateReceipt.path));
4855
+ return { path: req.fleetGateReceipt.path, pathHash: createHash("sha256").update(content).digest("hex") };
4856
+ } catch {
4857
+ return { path: req.fleetGateReceipt.path, pathHash: createHash("sha256").update("").digest("hex") };
4858
+ }
4859
+ })();
4832
4860
  // Sealed from the stored board, never from anything the worker said
4833
4861
  // about itself. Absent when the run had no ledger.
4834
4862
  const ledgerContribution = (() => {
@@ -4864,6 +4892,7 @@ export function createDispatchBundle(
4864
4892
  outcome,
4865
4893
  outcomeCode,
4866
4894
  lineage,
4895
+ ...(req.intent !== undefined ? { intent: structuredClone(req.intent) } : {}),
4867
4896
  identity,
4868
4897
  node: placement?.node ?? LOCAL_RUN_NODE,
4869
4898
  ...receiptAttestationFields(worker.attestation?.() ?? null),
@@ -4874,7 +4903,9 @@ export function createDispatchBundle(
4874
4903
  ...(lifecycle.briefing ? { briefing: lifecycle.briefing } : {}),
4875
4904
  ...(steering.length > 0 ? { steering: steering.map((entry) => ({ ...entry })) } : {}),
4876
4905
  ...(req.gate !== undefined ? { gate: req.gate } : {}),
4906
+ ...(req.council !== undefined ? { council: req.council } : {}),
4877
4907
  ...(req.plan !== undefined ? { plan: req.plan } : {}),
4908
+ ...(fleetGate !== null ? { fleetGate } : {}),
4878
4909
  ...(lifecycle.personaOverride ? { personaOverride: lifecycle.personaOverride } : {}),
4879
4910
  projectContext: lifecycle.projectContext,
4880
4911
  rulesApplied: lifecycle.rulesApplied,
@@ -5004,6 +5035,7 @@ export function createDispatchBundle(
5004
5035
  durationMs,
5005
5036
  exitCode: receipt.exitCode,
5006
5037
  toolActivity: receipt.toolActivity ?? null,
5038
+ ...(receipt.hostVerification !== undefined ? { hostVerification: receipt.hostVerification.status } : {}),
5007
5039
  ...(receipt.skillActivations && receipt.skillActivations.length > 0
5008
5040
  ? { skillActivations: [...receipt.skillActivations] }
5009
5041
  : {}),
@@ -5102,7 +5134,7 @@ export function createDispatchBundle(
5102
5134
  const capturedOutput = outputCapture.snapshot();
5103
5135
  const observedRunEffects = runEffects.snapshot();
5104
5136
  const appliedResultContract = appliesRecipeResultContract(req.gate?.role)
5105
- ? (lifecycle.recipe?.resultContract ?? null)
5137
+ ? (req.resultContractOverride ?? lifecycle.recipe?.resultContract ?? null)
5106
5138
  : null;
5107
5139
  const resultContract = validateRecipeResult({
5108
5140
  contract: appliedResultContract,
@@ -5166,13 +5198,40 @@ export function createDispatchBundle(
5166
5198
  finalDetail = [finalDetail, describeUngroundedValidations(validationGrounding)].filter(Boolean).join("; ");
5167
5199
  }
5168
5200
  const hasTerminalArtifact =
5169
- lifecycle.recipe?.resultContract.kind === "architect-plan" && resultValidation?.conformance === "pass";
5201
+ (req.resultContractOverride ?? lifecycle.recipe?.resultContract)?.kind === "architect-plan" &&
5202
+ resultValidation?.conformance === "pass";
5170
5203
  if (finalOutcome === "succeeded" && !hasDurableFinalOutput(capturedOutput) && !hasTerminalArtifact) {
5171
5204
  finalOutcome = "failed";
5172
5205
  outcomeCode = "worker_final_output_missing";
5173
5206
  finalDetail = WORKER_FINAL_OUTPUT_MISSING_DETAIL;
5174
5207
  failureMessage = finalDetail;
5175
5208
  }
5209
+ const hostVerification = await runHostVerification({
5210
+ runId: envelope.id,
5211
+ request: req,
5212
+ workerSuccessful: finalOutcome === "succeeded",
5213
+ onDiagnostic: (error) => reportDispatchDiagnostic(`write host verification memo for ${envelope.id}`, error),
5214
+ });
5215
+ const hostRejection = hostVerificationRejection(hostVerification);
5216
+ if (hostRejection !== null && finalOutcome === "succeeded") {
5217
+ finalOutcome = "failed";
5218
+ outcomeCode = hostRejection.outcomeCode;
5219
+ finalDetail = hostRejection.detail;
5220
+ failureMessage = finalDetail;
5221
+ }
5222
+ let worktreeReceipt: RunReceiptDraft["worktree"];
5223
+ if (req.taskWorktree !== undefined && finalOutcome === "succeeded") {
5224
+ worktreeReceipt = applyTaskWorktree({
5225
+ worktree: req.taskWorktree,
5226
+ apply: req.apply ?? "merge",
5227
+ protectedPaths: getProtectedArtifactState().artifacts.map((artifact) => artifact.path),
5228
+ });
5229
+ if (worktreeReceipt.reason !== undefined) {
5230
+ finalOutcome = "failed";
5231
+ finalDetail = worktreeReceipt.reason;
5232
+ failureMessage = finalDetail;
5233
+ }
5234
+ }
5176
5235
  const status = runStatusForOutcome(finalOutcome);
5177
5236
  const failureClass = classifyFailure(evidence, result, finalOutcome, outcomeCode);
5178
5237
  const receiptDraft = buildReceiptDraft(
@@ -5186,6 +5245,8 @@ export function createDispatchBundle(
5186
5245
  sealedResultContractFact,
5187
5246
  validationGrounding === null ? null : { ...validationGrounding, ungrounded: [...validationGrounding.ungrounded] },
5188
5247
  );
5248
+ if (hostVerification !== undefined) receiptDraft.hostVerification = hostVerification;
5249
+ if (worktreeReceipt !== undefined) receiptDraft.worktree = worktreeReceipt;
5189
5250
  const ledgerPatch: Partial<RunEnvelope> = {
5190
5251
  status,
5191
5252
  outcome: finalOutcome,
@@ -5216,6 +5277,13 @@ export function createDispatchBundle(
5216
5277
  ledgerRef.update(envelope.id, ledgerPatch);
5217
5278
  const receipt = ledgerRef.recordReceipt(envelope.id, sealRouteDecision(receiptDraft, routeObservation.decision));
5218
5279
  await ledgerRef.persist();
5280
+ if (worktreeReceipt?.applied === true && req.taskWorktree !== undefined) {
5281
+ try {
5282
+ cleanupTaskWorktree(req.taskWorktree, true);
5283
+ } catch (cleanupError) {
5284
+ reportDispatchDiagnostic(`clean applied task worktree ${req.taskWorktree.runId}`, cleanupError);
5285
+ }
5286
+ }
5219
5287
  active.delete(envelope.id);
5220
5288
  recordTargetOutcome(
5221
5289
  lifecycle.target.target.id,
@@ -5303,10 +5371,58 @@ export function createDispatchBundle(
5303
5371
  events: AsyncIterableIterator<unknown>;
5304
5372
  finalPromise: Promise<RunReceipt>;
5305
5373
  }> {
5374
+ let prepared = req;
5375
+ let createdWorktree: DispatchRequest["taskWorktree"];
5376
+ let writerLease: CheckoutWriterLease | undefined;
5377
+ const writerRecipe = agents.get(req.agentId);
5378
+ if (writerRecipe !== null && normalizeAgentSpec(writerRecipe).capabilityClass === "workspace-edit") {
5379
+ const checkout = req.taskWorktree?.root ?? gitCheckoutRoot(req.cwd ?? process.cwd());
5380
+ if (checkout !== null) writerLease = acquireCheckoutWriterLease({ checkout });
5381
+ }
5382
+ if (req.worktree === true && req.taskWorktree === undefined) {
5383
+ const requestedCwd = resolvePath(req.cwd ?? process.cwd());
5384
+ const root = gitCheckoutRoot(requestedCwd);
5385
+ if (root === null) throw new Error("worktree_non_git_checkout");
5386
+ if (req.taskWorktreeDestination !== undefined && root !== req.taskWorktreeDestination) {
5387
+ writerLease?.release();
5388
+ throw new Error("dispatch: worktree merge destination differs from the approved execution snapshot");
5389
+ }
5390
+ const runId = newRunId();
5391
+ let taskWorktree: NonNullable<DispatchRequest["taskWorktree"]>;
5392
+ try {
5393
+ taskWorktree = createTaskWorktree(root, runId);
5394
+ } catch (error) {
5395
+ writerLease?.release();
5396
+ throw error;
5397
+ }
5398
+ createdWorktree = taskWorktree;
5399
+ const cwdRelative = relative(root, requestedCwd);
5400
+ const workerCwd = resolvePath(taskWorktree.path, cwdRelative);
5401
+ const writeRoots = req.writeRoots?.map((entry) => {
5402
+ const rel = relative(root, entry);
5403
+ return rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel) ? entry : resolvePath(taskWorktree.path, rel);
5404
+ });
5405
+ prepared = {
5406
+ ...req,
5407
+ runIdHint: runId,
5408
+ taskWorktree,
5409
+ cwd: workerCwd,
5410
+ protectedArtifactRemap: { sourceRoot: root, workerRoot: taskWorktree.path },
5411
+ ...(writeRoots === undefined ? {} : { writeRoots }),
5412
+ };
5413
+ }
5306
5414
  let handle: Awaited<ReturnType<typeof dispatchAttempt>>;
5307
5415
  try {
5308
- handle = await dispatchAttempt(req, observer);
5416
+ handle = await dispatchAttempt(prepared, observer);
5309
5417
  } catch (error) {
5418
+ writerLease?.release();
5419
+ if (createdWorktree !== undefined) {
5420
+ try {
5421
+ cleanupTaskWorktree(createdWorktree, true);
5422
+ } catch (cleanupError) {
5423
+ reportDispatchDiagnostic(`clean task worktree ${createdWorktree.runId} after admission failure`, cleanupError);
5424
+ }
5425
+ }
5310
5426
  if (req.reservation !== undefined && req.lineage === undefined) {
5311
5427
  rollbackDispatchReservation(req.reservation.ownerId, now());
5312
5428
  }
@@ -5314,7 +5430,7 @@ export function createDispatchBundle(
5314
5430
  }
5315
5431
  // Requests carrying lineage are internal attempts (retry or nested
5316
5432
  // orchestration) and retain the per-attempt handle contract.
5317
- if (req.lineage) return handle;
5433
+ if (req.lineage) return { ...handle, finalPromise: handle.finalPromise.finally(() => writerLease?.release()) };
5318
5434
 
5319
5435
  const assignment = assignments.open(handle.runId, assignmentPolicyFor(handle.effectiveRequest));
5320
5436
  persistAssignment(registerAssignment(assignment.id), `${assignment.id}:open`);
@@ -5327,9 +5443,10 @@ export function createDispatchBundle(
5327
5443
  const reservation = req.reservation;
5328
5444
  const terminal =
5329
5445
  reservation === undefined
5330
- ? assignment.terminal
5446
+ ? assignment.terminal.finally(() => writerLease?.release())
5331
5447
  : assignment.terminal.finally(() => {
5332
5448
  releaseDispatchReservationMember(reservation.ownerId, reservation.memberId, now());
5449
+ writerLease?.release();
5333
5450
  });
5334
5451
  return { runId: handle.runId, events: assignment.events, finalPromise: terminal };
5335
5452
  }
@@ -5713,6 +5830,137 @@ export function createDispatchBundle(
5713
5830
  };
5714
5831
  }
5715
5832
 
5833
+ async function sealCouncilSynthesis(input: {
5834
+ group: string;
5835
+ round: number;
5836
+ kind: "none" | "vote";
5837
+ text: string;
5838
+ subjects: ReadonlyArray<{ runId: string; digest: string | null }>;
5839
+ template: RunReceipt;
5840
+ }): Promise<RunReceipt> {
5841
+ const l = requireLedger();
5842
+ const runId = newRunId();
5843
+ const task = `Council ${input.kind} synthesis`;
5844
+ // The synthesis is the council's own verdict, filed under the origin the
5845
+ // council was asked from: an internal origin would keep it out of the
5846
+ // transcript worker fold, and with it out of /share.
5847
+ const origin = input.template.requestOrigin ?? "agent";
5848
+ const gate = { role: "synthesis" as const, group: input.group, cycle: input.round, subjects: [...input.subjects] };
5849
+ const council = { group: input.group, label: "synthesis", round: input.round };
5850
+ const created = l.create({
5851
+ id: runId,
5852
+ agentId: "council-synthesis",
5853
+ executionRole: "judge",
5854
+ requestOrigin: origin,
5855
+ task,
5856
+ targetId: input.template.targetId,
5857
+ wireModelId: input.template.wireModelId,
5858
+ runtimeId: input.template.runtimeId,
5859
+ runtimeKind: input.template.runtimeKind,
5860
+ sessionId: input.template.sessionId,
5861
+ cwd: input.template.reproducibility?.cwd ?? process.cwd(),
5862
+ });
5863
+ const endedAt = new Date().toISOString();
5864
+ l.update(runId, {
5865
+ status: "completed",
5866
+ outcome: "succeeded",
5867
+ endedAt,
5868
+ exitCode: 0,
5869
+ gate,
5870
+ council,
5871
+ ...(input.template.plan !== undefined ? { plan: input.template.plan } : {}),
5872
+ });
5873
+ const receipt = l.recordReceipt(runId, {
5874
+ runId,
5875
+ agentId: "council-synthesis",
5876
+ executionRole: "judge",
5877
+ requestOrigin: origin,
5878
+ task,
5879
+ targetId: input.template.targetId,
5880
+ wireModelId: input.template.wireModelId,
5881
+ runtimeId: input.template.runtimeId,
5882
+ runtimeKind: input.template.runtimeKind,
5883
+ startedAt: created.startedAt,
5884
+ endedAt,
5885
+ outcome: "succeeded",
5886
+ outcomeDetail: null,
5887
+ exitCode: 0,
5888
+ tokenCount: 0,
5889
+ inputTokenCount: 0,
5890
+ outputTokenCount: 0,
5891
+ reasoningTokenCount: 0,
5892
+ cacheReadTokenCount: 0,
5893
+ cacheWriteTokenCount: 0,
5894
+ output: { state: "final", text: input.text, bytes: Buffer.byteLength(input.text, "utf8"), truncated: false },
5895
+ costUsd: 0,
5896
+ costProvenance: "unknown",
5897
+ compiledPromptHash: null,
5898
+ staticCompositionHash: null,
5899
+ clioVersion: readClioVersion(),
5900
+ piMonoVersion: readPiMonoVersion(),
5901
+ platform: process.platform,
5902
+ nodeVersion: process.version,
5903
+ toolCalls: 0,
5904
+ toolStats: [],
5905
+ verification: { state: "unverified", basis: "no-validation-tool" },
5906
+ routingIntent: defaultRoutingIntent({}),
5907
+ quality: createRunReceiptQuality({ runtimeEnforceable: false, enforcementPassed: null, resultContract: null }),
5908
+ reproducibility: collectReproducibility(input.template.reproducibility?.cwd ?? process.cwd(), null),
5909
+ sessionId: input.template.sessionId,
5910
+ gate,
5911
+ council,
5912
+ ...(input.template.plan !== undefined ? { plan: input.template.plan } : {}),
5913
+ });
5914
+ await l.persist();
5915
+ // The synthesis never ran as a worker, so nothing else publishes its
5916
+ // lifecycle. The Fleet Runs board and /share are built from these
5917
+ // events, and a council whose synthesis they cannot see is a council
5918
+ // whose verdict the operator cannot read or share.
5919
+ const identity: DispatchRunIdentity = {
5920
+ runId,
5921
+ agentId: "council-synthesis",
5922
+ task,
5923
+ requestOrigin: origin,
5924
+ targetId: receipt.targetId,
5925
+ wireModelId: receipt.wireModelId,
5926
+ runtimeId: receipt.runtimeId,
5927
+ runtimeKind: receipt.runtimeKind,
5928
+ gate: { role: gate.role, cycle: gate.cycle },
5929
+ council,
5930
+ };
5931
+ context.bus.emit(BusChannels.DispatchEnqueued, { ...identity, requestOrigin: origin });
5932
+ context.bus.emit(BusChannels.DispatchStarted, {
5933
+ ...identity,
5934
+ requestOrigin: origin,
5935
+ pid: null,
5936
+ assignmentId: runId,
5937
+ attempt: 0,
5938
+ });
5939
+ context.bus.emit(BusChannels.DispatchCompleted, {
5940
+ ...identity,
5941
+ requestOrigin: origin,
5942
+ outcome: "succeeded",
5943
+ outcomeCode: null,
5944
+ outcomeDetail: null,
5945
+ lineage: input.template.lineage ?? { parentRunId: null, rootRunId: runId, attempt: 0, depth: 0 },
5946
+ tokenCount: 0,
5947
+ inputTokenCount: 0,
5948
+ outputTokenCount: 0,
5949
+ cacheReadTokenCount: 0,
5950
+ cacheWriteTokenCount: 0,
5951
+ reasoningTokenCount: 0,
5952
+ staticShellHash: null,
5953
+ sessionShellHash: null,
5954
+ dynamicHash: null,
5955
+ costUsd: 0,
5956
+ costProvenance: "unknown",
5957
+ durationMs: 0,
5958
+ exitCode: 0,
5959
+ toolActivity: null,
5960
+ });
5961
+ return receipt;
5962
+ }
5963
+
5716
5964
  const extension: DomainExtension = {
5717
5965
  async start() {
5718
5966
  // No in-memory executor survives a process restart, so every active
@@ -5947,6 +6195,7 @@ export function createDispatchBundle(
5947
6195
  protectedArtifactState: () => getProtectedArtifactState(),
5948
6196
  dispatch,
5949
6197
  dispatchBatch,
6198
+ sealCouncilSynthesis,
5950
6199
  listRuns(status) {
5951
6200
  const l = requireLedger();
5952
6201
  return status ? l.list({ status }) : l.list();
@@ -0,0 +1,14 @@
1
+ import { CODE_STEP_EXCERPT_MAX_BYTES } from "./code-step.js";
2
+
3
+ export function gateBaselineFailure(passed: boolean): "gate_not_discriminating" | null {
4
+ return passed ? "gate_not_discriminating" : null;
5
+ }
6
+
7
+ export function gateFailureLines(output: string): string {
8
+ const lines = output
9
+ .split("\n")
10
+ .filter((line) => line.startsWith("FAIL"))
11
+ .join("\n");
12
+ if (Buffer.byteLength(lines, "utf8") <= CODE_STEP_EXCERPT_MAX_BYTES) return lines;
13
+ return Buffer.from(lines, "utf8").subarray(0, CODE_STEP_EXCERPT_MAX_BYTES).toString("utf8");
14
+ }
@@ -42,6 +42,10 @@ export interface FleetPlanAgentContext {
42
42
  attempt: number;
43
43
  /** Set when this node is a loop's verification, which is a gate position. */
44
44
  gateRole?: "reviewer";
45
+ target?: string;
46
+ profile?: string;
47
+ planRole?: true;
48
+ gateAuthorRole?: true;
45
49
  }
46
50
 
47
51
  export interface FleetPlanAgentResolution {
@@ -97,7 +101,7 @@ function declaredLoops(steps: ReadonlyArray<FleetContractStep>): Map<string, Exe
97
101
  }
98
102
  loops.set(step.id, {
99
103
  id: step.id,
100
- checkKind: step.check.kind,
104
+ checkKind: step.check.kind === "gate" ? "code" : step.check.kind,
101
105
  maxAttempts: step.maxAttempts,
102
106
  checkStepIds,
103
107
  repairStepIds,
@@ -127,6 +131,10 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
127
131
  dependencies: ReadonlyArray<string>,
128
132
  writes: ReadonlyArray<string> | undefined,
129
133
  loop?: { loopId: string; role: "check" | "repair"; attempt: number },
134
+ extra?: {
135
+ gate?: { path: string; commandId: string };
136
+ plan?: { roster: ReadonlyArray<string>; maxTasks: number; proposals: boolean };
137
+ },
130
138
  ): ExecutionPlanStepInput => {
131
139
  const resolved = resolveAgent(context);
132
140
  return {
@@ -142,14 +150,49 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
142
150
  executionRole: resolved.executionRole,
143
151
  ...boundary(context.scope, writes),
144
152
  ...(loop !== undefined ? { loop } : {}),
153
+ ...(context.target !== undefined ? { target: context.target } : {}),
154
+ ...(context.profile !== undefined ? { profile: context.profile } : {}),
155
+ ...extra,
145
156
  };
146
157
  };
158
+ const routeOf = (value: { target?: string; profile?: string }): { target?: string; profile?: string } => ({
159
+ ...(value.target !== undefined ? { target: value.target } : {}),
160
+ ...(value.profile !== undefined ? { profile: value.profile } : {}),
161
+ });
147
162
 
148
163
  for (const step of contract.steps) {
149
164
  const dependencies = expandDependencies(step.dependencies, loops);
150
165
  if (step.kind === "agent") {
151
166
  steps.push(
152
- agentNode({ stepId: step.id, agentId: step.agent, scope: step.scope, attempt: 0 }, dependencies, step.writes),
167
+ agentNode(
168
+ { stepId: step.id, agentId: step.agent, scope: step.scope, attempt: 0, ...routeOf(step) },
169
+ dependencies,
170
+ step.writes,
171
+ ),
172
+ );
173
+ continue;
174
+ }
175
+ if (step.kind === "gate") {
176
+ steps.push(
177
+ agentNode(
178
+ { stepId: step.id, agentId: step.agent, scope: "workspace", attempt: 0, gateAuthorRole: true, ...routeOf(step) },
179
+ dependencies,
180
+ [step.path],
181
+ undefined,
182
+ { gate: { path: step.path, commandId: step.run } },
183
+ ),
184
+ );
185
+ continue;
186
+ }
187
+ if (step.kind === "plan") {
188
+ steps.push(
189
+ agentNode(
190
+ { stepId: step.id, agentId: step.agent, scope: step.scope, attempt: 0, planRole: true, ...routeOf(step) },
191
+ dependencies,
192
+ step.writes,
193
+ undefined,
194
+ { plan: { roster: [...step.roster], maxTasks: step.maxTasks, proposals: step.proposals } },
195
+ ),
153
196
  );
154
197
  continue;
155
198
  }
@@ -185,6 +228,21 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
185
228
  // when a workspace step lands after it.
186
229
  verification: true,
187
230
  });
231
+ } else if (step.check.kind === "gate") {
232
+ const gateId = step.check.gate;
233
+ const gate = contract.steps.find((candidate) => candidate.id === gateId);
234
+ if (gate?.kind !== "gate") throw new Error(`fleet plan: gate '${gateId}' failed to resolve`);
235
+ steps.push({
236
+ kind: "code",
237
+ id: checkId,
238
+ commandId: gate.run,
239
+ scope: "readonly",
240
+ dependencies: checkDependencies,
241
+ writes: [],
242
+ loop: membership,
243
+ verification: true,
244
+ gate: { gateId: gate.id, path: gate.path },
245
+ });
188
246
  } else {
189
247
  steps.push(
190
248
  agentNode(
@@ -194,6 +252,7 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
194
252
  scope: step.check.scope,
195
253
  attempt: 0,
196
254
  gateRole: "reviewer",
255
+ ...routeOf(step.check),
197
256
  },
198
257
  checkDependencies,
199
258
  step.check.writes,
@@ -205,7 +264,7 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
205
264
  const repairId = loop.repairStepIds[attempt - 1] ?? "";
206
265
  steps.push(
207
266
  agentNode(
208
- { stepId: repairId, agentId: step.repair.agent, scope: step.repair.scope, attempt },
267
+ { stepId: repairId, agentId: step.repair.agent, scope: step.repair.scope, attempt, ...routeOf(step.repair) },
209
268
  [checkId],
210
269
  step.repair.writes,
211
270
  { loopId: loop.id, role: "repair", attempt },
@@ -218,6 +277,7 @@ export function compileFleetExecutionPlan(input: CompileFleetPlanInput): Executi
218
277
  topology: "fleet",
219
278
  rootTask: task,
220
279
  maxWorkers: contract.maxWorkers,
280
+ ...(contract.writers === 1 ? { writers: 1 as const } : {}),
221
281
  onFailure: contract.onFailure,
222
282
  steps,
223
283
  loops: [...loops.values()],