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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ArtifactStore } from "./artifact-store.js";
|
|
2
|
+
import { CanonicalRunService, type CanonicalRunServiceOptions } from "./canonical-run-service.js";
|
|
3
|
+
import { DurablePlanStore } from "./plan-store.js";
|
|
4
|
+
import { ImmutableCanonicalResultStore } from "./result-store.js";
|
|
5
|
+
import { DurableRunStore } from "./run-store.js";
|
|
6
|
+
export interface CanonicalRuntimeStores {
|
|
7
|
+
readonly root_directory: string;
|
|
8
|
+
readonly plan_store: DurablePlanStore;
|
|
9
|
+
readonly run_store: DurableRunStore;
|
|
10
|
+
readonly artifact_store: ArtifactStore;
|
|
11
|
+
readonly result_store: ImmutableCanonicalResultStore;
|
|
12
|
+
}
|
|
13
|
+
export interface CanonicalRuntimeComposition extends CanonicalRuntimeStores {
|
|
14
|
+
readonly service: CanonicalRunService;
|
|
15
|
+
}
|
|
16
|
+
export interface CreateCanonicalRuntimeCompositionOptions extends Omit<CanonicalRunServiceOptions, "plan_store" | "run_store" | "artifact_store" | "result_store" | "handoff_root_directory"> {
|
|
17
|
+
readonly project_directory: string;
|
|
18
|
+
readonly run_store?: DurableRunStore;
|
|
19
|
+
}
|
|
20
|
+
export declare function canonicalRuntimeRoot(projectDirectory: string): string;
|
|
21
|
+
export declare function createCanonicalRuntimeStores(projectDirectory: string, runStore?: DurableRunStore): CanonicalRuntimeStores;
|
|
22
|
+
export declare function createCanonicalRuntimeComposition(options: CreateCanonicalRuntimeCompositionOptions): CanonicalRuntimeComposition;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { join, resolve } from "node:path";
|
|
2
|
+
import { ArtifactStore } from "./artifact-store.js";
|
|
3
|
+
import { CanonicalRunService, } from "./canonical-run-service.js";
|
|
4
|
+
import { DurablePlanStore } from "./plan-store.js";
|
|
5
|
+
import { ImmutableCanonicalResultStore } from "./result-store.js";
|
|
6
|
+
import { DurableRunStore } from "./run-store.js";
|
|
7
|
+
export function canonicalRuntimeRoot(projectDirectory) {
|
|
8
|
+
return join(resolve(projectDirectory), ".dna", "runtime");
|
|
9
|
+
}
|
|
10
|
+
export function createCanonicalRuntimeStores(projectDirectory, runStore) {
|
|
11
|
+
const rootDirectory = canonicalRuntimeRoot(projectDirectory);
|
|
12
|
+
return {
|
|
13
|
+
root_directory: rootDirectory,
|
|
14
|
+
plan_store: new DurablePlanStore({ root_directory: rootDirectory }),
|
|
15
|
+
run_store: runStore ?? new DurableRunStore({ root_directory: rootDirectory }),
|
|
16
|
+
artifact_store: new ArtifactStore({ root_directory: rootDirectory }),
|
|
17
|
+
result_store: new ImmutableCanonicalResultStore({ root_directory: rootDirectory }),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function createCanonicalRuntimeComposition(options) {
|
|
21
|
+
const { project_directory, run_store, ...serviceOptions } = options;
|
|
22
|
+
const stores = createCanonicalRuntimeStores(project_directory, run_store);
|
|
23
|
+
return {
|
|
24
|
+
...stores,
|
|
25
|
+
service: new CanonicalRunService({
|
|
26
|
+
...serviceOptions,
|
|
27
|
+
plan_store: stores.plan_store,
|
|
28
|
+
run_store: stores.run_store,
|
|
29
|
+
artifact_store: stores.artifact_store,
|
|
30
|
+
result_store: stores.result_store,
|
|
31
|
+
handoff_root_directory: join(stores.root_directory, "handoffs"),
|
|
32
|
+
}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ConstraintIR, IntentDNA, RoleDef, WorkflowPlan } from "../schema/types.js";
|
|
2
|
+
import { type ExecutableRunPlanPayload, type Sha256Digest } from "./executable-run-plan.js";
|
|
3
|
+
import { type WorkflowRuntimeManifest } from "./workflow-runtime-manifest.js";
|
|
4
|
+
export declare const CANONICAL_TARGET_COMPILER_NAME = "intentdna";
|
|
5
|
+
export declare const CANONICAL_TARGET_COMPILER_VERSION = "1";
|
|
6
|
+
export interface CanonicalTarget {
|
|
7
|
+
readonly kind: "workflow" | "controller";
|
|
8
|
+
readonly key: string;
|
|
9
|
+
}
|
|
10
|
+
export interface CanonicalDnaSource {
|
|
11
|
+
readonly dna: IntentDNA;
|
|
12
|
+
readonly source_id: string;
|
|
13
|
+
readonly relative_ref: string;
|
|
14
|
+
readonly content_digest: Sha256Digest;
|
|
15
|
+
readonly snapshot_bytes: Uint8Array;
|
|
16
|
+
}
|
|
17
|
+
export interface CompileCanonicalTargetRequest {
|
|
18
|
+
readonly dna_sources: readonly (IntentDNA | CanonicalDnaSource)[];
|
|
19
|
+
readonly target: CanonicalTarget;
|
|
20
|
+
readonly context?: string | null;
|
|
21
|
+
readonly attempt_policy?: {
|
|
22
|
+
readonly timeout_ms: number | null;
|
|
23
|
+
readonly cancellation_grace_ms: number;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface CanonicalRuntimeProjection {
|
|
27
|
+
readonly roles: Readonly<Record<string, RoleDef>>;
|
|
28
|
+
readonly constraint_ir: ConstraintIR;
|
|
29
|
+
readonly workflow_manifest: WorkflowRuntimeManifest;
|
|
30
|
+
readonly workflow_plan: WorkflowPlan;
|
|
31
|
+
readonly workflow_asset: string | null;
|
|
32
|
+
}
|
|
33
|
+
export interface CanonicalTargetCompilation {
|
|
34
|
+
readonly plan: ExecutableRunPlanPayload;
|
|
35
|
+
readonly projection: CanonicalRuntimeProjection;
|
|
36
|
+
}
|
|
37
|
+
export type CanonicalTargetCompilerErrorCode = "invalid_sources" | "invalid_target" | "target_not_found" | "unsupported_target_kind" | "controller_compilation_failed" | "workflow_compilation_failed" | "workflow_adaptation_failed";
|
|
38
|
+
export declare class CanonicalTargetCompilerError extends Error {
|
|
39
|
+
readonly code: CanonicalTargetCompilerErrorCode;
|
|
40
|
+
constructor(code: CanonicalTargetCompilerErrorCode, message: string, options?: ErrorOptions);
|
|
41
|
+
}
|
|
42
|
+
export declare class CanonicalTargetCompiler {
|
|
43
|
+
compile(request: CompileCanonicalTargetRequest): ExecutableRunPlanPayload;
|
|
44
|
+
compileCascade(request: CompileCanonicalTargetRequest): CanonicalTargetCompilation;
|
|
45
|
+
}
|
|
46
|
+
export declare function compileCanonicalTarget(request: CompileCanonicalTargetRequest): ExecutableRunPlanPayload;
|
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { TextDecoder } from "node:util";
|
|
3
|
+
import { cascadeDNA } from "../compiler/cascade.js";
|
|
4
|
+
import { activateDNA } from "../compiler/activate.js";
|
|
5
|
+
import { compileDNA } from "../compiler/compile.js";
|
|
6
|
+
import { attachProvenance } from "../compiler/provenance.js";
|
|
7
|
+
import { compileWorkflow } from "../compiler/workflow.js";
|
|
8
|
+
import { compileControllerPlan, ControllerPlanCompilerError, } from "../compiler/controller.js";
|
|
9
|
+
import { validateDNA } from "../schema/validate.js";
|
|
10
|
+
import { parseYAML } from "../schema/yaml-parser.js";
|
|
11
|
+
import { canonicalizeJson } from "./canonical-json.js";
|
|
12
|
+
import { createWorkflowRuntimeManifest, } from "./workflow-runtime-manifest.js";
|
|
13
|
+
import { defaultResolvedVariableScopes, indexWorkflowVariableScopes, resolvedVariablesForNamespace, } from "./workflow-variable-scopes.js";
|
|
14
|
+
import { adaptWorkflowPlanToExecutableRunPlan, } from "./workflow-plan-adapter.js";
|
|
15
|
+
export const CANONICAL_TARGET_COMPILER_NAME = "intentdna";
|
|
16
|
+
export const CANONICAL_TARGET_COMPILER_VERSION = "1";
|
|
17
|
+
export class CanonicalTargetCompilerError extends Error {
|
|
18
|
+
code;
|
|
19
|
+
constructor(code, message, options) {
|
|
20
|
+
super(message, options);
|
|
21
|
+
this.name = "CanonicalTargetCompilerError";
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function digestBytes(value) {
|
|
26
|
+
return `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
27
|
+
}
|
|
28
|
+
function hasStableArtifactIdentity(artifact) {
|
|
29
|
+
return artifact.artifact_id !== undefined
|
|
30
|
+
|| artifact.name !== undefined
|
|
31
|
+
|| artifact.path !== undefined
|
|
32
|
+
|| artifact.type === "git_commit";
|
|
33
|
+
}
|
|
34
|
+
function authoredArtifactIdentity(artifact) {
|
|
35
|
+
return artifact.artifact_id
|
|
36
|
+
?? artifact.name
|
|
37
|
+
?? artifact.path
|
|
38
|
+
?? (artifact.type === "git_commit" ? "git_commit" : null);
|
|
39
|
+
}
|
|
40
|
+
function containsRuntimeIdentityTemplate(value) {
|
|
41
|
+
return value.includes("$ARGUMENTS") || /\{\{[^}]+\}\}/u.test(value);
|
|
42
|
+
}
|
|
43
|
+
function controllerArtifactId(stepId, direction, index) {
|
|
44
|
+
return `${stepId}-${direction}-${index}`;
|
|
45
|
+
}
|
|
46
|
+
function canonicalControllerHandoffIdentities(plan) {
|
|
47
|
+
const outputIds = new Map();
|
|
48
|
+
const stepsWithOutputs = plan.steps.map((step) => {
|
|
49
|
+
if (step.handoff === null)
|
|
50
|
+
return step;
|
|
51
|
+
const produces = (step.handoff.produces ?? []).flatMap((artifact, index) => {
|
|
52
|
+
const identity = authoredArtifactIdentity(artifact);
|
|
53
|
+
if (identity === null)
|
|
54
|
+
return artifact.required === false ? [] : [artifact];
|
|
55
|
+
if (!containsRuntimeIdentityTemplate(identity))
|
|
56
|
+
return [artifact];
|
|
57
|
+
const artifactId = controllerArtifactId(step.id, "output", index);
|
|
58
|
+
outputIds.set(`${step.id}\u0000${identity}`, artifactId);
|
|
59
|
+
return [{ ...artifact, artifact_id: artifactId }];
|
|
60
|
+
});
|
|
61
|
+
return { ...step, handoff: { ...step.handoff, produces } };
|
|
62
|
+
});
|
|
63
|
+
return {
|
|
64
|
+
...plan,
|
|
65
|
+
steps: stepsWithOutputs.map((step) => {
|
|
66
|
+
if (step.handoff === null)
|
|
67
|
+
return step;
|
|
68
|
+
const consumes = (step.handoff.consumes ?? []).map((artifact, index) => {
|
|
69
|
+
const identity = authoredArtifactIdentity(artifact);
|
|
70
|
+
if (identity === null || !containsRuntimeIdentityTemplate(identity))
|
|
71
|
+
return artifact;
|
|
72
|
+
const sourceArtifactId = artifact.from === undefined
|
|
73
|
+
? undefined
|
|
74
|
+
: outputIds.get(`${artifact.from}\u0000${identity}`);
|
|
75
|
+
return {
|
|
76
|
+
...artifact,
|
|
77
|
+
artifact_id: sourceArtifactId
|
|
78
|
+
?? controllerArtifactId(step.id, "input", index),
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
return { ...step, handoff: { ...step.handoff, consumes } };
|
|
82
|
+
}),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function logicalSourceRef(dna, index) {
|
|
86
|
+
const segment = dna.id.replace(/[^A-Za-z0-9._-]/gu, "_") || `source-${index}`;
|
|
87
|
+
return `sources/${segment}.dna.json`;
|
|
88
|
+
}
|
|
89
|
+
function isCanonicalDnaSource(source) {
|
|
90
|
+
return Object.prototype.hasOwnProperty.call(source, "dna");
|
|
91
|
+
}
|
|
92
|
+
function normalizeSources(sources) {
|
|
93
|
+
if (sources.length === 0) {
|
|
94
|
+
throw new CanonicalTargetCompilerError("invalid_sources", "dna_sources must contain at least one source");
|
|
95
|
+
}
|
|
96
|
+
return sources.map((source, index) => {
|
|
97
|
+
try {
|
|
98
|
+
const candidate = isCanonicalDnaSource(source)
|
|
99
|
+
? source
|
|
100
|
+
: (() => {
|
|
101
|
+
const bytes = Buffer.from(canonicalizeJson(source), "utf8");
|
|
102
|
+
return {
|
|
103
|
+
dna: source,
|
|
104
|
+
source_id: source.id,
|
|
105
|
+
relative_ref: logicalSourceRef(source, index),
|
|
106
|
+
content_digest: digestBytes(bytes),
|
|
107
|
+
snapshot_bytes: bytes,
|
|
108
|
+
};
|
|
109
|
+
})();
|
|
110
|
+
if (!(candidate.snapshot_bytes instanceof Uint8Array)) {
|
|
111
|
+
throw new TypeError("snapshot_bytes must be a Uint8Array");
|
|
112
|
+
}
|
|
113
|
+
const snapshotBytes = Uint8Array.from(candidate.snapshot_bytes);
|
|
114
|
+
const actualDigest = digestBytes(snapshotBytes);
|
|
115
|
+
if (candidate.content_digest !== actualDigest) {
|
|
116
|
+
throw new TypeError(`content_digest does not match snapshot bytes (${actualDigest})`);
|
|
117
|
+
}
|
|
118
|
+
const raw = new TextDecoder("utf-8", { fatal: true }).decode(snapshotBytes);
|
|
119
|
+
let snapshotDna;
|
|
120
|
+
try {
|
|
121
|
+
snapshotDna = JSON.parse(raw);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
snapshotDna = parseYAML(raw);
|
|
125
|
+
}
|
|
126
|
+
const validation = validateDNA(snapshotDna);
|
|
127
|
+
if (!validation.valid) {
|
|
128
|
+
throw new TypeError(validation.errors
|
|
129
|
+
.map((error) => `${error.path}: ${error.message}`)
|
|
130
|
+
.join("; "));
|
|
131
|
+
}
|
|
132
|
+
if (candidate.source_id !== snapshotDna.id || candidate.source_id !== candidate.dna.id) {
|
|
133
|
+
throw new TypeError("source_id must match the snapshot DNA id");
|
|
134
|
+
}
|
|
135
|
+
if (canonicalizeJson(candidate.dna) !== canonicalizeJson(snapshotDna)) {
|
|
136
|
+
throw new TypeError("parsed DNA must match snapshot bytes");
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
...candidate,
|
|
140
|
+
dna: snapshotDna,
|
|
141
|
+
content_digest: actualDigest,
|
|
142
|
+
snapshot_bytes: snapshotBytes,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
throw new CanonicalTargetCompilerError("invalid_sources", `DNA source at index ${index} is not bound to a valid immutable snapshot`, { cause: error });
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
function collectTemplateInputNames(value, names) {
|
|
151
|
+
if (typeof value === "string") {
|
|
152
|
+
for (const match of value.matchAll(/\{\{(\w+)\}\}/gu))
|
|
153
|
+
names.add(match[1]);
|
|
154
|
+
if (/(^|[^\\])\$ARGUMENTS/u.test(value))
|
|
155
|
+
names.add("ARGUMENTS");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
for (const item of value)
|
|
160
|
+
collectTemplateInputNames(item, names);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (value !== null && typeof value === "object") {
|
|
164
|
+
for (const item of Object.values(value)) {
|
|
165
|
+
collectTemplateInputNames(item, names);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function variableDefinition(name, value) {
|
|
170
|
+
if (value === undefined) {
|
|
171
|
+
return {
|
|
172
|
+
name,
|
|
173
|
+
type: "string",
|
|
174
|
+
required: true,
|
|
175
|
+
sensitive: false,
|
|
176
|
+
description: `Runtime value for template input '${name}'`,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
name,
|
|
181
|
+
type: typeof value === "string"
|
|
182
|
+
? "string"
|
|
183
|
+
: value.runtime_type ?? "string",
|
|
184
|
+
required: false,
|
|
185
|
+
sensitive: false,
|
|
186
|
+
description: typeof value === "string"
|
|
187
|
+
? `Runtime value for declared input '${name}'`
|
|
188
|
+
: value.description,
|
|
189
|
+
default_value: typeof value === "string" ? value : value.default,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function inputContract(dnas, targetKey, workflow, roles) {
|
|
193
|
+
const variableIndex = indexWorkflowVariableScopes([...dnas]);
|
|
194
|
+
const namespace = variableIndex.workflowNamespaces.get(targetKey) ?? "";
|
|
195
|
+
const declared = {
|
|
196
|
+
...(namespace ? variableIndex.rawByNamespace.get("") : {}),
|
|
197
|
+
...(variableIndex.rawByNamespace.get(namespace) ?? {}),
|
|
198
|
+
};
|
|
199
|
+
const names = new Set(Object.keys(declared));
|
|
200
|
+
collectTemplateInputNames(workflow, names);
|
|
201
|
+
for (const roleName of new Set(workflow.steps.map((step) => step.role))) {
|
|
202
|
+
const role = roles[roleName];
|
|
203
|
+
if (role)
|
|
204
|
+
collectTemplateInputNames(role, names);
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
inputs: [...names]
|
|
208
|
+
.sort()
|
|
209
|
+
.map((name) => variableDefinition(name, declared[name])),
|
|
210
|
+
allow_unknown_inputs: false,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
function sourceProvenance(sources) {
|
|
214
|
+
return {
|
|
215
|
+
compiler: {
|
|
216
|
+
name: CANONICAL_TARGET_COMPILER_NAME,
|
|
217
|
+
version: CANONICAL_TARGET_COMPILER_VERSION,
|
|
218
|
+
},
|
|
219
|
+
sources: sources.map((source) => ({
|
|
220
|
+
source_id: source.source_id,
|
|
221
|
+
relative_ref: source.relative_ref,
|
|
222
|
+
content_digest: source.content_digest,
|
|
223
|
+
})),
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function canonicalCompletion(completion) {
|
|
227
|
+
return JSON.parse(canonicalizeJson(completion));
|
|
228
|
+
}
|
|
229
|
+
function canonicalRuntimeWorkflowPlan(workflow, plan) {
|
|
230
|
+
const definitions = new Map(workflow.steps.map((step) => [step.id, step]));
|
|
231
|
+
const steps = plan.steps.map((step) => {
|
|
232
|
+
const definition = definitions.get(step.id);
|
|
233
|
+
const runtime = definition?.canonical_runtime;
|
|
234
|
+
if (definition === undefined || runtime === undefined)
|
|
235
|
+
return step;
|
|
236
|
+
const sourceByArtifactId = runtime.handoff_sources ?? {};
|
|
237
|
+
const authoredConsumes = step.handoff?.consumes ?? [];
|
|
238
|
+
for (const [artifactId, sourceStepId] of Object.entries(sourceByArtifactId)) {
|
|
239
|
+
const matches = authoredConsumes.filter((artifact) => artifact.artifact_id === artifactId);
|
|
240
|
+
if (matches.length !== 1) {
|
|
241
|
+
throw new Error(`step '${step.id}' canonical source '${artifactId}' must identify one consumed artifact`);
|
|
242
|
+
}
|
|
243
|
+
if (matches[0].from !== undefined && matches[0].from !== sourceStepId) {
|
|
244
|
+
throw new Error(`step '${step.id}' canonical source '${artifactId}' conflicts with authored source '${matches[0].from}'`);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
const canonicalConsumes = authoredConsumes.map((artifact) => {
|
|
248
|
+
const sourceStepId = artifact.artifact_id === undefined
|
|
249
|
+
? undefined
|
|
250
|
+
: sourceByArtifactId[artifact.artifact_id];
|
|
251
|
+
return sourceStepId === undefined ? artifact : { ...artifact, from: sourceStepId };
|
|
252
|
+
});
|
|
253
|
+
let prompt = step.prompt;
|
|
254
|
+
const pathBindings = {};
|
|
255
|
+
for (const artifactId of runtime.prompt_handoff_bindings ?? []) {
|
|
256
|
+
const matches = canonicalConsumes.filter((artifact) => (artifact.artifact_id === artifactId));
|
|
257
|
+
if (matches.length !== 1 || !matches[0].from || !matches[0].path) {
|
|
258
|
+
throw new Error(`step '${step.id}' canonical binding '${artifactId}' must identify one path handoff with a source step`);
|
|
259
|
+
}
|
|
260
|
+
if (prompt === null || !prompt.includes(matches[0].path)) {
|
|
261
|
+
throw new Error(`step '${step.id}' canonical binding '${artifactId}' is not referenced by the authored prompt`);
|
|
262
|
+
}
|
|
263
|
+
const replacement = `{{handoff.${step.id}:${artifactId}.materialized_path}}`;
|
|
264
|
+
prompt = prompt.replaceAll(matches[0].path, replacement);
|
|
265
|
+
pathBindings[matches[0].path] = replacement;
|
|
266
|
+
}
|
|
267
|
+
if (runtime.prompt_suffix !== undefined) {
|
|
268
|
+
prompt = `${prompt ?? step.description}\n\n${runtime.prompt_suffix}`;
|
|
269
|
+
}
|
|
270
|
+
const canonicalOutputs = runtime.outputs ?? [];
|
|
271
|
+
return {
|
|
272
|
+
...step,
|
|
273
|
+
prompt,
|
|
274
|
+
handoff: step.handoff === null
|
|
275
|
+
? (canonicalOutputs.length === 0 ? null : { produces: canonicalOutputs })
|
|
276
|
+
: {
|
|
277
|
+
...step.handoff,
|
|
278
|
+
consumes: canonicalConsumes,
|
|
279
|
+
produces: [...(step.handoff.produces ?? []), ...canonicalOutputs],
|
|
280
|
+
},
|
|
281
|
+
completion: runtime.completion === undefined
|
|
282
|
+
? step.completion
|
|
283
|
+
: runtime.completion.map(canonicalCompletion),
|
|
284
|
+
canonical_handoff_path_bindings: pathBindings,
|
|
285
|
+
canonical_requires_attested_independent_submit: runtime.require_attested_independent_submit === true,
|
|
286
|
+
};
|
|
287
|
+
});
|
|
288
|
+
return { ...plan, steps };
|
|
289
|
+
}
|
|
290
|
+
function compilerProvenance(sources) {
|
|
291
|
+
return sources.map((source) => ({
|
|
292
|
+
source_id: source.source_id,
|
|
293
|
+
source_ref: source.relative_ref,
|
|
294
|
+
resolved_path: source.relative_ref,
|
|
295
|
+
fingerprint: source.content_digest,
|
|
296
|
+
type: source.dna.type,
|
|
297
|
+
cascade_priority: source.dna.cascade.priority,
|
|
298
|
+
version: source.dna.version,
|
|
299
|
+
trust: "input",
|
|
300
|
+
genes: Object.keys(source.dna.genes ?? {}).sort(),
|
|
301
|
+
}));
|
|
302
|
+
}
|
|
303
|
+
export class CanonicalTargetCompiler {
|
|
304
|
+
compile(request) {
|
|
305
|
+
return this.compileCascade(request).plan;
|
|
306
|
+
}
|
|
307
|
+
compileCascade(request) {
|
|
308
|
+
if (request.target.kind !== "workflow"
|
|
309
|
+
&& request.target.kind !== "controller") {
|
|
310
|
+
throw new CanonicalTargetCompilerError("invalid_target", `target kind must be 'workflow' or 'controller', received '${String(request.target.kind)}'`);
|
|
311
|
+
}
|
|
312
|
+
if (!request.target.key.trim()) {
|
|
313
|
+
throw new CanonicalTargetCompilerError("invalid_target", "target key must be non-empty");
|
|
314
|
+
}
|
|
315
|
+
const sources = normalizeSources(request.dna_sources);
|
|
316
|
+
const dnas = sources.map((source) => source.dna);
|
|
317
|
+
const cascaded = cascadeDNA(dnas);
|
|
318
|
+
const activated = activateDNA(cascaded, request.context ?? null, null);
|
|
319
|
+
const constraintIr = attachProvenance(compileDNA(activated, cascaded), compilerProvenance(sources));
|
|
320
|
+
if (request.target.kind === "controller") {
|
|
321
|
+
const controller = cascaded.controllers[request.target.key];
|
|
322
|
+
if (!controller) {
|
|
323
|
+
throw new CanonicalTargetCompilerError("target_not_found", `controller '${request.target.key}' not found; available: ${Object.keys(cascaded.controllers).sort().join(", ") || "(none)"}`);
|
|
324
|
+
}
|
|
325
|
+
if (!controller.runtime_program) {
|
|
326
|
+
throw new CanonicalTargetCompilerError("controller_compilation_failed", `controller '${request.target.key}' has no typed runtime_program`);
|
|
327
|
+
}
|
|
328
|
+
const diagnosisKey = controller.diagnosis_workflow;
|
|
329
|
+
const fixKey = controller.fix_workflow;
|
|
330
|
+
if (!diagnosisKey || !fixKey) {
|
|
331
|
+
throw new CanonicalTargetCompilerError("controller_compilation_failed", `controller '${request.target.key}' must reference diagnosis_workflow and fix_workflow`);
|
|
332
|
+
}
|
|
333
|
+
const compileComponent = (workflowKey) => {
|
|
334
|
+
const definition = cascaded.workflows[workflowKey];
|
|
335
|
+
if (!definition) {
|
|
336
|
+
throw new CanonicalTargetCompilerError("controller_compilation_failed", `controller '${request.target.key}' references missing workflow '${workflowKey}'`);
|
|
337
|
+
}
|
|
338
|
+
const result = compileWorkflow(definition, {
|
|
339
|
+
workflow_key: workflowKey,
|
|
340
|
+
roles: cascaded.roles,
|
|
341
|
+
});
|
|
342
|
+
if (!result.ok || !result.plan) {
|
|
343
|
+
throw new CanonicalTargetCompilerError("workflow_compilation_failed", `workflow '${workflowKey}' compilation failed:\n${result.errors
|
|
344
|
+
.map((error) => ` ${error.path}: ${error.message}`)
|
|
345
|
+
.join("\n")}`);
|
|
346
|
+
}
|
|
347
|
+
const workflowPlan = canonicalRuntimeWorkflowPlan(definition, result.plan);
|
|
348
|
+
const controllerPlan = canonicalControllerHandoffIdentities({
|
|
349
|
+
...workflowPlan,
|
|
350
|
+
steps: workflowPlan.steps.map((step) => ({
|
|
351
|
+
...step,
|
|
352
|
+
handoff: step.handoff === null
|
|
353
|
+
? null
|
|
354
|
+
: {
|
|
355
|
+
...step.handoff,
|
|
356
|
+
produces: step.handoff.produces?.filter((artifact) => (artifact.required !== false || hasStableArtifactIdentity(artifact))),
|
|
357
|
+
},
|
|
358
|
+
blocked_items_path: undefined,
|
|
359
|
+
handoff_to: undefined,
|
|
360
|
+
max_handoffs: undefined,
|
|
361
|
+
on_handoff_exhausted: undefined,
|
|
362
|
+
on_fail: step.on_fail === "retry_with_feedback"
|
|
363
|
+
? step.on_fail
|
|
364
|
+
: undefined,
|
|
365
|
+
})),
|
|
366
|
+
});
|
|
367
|
+
return {
|
|
368
|
+
definition,
|
|
369
|
+
workflow_plan: workflowPlan,
|
|
370
|
+
executable_plan: adaptWorkflowPlanToExecutableRunPlan(controllerPlan, {
|
|
371
|
+
roles: cascaded.roles,
|
|
372
|
+
target_key: workflowKey,
|
|
373
|
+
input_contract: inputContract(dnas, workflowKey, definition, cascaded.roles),
|
|
374
|
+
source_provenance: sourceProvenance(sources),
|
|
375
|
+
global_constraints: constraintIr.prompt_directives.map((directive) => directive.text),
|
|
376
|
+
timeout_ms: request.attempt_policy?.timeout_ms,
|
|
377
|
+
cancellation_grace_ms: request.attempt_policy?.cancellation_grace_ms,
|
|
378
|
+
}),
|
|
379
|
+
};
|
|
380
|
+
};
|
|
381
|
+
try {
|
|
382
|
+
const diagnosis = compileComponent(diagnosisKey);
|
|
383
|
+
const fix = compileComponent(fixKey);
|
|
384
|
+
const plan = compileControllerPlan({
|
|
385
|
+
controller_key: request.target.key,
|
|
386
|
+
controller,
|
|
387
|
+
diagnosis_plan: diagnosis.executable_plan,
|
|
388
|
+
fix_plan: fix.executable_plan,
|
|
389
|
+
source_provenance: sourceProvenance(sources),
|
|
390
|
+
});
|
|
391
|
+
if (!constraintIr.compiled_ir_hash) {
|
|
392
|
+
throw new Error("compiled IR hash is required for runtime projection");
|
|
393
|
+
}
|
|
394
|
+
const variableIndex = indexWorkflowVariableScopes(dnas);
|
|
395
|
+
const defaultVariables = defaultResolvedVariableScopes(variableIndex);
|
|
396
|
+
const runtimePlans = Object.entries(cascaded.workflows).map(([workflowKey, definition]) => {
|
|
397
|
+
const result = compileWorkflow(definition, {
|
|
398
|
+
workflow_key: workflowKey,
|
|
399
|
+
roles: cascaded.roles,
|
|
400
|
+
});
|
|
401
|
+
if (!result.ok || !result.plan) {
|
|
402
|
+
throw new CanonicalTargetCompilerError("workflow_compilation_failed", `workflow '${workflowKey}' compilation failed:\n${result.errors
|
|
403
|
+
.map((error) => ` ${error.path}: ${error.message}`)
|
|
404
|
+
.join("\n")}`);
|
|
405
|
+
}
|
|
406
|
+
const runtimePlan = canonicalRuntimeWorkflowPlan(definition, result.plan);
|
|
407
|
+
const namespace = variableIndex.workflowNamespaces.get(workflowKey) ?? "";
|
|
408
|
+
return {
|
|
409
|
+
plan: runtimePlan,
|
|
410
|
+
resolvedVariables: resolvedVariablesForNamespace(defaultVariables, namespace),
|
|
411
|
+
runtimeInputRefs: inputContract(dnas, workflowKey, definition, cascaded.roles).inputs.map((input) => input.name),
|
|
412
|
+
workflowAsset: variableIndex.workflowAssets.get(workflowKey),
|
|
413
|
+
additionalRuntimeValues: constraintIr.verifier_specs?.filter((spec) => spec.workflow_name === workflowKey) ?? [],
|
|
414
|
+
};
|
|
415
|
+
});
|
|
416
|
+
return {
|
|
417
|
+
plan,
|
|
418
|
+
projection: {
|
|
419
|
+
roles: cascaded.roles,
|
|
420
|
+
constraint_ir: constraintIr,
|
|
421
|
+
workflow_manifest: createWorkflowRuntimeManifest({
|
|
422
|
+
plans: runtimePlans,
|
|
423
|
+
compiledAt: constraintIr.compiled_at,
|
|
424
|
+
compiledIrHash: constraintIr.compiled_ir_hash,
|
|
425
|
+
sourceDnaIds: constraintIr.source_dna_ids,
|
|
426
|
+
}),
|
|
427
|
+
workflow_plan: fix.workflow_plan,
|
|
428
|
+
workflow_asset: variableIndex.workflowAssets.get(fixKey) ?? null,
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
if (error instanceof CanonicalTargetCompilerError)
|
|
434
|
+
throw error;
|
|
435
|
+
throw new CanonicalTargetCompilerError("controller_compilation_failed", `controller '${request.target.key}' could not be adapted to an executable plan`, { cause: error instanceof ControllerPlanCompilerError ? error : error });
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const workflow = cascaded.workflows[request.target.key];
|
|
439
|
+
if (!workflow) {
|
|
440
|
+
throw new CanonicalTargetCompilerError("target_not_found", `workflow '${request.target.key}' not found; available: ${Object.keys(cascaded.workflows).sort().join(", ") || "(none)"}`);
|
|
441
|
+
}
|
|
442
|
+
const compiled = compileWorkflow(workflow, {
|
|
443
|
+
workflow_key: request.target.key,
|
|
444
|
+
roles: cascaded.roles,
|
|
445
|
+
});
|
|
446
|
+
if (!compiled.ok || !compiled.plan) {
|
|
447
|
+
throw new CanonicalTargetCompilerError("workflow_compilation_failed", `workflow '${request.target.key}' compilation failed:\n${compiled.errors
|
|
448
|
+
.map((error) => ` ${error.path}: ${error.message}`)
|
|
449
|
+
.join("\n")}`);
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
const canonicalPlan = canonicalRuntimeWorkflowPlan(workflow, compiled.plan);
|
|
453
|
+
const plan = adaptWorkflowPlanToExecutableRunPlan(canonicalPlan, {
|
|
454
|
+
roles: cascaded.roles,
|
|
455
|
+
target_key: request.target.key,
|
|
456
|
+
input_contract: inputContract(dnas, request.target.key, workflow, cascaded.roles),
|
|
457
|
+
source_provenance: sourceProvenance(sources),
|
|
458
|
+
global_constraints: constraintIr.prompt_directives.map((directive) => directive.text),
|
|
459
|
+
timeout_ms: request.attempt_policy?.timeout_ms,
|
|
460
|
+
cancellation_grace_ms: request.attempt_policy?.cancellation_grace_ms,
|
|
461
|
+
});
|
|
462
|
+
if (!constraintIr.compiled_ir_hash) {
|
|
463
|
+
throw new Error("compiled IR hash is required for runtime projection");
|
|
464
|
+
}
|
|
465
|
+
const variableIndex = indexWorkflowVariableScopes(dnas);
|
|
466
|
+
const defaultVariables = defaultResolvedVariableScopes(variableIndex);
|
|
467
|
+
const runtimePlans = Object.entries(cascaded.workflows).map(([workflowKey, definition]) => {
|
|
468
|
+
const result = workflowKey === request.target.key
|
|
469
|
+
? compiled
|
|
470
|
+
: compileWorkflow(definition, {
|
|
471
|
+
workflow_key: workflowKey,
|
|
472
|
+
roles: cascaded.roles,
|
|
473
|
+
});
|
|
474
|
+
if (!result.ok || !result.plan) {
|
|
475
|
+
throw new CanonicalTargetCompilerError("workflow_compilation_failed", `workflow '${workflowKey}' compilation failed:\n${result.errors
|
|
476
|
+
.map((error) => ` ${error.path}: ${error.message}`)
|
|
477
|
+
.join("\n")}`);
|
|
478
|
+
}
|
|
479
|
+
const runtimePlan = canonicalRuntimeWorkflowPlan(definition, result.plan);
|
|
480
|
+
const namespace = variableIndex.workflowNamespaces.get(workflowKey) ?? "";
|
|
481
|
+
return {
|
|
482
|
+
plan: runtimePlan,
|
|
483
|
+
resolvedVariables: resolvedVariablesForNamespace(defaultVariables, namespace),
|
|
484
|
+
runtimeInputRefs: inputContract(dnas, workflowKey, definition, cascaded.roles).inputs.map((input) => input.name),
|
|
485
|
+
workflowAsset: variableIndex.workflowAssets.get(workflowKey),
|
|
486
|
+
additionalRuntimeValues: constraintIr.verifier_specs?.filter((spec) => spec.workflow_name === workflowKey) ?? [],
|
|
487
|
+
};
|
|
488
|
+
});
|
|
489
|
+
return {
|
|
490
|
+
plan,
|
|
491
|
+
projection: {
|
|
492
|
+
roles: cascaded.roles,
|
|
493
|
+
constraint_ir: constraintIr,
|
|
494
|
+
workflow_manifest: createWorkflowRuntimeManifest({
|
|
495
|
+
plans: runtimePlans,
|
|
496
|
+
compiledAt: constraintIr.compiled_at,
|
|
497
|
+
compiledIrHash: constraintIr.compiled_ir_hash,
|
|
498
|
+
sourceDnaIds: constraintIr.source_dna_ids,
|
|
499
|
+
}),
|
|
500
|
+
workflow_plan: canonicalPlan,
|
|
501
|
+
workflow_asset: variableIndex.workflowAssets.get(request.target.key) ?? null,
|
|
502
|
+
},
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
catch (error) {
|
|
506
|
+
if (error instanceof CanonicalTargetCompilerError)
|
|
507
|
+
throw error;
|
|
508
|
+
throw new CanonicalTargetCompilerError("workflow_adaptation_failed", `workflow '${request.target.key}' could not be adapted to an executable plan`, { cause: error });
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
export function compileCanonicalTarget(request) {
|
|
513
|
+
return new CanonicalTargetCompiler().compile(request);
|
|
514
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function canonicalClaudeAgentName(role, workerSessionId) {
|
|
3
|
+
if (role.length === 0)
|
|
4
|
+
throw new TypeError("role must not be empty");
|
|
5
|
+
if (workerSessionId.length === 0) {
|
|
6
|
+
throw new TypeError("workerSessionId must not be empty");
|
|
7
|
+
}
|
|
8
|
+
const identity = createHash("sha256")
|
|
9
|
+
.update(role, "utf8")
|
|
10
|
+
.update("\0", "utf8")
|
|
11
|
+
.update(workerSessionId, "utf8")
|
|
12
|
+
.digest("hex")
|
|
13
|
+
.slice(0, 32);
|
|
14
|
+
return `dna-attempt-${identity}`;
|
|
15
|
+
}
|
|
16
|
+
export function legacyClaudeAgentName(role) {
|
|
17
|
+
return `dna-${role
|
|
18
|
+
.replace(/([a-z0-9])([A-Z])/gu, "$1-$2")
|
|
19
|
+
.replace(/[_\s]+/gu, "-")
|
|
20
|
+
.toLowerCase()}`;
|
|
21
|
+
}
|