intentdna 1.8.7 → 1.9.0-rc.2
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/dist/cli/commands/candidate-promotion.d.ts +15 -0
- package/dist/cli/commands/candidate-promotion.js +197 -0
- package/dist/cli/commands/run-lifecycle.js +1 -0
- package/dist/cli/commands/run.js +76 -42
- package/dist/cli/commands/setup.d.ts +10 -0
- package/dist/cli/commands/setup.js +129 -36
- package/dist/cli/commands/source-assets.d.ts +32 -0
- package/dist/cli/commands/source-assets.js +154 -0
- package/dist/cli/commands/sync.js +52 -12
- package/dist/cli/commands/templates.js +59 -37
- package/dist/cli/index.js +161 -5
- package/dist/compiler/cascade.js +1 -1
- package/dist/hooks/cli.d.ts +6 -0
- package/dist/hooks/cli.js +118 -10
- package/dist/hooks/enforcement-boundary.d.ts +1 -1
- package/dist/hooks/protocol.d.ts +1 -1
- package/dist/hooks/state.d.ts +5 -0
- package/dist/hooks/state.js +21 -6
- package/dist/mcp/tools-state.js +251 -15
- package/dist/runtime/claude-sync-target.d.ts +2 -0
- package/dist/runtime/claude-sync-target.js +1 -1
- package/dist/runtime/evaluator.d.ts +13 -0
- package/dist/runtime/evaluator.js +46 -0
- package/dist/runtime/index.d.ts +8 -4
- package/dist/runtime/index.js +4 -2
- package/dist/runtime/plugin-adapter.d.ts +14 -1
- package/dist/runtime/plugin-adapter.js +23 -9
- package/dist/runtime/run-contracts.d.ts +5 -3
- package/dist/runtime/run-contracts.js +11 -1
- package/dist/runtime/run-controller.d.ts +15 -1
- package/dist/runtime/run-controller.js +235 -94
- package/dist/runtime/skill-adapter.js +24 -53
- package/dist/runtime/verifier.js +101 -2
- package/dist/runtime/workflow-plan-adapter.d.ts +2 -1
- package/dist/runtime/workflow-plan-adapter.js +78 -11
- package/dist/runtime/workflow-runtime-manifest.d.ts +48 -0
- package/dist/runtime/workflow-runtime-manifest.js +196 -0
- package/dist/runtime/workflow-variable-scopes.d.ts +12 -0
- package/dist/runtime/workflow-variable-scopes.js +55 -0
- package/dist/schema/types.d.ts +9 -0
- package/dist/schema/validate.js +29 -1
- package/dist/sources/authoring-packet.d.ts +8 -0
- package/dist/sources/authoring-packet.js +85 -0
- package/dist/sources/discovery.d.ts +7 -0
- package/dist/sources/discovery.js +150 -0
- package/dist/sources/license.d.ts +5 -0
- package/dist/sources/license.js +94 -0
- package/dist/sources/manifest.d.ts +11 -0
- package/dist/sources/manifest.js +84 -0
- package/dist/sources/skill-markdown.d.ts +6 -0
- package/dist/sources/skill-markdown.js +174 -0
- package/dist/sources/types.d.ts +125 -0
- package/dist/sources/types.js +1 -0
- package/dist/templates/asset-refinery.dna.yaml +192 -0
- package/dist/templates/flutter-behavior-lock.dna.yaml +17 -0
- package/dist/templates/flutter-refactoring-rescue.dna.yaml +11 -0
- package/dist/templates/flutter-rewrite.dna.yaml +154 -28
- package/dist/templates/metadata.js +2 -0
- package/dist/templates/multi-agent-handoff-coordination.dna.yaml +102 -37
- package/dist/templates/persistent-executor.dna.yaml +61 -3
- package/dist/templates/research-improvement.dna.yaml +295 -0
- package/dist/templates/subagent-parallel.dna.yaml +90 -17
- package/dist/templates/workflow-evidence-review.dna.yaml +8 -0
- package/dist/workflow/candidate-projection.d.ts +33 -0
- package/dist/workflow/candidate-projection.js +70 -0
- package/package.json +3 -1
- package/scripts/sync-release-plugin.cjs +87 -0
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
{
|
|
10
10
|
"name": "intentdna",
|
|
11
11
|
"description": "Declarative policy layer for AI agent behavior with plugin-managed hook runtime for Claude Code.",
|
|
12
|
-
"version": "1.
|
|
12
|
+
"version": "1.9.0-rc.2",
|
|
13
13
|
"author": {
|
|
14
14
|
"name": "Samuel"
|
|
15
15
|
},
|
|
@@ -25,5 +25,5 @@
|
|
|
25
25
|
]
|
|
26
26
|
}
|
|
27
27
|
],
|
|
28
|
-
"version": "1.
|
|
28
|
+
"version": "1.9.0-rc.2"
|
|
29
29
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface PromoteCandidateOptions {
|
|
2
|
+
projectDir: string;
|
|
3
|
+
candidatePath: string;
|
|
4
|
+
templateName: string;
|
|
5
|
+
}
|
|
6
|
+
export interface CandidatePromotionResult {
|
|
7
|
+
schema_version: "intentdna.candidate_promotion_result.v1";
|
|
8
|
+
candidate_path: string;
|
|
9
|
+
template_path: string;
|
|
10
|
+
receipt_path: string;
|
|
11
|
+
candidate_sha256: string;
|
|
12
|
+
template_sha256: string;
|
|
13
|
+
exact_bytes_preserved: true;
|
|
14
|
+
}
|
|
15
|
+
export declare function promoteCandidate(options: PromoteCandidateOptions): Promise<CandidatePromotionResult>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, open, readFile, realpath, rm, stat, } from "node:fs/promises";
|
|
3
|
+
import { basename, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { formatDiagnostics, hasDiagnosticErrors } from "../../compiler/diagnostics.js";
|
|
5
|
+
import { loadDNAWithDiagnostics } from "../../compiler/input-resolver.js";
|
|
6
|
+
const CANDIDATE_SUFFIX = ".candidate.dna.yaml";
|
|
7
|
+
const SAFE_TEMPLATE_NAME = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
|
|
8
|
+
function sha256(bytes) {
|
|
9
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
10
|
+
}
|
|
11
|
+
function isErrnoException(error) {
|
|
12
|
+
return error instanceof Error && "code" in error;
|
|
13
|
+
}
|
|
14
|
+
function isStrictlyWithin(parent, child) {
|
|
15
|
+
const pathFromParent = relative(parent, child);
|
|
16
|
+
return pathFromParent !== ""
|
|
17
|
+
&& pathFromParent !== ".."
|
|
18
|
+
&& !pathFromParent.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`)
|
|
19
|
+
&& !isAbsolute(pathFromParent);
|
|
20
|
+
}
|
|
21
|
+
function validateTemplateName(templateName) {
|
|
22
|
+
if (!SAFE_TEMPLATE_NAME.test(templateName)) {
|
|
23
|
+
throw new Error("Invalid template name: use one safe path component containing only letters, numbers, hyphens, or underscores");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function assertPathMissing(path, label) {
|
|
27
|
+
try {
|
|
28
|
+
await lstat(path);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
if (isErrnoException(error) && error.code === "ENOENT")
|
|
32
|
+
return;
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`${label} already exists; overwrite is not allowed`);
|
|
36
|
+
}
|
|
37
|
+
async function ensureSafeTemplatesDirectory(templatesDir, dnaRealPath) {
|
|
38
|
+
try {
|
|
39
|
+
const entry = await lstat(templatesDir);
|
|
40
|
+
if (entry.isSymbolicLink()) {
|
|
41
|
+
throw new Error("Templates directory must not be a symlink");
|
|
42
|
+
}
|
|
43
|
+
if (!entry.isDirectory()) {
|
|
44
|
+
throw new Error("Templates path is not a directory");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!isErrnoException(error) || error.code !== "ENOENT")
|
|
49
|
+
throw error;
|
|
50
|
+
await mkdir(templatesDir);
|
|
51
|
+
}
|
|
52
|
+
const entry = await lstat(templatesDir);
|
|
53
|
+
if (entry.isSymbolicLink()) {
|
|
54
|
+
throw new Error("Templates directory must not be a symlink");
|
|
55
|
+
}
|
|
56
|
+
if (!entry.isDirectory()) {
|
|
57
|
+
throw new Error("Templates path is not a directory");
|
|
58
|
+
}
|
|
59
|
+
const templatesRealPath = await realpath(templatesDir);
|
|
60
|
+
if (!isStrictlyWithin(dnaRealPath, templatesRealPath)) {
|
|
61
|
+
throw new Error("Templates directory escapes the project .dna directory");
|
|
62
|
+
}
|
|
63
|
+
return templatesRealPath;
|
|
64
|
+
}
|
|
65
|
+
async function removeCreatedPath(path) {
|
|
66
|
+
try {
|
|
67
|
+
await rm(path, { force: true });
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
export async function promoteCandidate(options) {
|
|
75
|
+
validateTemplateName(options.templateName);
|
|
76
|
+
const projectPath = resolve(options.projectDir);
|
|
77
|
+
const projectRealPath = await realpath(projectPath);
|
|
78
|
+
const dnaPath = join(projectPath, ".dna");
|
|
79
|
+
const authoringPath = join(dnaPath, "authoring");
|
|
80
|
+
const candidatePath = resolve(projectPath, options.candidatePath);
|
|
81
|
+
if (!isStrictlyWithin(authoringPath, candidatePath)) {
|
|
82
|
+
throw new Error("Candidate must be located within the project .dna/authoring directory");
|
|
83
|
+
}
|
|
84
|
+
const candidateFilename = basename(candidatePath);
|
|
85
|
+
if (!candidateFilename.endsWith(CANDIDATE_SUFFIX)
|
|
86
|
+
|| candidateFilename.length === CANDIDATE_SUFFIX.length) {
|
|
87
|
+
throw new Error(`Candidate filename must end with ${CANDIDATE_SUFFIX}`);
|
|
88
|
+
}
|
|
89
|
+
const dnaRealPath = await realpath(dnaPath);
|
|
90
|
+
if (!isStrictlyWithin(projectRealPath, dnaRealPath)) {
|
|
91
|
+
throw new Error("Project .dna directory escapes the project root");
|
|
92
|
+
}
|
|
93
|
+
const authoringRealPath = await realpath(authoringPath);
|
|
94
|
+
if (!isStrictlyWithin(dnaRealPath, authoringRealPath)) {
|
|
95
|
+
throw new Error("Authoring directory escapes the project .dna directory");
|
|
96
|
+
}
|
|
97
|
+
const candidateRealPath = await realpath(candidatePath);
|
|
98
|
+
if (!isStrictlyWithin(authoringRealPath, candidateRealPath)) {
|
|
99
|
+
throw new Error("Candidate symlink or path escapes the project authoring directory");
|
|
100
|
+
}
|
|
101
|
+
const candidateEntry = await stat(candidateRealPath);
|
|
102
|
+
if (!candidateEntry.isFile()) {
|
|
103
|
+
throw new Error("Candidate must be a regular file in the project authoring directory");
|
|
104
|
+
}
|
|
105
|
+
const candidateBytesBeforeValidation = await readFile(candidateRealPath);
|
|
106
|
+
const validation = await loadDNAWithDiagnostics(candidateRealPath);
|
|
107
|
+
if (!validation.dna || hasDiagnosticErrors(validation.diagnostics)) {
|
|
108
|
+
const details = formatDiagnostics(validation.diagnostics);
|
|
109
|
+
throw new Error(`Candidate validation failed${details ? `:\n${details}` : ""}`);
|
|
110
|
+
}
|
|
111
|
+
const candidateRealPathAfterValidation = await realpath(candidatePath);
|
|
112
|
+
if (candidateRealPathAfterValidation !== candidateRealPath) {
|
|
113
|
+
throw new Error("Candidate path changed during validation; promotion refused");
|
|
114
|
+
}
|
|
115
|
+
const candidateBytes = await readFile(candidateRealPathAfterValidation);
|
|
116
|
+
if (!candidateBytes.equals(candidateBytesBeforeValidation)) {
|
|
117
|
+
throw new Error("Candidate bytes changed during validation; promotion refused");
|
|
118
|
+
}
|
|
119
|
+
const templatesPath = join(dnaPath, "templates");
|
|
120
|
+
const templatesRealPath = await ensureSafeTemplatesDirectory(templatesPath, dnaRealPath);
|
|
121
|
+
const templatePath = join(templatesPath, `${options.templateName}.dna.yaml`);
|
|
122
|
+
const templateWritePath = join(templatesRealPath, `${options.templateName}.dna.yaml`);
|
|
123
|
+
const candidateName = candidateFilename.slice(0, -CANDIDATE_SUFFIX.length);
|
|
124
|
+
const receiptPath = join(authoringPath, `${candidateName}.candidate-review.json`);
|
|
125
|
+
const receiptWritePath = join(authoringRealPath, `${candidateName}.candidate-review.json`);
|
|
126
|
+
await assertPathMissing(templateWritePath, "Promotion target");
|
|
127
|
+
await assertPathMissing(receiptWritePath, "Candidate review receipt");
|
|
128
|
+
const candidateHash = sha256(candidateBytes);
|
|
129
|
+
let templateCreated = false;
|
|
130
|
+
let receiptCreated = false;
|
|
131
|
+
let templateHandle;
|
|
132
|
+
let receiptHandle;
|
|
133
|
+
try {
|
|
134
|
+
templateHandle = await open(templateWritePath, "wx");
|
|
135
|
+
templateCreated = true;
|
|
136
|
+
await templateHandle.writeFile(candidateBytes);
|
|
137
|
+
await templateHandle.sync();
|
|
138
|
+
await templateHandle.close();
|
|
139
|
+
templateHandle = undefined;
|
|
140
|
+
const currentTemplatesRealPath = await realpath(templatesPath);
|
|
141
|
+
if (currentTemplatesRealPath !== templatesRealPath) {
|
|
142
|
+
throw new Error("Templates directory changed during promotion; promotion refused");
|
|
143
|
+
}
|
|
144
|
+
const promotedBytes = await readFile(templateWritePath);
|
|
145
|
+
const templateHash = sha256(promotedBytes);
|
|
146
|
+
if (!promotedBytes.equals(candidateBytes) || templateHash !== candidateHash) {
|
|
147
|
+
throw new Error("Promoted template does not preserve the validated candidate bytes exactly");
|
|
148
|
+
}
|
|
149
|
+
const receipt = {
|
|
150
|
+
schema_version: "intentdna.candidate_promotion_receipt.v1",
|
|
151
|
+
decision: "promoted",
|
|
152
|
+
source: "explicit_human_promotion",
|
|
153
|
+
mutation_performed: true,
|
|
154
|
+
promoted_at: new Date().toISOString(),
|
|
155
|
+
candidate_path: candidatePath,
|
|
156
|
+
template_path: templatePath,
|
|
157
|
+
candidate_sha256: candidateHash,
|
|
158
|
+
template_sha256: templateHash,
|
|
159
|
+
exact_bytes_preserved: true,
|
|
160
|
+
};
|
|
161
|
+
const receiptBytes = Buffer.from(`${JSON.stringify(receipt, null, 2)}\n`, "utf8");
|
|
162
|
+
receiptHandle = await open(receiptWritePath, "wx");
|
|
163
|
+
receiptCreated = true;
|
|
164
|
+
await receiptHandle.writeFile(receiptBytes);
|
|
165
|
+
await receiptHandle.sync();
|
|
166
|
+
await receiptHandle.close();
|
|
167
|
+
receiptHandle = undefined;
|
|
168
|
+
return {
|
|
169
|
+
schema_version: "intentdna.candidate_promotion_result.v1",
|
|
170
|
+
candidate_path: candidatePath,
|
|
171
|
+
template_path: templatePath,
|
|
172
|
+
receipt_path: receiptPath,
|
|
173
|
+
candidate_sha256: candidateHash,
|
|
174
|
+
template_sha256: templateHash,
|
|
175
|
+
exact_bytes_preserved: true,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
await receiptHandle?.close().catch(() => undefined);
|
|
180
|
+
await templateHandle?.close().catch(() => undefined);
|
|
181
|
+
const cleanupErrors = [];
|
|
182
|
+
if (receiptCreated) {
|
|
183
|
+
const cleanupError = await removeCreatedPath(receiptWritePath);
|
|
184
|
+
if (cleanupError)
|
|
185
|
+
cleanupErrors.push(cleanupError);
|
|
186
|
+
}
|
|
187
|
+
if (templateCreated) {
|
|
188
|
+
const cleanupError = await removeCreatedPath(templateWritePath);
|
|
189
|
+
if (cleanupError)
|
|
190
|
+
cleanupErrors.push(cleanupError);
|
|
191
|
+
}
|
|
192
|
+
if (cleanupErrors.length > 0) {
|
|
193
|
+
throw new AggregateError([error, ...cleanupErrors], "Candidate promotion failed and cleanup did not complete");
|
|
194
|
+
}
|
|
195
|
+
throw error;
|
|
196
|
+
}
|
|
197
|
+
}
|
package/dist/cli/commands/run.js
CHANGED
|
@@ -10,19 +10,20 @@ import { isAbsolute, join, relative, resolve, sep, } from "node:path";
|
|
|
10
10
|
import { cascadeDNA } from "../../compiler/cascade.js";
|
|
11
11
|
import { compileFromFiles, expandDNAInputFiles, loadDNA, } from "../../compiler/index.js";
|
|
12
12
|
import { compileWorkflow } from "../../compiler/workflow.js";
|
|
13
|
-
import { trustedEvidenceCaptureAttribution } from "../../governance/index.js";
|
|
14
13
|
import { DNAStateManager } from "../../hooks/state-manager.js";
|
|
15
14
|
import { appendEvidenceCaptureEvent, EVIDENCE_CAPTURE_SCHEMA_VERSION, } from "../../hooks/state.js";
|
|
16
15
|
import { compileAllRolesToAgentMD, toKebabCase, writeAgentMDFiles, } from "../../runtime/agent-md.js";
|
|
17
16
|
import { HandoffResolver } from "../../runtime/handoff-resolver.js";
|
|
18
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";
|
|
19
20
|
import { ImmutableResultStore } from "../../runtime/result-store.js";
|
|
20
21
|
import { RunController, } from "../../runtime/run-controller.js";
|
|
21
22
|
import { DurableRunStore, RunStoreError, } from "../../runtime/run-store.js";
|
|
22
23
|
import { createClaudeExecutionProvider } from "../../runtime/providers/claude.js";
|
|
23
24
|
import { createCodexExecutionProvider } from "../../runtime/providers/codex.js";
|
|
24
25
|
import { runCheckpointVerifier, runCompletionVerifier, } from "../../runtime/verifier.js";
|
|
25
|
-
import { adaptWorkflowPlan, } from "../../runtime/workflow-plan-adapter.js";
|
|
26
|
+
import { adaptWorkflowPlan, adaptWorkflowRetryLoop, } from "../../runtime/workflow-plan-adapter.js";
|
|
26
27
|
import { executeWorkerAttempt, } from "../../runtime/worker-executor.js";
|
|
27
28
|
import { allocateAttemptWorkspace, applyWorkspaceLifecycleDecision, planAttemptWorkspace, } from "../../runtime/workspace-isolation.js";
|
|
28
29
|
import { runLifecycleCancel, runLifecycleInspect, runLifecycleResume, runLifecycleStart, runLifecycleStatus, } from "./run-lifecycle.js";
|
|
@@ -191,6 +192,8 @@ async function compileRunAssets(metadata) {
|
|
|
191
192
|
for (const file of metadata.dna_files)
|
|
192
193
|
dnas.push(await loadDNA(file));
|
|
193
194
|
const cascaded = cascadeDNA(dnas);
|
|
195
|
+
const variableScopeIndex = indexWorkflowVariableScopes(dnas);
|
|
196
|
+
const defaultVariableScopes = defaultResolvedVariableScopes(variableScopeIndex);
|
|
194
197
|
const ir = await compileFromFiles([...metadata.dna_files], {
|
|
195
198
|
context: metadata.context ?? undefined,
|
|
196
199
|
});
|
|
@@ -199,26 +202,31 @@ async function compileRunAssets(metadata) {
|
|
|
199
202
|
throw new Error(`workflow '${metadata.workflow_name}' not found; available: `
|
|
200
203
|
+ `${Object.keys(cascaded.workflows).join(", ") || "(none)"}`);
|
|
201
204
|
}
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
const runtimePlans = [];
|
|
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"));
|
|
211
|
+
}
|
|
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
|
+
});
|
|
208
218
|
}
|
|
209
|
-
const plan =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const workflowAsset = attribution.workflowAsset === "unknown"
|
|
214
|
-
? null
|
|
215
|
-
: attribution.workflowAsset;
|
|
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;
|
|
216
223
|
if (metadata.workflow_asset !== null
|
|
217
224
|
&& workflowAsset !== metadata.workflow_asset) {
|
|
218
225
|
throw new Error(`workflow asset changed from '${metadata.workflow_asset}' to '${workflowAsset ?? "unknown"}'`);
|
|
219
226
|
}
|
|
220
227
|
return {
|
|
221
228
|
plan,
|
|
229
|
+
runtime_plans: runtimePlans,
|
|
222
230
|
roles: cascaded.roles,
|
|
223
231
|
ir,
|
|
224
232
|
plan_digest: planDigest(plan, cascaded.roles),
|
|
@@ -227,6 +235,20 @@ async function compileRunAssets(metadata) {
|
|
|
227
235
|
}
|
|
228
236
|
async function compileCanonicalRun(metadata) {
|
|
229
237
|
const assets = await compileRunAssets(metadata);
|
|
238
|
+
if (!assets.ir.compiled_ir_hash) {
|
|
239
|
+
throw new Error("Compiled IR hash is required for canonical workflow runtime bindings");
|
|
240
|
+
}
|
|
241
|
+
const workflowRuntime = createWorkflowRuntimeManifest({
|
|
242
|
+
plans: assets.runtime_plans.map((runtimePlan) => ({
|
|
243
|
+
plan: runtimePlan.plan,
|
|
244
|
+
resolvedVariables: runtimePlan.resolved_variables,
|
|
245
|
+
workflowAsset: runtimePlan.workflow_asset ?? undefined,
|
|
246
|
+
additionalRuntimeValues: assets.ir.verifier_specs?.filter((spec) => spec.workflow_name === runtimePlan.plan.workflow_key) ?? [],
|
|
247
|
+
})),
|
|
248
|
+
compiledAt: assets.ir.compiled_at,
|
|
249
|
+
compiledIrHash: assets.ir.compiled_ir_hash,
|
|
250
|
+
sourceDnaIds: assets.ir.source_dna_ids,
|
|
251
|
+
});
|
|
230
252
|
const adapterOptions = {
|
|
231
253
|
roles: assets.roles,
|
|
232
254
|
variables: metadata.variables,
|
|
@@ -236,13 +258,15 @@ async function compileCanonicalRun(metadata) {
|
|
|
236
258
|
};
|
|
237
259
|
return {
|
|
238
260
|
...assets,
|
|
261
|
+
workflow_runtime: workflowRuntime,
|
|
239
262
|
definitions: adaptWorkflowPlan(assets.plan, adapterOptions),
|
|
263
|
+
retry_loop: adaptWorkflowRetryLoop(assets.plan, adapterOptions),
|
|
240
264
|
};
|
|
241
265
|
}
|
|
242
266
|
async function syncRuntimeArtifacts(compiled, metadata) {
|
|
243
267
|
const agents = compileAllRolesToAgentMD(compiled.roles, compiled.ir);
|
|
244
268
|
await writeAgentMDFiles(agents, metadata.agents_directory);
|
|
245
|
-
await writeCompiledIR(metadata.project_directory, createCompiledIR(compiled.ir, compiled.roles));
|
|
269
|
+
await writeCompiledIR(metadata.project_directory, createCompiledIR(compiled.ir, compiled.roles, compiled.workflow_runtime));
|
|
246
270
|
}
|
|
247
271
|
function projectPathInWorkspace(metadata, workspaceDirectory, projectPath) {
|
|
248
272
|
const projectRelative = relative(metadata.project_directory, projectPath);
|
|
@@ -261,7 +285,7 @@ async function syncAttemptRuntimeArtifacts(compiled, metadata, workspace) {
|
|
|
261
285
|
const agents = compileAllRolesToAgentMD(compiled.roles, compiled.ir);
|
|
262
286
|
await writeAgentMDFiles(agents, agentsDirectory);
|
|
263
287
|
}
|
|
264
|
-
await writeCompiledIR(workspace.working_directory, createCompiledIR(compiled.ir, compiled.roles));
|
|
288
|
+
await writeCompiledIR(workspace.working_directory, createCompiledIR(compiled.ir, compiled.roles, compiled.workflow_runtime));
|
|
265
289
|
}
|
|
266
290
|
function createProvider(metadata) {
|
|
267
291
|
if (metadata.provider.kind === "codex") {
|
|
@@ -371,7 +395,7 @@ async function applyStepValidation(metadata, compiled, packet, execution) {
|
|
|
371
395
|
}
|
|
372
396
|
const variables = {
|
|
373
397
|
...metadata.variables,
|
|
374
|
-
round: String(packet.
|
|
398
|
+
round: String(packet.workflow_round),
|
|
375
399
|
};
|
|
376
400
|
let failure = null;
|
|
377
401
|
for (const completion of step.completion ?? []) {
|
|
@@ -461,31 +485,40 @@ function createWorkspaceAwareExecutor(metadata, compiled, runStore) {
|
|
|
461
485
|
let lifecycleRecorded = false;
|
|
462
486
|
try {
|
|
463
487
|
await syncAttemptRuntimeArtifacts(compiled, metadata, workspace);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
started_at: packet.created_at,
|
|
478
|
-
inputs: { ...metadata.variables },
|
|
479
|
-
resolved_variables: {
|
|
480
|
-
...metadata.variables,
|
|
481
|
-
round: String(packet.attempt_number),
|
|
482
|
-
},
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
catch (error) {
|
|
486
|
-
log(`Workflow evidence state unavailable for ${packet.step_id}; `
|
|
487
|
-
+ `continuing fail-open: ${error instanceof Error ? error.message : String(error)}`);
|
|
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;
|
|
488
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
|
+
});
|
|
489
522
|
const inheritedProcessObserver = options?.onProcessStart;
|
|
490
523
|
let execution = await executeWorkerAttempt(workerPacket, provider, {
|
|
491
524
|
...options,
|
|
@@ -533,6 +566,7 @@ function buildRuntime(metadata, compiled) {
|
|
|
533
566
|
run_store: runStore,
|
|
534
567
|
handoff_resolver: resolver,
|
|
535
568
|
steps: compiled.definitions,
|
|
569
|
+
workflow_retry_loop: compiled.retry_loop,
|
|
536
570
|
provider_for_step: () => provider,
|
|
537
571
|
reconcile_attempt: reconcileLocalProcess,
|
|
538
572
|
execute_attempt: createWorkspaceAwareExecutor(metadata, compiled, runStore),
|
|
@@ -19,4 +19,14 @@ export interface SetupOptions {
|
|
|
19
19
|
yes: boolean;
|
|
20
20
|
upgrade: boolean;
|
|
21
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if intentdna is already registered as a plugin.
|
|
24
|
+
*/
|
|
25
|
+
export interface InstalledPluginInfo {
|
|
26
|
+
version: string | null;
|
|
27
|
+
enabled: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function parseInstalledPluginInfo(output: string): InstalledPluginInfo | null;
|
|
30
|
+
export declare function releaseChannelForVersion(version: string | null): "latest" | "next";
|
|
31
|
+
export declare function shouldInstallLatestVersion(installedVersion: string, latestVersion: string): boolean;
|
|
22
32
|
export declare function runSetup(opts: SetupOptions): Promise<number>;
|