intentdna 1.9.0 → 1.9.3
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 +2897 -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 +788 -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/providers/codex.js +2 -2
- 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 +6 -2
- package/scripts/build-windows-job-keeper.mjs +172 -0
- package/scripts/verify-windows-job-keeper.mjs +184 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LEGACY_RUN_STORE_SCHEMA_VERSION, RunStoreError, } from "../../runtime/run-store.js";
|
|
1
2
|
class LifecycleUsageError extends Error {
|
|
2
3
|
constructor(message) {
|
|
3
4
|
super(message);
|
|
@@ -15,31 +16,44 @@ const TASK_STATES = [
|
|
|
15
16
|
"skipped",
|
|
16
17
|
"cancelled",
|
|
17
18
|
];
|
|
19
|
+
const ACTIVATION_STATES = [
|
|
20
|
+
"dormant",
|
|
21
|
+
"dependency_blocked",
|
|
22
|
+
"ready",
|
|
23
|
+
"claimed",
|
|
24
|
+
"running",
|
|
25
|
+
"waiting_for_human",
|
|
26
|
+
"succeeded",
|
|
27
|
+
"failed",
|
|
28
|
+
"skipped",
|
|
29
|
+
"cancelled",
|
|
30
|
+
];
|
|
18
31
|
function requireText(value, label) {
|
|
19
|
-
if (!value.trim())
|
|
32
|
+
if (!value.trim())
|
|
20
33
|
throw new LifecycleUsageError(`${label} is required`);
|
|
21
|
-
}
|
|
22
34
|
}
|
|
23
35
|
function requireRunId(runId) {
|
|
24
36
|
requireText(runId, "run_id");
|
|
25
37
|
}
|
|
38
|
+
function validateReason(reason) {
|
|
39
|
+
if (reason !== null && !reason.trim()) {
|
|
40
|
+
throw new LifecycleUsageError("reason must not be empty");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
26
43
|
function validateStartRun(run) {
|
|
27
44
|
requireRunId(run.run_id);
|
|
28
45
|
requireText(run.workflow_id, "workflow_id");
|
|
29
46
|
requireText(run.plan_digest, "plan_digest");
|
|
30
47
|
requireText(run.created_at, "created_at");
|
|
31
48
|
requireText(run.updated_at, "updated_at");
|
|
32
|
-
if (run.status !== "active"
|
|
33
|
-
|| run.cancellation !== null
|
|
34
|
-
|| run.terminal !== null) {
|
|
49
|
+
if (run.status !== "active" || run.cancellation !== null || run.terminal !== null) {
|
|
35
50
|
throw new LifecycleUsageError("start requires an active run without cancellation or terminal state");
|
|
36
51
|
}
|
|
37
52
|
}
|
|
38
|
-
function
|
|
53
|
+
function legacyStatusProjection(snapshot) {
|
|
39
54
|
const taskCounts = Object.fromEntries(TASK_STATES.map((state) => [state, 0]));
|
|
40
|
-
for (const task of snapshot.tasks)
|
|
55
|
+
for (const task of snapshot.tasks)
|
|
41
56
|
taskCounts[task.state] += 1;
|
|
42
|
-
}
|
|
43
57
|
return {
|
|
44
58
|
schema_version: "intentdna.run_status.v1",
|
|
45
59
|
run_id: snapshot.run.run_id,
|
|
@@ -56,14 +70,36 @@ function statusProjection(snapshot) {
|
|
|
56
70
|
updated_at: snapshot.run.updated_at,
|
|
57
71
|
};
|
|
58
72
|
}
|
|
73
|
+
export function canonicalViewStatusProjection(view) {
|
|
74
|
+
const activationCounts = Object.fromEntries(ACTIVATION_STATES.map((state) => [state, 0]));
|
|
75
|
+
for (const activation of view.activations) {
|
|
76
|
+
activationCounts[activation.state] += 1;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
schema_version: "intentdna.run_status.v2",
|
|
80
|
+
ledger_schema_version: 2,
|
|
81
|
+
run_id: view.run.run_id,
|
|
82
|
+
target: view.run.target,
|
|
83
|
+
plan_id: view.run.plan_id,
|
|
84
|
+
binding_id: view.run.run_binding.binding_id,
|
|
85
|
+
status: view.run.status,
|
|
86
|
+
record_version: view.record_version,
|
|
87
|
+
activation_counts: activationCounts,
|
|
88
|
+
attempt_count: view.attempts.length,
|
|
89
|
+
result_count: view.results.length,
|
|
90
|
+
cancellation: view.run.cancellation,
|
|
91
|
+
terminal: view.run.terminal,
|
|
92
|
+
created_at: view.run.created_at,
|
|
93
|
+
updated_at: view.run.updated_at,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
59
96
|
function durableHandoffs(snapshot) {
|
|
60
97
|
return snapshot.events.flatMap((event) => {
|
|
61
98
|
if (event.type !== "handoff_resolved"
|
|
62
99
|
|| event.step_id === null
|
|
63
100
|
|| event.attempt_id === null
|
|
64
|
-
|| event.worker_session_id === null)
|
|
101
|
+
|| event.worker_session_id === null)
|
|
65
102
|
return [];
|
|
66
|
-
}
|
|
67
103
|
return [{
|
|
68
104
|
event_id: event.event_id,
|
|
69
105
|
sequence: event.sequence,
|
|
@@ -77,7 +113,7 @@ function durableHandoffs(snapshot) {
|
|
|
77
113
|
}];
|
|
78
114
|
});
|
|
79
115
|
}
|
|
80
|
-
function
|
|
116
|
+
function legacyInspectionProjection(snapshot) {
|
|
81
117
|
return {
|
|
82
118
|
schema_version: "intentdna.run_inspection.v1",
|
|
83
119
|
record_version: snapshot.record_version,
|
|
@@ -92,13 +128,31 @@ function inspectionProjection(snapshot) {
|
|
|
92
128
|
ledger_updated_at: snapshot.updated_at,
|
|
93
129
|
};
|
|
94
130
|
}
|
|
131
|
+
function canonicalInspectionProjection(view) {
|
|
132
|
+
return {
|
|
133
|
+
schema_version: "intentdna.run_inspection.v2",
|
|
134
|
+
ledger_schema_version: 2,
|
|
135
|
+
record_version: view.record_version,
|
|
136
|
+
run: view.run,
|
|
137
|
+
activations: view.activations,
|
|
138
|
+
attempts: view.attempts,
|
|
139
|
+
claims: view.claims.map((claim) => {
|
|
140
|
+
const { claim_token: _claimToken, ...publicClaim } = claim;
|
|
141
|
+
return publicClaim;
|
|
142
|
+
}),
|
|
143
|
+
events: view.events,
|
|
144
|
+
result_refs: view.results,
|
|
145
|
+
ledger_created_at: view.ledger.created_at,
|
|
146
|
+
ledger_updated_at: view.ledger.updated_at,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
95
149
|
function writeJson(value) {
|
|
96
150
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
97
151
|
}
|
|
98
|
-
function
|
|
99
|
-
const summary =
|
|
100
|
-
.filter((
|
|
101
|
-
.map((state) => `${state}=${
|
|
152
|
+
function nonZeroCounts(counts) {
|
|
153
|
+
const summary = Object.entries(counts)
|
|
154
|
+
.filter(([, count]) => count > 0)
|
|
155
|
+
.map(([state, count]) => `${state}=${count}`)
|
|
102
156
|
.join(", ");
|
|
103
157
|
return summary || "none";
|
|
104
158
|
}
|
|
@@ -106,10 +160,16 @@ function writeHumanStatus(status, action) {
|
|
|
106
160
|
if (action)
|
|
107
161
|
process.stdout.write(`Action: ${action}\n`);
|
|
108
162
|
process.stdout.write(`Run: ${status.run_id}\n`);
|
|
109
|
-
|
|
163
|
+
if (status.schema_version === "intentdna.run_status.v1") {
|
|
164
|
+
process.stdout.write(`Workflow: ${status.workflow_id}\n`);
|
|
165
|
+
process.stdout.write(`Tasks: ${nonZeroCounts(status.task_counts)}\n`);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
process.stdout.write(`Target: ${status.target.kind}:${status.target.key}\n`);
|
|
169
|
+
process.stdout.write(`Activations: ${nonZeroCounts(status.activation_counts)}\n`);
|
|
170
|
+
}
|
|
110
171
|
process.stdout.write(`Status: ${status.status}\n`);
|
|
111
172
|
process.stdout.write(`Ledger version: ${status.record_version}\n`);
|
|
112
|
-
process.stdout.write(`Tasks: ${nonZeroTaskCounts(status.task_counts)}\n`);
|
|
113
173
|
process.stdout.write(`Attempts: ${status.attempt_count}; results: ${status.result_count}\n`);
|
|
114
174
|
if (status.cancellation) {
|
|
115
175
|
process.stdout.write(`Cancellation: ${status.cancellation.requested_at}`
|
|
@@ -121,52 +181,93 @@ function writeHumanStatus(status, action) {
|
|
|
121
181
|
}
|
|
122
182
|
}
|
|
123
183
|
function writeInspection(snapshot, json) {
|
|
124
|
-
const projection =
|
|
184
|
+
const projection = legacyInspectionProjection(snapshot);
|
|
125
185
|
if (json) {
|
|
126
186
|
writeJson(projection);
|
|
127
187
|
return;
|
|
128
188
|
}
|
|
129
|
-
writeHumanStatus(
|
|
189
|
+
writeHumanStatus(legacyStatusProjection(snapshot));
|
|
190
|
+
const attempts = projection.attempts;
|
|
130
191
|
process.stdout.write("\nAttempts:\n");
|
|
131
|
-
if (
|
|
192
|
+
if (attempts.length === 0)
|
|
132
193
|
process.stdout.write(" (none)\n");
|
|
133
|
-
|
|
134
|
-
for (const attempt of projection.attempts) {
|
|
194
|
+
for (const attempt of attempts) {
|
|
135
195
|
process.stdout.write(` ${attempt.attempt_id} step=${attempt.step_id}`
|
|
136
196
|
+ ` worker=${attempt.worker_session_id} phase=${attempt.phase}`
|
|
137
197
|
+ ` pid=${attempt.process_id ?? "-"}`
|
|
138
198
|
+ ` outcome=${attempt.terminal_outcome?.kind ?? "none"}\n`);
|
|
139
199
|
}
|
|
140
200
|
process.stdout.write("\nResolved handoffs:\n");
|
|
141
|
-
if (projection.handoffs.length === 0)
|
|
201
|
+
if (projection.handoffs.length === 0)
|
|
142
202
|
process.stdout.write(" (none)\n");
|
|
143
|
-
}
|
|
144
203
|
for (const handoff of projection.handoffs) {
|
|
145
204
|
process.stdout.write(` attempt=${handoff.attempt_id} worker=${handoff.worker_session_id}`
|
|
146
205
|
+ ` binding=${JSON.stringify(handoff.binding)}\n`);
|
|
147
206
|
}
|
|
148
207
|
process.stdout.write("\nCommitted results:\n");
|
|
149
|
-
if (projection.results.length === 0)
|
|
208
|
+
if (projection.results.length === 0)
|
|
150
209
|
process.stdout.write(" (none)\n");
|
|
151
|
-
}
|
|
152
210
|
for (const result of projection.results) {
|
|
153
211
|
process.stdout.write(` ${result.result_id} attempt=${result.attempt_id}`
|
|
154
212
|
+ ` worker=${result.worker_session_id}`
|
|
155
213
|
+ ` outputs=${JSON.stringify(result.outputs)}\n`);
|
|
156
214
|
}
|
|
157
215
|
process.stdout.write("\nEvents:\n");
|
|
158
|
-
if (projection.events.length === 0)
|
|
216
|
+
if (projection.events.length === 0)
|
|
159
217
|
process.stdout.write(" (none)\n");
|
|
218
|
+
for (const event of projection.events) {
|
|
219
|
+
process.stdout.write(` ${event.sequence} ${event.type}`
|
|
220
|
+
+ ` step=${event.step_id ?? "-"} attempt=${event.attempt_id ?? "-"}`
|
|
221
|
+
+ ` payload=${JSON.stringify(event.payload)}\n`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function writeCanonicalInspection(view, json) {
|
|
225
|
+
const projection = canonicalInspectionProjection(view);
|
|
226
|
+
if (json) {
|
|
227
|
+
writeJson(projection);
|
|
228
|
+
return;
|
|
160
229
|
}
|
|
230
|
+
writeHumanStatus(canonicalViewStatusProjection(view));
|
|
231
|
+
process.stdout.write("\nAttempts:\n");
|
|
232
|
+
if (projection.attempts.length === 0)
|
|
233
|
+
process.stdout.write(" (none)\n");
|
|
234
|
+
for (const attempt of projection.attempts) {
|
|
235
|
+
const executor = attempt.executor?.kind ?? "unattached";
|
|
236
|
+
process.stdout.write(` ${attempt.attempt_id} step=${attempt.step_id}`
|
|
237
|
+
+ ` activation=${attempt.activation_id}`
|
|
238
|
+
+ ` executor=${executor} phase=${attempt.phase}`
|
|
239
|
+
+ ` outcome=${attempt.terminal_outcome?.kind ?? "none"}\n`);
|
|
240
|
+
}
|
|
241
|
+
process.stdout.write("\nResult references:\n");
|
|
242
|
+
if (projection.result_refs.length === 0)
|
|
243
|
+
process.stdout.write(" (none)\n");
|
|
244
|
+
for (const result of projection.result_refs) {
|
|
245
|
+
process.stdout.write(` ${result.result_id} activation=${result.activation_id}`
|
|
246
|
+
+ ` attempt=${result.attempt_id} digest=${result.result_digest}\n`);
|
|
247
|
+
}
|
|
248
|
+
process.stdout.write("\nEvents:\n");
|
|
249
|
+
if (projection.events.length === 0)
|
|
250
|
+
process.stdout.write(" (none)\n");
|
|
161
251
|
for (const event of projection.events) {
|
|
162
252
|
process.stdout.write(` ${event.sequence} ${event.type}`
|
|
163
253
|
+ ` step=${event.step_id ?? "-"} attempt=${event.attempt_id ?? "-"}`
|
|
164
|
-
+ ` worker=${event.worker_session_id ?? "-"}`
|
|
165
254
|
+ ` payload=${JSON.stringify(event.payload)}\n`);
|
|
166
255
|
}
|
|
167
256
|
}
|
|
168
|
-
function
|
|
169
|
-
const status =
|
|
257
|
+
function writeLegacyOperation(action, snapshot, json) {
|
|
258
|
+
const status = legacyStatusProjection(snapshot);
|
|
259
|
+
if (json) {
|
|
260
|
+
writeJson({
|
|
261
|
+
schema_version: "intentdna.run_lifecycle_operation.v1",
|
|
262
|
+
action,
|
|
263
|
+
status,
|
|
264
|
+
});
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
writeHumanStatus(status, action);
|
|
268
|
+
}
|
|
269
|
+
export function writeCanonicalOperation(action, view, json) {
|
|
270
|
+
const status = canonicalViewStatusProjection(view);
|
|
170
271
|
if (json) {
|
|
171
272
|
writeJson({
|
|
172
273
|
schema_version: "intentdna.run_lifecycle_operation.v1",
|
|
@@ -180,6 +281,15 @@ function writeOperation(action, snapshot, json) {
|
|
|
180
281
|
function operationExitCode(snapshot) {
|
|
181
282
|
return snapshot.run.status === "failed" ? 1 : 0;
|
|
182
283
|
}
|
|
284
|
+
function canonicalOperationExitCode(view) {
|
|
285
|
+
return view.run.status === "failed" ? 1 : 0;
|
|
286
|
+
}
|
|
287
|
+
async function requireCanonicalService(options) {
|
|
288
|
+
const service = options.service ?? await options.service_factory?.();
|
|
289
|
+
if (!service)
|
|
290
|
+
throw new LifecycleUsageError("canonical run operation requires a service");
|
|
291
|
+
return service;
|
|
292
|
+
}
|
|
183
293
|
async function handle(operation) {
|
|
184
294
|
try {
|
|
185
295
|
return await operation();
|
|
@@ -190,20 +300,13 @@ async function handle(operation) {
|
|
|
190
300
|
return error instanceof LifecycleUsageError ? 2 : 1;
|
|
191
301
|
}
|
|
192
302
|
}
|
|
193
|
-
export async function runLifecycleStart(options) {
|
|
194
|
-
return handle(async () => {
|
|
195
|
-
validateStartRun(options.run);
|
|
196
|
-
await options.controller.createRun({ run: options.run });
|
|
197
|
-
const snapshot = await options.controller.runUntilTerminal(options.run.run_id);
|
|
198
|
-
writeOperation("start", snapshot, options.json === true);
|
|
199
|
-
return operationExitCode(snapshot);
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
303
|
export async function runLifecycleStatus(options) {
|
|
203
304
|
return handle(async () => {
|
|
204
305
|
requireRunId(options.run_id);
|
|
205
|
-
const
|
|
206
|
-
const projection =
|
|
306
|
+
const stored = await options.run_store.requireAnyRun(options.run_id);
|
|
307
|
+
const projection = stored.schema_version === LEGACY_RUN_STORE_SCHEMA_VERSION
|
|
308
|
+
? legacyStatusProjection(stored)
|
|
309
|
+
: canonicalViewStatusProjection(await (await requireCanonicalService(options)).inspect(options.run_id));
|
|
207
310
|
if (options.json === true)
|
|
208
311
|
writeJson(projection);
|
|
209
312
|
else
|
|
@@ -214,28 +317,59 @@ export async function runLifecycleStatus(options) {
|
|
|
214
317
|
export async function runLifecycleInspect(options) {
|
|
215
318
|
return handle(async () => {
|
|
216
319
|
requireRunId(options.run_id);
|
|
217
|
-
const
|
|
218
|
-
|
|
320
|
+
const stored = await options.run_store.requireAnyRun(options.run_id);
|
|
321
|
+
if (stored.schema_version === LEGACY_RUN_STORE_SCHEMA_VERSION) {
|
|
322
|
+
writeInspection(stored, options.json === true);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
const audit = await (await requireCanonicalService(options)).inspectAudit(options.run_id);
|
|
326
|
+
writeCanonicalInspection(audit, options.json === true);
|
|
327
|
+
}
|
|
219
328
|
return 0;
|
|
220
329
|
});
|
|
221
330
|
}
|
|
222
331
|
export async function runLifecycleResume(options) {
|
|
223
332
|
return handle(async () => {
|
|
224
333
|
requireRunId(options.run_id);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
334
|
+
const stored = await options.run_store.requireAnyRun(options.run_id);
|
|
335
|
+
if (stored.schema_version === LEGACY_RUN_STORE_SCHEMA_VERSION) {
|
|
336
|
+
throw new RunStoreError("unsupported_legacy_run", `unsupported_legacy_run: legacy run ${options.run_id} cannot resume through the canonical scheduler`);
|
|
337
|
+
}
|
|
338
|
+
const service = await requireCanonicalService(options);
|
|
339
|
+
const view = options.drive
|
|
340
|
+
? await options.drive(service, options.run_id)
|
|
341
|
+
: await service.resume(options.run_id);
|
|
342
|
+
writeCanonicalOperation("resume", view, options.json === true);
|
|
343
|
+
return canonicalOperationExitCode(view);
|
|
228
344
|
});
|
|
229
345
|
}
|
|
230
346
|
export async function runLifecycleCancel(options) {
|
|
231
347
|
return handle(async () => {
|
|
232
348
|
requireRunId(options.run_id);
|
|
233
349
|
const reason = options.reason ?? null;
|
|
234
|
-
|
|
235
|
-
|
|
350
|
+
validateReason(reason);
|
|
351
|
+
const stored = await options.run_store.requireAnyRun(options.run_id);
|
|
352
|
+
if (stored.schema_version === LEGACY_RUN_STORE_SCHEMA_VERSION) {
|
|
353
|
+
const snapshot = await options.run_store.cancelLegacyRun(options.run_id, reason);
|
|
354
|
+
writeLegacyOperation("cancel", snapshot, options.json === true);
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
357
|
+
const view = await (await requireCanonicalService(options)).cancel(options.run_id, reason);
|
|
358
|
+
writeCanonicalOperation("cancel", view, options.json === true);
|
|
359
|
+
return 0;
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
export async function runLifecycleClose(options) {
|
|
363
|
+
return handle(async () => {
|
|
364
|
+
requireRunId(options.run_id);
|
|
365
|
+
const reason = options.reason ?? null;
|
|
366
|
+
validateReason(reason);
|
|
367
|
+
const stored = await options.run_store.requireAnyRun(options.run_id);
|
|
368
|
+
if (stored.schema_version !== LEGACY_RUN_STORE_SCHEMA_VERSION) {
|
|
369
|
+
throw new Error(`unsupported: canonical run ${options.run_id} does not support close`);
|
|
236
370
|
}
|
|
237
|
-
const snapshot = await options.
|
|
238
|
-
|
|
371
|
+
const snapshot = await options.run_store.closeLegacyRun(options.run_id, reason);
|
|
372
|
+
writeLegacyOperation("close", snapshot, options.json === true);
|
|
239
373
|
return 0;
|
|
240
374
|
});
|
|
241
375
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RunView } from "../../runtime/canonical-run-service.js";
|
|
2
|
+
import type { CanonicalRuntimeProjection, CanonicalTarget } from "../../runtime/canonical-target-compiler.js";
|
|
3
|
+
import { type CompiledIRFile } from "../../runtime/plugin-adapter.js";
|
|
4
|
+
import type { PushDriverAttemptContext, PushDriverAttemptLifecycleHooks } from "../../runtime/push-driver.js";
|
|
5
|
+
export interface ObservedCanonicalRunMetadata {
|
|
6
|
+
readonly project_directory: string;
|
|
7
|
+
readonly target: CanonicalTarget;
|
|
8
|
+
readonly task_id: string;
|
|
9
|
+
readonly workflow_asset: string | null;
|
|
10
|
+
readonly runtime_projection: CompiledIRFile;
|
|
11
|
+
}
|
|
12
|
+
export declare function durableRuntimeProjection(projection: CanonicalRuntimeProjection): CompiledIRFile;
|
|
13
|
+
export declare function syncRuntimeProjection(projectDirectory: string, agentsDirectory: string, projection: CanonicalRuntimeProjection, compiled: CompiledIRFile): Promise<void>;
|
|
14
|
+
export declare function createRunAttemptObservers(readMetadata: (context: PushDriverAttemptContext) => ObservedCanonicalRunMetadata): PushDriverAttemptLifecycleHooks;
|
|
15
|
+
export declare function writeTerminalDriverFailure(view: RunView): void;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { DNAStateManager } from "../../hooks/state-manager.js";
|
|
4
|
+
import { appendEvidenceCaptureEvent, EVIDENCE_CAPTURE_SCHEMA_VERSION, writeCanonicalHookRuntimeProjection, } from "../../hooks/state.js";
|
|
5
|
+
import { compileAllRolesToAgentMD, writeAgentMDFiles } from "../../runtime/agent-md.js";
|
|
6
|
+
import { durableCanonicalRuntimeProjection } from "../../runtime/canonical-runtime-projection.js";
|
|
7
|
+
import { writeCompiledIR, } from "../../runtime/plugin-adapter.js";
|
|
8
|
+
import { WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES } from "../../runtime/workflow-runtime-manifest.js";
|
|
9
|
+
function logicalWorkflowStep(stepId) {
|
|
10
|
+
const separator = stepId.lastIndexOf("/");
|
|
11
|
+
const component = separator === -1 ? null : stepId.slice(0, separator);
|
|
12
|
+
const localStepId = separator === -1 ? stepId : stepId.slice(separator + 1);
|
|
13
|
+
const prefix = "worker:";
|
|
14
|
+
if (!localStepId.startsWith(prefix) || localStepId.length === prefix.length) {
|
|
15
|
+
throw new Error(`canonical worker step id is invalid: ${stepId}`);
|
|
16
|
+
}
|
|
17
|
+
return { component, step_id: localStepId.slice(prefix.length) };
|
|
18
|
+
}
|
|
19
|
+
function workflowBindingForStep(metadata, stepId) {
|
|
20
|
+
const manifest = metadata.runtime_projection.workflow_runtime;
|
|
21
|
+
if (!manifest)
|
|
22
|
+
throw new Error("Missing canonical workflow runtime manifest");
|
|
23
|
+
const logical = logicalWorkflowStep(stepId);
|
|
24
|
+
const workflowName = metadata.target.kind === "workflow"
|
|
25
|
+
? metadata.target.key
|
|
26
|
+
: Object.keys(manifest.workflows).filter((name) => (name === logical.component || name.endsWith(`_${logical.component}`)))[0];
|
|
27
|
+
if (!workflowName || !manifest.workflows[workflowName]) {
|
|
28
|
+
throw new Error(`Missing runtime binding for canonical step ${stepId}`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
manifest,
|
|
32
|
+
binding: manifest.workflows[workflowName],
|
|
33
|
+
workflow_name: workflowName,
|
|
34
|
+
logical_step_id: logical.step_id,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export function durableRuntimeProjection(projection) {
|
|
38
|
+
return durableCanonicalRuntimeProjection(projection);
|
|
39
|
+
}
|
|
40
|
+
export async function syncRuntimeProjection(projectDirectory, agentsDirectory, projection, compiled) {
|
|
41
|
+
await writeAgentMDFiles(compileAllRolesToAgentMD(projection.roles, projection.constraint_ir), agentsDirectory);
|
|
42
|
+
await writeCompiledIR(projectDirectory, compiled);
|
|
43
|
+
}
|
|
44
|
+
async function writeAttemptState(context, metadata) {
|
|
45
|
+
const compiled = metadata.runtime_projection;
|
|
46
|
+
const { manifest, binding, workflow_name: workflowName, logical_step_id: logicalStepId } = workflowBindingForStep(metadata, context.packet.step_id);
|
|
47
|
+
if (context.packet.workspace.isolation === "worktree") {
|
|
48
|
+
await writeAgentMDFiles(compileAllRolesToAgentMD(compiled.roles ?? {}, compiled.ir), join(context.packet.workspace.working_directory, ".claude", "agents"));
|
|
49
|
+
await writeCompiledIR(context.packet.workspace.working_directory, compiled);
|
|
50
|
+
}
|
|
51
|
+
const state = new DNAStateManager(context.packet.workspace.working_directory, context.worker_session_id);
|
|
52
|
+
const compiledIRHash = compiled.compiled_ir_hash ?? compiled.ir.compiled_ir_hash;
|
|
53
|
+
if (!compiledIRHash || manifest.compiled_ir_hash !== compiledIRHash) {
|
|
54
|
+
throw new Error("Canonical Hook projection is missing its pinned compiled IR hash");
|
|
55
|
+
}
|
|
56
|
+
await writeCanonicalHookRuntimeProjection(context.packet.workspace.working_directory, {
|
|
57
|
+
schema_version: "intentdna.canonical_hook_projection.v2",
|
|
58
|
+
run_id: context.packet.run_id,
|
|
59
|
+
step_id: context.packet.step_id,
|
|
60
|
+
attempt_id: context.packet.attempt_id,
|
|
61
|
+
worker_session_id: context.worker_session_id,
|
|
62
|
+
compiled_ir_hash: compiledIRHash,
|
|
63
|
+
compiled_ir: compiled,
|
|
64
|
+
lease_token: context.lease.claim_token,
|
|
65
|
+
owner_id: context.lease.owner_id,
|
|
66
|
+
});
|
|
67
|
+
const inputs = {};
|
|
68
|
+
for (const name of binding.runtime_input_refs) {
|
|
69
|
+
const value = WORKFLOW_RUNTIME_INPUT_VARIABLE_NAMES.has(name)
|
|
70
|
+
? metadata.task_id
|
|
71
|
+
: context.binding.validated_inputs[name];
|
|
72
|
+
if (typeof value === "string")
|
|
73
|
+
inputs[name] = value;
|
|
74
|
+
}
|
|
75
|
+
await state.writeWorkflowState({
|
|
76
|
+
active: true,
|
|
77
|
+
workflow_asset: binding.workflow_asset,
|
|
78
|
+
workflow: workflowName,
|
|
79
|
+
current_step: logicalStepId,
|
|
80
|
+
current_role: context.packet.role,
|
|
81
|
+
iteration: context.packet.workflow_round,
|
|
82
|
+
session_id: context.worker_session_id,
|
|
83
|
+
worker_session_id: context.worker_session_id,
|
|
84
|
+
run_id: context.packet.run_id,
|
|
85
|
+
step_id: context.packet.step_id,
|
|
86
|
+
attempt_id: context.packet.attempt_id,
|
|
87
|
+
started_at: context.packet.created_at,
|
|
88
|
+
inputs,
|
|
89
|
+
resolved_variables: binding.resolved_variables,
|
|
90
|
+
resolved_variables_source: "compiled_manifest",
|
|
91
|
+
workflow_runtime_manifest_hash: manifest.manifest_hash,
|
|
92
|
+
workflow_runtime_compiled_ir_hash: manifest.compiled_ir_hash,
|
|
93
|
+
workflow_inputs_pinned: true,
|
|
94
|
+
});
|
|
95
|
+
return state;
|
|
96
|
+
}
|
|
97
|
+
async function recordEvidence(context, response, metadata) {
|
|
98
|
+
const { binding, workflow_name: workflowName, logical_step_id: logicalStepId } = workflowBindingForStep(metadata, context.packet.step_id);
|
|
99
|
+
const result = response.result;
|
|
100
|
+
const status = result.terminal_outcome.kind === "worker_process"
|
|
101
|
+
? result.terminal_outcome.status
|
|
102
|
+
: result.terminal_outcome.kind;
|
|
103
|
+
const processOutcome = result.terminal_outcome.kind === "worker_process"
|
|
104
|
+
? result.terminal_outcome.process
|
|
105
|
+
: null;
|
|
106
|
+
const startedAt = Date.parse(result.started_at);
|
|
107
|
+
const completedAt = Date.parse(result.completed_at);
|
|
108
|
+
const persisted = await appendEvidenceCaptureEvent(metadata.project_directory, {
|
|
109
|
+
schema_version: EVIDENCE_CAPTURE_SCHEMA_VERSION,
|
|
110
|
+
event_id: `c5:${randomUUID()}`,
|
|
111
|
+
captured_at: result.committed_at,
|
|
112
|
+
project_root: metadata.project_directory,
|
|
113
|
+
source: "dna_cli",
|
|
114
|
+
workflow_asset: metadata.workflow_asset ?? "unknown",
|
|
115
|
+
workflow_id: workflowName,
|
|
116
|
+
run_id: context.packet.run_id,
|
|
117
|
+
step_id: logicalStepId,
|
|
118
|
+
fact: "result",
|
|
119
|
+
summary: `dna run recorded ${status === "succeeded" ? "success" : "failure"} for workflow step ${logicalStepId}`,
|
|
120
|
+
result: status === "succeeded" ? "success" : "failure",
|
|
121
|
+
exit_code: processOutcome !== null && "exit_code" in processOutcome
|
|
122
|
+
? processOutcome.exit_code ?? undefined
|
|
123
|
+
: undefined,
|
|
124
|
+
duration_ms: Number.isFinite(startedAt) && Number.isFinite(completedAt)
|
|
125
|
+
? Math.max(0, completedAt - startedAt)
|
|
126
|
+
: undefined,
|
|
127
|
+
});
|
|
128
|
+
if (!persisted)
|
|
129
|
+
throw new Error("evidence capture event was not persisted");
|
|
130
|
+
}
|
|
131
|
+
function writeObservationFailure(kind, error) {
|
|
132
|
+
process.stderr.write(`Warning: post-submit ${kind} observation failed: `
|
|
133
|
+
+ `${error instanceof Error ? error.message : String(error)}\n`);
|
|
134
|
+
}
|
|
135
|
+
function writeVerificationDiagnostics(context, response) {
|
|
136
|
+
const outcome = response.result.terminal_outcome;
|
|
137
|
+
if (outcome.kind === "worker_process"
|
|
138
|
+
&& outcome.status === "failed"
|
|
139
|
+
&& outcome.reason_code !== null
|
|
140
|
+
&& [
|
|
141
|
+
"unsupported_output_contract",
|
|
142
|
+
"invalid_provider_output",
|
|
143
|
+
"driver_postprocess_failed",
|
|
144
|
+
].includes(outcome.reason_code)) {
|
|
145
|
+
process.stderr.write(`Error: ${outcome.reason_code}: ${outcome.message ?? "provider output post-processing failed"}\n`);
|
|
146
|
+
}
|
|
147
|
+
const verifierById = new Map(context.run.plan.plan.verifier_plan.map((verifier) => [verifier.verifier_id, verifier]));
|
|
148
|
+
const logicalStepId = logicalWorkflowStep(context.packet.step_id).step_id;
|
|
149
|
+
for (const result of response.result.verification_results) {
|
|
150
|
+
if (result.passed)
|
|
151
|
+
continue;
|
|
152
|
+
const verifier = verifierById.get(result.verifier_id);
|
|
153
|
+
if (!verifier) {
|
|
154
|
+
process.stderr.write(`Verification failed: ${result.verifier_id}`
|
|
155
|
+
+ ` reason=${result.reason_code} (step: ${logicalStepId})\n`);
|
|
156
|
+
}
|
|
157
|
+
else if (verifier.kind === "checkpoint") {
|
|
158
|
+
process.stderr.write(`Checkpoint failed: ${verifier.assertion}`
|
|
159
|
+
+ `${verifier.message ? ` - ${verifier.message}` : ""}`
|
|
160
|
+
+ ` (step: ${logicalStepId})\n`);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
process.stderr.write(`Completion failed: ${result.reason_code} (step: ${logicalStepId})\n`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
export function createRunAttemptObservers(readMetadata) {
|
|
168
|
+
const states = new Map();
|
|
169
|
+
return {
|
|
170
|
+
before_execute: async (context) => {
|
|
171
|
+
states.set(context.lease.attempt_id, await writeAttemptState(context, readMetadata(context)));
|
|
172
|
+
},
|
|
173
|
+
after_submit: async (context, _execution, response) => {
|
|
174
|
+
try {
|
|
175
|
+
await recordEvidence(context, response, readMetadata(context));
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
writeObservationFailure("evidence", error);
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
writeVerificationDiagnostics(context, response);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
writeObservationFailure("verification diagnostic", error);
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
await states.get(context.lease.attempt_id)?.cleanup().catch(() => undefined);
|
|
188
|
+
states.delete(context.lease.attempt_id);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
on_attempt_error: async (context) => {
|
|
192
|
+
if (!context)
|
|
193
|
+
return;
|
|
194
|
+
await states.get(context.lease.attempt_id)?.cleanup().catch(() => undefined);
|
|
195
|
+
states.delete(context.lease.attempt_id);
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export function writeTerminalDriverFailure(view) {
|
|
200
|
+
if (view.run.status !== "failed")
|
|
201
|
+
return;
|
|
202
|
+
const failure = [...view.attempts].reverse().find((attempt) => (attempt.phase === "terminal"
|
|
203
|
+
&& attempt.terminal_outcome?.kind === "worker_process"
|
|
204
|
+
&& attempt.terminal_outcome.status === "failed"
|
|
205
|
+
&& attempt.terminal_outcome.reason_code === "driver_prelaunch_failed"));
|
|
206
|
+
if (failure?.terminal_outcome?.kind !== "worker_process")
|
|
207
|
+
return;
|
|
208
|
+
process.stderr.write(`Error: ${failure.terminal_outcome.reason_code}: `
|
|
209
|
+
+ `${failure.terminal_outcome.message ?? "provider launch preparation failed"}\n`);
|
|
210
|
+
}
|
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* The CLI compiles one workflow into Controller definitions, persists one
|
|
5
|
-
* durable run ledger, and launches one disposable provider session per Step
|
|
6
|
-
* attempt. Cross-Step inputs come only from committed results.
|
|
7
|
-
*/
|
|
8
|
-
import type { TransitionRule } from "../../schema/types.js";
|
|
1
|
+
/** Intent DNA - canonical dna run CLI transport. */
|
|
2
|
+
import type { AttemptExecutionAuthorityPort, ExecutionRecoveryProbeRequest } from "../../runtime/execution-authority.js";
|
|
3
|
+
import { type LocalExecutionAuthorityBinding, type LocalExecutionBindRequest, type LocalExecutionPreparationRequest, type LocalExecutionRecoveryBinding, type LocalPreparedExecutionHandle } from "../../runtime/local-execution-authority.js";
|
|
9
4
|
import { type RunLifecycleAction } from "./run-lifecycle.js";
|
|
10
5
|
export type RunProviderKind = "claude" | "codex";
|
|
11
6
|
export interface RunOptions {
|
|
@@ -13,6 +8,7 @@ export interface RunOptions {
|
|
|
13
8
|
runId?: string;
|
|
14
9
|
dnaFiles: string[];
|
|
15
10
|
workflowName?: string;
|
|
11
|
+
controllerName?: string;
|
|
16
12
|
taskId?: string;
|
|
17
13
|
context?: string;
|
|
18
14
|
vars?: Record<string, string>;
|
|
@@ -31,17 +27,17 @@ export interface RunOptions {
|
|
|
31
27
|
reason?: string | null;
|
|
32
28
|
json?: boolean;
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
export interface RunExecutionAuthority extends AttemptExecutionAuthorityPort {
|
|
31
|
+
readonly authority_instance_id: string;
|
|
32
|
+
assertExecutionSupported(): void;
|
|
33
|
+
prepareExecution(request: LocalExecutionPreparationRequest): LocalPreparedExecutionHandle;
|
|
34
|
+
abandonPreparedExecution(handle: LocalPreparedExecutionHandle): void;
|
|
35
|
+
recoverPreparedExecution(request: LocalExecutionPreparationRequest): LocalPreparedExecutionHandle | null;
|
|
36
|
+
bindExecution(handle: LocalPreparedExecutionHandle, request: LocalExecutionBindRequest): LocalExecutionAuthorityBinding;
|
|
37
|
+
recoverExecution(request: ExecutionRecoveryProbeRequest): LocalExecutionRecoveryBinding;
|
|
38
|
+
}
|
|
39
|
+
export interface RunCompositionOptions {
|
|
40
|
+
readonly execution_authority_factory?: (rootDirectory: string) => RunExecutionAuthority;
|
|
43
41
|
}
|
|
44
42
|
export declare function substituteTemplate(template: string, vars: Record<string, string>): string;
|
|
45
|
-
export declare function
|
|
46
|
-
export declare function checkRetryNeeded(stepResults: Map<string, StepResult>, transitions: TransitionRule[]): boolean;
|
|
47
|
-
export declare function runRun(opts: RunOptions): Promise<number>;
|
|
43
|
+
export declare function runRun(opts: RunOptions, composition?: RunCompositionOptions): Promise<number>;
|