@sema-agent/core 7.16.0 → 7.17.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 (193) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/dist/agents/builtin-agents.d.ts +3 -3
  3. package/dist/agents/cross-session-envelope.d.ts +0 -1
  4. package/dist/agents/cross-session-envelope.js +0 -1
  5. package/dist/agents/peer-notices.d.ts +0 -9
  6. package/dist/agents/peer-notices.js +0 -63
  7. package/dist/agents/subagent.js +7 -15
  8. package/dist/brain/request-params.d.ts +2 -2
  9. package/dist/config/defaults.d.ts +0 -7
  10. package/dist/config/defaults.js +0 -2
  11. package/dist/core/artifact-host.d.ts +218 -0
  12. package/dist/core/artifact-host.js +92 -0
  13. package/dist/core/ask-origin.d.ts +29 -3
  14. package/dist/core/ask-origin.js +17 -9
  15. package/dist/core/ask-question.d.ts +25 -0
  16. package/dist/core/ask-question.js +4 -0
  17. package/dist/core/ask-unresolvable-notice.d.ts +30 -21
  18. package/dist/core/ask-unresolvable-notice.js +16 -8
  19. package/dist/core/auto-compaction.d.ts +10 -10
  20. package/dist/core/auto-mode-arming.d.ts +2 -2
  21. package/dist/core/background-agent-store.d.ts +5 -5
  22. package/dist/core/background-shell.d.ts +1 -1
  23. package/dist/core/checkpoint-execution-record.d.ts +0 -3
  24. package/dist/core/checkpoint-execution-record.js +0 -4
  25. package/dist/core/checkpoint-store.d.ts +71 -99
  26. package/dist/core/checkpoint-store.js +9 -16
  27. package/dist/core/context-edit.d.ts +36 -104
  28. package/dist/core/context-edit.js +37 -60
  29. package/dist/core/effective-path-target.d.ts +68 -8
  30. package/dist/core/effective-path-target.js +42 -30
  31. package/dist/core/engine-notice.d.ts +46 -32
  32. package/dist/core/env-budget.d.ts +31 -0
  33. package/dist/core/env-budget.js +39 -0
  34. package/dist/core/exec-output-tail.d.ts +5 -5
  35. package/dist/core/file-history-store.js +3 -6
  36. package/dist/core/fs-write-gate-policy.d.ts +1 -1
  37. package/dist/core/fs-write-gate-policy.js +3 -3
  38. package/dist/core/gate-lanes.js +9 -15
  39. package/dist/core/governance-codes.d.ts +1 -1
  40. package/dist/core/governance-codes.js +2 -0
  41. package/dist/core/hands-band.d.ts +1 -1
  42. package/dist/core/hooks.d.ts +18 -17
  43. package/dist/core/lsp-session.d.ts +1 -1
  44. package/dist/core/lsp.d.ts +1 -1
  45. package/dist/core/lsp.js +5 -4
  46. package/dist/core/mcp.d.ts +5 -5
  47. package/dist/core/mcp.js +4 -48
  48. package/dist/core/memory-recall.d.ts +0 -8
  49. package/dist/core/memory.d.ts +1 -1
  50. package/dist/core/oracle-isolation.js +2 -2
  51. package/dist/core/parent-spec-seat.d.ts +66 -0
  52. package/dist/core/parent-spec-seat.js +47 -0
  53. package/dist/core/permission-rule-model.d.ts +11 -6
  54. package/dist/core/permission-rule-model.js +53 -37
  55. package/dist/core/permission-rules.js +8 -3
  56. package/dist/core/protocol-naming.d.ts +2 -2
  57. package/dist/core/remote-env.d.ts +1 -1
  58. package/dist/core/retired-keys.d.ts +19 -0
  59. package/dist/core/retired-keys.js +15 -0
  60. package/dist/core/runner/active-skill-scope.js +3 -3
  61. package/dist/core/runner/assemble-result.d.ts +15 -13
  62. package/dist/core/runner/assemble-result.js +5 -4
  63. package/dist/core/runner/compaction-call-options.d.ts +25 -89
  64. package/dist/core/runner/contracts.d.ts +10 -9
  65. package/dist/core/runner/gate-exit.js +15 -1
  66. package/dist/core/runner/halt-attribution.d.ts +35 -0
  67. package/dist/core/runner/halt-attribution.js +16 -0
  68. package/dist/core/runner/memory-consolidation.d.ts +0 -2
  69. package/dist/core/runner/memory-consolidation.js +0 -1
  70. package/dist/core/runner/prepare-artifact.d.ts +53 -0
  71. package/dist/core/runner/prepare-artifact.js +77 -0
  72. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  73. package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
  74. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  75. package/dist/core/runner/prepare-config-doors.js +5 -2
  76. package/dist/core/runner/prepare-context-lane.d.ts +2 -2
  77. package/dist/core/runner/prepare-context-lane.js +2 -13
  78. package/dist/core/runner/prepare-file-history.d.ts +1 -1
  79. package/dist/core/runner/prepare-file-history.js +3 -52
  80. package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
  81. package/dist/core/runner/prepare-hands-readface.js +3 -2
  82. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
  83. package/dist/core/runner/prepare-inherited-gate.js +7 -8
  84. package/dist/core/runner/prepare-memory.d.ts +4 -6
  85. package/dist/core/runner/prepare-memory.js +4 -2
  86. package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
  87. package/dist/core/runner/prepare-policy-chain.js +15 -30
  88. package/dist/core/runner/prepare-prompt-assembly.js +1 -1
  89. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  90. package/dist/core/runner/prepare-suspend-saga.js +0 -2
  91. package/dist/core/runner/prepare-task.js +10 -9
  92. package/dist/core/runner/prepare-wiring-manifest.js +9 -7
  93. package/dist/core/runner/resume-admission.js +2 -1
  94. package/dist/core/runner/resume-internals-and-config.js +4 -0
  95. package/dist/core/runner/retired-runner-deps.d.ts +3 -0
  96. package/dist/core/runner/retired-runner-deps.js +7 -0
  97. package/dist/core/runner/run-harness-handlers.js +2 -3
  98. package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
  99. package/dist/core/runner/run-settle-and-teardown.js +3 -1
  100. package/dist/core/runner/session-file-state-replay.js +3 -3
  101. package/dist/core/runner/session-rule-policy.js +3 -4
  102. package/dist/core/runner/stream-halt-verbs.js +1 -1
  103. package/dist/core/runner/stream-steer-verb.js +1 -4
  104. package/dist/core/runner/workspace-path.js +3 -0
  105. package/dist/core/runner-deps.d.ts +30 -49
  106. package/dist/core/safe-notify.d.ts +1 -1
  107. package/dist/core/sensitive-path-policy.d.ts +1 -1
  108. package/dist/core/sensitive-path-policy.js +4 -4
  109. package/dist/core/session.d.ts +2 -2
  110. package/dist/core/skill-tool-specifier.d.ts +1 -1
  111. package/dist/core/skills-directory.d.ts +1 -1
  112. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  113. package/dist/core/strategy-store.d.ts +8 -11
  114. package/dist/core/task-event.d.ts +20 -17
  115. package/dist/core/task-limits.d.ts +1 -1
  116. package/dist/core/task-notification.d.ts +5 -5
  117. package/dist/core/task-registry-agent.d.ts +5 -5
  118. package/dist/core/task-registry-monitor.d.ts +2 -2
  119. package/dist/core/task-registry-shared.d.ts +27 -27
  120. package/dist/core/task-registry-shared.js +3 -13
  121. package/dist/core/task-registry.d.ts +8 -8
  122. package/dist/core/task-result.d.ts +37 -37
  123. package/dist/core/task-spec.d.ts +5 -5
  124. package/dist/core/task-stream.d.ts +16 -11
  125. package/dist/core/tool-catalog-entries.d.ts +1 -0
  126. package/dist/core/tool-catalog-entries.js +5 -0
  127. package/dist/core/tool-conformance.d.ts +0 -2
  128. package/dist/core/tool-conformance.js +1 -3
  129. package/dist/core/tool-errors.d.ts +5 -0
  130. package/dist/core/tool-errors.js +1 -0
  131. package/dist/core/tool-policy.d.ts +51 -22
  132. package/dist/core/tool-policy.js +28 -5
  133. package/dist/core/tool-result-budget.d.ts +1 -1
  134. package/dist/core/tool-result-store.d.ts +11 -11
  135. package/dist/core/tool-spec.d.ts +34 -38
  136. package/dist/core/tools.d.ts +2 -2
  137. package/dist/core/trace.d.ts +12 -15
  138. package/dist/core/untrusted-text.d.ts +1 -1
  139. package/dist/core/wiring-manifest.d.ts +4 -3
  140. package/dist/core/workflow-journal-store.d.ts +11 -11
  141. package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
  142. package/dist/engine/execution-env/node-execution-env.js +4 -2
  143. package/dist/engine/harness/agent-harness.d.ts +47 -4
  144. package/dist/engine/harness/agent-harness.js +52 -9
  145. package/dist/engine/loop/agent-loop.js +15 -8
  146. package/dist/engine/loop/types.d.ts +5 -0
  147. package/dist/index.d.ts +19 -14
  148. package/dist/index.js +15 -10
  149. package/dist/internal/harness.d.ts +1 -0
  150. package/dist/orchestration/run-spec.d.ts +3 -0
  151. package/dist/orchestration/run-spec.js +3 -3
  152. package/dist/orchestration/run-workflow-tool.d.ts +12 -6
  153. package/dist/orchestration/run-workflow-tool.js +23 -5
  154. package/dist/orchestration/workflow-primitives.d.ts +4 -2
  155. package/dist/orchestration/workflow-primitives.js +1 -4
  156. package/dist/orchestration/workflow-types.d.ts +34 -4
  157. package/dist/orchestration/workflow-types.js +2 -0
  158. package/dist/orchestration/workflow.d.ts +39 -7
  159. package/dist/orchestration/workflow.js +234 -113
  160. package/dist/prompt-assembly/assemble.d.ts +2 -2
  161. package/dist/prompt-assembly/assemble.js +9 -9
  162. package/dist/prompt-assembly/composer.d.ts +6 -7
  163. package/dist/prompt-assembly/composer.js +10 -10
  164. package/dist/prompt-assembly/epoch.js +1 -1
  165. package/dist/prompt-assembly/event-registry.d.ts +0 -21
  166. package/dist/prompt-assembly/packs/sema-default.js +30 -30
  167. package/dist/prompt-assembly/types.d.ts +14 -14
  168. package/dist/prompts/coordinator.d.ts +0 -13
  169. package/dist/prompts/coordinator.js +0 -3
  170. package/dist/stores/file/checkpoint-store.js +1 -5
  171. package/dist/stores/file/file-history-store.js +2 -2
  172. package/dist/stores/file/permission-rule-store.d.ts +4 -35
  173. package/dist/stores/file/permission-rule-store.js +4 -148
  174. package/dist/tools/artifact/artifact-text.d.ts +62 -0
  175. package/dist/tools/artifact/artifact-text.js +68 -0
  176. package/dist/tools/artifact/artifact-tool.d.ts +52 -0
  177. package/dist/tools/artifact/artifact-tool.js +351 -0
  178. package/dist/tools/artifact/local-stub.d.ts +26 -0
  179. package/dist/tools/artifact/local-stub.js +296 -0
  180. package/dist/tools/fs/bash-readonly-classifier.d.ts +74 -8
  181. package/dist/tools/fs/bash-readonly-classifier.js +171 -60
  182. package/dist/tools/fs/fs-bash.d.ts +14 -10
  183. package/dist/tools/fs/fs-bash.js +19 -27
  184. package/dist/tools/fs/fs-search-tools.js +5 -2
  185. package/dist/tools/fs/fs-shared.d.ts +2 -0
  186. package/dist/tools/fs/fs-shared.js +3 -11
  187. package/dist/tools/fs/index.d.ts +4 -0
  188. package/dist/tools/fs/index.js +4 -1
  189. package/dist/tools/fs/safety.d.ts +117 -1
  190. package/dist/tools/fs/safety.js +131 -31
  191. package/dist/tools/fs/search.js +8 -6
  192. package/package.json +2 -1
  193. package/test/export-surface.snapshot.json +115 -39
@@ -3,6 +3,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
3
3
  import { createHash, randomUUID } from "node:crypto";
4
4
  import { availableParallelism } from "node:os";
5
5
  import { uuidv7 } from "../internal/harness.js";
6
+ import { applyParentSpecSeat } from "../core/parent-spec-seat.js";
6
7
  import { TERMINAL_CAUSE_IS_REPLAYABLE, isTerminalCauseKind } from "../core/terminal-cause.js";
7
8
  import { terminalProjection } from "../core/runner/terminal-projection.js";
8
9
  import { builtinAgentDefinitions } from "../agents/builtin-agents.js";
@@ -219,14 +220,24 @@ export class WorkflowJournalIncompatibleError extends Error {
219
220
  fromRunId;
220
221
  ordinal;
221
222
  code = "workflow.journal_incompatible";
222
- constructor(fromRunId, ordinal, reason) {
223
- super(`startWorkflow: resume from "${fromRunId}" was REFUSED — ${reason ?? `journal entry #${ordinal} carries no terminal cause (a journal written before TaskResult.terminal, or a malformed entry)`}. ` +
223
+ constructor(fromRunId, ordinal) {
224
+ super(`startWorkflow: resume from "${fromRunId}" was REFUSED — journal entry #${ordinal} carries no terminal cause (a journal written before TaskResult.terminal, or a malformed entry). ` +
224
225
  "Completed work must not run again; start a fresh run instead of resuming this journal.");
225
226
  this.fromRunId = fromRunId;
226
227
  this.ordinal = ordinal;
227
228
  this.name = "WorkflowJournalIncompatibleError";
228
229
  }
229
230
  }
231
+ class WorkflowParkRefusal extends Error {
232
+ code;
233
+ detail;
234
+ constructor(code, message, detail) {
235
+ super(message);
236
+ this.code = code;
237
+ this.detail = detail;
238
+ this.name = "WorkflowParkRefusal";
239
+ }
240
+ }
230
241
  const workflowDepthStore = new AsyncLocalStorage();
231
242
  function currentWorkflowDepth() {
232
243
  return workflowDepthStore.getStore()?.depth ?? 0;
@@ -461,8 +472,11 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
461
472
  if (opts.resumeFromRunId !== undefined && opts.journalStore === undefined) {
462
473
  throw new Error("runWorkflow: resumeFromRunId requires a journalStore to load the prior run's journal");
463
474
  }
464
- if (opts.defaultDurableApproval !== undefined && opts.store === undefined) {
465
- throw new Error("runWorkflow: defaultDurableApproval requires a WorkflowRunStore — a workflow child's park is recorded on its wa* row, and without a store there is no durable row for a host to route the parked approval by");
475
+ if (opts.checkpointStore !== undefined && opts.store === undefined) {
476
+ throw new WorkflowParkRefusal("workflow.park_requires_run_store", "runWorkflow: checkpointStore (the store this run's children park in) requires a WorkflowRunStore — a workflow child's park is recorded on its wa* row and proven against the checkpoint store at the next resume, and without a run store there is no durable row for a host to route the parked approval by", {});
477
+ }
478
+ if (opts.defaultDurableApproval !== undefined && (opts.store === undefined || opts.checkpointStore === undefined)) {
479
+ throw new WorkflowParkRefusal("workflow.park_requires_run_store", "runWorkflow: defaultDurableApproval requires a WorkflowRunStore and a checkpointStore — a workflow child's park is recorded on its wa* row and read back from the checkpoint store at the next resume; without both there is no durable row for a host to route the parked approval by, or no way for a resume to prove the park", {});
466
480
  }
467
481
  if (opts.parkedResume !== undefined && opts.resumeFromRunId === undefined) {
468
482
  throw new Error("runWorkflow: parkedResume drives a PARKED ordinal of a prior run and requires resumeFromRunId");
@@ -507,22 +521,23 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
507
521
  let persistTail = Promise.resolve();
508
522
  const persist = (mode) => {
509
523
  if (!store)
510
- return persistTail;
524
+ return Promise.resolve({ ok: true });
511
525
  const snapshot = structuredClone(run);
512
526
  const next = persistTail.then(async () => {
513
527
  try {
514
528
  if (mode === "create") {
515
529
  snapshot.rev = storeRev;
516
530
  await store.put(runId, snapshot);
531
+ return { ok: true };
517
532
  }
518
- else {
519
- snapshot.rev = storeRev;
520
- const won = await store.update(runId, scope, snapshot, { rev: storeRev });
521
- if (won)
522
- storeRev += 1;
523
- }
533
+ snapshot.rev = storeRev;
534
+ const won = await store.update(runId, scope, snapshot, { rev: storeRev });
535
+ if (won)
536
+ storeRev += 1;
537
+ return won ? { ok: true } : { ok: false, cause: "cas_lost" };
524
538
  }
525
539
  catch {
540
+ return { ok: false, cause: "threw" };
526
541
  }
527
542
  });
528
543
  persistTail = next;
@@ -546,6 +561,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
546
561
  startedAt: 0,
547
562
  })),
548
563
  agents: [],
564
+ ...(opts.resumeFromRunId === undefined ? { parks: [] } : {}),
549
565
  groups: [],
550
566
  stats: { tokens: 0, turns: 0, costMicroUsd: 0, nested: { tokens: 0, turns: 0, tasks: 0, costMicroUsd: 0 } },
551
567
  startedAt: t0,
@@ -642,6 +658,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
642
658
  const sem = createSemaphore(concurrency);
643
659
  const spawnAttribution = {
644
660
  isDelegatedChild: true,
661
+ ...(opts.questionFaceStripped === true ? { questionFaceStripped: true } : {}),
645
662
  ...(opts.interactionPosture !== undefined ? { parentInteractionPosture: opts.interactionPosture } : {}),
646
663
  ...(opts.parentToolCallId !== undefined ? { parentToolCallId: opts.parentToolCallId } : {}),
647
664
  ...(opts.parentTaskId !== undefined ? { parentTaskId: opts.parentTaskId } : {}),
@@ -819,26 +836,93 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
819
836
  });
820
837
  }
821
838
  };
822
- const writtenOrdinals = new Set();
823
- const carryUnresolvedParks = async () => {
824
- if (opts.resumeFromRunId === undefined || !journalStore)
825
- return;
826
- for (const e of replayByOrdinal) {
827
- if (e?.parked === undefined)
828
- continue;
829
- const ord = callKeyOrdinal(e.callKey);
830
- if (writtenOrdinals.has(ord))
831
- continue;
832
- if (workflowSignal.aborted) {
833
- emitLogLine(`resume-journal: the prior run's PARKED leg at ordinal ${ord} was NOT carried into this run's journal (the run was cancelled or timed out first) — resume from the prior run, not from this one.`);
834
- continue;
839
+ const parkSeat = opts.checkpointStore;
840
+ const parkedByOrdinal = [];
841
+ const errText = (err) => (err instanceof Error ? err.message : String(err));
842
+ const withoutToken = (text, token) => (token === "" ? text : text.split(token).join("<token>"));
843
+ const parkBindingBroken = (ordinal, why) => new WorkflowParkRefusal("workflow.park_binding_broken", `workflow resume: ordinal ${ordinal}'s park cannot be bound to its checkpoint (${why}) — the recorded park is inconsistent. Decide the checkpoint through the deployment's approval channel and start a fresh run.`, { ordinal });
844
+ const readParkTruth = async (seat, key, ordinal) => {
845
+ if (key.token === "" || key.sessionId === "")
846
+ throw parkBindingBroken(ordinal, "the record names no token or no session for it");
847
+ const read = await finalizeWithin(timers, new Promise((resolve) => resolve(seat.get(key.token))).then((cp) => ({ cp }), (err) => ({ err })), "timeout");
848
+ if (read === "timeout" || "err" in read) {
849
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `workflow resume: ordinal ${ordinal} is recorded as PARKED and its checkpoint could not be read from the checkpoint store (${read === "timeout" ? `the read did not settle within ${WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS}ms` : withoutToken(errText(read.err), key.token)}); the resume is refused rather than run beside a pinned child.`, { reason: "checkpoint_store_threw" });
850
+ }
851
+ const cp = read.cp;
852
+ if (cp === null)
853
+ return { kind: "not_pending", key, status: "absent" };
854
+ if (cp.token !== key.token)
855
+ throw parkBindingBroken(ordinal, "the checkpoint store answered a row under another token");
856
+ if (cp.sessionId !== key.sessionId)
857
+ throw parkBindingBroken(ordinal, "the checkpoint belongs to another session than the parked row");
858
+ if (cp.status !== "pending")
859
+ return { kind: "not_pending", key, status: cp.status, ...(cp.checkpointId !== undefined ? { checkpointId: cp.checkpointId } : {}) };
860
+ return { kind: "pending", key, cp };
861
+ };
862
+ const parkMaterialOf = async (key, ordinal) => {
863
+ let entry;
864
+ if (key.originRunId === opts.resumeFromRunId)
865
+ entry = replayByOrdinal[ordinal];
866
+ else if (journalStore) {
867
+ const loaded = await finalizeWithin(timers, new Promise((resolve) => resolve(journalStore.load(key.originRunId, scope))).then((entries) => ({ entries }), (err) => ({ err })), "timeout");
868
+ if (loaded === "timeout" || "err" in loaded) {
869
+ emitRunLog(`resume: the journal of run "${clampRunIdText(key.originRunId)}" (the origin of the park at ordinal ${ordinal}) could not be read (${loaded === "timeout" ? `the load did not settle within ${WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS}ms` : withoutToken(errText(loaded.err), key.token)}).`);
835
870
  }
836
- const outcome = await finalizeWithin(timers, journalAppendParked(e.callKey, e.parked).then(() => "ok", (err) => ({ err })), "timeout");
837
- if (outcome !== "ok") {
838
- const why = outcome === "timeout" ? `the append did not settle within ${WORKFLOW_RESUME_CLAIM_FINALIZE_TIMEOUT_MS}ms` : outcome.err instanceof Error ? outcome.err.message : String(outcome.err);
839
- emitLogLine(`resume-journal: the prior run's PARKED leg at ordinal ${ord} could not be carried into this run's journal (${why}) — resume from the prior run, not from this one.`);
871
+ else {
872
+ entry = loaded.entries.filter((e) => e.parked !== undefined && callKeyOrdinal(e.callKey) === ordinal).at(-1);
840
873
  }
841
874
  }
875
+ const p = entry?.parked;
876
+ if (p === undefined || entry === undefined || p.terminal.token !== key.token || p.sessionId !== key.sessionId || entry.callKey !== key.callKey)
877
+ return undefined;
878
+ return p;
879
+ };
880
+ const degradedParkMaterial = (cp) => ({
881
+ taskId: cp.sourceTaskId ?? "",
882
+ sessionId: cp.sessionId,
883
+ result: "",
884
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
885
+ terminal: { kind: "paused", gate: cp.gate, token: cp.token, ...(cp.checkpointId !== undefined ? { checkpointId: cp.checkpointId } : {}) },
886
+ });
887
+ const disposeParkedOrdinal = (truth, ordinal, callKey) => {
888
+ if (truth.key.callKey !== callKey)
889
+ throw parkedCallChangedError(ordinal);
890
+ const entry = parkedResumeFor(truth.key.token);
891
+ if (truth.kind === "not_pending") {
892
+ if (entry === undefined) {
893
+ throw new WorkflowParkRefusal("workflow.park_not_pending", `workflow resume: ordinal ${ordinal} is recorded as PARKED, but the checkpoint store says its approval checkpoint is ${truth.status}${truth.checkpointId !== undefined ? ` (checkpoint ${truth.checkpointId})` : ""} — decided, expired or reaped outside this workflow. Hand the resume a parkedResume entry for this ordinal's token to settle the ordinal as failed and continue the script, or start a fresh run.`, { ...(truth.checkpointId !== undefined ? { checkpointId: truth.checkpointId } : {}), ordinal, status: truth.status === "absent" ? "absent" : truth.status === "expired" ? "expired" : "resolved" });
894
+ }
895
+ const result = {
896
+ taskId: callKey,
897
+ sessionId: truth.key.sessionId,
898
+ terminal: {
899
+ kind: "failed",
900
+ code: "workflow.park_not_pending",
901
+ message: `the approval checkpoint this call was parked on is ${truth.status}${truth.checkpointId !== undefined ? ` (checkpoint ${truth.checkpointId})` : ""} — decided, expired or reaped outside this workflow; settled as failed on the resume's acknowledgement. The call did not run.`,
902
+ },
903
+ result: "",
904
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
905
+ };
906
+ return { kind: "settle", result };
907
+ }
908
+ const paused = truth.material ?? degradedParkMaterial(truth.cp);
909
+ if (truth.material === undefined)
910
+ emitRunLog(`resume: ordinal ${ordinal} re-parks from its checkpoint row alone — the origin run's journal entry for the park was not readable or did not match the recorded park (empty result, zero stats).`);
911
+ if (entry?.outcome !== undefined) {
912
+ return { kind: "drive", drive: { token: entry.token, outcome: entry.outcome, ...(entry.inheritedGate !== undefined ? { inheritedGate: entry.inheritedGate } : {}), sessionId: paused.sessionId || undefined, paused } };
913
+ }
914
+ return { kind: "repark", paused };
915
+ };
916
+ const assertChildParkSeat = (s) => {
917
+ const seat = s.checkpointStore;
918
+ if (seat === undefined || seat === "disabled")
919
+ return;
920
+ if (parkSeat === undefined) {
921
+ throw new WorkflowParkRefusal("workflow.park_requires_run_store", "workflow agent: the child's checkpointStore would let it park where this workflow cannot read the park back — a workflow whose children can park needs a WorkflowRunStore and that checkpoint store as its read seat (RunWorkflowOptions.store + checkpointStore); wire both, or leave the child's seat unset.", {});
922
+ }
923
+ if (seat !== parkSeat) {
924
+ throw Object.assign(new Error("agent.checkpointStore is not the store this run's children park in (the workflow's resolved checkpoint seat, RunWorkflowOptions.checkpointStore) — wire the one store, or leave the field unset and let the seat ride."), { code: "config.invalid_checkpoint_store" });
925
+ }
842
926
  };
843
927
  const driveNeverStarted = (r) => r.terminal.kind === "failed" && (r.terminal.code === "resume.reopened_unstarted" || r.terminal.code === "resume.reopen_failed_unstarted");
844
928
  const isPausedResult = (r) => isDurablePause(r.terminal);
@@ -895,10 +979,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
895
979
  if (dbg)
896
980
  console.error(`[sema:wf-journal] runId=${runId} callKey=${callKey} TOMBSTONE-ERROR ${err instanceof Error ? err.message : String(err)}`);
897
981
  }
898
- writtenOrdinals.add(callKeyOrdinal(callKey));
899
982
  return;
900
983
  }
901
- writtenOrdinals.add(callKeyOrdinal(callKey));
902
984
  const p = journalStore.append(runId, scope, { callKey, result: stored });
903
985
  journalTail = journalTail.then(() => p).catch(() => undefined);
904
986
  try {
@@ -915,24 +997,9 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
915
997
  const journalAppendParked = async (callKey, paused) => {
916
998
  if (!journalStore)
917
999
  return;
918
- writtenOrdinals.add(callKeyOrdinal(callKey));
919
- let lastErr;
920
- for (let attempt = 0; attempt < 3; attempt++) {
921
- const p = journalStore.append(runId, scope, { callKey, parked: paused });
922
- journalTail = journalTail.then(() => p).catch(() => undefined);
923
- try {
924
- await p;
925
- return;
926
- }
927
- catch (err) {
928
- lastErr = err;
929
- if (err?.code === JOURNAL_OVERSIZE_ERROR_CODE)
930
- break;
931
- if (attempt < 2)
932
- await new Promise((r) => timers.setTimeout(r, 25 * (attempt + 1)));
933
- }
934
- }
935
- throw lastErr;
1000
+ const p = journalStore.append(runId, scope, { callKey, parked: paused });
1001
+ journalTail = journalTail.then(() => p).catch(() => undefined);
1002
+ await p;
936
1003
  };
937
1004
  const parkAgentLeg = async (rec, paused, activityTail, chargeLive) => {
938
1005
  const s = paused.stats;
@@ -951,19 +1018,50 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
951
1018
  rec.activity = activityTail.slice();
952
1019
  foldLegApprovalReport(rec, paused);
953
1020
  emit({ type: "agent_end", runId, label: rec.label, phase: rec.phase, ...(rec.groupId !== undefined ? { groupId: rec.groupId } : {}), status: "parked", ...(paused.model !== undefined ? { modelResolved: paused.model } : {}), ts: rec.endedAt });
954
- void persist("update");
955
1021
  bceLive.delete(waIdOf(rec.callKey));
956
1022
  if (parkedLeg === undefined)
957
1023
  parkedLeg = { label: rec.label, callKey: rec.callKey, paused };
1024
+ const ordinal = callKeyOrdinal(rec.callKey);
1025
+ const parks = run.parks ?? (run.parks = []);
1026
+ const at = parks.findIndex((e) => e.callKey === rec.callKey);
1027
+ const key = { callKey: rec.callKey, sessionId: paused.sessionId, token: paused.terminal.token, originRunId: at === -1 ? runId : parks[at].originRunId };
1028
+ if (at === -1)
1029
+ parks.push(key);
1030
+ else
1031
+ parks[at] = key;
1032
+ void persist("update");
958
1033
  try {
959
1034
  await journalAppendParked(rec.callKey, paused);
1035
+ key.originRunId = runId;
960
1036
  }
961
1037
  catch (err) {
962
- emitRunLog(`resume-journal: the PARKED leg at ordinal ${callKeyOrdinal(rec.callKey)} ("${rec.label.slice(0, 80)}") could not be recorded (${err instanceof Error ? err.message : String(err)}) — ` +
963
- "a resume of this run will NOT find the park at its ordinal; decide the checkpoint through the deployment's approval channel and start a fresh run instead of resuming this one.");
1038
+ emitRunLog(`resume-journal: the PARKED leg at ordinal ${ordinal} ("${rec.label.slice(0, 80)}") could not be recorded in this run's journal (${withoutToken(errText(err), key.token)}) — ` +
1039
+ "the park stands: a resume of this run finds it through the run record and the checkpoint store.");
1040
+ }
1041
+ const persisted = await finalizeWithin(timers, persist("update"), { ok: false, cause: "timeout" });
1042
+ if (!persisted.ok) {
1043
+ emitRunLog(`resume-journal: the PARKED leg at ordinal ${ordinal} ("${rec.label.slice(0, 80)}") could not be persisted on the run record (${persisted.cause}) — ` +
1044
+ "the park stands: a resume of this run finds it through the journal and the checkpoint store.");
964
1045
  }
965
1046
  throw parkedError();
966
1047
  };
1048
+ const recordParkDisposition = async (d, call, agentOpts) => {
1049
+ const { label, callKey, groupId, phase, phaseInstance, prompt } = call;
1050
+ const at = now();
1051
+ const model = d.kind === "repark" ? d.paused.model : undefined;
1052
+ const rec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(model !== undefined ? { model } : {}), ...(d.kind === "repark" ? { replayed: true } : {}), status: "running", queuedAt: at, startedAt: at };
1053
+ if (d.kind === "repark" && run.resume)
1054
+ run.resume.replayed += 1;
1055
+ run.agents.push(rec);
1056
+ if (phaseInstance)
1057
+ agentPhaseOf.set(rec, phaseInstance);
1058
+ emit({ type: "agent_start", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), callKey, prompt, ...(model !== undefined ? { model } : {}), ...(d.kind === "repark" ? { replayed: true } : {}), ts: at });
1059
+ bceSpawn(callKey, label, agentOpts.agentType, d.kind === "repark", (d.kind === "repark" ? d.paused.sessionId : d.result.sessionId) || undefined, model);
1060
+ if (d.kind === "repark")
1061
+ return await parkAgentLeg(rec, d.paused, [], false);
1062
+ await settleAgentResult(rec, d.result, [], agentOpts, "awaited");
1063
+ return d.result;
1064
+ };
967
1065
  const createActivityCapture = (callKey, label, groupId, rec) => {
968
1066
  const tail = [];
969
1067
  let beatCount = 0;
@@ -1168,10 +1266,12 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1168
1266
  }
1169
1267
  }
1170
1268
  const inheritedThinkingSnap = spec.thinking === undefined ? opts.defaultThinking?.() : undefined;
1269
+ const inheritedPrincipal = spec.principal === undefined ? opts.parentSpecSeat?.principal : undefined;
1171
1270
  const specForIdentity = {
1172
1271
  ...spec,
1173
1272
  ...(inheritedModelSnap !== undefined ? { model: inheritedModelSnap } : {}),
1174
1273
  ...(inheritedThinkingSnap !== undefined ? { thinking: inheritedThinkingSnap } : {}),
1274
+ ...(inheritedPrincipal !== undefined ? { principal: inheritedPrincipal } : {}),
1175
1275
  };
1176
1276
  const callKey = workflowAgentCallKey(run.agents.length, specForIdentity, agentOpts);
1177
1277
  const prompt = boundedRedactedSummary(spec.systemPrompt ? `${spec.systemPrompt}\n\n${spec.objective}` : spec.objective, MAX_TRANSCRIPT_CHARS);
@@ -1327,27 +1427,12 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1327
1427
  const { label, phase, phaseInstance, groupId, inheritedModelSnap, inheritedModelError, inheritedThinkingSnap, callKey, prompt, model } = prepareAgentCall(spec, agentOpts);
1328
1428
  let drive;
1329
1429
  const cached = opts.resumeFromRunId !== undefined ? replayByOrdinal[run.agents.length] : undefined;
1330
- if (cached?.parked !== undefined) {
1331
- if (cached.callKey !== callKey)
1332
- throw parkedCallChangedError(run.agents.length);
1333
- {
1334
- const decision = parkedResumeFor(cached.parked.terminal.token);
1335
- if (decision === undefined) {
1336
- if (finalized)
1337
- return cached.parked;
1338
- const at = now();
1339
- const reparkRec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(cached.parked.model !== undefined ? { model: cached.parked.model } : {}), replayed: true, status: "running", queuedAt: at, startedAt: at };
1340
- if (run.resume)
1341
- run.resume.replayed += 1;
1342
- run.agents.push(reparkRec);
1343
- if (phaseInstance)
1344
- agentPhaseOf.set(reparkRec, phaseInstance);
1345
- emit({ type: "agent_start", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), callKey, prompt, ...(cached.parked.model !== undefined ? { model: cached.parked.model } : {}), replayed: true, ts: at });
1346
- bceSpawn(callKey, label, agentOpts.agentType, true, cached.parked.sessionId || undefined, cached.parked.model);
1347
- return await parkAgentLeg(reparkRec, cached.parked, [], false);
1348
- }
1349
- drive = { token: decision.token, outcome: decision.outcome, ...(decision.inheritedGate !== undefined ? { inheritedGate: decision.inheritedGate } : {}), sessionId: cached.parked.sessionId || undefined, paused: cached.parked };
1350
- }
1430
+ const truth = opts.resumeFromRunId !== undefined ? parkedByOrdinal[run.agents.length] : undefined;
1431
+ if (truth !== undefined) {
1432
+ const d = disposeParkedOrdinal(truth, run.agents.length, callKey);
1433
+ if (d.kind !== "drive")
1434
+ return await recordParkDisposition(d, { label, callKey, groupId, phase, phaseInstance, prompt }, agentOpts);
1435
+ drive = d.drive;
1351
1436
  }
1352
1437
  if (parkedLeg !== undefined && drive === undefined)
1353
1438
  throw parkedError();
@@ -1442,12 +1527,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1442
1527
  throw inheritedModelError;
1443
1528
  const typedSpec0 = applyWorkflowAgentType(spec, agentOpts.agentType, agentRegistry);
1444
1529
  const typedSpec = withWorkflowChildSeats(typedSpec0, inheritedModelSnap, inheritedThinkingSnap, runnerRoleMap(runner));
1445
- const framedSpec = withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema);
1446
- const authInherit = framedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1447
- const parkInherit = durableApprovalInherit(framedSpec);
1530
+ const seatedSpec = applyParentSpecSeat(withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema), opts.parentSpecSeat);
1531
+ const authInherit = seatedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1532
+ assertChildParkSeat(seatedSpec);
1533
+ const parkInherit = durableApprovalInherit(seatedSpec);
1448
1534
  const preReviewSpec = agentOpts.schema
1449
- ? { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1450
- : { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1535
+ ? { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1536
+ : { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1451
1537
  const runSpec = await reviewSpawnBeforeLaunch("ctx.agent", label, callKey, preReviewSpec, effectiveSignal);
1452
1538
  const enrichedForward = opts.onForwardEvent !== undefined
1453
1539
  ? (e) => {
@@ -1645,7 +1731,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1645
1731
  }
1646
1732
  catch (err) {
1647
1733
  if (drive !== undefined && !finalized && !(err instanceof WorkflowAgentParkedError)) {
1648
- emitRunLog(`[park] the parked leg "${label}" could not be driven — the resume was refused before the checkpoint was consumed (${err instanceof Error ? err.message : String(err)}); the park stands.`);
1734
+ emitRunLog(`[park] the parked leg "${label}" could not be driven — the resume was refused before the checkpoint was consumed (${withoutToken(errText(err), drive.token)}); the park stands.`);
1649
1735
  return await parkAgentLeg(rec, drive.paused, activityTail, false);
1650
1736
  }
1651
1737
  if (!finalized) {
@@ -1723,24 +1809,14 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1723
1809
  }
1724
1810
  const { label, phase, phaseInstance, groupId, inheritedModelSnap, inheritedModelError, inheritedThinkingSnap, callKey, prompt, model } = prepareAgentCall(spec, agentOpts);
1725
1811
  let drive;
1726
- const parkedHere = opts.resumeFromRunId !== undefined ? replayByOrdinal[run.agents.length] : undefined;
1727
- if (parkedHere?.parked !== undefined) {
1728
- if (parkedHere.callKey !== callKey)
1729
- throw parkedCallChangedError(run.agents.length);
1730
- const decision = parkedResumeFor(parkedHere.parked.terminal.token);
1731
- if (decision === undefined) {
1732
- const at = now();
1733
- const reparkRec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(parkedHere.parked.model !== undefined ? { model: parkedHere.parked.model } : {}), replayed: true, status: "running", queuedAt: at, startedAt: at };
1734
- if (run.resume)
1735
- run.resume.replayed += 1;
1736
- run.agents.push(reparkRec);
1737
- if (phaseInstance)
1738
- agentPhaseOf.set(reparkRec, phaseInstance);
1739
- emit({ type: "agent_start", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), callKey, prompt, ...(parkedHere.parked.model !== undefined ? { model: parkedHere.parked.model } : {}), replayed: true, ts: at });
1740
- bceSpawn(callKey, label, agentOpts.agentType, true, parkedHere.parked.sessionId || undefined, parkedHere.parked.model);
1741
- return await parkAgentLeg(reparkRec, parkedHere.parked, [], false);
1812
+ const truth = opts.resumeFromRunId !== undefined ? parkedByOrdinal[run.agents.length] : undefined;
1813
+ if (truth !== undefined) {
1814
+ const d = disposeParkedOrdinal(truth, run.agents.length, callKey);
1815
+ if (d.kind !== "drive") {
1816
+ const settled = await recordParkDisposition(d, { label, callKey, groupId, phase, phaseInstance, prompt }, agentOpts);
1817
+ return { runId, label, callKey, steer: async () => Promise.reject(new Error(`workflow agent "${label}" settled at its parked ordinal — nothing to steer`)), result: async () => settled };
1742
1818
  }
1743
- drive = { token: decision.token, outcome: decision.outcome, ...(decision.inheritedGate !== undefined ? { inheritedGate: decision.inheritedGate } : {}), sessionId: parkedHere.parked.sessionId || undefined, paused: parkedHere.parked };
1819
+ drive = d.drive;
1744
1820
  }
1745
1821
  if (drive === undefined) {
1746
1822
  noteDivergence(run.agents.length, "ctx.agentStream results are never replayed");
@@ -1817,12 +1893,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1817
1893
  throw inheritedModelError;
1818
1894
  const typedSpec0 = applyWorkflowAgentType(spec, agentOpts.agentType, agentRegistry);
1819
1895
  const typedSpec = withWorkflowChildSeats(typedSpec0, inheritedModelSnap, inheritedThinkingSnap, runnerRoleMap(runner));
1820
- const framedSpec = withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema);
1821
- const authInherit = framedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1822
- const parkInherit = durableApprovalInherit(framedSpec);
1896
+ const seatedSpec = applyParentSpecSeat(withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema), opts.parentSpecSeat);
1897
+ const authInherit = seatedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1898
+ assertChildParkSeat(seatedSpec);
1899
+ const parkInherit = durableApprovalInherit(seatedSpec);
1823
1900
  const baseRunSpec = agentOpts.schema
1824
- ? { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1825
- : { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1901
+ ? { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1902
+ : { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1826
1903
  const preReviewSpec = childSessionId !== undefined ? { ...baseRunSpec, sessionId: childSessionId } : baseRunSpec;
1827
1904
  const runSpec = await reviewSpawnBeforeLaunch("ctx.agentStream", label, callKey, preReviewSpec, effectiveSignal);
1828
1905
  const enrichedForwardS = opts.onForwardEvent !== undefined
@@ -1864,7 +1941,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1864
1941
  }
1865
1942
  catch (err) {
1866
1943
  if (drive !== undefined && !finalized) {
1867
- emitRunLog(`[park] the parked leg "${label}" could not be driven — the resume was refused before the checkpoint was consumed (${err instanceof Error ? err.message : String(err)}); the park stands.`);
1944
+ emitRunLog(`[park] the parked leg "${label}" could not be driven — the resume was refused before the checkpoint was consumed (${withoutToken(errText(err), drive.token)}); the park stands.`);
1868
1945
  releaseLeg();
1869
1946
  return await parkAgentLeg(rec, drive.paused, [], false);
1870
1947
  }
@@ -2116,23 +2193,69 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2116
2193
  }
2117
2194
  replayByOrdinal[callKeyOrdinal(e.callKey)] = e;
2118
2195
  }
2196
+ const fromRunId = clampRunIdText(opts.resumeFromRunId);
2197
+ let prior = null;
2119
2198
  if (opts.store !== undefined) {
2120
- let prior;
2121
2199
  try {
2122
2200
  prior = await opts.store.get(opts.resumeFromRunId);
2123
2201
  }
2124
2202
  catch (err) {
2125
- throw new WorkflowJournalIncompatibleError(clampRunIdText(opts.resumeFromRunId), -1, `the prior run's record could not be read from the run store (${err instanceof Error ? err.message : String(err)}), so a lost park cannot be ruled out`);
2203
+ const errCode = err instanceof Error ? err.code : undefined;
2204
+ const errClass = err instanceof Error ? `${err.name}${typeof errCode === "string" ? `/${errCode}` : ""}` : typeof err;
2205
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED — the prior run's record could not be read from the run store (${errClass}), so its parks cannot be proven.`, { reason: "store_threw" });
2126
2206
  }
2127
- if (prior !== null && prior.scope === scope) {
2128
- for (const row of prior.agents) {
2129
- if (row.status !== "parked")
2130
- continue;
2131
- const ord = callKeyOrdinal(row.callKey);
2132
- if (replayByOrdinal[ord]?.parked === undefined) {
2133
- throw new WorkflowJournalIncompatibleError(clampRunIdText(opts.resumeFromRunId), ord, `the prior run's agent #${ord} ("${row.label.slice(0, 80)}") is PARKED on an approval checkpoint but its journal entry is missing (the park was not recorded) a resume would run that call live beside the pinned child`);
2134
- }
2207
+ if (prior !== null && prior.scope !== scope)
2208
+ prior = null;
2209
+ }
2210
+ if (opts.store !== undefined && prior === null)
2211
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED — the prior run's record is not in the run store under this scope, so its parks cannot be proven.`, { reason: "record_missing" });
2212
+ if (parkSeat !== undefined && prior !== null && prior.status === "running")
2213
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED the prior run is still recorded as running; a run that could park must have finished before it is resumed (a crashed park-capable run is decided through the deployment's approval channel, not resumed).`, { reason: "record_not_terminal" });
2214
+ if (prior !== null && !Array.isArray(prior.parks))
2215
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED — the prior run's record carries no parks list (written by an older engine, or stripped by the store's projection), so its parks cannot be proven.`, { reason: "parks_unreadable" });
2216
+ const candidates = new Map();
2217
+ const recordParks = prior !== null && Array.isArray(prior.parks) ? prior.parks : [];
2218
+ for (const raw of recordParks) {
2219
+ const e = raw;
2220
+ if (e === null || typeof e !== "object" || typeof e.callKey !== "string" || typeof e.sessionId !== "string" || typeof e.token !== "string" || typeof e.originRunId !== "string" || !/^\d+:/.test(e.callKey)) {
2221
+ throw parkBindingBroken(-1, "a recorded park entry is malformed or its call key has no ordinal");
2222
+ }
2223
+ candidates.set(callKeyOrdinal(e.callKey), { callKey: e.callKey, sessionId: e.sessionId, token: e.token, originRunId: e.originRunId });
2224
+ }
2225
+ for (const [ord, e] of replayByOrdinal.entries()) {
2226
+ if (e?.parked === undefined)
2227
+ continue;
2228
+ const fromJournal = { callKey: e.callKey, sessionId: e.parked.sessionId, token: e.parked.terminal.token, originRunId: opts.resumeFromRunId };
2229
+ const recorded = candidates.get(ord);
2230
+ if (recorded === undefined)
2231
+ candidates.set(ord, fromJournal);
2232
+ else if (recorded.token !== fromJournal.token || recorded.sessionId !== fromJournal.sessionId || recorded.callKey !== fromJournal.callKey)
2233
+ throw parkBindingBroken(ord, "the run record and the journal name different tokens, sessions or calls for it");
2234
+ }
2235
+ const inherited = [];
2236
+ for (const [ord, key] of [...candidates.entries()].sort((a, b) => a[0] - b[0])) {
2237
+ if (replayByOrdinal[ord]?.result !== undefined)
2238
+ continue;
2239
+ inherited.push(key);
2240
+ }
2241
+ run.parks = inherited;
2242
+ if (parkSeat === undefined) {
2243
+ if (inherited.length > 0) {
2244
+ throw new WorkflowParkRefusal("workflow.park_truth_unreadable", `startWorkflow: resume from "${fromRunId}" was REFUSED — it is responsible for ${inherited.length} recorded park(s) and this workflow holds no checkpoint store to prove them against (RunWorkflowOptions.checkpointStore); without the seat a parked call would run live beside its pinned child.`, { reason: "capability_not_armed" });
2245
+ }
2246
+ }
2247
+ else {
2248
+ for (const key of inherited) {
2249
+ const ord = callKeyOrdinal(key.callKey);
2250
+ let truth = await readParkTruth(parkSeat, key, ord);
2251
+ if (truth.kind === "not_pending" && parkedResumeFor(key.token) !== undefined)
2252
+ truth = await readParkTruth(parkSeat, key, ord);
2253
+ if (truth.kind === "pending") {
2254
+ const material = await parkMaterialOf(key, ord);
2255
+ if (material !== undefined)
2256
+ truth = { ...truth, material };
2135
2257
  }
2258
+ parkedByOrdinal[ord] = truth;
2136
2259
  }
2137
2260
  }
2138
2261
  run.resume = {
@@ -2179,7 +2302,6 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2179
2302
  await awaitLegsIdle(workflowSignal);
2180
2303
  throw parkedError();
2181
2304
  }
2182
- await carryUnresolvedParks();
2183
2305
  if (maxResultChars !== undefined) {
2184
2306
  let size = 0;
2185
2307
  try {
@@ -2214,7 +2336,6 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2214
2336
  catch (err) {
2215
2337
  if (parkedLeg !== undefined)
2216
2338
  await awaitLegsIdle(workflowSignal);
2217
- await carryUnresolvedParks();
2218
2339
  finalized = true;
2219
2340
  const unsettledOnFailure = settleActiveUsageBeats();
2220
2341
  stampBudgetOvershoot(unsettledOnFailure);
@@ -37,8 +37,8 @@ export interface AssembledPrompt {
37
37
  renderWithDate?: (date: string) => string;
38
38
  /** The initially-rendered system prompt. */
39
39
  systemPrompt: string;
40
- /** v1 `prompt.assembled` block view (id wordlist contract preserved). */
41
- legacyBlocks: Array<{
40
+ /** The `prompt.assembled` block view (the id wordlist is a downstream contract). */
41
+ blocks: Array<{
42
42
  id: string;
43
43
  text: string;
44
44
  }>;
@@ -23,7 +23,7 @@ function subsetPack(base, opts) {
23
23
  ...base,
24
24
  sections: base.sections
25
25
  .filter((s) => (opts.keep ? opts.keep.has(s.id) : true) && !(opts.exclude?.has(s.id) ?? false))
26
- .map((s) => (s.id === "core/role.base" && opts.roleBaseLegacyId ? { ...s, legacyBlockId: opts.roleBaseLegacyId } : s)),
26
+ .map((s) => (s.id === "core/role.base" && opts.roleBaseBlockId ? { ...s, blockId: opts.roleBaseBlockId } : s)),
27
27
  };
28
28
  }
29
29
  function verifyDeclaredContentHash(d, onWarn) {
@@ -65,7 +65,7 @@ function mountDeclarations(basePack, decls, onWarn) {
65
65
  cacheClass: "prefix-stable",
66
66
  carrier: "system.block",
67
67
  content: () => d.text,
68
- legacyBlockId: d.id,
68
+ blockId: d.id,
69
69
  ...(declaredContentHash !== undefined ? { declaredContentHash } : {}),
70
70
  origin: "provider",
71
71
  });
@@ -137,7 +137,7 @@ export function assemblePrompt(inputs) {
137
137
  verifyDeclaredContentHash(assembledIdentity, onWarn);
138
138
  onWarn?.(`prompt-provider stableBlocks: contentHash on "${assembledIdentity.id}" is ignored on the pass-through path (the text is emitted as core/role.base — a digest there would carry the wrong identity). Declare ONLY the role layer to keep the reconciliation anchor.`, "prompt-constitution");
139
139
  }
140
- const ptPack0 = subsetPack(SEMA_DEFAULT_PACK, { keep: OPAQUE_KEPT, roleBaseLegacyId: "provider.assembled" });
140
+ const ptPack0 = subsetPack(SEMA_DEFAULT_PACK, { keep: OPAQUE_KEPT, roleBaseBlockId: "provider.assembled" });
141
141
  const ptPack = { ...ptPack0, sections: ptPack0.sections.map((s) => (s.id === "core/role.base" ? { ...s, origin: "provider" } : s)) };
142
142
  let lastPT = compose(ptPack, renderInputsPT(inputs.date));
143
143
  return {
@@ -150,8 +150,8 @@ export function assemblePrompt(inputs) {
150
150
  get systemPrompt() {
151
151
  return lastPT.text;
152
152
  },
153
- get legacyBlocks() {
154
- return lastPT.legacyBlocks;
153
+ get blocks() {
154
+ return lastPT.blocks;
155
155
  },
156
156
  get sections() {
157
157
  return lastPT.sections;
@@ -180,13 +180,13 @@ export function assemblePrompt(inputs) {
180
180
  constitution = "replaced";
181
181
  roleBase = roleOut;
182
182
  roleBaseOrigin = "provider";
183
- pack = subsetPack(SEMA_DEFAULT_PACK, { exclude: REPLACE_ALL_EXCLUDED, roleBaseLegacyId: "provider.replace_all" });
183
+ pack = subsetPack(SEMA_DEFAULT_PACK, { exclude: REPLACE_ALL_EXCLUDED, roleBaseBlockId: "provider.replace_all" });
184
184
  }
185
185
  else if (roleOut.includes(CYBER_RISK) && roleOut.includes(URL_SAFETY) && roleOut.includes(HARNESS_SECTION_ANCHOR)) {
186
186
  constitution = "provider-assembled";
187
187
  roleBase = roleOut;
188
188
  roleBaseOrigin = "provider";
189
- pack = subsetPack(SEMA_DEFAULT_PACK, { keep: OPAQUE_KEPT, roleBaseLegacyId: "provider.assembled" });
189
+ pack = subsetPack(SEMA_DEFAULT_PACK, { keep: OPAQUE_KEPT, roleBaseBlockId: "provider.assembled" });
190
190
  onWarn?.("prompt-constitution: this PromptProvider returns an already-assembled prompt (constitution anchor found). Core now appends the constitution structurally — return ONLY the role base from stableSystem (or set replaceAll:true to own the whole base). Passed through un-doubled.", "prompt-constitution");
191
191
  }
192
192
  else {
@@ -227,8 +227,8 @@ export function assemblePrompt(inputs) {
227
227
  get systemPrompt() {
228
228
  return last.text;
229
229
  },
230
- get legacyBlocks() {
231
- return last.legacyBlocks;
230
+ get blocks() {
231
+ return last.blocks;
232
232
  },
233
233
  get sections() {
234
234
  return last.sections;