intentdna 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +227 -87
- package/dist/cli/commands/run-lifecycle.d.ts +61 -11
- package/dist/cli/commands/run-lifecycle.js +184 -50
- package/dist/cli/commands/run-observer.d.ts +15 -0
- package/dist/cli/commands/run-observer.js +210 -0
- package/dist/cli/commands/run.d.ts +16 -20
- package/dist/cli/commands/run.js +461 -659
- package/dist/cli/commands/sync.js +16 -12
- package/dist/cli/index.js +14 -5
- package/dist/compiler/compile.d.ts +1 -0
- package/dist/compiler/compile.js +1 -1
- package/dist/compiler/controller.d.ts +16 -0
- package/dist/compiler/controller.js +693 -0
- package/dist/compiler/input-resolver.d.ts +2 -0
- package/dist/compiler/input-resolver.js +22 -6
- package/dist/hooks/cli.d.ts +45 -0
- package/dist/hooks/cli.js +263 -31
- package/dist/hooks/host-event.d.ts +57 -0
- package/dist/hooks/host-event.js +187 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/protocol.d.ts +7 -0
- package/dist/hooks/schema.d.ts +1 -0
- package/dist/hooks/schema.js +89 -1
- package/dist/hooks/state.d.ts +23 -1
- package/dist/hooks/state.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-run.d.ts +4 -0
- package/dist/mcp/tools-run.js +419 -0
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +13 -241
- package/dist/runtime/artifact-store.d.ts +146 -0
- package/dist/runtime/artifact-store.js +1436 -0
- package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
- package/dist/runtime/canonical-attempt-outcome.js +195 -0
- package/dist/runtime/canonical-json.d.ts +10 -0
- package/dist/runtime/canonical-json.js +105 -0
- package/dist/runtime/canonical-run-application.d.ts +93 -0
- package/dist/runtime/canonical-run-application.js +229 -0
- package/dist/runtime/canonical-run-service.d.ts +454 -0
- package/dist/runtime/canonical-run-service.js +4760 -0
- package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
- package/dist/runtime/canonical-runtime-composition.js +34 -0
- package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
- package/dist/runtime/canonical-runtime-projection.js +4 -0
- package/dist/runtime/canonical-target-compiler.d.ts +46 -0
- package/dist/runtime/canonical-target-compiler.js +514 -0
- package/dist/runtime/claude-agent-identity.d.ts +2 -0
- package/dist/runtime/claude-agent-identity.js +21 -0
- package/dist/runtime/executable-run-plan.d.ts +328 -0
- package/dist/runtime/executable-run-plan.js +1485 -0
- package/dist/runtime/execution-authority.d.ts +83 -0
- package/dist/runtime/execution-authority.js +98 -0
- package/dist/runtime/handoff-resolver.js +4 -1
- package/dist/runtime/harness-pull-adapter.d.ts +146 -0
- package/dist/runtime/harness-pull-adapter.js +315 -0
- package/dist/runtime/index.d.ts +39 -14
- package/dist/runtime/index.js +29 -7
- package/dist/runtime/local-execution-authority.d.ts +194 -0
- package/dist/runtime/local-execution-authority.js +2877 -0
- package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
- package/dist/runtime/local-execution-reconciliation.js +107 -0
- package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
- package/dist/runtime/local-execution-supervisor-script.js +793 -0
- package/dist/runtime/local-provider-sandbox.d.ts +42 -0
- package/dist/runtime/local-provider-sandbox.js +154 -0
- package/dist/runtime/local-verifier-execution.d.ts +24 -0
- package/dist/runtime/local-verifier-execution.js +290 -0
- package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
- package/dist/runtime/local-windows-execution-supervisor-script.js +771 -0
- package/dist/runtime/plan-store.d.ts +30 -0
- package/dist/runtime/plan-store.js +231 -0
- package/dist/runtime/process-tree.d.ts +9 -0
- package/dist/runtime/process-tree.js +97 -24
- package/dist/runtime/push-driver.d.ts +184 -0
- package/dist/runtime/push-driver.js +1002 -0
- package/dist/runtime/result-store.d.ts +34 -3
- package/dist/runtime/result-store.js +1073 -2
- package/dist/runtime/run-binding.d.ts +68 -0
- package/dist/runtime/run-binding.js +278 -0
- package/dist/runtime/run-contracts.d.ts +575 -0
- package/dist/runtime/run-contracts.js +58 -7
- package/dist/runtime/run-controller.d.ts +1 -6
- package/dist/runtime/run-controller.js +0 -41
- package/dist/runtime/run-store.d.ts +85 -7
- package/dist/runtime/run-store.js +2528 -186
- package/dist/runtime/skill-adapter.d.ts +18 -7
- package/dist/runtime/skill-adapter.js +100 -742
- package/dist/runtime/structured-output-validator.d.ts +44 -0
- package/dist/runtime/structured-output-validator.js +171 -0
- package/dist/runtime/verifier-command-binding.d.ts +22 -0
- package/dist/runtime/verifier-command-binding.js +162 -0
- package/dist/runtime/verifier.d.ts +46 -6
- package/dist/runtime/verifier.js +355 -53
- package/dist/runtime/windows-job-keeper.d.ts +47 -0
- package/dist/runtime/windows-job-keeper.js +231 -0
- package/dist/runtime/worker-executor.d.ts +17 -1
- package/dist/runtime/worker-executor.js +26 -5
- package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
- package/dist/runtime/workflow-plan-adapter.js +502 -6
- package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
- package/dist/runtime/workflow-runtime-manifest.js +5 -0
- package/dist/runtime/workspace-isolation.d.ts +29 -2
- package/dist/runtime/workspace-isolation.js +488 -11
- package/dist/runtime/workspace-observation.d.ts +44 -0
- package/dist/runtime/workspace-observation.js +216 -0
- package/dist/schema/controller-registry.d.ts +0 -7
- package/dist/schema/controller-registry.js +0 -8
- package/dist/schema/types.d.ts +84 -10
- package/dist/schema/types.js +2 -0
- package/dist/schema/validate.js +151 -1
- package/dist/schema/validators/controllers.d.ts +1 -1
- package/dist/schema/validators/controllers.js +126 -41
- package/dist/schema/workflow-authoring-contract.js +2 -0
- package/dist/schema/yaml-parser.js +1 -1
- package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
- package/dist/templates/flutter-rewrite.dna.yaml +87 -60
- package/dist/templates/safe-refactoring.dna.yaml +1 -0
- package/native/windows-job-keeper/README.md +80 -0
- package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/keeper.c +979 -0
- package/native/windows-job-keeper/manifest.json +33 -0
- package/package.json +5 -1
- package/scripts/build-windows-job-keeper.mjs +172 -0
- package/scripts/verify-windows-job-keeper.mjs +184 -0
package/dist/cli/commands/run.js
CHANGED
|
@@ -1,68 +1,90 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { compileWorkflow } from "../../compiler/workflow.js";
|
|
13
|
-
import { DNAStateManager } from "../../hooks/state-manager.js";
|
|
14
|
-
import { appendEvidenceCaptureEvent, EVIDENCE_CAPTURE_SCHEMA_VERSION, } from "../../hooks/state.js";
|
|
15
|
-
import { compileAllRolesToAgentMD, toKebabCase, writeAgentMDFiles, } from "../../runtime/agent-md.js";
|
|
16
|
-
import { HandoffResolver } from "../../runtime/handoff-resolver.js";
|
|
17
|
-
import { createCompiledIR, writeCompiledIR } from "../../runtime/plugin-adapter.js";
|
|
18
|
-
import { createWorkflowRuntimeManifest, WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES, } from "../../runtime/workflow-runtime-manifest.js";
|
|
19
|
-
import { defaultResolvedVariableScopes, indexWorkflowVariableScopes, resolvedVariablesForNamespace, } from "../../runtime/workflow-variable-scopes.js";
|
|
20
|
-
import { ImmutableResultStore } from "../../runtime/result-store.js";
|
|
21
|
-
import { RunController, } from "../../runtime/run-controller.js";
|
|
22
|
-
import { DurableRunStore, RunStoreError, } from "../../runtime/run-store.js";
|
|
1
|
+
/** Intent DNA - canonical dna run CLI transport. */
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
|
+
import { join, resolve } from "node:path";
|
|
4
|
+
import { toKebabCase } from "../../runtime/agent-md.js";
|
|
5
|
+
import { CanonicalRunApplication, } from "../../runtime/canonical-run-application.js";
|
|
6
|
+
import { canonicalRuntimeRoot as runtimeRoot, createCanonicalRuntimeComposition, } from "../../runtime/canonical-runtime-composition.js";
|
|
7
|
+
import { LocalAttemptExecutionAuthority, } from "../../runtime/local-execution-authority.js";
|
|
8
|
+
import { reconcileLocalExecutionPreparations } from "../../runtime/local-execution-reconciliation.js";
|
|
9
|
+
import { LocalProviderNetworkSandbox } from "../../runtime/local-provider-sandbox.js";
|
|
10
|
+
import { ProductionLocalVerifierExecutionPort } from "../../runtime/local-verifier-execution.js";
|
|
11
|
+
import { canonicalizeJson } from "../../runtime/canonical-json.js";
|
|
23
12
|
import { createClaudeExecutionProvider } from "../../runtime/providers/claude.js";
|
|
24
13
|
import { createCodexExecutionProvider } from "../../runtime/providers/codex.js";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
14
|
+
import { PushDriver, } from "../../runtime/push-driver.js";
|
|
15
|
+
import { CALLER_ATTESTATION_SCHEMA_VERSION, } from "../../runtime/run-contracts.js";
|
|
16
|
+
import { validateWorkflowRuntimeManifest, WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES, } from "../../runtime/workflow-runtime-manifest.js";
|
|
17
|
+
import { DurableRunStore } from "../../runtime/run-store.js";
|
|
18
|
+
import { LocalAttemptWorkspaceAuthority } from "../../runtime/workspace-isolation.js";
|
|
19
|
+
import { createRunAttemptObservers, durableRuntimeProjection, syncRuntimeProjection, writeTerminalDriverFailure, } from "./run-observer.js";
|
|
20
|
+
import { runLifecycleCancel, runLifecycleClose, runLifecycleInspect, runLifecycleResume, runLifecycleStatus, writeCanonicalOperation, } from "./run-lifecycle.js";
|
|
21
|
+
function canonicalInputs(metadata) {
|
|
22
|
+
const { workflow: _workflow, ...inputs } = metadata.variables;
|
|
23
|
+
return inputs;
|
|
24
|
+
}
|
|
25
|
+
function metadataForBinding(metadata, binding) {
|
|
26
|
+
const variables = {
|
|
27
|
+
workflow: metadata.target.key,
|
|
28
|
+
};
|
|
29
|
+
for (const [name, value] of Object.entries(binding.binding.validated_inputs)) {
|
|
30
|
+
if (typeof value === "string")
|
|
31
|
+
variables[name] = value;
|
|
32
|
+
}
|
|
33
|
+
return { ...metadata, variables };
|
|
34
|
+
}
|
|
35
|
+
function callerAttestationDigest(payload) {
|
|
36
|
+
return `sha256:${createHash("sha256")
|
|
37
|
+
.update(canonicalizeJson(payload), "utf8")
|
|
38
|
+
.digest("hex")}`;
|
|
39
|
+
}
|
|
40
|
+
function createCallerAttestationAuthority(authorityId) {
|
|
41
|
+
if (authorityId.length === 0) {
|
|
42
|
+
throw new Error("caller attestation authority id must not be empty");
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
authority_id: authorityId,
|
|
46
|
+
attest: (request) => {
|
|
47
|
+
const payload = {
|
|
48
|
+
schema_version: CALLER_ATTESTATION_SCHEMA_VERSION,
|
|
49
|
+
authority_id: authorityId,
|
|
50
|
+
agent_id: request.worker_session_id,
|
|
51
|
+
session_id: request.provider_session_id ?? request.worker_session_id,
|
|
52
|
+
run_id: request.run_id,
|
|
53
|
+
step_id: request.step_id,
|
|
54
|
+
activation_id: request.activation_id,
|
|
55
|
+
attempt_id: request.attempt_id,
|
|
56
|
+
claim_epoch: request.claim_epoch,
|
|
57
|
+
worker_session_id: request.worker_session_id,
|
|
58
|
+
submission_key_digest: request.submission_key_digest,
|
|
59
|
+
execution_digest: request.execution_digest,
|
|
60
|
+
};
|
|
61
|
+
return {
|
|
62
|
+
kind: "caller_attestation",
|
|
63
|
+
attestation_ref: [
|
|
64
|
+
"attestation:/local-cli",
|
|
65
|
+
encodeURIComponent(authorityId),
|
|
66
|
+
encodeURIComponent(request.run_id),
|
|
67
|
+
encodeURIComponent(request.attempt_id),
|
|
68
|
+
].join("/"),
|
|
69
|
+
attestation_digest: callerAttestationDigest(payload),
|
|
70
|
+
payload,
|
|
71
|
+
};
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
}
|
|
30
75
|
class RunUsageError extends Error {
|
|
31
76
|
constructor(message) {
|
|
32
77
|
super(message);
|
|
33
78
|
this.name = "RunUsageError";
|
|
34
79
|
}
|
|
35
80
|
}
|
|
36
|
-
// ── Compatibility-only pure helpers ───────────────────────
|
|
37
81
|
export function substituteTemplate(template, vars) {
|
|
38
|
-
|
|
82
|
+
const argumentsValue = vars.ARGUMENTS ?? vars.arguments ?? vars.task_id;
|
|
83
|
+
const resolved = argumentsValue === undefined
|
|
84
|
+
? template
|
|
85
|
+
: template.replace(/(^|[^\\])\$ARGUMENTS/g, (_match, prefix) => `${prefix}${argumentsValue}`);
|
|
86
|
+
return resolved.replace(/\{\{(\w+)\}\}/g, (match, key) => key in vars ? vars[key] : match);
|
|
39
87
|
}
|
|
40
|
-
export function evaluateRunIf(condition, round) {
|
|
41
|
-
if (condition === null)
|
|
42
|
-
return true;
|
|
43
|
-
const eqMatch = condition.match(/^round\s*==\s*(\d+)$/);
|
|
44
|
-
if (eqMatch)
|
|
45
|
-
return round === parseInt(eqMatch[1], 10);
|
|
46
|
-
const gtMatch = condition.match(/^round\s*>\s*(\d+)$/);
|
|
47
|
-
if (gtMatch)
|
|
48
|
-
return round > parseInt(gtMatch[1], 10);
|
|
49
|
-
const geMatch = condition.match(/^round\s*>=\s*(\d+)$/);
|
|
50
|
-
if (geMatch)
|
|
51
|
-
return round >= parseInt(geMatch[1], 10);
|
|
52
|
-
if (condition === "retry")
|
|
53
|
-
return round > 1;
|
|
54
|
-
return true;
|
|
55
|
-
}
|
|
56
|
-
export function checkRetryNeeded(stepResults, transitions) {
|
|
57
|
-
for (const transition of transitions) {
|
|
58
|
-
if (transition.condition === "fail"
|
|
59
|
-
&& stepResults.get(transition.from)?.status === "fail") {
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
// ── Validation and durable metadata ───────────────────────
|
|
66
88
|
function requireText(value, label) {
|
|
67
89
|
const trimmed = value?.trim();
|
|
68
90
|
if (!trimmed)
|
|
@@ -70,21 +92,18 @@ function requireText(value, label) {
|
|
|
70
92
|
return trimmed;
|
|
71
93
|
}
|
|
72
94
|
function requirePositiveNumber(value, label) {
|
|
73
|
-
if (!Number.isFinite(value) || value <= 0)
|
|
95
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
74
96
|
throw new RunUsageError(`${label} must be a positive number`);
|
|
75
|
-
}
|
|
76
97
|
return value;
|
|
77
98
|
}
|
|
78
99
|
function requirePositiveInteger(value, label) {
|
|
79
|
-
if (!Number.isSafeInteger(value) || value <= 0)
|
|
100
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
80
101
|
throw new RunUsageError(`${label} must be a positive integer`);
|
|
81
|
-
}
|
|
82
102
|
return value;
|
|
83
103
|
}
|
|
84
104
|
function requireNonNegativeInteger(value, label) {
|
|
85
|
-
if (!Number.isSafeInteger(value) || value < 0)
|
|
105
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
86
106
|
throw new RunUsageError(`${label} must be a non-negative integer`);
|
|
87
|
-
}
|
|
88
107
|
return value;
|
|
89
108
|
}
|
|
90
109
|
function asRunId(value) {
|
|
@@ -93,679 +112,471 @@ function asRunId(value) {
|
|
|
93
112
|
function isRecord(value) {
|
|
94
113
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
95
114
|
}
|
|
96
|
-
function
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
result[key] = entry;
|
|
105
|
-
}
|
|
106
|
-
return result;
|
|
115
|
+
function hasExactKeys(value, keys) {
|
|
116
|
+
const expected = new Set(keys);
|
|
117
|
+
return Object.keys(value).length === expected.size
|
|
118
|
+
&& Object.keys(value).every((key) => expected.has(key));
|
|
119
|
+
}
|
|
120
|
+
function metadataError(detail) {
|
|
121
|
+
throw new Error(`run metadata is invalid: ${detail}`);
|
|
107
122
|
}
|
|
108
123
|
function readCanonicalMetadata(value) {
|
|
109
|
-
if (
|
|
110
|
-
|
|
124
|
+
if (isRecord(value)
|
|
125
|
+
&& value.schema_version === "intentdna.canonical_run.v2"
|
|
126
|
+
&& typeof value.workflow_name === "string") {
|
|
127
|
+
const { workflow_name: workflowName, ...legacy } = value;
|
|
128
|
+
return readCanonicalMetadata({
|
|
129
|
+
...legacy,
|
|
130
|
+
schema_version: "intentdna.canonical_run.v3",
|
|
131
|
+
target: { kind: "workflow", key: workflowName },
|
|
132
|
+
});
|
|
111
133
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
134
|
+
if (!isRecord(value)
|
|
135
|
+
|| value.schema_version !== "intentdna.canonical_run.v3"
|
|
136
|
+
|| !hasExactKeys(value, [
|
|
137
|
+
"schema_version",
|
|
138
|
+
"project_directory",
|
|
139
|
+
"dna_files",
|
|
140
|
+
"target",
|
|
141
|
+
"task_id",
|
|
142
|
+
"context",
|
|
143
|
+
"variables",
|
|
144
|
+
"agents_directory",
|
|
145
|
+
"provider",
|
|
146
|
+
"max_concurrency",
|
|
147
|
+
"timeout_ms",
|
|
148
|
+
"cancellation_grace_ms",
|
|
149
|
+
"workflow_asset",
|
|
150
|
+
"runtime_projection",
|
|
151
|
+
])
|
|
152
|
+
|| typeof value.project_directory !== "string"
|
|
153
|
+
|| value.project_directory.length === 0
|
|
154
|
+
|| !Array.isArray(value.dna_files)
|
|
155
|
+
|| !value.dna_files.every((file) => typeof file === "string" && file.length > 0)
|
|
156
|
+
|| !isRecord(value.target)
|
|
157
|
+
|| !hasExactKeys(value.target, ["kind", "key"])
|
|
158
|
+
|| (value.target.kind !== "workflow" && value.target.kind !== "controller")
|
|
159
|
+
|| typeof value.target.key !== "string"
|
|
160
|
+
|| value.target.key.length === 0
|
|
121
161
|
|| typeof value.task_id !== "string"
|
|
122
|
-
||
|
|
162
|
+
|| value.task_id.length === 0
|
|
163
|
+
|| (value.context !== null && typeof value.context !== "string")
|
|
164
|
+
|| !isRecord(value.variables)
|
|
165
|
+
|| !Object.values(value.variables).every((item) => typeof item === "string")
|
|
123
166
|
|| typeof value.agents_directory !== "string"
|
|
124
|
-
||
|
|
125
|
-
|| !
|
|
126
|
-
|| (
|
|
127
|
-
|
|
128
|
-
|
|
167
|
+
|| value.agents_directory.length === 0
|
|
168
|
+
|| !isRecord(value.provider)
|
|
169
|
+
|| !hasExactKeys(value.provider, [
|
|
170
|
+
"kind",
|
|
171
|
+
"executable",
|
|
172
|
+
"max_budget_usd",
|
|
173
|
+
"permission_mode",
|
|
174
|
+
])
|
|
175
|
+
|| (value.provider.kind !== "claude" && value.provider.kind !== "codex")
|
|
176
|
+
|| (value.provider.executable !== null && (typeof value.provider.executable !== "string"
|
|
177
|
+
|| value.provider.executable.length === 0))
|
|
178
|
+
|| typeof value.provider.max_budget_usd !== "number"
|
|
179
|
+
|| !Number.isFinite(value.provider.max_budget_usd)
|
|
180
|
+
|| value.provider.max_budget_usd <= 0
|
|
181
|
+
|| typeof value.provider.permission_mode !== "string"
|
|
182
|
+
|| value.provider.permission_mode.length === 0
|
|
129
183
|
|| typeof value.max_concurrency !== "number"
|
|
130
|
-
|| (
|
|
184
|
+
|| !Number.isSafeInteger(value.max_concurrency)
|
|
185
|
+
|| value.max_concurrency <= 0
|
|
186
|
+
|| (value.timeout_ms !== null && (typeof value.timeout_ms !== "number"
|
|
187
|
+
||
|
|
188
|
+
!Number.isSafeInteger(value.timeout_ms)
|
|
189
|
+
|| value.timeout_ms < 0))
|
|
131
190
|
|| typeof value.cancellation_grace_ms !== "number"
|
|
132
|
-
|| (
|
|
133
|
-
|
|
191
|
+
|| !Number.isSafeInteger(value.cancellation_grace_ms)
|
|
192
|
+
|| value.cancellation_grace_ms < 0
|
|
193
|
+
|| (value.workflow_asset !== null && typeof value.workflow_asset !== "string")
|
|
194
|
+
|| !isRecord(value.runtime_projection)
|
|
195
|
+
|| value.runtime_projection.ir_version !== 1
|
|
196
|
+
|| !isRecord(value.runtime_projection.ir)
|
|
197
|
+
|| !isRecord(value.runtime_projection.workflow_runtime)) {
|
|
198
|
+
metadataError("expected strict intentdna.canonical_run.v3 data");
|
|
199
|
+
}
|
|
200
|
+
const compiledHash = value.runtime_projection.compiled_ir_hash;
|
|
201
|
+
if (typeof compiledHash !== "string"
|
|
202
|
+
|| value.runtime_projection.ir.compiled_ir_hash !== compiledHash) {
|
|
203
|
+
metadataError("runtime projection digest is missing or inconsistent");
|
|
204
|
+
}
|
|
205
|
+
const manifest = validateWorkflowRuntimeManifest(value.runtime_projection.workflow_runtime, compiledHash);
|
|
206
|
+
if (!manifest.valid) {
|
|
207
|
+
metadataError(manifest.reason);
|
|
208
|
+
}
|
|
209
|
+
if (value.target.kind === "workflow" && !manifest.manifest.workflows[value.target.key]) {
|
|
210
|
+
metadataError(`runtime projection does not contain workflow ${value.target.key}`);
|
|
134
211
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
? null
|
|
148
|
-
: requireText(provider.executable, "run metadata provider.executable"),
|
|
149
|
-
max_budget_usd: requirePositiveNumber(provider.max_budget_usd, "run metadata provider.max_budget_usd"),
|
|
150
|
-
permission_mode: requireText(provider.permission_mode, "run metadata provider.permission_mode"),
|
|
212
|
+
return value;
|
|
213
|
+
}
|
|
214
|
+
function providerConfiguration(metadata) {
|
|
215
|
+
return {
|
|
216
|
+
provider: metadata.provider.kind,
|
|
217
|
+
model: null,
|
|
218
|
+
options: {
|
|
219
|
+
...(metadata.provider.executable === null
|
|
220
|
+
? {}
|
|
221
|
+
: { executable: metadata.provider.executable }),
|
|
222
|
+
max_budget_usd: metadata.provider.max_budget_usd,
|
|
223
|
+
permission_mode: metadata.provider.permission_mode,
|
|
151
224
|
},
|
|
152
|
-
|
|
153
|
-
timeout_ms: timeoutMs === null
|
|
154
|
-
? null
|
|
155
|
-
: requireNonNegativeInteger(timeoutMs, "run metadata timeout_ms"),
|
|
156
|
-
cancellation_grace_ms: requireNonNegativeInteger(value.cancellation_grace_ms, "run metadata cancellation_grace_ms"),
|
|
157
|
-
workflow_asset: workflowAsset,
|
|
225
|
+
credential_references: [],
|
|
158
226
|
};
|
|
159
|
-
requireText(metadata.project_directory, "run metadata project_directory");
|
|
160
|
-
requireText(metadata.workflow_name, "run metadata workflow_name");
|
|
161
|
-
requireText(metadata.task_id, "run metadata task_id");
|
|
162
|
-
requireText(metadata.agents_directory, "run metadata agents_directory");
|
|
163
|
-
if (metadata.dna_files.length === 0) {
|
|
164
|
-
throw new Error("run metadata dna_files must not be empty");
|
|
165
|
-
}
|
|
166
|
-
return metadata;
|
|
167
|
-
}
|
|
168
|
-
function stableJson(value) {
|
|
169
|
-
if (Array.isArray(value)) {
|
|
170
|
-
return `[${value.map(stableJson).join(",")}]`;
|
|
171
|
-
}
|
|
172
|
-
if (value !== null && typeof value === "object") {
|
|
173
|
-
const entries = Object.entries(value)
|
|
174
|
-
.filter(([, entry]) => entry !== undefined)
|
|
175
|
-
.sort(([left], [right]) => left.localeCompare(right));
|
|
176
|
-
return `{${entries.map(([key, entry]) => `${JSON.stringify(key)}:${stableJson(entry)}`).join(",")}}`;
|
|
177
|
-
}
|
|
178
|
-
return JSON.stringify(value) ?? "null";
|
|
179
|
-
}
|
|
180
|
-
function planDigest(plan, roles) {
|
|
181
|
-
const stablePlan = { ...plan, compiled_at: null };
|
|
182
|
-
return createHash("sha256")
|
|
183
|
-
.update(stableJson({ plan: stablePlan, roles }), "utf-8")
|
|
184
|
-
.digest("hex");
|
|
185
227
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
for (const [workflowKey, workflowDefinition] of Object.entries(cascaded.workflows)) {
|
|
207
|
-
const result = compileWorkflow(workflowDefinition, { workflow_key: workflowKey });
|
|
208
|
-
if (!result.ok || !result.plan) {
|
|
209
|
-
throw new Error(`workflow '${workflowKey}' compilation failed:\n`
|
|
210
|
-
+ result.errors.map((error) => ` ${error.path}: ${error.message}`).join("\n"));
|
|
228
|
+
function assertRunComposition(view, metadata, projectDirectory) {
|
|
229
|
+
const binding = view.run.run_binding.binding;
|
|
230
|
+
if (resolve(binding.project_root) !== projectDirectory
|
|
231
|
+
|| resolve(binding.workspace_root) !== projectDirectory
|
|
232
|
+
|| resolve(metadata.project_directory) !== projectDirectory) {
|
|
233
|
+
throw new Error(`run ${view.run.run_id} belongs to project ${binding.project_root}, not ${projectDirectory}`);
|
|
234
|
+
}
|
|
235
|
+
if (view.run.target.kind !== metadata.target.kind
|
|
236
|
+
|| view.run.target.key !== metadata.target.key) {
|
|
237
|
+
throw new Error("run metadata target does not match the immutable run target");
|
|
238
|
+
}
|
|
239
|
+
const expectedProvider = providerConfiguration(metadata);
|
|
240
|
+
if (canonicalizeJson(binding.provider_config)
|
|
241
|
+
!== canonicalizeJson(expectedProvider)) {
|
|
242
|
+
throw new Error("run metadata provider projection does not match the immutable run binding");
|
|
243
|
+
}
|
|
244
|
+
for (const name of WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES) {
|
|
245
|
+
const pinned = binding.validated_inputs[name];
|
|
246
|
+
if (pinned !== undefined && pinned !== metadata.task_id) {
|
|
247
|
+
throw new Error(`run metadata task_id does not match pinned input ${name}`);
|
|
211
248
|
}
|
|
212
|
-
const namespace = variableScopeIndex.workflowNamespaces.get(workflowKey) ?? "";
|
|
213
|
-
runtimePlans.push({
|
|
214
|
-
plan: result.plan,
|
|
215
|
-
workflow_asset: variableScopeIndex.workflowAssets.get(workflowKey) ?? null,
|
|
216
|
-
resolved_variables: resolvedVariablesForNamespace(defaultVariableScopes, namespace, workflowKey === metadata.workflow_name ? { ...metadata.variables } : {}),
|
|
217
|
-
});
|
|
218
249
|
}
|
|
219
|
-
const plan = runtimePlans.find((item) => item.plan.workflow_key === metadata.workflow_name)?.plan;
|
|
220
|
-
if (!plan)
|
|
221
|
-
throw new Error(`compiled workflow '${metadata.workflow_name}' is missing from runtime plans`);
|
|
222
|
-
const workflowAsset = runtimePlans.find((item) => item.plan.workflow_key === metadata.workflow_name)?.workflow_asset ?? null;
|
|
223
|
-
if (metadata.workflow_asset !== null
|
|
224
|
-
&& workflowAsset !== metadata.workflow_asset) {
|
|
225
|
-
throw new Error(`workflow asset changed from '${metadata.workflow_asset}' to '${workflowAsset ?? "unknown"}'`);
|
|
226
|
-
}
|
|
227
|
-
return {
|
|
228
|
-
plan,
|
|
229
|
-
runtime_plans: runtimePlans,
|
|
230
|
-
roles: cascaded.roles,
|
|
231
|
-
ir,
|
|
232
|
-
plan_digest: planDigest(plan, cascaded.roles),
|
|
233
|
-
workflow_asset: workflowAsset,
|
|
234
|
-
};
|
|
235
250
|
}
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
+
function buildRuntime(projectDirectory, composition, runStore = new DurableRunStore({ root_directory: runtimeRoot(projectDirectory) })) {
|
|
252
|
+
const root = runtimeRoot(projectDirectory);
|
|
253
|
+
const configuredExecutionAuthority = composition.execution_authority_factory?.(join(root, "execution-authority"));
|
|
254
|
+
const localExecutionAuthority = configuredExecutionAuthority === undefined
|
|
255
|
+
? new LocalAttemptExecutionAuthority({
|
|
256
|
+
root_directory: join(root, "execution-authority"),
|
|
257
|
+
})
|
|
258
|
+
: null;
|
|
259
|
+
const executionAuthority = configuredExecutionAuthority
|
|
260
|
+
?? localExecutionAuthority;
|
|
261
|
+
const composed = createCanonicalRuntimeComposition({
|
|
262
|
+
project_directory: projectDirectory,
|
|
263
|
+
run_store: runStore,
|
|
264
|
+
workspace_authority: new LocalAttemptWorkspaceAuthority({
|
|
265
|
+
lock_root: join(root, "workspace-locks"),
|
|
266
|
+
}),
|
|
267
|
+
execution_authority: executionAuthority,
|
|
268
|
+
verifier_execution_port: new ProductionLocalVerifierExecutionPort({
|
|
269
|
+
root_directory: join(root, "verifier-execution-authority"),
|
|
270
|
+
}),
|
|
251
271
|
});
|
|
252
|
-
const adapterOptions = {
|
|
253
|
-
roles: assets.roles,
|
|
254
|
-
variables: metadata.variables,
|
|
255
|
-
working_directory: metadata.project_directory,
|
|
256
|
-
timeout_ms: metadata.timeout_ms,
|
|
257
|
-
cancellation_grace_ms: metadata.cancellation_grace_ms,
|
|
258
|
-
};
|
|
259
272
|
return {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
273
|
+
run_store: runStore,
|
|
274
|
+
execution_authority: executionAuthority,
|
|
275
|
+
local_execution_authority: localExecutionAuthority,
|
|
276
|
+
service: composed.service,
|
|
264
277
|
};
|
|
265
278
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
function projectPathInWorkspace(metadata, workspaceDirectory, projectPath) {
|
|
272
|
-
const projectRelative = relative(metadata.project_directory, projectPath);
|
|
273
|
-
const outsideProject = (projectRelative === ".."
|
|
274
|
-
|| projectRelative.startsWith(`..${sep}`)
|
|
275
|
-
|| isAbsolute(projectRelative));
|
|
276
|
-
return outsideProject
|
|
277
|
-
? projectPath
|
|
278
|
-
: resolve(workspaceDirectory, projectRelative);
|
|
279
|
+
function buildInspectionService(projectDirectory, runStore) {
|
|
280
|
+
return createCanonicalRuntimeComposition({
|
|
281
|
+
project_directory: projectDirectory,
|
|
282
|
+
run_store: runStore,
|
|
283
|
+
}).service;
|
|
279
284
|
}
|
|
280
|
-
async function
|
|
281
|
-
if (
|
|
285
|
+
async function reconcileLocalPreparations(runtime, view) {
|
|
286
|
+
if (runtime.local_execution_authority === null)
|
|
282
287
|
return;
|
|
283
|
-
|
|
284
|
-
if (agentsDirectory !== metadata.agents_directory) {
|
|
285
|
-
const agents = compileAllRolesToAgentMD(compiled.roles, compiled.ir);
|
|
286
|
-
await writeAgentMDFiles(agents, agentsDirectory);
|
|
287
|
-
}
|
|
288
|
-
await writeCompiledIR(workspace.working_directory, createCompiledIR(compiled.ir, compiled.roles, compiled.workflow_runtime));
|
|
288
|
+
await reconcileLocalExecutionPreparations(runtime.local_execution_authority, view);
|
|
289
289
|
}
|
|
290
|
-
function createProvider(
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
});
|
|
290
|
+
function createProvider(binding) {
|
|
291
|
+
const configuration = binding.provider_config;
|
|
292
|
+
const executable = configuration.options.executable;
|
|
293
|
+
if (configuration.provider === "codex") {
|
|
294
|
+
return createCodexExecutionProvider({ executable });
|
|
295
|
+
}
|
|
296
|
+
if (configuration.provider !== "claude") {
|
|
297
|
+
throw new Error(`unsupported provider in immutable run binding: ${configuration.provider}`);
|
|
298
|
+
}
|
|
299
|
+
const maxBudget = configuration.options.max_budget_usd;
|
|
300
|
+
const permissionMode = configuration.options.permission_mode;
|
|
301
|
+
if (maxBudget === undefined || permissionMode === undefined) {
|
|
302
|
+
throw new Error("Claude run binding is missing launch budget or permission mode");
|
|
295
303
|
}
|
|
296
304
|
return createClaudeExecutionProvider({
|
|
297
|
-
executable
|
|
305
|
+
executable,
|
|
298
306
|
agentName: (packet) => `dna-${toKebabCase(packet.role)}`,
|
|
299
307
|
extraArgs: [
|
|
300
308
|
"--max-budget-usd",
|
|
301
|
-
String(
|
|
309
|
+
String(maxBudget),
|
|
302
310
|
"--permission-mode",
|
|
303
|
-
|
|
311
|
+
permissionMode,
|
|
304
312
|
],
|
|
305
313
|
});
|
|
306
314
|
}
|
|
307
|
-
function
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
async function recordAttemptProcess(runStore, packet, processId) {
|
|
324
|
-
if (!Number.isSafeInteger(processId) || processId <= 0) {
|
|
325
|
-
throw new Error(`provider returned invalid process id ${processId}`);
|
|
326
|
-
}
|
|
327
|
-
while (true) {
|
|
328
|
-
const snapshot = await runStore.requireRun(packet.run_id);
|
|
329
|
-
const attempt = snapshot.attempts.find((entry) => entry.attempt_id === packet.attempt_id);
|
|
330
|
-
if (!attempt
|
|
331
|
-
|| attempt.step_id !== packet.step_id
|
|
332
|
-
|| attempt.worker_session_id !== packet.worker_session_id) {
|
|
333
|
-
throw new Error(`attempt ${packet.attempt_id} disappeared before process registration`);
|
|
334
|
-
}
|
|
335
|
-
const recordedProcessId = attempt.process_id ?? null;
|
|
336
|
-
if (recordedProcessId === processId || attempt.phase === "terminal")
|
|
337
|
-
return;
|
|
338
|
-
if (recordedProcessId !== null) {
|
|
339
|
-
throw new Error(`attempt ${packet.attempt_id} already owns process ${recordedProcessId}`);
|
|
340
|
-
}
|
|
341
|
-
try {
|
|
342
|
-
await runStore.updateRun({
|
|
343
|
-
run_id: packet.run_id,
|
|
344
|
-
expected_record_version: snapshot.record_version,
|
|
345
|
-
attempts: [{ ...attempt, process_id: processId }],
|
|
346
|
-
});
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
catch (error) {
|
|
350
|
-
if (error instanceof RunStoreError && error.code === "version_conflict") {
|
|
351
|
-
continue;
|
|
352
|
-
}
|
|
353
|
-
throw error;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
const reconcileLocalProcess = async (attempt) => {
|
|
358
|
-
const processId = attempt.process_id ?? null;
|
|
359
|
-
if (processId === null
|
|
360
|
-
|| !Number.isSafeInteger(processId)
|
|
361
|
-
|| processId <= 0) {
|
|
362
|
-
return "unknown";
|
|
363
|
-
}
|
|
364
|
-
try {
|
|
365
|
-
process.kill(processId, 0);
|
|
366
|
-
return "live";
|
|
367
|
-
}
|
|
368
|
-
catch (error) {
|
|
369
|
-
const code = error instanceof Error
|
|
370
|
-
? error.code
|
|
371
|
-
: undefined;
|
|
372
|
-
if (code === "EPERM")
|
|
373
|
-
return "live";
|
|
374
|
-
if (code === "ESRCH")
|
|
375
|
-
return "dead";
|
|
376
|
-
return "unknown";
|
|
377
|
-
}
|
|
378
|
-
};
|
|
379
|
-
function verifierFailureMessage(prefix, stepId, result) {
|
|
380
|
-
const exitCode = result.exit_code === undefined
|
|
381
|
-
? ""
|
|
382
|
-
: ` exit_code=${result.exit_code}`;
|
|
383
|
-
const target = result.target ? ` target=${result.target}` : "";
|
|
384
|
-
const message = result.message ? ` - ${result.message}` : "";
|
|
385
|
-
const detail = `${prefix}${target}${exitCode}${message}`;
|
|
386
|
-
log(`${detail} (step: ${stepId})`);
|
|
387
|
-
return detail;
|
|
388
|
-
}
|
|
389
|
-
async function applyStepValidation(metadata, compiled, packet, execution) {
|
|
390
|
-
if (execution.result.outcome.kind !== "success")
|
|
391
|
-
return execution;
|
|
392
|
-
const step = compiled.plan.steps.find((entry) => entry.id === packet.step_id);
|
|
393
|
-
if (!step) {
|
|
394
|
-
throw new Error(`compiled Step ${packet.step_id} disappeared before validation`);
|
|
395
|
-
}
|
|
396
|
-
const variables = {
|
|
397
|
-
...metadata.variables,
|
|
398
|
-
round: String(packet.workflow_round),
|
|
399
|
-
};
|
|
400
|
-
let failure = null;
|
|
401
|
-
for (const completion of step.completion ?? []) {
|
|
402
|
-
const result = await runCompletionVerifier(completion, compiled.ir, {
|
|
403
|
-
projectDir: packet.workspace.working_directory,
|
|
404
|
-
variables,
|
|
405
|
-
});
|
|
406
|
-
if (!result.passed) {
|
|
407
|
-
failure = verifierFailureMessage("Completion failed", step.id, result);
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
|
-
if (!failure) {
|
|
412
|
-
for (const checkpoint of step.checkpoints ?? []) {
|
|
413
|
-
const result = await runCheckpointVerifier(checkpoint, compiled.ir, {
|
|
414
|
-
projectDir: packet.workspace.working_directory,
|
|
415
|
-
variables,
|
|
416
|
-
});
|
|
417
|
-
if (result.passed)
|
|
418
|
-
continue;
|
|
419
|
-
const detail = verifierFailureMessage(`Checkpoint failed: ${checkpoint.assert}`, step.id, result);
|
|
420
|
-
if ((checkpoint.action ?? "block") === "block") {
|
|
421
|
-
failure = detail;
|
|
422
|
-
break;
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
if (!failure)
|
|
427
|
-
return execution;
|
|
428
|
-
return {
|
|
429
|
-
...execution,
|
|
430
|
-
result: {
|
|
431
|
-
...execution.result,
|
|
432
|
-
outputs: [],
|
|
433
|
-
outcome: {
|
|
434
|
-
kind: "malformed_result",
|
|
435
|
-
exit_code: 0,
|
|
436
|
-
signal: null,
|
|
437
|
-
error: failure,
|
|
438
|
-
},
|
|
315
|
+
function createDriver(runtime, view, metadata, projectDirectory) {
|
|
316
|
+
assertRunComposition(view, metadata, projectDirectory);
|
|
317
|
+
runtime.execution_authority.assertExecutionSupported();
|
|
318
|
+
const networkSandbox = new LocalProviderNetworkSandbox();
|
|
319
|
+
return new PushDriver({
|
|
320
|
+
service: runtime.service,
|
|
321
|
+
provider_factory: ({ run, binding }) => {
|
|
322
|
+
assertRunComposition(run, readCanonicalMetadata(run.run.metadata), projectDirectory);
|
|
323
|
+
return run.plan.plan.workspace_policy.allow_network
|
|
324
|
+
? createProvider(binding)
|
|
325
|
+
: networkSandbox.wrap(createProvider(binding));
|
|
326
|
+
},
|
|
327
|
+
capabilities: {
|
|
328
|
+
network_policy_enforcement: networkSandbox.network_policy_enforcement,
|
|
439
329
|
},
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
async function recordStepResultEvidence(metadata, packet, execution) {
|
|
443
|
-
const outcome = execution.result.outcome;
|
|
444
|
-
const succeeded = outcome.kind === "success";
|
|
445
|
-
const startedAt = Date.parse(execution.result.started_at);
|
|
446
|
-
const completedAt = Date.parse(execution.result.completed_at);
|
|
447
|
-
await appendEvidenceCaptureEvent(metadata.project_directory, {
|
|
448
|
-
schema_version: EVIDENCE_CAPTURE_SCHEMA_VERSION,
|
|
449
|
-
event_id: `c5:${randomUUID()}`,
|
|
450
|
-
captured_at: execution.result.completed_at,
|
|
451
|
-
project_root: metadata.project_directory,
|
|
452
|
-
source: "dna_cli",
|
|
453
|
-
workflow_asset: metadata.workflow_asset ?? "unknown",
|
|
454
|
-
workflow_id: metadata.workflow_name,
|
|
455
|
-
run_id: packet.run_id,
|
|
456
|
-
step_id: packet.step_id,
|
|
457
|
-
fact: "result",
|
|
458
|
-
summary: `dna run recorded ${succeeded ? "success" : "failure"} for workflow step ${packet.step_id}`,
|
|
459
|
-
result: succeeded ? "success" : "failure",
|
|
460
|
-
exit_code: "exit_code" in outcome
|
|
461
|
-
? outcome.exit_code ?? undefined
|
|
462
|
-
: undefined,
|
|
463
|
-
duration_ms: Number.isFinite(startedAt) && Number.isFinite(completedAt)
|
|
464
|
-
? Math.max(0, completedAt - startedAt)
|
|
465
|
-
: undefined,
|
|
466
|
-
});
|
|
467
|
-
}
|
|
468
|
-
function createWorkspaceAwareExecutor(metadata, compiled, runStore) {
|
|
469
|
-
return async (packet, provider, options) => {
|
|
470
|
-
const workspace = allocateAttemptWorkspace(planAttemptWorkspace({
|
|
471
|
-
isolation: packet.workspace.isolation,
|
|
472
|
-
repository_directory: metadata.project_directory,
|
|
473
|
-
run_id: packet.run_id,
|
|
474
|
-
step_id: packet.step_id,
|
|
475
|
-
attempt_id: packet.attempt_id,
|
|
476
|
-
}));
|
|
477
|
-
const workerPacket = {
|
|
478
|
-
...packet,
|
|
479
|
-
workspace: {
|
|
480
|
-
isolation: workspace.isolation,
|
|
481
|
-
working_directory: workspace.working_directory,
|
|
482
|
-
},
|
|
483
|
-
};
|
|
484
|
-
const state = new DNAStateManager(workspace.working_directory, packet.worker_session_id);
|
|
485
|
-
let lifecycleRecorded = false;
|
|
486
|
-
try {
|
|
487
|
-
await syncAttemptRuntimeArtifacts(compiled, metadata, workspace);
|
|
488
|
-
const runtimeBinding = compiled.workflow_runtime.workflows[metadata.workflow_name];
|
|
489
|
-
if (!runtimeBinding)
|
|
490
|
-
throw new Error(`Missing runtime binding for ${metadata.workflow_name}`);
|
|
491
|
-
const canonicalArguments = metadata.variables.ARGUMENTS ??
|
|
492
|
-
metadata.variables.arguments ?? metadata.variables.task_id;
|
|
493
|
-
const runtimeInputs = {};
|
|
494
|
-
for (const name of runtimeBinding.runtime_input_refs) {
|
|
495
|
-
const value = WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES.has(name)
|
|
496
|
-
? canonicalArguments
|
|
497
|
-
: metadata.variables[name];
|
|
498
|
-
if (value === undefined)
|
|
499
|
-
throw new Error(`Missing runtime input ${name} for ${metadata.workflow_name}`);
|
|
500
|
-
runtimeInputs[name] = value;
|
|
501
|
-
}
|
|
502
|
-
await state.writeWorkflowState({
|
|
503
|
-
active: true,
|
|
504
|
-
workflow_asset: runtimeBinding.workflow_asset,
|
|
505
|
-
workflow: metadata.workflow_name,
|
|
506
|
-
current_step: packet.step_id,
|
|
507
|
-
current_role: packet.role,
|
|
508
|
-
iteration: packet.workflow_round,
|
|
509
|
-
session_id: packet.worker_session_id,
|
|
510
|
-
worker_session_id: packet.worker_session_id,
|
|
511
|
-
run_id: packet.run_id,
|
|
512
|
-
step_id: packet.step_id,
|
|
513
|
-
attempt_id: packet.attempt_id,
|
|
514
|
-
started_at: packet.created_at,
|
|
515
|
-
inputs: Object.keys(runtimeInputs).length > 0 ? runtimeInputs : undefined,
|
|
516
|
-
resolved_variables: runtimeBinding.resolved_variables,
|
|
517
|
-
resolved_variables_source: "compiled_manifest",
|
|
518
|
-
workflow_runtime_manifest_hash: compiled.workflow_runtime.manifest_hash,
|
|
519
|
-
workflow_runtime_compiled_ir_hash: compiled.workflow_runtime.compiled_ir_hash,
|
|
520
|
-
workflow_inputs_pinned: true,
|
|
521
|
-
});
|
|
522
|
-
const inheritedProcessObserver = options?.onProcessStart;
|
|
523
|
-
let execution = await executeWorkerAttempt(workerPacket, provider, {
|
|
524
|
-
...options,
|
|
525
|
-
onProcessStart: async (processId) => {
|
|
526
|
-
await recordAttemptProcess(runStore, workerPacket, processId);
|
|
527
|
-
await inheritedProcessObserver?.(processId);
|
|
528
|
-
},
|
|
529
|
-
});
|
|
530
|
-
execution = await applyStepValidation(metadata, compiled, workerPacket, execution);
|
|
531
|
-
await recordStepResultEvidence(metadata, workerPacket, execution);
|
|
532
|
-
const reason = execution.result.outcome.kind === "cancelled"
|
|
533
|
-
? "attempt_cancelled"
|
|
534
|
-
: execution.result.outcome.kind === "success"
|
|
535
|
-
? preservationReason(workerPacket)
|
|
536
|
-
: "attempt_failed";
|
|
537
|
-
preserveWorkspace(workspace, reason, `attempt outcome: ${execution.result.outcome.kind}`);
|
|
538
|
-
lifecycleRecorded = true;
|
|
539
|
-
return execution;
|
|
540
|
-
}
|
|
541
|
-
catch (error) {
|
|
542
|
-
if (!lifecycleRecorded) {
|
|
543
|
-
preserveWorkspace(workspace, "controller_policy", `attempt launch failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
544
|
-
}
|
|
545
|
-
throw error;
|
|
546
|
-
}
|
|
547
|
-
finally {
|
|
548
|
-
try {
|
|
549
|
-
await state.cleanup();
|
|
550
|
-
}
|
|
551
|
-
catch (error) {
|
|
552
|
-
log(`Worker Hook state cleanup failed for ${packet.worker_session_id}: `
|
|
553
|
-
+ `${error instanceof Error ? error.message : String(error)}`);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
};
|
|
557
|
-
}
|
|
558
|
-
function buildRuntime(metadata, compiled) {
|
|
559
|
-
const runStore = new DurableRunStore({
|
|
560
|
-
root_directory: runtimeRoot(metadata.project_directory),
|
|
561
|
-
});
|
|
562
|
-
const resultStore = new ImmutableResultStore(runStore);
|
|
563
|
-
const resolver = new HandoffResolver(resultStore);
|
|
564
|
-
const provider = createProvider(metadata);
|
|
565
|
-
const controller = new RunController({
|
|
566
|
-
run_store: runStore,
|
|
567
|
-
handoff_resolver: resolver,
|
|
568
|
-
steps: compiled.definitions,
|
|
569
|
-
workflow_retry_loop: compiled.retry_loop,
|
|
570
|
-
provider_for_step: () => provider,
|
|
571
|
-
reconcile_attempt: reconcileLocalProcess,
|
|
572
|
-
execute_attempt: createWorkspaceAwareExecutor(metadata, compiled, runStore),
|
|
573
330
|
max_concurrency: metadata.max_concurrency,
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
331
|
+
caller_attestation_authority: createCallerAttestationAuthority(runtime.execution_authority.authority_instance_id),
|
|
332
|
+
execution_authority: {
|
|
333
|
+
prepare: async (context) => {
|
|
334
|
+
await reconcileLocalPreparations(runtime, await runtime.service.inspect(context.lease.run_id));
|
|
335
|
+
return runtime.execution_authority.prepareExecution({
|
|
336
|
+
run_id: context.lease.run_id,
|
|
337
|
+
step_id: context.lease.step_id,
|
|
338
|
+
activation_id: context.lease.activation_id,
|
|
339
|
+
attempt_id: context.lease.attempt_id,
|
|
340
|
+
execution_claim_epoch: context.execution_claim_epoch,
|
|
341
|
+
worker_session_id: context.worker_session_id,
|
|
342
|
+
});
|
|
343
|
+
},
|
|
344
|
+
abandon: (_context, preparation) => runtime.execution_authority.abandonPreparedExecution(preparation),
|
|
345
|
+
bind: (context, preparation) => runtime.execution_authority.bindExecution(preparation, { started_at: context.started_at }),
|
|
346
|
+
recover: (context) => {
|
|
347
|
+
const preparationRequest = {
|
|
348
|
+
run_id: context.lease.run_id,
|
|
349
|
+
step_id: context.lease.step_id,
|
|
350
|
+
activation_id: context.lease.activation_id,
|
|
351
|
+
attempt_id: context.lease.attempt_id,
|
|
352
|
+
execution_claim_epoch: context.execution_claim_epoch,
|
|
353
|
+
worker_session_id: context.worker_session_id,
|
|
354
|
+
};
|
|
355
|
+
const prepared = runtime.execution_authority.recoverPreparedExecution(preparationRequest);
|
|
356
|
+
if (prepared !== null) {
|
|
357
|
+
return {
|
|
358
|
+
kind: "launch",
|
|
359
|
+
binding: runtime.execution_authority.bindExecution(prepared, { started_at: context.started_at }),
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
return runtime.execution_authority.recoverExecution({
|
|
363
|
+
run_id: context.lease.run_id,
|
|
364
|
+
step_id: context.lease.step_id,
|
|
365
|
+
activation_id: context.lease.activation_id,
|
|
366
|
+
attempt_id: context.lease.attempt_id,
|
|
367
|
+
execution_claim_epoch: context.execution_claim_epoch,
|
|
368
|
+
executor: { kind: "worker", worker_session_id: context.worker_session_id },
|
|
369
|
+
started_at: context.started_at,
|
|
370
|
+
});
|
|
371
|
+
},
|
|
586
372
|
},
|
|
587
|
-
|
|
588
|
-
max_concurrency: 1,
|
|
589
|
-
lease_duration_ms: 15_000,
|
|
590
|
-
lease_heartbeat_ms: 3_000,
|
|
591
|
-
scheduler_poll_ms: 50,
|
|
373
|
+
lifecycle_hooks: createRunAttemptObservers((context) => (readCanonicalMetadata(context.run.run.metadata))),
|
|
592
374
|
});
|
|
593
375
|
}
|
|
594
|
-
|
|
595
|
-
if (opts.dnaFiles.length === 0)
|
|
376
|
+
function startMetadata(opts, projectDirectory) {
|
|
377
|
+
if (opts.dnaFiles.length === 0)
|
|
596
378
|
throw new RunUsageError("--dna requires at least one DNA file");
|
|
597
|
-
|
|
598
|
-
|
|
379
|
+
const selectedTargets = [opts.workflowName, opts.controllerName]
|
|
380
|
+
.filter((value) => value?.trim()).length;
|
|
381
|
+
if (selectedTargets !== 1) {
|
|
382
|
+
throw new RunUsageError("exactly one of --workflow or --controller is required");
|
|
383
|
+
}
|
|
384
|
+
const target = opts.controllerName?.trim()
|
|
385
|
+
? { kind: "controller", key: requireText(opts.controllerName, "--controller") }
|
|
386
|
+
: { kind: "workflow", key: requireText(opts.workflowName, "--workflow") };
|
|
599
387
|
const taskId = requireText(opts.taskId, "--task");
|
|
600
|
-
const
|
|
601
|
-
cwd: projectDirectory,
|
|
602
|
-
});
|
|
603
|
-
if (expanded.length === 0) {
|
|
604
|
-
throw new RunUsageError("--dna did not resolve any DNA files");
|
|
605
|
-
}
|
|
606
|
-
const variables = {
|
|
607
|
-
...(opts.vars ?? {}),
|
|
608
|
-
task_id: taskId,
|
|
609
|
-
ARGUMENTS: taskId,
|
|
610
|
-
workflow: workflowName,
|
|
611
|
-
};
|
|
612
|
-
const maxBudget = requirePositiveNumber(opts.maxBudget ?? 5, "--max-budget");
|
|
613
|
-
const maxConcurrency = requirePositiveInteger(opts.maxConcurrency ?? 1, "--max-concurrency");
|
|
614
|
-
const timeoutMs = opts.timeoutMs === null || opts.timeoutMs === undefined
|
|
615
|
-
? null
|
|
616
|
-
: requireNonNegativeInteger(opts.timeoutMs, "--timeout-ms");
|
|
617
|
-
const cancellationGraceMs = requireNonNegativeInteger(opts.cancellationGraceMs ?? 5_000, "--cancellation-grace-ms");
|
|
618
|
-
const permissionMode = requireText(opts.permissionMode ?? "default", "--permission-mode");
|
|
388
|
+
const timeoutMs = opts.timeoutMs == null ? null : requireNonNegativeInteger(opts.timeoutMs, "--timeout-ms");
|
|
619
389
|
return {
|
|
620
|
-
schema_version: "intentdna.canonical_run.
|
|
390
|
+
schema_version: "intentdna.canonical_run.v3",
|
|
621
391
|
project_directory: projectDirectory,
|
|
622
|
-
dna_files:
|
|
623
|
-
|
|
392
|
+
dna_files: opts.dnaFiles.map((file) => resolve(projectDirectory, file)),
|
|
393
|
+
target,
|
|
624
394
|
task_id: taskId,
|
|
625
395
|
context: opts.context ?? null,
|
|
626
|
-
variables
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
:
|
|
396
|
+
variables: {
|
|
397
|
+
...(opts.vars ?? {}),
|
|
398
|
+
ARGUMENTS: taskId,
|
|
399
|
+
arguments: taskId,
|
|
400
|
+
task_id: taskId,
|
|
401
|
+
workflow: target.key,
|
|
402
|
+
},
|
|
403
|
+
agents_directory: opts.agentsDir ? resolve(projectDirectory, opts.agentsDir) : join(projectDirectory, ".claude", "agents"),
|
|
630
404
|
provider: {
|
|
631
405
|
kind: opts.provider ?? "claude",
|
|
632
|
-
executable: opts.providerExecutable === undefined
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
max_budget_usd: maxBudget,
|
|
636
|
-
permission_mode: permissionMode,
|
|
406
|
+
executable: opts.providerExecutable === undefined ? null : requireText(opts.providerExecutable, "--provider-executable"),
|
|
407
|
+
max_budget_usd: requirePositiveNumber(opts.maxBudget ?? 5, "--max-budget"),
|
|
408
|
+
permission_mode: requireText(opts.permissionMode ?? "default", "--permission-mode"),
|
|
637
409
|
},
|
|
638
|
-
max_concurrency: maxConcurrency,
|
|
410
|
+
max_concurrency: requirePositiveInteger(opts.maxConcurrency ?? 1, "--max-concurrency"),
|
|
639
411
|
timeout_ms: timeoutMs,
|
|
640
|
-
cancellation_grace_ms: cancellationGraceMs,
|
|
412
|
+
cancellation_grace_ms: requireNonNegativeInteger(opts.cancellationGraceMs ?? 5_000, "--cancellation-grace-ms"),
|
|
641
413
|
workflow_asset: null,
|
|
414
|
+
runtime_projection: null,
|
|
642
415
|
};
|
|
643
416
|
}
|
|
644
|
-
async function
|
|
645
|
-
const
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
417
|
+
async function previewRun(metadata) {
|
|
418
|
+
const app = new CanonicalRunApplication({});
|
|
419
|
+
const compiled = await app.compileTarget({
|
|
420
|
+
dna_sources: metadata.dna_files,
|
|
421
|
+
target: metadata.target,
|
|
422
|
+
inputs: canonicalInputs(metadata),
|
|
423
|
+
input_policy: "declared_only",
|
|
424
|
+
environment: { project_root: metadata.project_directory },
|
|
425
|
+
provider: providerConfiguration(metadata),
|
|
426
|
+
max_concurrency: metadata.max_concurrency,
|
|
427
|
+
context: metadata.context,
|
|
428
|
+
attempt_policy: { timeout_ms: metadata.timeout_ms, cancellation_grace_ms: metadata.cancellation_grace_ms },
|
|
429
|
+
});
|
|
430
|
+
if (!compiled.projection)
|
|
431
|
+
throw new Error("canonical compiler did not produce a runtime projection");
|
|
432
|
+
const pinnedMetadata = metadataForBinding(metadata, compiled.run_binding);
|
|
433
|
+
return printDryRun(compiled.projection.workflow_plan, pinnedMetadata, compiled.run_binding.binding.plan_id);
|
|
656
434
|
}
|
|
657
|
-
|
|
658
|
-
export async function runRun(opts) {
|
|
435
|
+
export async function runRun(opts, composition = {}) {
|
|
659
436
|
const action = opts.action ?? "start";
|
|
660
437
|
const projectDirectory = resolve(opts.projectDir ?? process.cwd());
|
|
661
|
-
const runStore = new DurableRunStore({
|
|
662
|
-
root_directory: runtimeRoot(projectDirectory),
|
|
663
|
-
});
|
|
664
438
|
try {
|
|
665
|
-
if (
|
|
666
|
-
&& action !== "status"
|
|
667
|
-
&& action !== "inspect"
|
|
668
|
-
&& action !== "resume"
|
|
669
|
-
&& action !== "cancel") {
|
|
439
|
+
if (!["start", "status", "inspect", "resume", "cancel", "close"].includes(action)) {
|
|
670
440
|
throw new RunUsageError(`unsupported run action '${String(action)}'`);
|
|
671
441
|
}
|
|
442
|
+
const runStore = new DurableRunStore({ root_directory: runtimeRoot(projectDirectory) });
|
|
443
|
+
const existingRunId = () => asRunId(requireText(opts.runId, "run_id"));
|
|
672
444
|
if (action === "status") {
|
|
673
445
|
return runLifecycleStatus({
|
|
674
446
|
run_store: runStore,
|
|
675
|
-
|
|
447
|
+
service_factory: async () => buildInspectionService(projectDirectory, runStore),
|
|
448
|
+
run_id: existingRunId(),
|
|
676
449
|
json: opts.json,
|
|
677
450
|
});
|
|
678
451
|
}
|
|
679
452
|
if (action === "inspect") {
|
|
680
453
|
return runLifecycleInspect({
|
|
681
454
|
run_store: runStore,
|
|
682
|
-
|
|
455
|
+
service_factory: async () => buildInspectionService(projectDirectory, runStore),
|
|
456
|
+
run_id: existingRunId(),
|
|
683
457
|
json: opts.json,
|
|
684
458
|
});
|
|
685
459
|
}
|
|
686
|
-
if (action === "
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
run_id: runId,
|
|
460
|
+
if (action === "close") {
|
|
461
|
+
return runLifecycleClose({
|
|
462
|
+
run_store: runStore,
|
|
463
|
+
run_id: existingRunId(),
|
|
464
|
+
reason: opts.reason,
|
|
692
465
|
json: opts.json,
|
|
693
466
|
});
|
|
694
467
|
}
|
|
468
|
+
if (action === "start") {
|
|
469
|
+
const initialMetadata = startMetadata(opts, projectDirectory);
|
|
470
|
+
if (opts.dryRun)
|
|
471
|
+
return await previewRun(initialMetadata);
|
|
472
|
+
}
|
|
473
|
+
let runtimeComposition = null;
|
|
474
|
+
const requireRuntime = () => {
|
|
475
|
+
runtimeComposition ??= buildRuntime(projectDirectory, composition, runStore);
|
|
476
|
+
return runtimeComposition;
|
|
477
|
+
};
|
|
695
478
|
if (action === "cancel") {
|
|
696
|
-
const runId = asRunId(requireText(opts.runId, "run_id"));
|
|
697
479
|
return runLifecycleCancel({
|
|
698
|
-
|
|
699
|
-
|
|
480
|
+
run_store: runStore,
|
|
481
|
+
service_factory: async () => {
|
|
482
|
+
const composed = requireRuntime();
|
|
483
|
+
return {
|
|
484
|
+
inspect: (runId) => composed.service.inspect(runId),
|
|
485
|
+
inspectAudit: (runId) => composed.service.inspectAudit(runId),
|
|
486
|
+
resume: (runId) => composed.service.resume(runId),
|
|
487
|
+
cancel: async (runId, reason) => {
|
|
488
|
+
await reconcileLocalPreparations(composed, await composed.service.inspect(runId));
|
|
489
|
+
const cancelled = await composed.service.cancel(runId, reason);
|
|
490
|
+
await reconcileLocalPreparations(composed, cancelled);
|
|
491
|
+
return cancelled;
|
|
492
|
+
},
|
|
493
|
+
};
|
|
494
|
+
},
|
|
495
|
+
run_id: existingRunId(),
|
|
700
496
|
reason: opts.reason,
|
|
701
497
|
json: opts.json,
|
|
702
498
|
});
|
|
703
499
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
500
|
+
if (action === "resume") {
|
|
501
|
+
const runId = existingRunId();
|
|
502
|
+
return runLifecycleResume({
|
|
503
|
+
run_store: runStore,
|
|
504
|
+
service_factory: async () => requireRuntime().service,
|
|
505
|
+
run_id: runId,
|
|
506
|
+
drive: async (service, id) => {
|
|
507
|
+
const composed = requireRuntime();
|
|
508
|
+
const view = await service.inspect(id);
|
|
509
|
+
await reconcileLocalPreparations(composed, view);
|
|
510
|
+
const metadata = readCanonicalMetadata(view.run.metadata);
|
|
511
|
+
const resumed = await createDriver(composed, view, metadata, projectDirectory).run(id, { resume: true });
|
|
512
|
+
await reconcileLocalPreparations(composed, resumed);
|
|
513
|
+
writeTerminalDriverFailure(resumed);
|
|
514
|
+
return resumed;
|
|
515
|
+
},
|
|
516
|
+
json: opts.json,
|
|
517
|
+
});
|
|
716
518
|
}
|
|
717
|
-
const
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
519
|
+
const initialMetadata = startMetadata(opts, projectDirectory);
|
|
520
|
+
const runtime = requireRuntime();
|
|
521
|
+
const app = new CanonicalRunApplication({
|
|
522
|
+
starter: runtime.service,
|
|
523
|
+
metadata_factory: ({ compiled }) => {
|
|
524
|
+
if (!compiled.projection) {
|
|
525
|
+
throw new Error("canonical compiler did not produce a runtime projection");
|
|
526
|
+
}
|
|
527
|
+
return metadataForBinding({
|
|
528
|
+
...initialMetadata,
|
|
529
|
+
workflow_asset: compiled.projection.workflow_asset,
|
|
530
|
+
runtime_projection: durableRuntimeProjection(compiled.projection),
|
|
531
|
+
}, compiled.run_binding);
|
|
532
|
+
},
|
|
533
|
+
projection_sink: async ({ phase, projection }) => {
|
|
534
|
+
if (phase !== "before_start")
|
|
535
|
+
return;
|
|
536
|
+
await syncRuntimeProjection(projectDirectory, initialMetadata.agents_directory, projection, durableRuntimeProjection(projection));
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
const started = await app.startTarget({
|
|
540
|
+
dna_sources: initialMetadata.dna_files,
|
|
541
|
+
target: initialMetadata.target,
|
|
542
|
+
inputs: canonicalInputs(initialMetadata),
|
|
543
|
+
input_policy: "declared_only",
|
|
544
|
+
environment: { project_root: projectDirectory, workspace_root: projectDirectory },
|
|
545
|
+
provider: providerConfiguration(initialMetadata),
|
|
546
|
+
max_concurrency: initialMetadata.max_concurrency,
|
|
547
|
+
context: initialMetadata.context,
|
|
548
|
+
attempt_policy: { timeout_ms: initialMetadata.timeout_ms, cancellation_grace_ms: initialMetadata.cancellation_grace_ms },
|
|
549
|
+
metadata: initialMetadata,
|
|
550
|
+
run_id: opts.runId?.trim() ? asRunId(opts.runId) : undefined,
|
|
747
551
|
});
|
|
552
|
+
const storedMetadata = readCanonicalMetadata(started.view.run.metadata);
|
|
553
|
+
const view = await createDriver(runtime, started.view, storedMetadata, projectDirectory).run(started.run_id, { initial_view: started.view });
|
|
554
|
+
await reconcileLocalPreparations(runtime, view);
|
|
555
|
+
writeTerminalDriverFailure(view);
|
|
556
|
+
writeCanonicalOperation("start", view, opts.json === true);
|
|
557
|
+
return view.run.status === "failed" ? 1 : 0;
|
|
748
558
|
}
|
|
749
559
|
catch (error) {
|
|
750
560
|
const usage = error instanceof RunUsageError;
|
|
751
|
-
|
|
752
|
-
|
|
561
|
+
const typedCode = error instanceof Error
|
|
562
|
+
&& "code" in error
|
|
563
|
+
&& typeof error.code === "string"
|
|
564
|
+
? error.code
|
|
565
|
+
: null;
|
|
566
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
567
|
+
process.stderr.write(`${usage ? "Usage error" : "Error"}: ${typedCode === null ? "" : `${typedCode}: `}${detail}\n`);
|
|
753
568
|
return usage ? 2 : 1;
|
|
754
569
|
}
|
|
755
570
|
}
|
|
756
571
|
function printDryRun(plan, metadata, digest) {
|
|
757
572
|
process.stderr.write("\n=== DRY RUN - Canonical Controller Plan ===\n\n");
|
|
758
|
-
process.stderr.write(`
|
|
759
|
-
process.stderr.write(`
|
|
760
|
-
process.stderr.write(`
|
|
761
|
-
process.stderr.write(`
|
|
762
|
-
process.stderr.write(`Durable root: ${runtimeRoot(metadata.project_directory)}\n`);
|
|
763
|
-
process.stderr.write(`Steps: ${plan.steps.length}\n\n`);
|
|
764
|
-
process.stderr.write(`Groups: ${plan.parallel_groups.length}\n`);
|
|
573
|
+
process.stderr.write(`Target: ${metadata.target.kind}:${metadata.target.key}\n`);
|
|
574
|
+
process.stderr.write(`Projection workflow: ${plan.name}\nPlan digest: ${digest}\nProvider: ${metadata.provider.kind}\n`);
|
|
575
|
+
process.stderr.write(`Max concurrency: ${metadata.max_concurrency}\nDurable root: ${runtimeRoot(metadata.project_directory)}\n`);
|
|
576
|
+
process.stderr.write(`Steps: ${plan.steps.length}\n\nGroups: ${plan.parallel_groups.length}\n`);
|
|
765
577
|
process.stderr.write(`Max retries: ${plan.retry.max_retries}\n`);
|
|
766
|
-
if (plan.retry.retry_from)
|
|
578
|
+
if (plan.retry.retry_from)
|
|
767
579
|
process.stderr.write(`Retry from: ${plan.retry.retry_from}\n`);
|
|
768
|
-
}
|
|
769
580
|
process.stderr.write(`Variables: ${JSON.stringify(metadata.variables)}\n\n`);
|
|
770
581
|
for (const group of plan.parallel_groups) {
|
|
771
582
|
process.stderr.write(`Group ${group.group_index + 1} (${group.isolation}):\n`);
|
|
@@ -774,24 +585,15 @@ function printDryRun(plan, metadata, digest) {
|
|
|
774
585
|
if (!step)
|
|
775
586
|
continue;
|
|
776
587
|
const prompt = substituteTemplate(step.prompt ?? step.description, metadata.variables);
|
|
777
|
-
|
|
778
|
-
const optional = step.optional ? " (optional)" : "";
|
|
779
|
-
process.stderr.write(` - ${step.id} -> agent: dna-${toKebabCase(step.role)}`
|
|
780
|
-
+ `${runIf}${optional}; independent session\n`);
|
|
588
|
+
process.stderr.write(` - ${step.id} -> agent: dna-${toKebabCase(step.role)}${step.run_if ? ` [run_if: ${step.run_if}]` : ""}${step.optional ? " (optional)" : ""}; independent session\n`);
|
|
781
589
|
process.stderr.write(` prompt: ${prompt.slice(0, 120)}${prompt.length > 120 ? "..." : ""}\n`);
|
|
782
590
|
}
|
|
783
591
|
}
|
|
784
592
|
if (plan.transitions.length > 0) {
|
|
785
593
|
process.stderr.write("\nTransitions:\n");
|
|
786
|
-
for (const transition of plan.transitions)
|
|
787
|
-
process.stderr.write(` ${transition.from} -> ${transition.to} `
|
|
788
|
-
+ `(on ${transition.condition})\n`);
|
|
789
|
-
}
|
|
594
|
+
for (const transition of plan.transitions)
|
|
595
|
+
process.stderr.write(` ${transition.from} -> ${transition.to} (on ${transition.condition})\n`);
|
|
790
596
|
}
|
|
791
597
|
process.stderr.write("\n");
|
|
792
598
|
return 0;
|
|
793
599
|
}
|
|
794
|
-
function log(message) {
|
|
795
|
-
const timestamp = new Date().toLocaleTimeString("en-US", { hour12: false });
|
|
796
|
-
process.stderr.write(`[${timestamp}] ${message}\n`);
|
|
797
|
-
}
|