@sema-agent/core 7.5.1 → 7.6.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 (132) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/dist/agents/cascade.d.ts +2 -2
  3. package/dist/agents/cascade.js +12 -10
  4. package/dist/agents/repair-loop.d.ts +5 -3
  5. package/dist/agents/repair-loop.js +13 -15
  6. package/dist/agents/subagent.d.ts +24 -42
  7. package/dist/agents/subagent.js +119 -105
  8. package/dist/agents/suspend-guard.d.ts +31 -19
  9. package/dist/agents/suspend-guard.js +14 -8
  10. package/dist/agents/teacher.js +9 -9
  11. package/dist/agents/team.d.ts +4 -3
  12. package/dist/agents/team.js +10 -8
  13. package/dist/agents/verify.d.ts +3 -3
  14. package/dist/agents/verify.js +17 -17
  15. package/dist/core/a2a.js +2 -1
  16. package/dist/core/ask-origin.d.ts +60 -7
  17. package/dist/core/ask-origin.js +26 -1
  18. package/dist/core/checkpoint-store.d.ts +78 -76
  19. package/dist/core/checkpoint-store.js +17 -1
  20. package/dist/core/gate-outcome.d.ts +189 -0
  21. package/dist/core/gate-outcome.js +70 -0
  22. package/dist/core/hooks.d.ts +18 -92
  23. package/dist/core/hooks.js +88 -85
  24. package/dist/core/mcp-failure.d.ts +104 -0
  25. package/dist/core/mcp-failure.js +128 -0
  26. package/dist/core/mcp.d.ts +21 -77
  27. package/dist/core/mcp.js +76 -150
  28. package/dist/core/pause-registry.d.ts +131 -0
  29. package/dist/core/pause-registry.js +27 -0
  30. package/dist/core/protocol-table.d.ts +5 -0
  31. package/dist/core/protocol-table.js +1 -0
  32. package/dist/core/runner/abort-race.d.ts +41 -0
  33. package/dist/core/runner/abort-race.js +38 -0
  34. package/dist/core/runner/assemble-result.d.ts +32 -41
  35. package/dist/core/runner/assemble-result.js +55 -74
  36. package/dist/core/runner/checkpoint-scope.d.ts +15 -3
  37. package/dist/core/runner/checkpoint-scope.js +3 -0
  38. package/dist/core/runner/compaction-call-options.d.ts +1 -1
  39. package/dist/core/runner/content-ask-bindings.d.ts +27 -0
  40. package/dist/core/runner/content-ask-bindings.js +1 -0
  41. package/dist/core/runner/contracts.d.ts +144 -50
  42. package/dist/core/runner/denial-limit-arms.d.ts +24 -1
  43. package/dist/core/runner/denial-limit-arms.js +24 -3
  44. package/dist/core/runner/gate-exit.d.ts +74 -0
  45. package/dist/core/runner/gate-exit.js +55 -0
  46. package/dist/core/runner/inherited-ask-grants.d.ts +46 -0
  47. package/dist/core/runner/inherited-ask-grants.js +29 -0
  48. package/dist/core/runner/park-commit.d.ts +102 -0
  49. package/dist/core/runner/park-commit.js +31 -0
  50. package/dist/core/runner/{prepare-permission-rules.d.ts → permission-rule-lanes.d.ts} +109 -3
  51. package/dist/core/runner/{prepare-permission-rules.js → permission-rule-lanes.js} +47 -1
  52. package/dist/core/runner/prepare-ask-lane.d.ts +107 -0
  53. package/dist/core/runner/prepare-ask-lane.js +131 -0
  54. package/dist/core/runner/prepare-boundary-parks.d.ts +102 -0
  55. package/dist/core/runner/prepare-boundary-parks.js +169 -0
  56. package/dist/core/runner/prepare-caps-and-workflow.js +1 -1
  57. package/dist/core/runner/prepare-context-lane.d.ts +119 -0
  58. package/dist/core/runner/prepare-context-lane.js +230 -0
  59. package/dist/core/runner/prepare-gate-stations.d.ts +174 -0
  60. package/dist/core/runner/prepare-gate-stations.js +265 -0
  61. package/dist/core/runner/prepare-hands-readface.d.ts +4 -4
  62. package/dist/core/runner/prepare-inherited-gate.d.ts +4 -4
  63. package/dist/core/runner/prepare-inherited-gate.js +1 -1
  64. package/dist/core/runner/prepare-memory-engine-session.d.ts +84 -0
  65. package/dist/core/runner/prepare-memory-engine-session.js +233 -0
  66. package/dist/core/runner/prepare-memory.d.ts +44 -26
  67. package/dist/core/runner/prepare-park-ask.d.ts +162 -0
  68. package/dist/core/runner/prepare-park-ask.js +377 -0
  69. package/dist/core/runner/prepare-policy-chain.d.ts +208 -0
  70. package/dist/core/runner/prepare-policy-chain.js +584 -0
  71. package/dist/core/runner/prepare-project-context.d.ts +1 -13
  72. package/dist/core/runner/prepare-project-context.js +1 -3
  73. package/dist/core/runner/prepare-prompt-assembly.d.ts +95 -0
  74. package/dist/core/runner/prepare-prompt-assembly.js +162 -0
  75. package/dist/core/runner/prepare-prompt-inputs.d.ts +1 -20
  76. package/dist/core/runner/prepare-protocol-tools.d.ts +3 -3
  77. package/dist/core/runner/prepare-protocol-tools.js +0 -3
  78. package/dist/core/runner/prepare-question-face.d.ts +3 -21
  79. package/dist/core/runner/prepare-question-face.js +2 -1
  80. package/dist/core/runner/prepare-safety-scan.d.ts +0 -5
  81. package/dist/core/runner/prepare-safety-scan.js +1 -2
  82. package/dist/core/runner/prepare-suspend-saga.d.ts +170 -0
  83. package/dist/core/runner/prepare-suspend-saga.js +308 -0
  84. package/dist/core/runner/prepare-task.d.ts +9 -136
  85. package/dist/core/runner/prepare-task.js +47 -2745
  86. package/dist/core/runner/prepare-turn-wiring.d.ts +154 -0
  87. package/dist/core/runner/prepare-turn-wiring.js +201 -0
  88. package/dist/core/runner/prepare-wiring-manifest.d.ts +17 -17
  89. package/dist/core/runner/prepare-wiring-manifest.js +16 -10
  90. package/dist/core/runner/prepare-workspace-restore.d.ts +2 -29
  91. package/dist/core/runner/prepare-workspace-restore.js +3 -16
  92. package/dist/core/runner/prompt-hash-salt.d.ts +1 -0
  93. package/dist/core/runner/prompt-hash-salt.js +2 -0
  94. package/dist/core/runner/remote-env-retry.d.ts +29 -0
  95. package/dist/core/runner/remote-env-retry.js +16 -0
  96. package/dist/core/runner/runtask.d.ts +16 -31
  97. package/dist/core/runner/runtask.js +110 -121
  98. package/dist/core/runner/terminal-projection.d.ts +22 -0
  99. package/dist/core/runner/terminal-projection.js +28 -0
  100. package/dist/core/session.d.ts +12 -0
  101. package/dist/core/session.js +3 -0
  102. package/dist/core/store-contracts/checkpoint-store-contract.d.ts +4 -1
  103. package/dist/core/store-contracts/checkpoint-store-contract.js +8 -2
  104. package/dist/core/terminal-cause.d.ts +137 -0
  105. package/dist/core/terminal-cause.js +9 -0
  106. package/dist/core/tool-policy.d.ts +43 -139
  107. package/dist/core/tool-policy.js +79 -112
  108. package/dist/core/types.d.ts +67 -164
  109. package/dist/core/wiring-manifest.d.ts +6 -3
  110. package/dist/core/workflow-journal-store.js +3 -4
  111. package/dist/engine/harness/agent-harness.d.ts +1 -1
  112. package/dist/index.d.ts +10 -7
  113. package/dist/index.js +8 -5
  114. package/dist/orchestration/builtin-workflows.d.ts +2 -2
  115. package/dist/orchestration/builtin-workflows.js +1 -1
  116. package/dist/orchestration/goal.js +8 -7
  117. package/dist/orchestration/run-spec.js +5 -3
  118. package/dist/orchestration/run-workflow-tool.d.ts +1 -1
  119. package/dist/orchestration/run-workflow-tool.js +4 -4
  120. package/dist/orchestration/workflow-governance.d.ts +4 -4
  121. package/dist/orchestration/workflow-governance.js +4 -2
  122. package/dist/orchestration/workflow-primitives.d.ts +1 -1
  123. package/dist/orchestration/workflow-primitives.js +1 -1
  124. package/dist/orchestration/workflow.d.ts +11 -0
  125. package/dist/orchestration/workflow.js +64 -39
  126. package/dist/prompts/supervisor.d.ts +1 -1
  127. package/dist/prompts/supervisor.js +3 -3
  128. package/dist/scenarios/scenario-registry.js +1 -1
  129. package/package.json +3 -1
  130. package/test/export-surface.snapshot.json +74 -22
  131. /package/dist/core/runner/{prepare-announce-once.d.ts → announce-once-ledger.d.ts} +0 -0
  132. /package/dist/core/runner/{prepare-announce-once.js → announce-once-ledger.js} +0 -0
@@ -14,7 +14,8 @@ import { resolveToolSubset, toolNameAllowed } from "./tool-filter.js";
14
14
  import { builtinAgentDefinitions } from "./builtin-agents.js";
15
15
  import { SUBAGENT_PROMPT } from "../prompts/default.js";
16
16
  import { hasSessionFork } from "../core/session.js";
17
- import { isDurablePause } from "./suspend-guard.js";
17
+ import { isDurablePause, pauseFamily } from "./suspend-guard.js";
18
+ import { terminalProjection } from "../core/runner/terminal-projection.js";
18
19
  import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
19
20
  import { createSafeNotifier } from "../core/safe-notify.js";
20
21
  import { boundedRedactedSummary } from "../core/untrusted-egress.js";
@@ -23,7 +24,6 @@ import { addWorktree } from "../core/git-worktree-env.js";
23
24
  import { shellQuote } from "../tools/fs/search.js";
24
25
  import { BG_AGENT_REAP_STOP_ERROR } from "../core/task-registry.js";
25
26
  import { readDurableOrgAdmission } from "../core/memory-admission.js";
26
- import { extractErrorCode } from "../brain/errors.js";
27
27
  import { governanceRetryClass } from "../core/governance-codes.js";
28
28
  import { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX, RUNNING_AGENT_OBSERVE_EVERY_BEATS, DELEGATION_MAX_CONCURRENT_DEFAULT, DELEGATION_MAX_PER_SESSION_DEFAULT } from "../config/defaults.js";
29
29
  function rollupDelegatedCost(stats, nested) {
@@ -349,39 +349,54 @@ function configError(message, code) {
349
349
  e.code = code;
350
350
  return e;
351
351
  }
352
- export function classifySubagentError(child) {
353
- if (child.status !== "failed")
352
+ export const SUBAGENT_ERROR_KINDS = ["rate_limit", "timeout", "network", "logic", "governance", "unknown"];
353
+ const SUBAGENT_ERROR_KIND_BY_CODE = {
354
+ rate_limit: "rate_limit",
355
+ server: "network",
356
+ network: "network",
357
+ "limits.max_walltime_exceeded": "timeout",
358
+ };
359
+ const SUBAGENT_RETRYABLE = {
360
+ rate_limit: true,
361
+ timeout: true,
362
+ network: true,
363
+ logic: false,
364
+ governance: false,
365
+ unknown: false,
366
+ };
367
+ export function classifySubagentError(terminal) {
368
+ if (terminal.kind !== "failed")
354
369
  return undefined;
355
- const code = child.errorCode ?? extractErrorCode(child.errorMessage);
356
- const msg = (child.errorMessage ?? "").toLowerCase();
357
- const governanceClass = code !== undefined ? governanceRetryClass(code) : undefined;
358
- const errorKind = code === "rate_limit"
359
- ? "rate_limit"
360
- : code === "server" && (msg.includes("overloaded") || msg.includes("529"))
361
- ? "overloaded"
362
- : code === "limits.max_walltime_exceeded" || ((code === "network" || code === "server") && msg.includes("timeout"))
363
- ? "timeout"
364
- : code === "network" || code === "server"
365
- ? "network"
366
- : governanceClass !== undefined
367
- ? "governance"
368
- : "logic";
369
- return { errorKind, retryable: errorKind === "governance" ? governanceClass === "transient" : errorKind !== "logic" };
370
+ const code = terminal.code;
371
+ if (code === undefined)
372
+ return { errorKind: "unknown", retryable: SUBAGENT_RETRYABLE.unknown };
373
+ const own = SUBAGENT_ERROR_KIND_BY_CODE[code];
374
+ if (own !== undefined)
375
+ return { errorKind: own, retryable: SUBAGENT_RETRYABLE[own] };
376
+ const governanceClass = governanceRetryClass(code);
377
+ if (governanceClass !== undefined)
378
+ return { errorKind: "governance", retryable: governanceClass === "transient" };
379
+ return { errorKind: "logic", retryable: SUBAGENT_RETRYABLE.logic };
380
+ }
381
+ function thrownErrorCode(e) {
382
+ const code = e !== null && typeof e === "object" ? e.code : undefined;
383
+ return typeof code === "string" ? code : undefined;
370
384
  }
371
385
  export function completedAgentCard(child, extras) {
386
+ const face = terminalProjection(child.terminal);
372
387
  return {
373
388
  type: "agent",
374
389
  ...(extras.attestation !== undefined ? { attestation: extras.attestation } : {}),
375
390
  ...(extras.subagentType !== undefined ? { subagent_type: extras.subagentType } : {}),
376
- status: child.status,
391
+ status: face.status,
377
392
  taskId: child.taskId,
378
393
  sessionId: child.sessionId,
379
394
  result: child.result,
380
395
  ...(child.salvagedOutput !== undefined ? { salvagedOutput: child.salvagedOutput } : {}),
381
396
  ...(child.structuredOutput !== undefined ? { structuredOutput: child.structuredOutput } : {}),
382
- ...(child.blockedReason !== undefined ? { blockedReason: child.blockedReason } : {}),
383
- ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}),
384
- ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}),
397
+ ...(face.blockedReason !== undefined ? { blockedReason: face.blockedReason } : {}),
398
+ ...(face.errorMessage !== undefined ? { errorMessage: face.errorMessage } : {}),
399
+ ...(face.errorCode !== undefined ? { errorCode: face.errorCode } : {}),
385
400
  ...providerFaultField(child),
386
401
  ...(child.retryAfterMs !== undefined ? { retryAfterMs: child.retryAfterMs } : {}),
387
402
  ...(child.degraded !== undefined ? { degraded: child.degraded } : {}),
@@ -397,16 +412,15 @@ export function completedAgentCard(child, extras) {
397
412
  export const SUBAGENT_SUSPENDED_AWAITING_APPROVAL = "suspended.awaiting_approval";
398
413
  export const SUBAGENT_SUSPENDED_NEEDS_REVIEW = "suspended.needs_review";
399
414
  export const SUBAGENT_SUSPENDED_CHECKPOINT_EXPIRED = "suspended.checkpoint_expired";
400
- function durablePauseDelegationReport(reportLabel, child) {
401
- const needsReview = child.status === "needs_review";
402
- const token = child.checkpointToken;
415
+ function durablePauseDelegationReport(reportLabel, paused) {
416
+ const needsReview = pauseFamily(paused) === "needs_review";
417
+ const token = paused.token;
403
418
  return {
404
419
  isError: true,
405
420
  content: `[Sub-agent report${reportLabel}]\n` +
406
421
  `status: suspended\n` +
407
422
  `This delegation is PARKED at an approval gate — it did not fail, and it is not finished. ` +
408
- `${needsReview ? "A human review" : "An approval"} it cannot grant itself is pending` +
409
- `${token !== undefined ? `, held by checkpoint ${token}` : " (the run reported no checkpoint token)"}. ` +
423
+ `${needsReview ? "A human review" : "An approval"} it cannot grant itself is pending, held by checkpoint ${token}. ` +
410
424
  `The gated action has NOT run.\n` +
411
425
  `Only the deployment's approval channel can release it: you cannot approve it from here, and no ` +
412
426
  `result from it will arrive in this turn. Do not re-issue the same approval-gated action — an ` +
@@ -414,7 +428,7 @@ function durablePauseDelegationReport(reportLabel, child) {
414
428
  `this delegation, or report that it is awaiting approval.`,
415
429
  details: {
416
430
  error: needsReview ? SUBAGENT_SUSPENDED_NEEDS_REVIEW : SUBAGENT_SUSPENDED_AWAITING_APPROVAL,
417
- ...(token !== undefined ? { checkpointToken: token } : {}),
431
+ checkpointToken: token,
418
432
  },
419
433
  };
420
434
  }
@@ -698,7 +712,8 @@ export function createSubagentResume(deps) {
698
712
  for await (const _ of stream)
699
713
  void _;
700
714
  const child = await stream.result();
701
- let status = child.status === "completed" ? "completed" : abort.signal.aborted ? "killed" : "failed";
715
+ const childFace = terminalProjection(child.terminal);
716
+ let status = child.terminal.kind === "completed" ? "completed" : abort.signal.aborted ? "killed" : "failed";
702
717
  const resumeHandbackWarning = await reviewHandback({
703
718
  review: deps.currentAutoModeReview,
704
719
  toolCallId: deps.parentToolCallId,
@@ -707,16 +722,14 @@ export function createSubagentResume(deps) {
707
722
  signal: abort.signal,
708
723
  });
709
724
  const resumeHandbackResult = withHandbackWarning(resumeHandbackWarning, child.result);
710
- const errCodeRevive = status === "failed" ? (child.errorCode ?? extractErrorCode(child.errorMessage)) : undefined;
711
- const errClassRevive = status === "failed"
712
- ? classifySubagentError({ status: "failed", ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}), ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}) })
713
- : undefined;
725
+ const errCodeRevive = status === "failed" ? childFace.errorCode : undefined;
726
+ const errClassRevive = status === "failed" ? classifySubagentError({ kind: "failed", ...(childFace.errorCode !== undefined ? { code: childFace.errorCode } : {}) }) : undefined;
714
727
  if (reviveCycle !== undefined && deps.registry !== undefined && deps.taskId !== undefined) {
715
728
  try {
716
729
  const winner = deps.registry.settleRevivedAgent(deps.taskId, reviveCycle, {
717
730
  status,
718
731
  ...resultSettleFields(resumeHandbackResult),
719
- ...(status !== "completed" ? { error: (child.errorMessage ?? `resumed run ${status}`).slice(0, 500) } : {}),
732
+ ...(status !== "completed" ? { error: (childFace.errorMessage ?? `resumed run ${status}`).slice(0, 500) } : {}),
720
733
  ...(errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
721
734
  ...(errClassRevive !== undefined ? { retryable: errClassRevive.retryable, errorKind: errClassRevive.errorKind } : {}),
722
735
  });
@@ -729,11 +742,11 @@ export function createSubagentResume(deps) {
729
742
  const stoppedByRevive = status === "killed" ? deps.registry?.getStopAttribution(deps.taskId ?? "") ?? "system" : undefined;
730
743
  const completionIdRevive = deps.registry?.getCompletionId(deps.taskId ?? "");
731
744
  const reviveName = deps.rowDescription ?? `sub-agent ${marker}`;
732
- const failReasonRevive = status === "failed" ? child.errorMessage : undefined;
745
+ const failReasonRevive = status === "failed" ? childFace.errorMessage : undefined;
733
746
  const reviveElapsedMs = Date.now() - reviveStartedAt;
734
747
  const reviveTerminalSummary = failReasonRevive !== undefined
735
748
  ? `Agent "${reviveName}" (resumed) failed: ${failReasonRevive}${ccElapsedTag(reviveElapsedMs)}`.slice(0, 300) + errorKindClause(errClassRevive)
736
- : `Agent "${reviveName}" (resumed) ${status === "killed" ? "stopped" : child.status === "completed" ? "finished" : String(child.status)}${ccElapsedTag(reviveElapsedMs)}`;
749
+ : `Agent "${reviveName}" (resumed) ${status === "killed" ? "stopped" : child.terminal.kind === "completed" ? "finished" : childFace.status}${ccElapsedTag(reviveElapsedMs)}`;
737
750
  const reviveDurableProbe = deps.registry !== undefined && deps.taskId !== undefined ? deps.registry.durableAgentRowProbe(deps.taskId) : undefined;
738
751
  const reviveDurableRow = reviveDurableProbe !== undefined ? await reviveDurableProbe().catch(() => false) : false;
739
752
  const resumableRevive = resumabilityClaim({
@@ -805,8 +818,8 @@ export function createSubagentResume(deps) {
805
818
  }
806
819
  catch (e) {
807
820
  const msgRevive = e instanceof Error ? e.message : String(e);
808
- const errCodeReviveReject = abort.signal.aborted ? undefined : extractErrorCode(msgRevive);
809
- const errClassReviveReject = abort.signal.aborted ? undefined : classifySubagentError({ status: "failed", errorMessage: msgRevive });
821
+ const errCodeReviveReject = abort.signal.aborted ? undefined : thrownErrorCode(e);
822
+ const errClassReviveReject = abort.signal.aborted ? undefined : classifySubagentError({ kind: "failed", ...(errCodeReviveReject !== undefined ? { code: errCodeReviveReject } : {}) });
810
823
  if (reviveCycle !== undefined && deps.registry !== undefined && deps.taskId !== undefined) {
811
824
  try {
812
825
  deps.registry.settleRevivedAgent(deps.taskId, reviveCycle, {
@@ -1896,11 +1909,11 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
1896
1909
  if (!isObserverTaskId(sid))
1897
1910
  throw new ObserverStoppedByUserError("observer identity revoked — delivery refused");
1898
1911
  const r = await opts.runner.runTask(spec, ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : undefined);
1899
- if (r.status !== "completed") {
1900
- if (r.errorCode === "resume.session_not_found") {
1901
- throw new ObserverResumeStateError(`observer session no longer exists${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
1902
- }
1903
- throw new Error(`observer run ended ${r.status}${r.errorMessage ? `: ${r.errorMessage}` : ""}`);
1912
+ if (r.terminal.kind !== "completed") {
1913
+ const failedText = r.terminal.kind === "failed" && r.terminal.message ? `: ${r.terminal.message}` : "";
1914
+ if (r.terminal.kind === "failed" && r.terminal.code === "resume.session_not_found")
1915
+ throw new ObserverResumeStateError(`observer session no longer exists${failedText}`);
1916
+ throw new Error(`observer run ended ${terminalProjection(r.terminal).status}${failedText}`);
1904
1917
  }
1905
1918
  };
1906
1919
  const spawner = {
@@ -2047,7 +2060,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2047
2060
  });
2048
2061
  const provenanceRequest = ctx.delegationProvenanceForChildren?.();
2049
2062
  const childProvenanceRef = provenanceRequest !== undefined ? { current: newDelegationProvenanceAggregate() } : undefined;
2050
- const childAttestation = (status) => childProvenanceRef !== undefined ? reduceDelegationAttestation(childProvenanceRef.current, { completed: status === "completed" }) : undefined;
2063
+ const childAttestation = (completed) => childProvenanceRef !== undefined ? reduceDelegationAttestation(childProvenanceRef.current, { completed }) : undefined;
2051
2064
  const liveCaptureFloor = reviveClaim === undefined
2052
2065
  ? { optedOut: (await ctx.memoryCaptureOptedOut) === true, indeterminate: (await ctx.memoryCaptureIndeterminate) === true }
2053
2066
  : undefined;
@@ -2135,7 +2148,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2135
2148
  ...(def?.memory ? { memory: def.memory } : {}),
2136
2149
  ...(def?.skills?.length ? { skills: def.skills } : {}),
2137
2150
  ...(ctx.principal !== undefined ? { principal: ctx.principal } : {}),
2138
- ...(ctx.checkpointStoreDisabledForChildren === true ? { checkpointStore: null } : {}),
2151
+ ...(ctx.checkpointStoreDisabledForChildren === true ? { checkpointStore: "disabled" } : {}),
2139
2152
  ...(childOnAsk !== undefined ? { onAsk: childOnAsk } : {}),
2140
2153
  ...(ctx.onQuestion !== undefined ? { onQuestion: ctx.onQuestion } : {}),
2141
2154
  ...(ctx.interactiveTools === false ? { interactiveTools: false } : {}),
@@ -2409,10 +2422,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2409
2422
  };
2410
2423
  }
2411
2424
  }
2412
- const settleForkRow = (status) => {
2425
+ const settleForkRow = (terminal) => {
2413
2426
  if (forkSettlementSeat === undefined || forkSettleId === undefined)
2414
2427
  return;
2415
- const att = status !== undefined ? childAttestation(status) : undefined;
2428
+ const att = terminal !== undefined ? childAttestation(terminal.kind === "completed") : undefined;
2416
2429
  const verdict = att === "external" ? "external" : att === "clean" ? "clean" : "unattestable";
2417
2430
  let settled = false;
2418
2431
  let lastErr;
@@ -2629,7 +2642,8 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2629
2642
  void opts.runner
2630
2643
  .runTask(bgForkSpec, bgForkInternals)
2631
2644
  .then(async (child) => {
2632
- settleForkRow(child.status);
2645
+ const childFace = terminalProjection(child.terminal);
2646
+ settleForkRow(child.terminal);
2633
2647
  dropHostAbortListener();
2634
2648
  await finishWorktree();
2635
2649
  try {
@@ -2651,25 +2665,23 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2651
2665
  signal: abort.signal,
2652
2666
  });
2653
2667
  const forkBgResult = withHandbackWarning(forkBgWarning, child.result);
2654
- const okBg = child.status === "completed";
2668
+ const okBg = child.terminal.kind === "completed";
2655
2669
  const reapedBg = !okBg && abort.signal.aborted;
2656
2670
  const collateralBg = reapedBg && bg.registry.getAccessibleTask(taskId, { ...(bgOwner !== undefined ? { owner: bgOwner } : {}), ...(bgScope !== undefined ? { scope: bgScope } : {}) })?.status === "running";
2657
2671
  const failedFork = !okBg && !reapedBg;
2658
- const errCodeFork = failedFork ? (child.errorCode ?? extractErrorCode(child.errorMessage)) : undefined;
2659
- const errClassFork = failedFork
2660
- ? classifySubagentError({ status: "failed", ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}), ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}) })
2661
- : undefined;
2672
+ const errCodeFork = failedFork ? childFace.errorCode : undefined;
2673
+ const errClassFork = failedFork ? classifySubagentError({ kind: "failed", ...(childFace.errorCode !== undefined ? { code: childFace.errorCode } : {}) }) : undefined;
2662
2674
  const settledBg = bg.registry.settleBackgroundAgent(taskId, {
2663
2675
  cycle: 0,
2664
2676
  status: okBg ? "completed" : reapedBg ? "killed" : "failed",
2665
2677
  ...resultSettleFields(forkBgResult),
2666
- ...(!okBg ? { error: reapedBg ? (collateralBg ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : child.errorMessage ?? String(child.status) } : {}),
2678
+ ...(!okBg ? { error: reapedBg ? (collateralBg ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : childFace.errorMessage ?? childFace.status } : {}),
2667
2679
  ...(errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
2668
2680
  ...(errClassFork !== undefined ? { retryable: errClassFork.retryable, errorKind: errClassFork.errorKind } : {}),
2669
2681
  }) ?? (abort.signal.aborted ? "killed" : okBg ? "completed" : "failed");
2670
2682
  const stoppedByBg = settledBg === "killed" ? bg.registry.getStopAttribution(taskId) ?? "system" : undefined;
2671
2683
  const completionIdFork = bg.registry.getCompletionId(taskId);
2672
- const stillbornFork = child.errorCode === "resume.session_not_found";
2684
+ const stillbornFork = child.terminal.kind === "failed" && child.terminal.code === "resume.session_not_found";
2673
2685
  if (stillbornFork) {
2674
2686
  try {
2675
2687
  await (releaseForked ? releaseForked() : opts.runner.sessions.release?.(forkedId));
@@ -2680,11 +2692,11 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2680
2692
  const residualFork = residualFields();
2681
2693
  const resumableFork = false;
2682
2694
  const forkTranscriptId = child.sessionId ?? forkedId;
2683
- const failReasonFork = settledBg === "failed" ? child.errorMessage : undefined;
2695
+ const failReasonFork = settledBg === "failed" ? childFace.errorMessage : undefined;
2684
2696
  const forkElapsedMs = Date.now() - forkBgStartedAt;
2685
2697
  const forkTerminalSummary = failReasonFork !== undefined
2686
2698
  ? `Agent "${shortDesc}" failed: ${failReasonFork}${ccElapsedTag(forkElapsedMs)}`.slice(0, 300) + errorKindClause(errClassFork)
2687
- : `${ccCompletionText(shortDesc, settledBg, String(child.status), forkElapsedMs)}`;
2699
+ : `${ccCompletionText(shortDesc, settledBg, childFace.status, forkElapsedMs)}`;
2688
2700
  const ownsTerminalFacesFork = bg.registry.claimAgentTerminalNotify(taskId);
2689
2701
  if (ownsTerminalFacesFork)
2690
2702
  sinkEmit({
@@ -2728,13 +2740,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2728
2740
  }
2729
2741
  })
2730
2742
  .catch((e) => {
2731
- settleForkRow("failed");
2743
+ settleForkRow({ kind: "failed" });
2732
2744
  dropHostAbortListener();
2733
2745
  void finishWorktree();
2734
2746
  const killed = abort.signal.aborted;
2735
2747
  const msgFork = e instanceof Error ? e.message : String(e);
2736
- const errCodeForkReject = killed ? undefined : extractErrorCode(msgFork);
2737
- const errClassForkReject = killed ? undefined : classifySubagentError({ status: "failed", errorMessage: msgFork });
2748
+ const errCodeForkReject = killed ? undefined : thrownErrorCode(e);
2749
+ const errClassForkReject = killed ? undefined : classifySubagentError({ kind: "failed", ...(errCodeForkReject !== undefined ? { code: errCodeForkReject } : {}) });
2738
2750
  const settledBg = bg.registry.settleBackgroundAgent(taskId, {
2739
2751
  cycle: 0,
2740
2752
  status: killed ? "killed" : "failed",
@@ -2826,7 +2838,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2826
2838
  let forkChild;
2827
2839
  try {
2828
2840
  forkChild = await opts.runner.runTask({ ...buildChildSpec(ctx.signal), sessionId: forkedId, requireExistingSession: true, objective: forkObjective }, forkInternals);
2829
- if (forkChild.errorCode === "resume.session_not_found") {
2841
+ if (forkChild.terminal.kind === "failed" && forkChild.terminal.code === "resume.session_not_found") {
2830
2842
  try {
2831
2843
  await (releaseForked ? releaseForked() : opts.runner.sessions.release?.(forkedId));
2832
2844
  }
@@ -2860,21 +2872,22 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2860
2872
  tasks: 1 + (nsf?.tasks ?? 0),
2861
2873
  costMicroUsd: rollupDelegatedCost(forkChild.stats, nsf),
2862
2874
  });
2863
- if (isDurablePause(forkChild.status)) {
2864
- return durablePauseDelegationReport(` · ${FORK_SUBAGENT_TYPE}`, forkChild);
2875
+ if (isDurablePause(forkChild.terminal)) {
2876
+ return durablePauseDelegationReport(` · ${FORK_SUBAGENT_TYPE}`, forkChild.terminal);
2865
2877
  }
2866
2878
  const wtLine = await finishWorktree();
2867
- const forkErr = classifySubagentError(forkChild);
2879
+ const forkFace = terminalProjection(forkChild.terminal);
2880
+ const forkErr = classifySubagentError(forkChild.terminal);
2868
2881
  const forkLines = [
2869
2882
  ...(forkHandbackWarning ? [forkHandbackWarning, ""] : []),
2870
2883
  `[Sub-agent report · ${FORK_SUBAGENT_TYPE}${a.description ? ` · ${String(a.description)}` : ""}] (forked session …${forkedId.slice(-8)})`,
2871
- `status: ${forkChild.status}`,
2884
+ `status: ${forkFace.status}`,
2872
2885
  modelNote ?? "",
2873
2886
  extraToolsNote ?? "",
2874
2887
  bgIgnoredNote ?? "",
2875
2888
  wtLine ?? "",
2876
- forkChild.blockedReason ? `blocked_reason: ${inlineUntrusted(forkChild.blockedReason, REPORT_FIELD_MAX)}` : "",
2877
- forkChild.errorMessage ? `error: ${inlineUntrusted(forkChild.errorMessage, REPORT_FIELD_MAX)}` : "",
2889
+ forkFace.blockedReason ? `blocked_reason: ${inlineUntrusted(forkFace.blockedReason, REPORT_FIELD_MAX)}` : "",
2890
+ forkFace.errorMessage ? `error: ${inlineUntrusted(forkFace.errorMessage, REPORT_FIELD_MAX)}` : "",
2878
2891
  forkErr ? `error_kind: ${forkErr.errorKind} (retryable: ${forkErr.retryable})` : "",
2879
2892
  `stats: turns=${forkChild.stats.turns} tokens=${forkChild.stats.tokens}`,
2880
2893
  "",
@@ -2889,7 +2902,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2889
2902
  subagentType: FORK_SUBAGENT_TYPE,
2890
2903
  error: forkErr,
2891
2904
  ...(() => {
2892
- const a = childAttestation(forkChild.status);
2905
+ const a = childAttestation(forkChild.terminal.kind === "completed");
2893
2906
  return a !== undefined ? { attestation: a } : {};
2894
2907
  })(),
2895
2908
  ...(forkHandbackWarning !== undefined ? { handbackWarning: forkHandbackWarning } : {}),
@@ -2948,10 +2961,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
2948
2961
  };
2949
2962
  }
2950
2963
  }
2951
- const settleDelegationRow = (status) => {
2964
+ const settleDelegationRow = (terminal) => {
2952
2965
  if (settlementSeat === undefined || bgSettleId === undefined)
2953
2966
  return;
2954
- const att = status !== undefined ? childAttestation(status) : undefined;
2967
+ const att = terminal !== undefined ? childAttestation(terminal.kind === "completed") : undefined;
2955
2968
  const verdict = att === "external" ? "external" : att === "clean" ? "clean" : "unattestable";
2956
2969
  let settled = false;
2957
2970
  let lastErr;
@@ -3347,7 +3360,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3347
3360
  catch (e) {
3348
3361
  return rollbackAndThrow(e);
3349
3362
  }
3350
- if (childResult.status === "failed") {
3363
+ if (childResult.terminal.kind === "failed") {
3351
3364
  let reopened = false;
3352
3365
  try {
3353
3366
  const cp = (await bg.checkpointStore.get(pr.ticket.token));
@@ -3367,7 +3380,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3367
3380
  return await cpS.expire(pr.ticket.token, cp.scope);
3368
3381
  },
3369
3382
  });
3370
- throw new Error(`parked resume could not complete and the approval was REOPENED (still approvable/retryable): ${childResult.errorMessage ?? childResult.status}`);
3383
+ throw new Error(`parked resume could not complete and the approval was REOPENED (still approvable/retryable): ${childResult.terminal.message ?? "failed"}`);
3371
3384
  }
3372
3385
  }
3373
3386
  return childResult;
@@ -3387,8 +3400,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3387
3400
  : opts.runner.runTask(bgSpec, bgInternals);
3388
3401
  void bgChildPromise
3389
3402
  .then(async (child) => {
3390
- settleDelegationRow(child.status);
3391
- closeObserverWindow(child.status);
3403
+ const childFace = terminalProjection(child.terminal);
3404
+ settleDelegationRow(child.terminal);
3405
+ closeObserverWindow(childFace.status);
3392
3406
  dropHostAbortListener();
3393
3407
  await finishWorktree();
3394
3408
  try {
@@ -3403,7 +3417,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3403
3417
  catch {
3404
3418
  }
3405
3419
  const seqAtSettle = bgRetain?.cycleSeq ?? (parkedResume !== undefined ? (reviveRow?.seq ?? 1) + 1 : reviveRow?.seq);
3406
- const bgHandbackWarning = isDurablePause(child.status)
3420
+ const bgHandbackWarning = isDurablePause(child.terminal)
3407
3421
  ? undefined
3408
3422
  : await reviewHandback({
3409
3423
  review: ctx.autoModeReview,
@@ -3414,15 +3428,16 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3414
3428
  });
3415
3429
  const bgHandbackResult = withHandbackWarning(bgHandbackWarning, child.result);
3416
3430
  if (bgRetain) {
3417
- if (isDurablePause(child.status))
3431
+ if (isDurablePause(child.terminal))
3418
3432
  bgRetainLedger?.abandon(ctx.toolCallId);
3419
- else if (child.status !== "completed" && abort.signal.aborted)
3433
+ else if (child.terminal.kind !== "completed" && abort.signal.aborted)
3420
3434
  void bgRetainLedger?.evict(ctx.toolCallId);
3421
3435
  else
3422
3436
  bgRetainLedger?.markSettled(ctx.toolCallId);
3423
3437
  }
3424
3438
  let unparkedPause;
3425
- if (isDurablePause(child.status) && child.checkpointToken !== undefined) {
3439
+ if (isDurablePause(child.terminal)) {
3440
+ const pausedToken = child.terminal.token;
3426
3441
  const cpStore = bg.checkpointStore;
3427
3442
  const expireByStoreScope = async (token2) => {
3428
3443
  try {
@@ -3437,7 +3452,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3437
3452
  };
3438
3453
  const wired = bg.agentStore !== undefined && cpStore !== undefined && bg.ensureChildSessionDurable !== undefined;
3439
3454
  if (wired) {
3440
- const token = child.checkpointToken;
3455
+ const token = pausedToken;
3441
3456
  if (abort.signal.aborted) {
3442
3457
  await expireByStoreScope(token);
3443
3458
  }
@@ -3470,10 +3485,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3470
3485
  }
3471
3486
  }
3472
3487
  else {
3473
- unparkedPause = { kind: "no_park_lane", checkpointToken: child.checkpointToken };
3488
+ unparkedPause = { kind: "no_park_lane", checkpointToken: pausedToken };
3474
3489
  }
3475
3490
  }
3476
- const ok = child.status === "completed";
3491
+ const ok = child.terminal.kind === "completed";
3477
3492
  const reaped = !ok && abort.signal.aborted;
3478
3493
  const collateral = reaped && bg.registry.getAccessibleTask(taskId, { ...(bgOwner !== undefined ? { owner: bgOwner } : {}), ...(bgScope !== undefined ? { scope: bgScope } : {}) })?.status === "running";
3479
3494
  const failedBg = !ok && !reaped;
@@ -3481,21 +3496,19 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3481
3496
  const errCodeBg = failedBg
3482
3497
  ? unparkedPause !== undefined
3483
3498
  ? unparkedPause.kind === "no_park_lane"
3484
- ? child.status === "needs_review"
3499
+ ? child.terminal.kind === "paused" && pauseFamily(child.terminal) === "needs_review"
3485
3500
  ? SUBAGENT_SUSPENDED_NEEDS_REVIEW
3486
3501
  : SUBAGENT_SUSPENDED_AWAITING_APPROVAL
3487
3502
  : SUBAGENT_SUSPENDED_CHECKPOINT_EXPIRED
3488
- : (child.errorCode ?? extractErrorCode(child.errorMessage))
3489
- : undefined;
3490
- const errClassBg = failedBg
3491
- ? classifySubagentError({ status: "failed", ...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}), ...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}) })
3503
+ : childFace.errorCode
3492
3504
  : undefined;
3505
+ const errClassBg = failedBg ? classifySubagentError({ kind: "failed", ...(childFace.errorCode !== undefined ? { code: childFace.errorCode } : {}) }) : undefined;
3493
3506
  const settled = bg.registry.settleBackgroundAgent(taskId, {
3494
3507
  cycle: 0,
3495
3508
  status: ok ? "completed" : reaped ? "killed" : "failed",
3496
3509
  seq: seqAtSettle ?? 1,
3497
3510
  ...resultSettleFields(bgHandbackResult),
3498
- ...(!ok ? { error: reaped ? (collateral ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : unparkedPauseReason ?? child.errorMessage ?? String(child.status) } : {}),
3511
+ ...(!ok ? { error: reaped ? (collateral ? BG_AGENT_COLLATERAL_REAP_REASON : BG_AGENT_REAP_STOP_ERROR) : unparkedPauseReason ?? childFace.errorMessage ?? childFace.status } : {}),
3499
3512
  ...(errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
3500
3513
  ...(errClassBg !== undefined ? { retryable: errClassBg.retryable, errorKind: errClassBg.errorKind } : {}),
3501
3514
  ...(failedBg && child.retryAfterMs !== undefined ? { retryAfterMs: child.retryAfterMs } : {}),
@@ -3522,11 +3535,11 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3522
3535
  durableRow: bgDurableRow,
3523
3536
  named: agentName !== undefined,
3524
3537
  });
3525
- const failReasonBg = settled === "failed" ? unparkedPauseReason ?? child.errorMessage : undefined;
3538
+ const failReasonBg = settled === "failed" ? unparkedPauseReason ?? childFace.errorMessage : undefined;
3526
3539
  const bgElapsedMs = Date.now() - bgStartedAt;
3527
3540
  const bgTerminalSummary = failReasonBg !== undefined
3528
3541
  ? `Agent "${shortDesc}" failed: ${failReasonBg}${ccElapsedTag(bgElapsedMs)}${observerNote}`.slice(0, 300) + errorKindClause(errClassBg)
3529
- : `${ccCompletionText(shortDesc, settled, String(child.status), bgElapsedMs)}${observerNote}`;
3542
+ : `${ccCompletionText(shortDesc, settled, childFace.status, bgElapsedMs)}${observerNote}`;
3530
3543
  const ownsTerminalFaces = bg.registry.claimAgentTerminalNotify(taskId);
3531
3544
  if (ownsTerminalFaces)
3532
3545
  sinkEmit({
@@ -3584,7 +3597,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3584
3597
  }
3585
3598
  })
3586
3599
  .catch(async (e) => {
3587
- settleDelegationRow("failed");
3600
+ settleDelegationRow({ kind: "failed" });
3588
3601
  closeObserverWindow("failed");
3589
3602
  dropHostAbortListener();
3590
3603
  void finishWorktree();
@@ -3597,8 +3610,8 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3597
3610
  bgRetainLedger?.markSettled(ctx.toolCallId);
3598
3611
  }
3599
3612
  const msg = e instanceof Error ? e.message : String(e);
3600
- const errCodeBgReject = killed ? undefined : extractErrorCode(msg);
3601
- const errClassBgReject = killed ? undefined : classifySubagentError({ status: "failed", errorMessage: msg });
3613
+ const errCodeBgReject = killed ? undefined : thrownErrorCode(e);
3614
+ const errClassBgReject = killed ? undefined : classifySubagentError({ kind: "failed", ...(errCodeBgReject !== undefined ? { code: errCodeBgReject } : {}) });
3602
3615
  const settled = bg.registry.settleBackgroundAgent(taskId, {
3603
3616
  cycle: 0,
3604
3617
  status: killed ? "killed" : "failed",
@@ -3755,7 +3768,8 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3755
3768
  finally {
3756
3769
  syncAdmission.release();
3757
3770
  }
3758
- await settleObserver(child.status);
3771
+ const childFace = terminalProjection(child.terminal);
3772
+ await settleObserver(childFace.status);
3759
3773
  const handbackWarning = await reviewHandback({
3760
3774
  review: ctx.autoModeReview,
3761
3775
  toolCallId: ctx.toolCallId,
@@ -3764,7 +3778,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3764
3778
  result: child.result,
3765
3779
  steps: stepRecorder.recentSteps(),
3766
3780
  edits: stepRecorder.editedFiles(),
3767
- ...(child.result || child.status === "completed" ? {} : { partialText: assistantTextTail.trim() }),
3781
+ ...(child.result || child.terminal.kind === "completed" ? {} : { partialText: assistantTextTail.trim() }),
3768
3782
  },
3769
3783
  signal: ctx.signal,
3770
3784
  });
@@ -3775,14 +3789,14 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3775
3789
  tasks: 1 + (ns?.tasks ?? 0),
3776
3790
  costMicroUsd: rollupDelegatedCost(child.stats, ns),
3777
3791
  });
3778
- if (isDurablePause(child.status)) {
3792
+ if (isDurablePause(child.terminal)) {
3779
3793
  if (retainEntry)
3780
3794
  ctx.subagentRetain?.abandon(ctx.toolCallId);
3781
- return durablePauseDelegationReport(def ? ` · ${def.name}` : "", child);
3795
+ return durablePauseDelegationReport(def ? ` · ${def.name}` : "", child.terminal);
3782
3796
  }
3783
3797
  let failureRetained = false;
3784
3798
  if (!retainEntry) {
3785
- if (child.status !== "completed" && opts.retainFailedSessions !== false) {
3799
+ if (child.terminal.kind !== "completed" && opts.retainFailedSessions !== false) {
3786
3800
  failureRetained = await retainFailedChildSession(child.sessionId);
3787
3801
  }
3788
3802
  if (!failureRetained) {
@@ -3794,16 +3808,16 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3794
3808
  }
3795
3809
  }
3796
3810
  const worktreeLine = await finishWorktree();
3797
- const errClass = classifySubagentError(child);
3811
+ const errClass = classifySubagentError(child.terminal);
3798
3812
  const lines = [
3799
3813
  ...(handbackWarning ? [handbackWarning, ""] : []),
3800
3814
  `[Sub-agent report${def ? ` · ${def.name}` : ""}${a.description ? ` · ${String(a.description)}` : ""}]`,
3801
- `status: ${child.status}`,
3815
+ `status: ${childFace.status}`,
3802
3816
  modelNote ?? "",
3803
3817
  extraToolsNote ?? "",
3804
3818
  worktreeLine ?? "",
3805
- child.blockedReason ? `blocked_reason: ${inlineUntrusted(child.blockedReason, REPORT_FIELD_MAX)}` : "",
3806
- child.errorMessage ? `error: ${inlineUntrusted(child.errorMessage, REPORT_FIELD_MAX)}` : "",
3819
+ childFace.blockedReason ? `blocked_reason: ${inlineUntrusted(childFace.blockedReason, REPORT_FIELD_MAX)}` : "",
3820
+ childFace.errorMessage ? `error: ${inlineUntrusted(childFace.errorMessage, REPORT_FIELD_MAX)}` : "",
3807
3821
  errClass ? `error_kind: ${errClass.errorKind} (retryable: ${errClass.retryable})` : "",
3808
3822
  observerFailure !== undefined
3809
3823
  ? `observer: failed (${inlineUntrusted(observerFailure, REPORT_FIELD_MAX)})`
@@ -3816,7 +3830,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3816
3830
  failureRetained
3817
3831
  ? `session_retained: this child's session is kept for a short time (~15 min) so its transcript stays inspectable via transcript_id; it is released automatically afterwards.`
3818
3832
  : "",
3819
- ...(child.status !== "completed"
3833
+ ...(child.terminal.kind !== "completed"
3820
3834
  ? (() => {
3821
3835
  const rs = stepRecorder.recentSteps();
3822
3836
  const ef = stepRecorder.editedFiles();
@@ -3837,7 +3851,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3837
3851
  "result:",
3838
3852
  child.result
3839
3853
  ? delimitUntrusted("sub-agent result", child.result)
3840
- : child.status !== "completed" && assistantTextTail.trim() !== ""
3854
+ : child.terminal.kind !== "completed" && assistantTextTail.trim() !== ""
3841
3855
  ? `partial findings (the child's last assistant text before it stopped):\n${delimitUntrusted("sub-agent partial findings", assistantTextTail.trim())}`
3842
3856
  : "(no text result)",
3843
3857
  "",
@@ -3853,7 +3867,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
3853
3867
  ...(def !== undefined ? { subagentType: def.name } : {}),
3854
3868
  error: errClass,
3855
3869
  ...(() => {
3856
- const a = childAttestation(child.status);
3870
+ const a = childAttestation(child.terminal.kind === "completed");
3857
3871
  return a !== undefined ? { attestation: a } : {};
3858
3872
  })(),
3859
3873
  ...(handbackWarning !== undefined ? { handbackWarning } : {}),