pi-daddy 0.43.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/CHANGELOG.md +104 -33
  2. package/README.md +27 -5
  3. package/THIRD_PARTY_LICENSES/musl-COPYRIGHT.txt +193 -0
  4. package/THIRD_PARTY_LICENSES/zig-MIT.txt +21 -0
  5. package/THIRD_PARTY_NOTICES.md +9 -0
  6. package/contracts/ledger-record/v1/README.md +40 -25
  7. package/dist/cli.d.ts +2 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +10 -5
  10. package/dist/cli.js.map +1 -1
  11. package/dist/executors/activity-session.js +116 -65
  12. package/dist/executors/activity-session.js.map +1 -1
  13. package/dist/executors/captured-execution.d.ts +13 -0
  14. package/dist/executors/captured-execution.d.ts.map +1 -0
  15. package/dist/executors/captured-execution.js +53 -0
  16. package/dist/executors/captured-execution.js.map +1 -0
  17. package/dist/executors/child-final.d.ts +31 -0
  18. package/dist/executors/child-final.d.ts.map +1 -0
  19. package/dist/executors/child-final.js +197 -0
  20. package/dist/executors/child-final.js.map +1 -0
  21. package/dist/executors/executor.d.ts +2 -1
  22. package/dist/executors/executor.d.ts.map +1 -1
  23. package/dist/executors/executor.js +12 -0
  24. package/dist/executors/executor.js.map +1 -1
  25. package/dist/executors/owned-worker.d.ts +18 -0
  26. package/dist/executors/owned-worker.d.ts.map +1 -0
  27. package/dist/executors/owned-worker.js +295 -0
  28. package/dist/executors/owned-worker.js.map +1 -0
  29. package/dist/executors/worker-artifact.d.ts +3 -0
  30. package/dist/executors/worker-artifact.d.ts.map +1 -0
  31. package/dist/executors/worker-artifact.js +3 -0
  32. package/dist/executors/worker-artifact.js.map +1 -0
  33. package/dist/governance/approval-prompt.d.ts +4 -3
  34. package/dist/governance/approval-prompt.d.ts.map +1 -1
  35. package/dist/governance/approval-prompt.js +13 -6
  36. package/dist/governance/approval-prompt.js.map +1 -1
  37. package/dist/governance/captured-worker-record.d.ts +9 -0
  38. package/dist/governance/captured-worker-record.d.ts.map +1 -0
  39. package/dist/governance/captured-worker-record.js +123 -0
  40. package/dist/governance/captured-worker-record.js.map +1 -0
  41. package/dist/governance/lease-record.d.ts +4 -0
  42. package/dist/governance/lease-record.d.ts.map +1 -1
  43. package/dist/governance/lease-record.js +19 -2
  44. package/dist/governance/lease-record.js.map +1 -1
  45. package/dist/governance/ledger-events.d.ts +5 -5
  46. package/dist/governance/ledger-events.d.ts.map +1 -1
  47. package/dist/governance/ledger-v3-validation.js +1 -1
  48. package/dist/governance/ledger-v3-validation.js.map +1 -1
  49. package/dist/governance/workspace-acceptance.d.ts +3 -1
  50. package/dist/governance/workspace-acceptance.d.ts.map +1 -1
  51. package/dist/governance/workspace-acceptance.js +21 -5
  52. package/dist/governance/workspace-acceptance.js.map +1 -1
  53. package/dist/governance/workspace-lease.d.ts.map +1 -1
  54. package/dist/governance/workspace-lease.js +44 -1
  55. package/dist/governance/workspace-lease.js.map +1 -1
  56. package/dist/kernel/authority-text.d.ts +3 -0
  57. package/dist/kernel/authority-text.d.ts.map +1 -0
  58. package/dist/kernel/authority-text.js +22 -0
  59. package/dist/kernel/authority-text.js.map +1 -0
  60. package/dist/kernel/bounded-read-failures.d.ts +9 -0
  61. package/dist/kernel/bounded-read-failures.d.ts.map +1 -0
  62. package/dist/kernel/bounded-read-failures.js +33 -0
  63. package/dist/kernel/bounded-read-failures.js.map +1 -0
  64. package/dist/kernel/bounded-read-sync.d.ts +24 -0
  65. package/dist/kernel/bounded-read-sync.d.ts.map +1 -0
  66. package/dist/kernel/bounded-read-sync.js +75 -0
  67. package/dist/kernel/bounded-read-sync.js.map +1 -0
  68. package/dist/kernel/bounded-read.d.ts +29 -2
  69. package/dist/kernel/bounded-read.d.ts.map +1 -1
  70. package/dist/kernel/bounded-read.js +98 -9
  71. package/dist/kernel/bounded-read.js.map +1 -1
  72. package/dist/kernel/capacity.d.ts +36 -0
  73. package/dist/kernel/capacity.d.ts.map +1 -0
  74. package/dist/kernel/capacity.js +94 -0
  75. package/dist/kernel/capacity.js.map +1 -0
  76. package/dist/kernel/captured-worker-contract.d.ts +38 -0
  77. package/dist/kernel/captured-worker-contract.d.ts.map +1 -0
  78. package/dist/kernel/captured-worker-contract.js +2 -0
  79. package/dist/kernel/captured-worker-contract.js.map +1 -0
  80. package/dist/kernel/catalog.d.ts +12 -3
  81. package/dist/kernel/catalog.d.ts.map +1 -1
  82. package/dist/kernel/catalog.js +28 -8
  83. package/dist/kernel/catalog.js.map +1 -1
  84. package/dist/kernel/chain.d.ts +1 -0
  85. package/dist/kernel/chain.d.ts.map +1 -1
  86. package/dist/kernel/chain.js.map +1 -1
  87. package/dist/kernel/definitions.d.ts +11 -1
  88. package/dist/kernel/definitions.d.ts.map +1 -1
  89. package/dist/kernel/definitions.js +6 -3
  90. package/dist/kernel/definitions.js.map +1 -1
  91. package/dist/kernel/fanout.d.ts +5 -25
  92. package/dist/kernel/fanout.d.ts.map +1 -1
  93. package/dist/kernel/fanout.js +15 -30
  94. package/dist/kernel/fanout.js.map +1 -1
  95. package/dist/kernel/grant-env.d.ts +0 -2
  96. package/dist/kernel/grant-env.d.ts.map +1 -1
  97. package/dist/kernel/grant-env.js +0 -1
  98. package/dist/kernel/grant-env.js.map +1 -1
  99. package/dist/kernel/model-preflight.d.ts +8 -0
  100. package/dist/kernel/model-preflight.d.ts.map +1 -1
  101. package/dist/kernel/model-preflight.js +23 -0
  102. package/dist/kernel/model-preflight.js.map +1 -1
  103. package/dist/kernel/selected-definitions.d.ts +15 -0
  104. package/dist/kernel/selected-definitions.d.ts.map +1 -0
  105. package/dist/kernel/selected-definitions.js +146 -0
  106. package/dist/kernel/selected-definitions.js.map +1 -0
  107. package/dist/kernel/skill-packages.d.ts +4 -2
  108. package/dist/kernel/skill-packages.d.ts.map +1 -1
  109. package/dist/kernel/skill-packages.js +34 -41
  110. package/dist/kernel/skill-packages.js.map +1 -1
  111. package/dist/kernel/skill-resources.d.ts.map +1 -1
  112. package/dist/kernel/skill-resources.js +8 -11
  113. package/dist/kernel/skill-resources.js.map +1 -1
  114. package/dist/kernel/workspace.d.ts +6 -3
  115. package/dist/kernel/workspace.d.ts.map +1 -1
  116. package/dist/kernel/workspace.js +10 -6
  117. package/dist/kernel/workspace.js.map +1 -1
  118. package/dist/products/dashboard-cli.js +1 -1
  119. package/dist/products/dashboard-cli.js.map +1 -1
  120. package/dist/products/dashboard-render.d.ts +1 -2
  121. package/dist/products/dashboard-render.d.ts.map +1 -1
  122. package/dist/products/dashboard-render.js +3 -1
  123. package/dist/products/dashboard-render.js.map +1 -1
  124. package/dist/products/dashboard-session-client.d.ts +1 -2
  125. package/dist/products/dashboard-session-client.d.ts.map +1 -1
  126. package/dist/products/dashboard-session-client.js.map +1 -1
  127. package/dist/products/episode-report.d.ts +12 -8
  128. package/dist/products/episode-report.d.ts.map +1 -1
  129. package/dist/products/episode-report.js +61 -12
  130. package/dist/products/episode-report.js.map +1 -1
  131. package/extensions/approvals.ts +1 -1
  132. package/extensions/chain-plan.ts +9 -3
  133. package/extensions/context-staging.ts +30 -35
  134. package/extensions/dashboard-session-server.ts +1 -2
  135. package/extensions/definition-describe.ts +44 -0
  136. package/extensions/definition-runtime.ts +89 -26
  137. package/extensions/delegate-chain.ts +273 -256
  138. package/extensions/delegation-activation.ts +1 -1
  139. package/extensions/delegation.ts +41 -53
  140. package/extensions/execute-child.ts +167 -134
  141. package/extensions/fanout-outcome.ts +1 -9
  142. package/extensions/grants-command.ts +2 -9
  143. package/extensions/grants.ts +106 -38
  144. package/extensions/init-command.ts +52 -12
  145. package/extensions/reload-environment.ts +21 -2
  146. package/extensions/retired-inputs.ts +7 -0
  147. package/extensions/run-delegation.ts +212 -265
  148. package/extensions/session-capacity.ts +53 -0
  149. package/extensions/session-environment.ts +5 -2
  150. package/extensions/session-model-prompt.ts +33 -32
  151. package/extensions/session-report.ts +28 -11
  152. package/extensions/session.ts +200 -157
  153. package/extensions/workspace-runtime.ts +2 -1
  154. package/native/README.md +25 -0
  155. package/native/linux-x64/worker +0 -0
  156. package/native/linux-x64/worker.sha256 +1 -0
  157. package/native/provenance.json +42 -0
  158. package/native/toolchain.json +18 -0
  159. package/native/worker.c +209 -0
  160. package/package.json +15 -7
  161. package/src/cli.ts +16 -6
  162. package/src/executors/activity-session.ts +120 -66
  163. package/src/executors/captured-execution.ts +61 -0
  164. package/src/executors/child-final.ts +190 -0
  165. package/src/executors/executor.ts +14 -1
  166. package/src/executors/owned-worker.ts +322 -0
  167. package/src/executors/worker-artifact.ts +2 -0
  168. package/src/governance/approval-prompt.ts +18 -8
  169. package/src/governance/captured-worker-record.ts +138 -0
  170. package/src/governance/lease-record.ts +21 -2
  171. package/src/governance/ledger-events.ts +5 -5
  172. package/src/governance/ledger-v3-validation.ts +1 -1
  173. package/src/governance/workspace-acceptance.ts +20 -4
  174. package/src/governance/workspace-lease.ts +59 -1
  175. package/src/kernel/authority-text.ts +17 -0
  176. package/src/kernel/bounded-read-failures.ts +42 -0
  177. package/src/kernel/bounded-read-sync.ts +87 -0
  178. package/src/kernel/bounded-read.ts +128 -11
  179. package/src/kernel/capacity.ts +123 -0
  180. package/src/kernel/captured-worker-contract.ts +29 -0
  181. package/src/kernel/catalog.ts +45 -16
  182. package/src/kernel/chain.ts +1 -0
  183. package/src/kernel/definitions.ts +11 -2
  184. package/src/kernel/fanout.ts +17 -32
  185. package/src/kernel/grant-env.ts +0 -3
  186. package/src/kernel/model-preflight.ts +29 -0
  187. package/src/kernel/selected-definitions.ts +156 -0
  188. package/src/kernel/skill-packages.ts +40 -38
  189. package/src/kernel/skill-resources.ts +8 -9
  190. package/src/kernel/workspace.ts +12 -5
  191. package/src/products/dashboard-cli.ts +1 -1
  192. package/src/products/dashboard-render.ts +6 -2
  193. package/src/products/dashboard-session-client.ts +1 -2
  194. package/src/products/episode-report.ts +80 -17
  195. package/dist/advisors/advisor.d.ts +0 -90
  196. package/dist/advisors/advisor.d.ts.map +0 -1
  197. package/dist/advisors/advisor.js +0 -154
  198. package/dist/advisors/advisor.js.map +0 -1
  199. package/dist/advisors/decider.d.ts +0 -88
  200. package/dist/advisors/decider.d.ts.map +0 -1
  201. package/dist/advisors/decider.js +0 -29
  202. package/dist/advisors/decider.js.map +0 -1
  203. package/dist/advisors/jev.d.ts +0 -41
  204. package/dist/advisors/jev.d.ts.map +0 -1
  205. package/dist/advisors/jev.js +0 -139
  206. package/dist/advisors/jev.js.map +0 -1
  207. package/dist/advisors/settings.d.ts +0 -49
  208. package/dist/advisors/settings.d.ts.map +0 -1
  209. package/dist/advisors/settings.js +0 -99
  210. package/dist/advisors/settings.js.map +0 -1
  211. package/dist/governance/episode-cost-gate.d.ts +0 -31
  212. package/dist/governance/episode-cost-gate.d.ts.map +0 -1
  213. package/dist/governance/episode-cost-gate.js +0 -112
  214. package/dist/governance/episode-cost-gate.js.map +0 -1
  215. package/extensions/advisor-session.ts +0 -67
  216. package/extensions/effort-advice.ts +0 -112
  217. package/extensions/pruning-advice.ts +0 -133
  218. package/src/advisors/advisor.ts +0 -246
  219. package/src/advisors/decider.ts +0 -74
  220. package/src/advisors/jev.ts +0 -163
  221. package/src/advisors/settings.ts +0 -115
  222. package/src/governance/episode-cost-gate.ts +0 -128
@@ -1,3 +1,5 @@
1
+ import { reconcileDelegationCapacity, reserveDelegationCapacity } from "./session-capacity.ts";
2
+ import { registerDefinitionDescribe } from "./definition-describe.ts";
1
3
  /**
2
4
  * Governed delegation, as pi sees it: the `delegate` and `delegate_all` tool registrations.
3
5
  *
@@ -37,7 +39,7 @@ import { newDelegationOccurrence } from "./execution-occurrence.ts";
37
39
  import { correlationShape as buildCorrelationShape } from "./correlation-shape.ts";
38
40
  import { assertDelegationAuthority } from "./delegation-authority.ts";
39
41
  import { contextShape } from "./context-shape.ts";
40
- import { ensureSessionModelPrompt } from "./session-model-prompt.ts";
42
+ import { withoutRetiredDelegationArguments } from "./retired-inputs.ts";
41
43
 
42
44
  /**
43
45
  * Wire a set of children to pi's partial-result channel — ADR-0032.
@@ -186,6 +188,9 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
186
188
  ),
187
189
  );
188
190
  const childShape = Type.Object({
191
+ definitionId: Type.Optional(
192
+ Type.String({ description: "Snapshot id returned by delegate_describe; required for Principal phases." }),
193
+ ),
189
194
  task: Type.String({ description: "The task for this sub-agent. It receives only this." }),
190
195
  agent: Type.Optional(Type.String({ description: describeAgent(spawnable()) })),
191
196
  tools: Type.Optional(Type.Array(Type.String(), { description: "Capabilities, when no 'agent' fits." })),
@@ -202,12 +207,12 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
202
207
  maxItems: MAX_CHILDREN_PER_CALL,
203
208
  description: "The sub-agents to run concurrently. Each is independent and unaware of the others.",
204
209
  }),
205
- episodeCostCeiling: Type.Optional(
206
- Type.Number({ exclusiveMinimum: 0, description: "Override the USD ceiling for this whole episode." }),
207
- ),
208
210
  });
209
211
 
210
212
  const delegateParams = Type.Object({
213
+ definitionId: Type.Optional(
214
+ Type.String({ description: "Snapshot id returned by delegate_describe; required for Principal phases." }),
215
+ ),
211
216
  task: Type.String({ description: "The task for the sub-agent. It receives only this." }),
212
217
  agent: Type.Optional(Type.String({ description: describeAgent(spawnable()) })),
213
218
  tools: Type.Optional(
@@ -227,9 +232,6 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
227
232
  }),
228
233
  ),
229
234
  thinking: thinkingShape,
230
- episodeCostCeiling: Type.Optional(
231
- Type.Number({ exclusiveMinimum: 0, description: "Override the USD ceiling for this whole episode." }),
232
- ),
233
235
  context: Type.Optional(contextShape()),
234
236
  correlation: Type.Optional(correlationShape),
235
237
  workspace: Type.Optional(workspaceShape),
@@ -244,60 +246,54 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
244
246
  "written by the operator. Use 'tools' only when no definition fits. Grant 'delegate' if the " +
245
247
  "sub-agent must itself delegate further; withhold it to make the sub-agent a leaf.",
246
248
  parameters: delegateParams,
249
+ prepareArguments: withoutRetiredDelegationArguments,
247
250
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
248
251
  assertDelegationAuthority(session);
249
- await ensureSessionModelPrompt(session, params.agent ? [params.agent] : [], {
250
- hasUI: ctx.hasUI && typeof ctx.ui.input === "function",
251
- input: (title, placeholder) => ctx.ui.input(title, placeholder),
252
- notify: (message, type) => ctx.ui.notify(message, type),
253
- });
254
252
  // ADR-0032: one child, same block. `_onUpdate` was discarded here, so a delegation showed the bare word
255
253
  // `delegate` for up to DEFAULT_TIMEOUT_MS — sixty minutes by default.
256
254
  const progress = progressReporter(session, [params.agent ?? "delegate"], onUpdate as never);
257
255
  const outcome = await runOneDelegation(
258
256
  session,
259
257
  {
258
+ definitionId: params.definitionId,
260
259
  task: params.task,
261
260
  agent: params.agent,
262
261
  tools: params.tools,
263
262
  model: params.model,
264
263
  thinking: params.thinking,
265
- episodeCostCeiling: params.episodeCostCeiling,
266
264
  context: params.context,
267
265
  correlation: params.correlation,
268
266
  workspace: params.workspace,
269
267
  },
270
268
  newDelegationOccurrence(session, 0),
271
- // A single blocking delegation spends nothing from the subtree budget: cardinality is already
272
- // bounded to one by the call being blocking, which is the accident fan-out removes. Passing the
273
- // budget through unchanged means a child can still fan out with what this session was given.
274
- session.fanoutBudget,
269
+ // Reserve one actual child plus the currently available disjoint subtree allowance.
270
+ undefined,
275
271
  ctx,
276
272
  signal,
277
273
  { onProgress: progress.sink(0), toolCallId: _toolCallId },
278
274
  );
279
275
  progress.settle([outcome]);
280
276
 
281
- if (!outcome.ok) {
282
- if (isCriticalAssuranceBlock(outcome)) throw new Error(outcome.text);
283
- // THROW, do not return. `AgentToolResult` has no `isError` field: pi sets it only when `execute`
284
- // throws (`pi-agent-core/dist/agent-loop.js` — a normal return is hardcoded `isError: false`).
285
- // Returning `isError: true` was silently discarded, so every refusal this package made was
286
- // recorded by pi as a SUCCESSFUL tool call. Found by the integration suite on its first run.
287
- const detail = outcome.text ? `\n\n${outcome.text}` : "";
288
- const message = `delegation refused: ${outcome.reason}${detail}`;
277
+ if (isCriticalAssuranceBlock(outcome)) throw new Error(outcome.text);
278
+ // A pre-launch refusal has no executed work/final to preserve; keep its established typed refusal.
279
+ if (!outcome.ok && !outcome.work && !outcome.final && !outcome.cleanup) {
280
+ const message = `delegation refused: ${outcome.reason}`;
289
281
  if (outcome.refusal) throw new GovernanceRefusal({ ...outcome.refusal, message });
290
282
  throw new Error(message);
291
283
  }
292
-
284
+ // Pi 1.0.4 preserves returned isError. Keep every outcome dimension and useful output together.
285
+ const failed = !outcome.ok || outcome.control === "failed";
293
286
  return {
294
- content: [{ type: "text", text: outcome.text || "(no output)" }],
295
- details: {
296
- granted: outcome.granted,
297
- depth: outcome.depth,
298
- exitCode: outcome.exitCode,
299
- retention: outcome.retention,
300
- },
287
+ isError: failed,
288
+ content: [
289
+ {
290
+ type: "text",
291
+ text: failed
292
+ ? `delegation failed: ${outcome.reason ?? "required control failed"}${outcome.text ? `\n\n${outcome.text}` : ""}`
293
+ : outcome.text || "(no output)",
294
+ },
295
+ ],
296
+ details: outcome,
301
297
  };
302
298
  },
303
299
  });
@@ -323,32 +319,24 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
323
319
  description:
324
320
  "Run several sub-agents CONCURRENTLY and return all their results. Each child is governed exactly " +
325
321
  "as with `delegate`: it holds only what you grant it, and you cannot grant what you do not hold. " +
326
- `At most ${MAX_CHILDREN_PER_CALL} children per call, and a session-wide budget bounds the total ` +
322
+ `At most ${MAX_CHILDREN_PER_CALL} children per call, and shared capacity bounds active descendants ` +
327
323
  "across the whole delegation subtree. Children cannot see each other or share context. Use this " +
328
324
  "when independent tasks can proceed in parallel — several reviewers over one diff, say — and read " +
329
325
  "every child's outcome, because one can be refused while the others succeed.",
330
326
  parameters: delegateAllParams,
327
+ prepareArguments: withoutRetiredDelegationArguments,
331
328
  async execute(_toolCallId, params, signal, onUpdate, ctx) {
332
329
  assertDelegationAuthority(session);
333
330
  const children = params.children ?? [];
334
- await ensureSessionModelPrompt(
335
- session,
336
- children.flatMap((child) => (child.agent ? [child.agent] : [])),
337
- {
338
- hasUI: ctx.hasUI && typeof ctx.ui.input === "function",
339
- input: (title, placeholder) => ctx.ui.input(title, placeholder),
340
- notify: (message, type) => ctx.ui.notify(message, type),
341
- },
342
- );
343
- const split = splitBudget(session.fanoutBudget, children.length);
331
+ if (session.capacityRefusal) throw new GovernanceRefusal(session.capacityRefusal);
332
+ await reconcileDelegationCapacity(session);
333
+ const split = splitBudget(session.capacity.available, children.length);
344
334
  if (!split.ok) {
345
335
  // Thrown, not returned: a returned `isError` is discarded by pi, so a refusal that came back as a
346
336
  // normal result would read to the orchestrator as a successful fan-out of zero children.
347
337
  throw new GovernanceRefusal(refusal("FANOUT_EXCEEDED", `fan-out refused: ${split.reason}`));
348
338
  }
349
339
 
350
- if (params.episodeCostCeiling !== undefined) session.episodeCostGate.setCeiling(params.episodeCostCeiling);
351
-
352
340
  // ADR-0032: ONE status block covering every child. `onUpdate` replaces the tool's rendered result, so a
353
341
  // painter per child would have each overwriting the others.
354
342
  const progress = progressReporter(
@@ -369,6 +357,8 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
369
357
  const pending = children.map(async (child, index): Promise<DelegationOutcome> => {
370
358
  try {
371
359
  return await runOneDelegation(session, child, occurrences[index], split.perChild, ctx, signal, {
360
+ // Reserve each batch share synchronously here, before another dispatch can interleave.
361
+ capacityReservation: reserveDelegationCapacity(session, occurrences[index].executionId, split.perChild),
372
362
  onProgress: progress.sink(index),
373
363
  toolCallId: _toolCallId,
374
364
  });
@@ -390,17 +380,14 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
390
380
  // be indistinguishable from an empty one, and a fan-out that hid its refusals would let an
391
381
  // orchestrator summarise four reviews when only three happened.
392
382
  const report = buildFanoutReport(outcomes, children);
393
-
394
- if (failed.length === children.length) {
395
- // All of them failed, so there is no partial result to hand back — and a tool that returns text
396
- // when nothing ran is exactly how a wrong summary gets written.
397
- const message = `fan-out failed: every child was refused or failed.\n\n${report}`;
398
- throw totalFanoutFailure(failed, message);
399
- }
383
+ if (failed.length === children.length && outcomes.every((o) => !o.work && !o.final && !o.cleanup))
384
+ throw totalFanoutFailure(failed, `fan-out failed: every child was refused or failed.\n\n${report}`);
400
385
 
401
386
  return {
387
+ isError: outcomes.some((o) => !o.ok || o.control === "failed"),
402
388
  content: [{ type: "text", text: report }],
403
389
  details: {
390
+ outcomes,
404
391
  children: outcomes.length,
405
392
  failed: failed.length,
406
393
  budgetPerChild: split.perChild,
@@ -415,6 +402,7 @@ export function registerDelegationTools(pi: ExtensionAPI, session: GrantsSession
415
402
  // ADR-0033. Registered here so all three tools appear together and share the `mayDelegate` guard, but its logic
416
403
  // lives in its own file: `delegate` and `delegate_all` differ only in cardinality, while a chain differs in
417
404
  // composition, and this file is near the 400-line ceiling.
405
+ registerDefinitionDescribe(pi, session);
418
406
  registerChainTool(pi, session);
419
407
 
420
408
  return {