@sema-agent/core 7.15.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.
- package/CHANGELOG.md +202 -0
- package/dist/agents/builtin-agents.d.ts +3 -3
- package/dist/agents/cross-session-envelope.d.ts +0 -1
- package/dist/agents/cross-session-envelope.js +0 -1
- package/dist/agents/peer-notices.d.ts +0 -9
- package/dist/agents/peer-notices.js +0 -63
- package/dist/agents/subagent.js +7 -15
- package/dist/brain/request-params.d.ts +2 -2
- package/dist/config/defaults.d.ts +0 -7
- package/dist/config/defaults.js +0 -2
- package/dist/core/artifact-host.d.ts +218 -0
- package/dist/core/artifact-host.js +92 -0
- package/dist/core/ask-origin.d.ts +29 -3
- package/dist/core/ask-origin.js +17 -9
- package/dist/core/ask-question.d.ts +25 -0
- package/dist/core/ask-question.js +4 -0
- package/dist/core/ask-unresolvable-notice.d.ts +30 -21
- package/dist/core/ask-unresolvable-notice.js +16 -8
- package/dist/core/auto-compaction.d.ts +10 -10
- package/dist/core/auto-mode-arming.d.ts +2 -2
- package/dist/core/background-agent-store.d.ts +5 -5
- package/dist/core/background-shell.d.ts +1 -1
- package/dist/core/checkpoint-execution-record.d.ts +0 -3
- package/dist/core/checkpoint-execution-record.js +0 -4
- package/dist/core/checkpoint-store.d.ts +71 -99
- package/dist/core/checkpoint-store.js +9 -16
- package/dist/core/context-edit.d.ts +36 -104
- package/dist/core/context-edit.js +37 -60
- package/dist/core/effective-path-target.d.ts +68 -8
- package/dist/core/effective-path-target.js +42 -30
- package/dist/core/engine-notice.d.ts +46 -32
- package/dist/core/env-budget.d.ts +31 -0
- package/dist/core/env-budget.js +39 -0
- package/dist/core/exec-output-tail.d.ts +5 -5
- package/dist/core/file-history-store.js +3 -6
- package/dist/core/fs-write-gate-policy.d.ts +1 -1
- package/dist/core/fs-write-gate-policy.js +3 -3
- package/dist/core/gate-lanes.js +9 -15
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +4 -0
- package/dist/core/hands-band.d.ts +1 -1
- package/dist/core/hooks.d.ts +18 -17
- package/dist/core/lsp-session.d.ts +1 -1
- package/dist/core/lsp.d.ts +1 -1
- package/dist/core/lsp.js +5 -4
- package/dist/core/mcp.d.ts +5 -5
- package/dist/core/mcp.js +4 -48
- package/dist/core/memory-engine/delegation-settlement.d.ts +37 -12
- package/dist/core/memory-engine/delegation-settlement.js +128 -39
- package/dist/core/memory-engine/engine.d.ts +9 -8
- package/dist/core/memory-engine/engine.js +115 -120
- package/dist/core/memory-engine/file-backend.d.ts +17 -5
- package/dist/core/memory-engine/file-backend.js +78 -34
- package/dist/core/memory-engine/index.d.ts +2 -2
- package/dist/core/memory-engine/index.js +2 -2
- package/dist/core/memory-engine/layout.d.ts +152 -54
- package/dist/core/memory-engine/layout.js +441 -104
- package/dist/core/memory-engine/types.d.ts +8 -3
- package/dist/core/memory-engine/types.js +5 -0
- package/dist/core/memory-recall.d.ts +0 -8
- package/dist/core/memory.d.ts +1 -1
- package/dist/core/oracle-isolation.js +2 -2
- package/dist/core/parent-spec-seat.d.ts +66 -0
- package/dist/core/parent-spec-seat.js +47 -0
- package/dist/core/permission-rule-model.d.ts +11 -6
- package/dist/core/permission-rule-model.js +53 -37
- package/dist/core/permission-rules.js +8 -3
- package/dist/core/protocol-naming.d.ts +2 -2
- package/dist/core/remote-env.d.ts +1 -1
- package/dist/core/retired-keys.d.ts +19 -0
- package/dist/core/retired-keys.js +15 -0
- package/dist/core/runner/abort-race.d.ts +3 -3
- package/dist/core/runner/active-skill-scope.d.ts +2 -2
- package/dist/core/runner/active-skill-scope.js +3 -3
- package/dist/core/runner/assemble-result.d.ts +42 -40
- package/dist/core/runner/assemble-result.js +5 -4
- package/dist/core/runner/checkpoint-scope.d.ts +4 -4
- package/dist/core/runner/clock-and-limits.d.ts +3 -3
- package/dist/core/runner/compaction-call-options.d.ts +35 -99
- package/dist/core/runner/compaction-knobs.d.ts +4 -4
- package/dist/core/runner/compaction-seams.d.ts +5 -5
- package/dist/core/runner/contracts.d.ts +232 -233
- package/dist/core/runner/decide-continuation.d.ts +6 -6
- package/dist/core/runner/denial-limit-arms.d.ts +8 -8
- package/dist/core/runner/derived-route-fallback.d.ts +2 -2
- package/dist/core/runner/gate-exit.d.ts +6 -6
- package/dist/core/runner/gate-exit.js +15 -1
- package/dist/core/runner/git-status-frame.d.ts +7 -7
- package/dist/core/runner/halt-attribution.d.ts +35 -0
- package/dist/core/runner/halt-attribution.js +16 -0
- package/dist/core/runner/inherited-ask-grants.d.ts +6 -6
- package/dist/core/runner/memory-capture-optout.d.ts +1 -1
- package/dist/core/runner/memory-consolidation-driver.d.ts +4 -4
- package/dist/core/runner/memory-consolidation.d.ts +6 -8
- package/dist/core/runner/memory-consolidation.js +0 -1
- package/dist/core/runner/park-commit.d.ts +6 -6
- package/dist/core/runner/permission-rule-lanes.d.ts +19 -19
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +4 -4
- package/dist/core/runner/prepare-artifact.d.ts +53 -0
- package/dist/core/runner/prepare-artifact.js +77 -0
- package/dist/core/runner/prepare-ask-lane.d.ts +3 -3
- package/dist/core/runner/prepare-boundary-parks.d.ts +3 -3
- package/dist/core/runner/prepare-caps-and-workflow.d.ts +9 -7
- package/dist/core/runner/prepare-caps-and-workflow.js +2 -3
- package/dist/core/runner/prepare-config-doors.d.ts +24 -24
- package/dist/core/runner/prepare-config-doors.js +5 -2
- package/dist/core/runner/prepare-context-lane.d.ts +8 -8
- package/dist/core/runner/prepare-context-lane.js +2 -13
- package/dist/core/runner/prepare-defer-classify.d.ts +2 -2
- package/dist/core/runner/prepare-delegation-surface.d.ts +1 -1
- package/dist/core/runner/prepare-file-history.d.ts +3 -3
- package/dist/core/runner/prepare-file-history.js +3 -52
- package/dist/core/runner/prepare-gate-stations.d.ts +5 -5
- package/dist/core/runner/prepare-hands-readface.d.ts +35 -38
- package/dist/core/runner/prepare-hands-readface.js +3 -2
- package/dist/core/runner/prepare-inherited-gate.d.ts +9 -6
- package/dist/core/runner/prepare-inherited-gate.js +7 -8
- package/dist/core/runner/prepare-listings.d.ts +3 -3
- package/dist/core/runner/prepare-lsp.d.ts +3 -3
- package/dist/core/runner/prepare-memory-engine-session.d.ts +5 -5
- package/dist/core/runner/prepare-memory.d.ts +4 -6
- package/dist/core/runner/prepare-memory.js +10 -9
- package/dist/core/runner/prepare-offload-wrappers.d.ts +2 -2
- package/dist/core/runner/prepare-park-ask.d.ts +6 -6
- package/dist/core/runner/prepare-policy-chain.d.ts +7 -7
- package/dist/core/runner/prepare-policy-chain.js +15 -30
- package/dist/core/runner/prepare-prompt-assembly.js +1 -1
- package/dist/core/runner/prepare-prompt-inputs.d.ts +5 -5
- package/dist/core/runner/prepare-protocol-tools.d.ts +1 -1
- package/dist/core/runner/prepare-question-face.d.ts +4 -4
- package/dist/core/runner/prepare-run-refs.d.ts +11 -11
- package/dist/core/runner/prepare-safety-scan.d.ts +2 -2
- package/dist/core/runner/prepare-suspend-saga.d.ts +4 -4
- package/dist/core/runner/prepare-suspend-saga.js +0 -2
- package/dist/core/runner/prepare-task.d.ts +12 -12
- package/dist/core/runner/prepare-task.js +10 -9
- package/dist/core/runner/prepare-tool-disclosure-mount.d.ts +2 -3
- package/dist/core/runner/prepare-wiring-manifest.d.ts +4 -4
- package/dist/core/runner/prepare-wiring-manifest.js +9 -7
- package/dist/core/runner/prepare-workspace-restore.d.ts +1 -1
- package/dist/core/runner/remote-env-retry.d.ts +3 -3
- package/dist/core/runner/resume-admission.d.ts +1 -1
- package/dist/core/runner/resume-admission.js +2 -1
- package/dist/core/runner/resume-claim.d.ts +1 -1
- package/dist/core/runner/resume-internals-and-config.d.ts +1 -1
- package/dist/core/runner/resume-internals-and-config.js +4 -0
- package/dist/core/runner/resume-policy-outcome.d.ts +1 -1
- package/dist/core/runner/resume-preflight.d.ts +1 -1
- package/dist/core/runner/resume-review-outcome.d.ts +1 -1
- package/dist/core/runner/retired-runner-deps.d.ts +3 -0
- package/dist/core/runner/retired-runner-deps.js +7 -0
- package/dist/core/runner/run-brain-sinks.d.ts +1 -1
- package/dist/core/runner/run-clock-and-content.d.ts +4 -4
- package/dist/core/runner/run-compaction-machinery.d.ts +3 -3
- package/dist/core/runner/run-git-lane.d.ts +1 -1
- package/dist/core/runner/run-harness-handlers.d.ts +2 -2
- package/dist/core/runner/run-harness-handlers.js +2 -3
- package/dist/core/runner/run-identity-wiring.d.ts +4 -4
- package/dist/core/runner/run-leg.d.ts +3 -3
- package/dist/core/runner/run-notification-lane.d.ts +2 -2
- package/dist/core/runner/run-reasoning-seat.d.ts +2 -2
- package/dist/core/runner/run-settle-and-teardown.d.ts +5 -5
- package/dist/core/runner/run-settle-and-teardown.js +3 -1
- package/dist/core/runner/run-telemetry-and-budget-seats.d.ts +2 -2
- package/dist/core/runner/run-terminal-adoption.d.ts +5 -5
- package/dist/core/runner/runtask.d.ts +14 -14
- package/dist/core/runner/session-file-state-replay.d.ts +3 -3
- package/dist/core/runner/session-file-state-replay.js +3 -3
- package/dist/core/runner/session-rule-policy.d.ts +2 -2
- package/dist/core/runner/session-rule-policy.js +3 -4
- package/dist/core/runner/steer-admission.d.ts +2 -2
- package/dist/core/runner/stream-halt-verbs.d.ts +1 -1
- package/dist/core/runner/stream-halt-verbs.js +1 -1
- package/dist/core/runner/stream-reap.d.ts +1 -1
- package/dist/core/runner/stream-steer-verb.js +1 -4
- package/dist/core/runner/synthetic-tools.d.ts +8 -8
- package/dist/core/runner/teardown-bounded.d.ts +2 -2
- package/dist/core/runner/tool-defer-gate.d.ts +1 -1
- package/dist/core/runner/tool-disclosure.d.ts +33 -33
- package/dist/core/runner/tool-end-body.d.ts +3 -3
- package/dist/core/runner/tool-face-overlay.d.ts +2 -2
- package/dist/core/runner/tool-output-projection.d.ts +2 -2
- package/dist/core/runner/turn-attachments.d.ts +53 -53
- package/dist/core/runner/usage-accounting.d.ts +4 -4
- package/dist/core/runner/workspace-path.d.ts +1 -1
- package/dist/core/runner/workspace-path.js +3 -0
- package/dist/core/runner-deps.d.ts +30 -49
- package/dist/core/safe-notify.d.ts +1 -1
- package/dist/core/sensitive-path-policy.d.ts +1 -1
- package/dist/core/sensitive-path-policy.js +4 -4
- package/dist/core/session.d.ts +2 -2
- package/dist/core/skill-tool-specifier.d.ts +1 -1
- package/dist/core/skills-directory.d.ts +1 -1
- package/dist/core/store-contracts/checkpoint-store-contract.js +1 -1
- package/dist/core/strategy-store.d.ts +8 -11
- package/dist/core/task-event.d.ts +20 -17
- package/dist/core/task-limits.d.ts +1 -1
- package/dist/core/task-notification.d.ts +5 -5
- package/dist/core/task-registry-agent.d.ts +5 -5
- package/dist/core/task-registry-monitor.d.ts +2 -2
- package/dist/core/task-registry-shared.d.ts +27 -27
- package/dist/core/task-registry-shared.js +3 -13
- package/dist/core/task-registry.d.ts +8 -8
- package/dist/core/task-result.d.ts +37 -37
- package/dist/core/task-spec.d.ts +5 -5
- package/dist/core/task-stream.d.ts +16 -11
- package/dist/core/tool-catalog-entries.d.ts +1 -0
- package/dist/core/tool-catalog-entries.js +5 -0
- package/dist/core/tool-conformance.d.ts +0 -2
- package/dist/core/tool-conformance.js +1 -3
- package/dist/core/tool-errors.d.ts +5 -0
- package/dist/core/tool-errors.js +1 -0
- package/dist/core/tool-policy.d.ts +51 -22
- package/dist/core/tool-policy.js +28 -5
- package/dist/core/tool-result-budget.d.ts +1 -1
- package/dist/core/tool-result-store.d.ts +11 -11
- package/dist/core/tool-spec.d.ts +34 -38
- package/dist/core/tools.d.ts +2 -2
- package/dist/core/trace.d.ts +12 -15
- package/dist/core/untrusted-text.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +4 -3
- package/dist/core/workflow-journal-store.d.ts +11 -11
- package/dist/engine/execution-env/node-execution-env.d.ts +3 -2
- package/dist/engine/execution-env/node-execution-env.js +4 -2
- package/dist/engine/harness/agent-harness.d.ts +47 -4
- package/dist/engine/harness/agent-harness.js +52 -9
- package/dist/engine/llm/types.d.ts +1 -1
- package/dist/engine/loop/agent-loop.js +15 -8
- package/dist/engine/loop/types.d.ts +5 -0
- package/dist/index.d.ts +21 -16
- package/dist/index.js +17 -12
- package/dist/internal/harness.d.ts +1 -0
- package/dist/orchestration/run-spec.d.ts +3 -0
- package/dist/orchestration/run-spec.js +3 -3
- package/dist/orchestration/run-workflow-tool.d.ts +12 -6
- package/dist/orchestration/run-workflow-tool.js +23 -5
- package/dist/orchestration/workflow-primitives.d.ts +4 -2
- package/dist/orchestration/workflow-primitives.js +1 -4
- package/dist/orchestration/workflow-types.d.ts +34 -4
- package/dist/orchestration/workflow-types.js +2 -0
- package/dist/orchestration/workflow.d.ts +39 -7
- package/dist/orchestration/workflow.js +234 -113
- package/dist/prompt-assembly/assemble.d.ts +2 -2
- package/dist/prompt-assembly/assemble.js +9 -9
- package/dist/prompt-assembly/composer.d.ts +6 -7
- package/dist/prompt-assembly/composer.js +10 -10
- package/dist/prompt-assembly/epoch.js +1 -1
- package/dist/prompt-assembly/event-registry.d.ts +0 -21
- package/dist/prompt-assembly/packs/sema-default.js +30 -30
- package/dist/prompt-assembly/types.d.ts +14 -14
- package/dist/prompts/coordinator.d.ts +0 -13
- package/dist/prompts/coordinator.js +0 -3
- package/dist/stores/file/checkpoint-store.js +1 -5
- package/dist/stores/file/file-history-store.js +2 -2
- package/dist/stores/file/permission-rule-store.d.ts +4 -35
- package/dist/stores/file/permission-rule-store.js +4 -148
- package/dist/tools/artifact/artifact-text.d.ts +62 -0
- package/dist/tools/artifact/artifact-text.js +68 -0
- package/dist/tools/artifact/artifact-tool.d.ts +52 -0
- package/dist/tools/artifact/artifact-tool.js +351 -0
- package/dist/tools/artifact/local-stub.d.ts +26 -0
- package/dist/tools/artifact/local-stub.js +296 -0
- package/dist/tools/fs/bash-readonly-classifier.d.ts +114 -48
- package/dist/tools/fs/bash-readonly-classifier.js +171 -60
- package/dist/tools/fs/encoding.d.ts +31 -40
- package/dist/tools/fs/fs-bash.d.ts +49 -45
- package/dist/tools/fs/fs-bash.js +19 -27
- package/dist/tools/fs/fs-pdf.d.ts +8 -8
- package/dist/tools/fs/fs-search-tools.d.ts +4 -4
- package/dist/tools/fs/fs-search-tools.js +5 -2
- package/dist/tools/fs/fs-shared.d.ts +146 -158
- package/dist/tools/fs/fs-shared.js +3 -11
- package/dist/tools/fs/fs-write.d.ts +8 -8
- package/dist/tools/fs/gh-rate-limit.d.ts +13 -14
- package/dist/tools/fs/index.d.ts +78 -78
- package/dist/tools/fs/index.js +4 -1
- package/dist/tools/fs/notebook.d.ts +24 -25
- package/dist/tools/fs/pdf.d.ts +3 -3
- package/dist/tools/fs/read-deny.d.ts +22 -22
- package/dist/tools/fs/read-face.d.ts +21 -24
- package/dist/tools/fs/repo-map.d.ts +8 -8
- package/dist/tools/fs/safety.d.ts +233 -130
- package/dist/tools/fs/safety.js +131 -31
- package/dist/tools/fs/search.d.ts +63 -71
- package/dist/tools/fs/search.js +8 -6
- package/package.json +6 -2
- 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
|
|
223
|
-
super(`startWorkflow: resume from "${fromRunId}" was REFUSED —
|
|
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.
|
|
465
|
-
throw new
|
|
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
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
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
|
-
|
|
837
|
-
|
|
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
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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 ${
|
|
963
|
-
"a resume of this run
|
|
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
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
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
|
|
1446
|
-
const authInherit =
|
|
1447
|
-
|
|
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
|
-
? { ...
|
|
1450
|
-
: { ...
|
|
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
|
|
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
|
|
1727
|
-
if (
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
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 =
|
|
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
|
|
1821
|
-
const authInherit =
|
|
1822
|
-
|
|
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
|
-
? { ...
|
|
1825
|
-
: { ...
|
|
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
|
|
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
|
-
|
|
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
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
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
|
-
/**
|
|
41
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
|
154
|
-
return lastPT.
|
|
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,
|
|
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,
|
|
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
|
|
231
|
-
return last.
|
|
230
|
+
get blocks() {
|
|
231
|
+
return last.blocks;
|
|
232
232
|
},
|
|
233
233
|
get sections() {
|
|
234
234
|
return last.sections;
|