@caupulican/pi-adaptative 0.86.3 → 0.86.5
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 +32 -0
- package/dist/core/agent-paths.d.ts +13 -0
- package/dist/core/agent-paths.d.ts.map +1 -1
- package/dist/core/agent-paths.js +23 -0
- package/dist/core/agent-paths.js.map +1 -1
- package/dist/core/autonomy/bounded-completion.d.ts +7 -0
- package/dist/core/autonomy/bounded-completion.d.ts.map +1 -1
- package/dist/core/autonomy/bounded-completion.js +12 -4
- package/dist/core/autonomy/bounded-completion.js.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.d.ts +2 -0
- package/dist/core/autonomy/lane-tool-surface.d.ts.map +1 -1
- package/dist/core/autonomy/lane-tool-surface.js +6 -3
- package/dist/core/autonomy/lane-tool-surface.js.map +1 -1
- package/dist/core/autonomy/subagent-prompt.js +1 -1
- package/dist/core/autonomy/subagent-prompt.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +25 -2
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +67 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts +4 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.d.ts.map +1 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js +20 -0
- package/dist/core/delegation/replay-safe-mailbox-bounds.js.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts +25 -0
- package/dist/core/delegation/sanitized-context-fork.d.ts.map +1 -0
- package/dist/core/delegation/sanitized-context-fork.js +299 -0
- package/dist/core/delegation/sanitized-context-fork.js.map +1 -0
- package/dist/core/delegation/session-root-mailbox.d.ts +75 -0
- package/dist/core/delegation/session-root-mailbox.d.ts.map +1 -0
- package/dist/core/delegation/session-root-mailbox.js +627 -0
- package/dist/core/delegation/session-root-mailbox.js.map +1 -0
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts +102 -9
- package/dist/core/delegation/worker-agent-control-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control-coordinator.js +1307 -125
- package/dist/core/delegation/worker-agent-control-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-agent-control.d.ts +183 -8
- package/dist/core/delegation/worker-agent-control.d.ts.map +1 -1
- package/dist/core/delegation/worker-agent-control.js +778 -48
- package/dist/core/delegation/worker-agent-control.js.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.d.ts +9 -0
- package/dist/core/delegation/worker-attempt-executor.d.ts.map +1 -1
- package/dist/core/delegation/worker-attempt-executor.js +479 -214
- package/dist/core/delegation/worker-attempt-executor.js.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.d.ts +9 -0
- package/dist/core/delegation/worker-authority-resolver.d.ts.map +1 -1
- package/dist/core/delegation/worker-authority-resolver.js +21 -2
- package/dist/core/delegation/worker-authority-resolver.js.map +1 -1
- package/dist/core/delegation/worker-context-fork-store.d.ts +71 -0
- package/dist/core/delegation/worker-context-fork-store.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-fork-store.js +437 -0
- package/dist/core/delegation/worker-context-fork-store.js.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts +18 -0
- package/dist/core/delegation/worker-context-inheritance-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js +28 -0
- package/dist/core/delegation/worker-context-inheritance-policy.js.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts +31 -0
- package/dist/core/delegation/worker-conversation-revision.d.ts.map +1 -0
- package/dist/core/delegation/worker-conversation-revision.js +203 -0
- package/dist/core/delegation/worker-conversation-revision.js.map +1 -0
- package/dist/core/delegation/worker-conversation-store.d.ts +130 -18
- package/dist/core/delegation/worker-conversation-store.d.ts.map +1 -1
- package/dist/core/delegation/worker-conversation-store.js +1263 -79
- package/dist/core/delegation/worker-conversation-store.js.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.d.ts +29 -2
- package/dist/core/delegation/worker-delegation-controller.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-controller.js +625 -111
- package/dist/core/delegation/worker-delegation-controller.js.map +1 -1
- package/dist/core/delegation/worker-delegation-request.d.ts +2 -0
- package/dist/core/delegation/worker-delegation-request.d.ts.map +1 -1
- package/dist/core/delegation/worker-delegation-request.js.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts +20 -1
- package/dist/core/delegation/worker-dispatch-scheduler.d.ts.map +1 -1
- package/dist/core/delegation/worker-dispatch-scheduler.js +210 -37
- package/dist/core/delegation/worker-dispatch-scheduler.js.map +1 -1
- package/dist/core/delegation/worker-fleet-limits.d.ts +38 -0
- package/dist/core/delegation/worker-fleet-limits.d.ts.map +1 -0
- package/dist/core/delegation/worker-fleet-limits.js +113 -0
- package/dist/core/delegation/worker-fleet-limits.js.map +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts +1 -0
- package/dist/core/delegation/worker-lane-projection.d.ts.map +1 -1
- package/dist/core/delegation/worker-lane-projection.js +6 -2
- package/dist/core/delegation/worker-lane-projection.js.map +1 -1
- package/dist/core/delegation/worker-lifecycle.d.ts +20 -3
- package/dist/core/delegation/worker-lifecycle.d.ts.map +1 -1
- package/dist/core/delegation/worker-lifecycle.js +42 -18
- package/dist/core/delegation/worker-lifecycle.js.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.d.ts +4 -0
- package/dist/core/delegation/worker-notification-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-notification-coordinator.js +45 -4
- package/dist/core/delegation/worker-notification-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts +50 -0
- package/dist/core/delegation/worker-provider-turn-protocol.d.ts.map +1 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js +185 -0
- package/dist/core/delegation/worker-provider-turn-protocol.js.map +1 -0
- package/dist/core/delegation/worker-recovery-coordinator.d.ts +45 -4
- package/dist/core/delegation/worker-recovery-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-recovery-coordinator.js +223 -17
- package/dist/core/delegation/worker-recovery-coordinator.js.map +1 -1
- package/dist/core/delegation/worker-retry-policy.d.ts +36 -0
- package/dist/core/delegation/worker-retry-policy.d.ts.map +1 -0
- package/dist/core/delegation/worker-retry-policy.js +41 -0
- package/dist/core/delegation/worker-retry-policy.js.map +1 -0
- package/dist/core/delegation/worker-runner.d.ts +3 -1
- package/dist/core/delegation/worker-runner.d.ts.map +1 -1
- package/dist/core/delegation/worker-runner.js +4 -2
- package/dist/core/delegation/worker-runner.js.map +1 -1
- package/dist/core/delegation/worker-task-view.d.ts +43 -0
- package/dist/core/delegation/worker-task-view.d.ts.map +1 -0
- package/dist/core/delegation/worker-task-view.js +194 -0
- package/dist/core/delegation/worker-task-view.js.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts +55 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.d.ts.map +1 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js +179 -0
- package/dist/core/delegation/worker-terminal-handoff-coordinator.js.map +1 -0
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts +2 -2
- package/dist/core/delegation/worker-tree-budget-coordinator.d.ts.map +1 -1
- package/dist/core/delegation/worker-tree-budget-coordinator.js +12 -46
- package/dist/core/delegation/worker-tree-budget-coordinator.js.map +1 -1
- package/dist/core/keybindings.d.ts +10 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +2 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/orchestration/attempt-ordering.d.ts +9 -0
- package/dist/core/orchestration/attempt-ordering.d.ts.map +1 -0
- package/dist/core/orchestration/attempt-ordering.js +29 -0
- package/dist/core/orchestration/attempt-ordering.js.map +1 -0
- package/dist/core/orchestration/capability-gateway.d.ts +19 -0
- package/dist/core/orchestration/capability-gateway.d.ts.map +1 -1
- package/dist/core/orchestration/capability-gateway.js +19 -2
- package/dist/core/orchestration/capability-gateway.js.map +1 -1
- package/dist/core/orchestration/contracts.d.ts +43 -3
- package/dist/core/orchestration/contracts.d.ts.map +1 -1
- package/dist/core/orchestration/contracts.js +24 -0
- package/dist/core/orchestration/contracts.js.map +1 -1
- package/dist/core/orchestration/delegation-ledger.d.ts +8 -3
- package/dist/core/orchestration/delegation-ledger.d.ts.map +1 -1
- package/dist/core/orchestration/delegation-ledger.js +148 -55
- package/dist/core/orchestration/delegation-ledger.js.map +1 -1
- package/dist/core/orchestration/event-store.d.ts +6 -3
- package/dist/core/orchestration/event-store.d.ts.map +1 -1
- package/dist/core/orchestration/event-store.js +228 -30
- package/dist/core/orchestration/event-store.js.map +1 -1
- package/dist/core/orchestration/task-runtime-codecs.d.ts +31 -0
- package/dist/core/orchestration/task-runtime-codecs.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-codecs.js +1471 -0
- package/dist/core/orchestration/task-runtime-codecs.js.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts +30 -0
- package/dist/core/orchestration/task-runtime-projection.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-projection.js +340 -0
- package/dist/core/orchestration/task-runtime-projection.js.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts +38 -0
- package/dist/core/orchestration/task-runtime-reducer.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-reducer.js +1270 -0
- package/dist/core/orchestration/task-runtime-reducer.js.map +1 -0
- package/dist/core/orchestration/task-runtime-state.d.ts +142 -0
- package/dist/core/orchestration/task-runtime-state.d.ts.map +1 -0
- package/dist/core/orchestration/task-runtime-state.js +14 -0
- package/dist/core/orchestration/task-runtime-state.js.map +1 -0
- package/dist/core/orchestration/task-runtime.d.ts +44 -98
- package/dist/core/orchestration/task-runtime.d.ts.map +1 -1
- package/dist/core/orchestration/task-runtime.js +357 -1113
- package/dist/core/orchestration/task-runtime.js.map +1 -1
- package/dist/core/orchestration/worker-context-fork-reference.d.ts +20 -0
- package/dist/core/orchestration/worker-context-fork-reference.d.ts.map +1 -0
- package/dist/core/orchestration/worker-context-fork-reference.js +53 -0
- package/dist/core/orchestration/worker-context-fork-reference.js.map +1 -0
- package/dist/core/research/research-runner.d.ts +2 -0
- package/dist/core/research/research-runner.d.ts.map +1 -1
- package/dist/core/research/research-runner.js +1 -0
- package/dist/core/research/research-runner.js.map +1 -1
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +8 -0
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/secrets/credential-project.d.ts.map +1 -1
- package/dist/core/secrets/credential-project.js +24 -2
- package/dist/core/secrets/credential-project.js.map +1 -1
- package/dist/core/security/secret-text.d.ts +3 -0
- package/dist/core/security/secret-text.d.ts.map +1 -1
- package/dist/core/security/secret-text.js +15 -0
- package/dist/core/security/secret-text.js.map +1 -1
- package/dist/core/settings-manager.d.ts +3 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +160 -8
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/delegate.d.ts +31 -4
- package/dist/core/tools/delegate.d.ts.map +1 -1
- package/dist/core/tools/delegate.js +749 -103
- package/dist/core/tools/delegate.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/activity-lane.d.ts +5 -0
- package/dist/modes/interactive/components/activity-lane.d.ts.map +1 -1
- package/dist/modes/interactive/components/activity-lane.js +133 -49
- package/dist/modes/interactive/components/activity-lane.js.map +1 -1
- package/dist/modes/interactive/components/agents-overlay.d.ts +40 -0
- package/dist/modes/interactive/components/agents-overlay.d.ts.map +1 -0
- package/dist/modes/interactive/components/agents-overlay.js +153 -0
- package/dist/modes/interactive/components/agents-overlay.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +18 -13
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts +1 -1
- package/dist/modes/interactive/interactive-event-controller.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-event-controller.js +6 -3
- package/dist/modes/interactive/interactive-event-controller.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +51 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/key-handlers.d.ts +1 -0
- package/dist/modes/interactive/key-handlers.d.ts.map +1 -1
- package/dist/modes/interactive/key-handlers.js +1 -0
- package/dist/modes/interactive/key-handlers.js.map +1 -1
- package/docs/harness-architecture.md +61 -17
- package/docs/settings.md +17 -9
- package/docs/worker-profiles.md +6 -2
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1,1471 @@
|
|
|
1
|
+
import { createAgentIdentity } from "./agent-resume.js";
|
|
2
|
+
import { validateAttemptUsageSnapshot } from "./attempt-usage.js";
|
|
3
|
+
import { ATTEMPT_STATUSES, isHarnessCapability, isResourcePointerKind, MAX_ORCHESTRATION_AGENT_BINDINGS, MAX_ORCHESTRATION_APPROVALS, MAX_ORCHESTRATION_ATTEMPTS, MAX_ORCHESTRATION_CHECKPOINT_SUMMARY_LENGTH, MAX_ORCHESTRATION_CHECKPOINTS, MAX_ORCHESTRATION_COLLECTION_LENGTH, MAX_ORCHESTRATION_DESCRIPTION_LENGTH, MAX_ORCHESTRATION_DISPATCH_INSTRUCTIONS_LENGTH, MAX_ORCHESTRATION_IDENTIFIER_LENGTH, MAX_ORCHESTRATION_MODEL_PROVIDER_LENGTH, MAX_ORCHESTRATION_NOTIFICATIONS, MAX_ORCHESTRATION_OBJECTIVE_EVIDENCE, MAX_ORCHESTRATION_OBJECTIVES, MAX_ORCHESTRATION_TASKS, MAX_WORKER_RESOURCE_PATH_LENGTH, MAX_WORKER_RESOURCE_POINTERS, OBJECTIVE_STATUSES, ORCHESTRATION_SCHEMA_VERSION, TASK_STATUSES, toJsonObject, WORKER_RESULT_STATUSES, WORKER_ROLES, } from "./contracts.js";
|
|
4
|
+
import { validateRiskBudget } from "./risk-budget.js";
|
|
5
|
+
import { assertRecordWithinLimit, assertRetainedProjectionNestedCollections, exactRecord, PROJECTION_RECORD_FIELDS, projectionSerializedBytes, record, retainedIdentifierArray, } from "./task-runtime-projection.js";
|
|
6
|
+
import { DurableTaskRuntimeError, missingTrustedCriteria, terminalAttemptStatus, } from "./task-runtime-state.js";
|
|
7
|
+
import { normalizeWorkerContextForkReference } from "./worker-context-fork-reference.js";
|
|
8
|
+
import { parseWorkerExecutionContract } from "./worker-execution-contract.js";
|
|
9
|
+
export const MAX_DATE_EPOCH_MS = 8_640_000_000_000_000;
|
|
10
|
+
export function projectionFromSnapshot(projection, throughOrdinal) {
|
|
11
|
+
const label = "orchestration projection snapshot";
|
|
12
|
+
const candidate = exactRecord(projection, label, ["lastOrdinal", ...PROJECTION_RECORD_FIELDS]);
|
|
13
|
+
if (!Number.isSafeInteger(candidate.lastOrdinal) ||
|
|
14
|
+
Number(candidate.lastOrdinal) < 0 ||
|
|
15
|
+
candidate.lastOrdinal !== throughOrdinal) {
|
|
16
|
+
throw new DurableTaskRuntimeError("Orchestration projection snapshot ordinal does not match its baseline.");
|
|
17
|
+
}
|
|
18
|
+
const snapshotRecords = {};
|
|
19
|
+
for (const field of PROJECTION_RECORD_FIELDS) {
|
|
20
|
+
snapshotRecords[field] = record(candidate[field], `${label}.${field}`);
|
|
21
|
+
}
|
|
22
|
+
assertRecordWithinLimit(snapshotRecords.agents, MAX_ORCHESTRATION_AGENT_BINDINGS, "agent binding");
|
|
23
|
+
assertRecordWithinLimit(snapshotRecords.objectives, MAX_ORCHESTRATION_OBJECTIVES, "objective");
|
|
24
|
+
assertRecordWithinLimit(snapshotRecords.tasks, MAX_ORCHESTRATION_TASKS, "task");
|
|
25
|
+
assertRecordWithinLimit(snapshotRecords.attempts, MAX_ORCHESTRATION_ATTEMPTS, "attempt");
|
|
26
|
+
assertRecordWithinLimit(snapshotRecords.checkpoints, MAX_ORCHESTRATION_CHECKPOINTS, "checkpoint");
|
|
27
|
+
assertRecordWithinLimit(snapshotRecords.approvals, MAX_ORCHESTRATION_APPROVALS, "approval");
|
|
28
|
+
assertRecordWithinLimit(snapshotRecords.notifications, MAX_ORCHESTRATION_NOTIFICATIONS, "notification");
|
|
29
|
+
assertRetainedProjectionNestedCollections(snapshotRecords.objectives, snapshotRecords.tasks, snapshotRecords.attempts, snapshotRecords.checkpoints, label);
|
|
30
|
+
projectionSerializedBytes(candidate);
|
|
31
|
+
const normalized = {
|
|
32
|
+
lastOrdinal: throughOrdinal,
|
|
33
|
+
agents: Object.fromEntries(Object.entries(snapshotRecords.agents).map(([agentId, value]) => [
|
|
34
|
+
agentId,
|
|
35
|
+
normalizeAgentBinding(value, `${label}.agents.${agentId}`),
|
|
36
|
+
])),
|
|
37
|
+
objectives: Object.fromEntries(Object.entries(snapshotRecords.objectives).map(([objectiveId, value]) => [
|
|
38
|
+
objectiveId,
|
|
39
|
+
objectiveRuntimeStateFromValue(value, `${label}.objectives.${objectiveId}`),
|
|
40
|
+
])),
|
|
41
|
+
tasks: Object.fromEntries(Object.entries(snapshotRecords.tasks).map(([taskId, value]) => [
|
|
42
|
+
taskId,
|
|
43
|
+
taskRuntimeStateFromValue(value, `${label}.tasks.${taskId}`),
|
|
44
|
+
])),
|
|
45
|
+
attempts: Object.fromEntries(Object.entries(snapshotRecords.attempts).map(([attemptId, value]) => [
|
|
46
|
+
attemptId,
|
|
47
|
+
attemptRuntimeStateFromValue(value, `${label}.attempts.${attemptId}`),
|
|
48
|
+
])),
|
|
49
|
+
checkpoints: Object.fromEntries(Object.entries(snapshotRecords.checkpoints).map(([checkpointId, value]) => [
|
|
50
|
+
checkpointId,
|
|
51
|
+
checkpointFromValue(value, `${label}.checkpoints.${checkpointId}`),
|
|
52
|
+
])),
|
|
53
|
+
approvals: Object.fromEntries(Object.entries(snapshotRecords.approvals).map(([approvalId, value]) => [
|
|
54
|
+
approvalId,
|
|
55
|
+
approvalRuntimeStateFromValue(value, `${label}.approvals.${approvalId}`),
|
|
56
|
+
])),
|
|
57
|
+
notifications: Object.fromEntries(Object.entries(snapshotRecords.notifications).map(([notificationId, value]) => [
|
|
58
|
+
notificationId,
|
|
59
|
+
notificationRuntimeStateFromValue(value, `${label}.notifications.${notificationId}`),
|
|
60
|
+
])),
|
|
61
|
+
};
|
|
62
|
+
validateSnapshotTaskGraph(normalized);
|
|
63
|
+
projectionSerializedBytes(normalized);
|
|
64
|
+
return normalized;
|
|
65
|
+
}
|
|
66
|
+
export function string(value, label) {
|
|
67
|
+
if (typeof value !== "string" || value.length === 0)
|
|
68
|
+
throw new DurableTaskRuntimeError(`${label} is required.`);
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
export function number(value, label) {
|
|
72
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
73
|
+
throw new DurableTaskRuntimeError(`${label} is invalid.`);
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
function boolean(value, label) {
|
|
77
|
+
if (typeof value !== "boolean")
|
|
78
|
+
throw new DurableTaskRuntimeError(`${label} is invalid.`);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
export function assertRiskBudget(budget, label) {
|
|
82
|
+
try {
|
|
83
|
+
validateRiskBudget(budget ?? {}, label);
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function stringArray(value, label) {
|
|
90
|
+
if (!Array.isArray(value) || !value.every((entry) => typeof entry === "string")) {
|
|
91
|
+
throw new DurableTaskRuntimeError(`${label} must be a string array.`);
|
|
92
|
+
}
|
|
93
|
+
return [...value];
|
|
94
|
+
}
|
|
95
|
+
export function dispatchIdentifier(value, label) {
|
|
96
|
+
const identifier = string(value, label);
|
|
97
|
+
if (identifier.length > MAX_ORCHESTRATION_IDENTIFIER_LENGTH) {
|
|
98
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its durable size bound.`);
|
|
99
|
+
}
|
|
100
|
+
return identifier;
|
|
101
|
+
}
|
|
102
|
+
function isoDate(value, label) {
|
|
103
|
+
const date = dispatchIdentifier(value, label);
|
|
104
|
+
const epochMs = Date.parse(date);
|
|
105
|
+
if (!Number.isFinite(epochMs) || Math.abs(epochMs) > MAX_DATE_EPOCH_MS || new Date(epochMs).toISOString() !== date) {
|
|
106
|
+
throw new DurableTaskRuntimeError(`${label} is invalid.`);
|
|
107
|
+
}
|
|
108
|
+
return date;
|
|
109
|
+
}
|
|
110
|
+
function optionalDispatchIdentifier(value, label, maxLength = MAX_ORCHESTRATION_IDENTIFIER_LENGTH) {
|
|
111
|
+
if (value === undefined)
|
|
112
|
+
return undefined;
|
|
113
|
+
const identifier = string(value, label);
|
|
114
|
+
if (identifier.length > maxLength)
|
|
115
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its durable size bound.`);
|
|
116
|
+
return identifier;
|
|
117
|
+
}
|
|
118
|
+
function dispatchInstructions(value, label) {
|
|
119
|
+
const instructions = string(value, label);
|
|
120
|
+
if (instructions.length > MAX_ORCHESTRATION_DISPATCH_INSTRUCTIONS_LENGTH) {
|
|
121
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its durable size bound.`);
|
|
122
|
+
}
|
|
123
|
+
return instructions;
|
|
124
|
+
}
|
|
125
|
+
function retainedDescription(value, label) {
|
|
126
|
+
const description = string(value, label).trim();
|
|
127
|
+
if (!description)
|
|
128
|
+
throw new DurableTaskRuntimeError(`${label} is required.`);
|
|
129
|
+
if (description.length > MAX_ORCHESTRATION_DESCRIPTION_LENGTH) {
|
|
130
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its durable size bound.`);
|
|
131
|
+
}
|
|
132
|
+
return description;
|
|
133
|
+
}
|
|
134
|
+
export function checkpointSummary(value, label) {
|
|
135
|
+
const summary = string(value, label).trim();
|
|
136
|
+
if (!summary)
|
|
137
|
+
throw new DurableTaskRuntimeError(`${label} is required.`);
|
|
138
|
+
if (summary.length > MAX_ORCHESTRATION_CHECKPOINT_SUMMARY_LENGTH) {
|
|
139
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its durable size bound.`);
|
|
140
|
+
}
|
|
141
|
+
return summary;
|
|
142
|
+
}
|
|
143
|
+
export function dispatchIdentifierArray(value, label) {
|
|
144
|
+
if (!Array.isArray(value) ||
|
|
145
|
+
value.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
146
|
+
!value.every((entry) => typeof entry === "string" && entry.length > 0 && entry.length <= MAX_ORCHESTRATION_IDENTIFIER_LENGTH)) {
|
|
147
|
+
throw new DurableTaskRuntimeError(`${label} must be a bounded identifier array.`);
|
|
148
|
+
}
|
|
149
|
+
if (new Set(value).size !== value.length) {
|
|
150
|
+
throw new DurableTaskRuntimeError(`${label} contains duplicates.`);
|
|
151
|
+
}
|
|
152
|
+
return [...value];
|
|
153
|
+
}
|
|
154
|
+
/** Validate dependency identity and ownership before any durable task event is appended. */
|
|
155
|
+
export function validateTaskDependencyIds(tasks, objectiveId, value) {
|
|
156
|
+
const dependencyIds = dispatchIdentifierArray(value ?? [], "task.dependsOn");
|
|
157
|
+
for (const dependencyId of dependencyIds) {
|
|
158
|
+
const dependency = tasks[dependencyId];
|
|
159
|
+
if (!dependency || dependency.task.objectiveId !== objectiveId) {
|
|
160
|
+
throw new DurableTaskRuntimeError(`Task dependency '${dependencyId}' is not in objective '${objectiveId}'.`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return dependencyIds;
|
|
164
|
+
}
|
|
165
|
+
const DISPATCH_FIELDS = new Set([
|
|
166
|
+
"taskId",
|
|
167
|
+
"profileId",
|
|
168
|
+
"instructions",
|
|
169
|
+
"resourcePointerIds",
|
|
170
|
+
"parentAgentId",
|
|
171
|
+
"requirementIds",
|
|
172
|
+
"executionKind",
|
|
173
|
+
"logicalLaneId",
|
|
174
|
+
"dispatchSequence",
|
|
175
|
+
"controlMessageId",
|
|
176
|
+
"provider",
|
|
177
|
+
"authorizationId",
|
|
178
|
+
"worktreeLaneKey",
|
|
179
|
+
"birthContextForkReference",
|
|
180
|
+
"executionContract",
|
|
181
|
+
]);
|
|
182
|
+
export function dispatchFromValue(value, label) {
|
|
183
|
+
const dispatch = record(value, label);
|
|
184
|
+
const unsupportedField = Object.keys(dispatch).find((field) => !DISPATCH_FIELDS.has(field));
|
|
185
|
+
if (unsupportedField)
|
|
186
|
+
throw new DurableTaskRuntimeError(`${label}.${unsupportedField} is unsupported.`);
|
|
187
|
+
const executionKind = dispatch.executionKind;
|
|
188
|
+
if (executionKind !== undefined && executionKind !== "in-process" && executionKind !== "managed-process") {
|
|
189
|
+
throw new DurableTaskRuntimeError(`${label}.executionKind is invalid.`);
|
|
190
|
+
}
|
|
191
|
+
const logicalLaneId = optionalDispatchIdentifier(dispatch.logicalLaneId, `${label}.logicalLaneId`);
|
|
192
|
+
const controlMessageId = optionalDispatchIdentifier(dispatch.controlMessageId, `${label}.controlMessageId`);
|
|
193
|
+
const parentAgentId = optionalDispatchIdentifier(dispatch.parentAgentId, `${label}.parentAgentId`);
|
|
194
|
+
const provider = optionalDispatchIdentifier(dispatch.provider, `${label}.provider`, MAX_ORCHESTRATION_MODEL_PROVIDER_LENGTH);
|
|
195
|
+
const authorizationId = optionalDispatchIdentifier(dispatch.authorizationId, `${label}.authorizationId`);
|
|
196
|
+
const worktreeLaneKey = optionalDispatchIdentifier(dispatch.worktreeLaneKey, `${label}.worktreeLaneKey`);
|
|
197
|
+
if (dispatch.dispatchSequence !== undefined &&
|
|
198
|
+
(!Number.isSafeInteger(dispatch.dispatchSequence) || Number(dispatch.dispatchSequence) < 1)) {
|
|
199
|
+
throw new DurableTaskRuntimeError(`${label}.dispatchSequence is invalid.`);
|
|
200
|
+
}
|
|
201
|
+
let executionContract;
|
|
202
|
+
let birthContextForkReference;
|
|
203
|
+
try {
|
|
204
|
+
executionContract =
|
|
205
|
+
dispatch.executionContract === undefined
|
|
206
|
+
? undefined
|
|
207
|
+
: parseWorkerExecutionContract(dispatch.executionContract);
|
|
208
|
+
birthContextForkReference =
|
|
209
|
+
dispatch.birthContextForkReference === undefined
|
|
210
|
+
? undefined
|
|
211
|
+
: normalizeWorkerContextForkReference(dispatch.birthContextForkReference);
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
throw new DurableTaskRuntimeError(`${label} is invalid: ${error instanceof Error ? error.message : String(error)}`);
|
|
215
|
+
}
|
|
216
|
+
if (executionContract && executionKind === "managed-process") {
|
|
217
|
+
throw new DurableTaskRuntimeError(`${label} cannot combine a worker execution contract with managed execution.`);
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
taskId: dispatchIdentifier(dispatch.taskId, `${label}.taskId`),
|
|
221
|
+
profileId: dispatchIdentifier(dispatch.profileId, `${label}.profileId`),
|
|
222
|
+
instructions: dispatchInstructions(dispatch.instructions, `${label}.instructions`),
|
|
223
|
+
resourcePointerIds: dispatchIdentifierArray(dispatch.resourcePointerIds, `${label}.resourcePointerIds`),
|
|
224
|
+
...(parentAgentId ? { parentAgentId } : {}),
|
|
225
|
+
requirementIds: dispatch.requirementIds === undefined
|
|
226
|
+
? []
|
|
227
|
+
: dispatchIdentifierArray(dispatch.requirementIds, `${label}.requirementIds`),
|
|
228
|
+
...(executionKind ? { executionKind } : {}),
|
|
229
|
+
...(logicalLaneId ? { logicalLaneId } : {}),
|
|
230
|
+
...(typeof dispatch.dispatchSequence === "number" ? { dispatchSequence: dispatch.dispatchSequence } : {}),
|
|
231
|
+
...(controlMessageId ? { controlMessageId } : {}),
|
|
232
|
+
...(provider ? { provider } : {}),
|
|
233
|
+
...(authorizationId ? { authorizationId } : {}),
|
|
234
|
+
...(worktreeLaneKey ? { worktreeLaneKey } : {}),
|
|
235
|
+
...(birthContextForkReference ? { birthContextForkReference } : {}),
|
|
236
|
+
...(executionContract ? { executionContract } : {}),
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
export function objectiveFromPayload(payload) {
|
|
240
|
+
return objectiveFromValue(payload.objective, "objective");
|
|
241
|
+
}
|
|
242
|
+
export function evidenceFromPayload(payload) {
|
|
243
|
+
return evidenceFromValue(payload.evidence, "evidence");
|
|
244
|
+
}
|
|
245
|
+
function objectiveFromValue(value, label) {
|
|
246
|
+
const objective = exactRecord(value, label, [
|
|
247
|
+
"schemaVersion",
|
|
248
|
+
"objectiveId",
|
|
249
|
+
"title",
|
|
250
|
+
"description",
|
|
251
|
+
"status",
|
|
252
|
+
"constraints",
|
|
253
|
+
"acceptanceCriteria",
|
|
254
|
+
"riskBudget",
|
|
255
|
+
"createdAt",
|
|
256
|
+
"updatedAt",
|
|
257
|
+
]);
|
|
258
|
+
if (objective.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
259
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
260
|
+
}
|
|
261
|
+
const status = string(objective.status, `${label}.status`);
|
|
262
|
+
if (!OBJECTIVE_STATUSES.some((candidate) => candidate === status)) {
|
|
263
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
264
|
+
}
|
|
265
|
+
const constraints = stringArray(objective.constraints, `${label}.constraints`);
|
|
266
|
+
if (constraints.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
267
|
+
constraints.some((constraint) => !constraint.trim() || constraint.length > MAX_ORCHESTRATION_DESCRIPTION_LENGTH)) {
|
|
268
|
+
throw new DurableTaskRuntimeError(`${label}.constraints must be a bounded description array.`);
|
|
269
|
+
}
|
|
270
|
+
if (!Array.isArray(objective.acceptanceCriteria)) {
|
|
271
|
+
throw new DurableTaskRuntimeError(`${label}.acceptanceCriteria must be an array.`);
|
|
272
|
+
}
|
|
273
|
+
if (objective.acceptanceCriteria.length > MAX_ORCHESTRATION_COLLECTION_LENGTH) {
|
|
274
|
+
throw new DurableTaskRuntimeError(`${label}.acceptanceCriteria exceeds its collection limit.`);
|
|
275
|
+
}
|
|
276
|
+
const acceptanceCriteria = objective.acceptanceCriteria.map((value, index) => {
|
|
277
|
+
const criterion = exactRecord(value, `${label}.acceptanceCriteria[${index}]`, [
|
|
278
|
+
"id",
|
|
279
|
+
"description",
|
|
280
|
+
"required",
|
|
281
|
+
"evaluator",
|
|
282
|
+
]);
|
|
283
|
+
return {
|
|
284
|
+
id: dispatchIdentifier(criterion.id, `${label}.acceptanceCriteria[${index}].id`),
|
|
285
|
+
description: retainedDescription(criterion.description, `${label}.acceptanceCriteria[${index}].description`),
|
|
286
|
+
required: boolean(criterion.required, `${label}.acceptanceCriteria[${index}].required`),
|
|
287
|
+
...(criterion.evaluator === undefined
|
|
288
|
+
? {}
|
|
289
|
+
: {
|
|
290
|
+
evaluator: dispatchIdentifier(criterion.evaluator, `${label}.acceptanceCriteria[${index}].evaluator`),
|
|
291
|
+
}),
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
assertAcceptanceCriteria(acceptanceCriteria);
|
|
295
|
+
const riskBudget = structuredClone(record(objective.riskBudget, `${label}.riskBudget`));
|
|
296
|
+
assertRiskBudget(riskBudget, `${label}.riskBudget`);
|
|
297
|
+
return {
|
|
298
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
299
|
+
objectiveId: dispatchIdentifier(objective.objectiveId, `${label}.objectiveId`),
|
|
300
|
+
title: retainedDescription(objective.title, `${label}.title`),
|
|
301
|
+
description: retainedDescription(objective.description, `${label}.description`),
|
|
302
|
+
status: status,
|
|
303
|
+
constraints,
|
|
304
|
+
acceptanceCriteria,
|
|
305
|
+
riskBudget,
|
|
306
|
+
createdAt: isoDate(objective.createdAt, `${label}.createdAt`),
|
|
307
|
+
updatedAt: isoDate(objective.updatedAt, `${label}.updatedAt`),
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
function evidenceFromValue(value, label) {
|
|
311
|
+
const evidence = exactRecord(value, label, [
|
|
312
|
+
"evidenceId",
|
|
313
|
+
"criterionId",
|
|
314
|
+
"kind",
|
|
315
|
+
"summary",
|
|
316
|
+
"artifactIds",
|
|
317
|
+
"trusted",
|
|
318
|
+
"createdAt",
|
|
319
|
+
"metadata",
|
|
320
|
+
]);
|
|
321
|
+
const kind = string(evidence.kind, `${label}.kind`);
|
|
322
|
+
if (!["observation", "command", "test", "review", "external"].includes(kind)) {
|
|
323
|
+
throw new DurableTaskRuntimeError(`${label}.kind is invalid.`);
|
|
324
|
+
}
|
|
325
|
+
const metadata = evidence.metadata === undefined ? undefined : structuredClone(record(evidence.metadata, `${label}.metadata`));
|
|
326
|
+
return {
|
|
327
|
+
evidenceId: dispatchIdentifier(evidence.evidenceId, `${label}.evidenceId`),
|
|
328
|
+
...(evidence.criterionId === undefined
|
|
329
|
+
? {}
|
|
330
|
+
: { criterionId: dispatchIdentifier(evidence.criterionId, `${label}.criterionId`) }),
|
|
331
|
+
kind: kind,
|
|
332
|
+
summary: retainedDescription(evidence.summary, `${label}.summary`),
|
|
333
|
+
artifactIds: dispatchIdentifierArray(evidence.artifactIds, `${label}.artifactIds`),
|
|
334
|
+
trusted: boolean(evidence.trusted, `${label}.trusted`),
|
|
335
|
+
createdAt: isoDate(evidence.createdAt, `${label}.createdAt`),
|
|
336
|
+
...(metadata ? { metadata: metadata } : {}),
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
function objectiveRuntimeStateFromValue(value, label) {
|
|
340
|
+
const state = exactRecord(value, label, ["objective", "taskIds", "evidence"]);
|
|
341
|
+
const objective = objectiveFromValue(state.objective, `${label}.objective`);
|
|
342
|
+
if (!Array.isArray(state.evidence) || state.evidence.length > MAX_ORCHESTRATION_OBJECTIVE_EVIDENCE) {
|
|
343
|
+
throw new DurableTaskRuntimeError(`${label}.evidence exceeds its retained collection limit (${MAX_ORCHESTRATION_OBJECTIVE_EVIDENCE}).`);
|
|
344
|
+
}
|
|
345
|
+
const evidence = state.evidence.map((item, index) => evidenceFromValue(item, `${label}.evidence[${index}]`));
|
|
346
|
+
const evidenceIds = new Set();
|
|
347
|
+
for (const item of evidence) {
|
|
348
|
+
if (evidenceIds.has(item.evidenceId)) {
|
|
349
|
+
throw new DurableTaskRuntimeError(`${label}.evidence contains duplicate id '${item.evidenceId}'.`);
|
|
350
|
+
}
|
|
351
|
+
evidenceIds.add(item.evidenceId);
|
|
352
|
+
if (item.criterionId && !objective.acceptanceCriteria.some((criterion) => criterion.id === item.criterionId)) {
|
|
353
|
+
throw new DurableTaskRuntimeError(`${label}.evidence references unknown acceptance criterion '${item.criterionId}'.`);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
objective,
|
|
358
|
+
taskIds: retainedIdentifierArray(state.taskIds, MAX_ORCHESTRATION_TASKS, `${label}.taskIds`),
|
|
359
|
+
evidence,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
export function assertAcceptanceCriteria(criteria) {
|
|
363
|
+
const criterionIds = criteria.map((criterion) => criterion.id);
|
|
364
|
+
if (criterionIds.some((id) => !id.trim()) ||
|
|
365
|
+
new Set(criterionIds).size !== criterionIds.length ||
|
|
366
|
+
criteria.some((criterion) => !criterion.description.trim())) {
|
|
367
|
+
throw new DurableTaskRuntimeError("Objective acceptance criteria require unique ids and descriptions.");
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
export function assertReferencedAcceptanceCriteriaRetained(tasks, current, acceptanceCriteria) {
|
|
371
|
+
const retainedCriterionIds = new Set(acceptanceCriteria.map((criterion) => criterion.id));
|
|
372
|
+
const referencedRemovedIds = current.taskIds.flatMap((taskId) => (tasks[taskId]?.task.acceptanceCriterionIds ?? []).filter((criterionId) => !retainedCriterionIds.has(criterionId)));
|
|
373
|
+
if (referencedRemovedIds.length > 0) {
|
|
374
|
+
throw new DurableTaskRuntimeError(`Cannot remove acceptance criteria referenced by tasks: ${[...new Set(referencedRemovedIds)].join(", ")}.`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
export function taskFromValue(value, label) {
|
|
378
|
+
const task = exactRecord(value, label, [
|
|
379
|
+
"schemaVersion",
|
|
380
|
+
"taskId",
|
|
381
|
+
"objectiveId",
|
|
382
|
+
"title",
|
|
383
|
+
"description",
|
|
384
|
+
"role",
|
|
385
|
+
"status",
|
|
386
|
+
"dependsOn",
|
|
387
|
+
"requiredCapabilities",
|
|
388
|
+
"acceptanceCriterionIds",
|
|
389
|
+
"verificationOfTaskId",
|
|
390
|
+
"riskBudget",
|
|
391
|
+
"createdAt",
|
|
392
|
+
"updatedAt",
|
|
393
|
+
]);
|
|
394
|
+
if (task.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
395
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
396
|
+
}
|
|
397
|
+
const role = string(task.role, `${label}.role`);
|
|
398
|
+
if (!WORKER_ROLES.some((candidate) => candidate === role)) {
|
|
399
|
+
throw new DurableTaskRuntimeError(`${label}.role is invalid.`);
|
|
400
|
+
}
|
|
401
|
+
const status = string(task.status, `${label}.status`);
|
|
402
|
+
if (!TASK_STATUSES.some((candidate) => candidate === status)) {
|
|
403
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
404
|
+
}
|
|
405
|
+
const requiredCapabilities = stringArray(task.requiredCapabilities, `${label}.requiredCapabilities`);
|
|
406
|
+
if (requiredCapabilities.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
407
|
+
new Set(requiredCapabilities).size !== requiredCapabilities.length ||
|
|
408
|
+
!requiredCapabilities.every(isHarnessCapability)) {
|
|
409
|
+
throw new DurableTaskRuntimeError(`${label}.requiredCapabilities must be a bounded capability array.`);
|
|
410
|
+
}
|
|
411
|
+
const riskBudget = structuredClone(record(task.riskBudget, `${label}.riskBudget`));
|
|
412
|
+
assertRiskBudget(riskBudget, `${label}.riskBudget`);
|
|
413
|
+
return {
|
|
414
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
415
|
+
taskId: dispatchIdentifier(task.taskId, `${label}.taskId`),
|
|
416
|
+
objectiveId: dispatchIdentifier(task.objectiveId, `${label}.objectiveId`),
|
|
417
|
+
title: retainedDescription(task.title, `${label}.title`),
|
|
418
|
+
description: retainedDescription(task.description, `${label}.description`),
|
|
419
|
+
role: role,
|
|
420
|
+
status: status,
|
|
421
|
+
dependsOn: dispatchIdentifierArray(task.dependsOn, `${label}.dependsOn`),
|
|
422
|
+
requiredCapabilities: requiredCapabilities,
|
|
423
|
+
acceptanceCriterionIds: dispatchIdentifierArray(task.acceptanceCriterionIds, `${label}.acceptanceCriterionIds`),
|
|
424
|
+
...(task.verificationOfTaskId === undefined
|
|
425
|
+
? {}
|
|
426
|
+
: {
|
|
427
|
+
verificationOfTaskId: dispatchIdentifier(task.verificationOfTaskId, `${label}.verificationOfTaskId`),
|
|
428
|
+
}),
|
|
429
|
+
riskBudget,
|
|
430
|
+
createdAt: isoDate(task.createdAt, `${label}.createdAt`),
|
|
431
|
+
updatedAt: isoDate(task.updatedAt, `${label}.updatedAt`),
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
export function taskFromPayload(payload) {
|
|
435
|
+
return taskFromValue(payload.task, "task");
|
|
436
|
+
}
|
|
437
|
+
function taskRuntimeStateFromValue(value, label) {
|
|
438
|
+
const state = exactRecord(value, label, ["task", "attemptIds", "verification"]);
|
|
439
|
+
let verification;
|
|
440
|
+
if (state.verification !== undefined) {
|
|
441
|
+
const record = exactRecord(state.verification, `${label}.verification`, [
|
|
442
|
+
"verifierTaskId",
|
|
443
|
+
"verifierAttemptId",
|
|
444
|
+
"verdict",
|
|
445
|
+
"reasonCode",
|
|
446
|
+
"completedAt",
|
|
447
|
+
]);
|
|
448
|
+
const verdict = string(record.verdict, `${label}.verification.verdict`);
|
|
449
|
+
if (verdict !== "accepted" && verdict !== "rejected" && verdict !== "inconclusive") {
|
|
450
|
+
throw new DurableTaskRuntimeError(`${label}.verification.verdict is invalid.`);
|
|
451
|
+
}
|
|
452
|
+
verification = {
|
|
453
|
+
verifierTaskId: dispatchIdentifier(record.verifierTaskId, `${label}.verification.verifierTaskId`),
|
|
454
|
+
verifierAttemptId: dispatchIdentifier(record.verifierAttemptId, `${label}.verification.verifierAttemptId`),
|
|
455
|
+
verdict,
|
|
456
|
+
reasonCode: dispatchIdentifier(record.reasonCode, `${label}.verification.reasonCode`),
|
|
457
|
+
completedAt: dispatchIdentifier(record.completedAt, `${label}.verification.completedAt`),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
task: taskFromValue(state.task, `${label}.task`),
|
|
462
|
+
attemptIds: retainedIdentifierArray(state.attemptIds, MAX_ORCHESTRATION_ATTEMPTS, `${label}.attemptIds`),
|
|
463
|
+
...(verification ? { verification } : {}),
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
function resourcePointerFromValue(value, label) {
|
|
467
|
+
const pointer = exactRecord(value, label, ["id", "kind", "uri", "readOnly", "digest", "metadata"]);
|
|
468
|
+
const kind = string(pointer.kind, `${label}.kind`);
|
|
469
|
+
if (!isResourcePointerKind(kind))
|
|
470
|
+
throw new DurableTaskRuntimeError(`${label}.kind is invalid.`);
|
|
471
|
+
const uri = string(pointer.uri, `${label}.uri`);
|
|
472
|
+
if (!uri.trim() || uri.length > MAX_WORKER_RESOURCE_PATH_LENGTH) {
|
|
473
|
+
throw new DurableTaskRuntimeError(`${label}.uri exceeds its durable size bound.`);
|
|
474
|
+
}
|
|
475
|
+
const metadata = pointer.metadata === undefined
|
|
476
|
+
? undefined
|
|
477
|
+
: structuredClone(record(pointer.metadata, `${label}.metadata`));
|
|
478
|
+
return {
|
|
479
|
+
id: dispatchIdentifier(pointer.id, `${label}.id`),
|
|
480
|
+
kind,
|
|
481
|
+
uri,
|
|
482
|
+
readOnly: boolean(pointer.readOnly, `${label}.readOnly`),
|
|
483
|
+
...(pointer.digest === undefined ? {} : { digest: dispatchIdentifier(pointer.digest, `${label}.digest`) }),
|
|
484
|
+
...(metadata ? { metadata } : {}),
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
function resourcePointersFromValue(value, label) {
|
|
488
|
+
if (!Array.isArray(value) || value.length > MAX_WORKER_RESOURCE_POINTERS) {
|
|
489
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its retained collection limit.`);
|
|
490
|
+
}
|
|
491
|
+
const pointers = value.map((pointer, index) => resourcePointerFromValue(pointer, `${label}[${index}]`));
|
|
492
|
+
if (new Set(pointers.map((pointer) => pointer.id)).size !== pointers.length) {
|
|
493
|
+
throw new DurableTaskRuntimeError(`${label} contains duplicate ids.`);
|
|
494
|
+
}
|
|
495
|
+
return pointers;
|
|
496
|
+
}
|
|
497
|
+
export function executionGrantFromValue(value, label) {
|
|
498
|
+
const grant = exactRecord(value, label, [
|
|
499
|
+
"schemaVersion",
|
|
500
|
+
"grantId",
|
|
501
|
+
"objectiveId",
|
|
502
|
+
"taskId",
|
|
503
|
+
"attemptId",
|
|
504
|
+
"subjectId",
|
|
505
|
+
"role",
|
|
506
|
+
"capabilities",
|
|
507
|
+
"allowedTools",
|
|
508
|
+
"resources",
|
|
509
|
+
"readPaths",
|
|
510
|
+
"writePaths",
|
|
511
|
+
"deniedPaths",
|
|
512
|
+
"budget",
|
|
513
|
+
"policyVersion",
|
|
514
|
+
"decisionTrace",
|
|
515
|
+
"issuedAt",
|
|
516
|
+
"expiresAt",
|
|
517
|
+
]);
|
|
518
|
+
if (grant.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
519
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
520
|
+
}
|
|
521
|
+
const role = string(grant.role, `${label}.role`);
|
|
522
|
+
if (!WORKER_ROLES.some((candidate) => candidate === role)) {
|
|
523
|
+
throw new DurableTaskRuntimeError(`${label}.role is invalid.`);
|
|
524
|
+
}
|
|
525
|
+
const capabilities = stringArray(grant.capabilities, `${label}.capabilities`);
|
|
526
|
+
if (capabilities.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
527
|
+
new Set(capabilities).size !== capabilities.length ||
|
|
528
|
+
!capabilities.every(isHarnessCapability)) {
|
|
529
|
+
throw new DurableTaskRuntimeError(`${label}.capabilities is invalid.`);
|
|
530
|
+
}
|
|
531
|
+
const boundedStrings = (field) => {
|
|
532
|
+
const values = stringArray(grant[field], `${label}.${field}`);
|
|
533
|
+
if (values.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
534
|
+
new Set(values).size !== values.length ||
|
|
535
|
+
values.some((item) => !item || item.length > MAX_ORCHESTRATION_DESCRIPTION_LENGTH)) {
|
|
536
|
+
throw new DurableTaskRuntimeError(`${label}.${field} is invalid.`);
|
|
537
|
+
}
|
|
538
|
+
return values;
|
|
539
|
+
};
|
|
540
|
+
if (!Array.isArray(grant.decisionTrace) || grant.decisionTrace.length > MAX_ORCHESTRATION_COLLECTION_LENGTH) {
|
|
541
|
+
throw new DurableTaskRuntimeError(`${label}.decisionTrace is invalid.`);
|
|
542
|
+
}
|
|
543
|
+
const resources = resourcePointersFromValue(grant.resources, `${label}.resources`);
|
|
544
|
+
const decisionTrace = grant.decisionTrace.map((value, index) => {
|
|
545
|
+
const decision = exactRecord(value, `${label}.decisionTrace[${index}]`, [
|
|
546
|
+
"capability",
|
|
547
|
+
"outcome",
|
|
548
|
+
"reasonCode",
|
|
549
|
+
"source",
|
|
550
|
+
]);
|
|
551
|
+
const capability = string(decision.capability, `${label}.decisionTrace[${index}].capability`);
|
|
552
|
+
if (!isHarnessCapability(capability)) {
|
|
553
|
+
throw new DurableTaskRuntimeError(`${label}.decisionTrace[${index}].capability is invalid.`);
|
|
554
|
+
}
|
|
555
|
+
const outcome = string(decision.outcome, `${label}.decisionTrace[${index}].outcome`);
|
|
556
|
+
if (outcome !== "allow" && outcome !== "deny") {
|
|
557
|
+
throw new DurableTaskRuntimeError(`${label}.decisionTrace[${index}].outcome is invalid.`);
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
capability,
|
|
561
|
+
outcome,
|
|
562
|
+
reasonCode: dispatchIdentifier(decision.reasonCode, `${label}.decisionTrace[${index}].reasonCode`),
|
|
563
|
+
source: retainedDescription(decision.source, `${label}.decisionTrace[${index}].source`),
|
|
564
|
+
};
|
|
565
|
+
});
|
|
566
|
+
const budget = structuredClone(record(grant.budget, `${label}.budget`));
|
|
567
|
+
assertRiskBudget(budget, `${label}.budget`);
|
|
568
|
+
const issuedAt = isoDate(grant.issuedAt, `${label}.issuedAt`);
|
|
569
|
+
const expiresAt = grant.expiresAt === undefined ? undefined : isoDate(grant.expiresAt, `${label}.expiresAt`);
|
|
570
|
+
if (expiresAt && Date.parse(expiresAt) <= Date.parse(issuedAt)) {
|
|
571
|
+
throw new DurableTaskRuntimeError(`${label}.expiresAt must be after issuedAt.`);
|
|
572
|
+
}
|
|
573
|
+
return {
|
|
574
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
575
|
+
grantId: dispatchIdentifier(grant.grantId, `${label}.grantId`),
|
|
576
|
+
objectiveId: dispatchIdentifier(grant.objectiveId, `${label}.objectiveId`),
|
|
577
|
+
taskId: dispatchIdentifier(grant.taskId, `${label}.taskId`),
|
|
578
|
+
attemptId: dispatchIdentifier(grant.attemptId, `${label}.attemptId`),
|
|
579
|
+
subjectId: dispatchIdentifier(grant.subjectId, `${label}.subjectId`),
|
|
580
|
+
role: role,
|
|
581
|
+
capabilities: capabilities,
|
|
582
|
+
allowedTools: boundedStrings("allowedTools"),
|
|
583
|
+
resources,
|
|
584
|
+
readPaths: boundedStrings("readPaths"),
|
|
585
|
+
writePaths: boundedStrings("writePaths"),
|
|
586
|
+
deniedPaths: boundedStrings("deniedPaths"),
|
|
587
|
+
budget,
|
|
588
|
+
policyVersion: dispatchIdentifier(grant.policyVersion, `${label}.policyVersion`),
|
|
589
|
+
decisionTrace,
|
|
590
|
+
issuedAt,
|
|
591
|
+
...(expiresAt ? { expiresAt } : {}),
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
function attemptRuntimeStateFromValue(value, label) {
|
|
595
|
+
const attempt = exactRecord(value, label, [
|
|
596
|
+
"attemptId",
|
|
597
|
+
"taskId",
|
|
598
|
+
"dispatch",
|
|
599
|
+
"status",
|
|
600
|
+
"reasonCode",
|
|
601
|
+
"grantId",
|
|
602
|
+
"grant",
|
|
603
|
+
"agentId",
|
|
604
|
+
"lease",
|
|
605
|
+
"retry",
|
|
606
|
+
"checkpointIds",
|
|
607
|
+
"result",
|
|
608
|
+
"createdAt",
|
|
609
|
+
"updatedAt",
|
|
610
|
+
]);
|
|
611
|
+
const status = string(attempt.status, `${label}.status`);
|
|
612
|
+
if (!ATTEMPT_STATUSES.some((candidate) => candidate === status)) {
|
|
613
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
614
|
+
}
|
|
615
|
+
const grant = attempt.grant === undefined ? undefined : executionGrantFromValue(attempt.grant, `${label}.grant`);
|
|
616
|
+
const createdAt = isoDate(attempt.createdAt, `${label}.createdAt`);
|
|
617
|
+
const updatedAt = isoDate(attempt.updatedAt, `${label}.updatedAt`);
|
|
618
|
+
if (Date.parse(updatedAt) < Date.parse(createdAt)) {
|
|
619
|
+
throw new DurableTaskRuntimeError(`${label}.updatedAt is before createdAt.`);
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
attemptId: dispatchIdentifier(attempt.attemptId, `${label}.attemptId`),
|
|
623
|
+
taskId: dispatchIdentifier(attempt.taskId, `${label}.taskId`),
|
|
624
|
+
dispatch: dispatchFromValue(attempt.dispatch, `${label}.dispatch`),
|
|
625
|
+
status: status,
|
|
626
|
+
...(attempt.reasonCode === undefined
|
|
627
|
+
? {}
|
|
628
|
+
: { reasonCode: dispatchIdentifier(attempt.reasonCode, `${label}.reasonCode`) }),
|
|
629
|
+
...(attempt.grantId === undefined ? {} : { grantId: dispatchIdentifier(attempt.grantId, `${label}.grantId`) }),
|
|
630
|
+
...(grant ? { grant } : {}),
|
|
631
|
+
...(attempt.agentId === undefined ? {} : { agentId: dispatchIdentifier(attempt.agentId, `${label}.agentId`) }),
|
|
632
|
+
...(attempt.lease === undefined ? {} : { lease: leaseFromPayload(toJsonObject({ lease: attempt.lease })) }),
|
|
633
|
+
...(attempt.retry === undefined ? {} : { retry: retryStateFromValue(attempt.retry, `${label}.retry`) }),
|
|
634
|
+
checkpointIds: retainedIdentifierArray(attempt.checkpointIds, MAX_ORCHESTRATION_CHECKPOINTS, `${label}.checkpointIds`),
|
|
635
|
+
...(attempt.result === undefined ? {} : { result: resultFromValue(attempt.result, `${label}.result`) }),
|
|
636
|
+
createdAt,
|
|
637
|
+
updatedAt,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
export function leaseFromPayload(payload) {
|
|
641
|
+
return leaseFromValue(payload.lease, "lease");
|
|
642
|
+
}
|
|
643
|
+
function leaseFromValue(value, label) {
|
|
644
|
+
const lease = exactRecord(value, label, [
|
|
645
|
+
"leaseId",
|
|
646
|
+
"attemptId",
|
|
647
|
+
"ownerId",
|
|
648
|
+
"fencingToken",
|
|
649
|
+
"issuedAt",
|
|
650
|
+
"expiresAt",
|
|
651
|
+
]);
|
|
652
|
+
const fencingToken = number(lease.fencingToken, `${label}.fencingToken`);
|
|
653
|
+
if (!Number.isSafeInteger(fencingToken) || fencingToken < 1) {
|
|
654
|
+
throw new DurableTaskRuntimeError(`${label}.fencingToken must be a positive safe integer.`);
|
|
655
|
+
}
|
|
656
|
+
const issuedAt = isoDate(lease.issuedAt, `${label}.issuedAt`);
|
|
657
|
+
const expiresAt = isoDate(lease.expiresAt, `${label}.expiresAt`);
|
|
658
|
+
if (Date.parse(expiresAt) <= Date.parse(issuedAt)) {
|
|
659
|
+
throw new DurableTaskRuntimeError(`${label}.expiresAt must be after issuedAt.`);
|
|
660
|
+
}
|
|
661
|
+
return {
|
|
662
|
+
leaseId: dispatchIdentifier(lease.leaseId, `${label}.leaseId`),
|
|
663
|
+
attemptId: dispatchIdentifier(lease.attemptId, `${label}.attemptId`),
|
|
664
|
+
ownerId: dispatchIdentifier(lease.ownerId, `${label}.ownerId`),
|
|
665
|
+
fencingToken,
|
|
666
|
+
issuedAt,
|
|
667
|
+
expiresAt,
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
export function retryStateFromValue(value, label) {
|
|
671
|
+
const retry = record(value, label);
|
|
672
|
+
const unsupportedField = Object.keys(retry).find((field) => field !== "retriesUsed" && field !== "notBefore");
|
|
673
|
+
if (unsupportedField)
|
|
674
|
+
throw new DurableTaskRuntimeError(`${label}.${unsupportedField} is unsupported.`);
|
|
675
|
+
const retriesUsed = number(retry.retriesUsed, `${label}.retriesUsed`);
|
|
676
|
+
if (!Number.isSafeInteger(retriesUsed) || retriesUsed < 1) {
|
|
677
|
+
throw new DurableTaskRuntimeError(`${label}.retriesUsed must be a positive safe integer.`);
|
|
678
|
+
}
|
|
679
|
+
const notBefore = string(retry.notBefore, `${label}.notBefore`);
|
|
680
|
+
const notBeforeMs = Date.parse(notBefore);
|
|
681
|
+
if (!Number.isFinite(notBeforeMs) ||
|
|
682
|
+
Math.abs(notBeforeMs) > MAX_DATE_EPOCH_MS ||
|
|
683
|
+
new Date(notBeforeMs).toISOString() !== notBefore) {
|
|
684
|
+
throw new DurableTaskRuntimeError(`${label}.notBefore is invalid.`);
|
|
685
|
+
}
|
|
686
|
+
return { retriesUsed, notBefore };
|
|
687
|
+
}
|
|
688
|
+
export function checkpointFromPayload(payload) {
|
|
689
|
+
return checkpointFromValue(payload.checkpoint, "checkpoint");
|
|
690
|
+
}
|
|
691
|
+
function checkpointFromValue(value, label) {
|
|
692
|
+
const checkpoint = exactRecord(value, label, [
|
|
693
|
+
"checkpointId",
|
|
694
|
+
"attemptId",
|
|
695
|
+
"fencingToken",
|
|
696
|
+
"summary",
|
|
697
|
+
"artifactIds",
|
|
698
|
+
"evidenceIds",
|
|
699
|
+
"usage",
|
|
700
|
+
"createdAt",
|
|
701
|
+
]);
|
|
702
|
+
const usage = checkpoint.usage === undefined ? undefined : usageFromPayload(checkpoint.usage, `${label}.usage`);
|
|
703
|
+
const fencingToken = number(checkpoint.fencingToken, `${label}.fencingToken`);
|
|
704
|
+
if (!Number.isSafeInteger(fencingToken) || fencingToken < 1) {
|
|
705
|
+
throw new DurableTaskRuntimeError(`${label}.fencingToken must be a positive safe integer.`);
|
|
706
|
+
}
|
|
707
|
+
return {
|
|
708
|
+
checkpointId: dispatchIdentifier(checkpoint.checkpointId, `${label}.checkpointId`),
|
|
709
|
+
attemptId: dispatchIdentifier(checkpoint.attemptId, `${label}.attemptId`),
|
|
710
|
+
fencingToken,
|
|
711
|
+
summary: checkpointSummary(checkpoint.summary, `${label}.summary`),
|
|
712
|
+
artifactIds: dispatchIdentifierArray(checkpoint.artifactIds, `${label}.artifactIds`),
|
|
713
|
+
evidenceIds: dispatchIdentifierArray(checkpoint.evidenceIds, `${label}.evidenceIds`),
|
|
714
|
+
...(usage ? { usage } : {}),
|
|
715
|
+
createdAt: isoDate(checkpoint.createdAt, `${label}.createdAt`),
|
|
716
|
+
};
|
|
717
|
+
}
|
|
718
|
+
export function usageFromPayload(value, label) {
|
|
719
|
+
const usage = record(value, label);
|
|
720
|
+
const expectedFields = [
|
|
721
|
+
"toolCalls",
|
|
722
|
+
"inputTokens",
|
|
723
|
+
"outputTokens",
|
|
724
|
+
"cacheReadTokens",
|
|
725
|
+
"cacheWriteTokens",
|
|
726
|
+
"totalTokens",
|
|
727
|
+
"costUsd",
|
|
728
|
+
"activeWallClockMs",
|
|
729
|
+
];
|
|
730
|
+
const unexpected = Object.keys(usage).find((field) => !expectedFields.includes(field));
|
|
731
|
+
if (unexpected)
|
|
732
|
+
throw new DurableTaskRuntimeError(`${label}.${unexpected} is unsupported.`);
|
|
733
|
+
try {
|
|
734
|
+
return validateAttemptUsageSnapshot({
|
|
735
|
+
toolCalls: number(usage.toolCalls, `${label}.toolCalls`),
|
|
736
|
+
inputTokens: number(usage.inputTokens, `${label}.inputTokens`),
|
|
737
|
+
outputTokens: number(usage.outputTokens, `${label}.outputTokens`),
|
|
738
|
+
cacheReadTokens: number(usage.cacheReadTokens, `${label}.cacheReadTokens`),
|
|
739
|
+
cacheWriteTokens: number(usage.cacheWriteTokens, `${label}.cacheWriteTokens`),
|
|
740
|
+
totalTokens: number(usage.totalTokens, `${label}.totalTokens`),
|
|
741
|
+
costUsd: number(usage.costUsd, `${label}.costUsd`),
|
|
742
|
+
activeWallClockMs: number(usage.activeWallClockMs, `${label}.activeWallClockMs`),
|
|
743
|
+
}, label);
|
|
744
|
+
}
|
|
745
|
+
catch (error) {
|
|
746
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
export function resultFromPayload(payload) {
|
|
750
|
+
return resultFromValue(payload.result, "result");
|
|
751
|
+
}
|
|
752
|
+
function resultFromValue(value, label) {
|
|
753
|
+
const result = exactRecord(value, label, [
|
|
754
|
+
"schemaVersion",
|
|
755
|
+
"resultId",
|
|
756
|
+
"objectiveId",
|
|
757
|
+
"taskId",
|
|
758
|
+
"attemptId",
|
|
759
|
+
"leaseId",
|
|
760
|
+
"fencingToken",
|
|
761
|
+
"status",
|
|
762
|
+
"reasonCode",
|
|
763
|
+
"summary",
|
|
764
|
+
"artifacts",
|
|
765
|
+
"evidence",
|
|
766
|
+
"errors",
|
|
767
|
+
"nextAction",
|
|
768
|
+
"usage",
|
|
769
|
+
"createdAt",
|
|
770
|
+
]);
|
|
771
|
+
if (result.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
772
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
773
|
+
}
|
|
774
|
+
const status = string(result.status, `${label}.status`);
|
|
775
|
+
if (!WORKER_RESULT_STATUSES.some((candidate) => candidate === status)) {
|
|
776
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
777
|
+
}
|
|
778
|
+
if (!Array.isArray(result.artifacts) || !Array.isArray(result.evidence) || !Array.isArray(result.errors)) {
|
|
779
|
+
throw new DurableTaskRuntimeError(`${label} collections are invalid.`);
|
|
780
|
+
}
|
|
781
|
+
if (result.artifacts.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
782
|
+
result.evidence.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
783
|
+
result.errors.length > MAX_ORCHESTRATION_COLLECTION_LENGTH) {
|
|
784
|
+
throw new DurableTaskRuntimeError(`${label} exceeds its collection limit.`);
|
|
785
|
+
}
|
|
786
|
+
const artifacts = result.artifacts.map((value, index) => {
|
|
787
|
+
const artifact = exactRecord(value, `${label}.artifacts[${index}]`, [
|
|
788
|
+
"artifactId",
|
|
789
|
+
"kind",
|
|
790
|
+
"uri",
|
|
791
|
+
"digest",
|
|
792
|
+
"sizeBytes",
|
|
793
|
+
"createdAt",
|
|
794
|
+
"metadata",
|
|
795
|
+
]);
|
|
796
|
+
const kind = string(artifact.kind, `${label}.artifacts[${index}].kind`);
|
|
797
|
+
if (!["diff", "file", "report", "test-result", "log", "structured-data"].includes(kind)) {
|
|
798
|
+
throw new DurableTaskRuntimeError(`${label}.artifacts[${index}].kind is invalid.`);
|
|
799
|
+
}
|
|
800
|
+
if (artifact.sizeBytes !== undefined &&
|
|
801
|
+
(!Number.isSafeInteger(artifact.sizeBytes) || Number(artifact.sizeBytes) < 0)) {
|
|
802
|
+
throw new DurableTaskRuntimeError(`${label}.artifacts[${index}].sizeBytes is invalid.`);
|
|
803
|
+
}
|
|
804
|
+
return {
|
|
805
|
+
artifactId: dispatchIdentifier(artifact.artifactId, `${label}.artifacts[${index}].artifactId`),
|
|
806
|
+
kind,
|
|
807
|
+
uri: retainedDescription(artifact.uri, `${label}.artifacts[${index}].uri`),
|
|
808
|
+
...(artifact.digest === undefined
|
|
809
|
+
? {}
|
|
810
|
+
: { digest: dispatchIdentifier(artifact.digest, `${label}.artifacts[${index}].digest`) }),
|
|
811
|
+
...(typeof artifact.sizeBytes === "number" ? { sizeBytes: artifact.sizeBytes } : {}),
|
|
812
|
+
createdAt: dispatchIdentifier(artifact.createdAt, `${label}.artifacts[${index}].createdAt`),
|
|
813
|
+
...(artifact.metadata === undefined
|
|
814
|
+
? {}
|
|
815
|
+
: { metadata: structuredClone(record(artifact.metadata, `${label}.artifacts[${index}].metadata`)) }),
|
|
816
|
+
};
|
|
817
|
+
});
|
|
818
|
+
const errors = result.errors.map((value, index) => {
|
|
819
|
+
const error = exactRecord(value, `${label}.errors[${index}]`, ["code", "message", "retryable", "details"]);
|
|
820
|
+
return {
|
|
821
|
+
code: dispatchIdentifier(error.code, `${label}.errors[${index}].code`),
|
|
822
|
+
message: retainedDescription(error.message, `${label}.errors[${index}].message`),
|
|
823
|
+
retryable: boolean(error.retryable, `${label}.errors[${index}].retryable`),
|
|
824
|
+
...(error.details === undefined
|
|
825
|
+
? {}
|
|
826
|
+
: { details: structuredClone(record(error.details, `${label}.errors[${index}].details`)) }),
|
|
827
|
+
};
|
|
828
|
+
});
|
|
829
|
+
const usage = exactRecord(result.usage, `${label}.usage`, [
|
|
830
|
+
"inputTokens",
|
|
831
|
+
"outputTokens",
|
|
832
|
+
"totalTokens",
|
|
833
|
+
"costUsd",
|
|
834
|
+
"wallClockMs",
|
|
835
|
+
"toolCalls",
|
|
836
|
+
]);
|
|
837
|
+
for (const field of ["inputTokens", "outputTokens", "totalTokens"]) {
|
|
838
|
+
if (usage[field] !== undefined && (!Number.isSafeInteger(usage[field]) || Number(usage[field]) < 0)) {
|
|
839
|
+
throw new DurableTaskRuntimeError(`${label}.usage.${field} is invalid.`);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (!Number.isSafeInteger(usage.toolCalls) || Number(usage.toolCalls) < 0) {
|
|
843
|
+
throw new DurableTaskRuntimeError(`${label}.usage.toolCalls is invalid.`);
|
|
844
|
+
}
|
|
845
|
+
if (usage.costUsd !== undefined &&
|
|
846
|
+
(typeof usage.costUsd !== "number" || !Number.isFinite(usage.costUsd) || usage.costUsd < 0)) {
|
|
847
|
+
throw new DurableTaskRuntimeError(`${label}.usage.costUsd is invalid.`);
|
|
848
|
+
}
|
|
849
|
+
if (typeof usage.wallClockMs !== "number" || !Number.isFinite(usage.wallClockMs) || usage.wallClockMs < 0) {
|
|
850
|
+
throw new DurableTaskRuntimeError(`${label}.usage.wallClockMs is invalid.`);
|
|
851
|
+
}
|
|
852
|
+
const fencingToken = number(result.fencingToken, `${label}.fencingToken`);
|
|
853
|
+
if (!Number.isSafeInteger(fencingToken) || fencingToken < 1) {
|
|
854
|
+
throw new DurableTaskRuntimeError(`${label}.fencingToken is invalid.`);
|
|
855
|
+
}
|
|
856
|
+
return {
|
|
857
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
858
|
+
resultId: dispatchIdentifier(result.resultId, `${label}.resultId`),
|
|
859
|
+
objectiveId: dispatchIdentifier(result.objectiveId, `${label}.objectiveId`),
|
|
860
|
+
taskId: dispatchIdentifier(result.taskId, `${label}.taskId`),
|
|
861
|
+
attemptId: dispatchIdentifier(result.attemptId, `${label}.attemptId`),
|
|
862
|
+
leaseId: dispatchIdentifier(result.leaseId, `${label}.leaseId`),
|
|
863
|
+
fencingToken,
|
|
864
|
+
status: status,
|
|
865
|
+
reasonCode: dispatchIdentifier(result.reasonCode, `${label}.reasonCode`),
|
|
866
|
+
summary: retainedDescription(result.summary, `${label}.summary`),
|
|
867
|
+
artifacts: artifacts,
|
|
868
|
+
evidence: result.evidence.map((item, index) => evidenceFromValue(item, `${label}.evidence[${index}]`)),
|
|
869
|
+
errors: errors,
|
|
870
|
+
...(result.nextAction === undefined
|
|
871
|
+
? {}
|
|
872
|
+
: { nextAction: retainedDescription(result.nextAction, `${label}.nextAction`) }),
|
|
873
|
+
usage: {
|
|
874
|
+
...(typeof usage.inputTokens === "number" ? { inputTokens: usage.inputTokens } : {}),
|
|
875
|
+
...(typeof usage.outputTokens === "number" ? { outputTokens: usage.outputTokens } : {}),
|
|
876
|
+
...(typeof usage.totalTokens === "number" ? { totalTokens: usage.totalTokens } : {}),
|
|
877
|
+
...(typeof usage.costUsd === "number" ? { costUsd: usage.costUsd } : {}),
|
|
878
|
+
wallClockMs: usage.wallClockMs,
|
|
879
|
+
toolCalls: usage.toolCalls,
|
|
880
|
+
},
|
|
881
|
+
createdAt: isoDate(result.createdAt, `${label}.createdAt`),
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function normalizeAgentBinding(value, label) {
|
|
885
|
+
const agent = exactRecord(value, label, [
|
|
886
|
+
"schemaVersion",
|
|
887
|
+
"agentId",
|
|
888
|
+
"resumeContext",
|
|
889
|
+
"parentAgentId",
|
|
890
|
+
"rootAgentId",
|
|
891
|
+
"depth",
|
|
892
|
+
"role",
|
|
893
|
+
"status",
|
|
894
|
+
"activeAttemptId",
|
|
895
|
+
"createdAt",
|
|
896
|
+
"updatedAt",
|
|
897
|
+
]);
|
|
898
|
+
if (agent.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
899
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
900
|
+
}
|
|
901
|
+
const agentId = dispatchIdentifier(agent.agentId, `${label}.agentId`);
|
|
902
|
+
const parentAgentId = optionalDispatchIdentifier(agent.parentAgentId, `${label}.parentAgentId`);
|
|
903
|
+
const rootAgentId = dispatchIdentifier(agent.rootAgentId, `${label}.rootAgentId`);
|
|
904
|
+
const depth = number(agent.depth, `${label}.depth`);
|
|
905
|
+
if (!Number.isSafeInteger(depth) || depth < 0) {
|
|
906
|
+
throw new DurableTaskRuntimeError(`${label} lineage is invalid.`);
|
|
907
|
+
}
|
|
908
|
+
if (parentAgentId && depth === 0)
|
|
909
|
+
throw new DurableTaskRuntimeError(`${label} child lineage depth is invalid.`);
|
|
910
|
+
const role = string(agent.role, `${label}.role`);
|
|
911
|
+
if (!WORKER_ROLES.some((candidate) => candidate === role)) {
|
|
912
|
+
throw new DurableTaskRuntimeError(`${label}.role is invalid.`);
|
|
913
|
+
}
|
|
914
|
+
const status = string(agent.status, `${label}.status`);
|
|
915
|
+
if (!["registered", "active", "suspended", "resuming", "retired"].includes(status)) {
|
|
916
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
917
|
+
}
|
|
918
|
+
const context = exactRecord(agent.resumeContext, `${label}.resumeContext`, [
|
|
919
|
+
"provider",
|
|
920
|
+
"sessionId",
|
|
921
|
+
"sessionDir",
|
|
922
|
+
"sessionFile",
|
|
923
|
+
"cwd",
|
|
924
|
+
"worktreeLaneKey",
|
|
925
|
+
"orchestrationProfileId",
|
|
926
|
+
"resourceProfileNames",
|
|
927
|
+
"modelRef",
|
|
928
|
+
"contextPointers",
|
|
929
|
+
"latestCheckpointId",
|
|
930
|
+
]);
|
|
931
|
+
const provider = string(context.provider, `${label}.resumeContext.provider`);
|
|
932
|
+
if (provider !== "pi" && provider !== "external") {
|
|
933
|
+
throw new DurableTaskRuntimeError(`${label}.resumeContext.provider is invalid.`);
|
|
934
|
+
}
|
|
935
|
+
const optionalPath = (field) => {
|
|
936
|
+
const candidate = context[field];
|
|
937
|
+
if (candidate === undefined)
|
|
938
|
+
return undefined;
|
|
939
|
+
const path = string(candidate, `${label}.resumeContext.${field}`);
|
|
940
|
+
if (!path.trim() || path.length > MAX_WORKER_RESOURCE_PATH_LENGTH) {
|
|
941
|
+
throw new DurableTaskRuntimeError(`${label}.resumeContext.${field} exceeds its durable size bound.`);
|
|
942
|
+
}
|
|
943
|
+
return path;
|
|
944
|
+
};
|
|
945
|
+
const cwd = string(context.cwd, `${label}.resumeContext.cwd`);
|
|
946
|
+
if (!cwd.trim() || cwd.length > MAX_WORKER_RESOURCE_PATH_LENGTH) {
|
|
947
|
+
throw new DurableTaskRuntimeError(`${label}.resumeContext.cwd exceeds its durable size bound.`);
|
|
948
|
+
}
|
|
949
|
+
const sessionDir = optionalPath("sessionDir");
|
|
950
|
+
const sessionFile = optionalPath("sessionFile");
|
|
951
|
+
const resumeContext = {
|
|
952
|
+
provider,
|
|
953
|
+
sessionId: dispatchIdentifier(context.sessionId, `${label}.resumeContext.sessionId`),
|
|
954
|
+
...(sessionDir ? { sessionDir } : {}),
|
|
955
|
+
...(sessionFile ? { sessionFile } : {}),
|
|
956
|
+
cwd,
|
|
957
|
+
...(context.worktreeLaneKey === undefined
|
|
958
|
+
? {}
|
|
959
|
+
: {
|
|
960
|
+
worktreeLaneKey: dispatchIdentifier(context.worktreeLaneKey, `${label}.resumeContext.worktreeLaneKey`),
|
|
961
|
+
}),
|
|
962
|
+
...(context.orchestrationProfileId === undefined
|
|
963
|
+
? {}
|
|
964
|
+
: {
|
|
965
|
+
orchestrationProfileId: dispatchIdentifier(context.orchestrationProfileId, `${label}.resumeContext.orchestrationProfileId`),
|
|
966
|
+
}),
|
|
967
|
+
resourceProfileNames: dispatchIdentifierArray(context.resourceProfileNames, `${label}.resumeContext.resourceProfileNames`),
|
|
968
|
+
...(context.modelRef === undefined
|
|
969
|
+
? {}
|
|
970
|
+
: { modelRef: dispatchIdentifier(context.modelRef, `${label}.resumeContext.modelRef`) }),
|
|
971
|
+
contextPointers: resourcePointersFromValue(context.contextPointers, `${label}.resumeContext.contextPointers`),
|
|
972
|
+
...(context.latestCheckpointId === undefined
|
|
973
|
+
? {}
|
|
974
|
+
: {
|
|
975
|
+
latestCheckpointId: dispatchIdentifier(context.latestCheckpointId, `${label}.resumeContext.latestCheckpointId`),
|
|
976
|
+
}),
|
|
977
|
+
};
|
|
978
|
+
try {
|
|
979
|
+
createAgentIdentity(agentId, resumeContext);
|
|
980
|
+
}
|
|
981
|
+
catch (error) {
|
|
982
|
+
throw new DurableTaskRuntimeError(error instanceof Error ? error.message : String(error));
|
|
983
|
+
}
|
|
984
|
+
const createdAt = isoDate(agent.createdAt, `${label}.createdAt`);
|
|
985
|
+
const updatedAt = isoDate(agent.updatedAt, `${label}.updatedAt`);
|
|
986
|
+
if (Date.parse(updatedAt) < Date.parse(createdAt)) {
|
|
987
|
+
throw new DurableTaskRuntimeError(`${label}.updatedAt is before createdAt.`);
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
991
|
+
agentId,
|
|
992
|
+
resumeContext,
|
|
993
|
+
...(parentAgentId ? { parentAgentId } : {}),
|
|
994
|
+
rootAgentId,
|
|
995
|
+
depth,
|
|
996
|
+
role: role,
|
|
997
|
+
status: status,
|
|
998
|
+
...(agent.activeAttemptId === undefined
|
|
999
|
+
? {}
|
|
1000
|
+
: { activeAttemptId: dispatchIdentifier(agent.activeAttemptId, `${label}.activeAttemptId`) }),
|
|
1001
|
+
createdAt,
|
|
1002
|
+
updatedAt,
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
export function agentFromPayload(payload) {
|
|
1006
|
+
return normalizeAgentBinding(payload.agent, "agent");
|
|
1007
|
+
}
|
|
1008
|
+
export function approvalFromPayload(payload) {
|
|
1009
|
+
return approvalFromValue(payload.approval, "approval");
|
|
1010
|
+
}
|
|
1011
|
+
function approvalFromValue(value, label) {
|
|
1012
|
+
const approval = exactRecord(value, label, [
|
|
1013
|
+
"schemaVersion",
|
|
1014
|
+
"approvalId",
|
|
1015
|
+
"objectiveId",
|
|
1016
|
+
"taskId",
|
|
1017
|
+
"attemptId",
|
|
1018
|
+
"reasonCode",
|
|
1019
|
+
"summary",
|
|
1020
|
+
"requestedCapabilities",
|
|
1021
|
+
"requestedBudget",
|
|
1022
|
+
"reversible",
|
|
1023
|
+
"createdAt",
|
|
1024
|
+
]);
|
|
1025
|
+
const capabilities = stringArray(approval.requestedCapabilities, `${label}.requestedCapabilities`);
|
|
1026
|
+
if (capabilities.length > MAX_ORCHESTRATION_COLLECTION_LENGTH ||
|
|
1027
|
+
new Set(capabilities).size !== capabilities.length ||
|
|
1028
|
+
!capabilities.every(isHarnessCapability)) {
|
|
1029
|
+
throw new DurableTaskRuntimeError(`${label} contains invalid capabilities.`);
|
|
1030
|
+
}
|
|
1031
|
+
const requestedBudget = approval.requestedBudget !== undefined
|
|
1032
|
+
? structuredClone(record(approval.requestedBudget, `${label}.requestedBudget`))
|
|
1033
|
+
: undefined;
|
|
1034
|
+
assertRiskBudget(requestedBudget, `${label}.requestedBudget`);
|
|
1035
|
+
if (capabilities.length === 0 && !requestedBudget) {
|
|
1036
|
+
throw new DurableTaskRuntimeError(`${label} must request capabilities or budget.`);
|
|
1037
|
+
}
|
|
1038
|
+
if (approval.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
1039
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
1040
|
+
}
|
|
1041
|
+
return {
|
|
1042
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
1043
|
+
approvalId: dispatchIdentifier(approval.approvalId, `${label}.approvalId`),
|
|
1044
|
+
objectiveId: dispatchIdentifier(approval.objectiveId, `${label}.objectiveId`),
|
|
1045
|
+
...(approval.taskId !== undefined ? { taskId: dispatchIdentifier(approval.taskId, `${label}.taskId`) } : {}),
|
|
1046
|
+
...(approval.attemptId !== undefined
|
|
1047
|
+
? { attemptId: dispatchIdentifier(approval.attemptId, `${label}.attemptId`) }
|
|
1048
|
+
: {}),
|
|
1049
|
+
reasonCode: dispatchIdentifier(approval.reasonCode, `${label}.reasonCode`),
|
|
1050
|
+
summary: retainedDescription(approval.summary, `${label}.summary`),
|
|
1051
|
+
requestedCapabilities: capabilities,
|
|
1052
|
+
...(requestedBudget ? { requestedBudget } : {}),
|
|
1053
|
+
reversible: boolean(approval.reversible, `${label}.reversible`),
|
|
1054
|
+
createdAt: isoDate(approval.createdAt, `${label}.createdAt`),
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
export function approvalResolutionFromPayload(payload) {
|
|
1058
|
+
return approvalResolutionFromValue(payload.resolution, "resolution");
|
|
1059
|
+
}
|
|
1060
|
+
function approvalResolutionFromValue(value, label) {
|
|
1061
|
+
const resolution = exactRecord(value, label, ["schemaVersion", "approvalId", "outcome", "reasonCode", "resolvedAt"]);
|
|
1062
|
+
if (resolution.schemaVersion !== ORCHESTRATION_SCHEMA_VERSION) {
|
|
1063
|
+
throw new DurableTaskRuntimeError(`${label}.schemaVersion is invalid.`);
|
|
1064
|
+
}
|
|
1065
|
+
const outcome = string(resolution.outcome, `${label}.outcome`);
|
|
1066
|
+
if (outcome !== "approved" && outcome !== "rejected") {
|
|
1067
|
+
throw new DurableTaskRuntimeError(`Unknown approval outcome '${outcome}'.`);
|
|
1068
|
+
}
|
|
1069
|
+
return {
|
|
1070
|
+
schemaVersion: ORCHESTRATION_SCHEMA_VERSION,
|
|
1071
|
+
approvalId: dispatchIdentifier(resolution.approvalId, `${label}.approvalId`),
|
|
1072
|
+
outcome,
|
|
1073
|
+
reasonCode: dispatchIdentifier(resolution.reasonCode, `${label}.reasonCode`),
|
|
1074
|
+
resolvedAt: isoDate(resolution.resolvedAt, `${label}.resolvedAt`),
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function approvalRuntimeStateFromValue(value, label) {
|
|
1078
|
+
const state = exactRecord(value, label, ["request", "status", "resolution"]);
|
|
1079
|
+
const request = approvalFromValue(state.request, `${label}.request`);
|
|
1080
|
+
const status = string(state.status, `${label}.status`);
|
|
1081
|
+
if (status !== "pending" && status !== "approved" && status !== "rejected") {
|
|
1082
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
1083
|
+
}
|
|
1084
|
+
const resolution = state.resolution === undefined ? undefined : approvalResolutionFromValue(state.resolution, `${label}.resolution`);
|
|
1085
|
+
if (status === "pending" && resolution) {
|
|
1086
|
+
throw new DurableTaskRuntimeError(`${label} pending approval cannot have a resolution.`);
|
|
1087
|
+
}
|
|
1088
|
+
if (status !== "pending" &&
|
|
1089
|
+
(!resolution || resolution.approvalId !== request.approvalId || resolution.outcome !== status)) {
|
|
1090
|
+
throw new DurableTaskRuntimeError(`${label} resolution does not match its approval state.`);
|
|
1091
|
+
}
|
|
1092
|
+
if (resolution && Date.parse(resolution.resolvedAt) < Date.parse(request.createdAt)) {
|
|
1093
|
+
throw new DurableTaskRuntimeError(`${label} resolution predates its request.`);
|
|
1094
|
+
}
|
|
1095
|
+
return { request, status, ...(resolution ? { resolution } : {}) };
|
|
1096
|
+
}
|
|
1097
|
+
function notificationRuntimeStateFromValue(value, label) {
|
|
1098
|
+
const notification = exactRecord(value, label, [
|
|
1099
|
+
"notificationId",
|
|
1100
|
+
"objectiveId",
|
|
1101
|
+
"attemptId",
|
|
1102
|
+
"status",
|
|
1103
|
+
"message",
|
|
1104
|
+
"createdAt",
|
|
1105
|
+
"deliveredAt",
|
|
1106
|
+
]);
|
|
1107
|
+
const status = string(notification.status, `${label}.status`);
|
|
1108
|
+
if (status !== "pending" && status !== "delivered") {
|
|
1109
|
+
throw new DurableTaskRuntimeError(`${label}.status is invalid.`);
|
|
1110
|
+
}
|
|
1111
|
+
const createdAt = isoDate(notification.createdAt, `${label}.createdAt`);
|
|
1112
|
+
const deliveredAt = notification.deliveredAt === undefined ? undefined : isoDate(notification.deliveredAt, `${label}.deliveredAt`);
|
|
1113
|
+
if ((status === "delivered") !== Boolean(deliveredAt)) {
|
|
1114
|
+
throw new DurableTaskRuntimeError(`${label} delivery status is inconsistent.`);
|
|
1115
|
+
}
|
|
1116
|
+
if (deliveredAt && Date.parse(deliveredAt) < Date.parse(createdAt)) {
|
|
1117
|
+
throw new DurableTaskRuntimeError(`${label}.deliveredAt predates createdAt.`);
|
|
1118
|
+
}
|
|
1119
|
+
return {
|
|
1120
|
+
notificationId: dispatchIdentifier(notification.notificationId, `${label}.notificationId`),
|
|
1121
|
+
objectiveId: dispatchIdentifier(notification.objectiveId, `${label}.objectiveId`),
|
|
1122
|
+
...(notification.attemptId === undefined
|
|
1123
|
+
? {}
|
|
1124
|
+
: { attemptId: dispatchIdentifier(notification.attemptId, `${label}.attemptId`) }),
|
|
1125
|
+
status,
|
|
1126
|
+
message: retainedDescription(notification.message, `${label}.message`),
|
|
1127
|
+
createdAt,
|
|
1128
|
+
...(deliveredAt ? { deliveredAt } : {}),
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
function taskDependencyReaches(tasks, startTaskId, targetTaskId, visited = new Set()) {
|
|
1132
|
+
if (startTaskId === targetTaskId)
|
|
1133
|
+
return true;
|
|
1134
|
+
if (visited.has(startTaskId))
|
|
1135
|
+
return false;
|
|
1136
|
+
visited.add(startTaskId);
|
|
1137
|
+
return (tasks[startTaskId]?.task.dependsOn ?? []).some((dependencyId) => taskDependencyReaches(tasks, dependencyId, targetTaskId, visited));
|
|
1138
|
+
}
|
|
1139
|
+
export function validateTaskContractForState(state, task, label) {
|
|
1140
|
+
const objective = state.objectives[task.objectiveId]?.objective;
|
|
1141
|
+
if (!objective)
|
|
1142
|
+
throw new DurableTaskRuntimeError(`${label} references an unknown objective.`);
|
|
1143
|
+
if (task.status !== (task.dependsOn.length === 0 ? "ready" : "pending")) {
|
|
1144
|
+
throw new DurableTaskRuntimeError(`${label} initial status does not match its dependencies.`);
|
|
1145
|
+
}
|
|
1146
|
+
for (const dependencyId of task.dependsOn) {
|
|
1147
|
+
if (dependencyId === task.taskId) {
|
|
1148
|
+
throw new DurableTaskRuntimeError(`${label} cannot depend on itself.`);
|
|
1149
|
+
}
|
|
1150
|
+
const dependency = state.tasks[dependencyId];
|
|
1151
|
+
if (!dependency || dependency.task.objectiveId !== task.objectiveId) {
|
|
1152
|
+
throw new DurableTaskRuntimeError(`${label} dependency '${dependencyId}' is not in its objective.`);
|
|
1153
|
+
}
|
|
1154
|
+
if (taskDependencyReaches(state.tasks, dependencyId, task.taskId)) {
|
|
1155
|
+
throw new DurableTaskRuntimeError(`${label} introduces a dependency cycle.`);
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
const criterionIds = new Set(objective.acceptanceCriteria.map((criterion) => criterion.id));
|
|
1159
|
+
const unknownCriteria = task.acceptanceCriterionIds.filter((criterionId) => !criterionIds.has(criterionId));
|
|
1160
|
+
if (unknownCriteria.length > 0) {
|
|
1161
|
+
throw new DurableTaskRuntimeError(`${label} references unknown acceptance criteria: ${unknownCriteria.join(", ")}.`);
|
|
1162
|
+
}
|
|
1163
|
+
if (task.verificationOfTaskId) {
|
|
1164
|
+
const subject = state.tasks[task.verificationOfTaskId];
|
|
1165
|
+
if (!subject || subject.task.objectiveId !== task.objectiveId) {
|
|
1166
|
+
throw new DurableTaskRuntimeError(`${label} verification subject is not in its objective.`);
|
|
1167
|
+
}
|
|
1168
|
+
if (task.role !== "verifier" || subject.task.role === "verifier") {
|
|
1169
|
+
throw new DurableTaskRuntimeError(`${label} has an invalid verification relationship.`);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
function validateSnapshotTaskGraph(state) {
|
|
1174
|
+
const label = "Orchestration projection snapshot";
|
|
1175
|
+
const taskObjectiveOwners = new Map();
|
|
1176
|
+
for (const [objectiveId, objectiveState] of Object.entries(state.objectives)) {
|
|
1177
|
+
if (objectiveState.objective.objectiveId !== objectiveId) {
|
|
1178
|
+
throw new DurableTaskRuntimeError(`${label} objective '${objectiveId}' has a mismatched id.`);
|
|
1179
|
+
}
|
|
1180
|
+
if (Date.parse(objectiveState.objective.updatedAt) < Date.parse(objectiveState.objective.createdAt)) {
|
|
1181
|
+
throw new DurableTaskRuntimeError(`${label} objective '${objectiveId}' has invalid lifecycle dates.`);
|
|
1182
|
+
}
|
|
1183
|
+
for (const taskId of objectiveState.taskIds) {
|
|
1184
|
+
const existingOwner = taskObjectiveOwners.get(taskId);
|
|
1185
|
+
if (existingOwner) {
|
|
1186
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' is listed by multiple objectives.`);
|
|
1187
|
+
}
|
|
1188
|
+
const task = state.tasks[taskId];
|
|
1189
|
+
if (!task || task.task.objectiveId !== objectiveId) {
|
|
1190
|
+
throw new DurableTaskRuntimeError(`${label} objective '${objectiveId}' lists an unknown or cross-objective task '${taskId}'.`);
|
|
1191
|
+
}
|
|
1192
|
+
taskObjectiveOwners.set(taskId, objectiveId);
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
for (const [taskId, taskState] of Object.entries(state.tasks)) {
|
|
1196
|
+
if (taskState.task.taskId !== taskId) {
|
|
1197
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' has a mismatched id.`);
|
|
1198
|
+
}
|
|
1199
|
+
const objectiveState = state.objectives[taskState.task.objectiveId];
|
|
1200
|
+
if (!objectiveState) {
|
|
1201
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' has no objective.`);
|
|
1202
|
+
}
|
|
1203
|
+
if (taskObjectiveOwners.get(taskId) !== taskState.task.objectiveId) {
|
|
1204
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' is missing from its objective task list.`);
|
|
1205
|
+
}
|
|
1206
|
+
if (Date.parse(taskState.task.updatedAt) < Date.parse(taskState.task.createdAt)) {
|
|
1207
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' has invalid lifecycle dates.`);
|
|
1208
|
+
}
|
|
1209
|
+
const criterionIds = new Set(objectiveState.objective.acceptanceCriteria.map((criterion) => criterion.id));
|
|
1210
|
+
const unknownCriteria = taskState.task.acceptanceCriterionIds.filter((criterionId) => !criterionIds.has(criterionId));
|
|
1211
|
+
if (unknownCriteria.length > 0) {
|
|
1212
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' references unknown acceptance criteria: ${unknownCriteria.join(", ")}.`);
|
|
1213
|
+
}
|
|
1214
|
+
for (const dependencyId of taskState.task.dependsOn) {
|
|
1215
|
+
if (dependencyId === taskId) {
|
|
1216
|
+
throw new DurableTaskRuntimeError(`${label} task graph contains a self-cycle.`);
|
|
1217
|
+
}
|
|
1218
|
+
const dependency = state.tasks[dependencyId];
|
|
1219
|
+
if (!dependency || dependency.task.objectiveId !== taskState.task.objectiveId) {
|
|
1220
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' dependency is not in its objective.`);
|
|
1221
|
+
}
|
|
1222
|
+
if (taskDependencyReaches(state.tasks, dependencyId, taskId)) {
|
|
1223
|
+
throw new DurableTaskRuntimeError(`${label} task graph contains a cycle.`);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
if (taskState.task.verificationOfTaskId) {
|
|
1227
|
+
const subject = state.tasks[taskState.task.verificationOfTaskId];
|
|
1228
|
+
if (!subject ||
|
|
1229
|
+
subject.task.objectiveId !== taskState.task.objectiveId ||
|
|
1230
|
+
taskState.task.role !== "verifier" ||
|
|
1231
|
+
subject.task.role === "verifier") {
|
|
1232
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' has an invalid verification relationship.`);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
const attemptTaskOwners = new Map();
|
|
1237
|
+
for (const [taskId, taskState] of Object.entries(state.tasks)) {
|
|
1238
|
+
for (const attemptId of taskState.attemptIds) {
|
|
1239
|
+
const existingOwner = attemptTaskOwners.get(attemptId);
|
|
1240
|
+
if (existingOwner) {
|
|
1241
|
+
throw new DurableTaskRuntimeError(`Orchestration projection snapshot attempt '${attemptId}' is listed by multiple tasks.`);
|
|
1242
|
+
}
|
|
1243
|
+
const attempt = state.attempts[attemptId];
|
|
1244
|
+
if (!attempt || attempt.taskId !== taskId || attempt.dispatch.taskId !== taskId) {
|
|
1245
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' lists an unknown or cross-task attempt '${attemptId}'.`);
|
|
1246
|
+
}
|
|
1247
|
+
attemptTaskOwners.set(attemptId, taskId);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
for (const [attemptId, attempt] of Object.entries(state.attempts)) {
|
|
1251
|
+
if (attempt.attemptId !== attemptId) {
|
|
1252
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' has a mismatched id.`);
|
|
1253
|
+
}
|
|
1254
|
+
if (attempt.dispatch.taskId !== attempt.taskId) {
|
|
1255
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' dispatch references a different task.`);
|
|
1256
|
+
}
|
|
1257
|
+
const task = state.tasks[attempt.taskId];
|
|
1258
|
+
if (!task || attemptTaskOwners.get(attemptId) !== attempt.taskId) {
|
|
1259
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' is missing from its owning task attempt list.`);
|
|
1260
|
+
}
|
|
1261
|
+
if (attempt.dispatch.executionContract?.worker.profile.role !== undefined) {
|
|
1262
|
+
const contract = attempt.dispatch.executionContract;
|
|
1263
|
+
if (contract.worker.profile.profileId !== attempt.dispatch.profileId ||
|
|
1264
|
+
contract.worker.profile.role !== task.task.role) {
|
|
1265
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' execution contract is inconsistent.`);
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
if (attempt.lease?.attemptId !== undefined && attempt.lease.attemptId !== attemptId) {
|
|
1269
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' has a cross-attempt lease.`);
|
|
1270
|
+
}
|
|
1271
|
+
if (attempt.grant) {
|
|
1272
|
+
if (attempt.grantId !== attempt.grant.grantId ||
|
|
1273
|
+
attempt.grant.attemptId !== attemptId ||
|
|
1274
|
+
attempt.grant.taskId !== attempt.taskId ||
|
|
1275
|
+
attempt.grant.objectiveId !== task.task.objectiveId ||
|
|
1276
|
+
attempt.grant.role !== task.task.role) {
|
|
1277
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' has an inconsistent grant.`);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
if (["leased", "running", "suspended"].includes(attempt.status) && (!attempt.grantId || !attempt.lease)) {
|
|
1281
|
+
throw new DurableTaskRuntimeError(`${label} live attempt '${attemptId}' lacks a grant or lease.`);
|
|
1282
|
+
}
|
|
1283
|
+
if (attempt.status === "queued" &&
|
|
1284
|
+
(attempt.lease || attempt.agentId || attempt.result || attempt.checkpointIds.length)) {
|
|
1285
|
+
throw new DurableTaskRuntimeError(`${label} queued attempt '${attemptId}' contains live execution state.`);
|
|
1286
|
+
}
|
|
1287
|
+
if (attempt.retry &&
|
|
1288
|
+
(!["suspended", "leased", "running"].includes(attempt.status) || !attempt.agentId || !attempt.lease)) {
|
|
1289
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' retains retry state outside the resumable agent lifecycle.`);
|
|
1290
|
+
}
|
|
1291
|
+
if (attempt.agentId && !state.agents[attempt.agentId]) {
|
|
1292
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' references an unknown agent.`);
|
|
1293
|
+
}
|
|
1294
|
+
if (attempt.result) {
|
|
1295
|
+
const result = attempt.result;
|
|
1296
|
+
if (attempt.status !== result.status ||
|
|
1297
|
+
result.attemptId !== attemptId ||
|
|
1298
|
+
result.taskId !== attempt.taskId ||
|
|
1299
|
+
result.objectiveId !== task.task.objectiveId ||
|
|
1300
|
+
!attempt.lease ||
|
|
1301
|
+
result.leaseId !== attempt.lease.leaseId ||
|
|
1302
|
+
result.fencingToken !== attempt.lease.fencingToken) {
|
|
1303
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' has an inconsistent result.`);
|
|
1304
|
+
}
|
|
1305
|
+
if (result.status === "completed" &&
|
|
1306
|
+
missingTrustedCriteria(result, task.task.acceptanceCriterionIds).length > 0) {
|
|
1307
|
+
throw new DurableTaskRuntimeError(`${label} completed attempt '${attemptId}' lacks trusted evidence.`);
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
else if (["completed", "partial", "failed"].includes(attempt.status)) {
|
|
1311
|
+
throw new DurableTaskRuntimeError(`${label} terminal attempt '${attemptId}' lacks its result.`);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
for (const [taskId, task] of Object.entries(state.tasks)) {
|
|
1315
|
+
const liveAttempts = task.attemptIds
|
|
1316
|
+
.map((attemptId) => state.attempts[attemptId])
|
|
1317
|
+
.filter((attempt) => !terminalAttemptStatus(attempt.status));
|
|
1318
|
+
if (liveAttempts.length > 1) {
|
|
1319
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' owns multiple live attempts.`);
|
|
1320
|
+
}
|
|
1321
|
+
const liveAttempt = liveAttempts[0];
|
|
1322
|
+
if (liveAttempt && ["leased", "running", "suspended"].includes(liveAttempt.status)) {
|
|
1323
|
+
if (task.task.status !== "running") {
|
|
1324
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' does not reflect its live attempt.`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1327
|
+
else if (liveAttempt?.status === "queued" && !["pending", "ready"].includes(task.task.status)) {
|
|
1328
|
+
throw new DurableTaskRuntimeError(`${label} queued task '${taskId}' has an invalid status.`);
|
|
1329
|
+
}
|
|
1330
|
+
else if (!liveAttempt && task.task.status === "running") {
|
|
1331
|
+
throw new DurableTaskRuntimeError(`${label} running task '${taskId}' has no live attempt.`);
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
const checkpointAttemptOwners = new Map();
|
|
1335
|
+
for (const [attemptId, attempt] of Object.entries(state.attempts)) {
|
|
1336
|
+
for (const checkpointId of attempt.checkpointIds) {
|
|
1337
|
+
if (checkpointAttemptOwners.has(checkpointId)) {
|
|
1338
|
+
throw new DurableTaskRuntimeError(`${label} checkpoint '${checkpointId}' is listed by multiple attempts.`);
|
|
1339
|
+
}
|
|
1340
|
+
const checkpoint = state.checkpoints[checkpointId];
|
|
1341
|
+
if (!checkpoint || checkpoint.attemptId !== attemptId) {
|
|
1342
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attemptId}' lists a cross-attempt checkpoint.`);
|
|
1343
|
+
}
|
|
1344
|
+
checkpointAttemptOwners.set(checkpointId, attemptId);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
for (const [checkpointId, checkpoint] of Object.entries(state.checkpoints)) {
|
|
1348
|
+
const attempt = state.attempts[checkpoint.attemptId];
|
|
1349
|
+
if (checkpoint.checkpointId !== checkpointId ||
|
|
1350
|
+
!attempt ||
|
|
1351
|
+
checkpointAttemptOwners.get(checkpointId) !== checkpoint.attemptId ||
|
|
1352
|
+
!attempt.lease ||
|
|
1353
|
+
checkpoint.fencingToken > attempt.lease.fencingToken ||
|
|
1354
|
+
Date.parse(checkpoint.createdAt) < Date.parse(attempt.createdAt)) {
|
|
1355
|
+
throw new DurableTaskRuntimeError(`${label} checkpoint '${checkpointId}' has invalid ownership.`);
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
for (const [agentId, agent] of Object.entries(state.agents)) {
|
|
1359
|
+
if (agent.agentId !== agentId)
|
|
1360
|
+
throw new DurableTaskRuntimeError(`${label} agent '${agentId}' has a mismatched id.`);
|
|
1361
|
+
if (agent.parentAgentId) {
|
|
1362
|
+
const parent = state.agents[agent.parentAgentId];
|
|
1363
|
+
if (!parent || parent.rootAgentId !== agent.rootAgentId || agent.depth !== parent.depth + 1) {
|
|
1364
|
+
throw new DurableTaskRuntimeError(`${label} agent '${agentId}' has invalid lineage.`);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
else if (agent.rootAgentId !== agentId || agent.depth !== 0) {
|
|
1368
|
+
throw new DurableTaskRuntimeError(`${label} root agent '${agentId}' has invalid lineage.`);
|
|
1369
|
+
}
|
|
1370
|
+
const lineage = new Set([agentId]);
|
|
1371
|
+
let ancestorId = agent.parentAgentId;
|
|
1372
|
+
while (ancestorId) {
|
|
1373
|
+
if (lineage.has(ancestorId))
|
|
1374
|
+
throw new DurableTaskRuntimeError(`${label} agent lineage contains a cycle.`);
|
|
1375
|
+
lineage.add(ancestorId);
|
|
1376
|
+
ancestorId = state.agents[ancestorId]?.parentAgentId;
|
|
1377
|
+
}
|
|
1378
|
+
const activeAttempt = agent.activeAttemptId ? state.attempts[agent.activeAttemptId] : undefined;
|
|
1379
|
+
if (agent.status === "registered" || agent.status === "retired") {
|
|
1380
|
+
if (agent.activeAttemptId)
|
|
1381
|
+
throw new DurableTaskRuntimeError(`${label} idle agent '${agentId}' owns an attempt.`);
|
|
1382
|
+
}
|
|
1383
|
+
else if (!activeAttempt || activeAttempt.agentId !== agentId) {
|
|
1384
|
+
throw new DurableTaskRuntimeError(`${label} active agent '${agentId}' lacks its attempt.`);
|
|
1385
|
+
}
|
|
1386
|
+
else if ((agent.status === "active" && !["leased", "running"].includes(activeAttempt.status)) ||
|
|
1387
|
+
((agent.status === "suspended" || agent.status === "resuming") && activeAttempt.status !== "suspended")) {
|
|
1388
|
+
throw new DurableTaskRuntimeError(`${label} agent '${agentId}' status conflicts with its attempt.`);
|
|
1389
|
+
}
|
|
1390
|
+
if (agent.resumeContext.latestCheckpointId) {
|
|
1391
|
+
const checkpoint = state.checkpoints[agent.resumeContext.latestCheckpointId];
|
|
1392
|
+
const attempt = checkpoint ? state.attempts[checkpoint.attemptId] : undefined;
|
|
1393
|
+
if (!checkpoint || attempt?.agentId !== agentId) {
|
|
1394
|
+
throw new DurableTaskRuntimeError(`${label} agent '${agentId}' has an unrelated latest checkpoint.`);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
for (const attempt of Object.values(state.attempts)) {
|
|
1399
|
+
if (!attempt.agentId || terminalAttemptStatus(attempt.status))
|
|
1400
|
+
continue;
|
|
1401
|
+
const agent = state.agents[attempt.agentId];
|
|
1402
|
+
if (!agent ||
|
|
1403
|
+
agent.activeAttemptId !== attempt.attemptId ||
|
|
1404
|
+
(["leased", "running"].includes(attempt.status) && agent.status !== "active") ||
|
|
1405
|
+
(attempt.status === "suspended" && agent.status !== "suspended" && agent.status !== "resuming")) {
|
|
1406
|
+
throw new DurableTaskRuntimeError(`${label} attempt '${attempt.attemptId}' has an inconsistent agent binding.`);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1409
|
+
for (const [approvalId, approval] of Object.entries(state.approvals)) {
|
|
1410
|
+
const request = approval.request;
|
|
1411
|
+
if (request.approvalId !== approvalId || !state.objectives[request.objectiveId]) {
|
|
1412
|
+
throw new DurableTaskRuntimeError(`${label} approval '${approvalId}' has invalid ownership.`);
|
|
1413
|
+
}
|
|
1414
|
+
const task = request.taskId ? state.tasks[request.taskId] : undefined;
|
|
1415
|
+
if ((request.taskId && task?.task.objectiveId !== request.objectiveId) ||
|
|
1416
|
+
(request.attemptId && !request.taskId)) {
|
|
1417
|
+
throw new DurableTaskRuntimeError(`${label} approval '${approvalId}' has a cross-objective task.`);
|
|
1418
|
+
}
|
|
1419
|
+
const attempt = request.attemptId ? state.attempts[request.attemptId] : undefined;
|
|
1420
|
+
if (request.attemptId && attempt?.taskId !== request.taskId) {
|
|
1421
|
+
throw new DurableTaskRuntimeError(`${label} approval '${approvalId}' has a cross-task attempt.`);
|
|
1422
|
+
}
|
|
1423
|
+
if (approval.status === "pending" && attempt && (attempt.status !== "queued" || attempt.grantId)) {
|
|
1424
|
+
throw new DurableTaskRuntimeError(`${label} pending approval '${approvalId}' no longer awaits policy.`);
|
|
1425
|
+
}
|
|
1426
|
+
const notification = state.notifications[`approval-requested:${approvalId}`];
|
|
1427
|
+
if (!notification ||
|
|
1428
|
+
notification.objectiveId !== request.objectiveId ||
|
|
1429
|
+
notification.attemptId !== request.attemptId ||
|
|
1430
|
+
notification.message !== request.summary) {
|
|
1431
|
+
throw new DurableTaskRuntimeError(`${label} approval '${approvalId}' lacks its notification.`);
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
for (const [notificationId, notification] of Object.entries(state.notifications)) {
|
|
1435
|
+
if (notification.notificationId !== notificationId || !state.objectives[notification.objectiveId]) {
|
|
1436
|
+
throw new DurableTaskRuntimeError(`${label} notification '${notificationId}' has invalid ownership.`);
|
|
1437
|
+
}
|
|
1438
|
+
if (notification.attemptId) {
|
|
1439
|
+
const attempt = state.attempts[notification.attemptId];
|
|
1440
|
+
const task = attempt ? state.tasks[attempt.taskId] : undefined;
|
|
1441
|
+
if (!attempt || task?.task.objectiveId !== notification.objectiveId) {
|
|
1442
|
+
throw new DurableTaskRuntimeError(`${label} notification '${notificationId}' has a cross-objective attempt.`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
for (const [taskId, subject] of Object.entries(state.tasks)) {
|
|
1447
|
+
const verification = subject.verification;
|
|
1448
|
+
if (!verification)
|
|
1449
|
+
continue;
|
|
1450
|
+
const verifierTask = state.tasks[verification.verifierTaskId];
|
|
1451
|
+
const verifierAttempt = state.attempts[verification.verifierAttemptId];
|
|
1452
|
+
if (!verifierTask ||
|
|
1453
|
+
verifierTask.task.verificationOfTaskId !== taskId ||
|
|
1454
|
+
!verifierAttempt ||
|
|
1455
|
+
verifierAttempt.taskId !== verification.verifierTaskId ||
|
|
1456
|
+
!terminalAttemptStatus(verifierAttempt.status)) {
|
|
1457
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' has an invalid verification record.`);
|
|
1458
|
+
}
|
|
1459
|
+
if (verification.verdict === "accepted" && subject.task.status !== "completed") {
|
|
1460
|
+
throw new DurableTaskRuntimeError(`${label} accepted verification for '${taskId}' is not completed.`);
|
|
1461
|
+
}
|
|
1462
|
+
if (verification.verdict !== "inconclusive" &&
|
|
1463
|
+
!verifierAttempt.result?.evidence.some((evidence) => evidence.trusted &&
|
|
1464
|
+
evidence.kind === "review" &&
|
|
1465
|
+
evidence.metadata?.subjectTaskId === taskId &&
|
|
1466
|
+
evidence.metadata.verdict === verification.verdict)) {
|
|
1467
|
+
throw new DurableTaskRuntimeError(`${label} task '${taskId}' verification lacks matching evidence.`);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
//# sourceMappingURL=task-runtime-codecs.js.map
|