@sema-agent/core 7.16.0 → 7.17.1

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 (207) hide show
  1. package/CHANGELOG.md +209 -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-engine/engine.js +2 -1
  49. package/dist/core/memory-engine/layout.d.ts +18 -6
  50. package/dist/core/memory-engine/layout.js +40 -21
  51. package/dist/core/memory-recall.d.ts +0 -8
  52. package/dist/core/memory.d.ts +1 -1
  53. package/dist/core/oracle-isolation.js +2 -2
  54. package/dist/core/parent-spec-seat.d.ts +66 -0
  55. package/dist/core/parent-spec-seat.js +47 -0
  56. package/dist/core/permission-rule-model.d.ts +11 -6
  57. package/dist/core/permission-rule-model.js +53 -37
  58. package/dist/core/permission-rules.js +8 -3
  59. package/dist/core/physical-path.d.ts +37 -0
  60. package/dist/core/physical-path.js +30 -0
  61. package/dist/core/protocol-naming.d.ts +2 -2
  62. package/dist/core/remote-env.d.ts +1 -1
  63. package/dist/core/retired-keys.d.ts +19 -0
  64. package/dist/core/retired-keys.js +15 -0
  65. package/dist/core/runner/active-skill-scope.js +3 -3
  66. package/dist/core/runner/assemble-result.d.ts +15 -13
  67. package/dist/core/runner/assemble-result.js +5 -4
  68. package/dist/core/runner/compaction-call-options.d.ts +25 -89
  69. package/dist/core/runner/contracts.d.ts +11 -10
  70. package/dist/core/runner/gate-exit.js +15 -1
  71. package/dist/core/runner/halt-attribution.d.ts +35 -0
  72. package/dist/core/runner/halt-attribution.js +16 -0
  73. package/dist/core/runner/memory-consolidation.d.ts +0 -2
  74. package/dist/core/runner/memory-consolidation.js +0 -1
  75. package/dist/core/runner/prepare-artifact.d.ts +52 -0
  76. package/dist/core/runner/prepare-artifact.js +63 -0
  77. package/dist/core/runner/prepare-caps-and-workflow.d.ts +4 -2
  78. package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
  79. package/dist/core/runner/prepare-config-doors.d.ts +3 -3
  80. package/dist/core/runner/prepare-config-doors.js +5 -2
  81. package/dist/core/runner/prepare-context-lane.d.ts +2 -2
  82. package/dist/core/runner/prepare-context-lane.js +2 -13
  83. package/dist/core/runner/prepare-file-history.d.ts +1 -1
  84. package/dist/core/runner/prepare-file-history.js +3 -52
  85. package/dist/core/runner/prepare-hands-readface.d.ts +4 -7
  86. package/dist/core/runner/prepare-hands-readface.js +3 -2
  87. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -2
  88. package/dist/core/runner/prepare-inherited-gate.js +7 -8
  89. package/dist/core/runner/prepare-memory.d.ts +4 -6
  90. package/dist/core/runner/prepare-memory.js +4 -2
  91. package/dist/core/runner/prepare-policy-chain.d.ts +3 -3
  92. package/dist/core/runner/prepare-policy-chain.js +15 -30
  93. package/dist/core/runner/prepare-prompt-assembly.js +1 -1
  94. package/dist/core/runner/prepare-question-face.js +2 -1
  95. package/dist/core/runner/prepare-safety-scan.d.ts +1 -1
  96. package/dist/core/runner/prepare-suspend-saga.js +0 -2
  97. package/dist/core/runner/prepare-task.js +12 -11
  98. package/dist/core/runner/prepare-wiring-manifest.js +9 -7
  99. package/dist/core/runner/resume-admission.js +2 -1
  100. package/dist/core/runner/resume-internals-and-config.js +4 -0
  101. package/dist/core/runner/retired-runner-deps.d.ts +3 -0
  102. package/dist/core/runner/retired-runner-deps.js +7 -0
  103. package/dist/core/runner/run-harness-handlers.js +5 -3
  104. package/dist/core/runner/run-settle-and-teardown.d.ts +1 -1
  105. package/dist/core/runner/run-settle-and-teardown.js +3 -1
  106. package/dist/core/runner/session-file-state-replay.js +3 -3
  107. package/dist/core/runner/session-rule-policy.js +3 -4
  108. package/dist/core/runner/stream-halt-verbs.js +1 -1
  109. package/dist/core/runner/stream-steer-verb.js +1 -4
  110. package/dist/core/runner/workspace-path.js +3 -0
  111. package/dist/core/runner-deps.d.ts +30 -49
  112. package/dist/core/safe-notify.d.ts +1 -1
  113. package/dist/core/sensitive-path-policy.d.ts +1 -1
  114. package/dist/core/sensitive-path-policy.js +11 -12
  115. package/dist/core/session.d.ts +2 -2
  116. package/dist/core/skill-tool-specifier.d.ts +1 -1
  117. package/dist/core/skills-directory.d.ts +1 -1
  118. package/dist/core/skills-directory.js +4 -3
  119. package/dist/core/spec-contract.js +5 -4
  120. package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
  121. package/dist/core/strategy-store.d.ts +8 -11
  122. package/dist/core/task-event.d.ts +20 -17
  123. package/dist/core/task-limits.d.ts +1 -1
  124. package/dist/core/task-notification.d.ts +5 -5
  125. package/dist/core/task-registry-agent.d.ts +5 -5
  126. package/dist/core/task-registry-monitor.d.ts +2 -2
  127. package/dist/core/task-registry-shared.d.ts +32 -28
  128. package/dist/core/task-registry-shared.js +4 -13
  129. package/dist/core/task-registry.d.ts +8 -8
  130. package/dist/core/task-result.d.ts +37 -37
  131. package/dist/core/task-spec.d.ts +5 -5
  132. package/dist/core/task-stream.d.ts +16 -11
  133. package/dist/core/tool-catalog-entries.d.ts +1 -0
  134. package/dist/core/tool-catalog-entries.js +6 -1
  135. package/dist/core/tool-conformance.d.ts +0 -2
  136. package/dist/core/tool-conformance.js +1 -3
  137. package/dist/core/tool-errors.d.ts +5 -0
  138. package/dist/core/tool-errors.js +1 -0
  139. package/dist/core/tool-policy.d.ts +66 -22
  140. package/dist/core/tool-policy.js +31 -5
  141. package/dist/core/tool-result-budget.d.ts +1 -1
  142. package/dist/core/tool-result-store.d.ts +11 -11
  143. package/dist/core/tool-spec.d.ts +34 -38
  144. package/dist/core/tools.d.ts +2 -2
  145. package/dist/core/trace.d.ts +12 -15
  146. package/dist/core/untrusted-text.d.ts +1 -1
  147. package/dist/core/wiring-manifest.d.ts +4 -3
  148. package/dist/core/workflow-journal-store.d.ts +11 -11
  149. package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
  150. package/dist/engine/execution-env/node-execution-env.js +8 -5
  151. package/dist/engine/harness/agent-harness.d.ts +47 -4
  152. package/dist/engine/harness/agent-harness.js +52 -9
  153. package/dist/engine/loop/agent-loop.js +15 -8
  154. package/dist/engine/loop/types.d.ts +5 -0
  155. package/dist/index.d.ts +20 -14
  156. package/dist/index.js +16 -10
  157. package/dist/internal/harness.d.ts +1 -0
  158. package/dist/orchestration/run-spec.d.ts +3 -0
  159. package/dist/orchestration/run-spec.js +3 -3
  160. package/dist/orchestration/run-workflow-tool.d.ts +12 -6
  161. package/dist/orchestration/run-workflow-tool.js +23 -5
  162. package/dist/orchestration/workflow-primitives.d.ts +4 -2
  163. package/dist/orchestration/workflow-primitives.js +1 -4
  164. package/dist/orchestration/workflow-script-store.js +9 -25
  165. package/dist/orchestration/workflow-types.d.ts +34 -4
  166. package/dist/orchestration/workflow-types.js +2 -0
  167. package/dist/orchestration/workflow.d.ts +39 -7
  168. package/dist/orchestration/workflow.js +237 -113
  169. package/dist/prompt-assembly/assemble.d.ts +2 -2
  170. package/dist/prompt-assembly/assemble.js +9 -9
  171. package/dist/prompt-assembly/composer.d.ts +6 -7
  172. package/dist/prompt-assembly/composer.js +10 -10
  173. package/dist/prompt-assembly/epoch.js +1 -1
  174. package/dist/prompt-assembly/event-registry.d.ts +0 -21
  175. package/dist/prompt-assembly/packs/sema-default.js +30 -30
  176. package/dist/prompt-assembly/types.d.ts +14 -14
  177. package/dist/prompts/coordinator.d.ts +0 -13
  178. package/dist/prompts/coordinator.js +0 -3
  179. package/dist/stores/cc/task-list-store.js +2 -10
  180. package/dist/stores/file/checkpoint-store.js +1 -5
  181. package/dist/stores/file/file-history-store.js +2 -2
  182. package/dist/stores/file/fs-atomic.d.ts +15 -18
  183. package/dist/stores/file/fs-atomic.js +4 -14
  184. package/dist/stores/file/mailbox-store.d.ts +7 -11
  185. package/dist/stores/file/mailbox-store.js +4 -11
  186. package/dist/stores/file/permission-rule-store.d.ts +4 -35
  187. package/dist/stores/file/permission-rule-store.js +4 -148
  188. package/dist/tools/artifact/artifact-text.d.ts +62 -0
  189. package/dist/tools/artifact/artifact-text.js +68 -0
  190. package/dist/tools/artifact/artifact-tool.d.ts +52 -0
  191. package/dist/tools/artifact/artifact-tool.js +351 -0
  192. package/dist/tools/artifact/local-stub.d.ts +26 -0
  193. package/dist/tools/artifact/local-stub.js +297 -0
  194. package/dist/tools/fs/bash-readonly-classifier.d.ts +90 -8
  195. package/dist/tools/fs/bash-readonly-classifier.js +294 -70
  196. package/dist/tools/fs/fs-bash.d.ts +14 -10
  197. package/dist/tools/fs/fs-bash.js +50 -33
  198. package/dist/tools/fs/fs-search-tools.js +5 -2
  199. package/dist/tools/fs/fs-shared.d.ts +2 -0
  200. package/dist/tools/fs/fs-shared.js +3 -11
  201. package/dist/tools/fs/index.d.ts +4 -0
  202. package/dist/tools/fs/index.js +4 -1
  203. package/dist/tools/fs/safety.d.ts +117 -1
  204. package/dist/tools/fs/safety.js +131 -31
  205. package/dist/tools/fs/search.js +8 -6
  206. package/package.json +2 -1
  207. package/test/export-surface.snapshot.json +119 -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;
@@ -449,6 +460,9 @@ export const workflowResumeClaimFallback = {
449
460
  table.delete(key);
450
461
  },
451
462
  };
463
+ function parkLaneArmed(opts) {
464
+ return opts.store !== undefined && opts.checkpointStore !== undefined;
465
+ }
452
466
  export function startWorkflow(runner, fn, opts = {}, internals) {
453
467
  const alsDepth = currentWorkflowDepth();
454
468
  const internalDepth = internals?.workflowDepth;
@@ -461,8 +475,11 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
461
475
  if (opts.resumeFromRunId !== undefined && opts.journalStore === undefined) {
462
476
  throw new Error("runWorkflow: resumeFromRunId requires a journalStore to load the prior run's journal");
463
477
  }
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");
478
+ if (opts.checkpointStore !== undefined && !parkLaneArmed(opts)) {
479
+ 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", {});
480
+ }
481
+ if (opts.defaultDurableApproval !== undefined && !parkLaneArmed(opts)) {
482
+ 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
483
  }
467
484
  if (opts.parkedResume !== undefined && opts.resumeFromRunId === undefined) {
468
485
  throw new Error("runWorkflow: parkedResume drives a PARKED ordinal of a prior run and requires resumeFromRunId");
@@ -507,22 +524,23 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
507
524
  let persistTail = Promise.resolve();
508
525
  const persist = (mode) => {
509
526
  if (!store)
510
- return persistTail;
527
+ return Promise.resolve({ ok: true });
511
528
  const snapshot = structuredClone(run);
512
529
  const next = persistTail.then(async () => {
513
530
  try {
514
531
  if (mode === "create") {
515
532
  snapshot.rev = storeRev;
516
533
  await store.put(runId, snapshot);
534
+ return { ok: true };
517
535
  }
518
- else {
519
- snapshot.rev = storeRev;
520
- const won = await store.update(runId, scope, snapshot, { rev: storeRev });
521
- if (won)
522
- storeRev += 1;
523
- }
536
+ snapshot.rev = storeRev;
537
+ const won = await store.update(runId, scope, snapshot, { rev: storeRev });
538
+ if (won)
539
+ storeRev += 1;
540
+ return won ? { ok: true } : { ok: false, cause: "cas_lost" };
524
541
  }
525
542
  catch {
543
+ return { ok: false, cause: "threw" };
526
544
  }
527
545
  });
528
546
  persistTail = next;
@@ -546,6 +564,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
546
564
  startedAt: 0,
547
565
  })),
548
566
  agents: [],
567
+ ...(opts.resumeFromRunId === undefined ? { parks: [] } : {}),
549
568
  groups: [],
550
569
  stats: { tokens: 0, turns: 0, costMicroUsd: 0, nested: { tokens: 0, turns: 0, tasks: 0, costMicroUsd: 0 } },
551
570
  startedAt: t0,
@@ -642,6 +661,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
642
661
  const sem = createSemaphore(concurrency);
643
662
  const spawnAttribution = {
644
663
  isDelegatedChild: true,
664
+ ...(opts.questionFaceStripped === true ? { questionFaceStripped: true } : {}),
645
665
  ...(opts.interactionPosture !== undefined ? { parentInteractionPosture: opts.interactionPosture } : {}),
646
666
  ...(opts.parentToolCallId !== undefined ? { parentToolCallId: opts.parentToolCallId } : {}),
647
667
  ...(opts.parentTaskId !== undefined ? { parentTaskId: opts.parentTaskId } : {}),
@@ -819,25 +839,92 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
819
839
  });
820
840
  }
821
841
  };
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;
842
+ const parkSeat = opts.checkpointStore;
843
+ const parkedByOrdinal = [];
844
+ const errText = (err) => (err instanceof Error ? err.message : String(err));
845
+ const withoutToken = (text, token) => (token === "" ? text : text.split(token).join("<token>"));
846
+ 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 });
847
+ const readParkTruth = async (seat, key, ordinal) => {
848
+ if (key.token === "" || key.sessionId === "")
849
+ throw parkBindingBroken(ordinal, "the record names no token or no session for it");
850
+ const read = await finalizeWithin(timers, new Promise((resolve) => resolve(seat.get(key.token))).then((cp) => ({ cp }), (err) => ({ err })), "timeout");
851
+ if (read === "timeout" || "err" in read) {
852
+ 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" });
853
+ }
854
+ const cp = read.cp;
855
+ if (cp === null)
856
+ return { kind: "not_pending", key, status: "absent" };
857
+ if (cp.token !== key.token)
858
+ throw parkBindingBroken(ordinal, "the checkpoint store answered a row under another token");
859
+ if (cp.sessionId !== key.sessionId)
860
+ throw parkBindingBroken(ordinal, "the checkpoint belongs to another session than the parked row");
861
+ if (cp.status !== "pending")
862
+ return { kind: "not_pending", key, status: cp.status, ...(cp.checkpointId !== undefined ? { checkpointId: cp.checkpointId } : {}) };
863
+ return { kind: "pending", key, cp };
864
+ };
865
+ const parkMaterialOf = async (key, ordinal) => {
866
+ let entry;
867
+ if (key.originRunId === opts.resumeFromRunId)
868
+ entry = replayByOrdinal[ordinal];
869
+ else if (journalStore) {
870
+ const loaded = await finalizeWithin(timers, new Promise((resolve) => resolve(journalStore.load(key.originRunId, scope))).then((entries) => ({ entries }), (err) => ({ err })), "timeout");
871
+ if (loaded === "timeout" || "err" in loaded) {
872
+ 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)}).`);
873
+ }
874
+ else {
875
+ entry = loaded.entries.filter((e) => e.parked !== undefined && callKeyOrdinal(e.callKey) === ordinal).at(-1);
835
876
  }
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.`);
877
+ }
878
+ const p = entry?.parked;
879
+ if (p === undefined || entry === undefined || p.terminal.token !== key.token || p.sessionId !== key.sessionId || entry.callKey !== key.callKey)
880
+ return undefined;
881
+ return p;
882
+ };
883
+ const degradedParkMaterial = (cp) => ({
884
+ taskId: cp.sourceTaskId ?? "",
885
+ sessionId: cp.sessionId,
886
+ result: "",
887
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
888
+ terminal: { kind: "paused", gate: cp.gate, token: cp.token, ...(cp.checkpointId !== undefined ? { checkpointId: cp.checkpointId } : {}) },
889
+ });
890
+ const disposeParkedOrdinal = (truth, ordinal, callKey) => {
891
+ if (truth.key.callKey !== callKey)
892
+ throw parkedCallChangedError(ordinal);
893
+ const entry = parkedResumeFor(truth.key.token);
894
+ if (truth.kind === "not_pending") {
895
+ if (entry === undefined) {
896
+ 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" });
840
897
  }
898
+ const result = {
899
+ taskId: callKey,
900
+ sessionId: truth.key.sessionId,
901
+ terminal: {
902
+ kind: "failed",
903
+ code: "workflow.park_not_pending",
904
+ 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.`,
905
+ },
906
+ result: "",
907
+ stats: { turns: 0, tokens: 0, costMicroUsd: 0, usageMissing: true },
908
+ };
909
+ return { kind: "settle", result };
910
+ }
911
+ const paused = truth.material ?? degradedParkMaterial(truth.cp);
912
+ if (truth.material === undefined)
913
+ 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).`);
914
+ if (entry?.outcome !== undefined) {
915
+ return { kind: "drive", drive: { token: entry.token, outcome: entry.outcome, ...(entry.inheritedGate !== undefined ? { inheritedGate: entry.inheritedGate } : {}), sessionId: paused.sessionId || undefined, paused } };
916
+ }
917
+ return { kind: "repark", paused };
918
+ };
919
+ const assertChildParkSeat = (s) => {
920
+ const seat = s.checkpointStore;
921
+ if (seat === undefined || seat === "disabled")
922
+ return;
923
+ if (!parkLaneArmed(opts)) {
924
+ 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.", {});
925
+ }
926
+ if (seat !== parkSeat) {
927
+ 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" });
841
928
  }
842
929
  };
843
930
  const driveNeverStarted = (r) => r.terminal.kind === "failed" && (r.terminal.code === "resume.reopened_unstarted" || r.terminal.code === "resume.reopen_failed_unstarted");
@@ -895,10 +982,8 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
895
982
  if (dbg)
896
983
  console.error(`[sema:wf-journal] runId=${runId} callKey=${callKey} TOMBSTONE-ERROR ${err instanceof Error ? err.message : String(err)}`);
897
984
  }
898
- writtenOrdinals.add(callKeyOrdinal(callKey));
899
985
  return;
900
986
  }
901
- writtenOrdinals.add(callKeyOrdinal(callKey));
902
987
  const p = journalStore.append(runId, scope, { callKey, result: stored });
903
988
  journalTail = journalTail.then(() => p).catch(() => undefined);
904
989
  try {
@@ -915,24 +1000,9 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
915
1000
  const journalAppendParked = async (callKey, paused) => {
916
1001
  if (!journalStore)
917
1002
  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;
1003
+ const p = journalStore.append(runId, scope, { callKey, parked: paused });
1004
+ journalTail = journalTail.then(() => p).catch(() => undefined);
1005
+ await p;
936
1006
  };
937
1007
  const parkAgentLeg = async (rec, paused, activityTail, chargeLive) => {
938
1008
  const s = paused.stats;
@@ -951,19 +1021,50 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
951
1021
  rec.activity = activityTail.slice();
952
1022
  foldLegApprovalReport(rec, paused);
953
1023
  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
1024
  bceLive.delete(waIdOf(rec.callKey));
956
1025
  if (parkedLeg === undefined)
957
1026
  parkedLeg = { label: rec.label, callKey: rec.callKey, paused };
1027
+ const ordinal = callKeyOrdinal(rec.callKey);
1028
+ const parks = run.parks ?? (run.parks = []);
1029
+ const at = parks.findIndex((e) => e.callKey === rec.callKey);
1030
+ const key = { callKey: rec.callKey, sessionId: paused.sessionId, token: paused.terminal.token, originRunId: at === -1 ? runId : parks[at].originRunId };
1031
+ if (at === -1)
1032
+ parks.push(key);
1033
+ else
1034
+ parks[at] = key;
1035
+ void persist("update");
958
1036
  try {
959
1037
  await journalAppendParked(rec.callKey, paused);
1038
+ key.originRunId = runId;
960
1039
  }
961
1040
  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.");
1041
+ 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)}) — ` +
1042
+ "the park stands: a resume of this run finds it through the run record and the checkpoint store.");
1043
+ }
1044
+ const persisted = await finalizeWithin(timers, persist("update"), { ok: false, cause: "timeout" });
1045
+ if (!persisted.ok) {
1046
+ emitRunLog(`resume-journal: the PARKED leg at ordinal ${ordinal} ("${rec.label.slice(0, 80)}") could not be persisted on the run record (${persisted.cause}) — ` +
1047
+ "the park stands: a resume of this run finds it through the journal and the checkpoint store.");
964
1048
  }
965
1049
  throw parkedError();
966
1050
  };
1051
+ const recordParkDisposition = async (d, call, agentOpts) => {
1052
+ const { label, callKey, groupId, phase, phaseInstance, prompt } = call;
1053
+ const at = now();
1054
+ const model = d.kind === "repark" ? d.paused.model : undefined;
1055
+ const rec = { label, callKey, ...(groupId !== undefined ? { groupId } : {}), phase, prompt, ...(model !== undefined ? { model } : {}), ...(d.kind === "repark" ? { replayed: true } : {}), status: "running", queuedAt: at, startedAt: at };
1056
+ if (d.kind === "repark" && run.resume)
1057
+ run.resume.replayed += 1;
1058
+ run.agents.push(rec);
1059
+ if (phaseInstance)
1060
+ agentPhaseOf.set(rec, phaseInstance);
1061
+ emit({ type: "agent_start", runId, label, phase, ...(groupId !== undefined ? { groupId } : {}), callKey, prompt, ...(model !== undefined ? { model } : {}), ...(d.kind === "repark" ? { replayed: true } : {}), ts: at });
1062
+ bceSpawn(callKey, label, agentOpts.agentType, d.kind === "repark", (d.kind === "repark" ? d.paused.sessionId : d.result.sessionId) || undefined, model);
1063
+ if (d.kind === "repark")
1064
+ return await parkAgentLeg(rec, d.paused, [], false);
1065
+ await settleAgentResult(rec, d.result, [], agentOpts, "awaited");
1066
+ return d.result;
1067
+ };
967
1068
  const createActivityCapture = (callKey, label, groupId, rec) => {
968
1069
  const tail = [];
969
1070
  let beatCount = 0;
@@ -1168,10 +1269,12 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1168
1269
  }
1169
1270
  }
1170
1271
  const inheritedThinkingSnap = spec.thinking === undefined ? opts.defaultThinking?.() : undefined;
1272
+ const inheritedPrincipal = spec.principal === undefined ? opts.parentSpecSeat?.principal : undefined;
1171
1273
  const specForIdentity = {
1172
1274
  ...spec,
1173
1275
  ...(inheritedModelSnap !== undefined ? { model: inheritedModelSnap } : {}),
1174
1276
  ...(inheritedThinkingSnap !== undefined ? { thinking: inheritedThinkingSnap } : {}),
1277
+ ...(inheritedPrincipal !== undefined ? { principal: inheritedPrincipal } : {}),
1175
1278
  };
1176
1279
  const callKey = workflowAgentCallKey(run.agents.length, specForIdentity, agentOpts);
1177
1280
  const prompt = boundedRedactedSummary(spec.systemPrompt ? `${spec.systemPrompt}\n\n${spec.objective}` : spec.objective, MAX_TRANSCRIPT_CHARS);
@@ -1327,27 +1430,12 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1327
1430
  const { label, phase, phaseInstance, groupId, inheritedModelSnap, inheritedModelError, inheritedThinkingSnap, callKey, prompt, model } = prepareAgentCall(spec, agentOpts);
1328
1431
  let drive;
1329
1432
  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
- }
1433
+ const truth = opts.resumeFromRunId !== undefined ? parkedByOrdinal[run.agents.length] : undefined;
1434
+ if (truth !== undefined) {
1435
+ const d = disposeParkedOrdinal(truth, run.agents.length, callKey);
1436
+ if (d.kind !== "drive")
1437
+ return await recordParkDisposition(d, { label, callKey, groupId, phase, phaseInstance, prompt }, agentOpts);
1438
+ drive = d.drive;
1351
1439
  }
1352
1440
  if (parkedLeg !== undefined && drive === undefined)
1353
1441
  throw parkedError();
@@ -1442,12 +1530,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1442
1530
  throw inheritedModelError;
1443
1531
  const typedSpec0 = applyWorkflowAgentType(spec, agentOpts.agentType, agentRegistry);
1444
1532
  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);
1533
+ const seatedSpec = applyParentSpecSeat(withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema), opts.parentSpecSeat);
1534
+ const authInherit = seatedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1535
+ assertChildParkSeat(seatedSpec);
1536
+ const parkInherit = durableApprovalInherit(seatedSpec);
1448
1537
  const preReviewSpec = agentOpts.schema
1449
- ? { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1450
- : { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1538
+ ? { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1539
+ : { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1451
1540
  const runSpec = await reviewSpawnBeforeLaunch("ctx.agent", label, callKey, preReviewSpec, effectiveSignal);
1452
1541
  const enrichedForward = opts.onForwardEvent !== undefined
1453
1542
  ? (e) => {
@@ -1645,7 +1734,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1645
1734
  }
1646
1735
  catch (err) {
1647
1736
  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.`);
1737
+ 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
1738
  return await parkAgentLeg(rec, drive.paused, activityTail, false);
1650
1739
  }
1651
1740
  if (!finalized) {
@@ -1723,24 +1812,14 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1723
1812
  }
1724
1813
  const { label, phase, phaseInstance, groupId, inheritedModelSnap, inheritedModelError, inheritedThinkingSnap, callKey, prompt, model } = prepareAgentCall(spec, agentOpts);
1725
1814
  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);
1815
+ const truth = opts.resumeFromRunId !== undefined ? parkedByOrdinal[run.agents.length] : undefined;
1816
+ if (truth !== undefined) {
1817
+ const d = disposeParkedOrdinal(truth, run.agents.length, callKey);
1818
+ if (d.kind !== "drive") {
1819
+ const settled = await recordParkDisposition(d, { label, callKey, groupId, phase, phaseInstance, prompt }, agentOpts);
1820
+ 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
1821
  }
1743
- drive = { token: decision.token, outcome: decision.outcome, ...(decision.inheritedGate !== undefined ? { inheritedGate: decision.inheritedGate } : {}), sessionId: parkedHere.parked.sessionId || undefined, paused: parkedHere.parked };
1822
+ drive = d.drive;
1744
1823
  }
1745
1824
  if (drive === undefined) {
1746
1825
  noteDivergence(run.agents.length, "ctx.agentStream results are never replayed");
@@ -1817,12 +1896,13 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1817
1896
  throw inheritedModelError;
1818
1897
  const typedSpec0 = applyWorkflowAgentType(spec, agentOpts.agentType, agentRegistry);
1819
1898
  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);
1899
+ const seatedSpec = applyParentSpecSeat(withWorkflowChildPersona(typedSpec, agentOpts.schema ?? typedSpec.outputSchema), opts.parentSpecSeat);
1900
+ const authInherit = seatedSpec.getApiKeyAndHeaders === undefined && opts.defaultGetApiKeyAndHeaders !== undefined ? { getApiKeyAndHeaders: opts.defaultGetApiKeyAndHeaders } : {};
1901
+ assertChildParkSeat(seatedSpec);
1902
+ const parkInherit = durableApprovalInherit(seatedSpec);
1823
1903
  const baseRunSpec = agentOpts.schema
1824
- ? { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1825
- : { ...framedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1904
+ ? { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal, outputSchema: agentOpts.schema }
1905
+ : { ...seatedSpec, ...authInherit, ...parkInherit, signal: effectiveSignal };
1826
1906
  const preReviewSpec = childSessionId !== undefined ? { ...baseRunSpec, sessionId: childSessionId } : baseRunSpec;
1827
1907
  const runSpec = await reviewSpawnBeforeLaunch("ctx.agentStream", label, callKey, preReviewSpec, effectiveSignal);
1828
1908
  const enrichedForwardS = opts.onForwardEvent !== undefined
@@ -1864,7 +1944,7 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
1864
1944
  }
1865
1945
  catch (err) {
1866
1946
  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.`);
1947
+ 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
1948
  releaseLeg();
1869
1949
  return await parkAgentLeg(rec, drive.paused, [], false);
1870
1950
  }
@@ -2116,23 +2196,69 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2116
2196
  }
2117
2197
  replayByOrdinal[callKeyOrdinal(e.callKey)] = e;
2118
2198
  }
2199
+ const fromRunId = clampRunIdText(opts.resumeFromRunId);
2200
+ let prior = null;
2119
2201
  if (opts.store !== undefined) {
2120
- let prior;
2121
2202
  try {
2122
2203
  prior = await opts.store.get(opts.resumeFromRunId);
2123
2204
  }
2124
2205
  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`);
2206
+ const errCode = err instanceof Error ? err.code : undefined;
2207
+ const errClass = err instanceof Error ? `${err.name}${typeof errCode === "string" ? `/${errCode}` : ""}` : typeof err;
2208
+ 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
2209
  }
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
- }
2210
+ if (prior !== null && prior.scope !== scope)
2211
+ prior = null;
2212
+ }
2213
+ if (opts.store !== undefined && prior === null)
2214
+ 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" });
2215
+ if (parkSeat !== undefined && prior !== null && prior.status === "running")
2216
+ 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" });
2217
+ if (prior !== null && !Array.isArray(prior.parks))
2218
+ 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" });
2219
+ const candidates = new Map();
2220
+ const recordParks = prior !== null && Array.isArray(prior.parks) ? prior.parks : [];
2221
+ for (const raw of recordParks) {
2222
+ const e = raw;
2223
+ 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)) {
2224
+ throw parkBindingBroken(-1, "a recorded park entry is malformed or its call key has no ordinal");
2225
+ }
2226
+ candidates.set(callKeyOrdinal(e.callKey), { callKey: e.callKey, sessionId: e.sessionId, token: e.token, originRunId: e.originRunId });
2227
+ }
2228
+ for (const [ord, e] of replayByOrdinal.entries()) {
2229
+ if (e?.parked === undefined)
2230
+ continue;
2231
+ const fromJournal = { callKey: e.callKey, sessionId: e.parked.sessionId, token: e.parked.terminal.token, originRunId: opts.resumeFromRunId };
2232
+ const recorded = candidates.get(ord);
2233
+ if (recorded === undefined)
2234
+ candidates.set(ord, fromJournal);
2235
+ else if (recorded.token !== fromJournal.token || recorded.sessionId !== fromJournal.sessionId || recorded.callKey !== fromJournal.callKey)
2236
+ throw parkBindingBroken(ord, "the run record and the journal name different tokens, sessions or calls for it");
2237
+ }
2238
+ const inherited = [];
2239
+ for (const [ord, key] of [...candidates.entries()].sort((a, b) => a[0] - b[0])) {
2240
+ if (replayByOrdinal[ord]?.result !== undefined)
2241
+ continue;
2242
+ inherited.push(key);
2243
+ }
2244
+ run.parks = inherited;
2245
+ if (parkSeat === undefined) {
2246
+ if (inherited.length > 0) {
2247
+ 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" });
2248
+ }
2249
+ }
2250
+ else {
2251
+ for (const key of inherited) {
2252
+ const ord = callKeyOrdinal(key.callKey);
2253
+ let truth = await readParkTruth(parkSeat, key, ord);
2254
+ if (truth.kind === "not_pending" && parkedResumeFor(key.token) !== undefined)
2255
+ truth = await readParkTruth(parkSeat, key, ord);
2256
+ if (truth.kind === "pending") {
2257
+ const material = await parkMaterialOf(key, ord);
2258
+ if (material !== undefined)
2259
+ truth = { ...truth, material };
2135
2260
  }
2261
+ parkedByOrdinal[ord] = truth;
2136
2262
  }
2137
2263
  }
2138
2264
  run.resume = {
@@ -2179,7 +2305,6 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2179
2305
  await awaitLegsIdle(workflowSignal);
2180
2306
  throw parkedError();
2181
2307
  }
2182
- await carryUnresolvedParks();
2183
2308
  if (maxResultChars !== undefined) {
2184
2309
  let size = 0;
2185
2310
  try {
@@ -2214,7 +2339,6 @@ export function startWorkflow(runner, fn, opts = {}, internals) {
2214
2339
  catch (err) {
2215
2340
  if (parkedLeg !== undefined)
2216
2341
  await awaitLegsIdle(workflowSignal);
2217
- await carryUnresolvedParks();
2218
2342
  finalized = true;
2219
2343
  const unsettledOnFailure = settleActiveUsageBeats();
2220
2344
  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;