pi-subagents 0.55.0 → 0.57.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 (97) hide show
  1. package/CHANGELOG.md +81 -3
  2. package/agents/claude-code-writer.md +15 -0
  3. package/agents/claude-code.md +15 -0
  4. package/agents/codex-exec-writer.md +15 -0
  5. package/agents/codex-exec.md +15 -0
  6. package/agents/cursor-agent-writer.md +14 -0
  7. package/agents/cursor-agent.md +14 -0
  8. package/docs/agents.md +111 -15
  9. package/docs/configuration.md +37 -0
  10. package/docs/extension-api.md +2 -2
  11. package/docs/models.md +6 -0
  12. package/docs/observability.md +8 -7
  13. package/docs/tool-reference.md +11 -1
  14. package/docs/workflows.md +20 -0
  15. package/package.json +1 -1
  16. package/skills/pi-subagents/references/execution-controls.md +1 -1
  17. package/src/agents/agent-management.ts +13 -5
  18. package/src/agents/agent-refinements.ts +4 -4
  19. package/src/agents/agent-serializer.ts +2 -0
  20. package/src/agents/agents.ts +242 -49
  21. package/src/agents/builtin-names.ts +6 -0
  22. package/src/agents/runtime-agent-registry.ts +8 -2
  23. package/src/api/preflight.ts +22 -3
  24. package/src/extension/config.ts +70 -0
  25. package/src/extension/doctor.ts +3 -3
  26. package/src/extension/index.ts +18 -4
  27. package/src/extension/public-execution.ts +29 -13
  28. package/src/extension/rpc.ts +20 -5
  29. package/src/extension/schemas.ts +9 -3
  30. package/src/extension/tool-description.ts +11 -9
  31. package/src/inspectors/herdr/actions.ts +2 -1
  32. package/src/inspectors/herdr/inspector-runner.ts +2 -10
  33. package/src/inspectors/herdr/session-roots-codec.ts +42 -0
  34. package/src/integrations/herdr-status.ts +46 -3
  35. package/src/runs/background/active-async-capacity.ts +77 -10
  36. package/src/runs/background/async-execution.ts +148 -27
  37. package/src/runs/background/async-job-tracker.ts +5 -0
  38. package/src/runs/background/async-resume.ts +3 -0
  39. package/src/runs/background/async-retention.ts +20 -3
  40. package/src/runs/background/async-status.ts +5 -0
  41. package/src/runs/background/chain-root-attachment.ts +15 -1
  42. package/src/runs/background/fleet-view.ts +16 -10
  43. package/src/runs/background/inspect-rpc.ts +8 -8
  44. package/src/runs/background/result-files.ts +27 -14
  45. package/src/runs/background/result-watcher.ts +9 -5
  46. package/src/runs/background/run-status.ts +33 -6
  47. package/src/runs/background/scheduled-runs.ts +7 -1
  48. package/src/runs/background/subagent-runner.ts +254 -49
  49. package/src/runs/background/wait-completions.ts +4 -0
  50. package/src/runs/foreground/execution.ts +107 -16
  51. package/src/runs/foreground/foreground-control.ts +6 -0
  52. package/src/runs/foreground/foreground-history.ts +25 -3
  53. package/src/runs/foreground/subagent-executor.ts +324 -84
  54. package/src/runs/foreground/workflow-detach-reconcile.ts +31 -7
  55. package/src/runs/shared/acceptance.ts +10 -5
  56. package/src/runs/shared/agent-contract.ts +1 -1
  57. package/src/runs/shared/child-protocol.ts +21 -7
  58. package/src/runs/shared/claude-code-adapter.ts +129 -0
  59. package/src/runs/shared/codex-exec-adapter.ts +129 -0
  60. package/src/runs/shared/completion-guard.ts +3 -2
  61. package/src/runs/shared/cursor-agent-adapter.ts +114 -0
  62. package/src/runs/shared/dynamic-fanout.ts +1 -1
  63. package/src/runs/shared/extension-bindings.ts +78 -0
  64. package/src/runs/shared/external-cli-contract.ts +167 -0
  65. package/src/runs/shared/external-cli-preflight.ts +122 -0
  66. package/src/runs/shared/external-cli-runner.ts +349 -54
  67. package/src/runs/shared/fast-mode-extension.ts +10 -0
  68. package/src/runs/shared/model-exclusions.ts +59 -8
  69. package/src/runs/shared/model-fallback.ts +25 -8
  70. package/src/runs/shared/mutation-evidence.ts +150 -0
  71. package/src/runs/shared/nested-events.ts +3 -1
  72. package/src/runs/shared/nested-render.ts +2 -2
  73. package/src/runs/shared/parallel-utils.ts +3 -0
  74. package/src/runs/shared/pi-args.ts +47 -0
  75. package/src/runs/shared/process-signal.ts +13 -0
  76. package/src/runs/shared/run-history.ts +21 -1
  77. package/src/runs/shared/structured-output.ts +18 -4
  78. package/src/runs/shared/subagent-prompt-runtime.ts +9 -5
  79. package/src/shared/fork-context.ts +21 -0
  80. package/src/shared/formatters.ts +7 -1
  81. package/src/shared/launch-contract.ts +6 -0
  82. package/src/shared/pruned-fork.ts +450 -0
  83. package/src/shared/session-file-trust.ts +19 -0
  84. package/src/shared/session-tokens.ts +14 -3
  85. package/src/shared/settings.ts +16 -3
  86. package/src/shared/shortcuts.ts +17 -0
  87. package/src/shared/types.ts +166 -9
  88. package/src/shared/workflow-child-permit.ts +116 -0
  89. package/src/slash/delegation-adapters.ts +0 -1
  90. package/src/slash/slash-commands.ts +8 -6
  91. package/src/tui/fleet-status.ts +27 -10
  92. package/src/tui/fleet-transcript.ts +11 -5
  93. package/src/tui/fleet.ts +28 -13
  94. package/src/tui/render.ts +47 -21
  95. package/src/workflows/scripted-workflow.ts +298 -30
  96. package/src/workflows/workflow-child-summary.ts +117 -0
  97. package/src/workflows/workflow-receipt.ts +119 -4
@@ -1,8 +1,9 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { writePrivateAtomicJson } from "../shared/atomic-json.ts";
4
- import type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
4
+ import type { ExternalCliReceiptMetadata, WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
5
5
  import type { WorkflowReceiptResumeReference, WorkflowScriptChildResult } from "./scripted-workflow.ts";
6
+ import { parseWorkflowChildSummary } from "./workflow-child-summary.ts";
6
7
 
7
8
  export type { WorkflowReceipt, WorkflowReceiptEntry, WorkflowReceiptState } from "../shared/types.ts";
8
9
 
@@ -32,9 +33,11 @@ export function buildWorkflowReceipt(input: {
32
33
  workflowRunId: string;
33
34
  state: WorkflowReceiptState;
34
35
  children: WorkflowScriptChildResult[];
36
+ workflowChildren?: WorkflowReceipt["workflowChildren"];
35
37
  createdAt?: number;
36
38
  }): WorkflowReceipt {
37
39
  const workflowRunId = assertSafeRunId(input.workflowRunId, "workflowRunId");
40
+ if (input.workflowChildren?.workflowRunId !== undefined && input.workflowChildren.workflowRunId !== workflowRunId) throw new Error("workflowChildren workflowRunId does not match its receipt.");
38
41
  const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
39
42
  for (const child of input.children) {
40
43
  const key = assertKey(child.key, "workflow receipt child key");
@@ -49,13 +52,14 @@ export function buildWorkflowReceipt(input: {
49
52
  ...(child.requestedContext ? { requestedContext: child.requestedContext } : {}),
50
53
  ...(child.resolvedContext ? { resolvedContext: child.resolvedContext } : {}),
51
54
  ...(child.outputReference ? { outputReference: child.outputReference } : {}),
55
+ ...(child.externalAdapter ? { externalAdapter: child.externalAdapter } : {}),
52
56
  continuation: { runIds },
53
57
  };
54
58
  entries[key] = resumability.state === "resumable"
55
59
  ? { ...base, latestRunId: latestRunId!, resumability }
56
60
  : { ...base, ...(latestRunId ? { latestRunId } : {}), resumability };
57
61
  }
58
- return { version: WORKFLOW_RECEIPT_VERSION, workflowRunId, state: input.state, createdAt: input.createdAt ?? Date.now(), entries };
62
+ return { version: WORKFLOW_RECEIPT_VERSION, workflowRunId, state: input.state, createdAt: input.createdAt ?? Date.now(), entries, ...(input.workflowChildren ? { workflowChildren: input.workflowChildren } : {}) };
59
63
  }
60
64
 
61
65
  export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt): string {
@@ -64,6 +68,108 @@ export function writeWorkflowReceipt(asyncDir: string, receipt: WorkflowReceipt)
64
68
  return receiptPath;
65
69
  }
66
70
 
71
+ const EXTERNAL_CLI_CAPABILITIES = {
72
+ stop: true,
73
+ steer: false,
74
+ resume: false,
75
+ structuredOutput: false,
76
+ toolEvents: false,
77
+ supervisor: "unsupported",
78
+ forkContext: false,
79
+ extensionBindings: false,
80
+ } as const;
81
+
82
+ function parseExternalCliReceiptMetadata(value: unknown, key: string, source: string): ExternalCliReceiptMetadata | undefined {
83
+ if (value === undefined) return undefined;
84
+ const label = `Invalid workflow receipt '${source}': entry '${key}' externalAdapter`;
85
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${label} must be an object.`);
86
+ const metadata = value as Record<string, unknown>;
87
+ const unknownMetadata = Object.keys(metadata).filter((field) => !["adapter", "capabilities", "safety", "outputArtifacts", "handoff", "supervisor", "nonResumableReason"].includes(field));
88
+ if (unknownMetadata.length > 0) throw new Error(`${label} has unsupported fields: ${unknownMetadata.join(", ")}.`);
89
+ const adapter = metadata.adapter;
90
+ if (!adapter || typeof adapter !== "object" || Array.isArray(adapter)) throw new Error(`${label}.adapter must be an object.`);
91
+ const adapterRecord = adapter as Record<string, unknown>;
92
+ const unknownAdapter = Object.keys(adapterRecord).filter((field) => !["id", "version", "executionMode"].includes(field));
93
+ if (unknownAdapter.length > 0) throw new Error(`${label}.adapter has unsupported fields: ${unknownAdapter.join(", ")}.`);
94
+ if ((adapterRecord.id !== "external-cli" && adapterRecord.id !== "codex-exec" && adapterRecord.id !== "codex-exec-writer" && adapterRecord.id !== "claude-code" && adapterRecord.id !== "claude-code-writer" && adapterRecord.id !== "cursor-agent" && adapterRecord.id !== "cursor-agent-writer" && adapterRecord.id !== "grok-build") || adapterRecord.version !== 1 || adapterRecord.executionMode !== (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer" || adapterRecord.id === "grok-build" ? "one-shot-prompt-file" : "one-shot-stdin")) throw new Error(`${label}.adapter is invalid.`);
95
+ const capabilities = metadata.capabilities;
96
+ if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) throw new Error(`${label}.capabilities must be an object.`);
97
+ const capabilityRecord = capabilities as Record<string, unknown>;
98
+ const unknownCapabilities = Object.keys(capabilityRecord).filter((field) => !(field in EXTERNAL_CLI_CAPABILITIES));
99
+ if (unknownCapabilities.length > 0) throw new Error(`${label}.capabilities has unsupported fields: ${unknownCapabilities.join(", ")}.`);
100
+ for (const [capability, expected] of Object.entries(EXTERNAL_CLI_CAPABILITIES)) {
101
+ if (capabilityRecord[capability] !== expected) throw new Error(`${label}.capabilities.${capability} is invalid.`);
102
+ }
103
+ const safety = metadata.safety;
104
+ if (adapterRecord.id === "codex-exec") {
105
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
106
+ const safetyRecord = safety as Record<string, unknown>;
107
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["sandbox", "approvalPolicy", "ephemeral"].includes(field));
108
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
109
+ if (safetyRecord.sandbox !== "read-only" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
110
+ } else if (adapterRecord.id === "codex-exec-writer") {
111
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
112
+ const safetyRecord = safety as Record<string, unknown>;
113
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "sandbox", "approvalPolicy", "ephemeral"].includes(field));
114
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
115
+ if (safetyRecord.access !== "workspace-write" || safetyRecord.sandbox !== "workspace-write" || safetyRecord.approvalPolicy !== "never" || safetyRecord.ephemeral !== true) throw new Error(`${label}.safety is invalid.`);
116
+ } else if (adapterRecord.id === "claude-code") {
117
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
118
+ const safetyRecord = safety as Record<string, unknown>;
119
+ const legacy = safetyRecord.authentication === undefined;
120
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !(legacy
121
+ ? ["permissionMode", "tools", "mcp", "settingSources", "sessionPersistence"]
122
+ : ["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"]).includes(field));
123
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
124
+ if (legacy) {
125
+ if (safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "none" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
126
+ } else if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "none" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
127
+ } else if (adapterRecord.id === "claude-code-writer") {
128
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
129
+ const safetyRecord = safety as Record<string, unknown>;
130
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "mcp", "settingSources", "userSettingsTrust", "sessionPersistence"].includes(field));
131
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
132
+ if (safetyRecord.access !== "workspace-write" || safetyRecord.authentication !== "existing-cli-required" || safetyRecord.permissionMode !== "acceptEdits" || safetyRecord.tools !== "Read,Write,Edit,Glob,Grep" || safetyRecord.mcp !== "empty-strict" || safetyRecord.settingSources !== "user" || safetyRecord.userSettingsTrust !== "required" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
133
+ } else if (adapterRecord.id === "cursor-agent" || adapterRecord.id === "cursor-agent-writer") {
134
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
135
+ const safetyRecord = safety as Record<string, unknown>;
136
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "mode", "sandbox", "workspaceTrust", "sessionReuse"].includes(field));
137
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
138
+ const writer = adapterRecord.id === "cursor-agent-writer";
139
+ if (safetyRecord.access !== (writer ? "workspace-write" : "read-only") || safetyRecord.authentication !== "cursor-api-key-or-existing-login" || safetyRecord.mode !== (writer ? "print" : "ask") || safetyRecord.sandbox !== "enabled" || safetyRecord.workspaceTrust !== "existing-required" || safetyRecord.sessionReuse !== false) throw new Error(`${label}.safety is invalid.`);
140
+ } else if (adapterRecord.id === "grok-build") {
141
+ if (!safety || typeof safety !== "object" || Array.isArray(safety)) throw new Error(`${label}.safety is missing.`);
142
+ const safetyRecord = safety as Record<string, unknown>;
143
+ const unknownSafety = Object.keys(safetyRecord).filter((field) => !["access", "authentication", "permissionMode", "tools", "deniedTools", "sandbox", "webSearch", "subagents", "config", "updates", "sessionPersistence"].includes(field));
144
+ if (unknownSafety.length > 0) throw new Error(`${label}.safety has unsupported fields: ${unknownSafety.join(", ")}.`);
145
+ if (safetyRecord.access !== "read-only" || safetyRecord.authentication !== "xai-api-key-required" || safetyRecord.permissionMode !== "plan" || safetyRecord.tools !== "read_file,grep,list_dir" || safetyRecord.deniedTools !== "run_terminal_cmd,search_replace,Agent,Bash,Edit,Write,MCPTool" || safetyRecord.sandbox !== "read-only" || safetyRecord.webSearch !== false || safetyRecord.subagents !== false || safetyRecord.config !== "temporary-home" || safetyRecord.updates !== "disabled" || safetyRecord.sessionPersistence !== false) throw new Error(`${label}.safety is invalid.`);
146
+ } else if (safety !== undefined) throw new Error(`${label}.safety is invalid for the generic adapter.`);
147
+ const handoff = metadata.handoff;
148
+ if (!handoff || typeof handoff !== "object" || Array.isArray(handoff)) throw new Error(`${label}.handoff must be an object.`);
149
+ const handoffRecord = handoff as Record<string, unknown>;
150
+ const unknownHandoff = Object.keys(handoffRecord).filter((field) => field !== "mode");
151
+ if (unknownHandoff.length > 0) throw new Error(`${label}.handoff has unsupported fields: ${unknownHandoff.join(", ")}.`);
152
+ if (handoffRecord.mode !== "fresh") throw new Error(`${label}.handoff is invalid.`);
153
+ const supervisor = metadata.supervisor;
154
+ if (!supervisor || typeof supervisor !== "object" || Array.isArray(supervisor)) throw new Error(`${label}.supervisor must be an object.`);
155
+ const supervisorRecord = supervisor as Record<string, unknown>;
156
+ const unknownSupervisor = Object.keys(supervisorRecord).filter((field) => !["mode", "reason"].includes(field));
157
+ if (unknownSupervisor.length > 0) throw new Error(`${label}.supervisor has unsupported fields: ${unknownSupervisor.join(", ")}.`);
158
+ if (supervisorRecord.mode !== "unsupported" || typeof supervisorRecord.reason !== "string" || !supervisorRecord.reason.trim()) throw new Error(`${label}.supervisor is invalid.`);
159
+ if (typeof metadata.nonResumableReason !== "string" || !metadata.nonResumableReason.trim()) throw new Error(`${label}.nonResumableReason is missing.`);
160
+ const outputArtifacts = metadata.outputArtifacts;
161
+ if (outputArtifacts !== undefined) {
162
+ if (!outputArtifacts || typeof outputArtifacts !== "object" || Array.isArray(outputArtifacts)) throw new Error(`${label}.outputArtifacts must be an object.`);
163
+ const unknownArtifacts = Object.keys(outputArtifacts).filter((field) => !["stdoutPath", "stderrPath", "finalOutputPath"].includes(field));
164
+ if (unknownArtifacts.length > 0) throw new Error(`${label}.outputArtifacts has unsupported fields: ${unknownArtifacts.join(", ")}.`);
165
+ for (const field of ["stdoutPath", "stderrPath", "finalOutputPath"] as const) {
166
+ const artifactPath = (outputArtifacts as Record<string, unknown>)[field];
167
+ if (artifactPath !== undefined && (typeof artifactPath !== "string" || !artifactPath.trim())) throw new Error(`${label}.outputArtifacts.${field} must be a non-empty string.`);
168
+ }
169
+ }
170
+ return value as ExternalCliReceiptMetadata;
171
+ }
172
+
67
173
  function parseEntry(value: unknown, key: string, source: string): WorkflowReceiptEntry {
68
174
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' must be an object.`);
69
175
  const entry = value as Record<string, unknown>;
@@ -84,6 +190,7 @@ function parseEntry(value: unknown, key: string, source: string): WorkflowReceip
84
190
  const reason = (resumability as Record<string, unknown>).reason;
85
191
  if (state === "resumable" && latestRunId === undefined) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' resumable entry has no retained run id.`);
86
192
  if (state === "not-resumable" && (typeof reason !== "string" || !reason.trim())) throw new Error(`Invalid workflow receipt '${source}': entry '${key}' non-resumable reason is missing.`);
193
+ parseExternalCliReceiptMetadata(entry.externalAdapter, key, source);
87
194
  return value as WorkflowReceiptEntry;
88
195
  }
89
196
 
@@ -93,7 +200,13 @@ export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string)
93
200
  try {
94
201
  value = JSON.parse(fs.readFileSync(receiptPath, "utf-8")) as unknown;
95
202
  } catch (error) {
96
- if ((error as NodeJS.ErrnoException).code === "ENOENT") throw new Error(`Workflow receipt '${workflowRunId}' was not found.`);
203
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
204
+ const workflowDir = path.dirname(receiptPath);
205
+ if (fs.existsSync(path.join(workflowDir, "status.json")) || fs.existsSync(path.join(workflowDir, "events.jsonl"))) {
206
+ throw new Error(`Workflow receipt '${workflowRunId}' is not available because the workflow may still be active or terminal receipt writing failed. Use direct child run IDs from status/events for direct resume after the normal retained-child checks.`);
207
+ }
208
+ throw new Error(`Workflow receipt '${workflowRunId}' was not found.`);
209
+ }
97
210
  throw new Error(`Workflow receipt '${workflowRunId}' could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error instanceof Error ? error : undefined });
98
211
  }
99
212
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`Invalid workflow receipt '${receiptPath}': expected an object.`);
@@ -107,7 +220,9 @@ export function readWorkflowReceipt(asyncDirRoot: string, workflowRunId: string)
107
220
  if (!receipt.entries || typeof receipt.entries !== "object" || Array.isArray(receipt.entries)) throw new Error(`Invalid workflow receipt '${receiptPath}': entries must be an object.`);
108
221
  const entries: Record<string, WorkflowReceiptEntry> = Object.create(null) as Record<string, WorkflowReceiptEntry>;
109
222
  for (const [key, entry] of Object.entries(receipt.entries as Record<string, unknown>)) entries[assertKey(key, "workflow receipt key")] = parseEntry(entry, key, receiptPath);
110
- return { version: 1, workflowRunId, state: receipt.state, createdAt: receipt.createdAt, entries };
223
+ const workflowChildren = parseWorkflowChildSummary(receipt.workflowChildren);
224
+ if (workflowChildren && workflowChildren.workflowRunId !== workflowRunId) throw new Error(`Workflow receipt '${receiptPath}' is stale: workflowChildren.workflowRunId does not match.`);
225
+ return { version: 1, workflowRunId, state: receipt.state, createdAt: receipt.createdAt, entries, ...(workflowChildren ? { workflowChildren } : {}) };
111
226
  }
112
227
 
113
228
  export function resolveWorkflowReceiptResumeEntry(input: {