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.
Files changed (69) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/dist/cli/commands/candidate-promotion.d.ts +15 -0
  4. package/dist/cli/commands/candidate-promotion.js +197 -0
  5. package/dist/cli/commands/run-lifecycle.js +1 -0
  6. package/dist/cli/commands/run.js +76 -42
  7. package/dist/cli/commands/setup.d.ts +10 -0
  8. package/dist/cli/commands/setup.js +129 -36
  9. package/dist/cli/commands/source-assets.d.ts +32 -0
  10. package/dist/cli/commands/source-assets.js +154 -0
  11. package/dist/cli/commands/sync.js +52 -12
  12. package/dist/cli/commands/templates.js +59 -37
  13. package/dist/cli/index.js +161 -5
  14. package/dist/compiler/cascade.js +1 -1
  15. package/dist/hooks/cli.d.ts +6 -0
  16. package/dist/hooks/cli.js +118 -10
  17. package/dist/hooks/enforcement-boundary.d.ts +1 -1
  18. package/dist/hooks/protocol.d.ts +1 -1
  19. package/dist/hooks/state.d.ts +5 -0
  20. package/dist/hooks/state.js +21 -6
  21. package/dist/mcp/tools-state.js +251 -15
  22. package/dist/runtime/claude-sync-target.d.ts +2 -0
  23. package/dist/runtime/claude-sync-target.js +1 -1
  24. package/dist/runtime/evaluator.d.ts +13 -0
  25. package/dist/runtime/evaluator.js +46 -0
  26. package/dist/runtime/index.d.ts +8 -4
  27. package/dist/runtime/index.js +4 -2
  28. package/dist/runtime/plugin-adapter.d.ts +14 -1
  29. package/dist/runtime/plugin-adapter.js +23 -9
  30. package/dist/runtime/run-contracts.d.ts +5 -3
  31. package/dist/runtime/run-contracts.js +11 -1
  32. package/dist/runtime/run-controller.d.ts +15 -1
  33. package/dist/runtime/run-controller.js +235 -94
  34. package/dist/runtime/skill-adapter.js +24 -53
  35. package/dist/runtime/verifier.js +101 -2
  36. package/dist/runtime/workflow-plan-adapter.d.ts +2 -1
  37. package/dist/runtime/workflow-plan-adapter.js +78 -11
  38. package/dist/runtime/workflow-runtime-manifest.d.ts +48 -0
  39. package/dist/runtime/workflow-runtime-manifest.js +196 -0
  40. package/dist/runtime/workflow-variable-scopes.d.ts +12 -0
  41. package/dist/runtime/workflow-variable-scopes.js +55 -0
  42. package/dist/schema/types.d.ts +9 -0
  43. package/dist/schema/validate.js +29 -1
  44. package/dist/sources/authoring-packet.d.ts +8 -0
  45. package/dist/sources/authoring-packet.js +85 -0
  46. package/dist/sources/discovery.d.ts +7 -0
  47. package/dist/sources/discovery.js +150 -0
  48. package/dist/sources/license.d.ts +5 -0
  49. package/dist/sources/license.js +94 -0
  50. package/dist/sources/manifest.d.ts +11 -0
  51. package/dist/sources/manifest.js +84 -0
  52. package/dist/sources/skill-markdown.d.ts +6 -0
  53. package/dist/sources/skill-markdown.js +174 -0
  54. package/dist/sources/types.d.ts +125 -0
  55. package/dist/sources/types.js +1 -0
  56. package/dist/templates/asset-refinery.dna.yaml +192 -0
  57. package/dist/templates/flutter-behavior-lock.dna.yaml +17 -0
  58. package/dist/templates/flutter-refactoring-rescue.dna.yaml +11 -0
  59. package/dist/templates/flutter-rewrite.dna.yaml +154 -28
  60. package/dist/templates/metadata.js +2 -0
  61. package/dist/templates/multi-agent-handoff-coordination.dna.yaml +102 -37
  62. package/dist/templates/persistent-executor.dna.yaml +61 -3
  63. package/dist/templates/research-improvement.dna.yaml +295 -0
  64. package/dist/templates/subagent-parallel.dna.yaml +90 -17
  65. package/dist/templates/workflow-evidence-review.dna.yaml +8 -0
  66. package/dist/workflow/candidate-projection.d.ts +33 -0
  67. package/dist/workflow/candidate-projection.js +70 -0
  68. package/package.json +3 -1
  69. 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.8.7",
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.8.7"
28
+ "version": "1.9.0-rc.2"
29
29
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intentdna",
3
- "version": "1.8.7",
3
+ "version": "1.9.0-rc.2",
4
4
  "description": "Declarative policy layer for AI agent behavior",
5
5
  "author": {
6
6
  "name": "Samuel"
@@ -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
+ }
@@ -9,6 +9,7 @@ const TASK_STATES = [
9
9
  "ready",
10
10
  "claimed",
11
11
  "running",
12
+ "round_succeeded",
12
13
  "succeeded",
13
14
  "failed",
14
15
  "skipped",
@@ -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 result = compileWorkflow(definition, {
203
- workflow_key: metadata.workflow_name,
204
- });
205
- if (!result.ok || !result.plan) {
206
- throw new Error("workflow compilation failed:\n"
207
- + result.errors.map((error) => ` ${error.path}: ${error.message}`).join("\n"));
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 = result.plan;
210
- const attribution = trustedEvidenceCaptureAttribution(ir, {
211
- workflowId: metadata.workflow_name,
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.attempt_number),
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
- try {
465
- await state.writeWorkflowState({
466
- active: true,
467
- workflow_asset: metadata.workflow_asset ?? undefined,
468
- workflow: metadata.workflow_name,
469
- current_step: packet.step_id,
470
- current_role: packet.role,
471
- iteration: packet.attempt_number,
472
- session_id: packet.worker_session_id,
473
- worker_session_id: packet.worker_session_id,
474
- run_id: packet.run_id,
475
- step_id: packet.step_id,
476
- attempt_id: packet.attempt_id,
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>;