intentdna 1.7.0 → 1.7.4
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 +26 -15
- package/dist/cli/commands/feedback.d.ts +2 -2
- package/dist/cli/commands/feedback.js +5 -5
- package/dist/cli/commands/sync.js +8 -5
- package/dist/cli/commands/verify.d.ts +32 -0
- package/dist/cli/commands/verify.js +173 -6
- package/dist/cli/index.js +3 -1
- package/dist/compiler/activate.js +11 -6
- package/dist/compiler/cascade.d.ts +3 -1
- package/dist/compiler/cascade.js +23 -1
- package/dist/compiler/compile.js +1 -0
- package/dist/compiler/index.d.ts +5 -1
- package/dist/compiler/index.js +7 -4
- package/dist/compiler/input-resolver.d.ts +8 -1
- package/dist/compiler/input-resolver.js +128 -22
- package/dist/compiler/provenance.d.ts +8 -0
- package/dist/compiler/provenance.js +127 -0
- package/dist/governance/index.d.ts +4 -3
- package/dist/governance/index.js +3 -4
- package/dist/governance/runtime-decision-event.d.ts +85 -0
- package/dist/governance/runtime-decision-event.js +231 -0
- package/dist/governance/types.d.ts +3 -3
- package/dist/governance/types.js +3 -3
- package/dist/hooks/cli.js +162 -13
- package/dist/hooks/state.d.ts +3 -10
- package/dist/hooks/state.js +147 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/tools-observability.js +3 -3
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +17 -7
- package/dist/report/kernel-report.d.ts +27 -0
- package/dist/report/kernel-report.js +60 -19
- package/dist/report/kernel-signals.d.ts +5 -2
- package/dist/report/kernel-signals.js +84 -2
- package/dist/report/report-package.d.ts +9 -1
- package/dist/report/report-package.js +32 -27
- package/dist/runtime/claude-sdk.d.ts +9 -4
- package/dist/runtime/claude-sdk.js +9 -0
- package/dist/runtime/plugin-adapter.d.ts +5 -1
- package/dist/runtime/plugin-adapter.js +2 -0
- package/dist/schema/types.d.ts +51 -0
- package/package.json +1 -1
- package/spec/README.md +1 -1
- package/spec/schema-spec.md +78 -1
package/dist/compiler/cascade.js
CHANGED
|
@@ -10,6 +10,21 @@
|
|
|
10
10
|
* - attract/repel: same target accumulates, different targets independent
|
|
11
11
|
* - amplify/suppress: factors multiply (1.5x × 2.0x = 3.0x)
|
|
12
12
|
*/
|
|
13
|
+
const PROTECTED_THRESHOLD_LAYERS = new Set(["species", "enterprise"]);
|
|
14
|
+
function originForDNA(dna) {
|
|
15
|
+
return {
|
|
16
|
+
source_dna_id: dna.id,
|
|
17
|
+
source_layer: dna.type,
|
|
18
|
+
cascade_priority: dna.cascade.priority,
|
|
19
|
+
protected_threshold: PROTECTED_THRESHOLD_LAYERS.has(dna.type),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function withCodonOrigins(gene, origin) {
|
|
23
|
+
return {
|
|
24
|
+
...gene,
|
|
25
|
+
codons: gene.codons.map((codon) => codon.type === "threshold" ? { ...codon, origin } : { ...codon }),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
13
28
|
/**
|
|
14
29
|
* Normalize workflow/workflows from an IntentDNA into a unified Record.
|
|
15
30
|
* Validation ensures workflow and workflows are mutually exclusive.
|
|
@@ -228,6 +243,7 @@ export function cascadeDNA(layers) {
|
|
|
228
243
|
const sorted = [...layers].sort((a, b) => a.cascade.priority - b.cascade.priority);
|
|
229
244
|
const mergedGenes = {};
|
|
230
245
|
const geneSourceNs = {}; // track which namespace defined each gene
|
|
246
|
+
const protectedThresholdGenes = new Set();
|
|
231
247
|
const mergedContexts = {};
|
|
232
248
|
const mergedRoles = {};
|
|
233
249
|
const mergedWorkflows = {};
|
|
@@ -239,7 +255,12 @@ export function cascadeDNA(layers) {
|
|
|
239
255
|
sourceIds.push(dna.id);
|
|
240
256
|
const ns = dna.namespace;
|
|
241
257
|
// Merge genes (cross-namespace: additive merge + warning)
|
|
242
|
-
|
|
258
|
+
const origin = originForDNA(dna);
|
|
259
|
+
for (const [name, rawGene] of Object.entries(dna.genes)) {
|
|
260
|
+
const gene = withCodonOrigins(rawGene, origin);
|
|
261
|
+
if (origin.protected_threshold && gene.codons.some((codon) => codon.type === "threshold")) {
|
|
262
|
+
protectedThresholdGenes.add(name);
|
|
263
|
+
}
|
|
243
264
|
if (mergedGenes[name]) {
|
|
244
265
|
const existingNs = geneSourceNs[name];
|
|
245
266
|
if (ns !== existingNs) {
|
|
@@ -332,5 +353,6 @@ export function cascadeDNA(layers) {
|
|
|
332
353
|
context_sources: contextSources,
|
|
333
354
|
planning_context: planningContext,
|
|
334
355
|
verifier_policy: verifierPolicy,
|
|
356
|
+
protected_threshold_genes: protectedThresholdGenes.size > 0 ? [...protectedThresholdGenes].sort() : undefined,
|
|
335
357
|
};
|
|
336
358
|
}
|
package/dist/compiler/compile.js
CHANGED
|
@@ -105,6 +105,7 @@ function geneToGates(name, gene) {
|
|
|
105
105
|
action: "block",
|
|
106
106
|
message: `DNA threshold violation: ${codon.condition} (gene: ${name})`,
|
|
107
107
|
source_gene: name,
|
|
108
|
+
origin: codon.origin,
|
|
108
109
|
});
|
|
109
110
|
}
|
|
110
111
|
if (codon.type === "sense" && codon.response === "escalate_to_human") {
|
package/dist/compiler/index.d.ts
CHANGED
|
@@ -9,10 +9,12 @@ export { cascadeDNA } from "./cascade.js";
|
|
|
9
9
|
export { activateDNA } from "./activate.js";
|
|
10
10
|
export { compileDNA } from "./compile.js";
|
|
11
11
|
export { compileWorkflow } from "./workflow.js";
|
|
12
|
+
export { PROVENANCE_LAYERS, PROVENANCE_SCHEMA_VERSION, attachProvenance, buildCascadeLayers, buildConstraintWinners, hashObject, hashString, } from "./provenance.js";
|
|
12
13
|
export type { CompileWorkflowOptions, CompileWorkflowResult, CompileWorkflowError, } from "./workflow.js";
|
|
13
14
|
export type { Diagnostic, DiagnosticSeverity } from "./diagnostics.js";
|
|
14
15
|
export { DNADiagnosticsError, formatDiagnostic, formatDiagnostics } from "./diagnostics.js";
|
|
15
|
-
export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
|
|
16
|
+
export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, expandDNAInputs, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
|
|
17
|
+
export type { ExpandedDNAInput, ExpandDNAInputOptions } from "./input-resolver.js";
|
|
16
18
|
/**
|
|
17
19
|
* Load and parse a DNA file from disk.
|
|
18
20
|
*/
|
|
@@ -20,6 +22,8 @@ export declare function loadDNA(filePath: string): Promise<IntentDNA>;
|
|
|
20
22
|
export interface CompileOptions {
|
|
21
23
|
context?: string;
|
|
22
24
|
role?: string;
|
|
25
|
+
cwd?: string;
|
|
26
|
+
enterprisePolicyDirs?: string[];
|
|
23
27
|
/** Additional epigenetic markers from evolution store */
|
|
24
28
|
epigenetic_markers?: EpigeneticMarker[];
|
|
25
29
|
}
|
package/dist/compiler/index.js
CHANGED
|
@@ -8,13 +8,15 @@ import { cascadeDNA } from "./cascade.js";
|
|
|
8
8
|
import { activateDNA } from "./activate.js";
|
|
9
9
|
import { compileDNA } from "./compile.js";
|
|
10
10
|
import { DNADiagnosticsError, formatDiagnostics } from "./diagnostics.js";
|
|
11
|
-
import { loadDNAWithDiagnostics } from "./input-resolver.js";
|
|
11
|
+
import { expandDNAInputs, loadDNAWithDiagnostics } from "./input-resolver.js";
|
|
12
|
+
import { attachProvenance } from "./provenance.js";
|
|
12
13
|
export { cascadeDNA } from "./cascade.js";
|
|
13
14
|
export { activateDNA } from "./activate.js";
|
|
14
15
|
export { compileDNA } from "./compile.js";
|
|
15
16
|
export { compileWorkflow } from "./workflow.js";
|
|
17
|
+
export { PROVENANCE_LAYERS, PROVENANCE_SCHEMA_VERSION, attachProvenance, buildCascadeLayers, buildConstraintWinners, hashObject, hashString, } from "./provenance.js";
|
|
16
18
|
export { DNADiagnosticsError, formatDiagnostic, formatDiagnostics } from "./diagnostics.js";
|
|
17
|
-
export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
|
|
19
|
+
export { assertNoNamespaceCollisions, checkNamespaceCollisions, detectDNAConfigs, expandDNAInputFiles, expandDNAInputs, loadDNAWithDiagnostics, resolveDNAInputs, resolveSpeciesReference, } from "./input-resolver.js";
|
|
18
20
|
/**
|
|
19
21
|
* Load and parse a DNA file from disk.
|
|
20
22
|
*/
|
|
@@ -33,8 +35,9 @@ export async function compileFromFiles(filePaths, contextOrOptions) {
|
|
|
33
35
|
const opts = typeof contextOrOptions === "string"
|
|
34
36
|
? { context: contextOrOptions }
|
|
35
37
|
: contextOrOptions ?? {};
|
|
38
|
+
const expanded = await expandDNAInputs(filePaths, { cwd: opts.cwd, enterprisePolicyDirs: opts.enterprisePolicyDirs });
|
|
36
39
|
const dnas = [];
|
|
37
|
-
for (const path of
|
|
40
|
+
for (const path of expanded.files) {
|
|
38
41
|
dnas.push(await loadDNA(path));
|
|
39
42
|
}
|
|
40
43
|
const cascaded = cascadeDNA(dnas);
|
|
@@ -43,5 +46,5 @@ export async function compileFromFiles(filePaths, contextOrOptions) {
|
|
|
43
46
|
cascaded.epigenetic_markers.push(...opts.epigenetic_markers);
|
|
44
47
|
}
|
|
45
48
|
const activated = activateDNA(cascaded, opts.context ?? null, opts.role ?? null);
|
|
46
|
-
return compileDNA(activated, cascaded);
|
|
49
|
+
return attachProvenance(compileDNA(activated, cascaded), expanded.provenance);
|
|
47
50
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IntentDNA } from "../schema/types.js";
|
|
1
|
+
import type { DNASourceProvenance, IntentDNA } from "../schema/types.js";
|
|
2
2
|
import type { Diagnostic } from "./diagnostics.js";
|
|
3
3
|
export interface ResolveDNAInputOptions {
|
|
4
4
|
cwd?: string;
|
|
@@ -14,7 +14,14 @@ export declare function resolveSpeciesReference(ref: string): string | null;
|
|
|
14
14
|
export declare function detectDNAConfigs(projectDir?: string): Promise<string[]>;
|
|
15
15
|
export interface ExpandDNAInputOptions {
|
|
16
16
|
cwd?: string;
|
|
17
|
+
enterprisePolicyDirs?: string[];
|
|
17
18
|
}
|
|
19
|
+
export interface ExpandedDNAInput {
|
|
20
|
+
files: string[];
|
|
21
|
+
provenance: DNASourceProvenance[];
|
|
22
|
+
diagnostics: Diagnostic[];
|
|
23
|
+
}
|
|
24
|
+
export declare function expandDNAInputs(files: string[], options?: ExpandDNAInputOptions): Promise<ExpandedDNAInput>;
|
|
18
25
|
export declare function expandDNAInputFiles(files: string[], options?: ExpandDNAInputOptions): Promise<string[]>;
|
|
19
26
|
export declare function checkNamespaceCollisions(configPaths: string[]): Promise<Diagnostic[]>;
|
|
20
27
|
export declare function assertNoNamespaceCollisions(configPaths: string[]): Promise<void>;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
1
3
|
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
4
|
import { dirname, resolve } from "node:path";
|
|
3
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -57,35 +59,139 @@ export async function detectDNAConfigs(projectDir = process.cwd()) {
|
|
|
57
59
|
}
|
|
58
60
|
return [];
|
|
59
61
|
}
|
|
60
|
-
|
|
62
|
+
function fingerprint(raw) {
|
|
63
|
+
return `sha256:${createHash("sha256").update(raw, "utf-8").digest("hex")}`;
|
|
64
|
+
}
|
|
65
|
+
function enterprisePolicyCandidates(name, cwd, options) {
|
|
66
|
+
const configuredDirs = [
|
|
67
|
+
...(process.env.INTENTDNA_ENTERPRISE_POLICY_DIR ? [process.env.INTENTDNA_ENTERPRISE_POLICY_DIR] : []),
|
|
68
|
+
...(options?.enterprisePolicyDirs ?? []),
|
|
69
|
+
];
|
|
70
|
+
const dirs = [
|
|
71
|
+
...configuredDirs.map((dir) => ({ dir, trust: "admin_shared" })),
|
|
72
|
+
{ dir: resolve(cwd, ".dna", "policies", "enterprise"), trust: "project_local" },
|
|
73
|
+
{ dir: resolve(homedir(), ".intentdna", "policies", "enterprise"), trust: "user_local" },
|
|
74
|
+
];
|
|
75
|
+
const names = [`${name}.dna.yaml`, `${name}.dna.yml`, `${name}.dna.json`];
|
|
76
|
+
return dirs.flatMap(({ dir, trust }) => names.map((file) => ({ path: resolve(dir, file), trust })));
|
|
77
|
+
}
|
|
78
|
+
async function resolveEnterpriseReference(ref, cwd, options) {
|
|
79
|
+
if (!ref.startsWith("enterprise:"))
|
|
80
|
+
return {};
|
|
81
|
+
const name = ref.slice("enterprise:".length);
|
|
82
|
+
for (const candidate of enterprisePolicyCandidates(name, cwd, options)) {
|
|
83
|
+
if (await fileExists(candidate.path))
|
|
84
|
+
return candidate;
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
}
|
|
88
|
+
function resolveFileReference(ref, fromFile) {
|
|
89
|
+
if (!ref.startsWith("file:"))
|
|
90
|
+
return {};
|
|
91
|
+
const raw = ref.slice("file:".length);
|
|
92
|
+
if (!raw)
|
|
93
|
+
return {};
|
|
94
|
+
return { path: resolve(dirname(fromFile), raw), trust: "explicit_file" };
|
|
95
|
+
}
|
|
96
|
+
function sourceTrustForInput(file, ref, cwd) {
|
|
97
|
+
if (ref.startsWith("species:"))
|
|
98
|
+
return "builtin";
|
|
99
|
+
if (ref.startsWith("enterprise:"))
|
|
100
|
+
return file.includes(`${resolve(cwd, ".dna", "policies", "enterprise")}`) ? "project_local" : "admin_shared";
|
|
101
|
+
if (ref.startsWith("file:"))
|
|
102
|
+
return "explicit_file";
|
|
103
|
+
return "input";
|
|
104
|
+
}
|
|
105
|
+
async function readDNAWithRaw(path) {
|
|
106
|
+
const raw = await readFile(path, "utf-8");
|
|
107
|
+
return { raw, dna: parseDNAContent(path, raw) };
|
|
108
|
+
}
|
|
109
|
+
export async function expandDNAInputs(files, options) {
|
|
61
110
|
const cwd = options?.cwd ?? process.cwd();
|
|
62
|
-
const
|
|
111
|
+
const ordered = [];
|
|
63
112
|
const seen = new Set();
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (position === "front")
|
|
69
|
-
expanded.unshift(path);
|
|
70
|
-
else
|
|
71
|
-
expanded.push(path);
|
|
72
|
-
};
|
|
73
|
-
for (const file of files) {
|
|
113
|
+
const diagnostics = [];
|
|
114
|
+
const provenance = new Map();
|
|
115
|
+
const stack = [];
|
|
116
|
+
const visit = async (file, ref, inheritedBy, trust) => {
|
|
74
117
|
const abs = resolve(cwd, file);
|
|
75
|
-
|
|
118
|
+
if (stack.includes(abs)) {
|
|
119
|
+
diagnostics.push({
|
|
120
|
+
severity: "error",
|
|
121
|
+
code: "inheritance_cycle",
|
|
122
|
+
message: `DNA inheritance cycle detected: ${[...stack, abs].join(" -> ")}`,
|
|
123
|
+
file: abs,
|
|
124
|
+
});
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
if (seen.has(abs))
|
|
128
|
+
return;
|
|
129
|
+
let loaded;
|
|
76
130
|
try {
|
|
77
|
-
|
|
78
|
-
for (const ref of dna.cascade?.inherits ?? []) {
|
|
79
|
-
const speciesPath = resolveSpeciesReference(ref);
|
|
80
|
-
if (speciesPath)
|
|
81
|
-
add(speciesPath, "front");
|
|
82
|
-
}
|
|
131
|
+
loaded = await readDNAWithRaw(abs);
|
|
83
132
|
}
|
|
84
|
-
catch {
|
|
85
|
-
|
|
133
|
+
catch (error) {
|
|
134
|
+
diagnostics.push({
|
|
135
|
+
severity: "error",
|
|
136
|
+
code: "inheritance_unresolved",
|
|
137
|
+
message: `Unable to resolve inherited DNA reference '${ref}': ${error instanceof Error ? error.message : String(error)}`,
|
|
138
|
+
file: inheritedBy ?? abs,
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
86
141
|
}
|
|
142
|
+
stack.push(abs);
|
|
143
|
+
for (const inheritRef of loaded.dna.cascade?.inherits ?? []) {
|
|
144
|
+
const speciesPath = resolveSpeciesReference(inheritRef);
|
|
145
|
+
if (speciesPath) {
|
|
146
|
+
await visit(speciesPath, inheritRef, abs, "builtin");
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const fileRef = resolveFileReference(inheritRef, abs);
|
|
150
|
+
if (fileRef.path) {
|
|
151
|
+
await visit(fileRef.path, inheritRef, abs, fileRef.trust);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const enterpriseRef = await resolveEnterpriseReference(inheritRef, cwd, options);
|
|
155
|
+
if (enterpriseRef.path) {
|
|
156
|
+
await visit(enterpriseRef.path, inheritRef, abs, enterpriseRef.trust);
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (inheritRef.startsWith("enterprise:") || inheritRef.startsWith("file:")) {
|
|
160
|
+
diagnostics.push({
|
|
161
|
+
severity: "error",
|
|
162
|
+
code: "inheritance_unresolved",
|
|
163
|
+
message: `Unable to resolve inherited DNA reference '${inheritRef}'`,
|
|
164
|
+
file: abs,
|
|
165
|
+
path: "cascade.inherits",
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
stack.pop();
|
|
170
|
+
seen.add(abs);
|
|
171
|
+
ordered.push(abs);
|
|
172
|
+
provenance.set(abs, {
|
|
173
|
+
source_id: loaded.dna.id,
|
|
174
|
+
source_ref: ref,
|
|
175
|
+
resolved_path: abs,
|
|
176
|
+
fingerprint: fingerprint(loaded.raw),
|
|
177
|
+
type: loaded.dna.type,
|
|
178
|
+
cascade_priority: loaded.dna.cascade.priority,
|
|
179
|
+
version: loaded.dna.version,
|
|
180
|
+
trust: trust ?? sourceTrustForInput(abs, ref, cwd),
|
|
181
|
+
inherited_by: inheritedBy,
|
|
182
|
+
genes: Object.keys(loaded.dna.genes ?? {}).sort(),
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
for (const file of files) {
|
|
186
|
+
await visit(resolve(cwd, file), file, undefined, "input");
|
|
187
|
+
}
|
|
188
|
+
if (hasDiagnosticErrors(diagnostics)) {
|
|
189
|
+
throw new DNADiagnosticsError("DNA inheritance resolution failed", diagnostics);
|
|
87
190
|
}
|
|
88
|
-
return
|
|
191
|
+
return { files: ordered, provenance: ordered.map((file) => provenance.get(file)).filter(Boolean), diagnostics };
|
|
192
|
+
}
|
|
193
|
+
export async function expandDNAInputFiles(files, options) {
|
|
194
|
+
return (await expandDNAInputs(files, options)).files;
|
|
89
195
|
}
|
|
90
196
|
export async function checkNamespaceCollisions(configPaths) {
|
|
91
197
|
const diagnostics = [];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CascadeLayerProvenance, ConstraintIR, ConstraintProvenance, DNASourceProvenance, ProvenanceCascadeLayerName } from "../schema/types.js";
|
|
2
|
+
export declare const PROVENANCE_SCHEMA_VERSION: "intentdna.provenance.v1.7.2";
|
|
3
|
+
export declare const PROVENANCE_LAYERS: readonly ProvenanceCascadeLayerName[];
|
|
4
|
+
export declare function hashString(content: string): string;
|
|
5
|
+
export declare function hashObject(value: unknown): string;
|
|
6
|
+
export declare function buildCascadeLayers(sources: DNASourceProvenance[]): Record<ProvenanceCascadeLayerName, CascadeLayerProvenance>;
|
|
7
|
+
export declare function buildConstraintWinners(ir: ConstraintIR, sources: DNASourceProvenance[]): ConstraintProvenance[];
|
|
8
|
+
export declare function attachProvenance(ir: ConstraintIR, sources: DNASourceProvenance[]): ConstraintIR;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export const PROVENANCE_SCHEMA_VERSION = "intentdna.provenance.v1.7.2";
|
|
3
|
+
export const PROVENANCE_LAYERS = [
|
|
4
|
+
"species",
|
|
5
|
+
"enterprise",
|
|
6
|
+
"project",
|
|
7
|
+
"personal",
|
|
8
|
+
"role",
|
|
9
|
+
"context",
|
|
10
|
+
"task",
|
|
11
|
+
];
|
|
12
|
+
function stable(value) {
|
|
13
|
+
if (Array.isArray(value))
|
|
14
|
+
return `[${value.map(stable).join(",")}]`;
|
|
15
|
+
if (value && typeof value === "object") {
|
|
16
|
+
return `{${Object.entries(value)
|
|
17
|
+
.filter(([key]) => key !== "compiled_ir_hash" && key !== "provenance" && key !== "compiled_at")
|
|
18
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
19
|
+
.map(([key, entry]) => `${JSON.stringify(key)}:${stable(entry)}`)
|
|
20
|
+
.join(",")}}`;
|
|
21
|
+
}
|
|
22
|
+
return JSON.stringify(value);
|
|
23
|
+
}
|
|
24
|
+
export function hashString(content) {
|
|
25
|
+
return `sha256:${createHash("sha256").update(content, "utf-8").digest("hex")}`;
|
|
26
|
+
}
|
|
27
|
+
export function hashObject(value) {
|
|
28
|
+
return hashString(stable(value));
|
|
29
|
+
}
|
|
30
|
+
function placeholderLayer(layer) {
|
|
31
|
+
return {
|
|
32
|
+
layer,
|
|
33
|
+
source_id: "unknown",
|
|
34
|
+
source_ref: "unknown",
|
|
35
|
+
fingerprint: "unknown",
|
|
36
|
+
status: "unknown",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function buildCascadeLayers(sources) {
|
|
40
|
+
const layers = Object.fromEntries(PROVENANCE_LAYERS.map((layer) => [layer, placeholderLayer(layer)]));
|
|
41
|
+
for (const layer of PROVENANCE_LAYERS) {
|
|
42
|
+
const layerSources = sources.filter((source) => source.type === layer);
|
|
43
|
+
if (layerSources.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
const fingerprint = hashObject(layerSources.map((source) => ({
|
|
46
|
+
source_id: source.source_id,
|
|
47
|
+
source_ref: source.source_ref,
|
|
48
|
+
fingerprint: source.fingerprint,
|
|
49
|
+
cascade_priority: source.cascade_priority,
|
|
50
|
+
})));
|
|
51
|
+
layers[layer] = {
|
|
52
|
+
layer,
|
|
53
|
+
source_id: layerSources.map((source) => source.source_id).join("+"),
|
|
54
|
+
source_ref: layerSources.map((source) => source.source_ref).join(","),
|
|
55
|
+
fingerprint,
|
|
56
|
+
status: "active",
|
|
57
|
+
sources: layerSources.map((source) => source.source_id),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return layers;
|
|
61
|
+
}
|
|
62
|
+
function sourceForGene(sources, geneId) {
|
|
63
|
+
return [...sources].reverse().find((source) => source.genes?.includes(geneId));
|
|
64
|
+
}
|
|
65
|
+
function constraintId(kind, sourceGene, discriminator) {
|
|
66
|
+
return `${kind}:${sourceGene || "unknown"}:${hashString(discriminator).slice("sha256:".length, "sha256:".length + 12)}`;
|
|
67
|
+
}
|
|
68
|
+
function actionFor(kind, constraint) {
|
|
69
|
+
if (constraint.action === "escalate")
|
|
70
|
+
return "escalate";
|
|
71
|
+
if (constraint.action === "warn")
|
|
72
|
+
return "warn";
|
|
73
|
+
if (kind === "post_validator")
|
|
74
|
+
return "validate";
|
|
75
|
+
return "block";
|
|
76
|
+
}
|
|
77
|
+
function constraintFrom(kind, constraint, sources) {
|
|
78
|
+
const sourceGene = constraint.source_gene ?? "unknown";
|
|
79
|
+
const originSource = constraint.origin
|
|
80
|
+
? sources.find((source) => source.source_id === constraint.origin?.source_dna_id)
|
|
81
|
+
: undefined;
|
|
82
|
+
const source = originSource ?? sourceForGene(sources, sourceGene);
|
|
83
|
+
const discriminator = constraint.condition ?? constraint.target ?? constraint.check ?? stable(constraint);
|
|
84
|
+
const sourceLayer = (constraint.origin?.source_layer ?? source?.type);
|
|
85
|
+
const sourceDnaId = constraint.origin?.source_dna_id ?? source?.source_id;
|
|
86
|
+
return {
|
|
87
|
+
constraint_id: constraintId(kind, sourceGene, discriminator),
|
|
88
|
+
source_layer: sourceLayer ?? "unknown",
|
|
89
|
+
source_dna_id: sourceDnaId ?? "unknown",
|
|
90
|
+
gene_id: sourceGene,
|
|
91
|
+
codon_type: kind === "tool_filter" ? "repel" : kind === "post_validator" ? "sense" : "threshold",
|
|
92
|
+
action: actionFor(kind, constraint),
|
|
93
|
+
provenance_reason: sourceDnaId && sourceLayer
|
|
94
|
+
? `${kind} '${discriminator}' compiled from ${sourceLayer} DNA '${sourceDnaId}'`
|
|
95
|
+
: `${kind} '${discriminator}' has no resolved source DNA`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export function buildConstraintWinners(ir, sources) {
|
|
99
|
+
return [
|
|
100
|
+
...ir.pre_execution_gates.map((gate) => constraintFrom("pre_gate", gate, sources)),
|
|
101
|
+
...ir.tool_filters.map((filter) => constraintFrom("tool_filter", filter, sources)),
|
|
102
|
+
...ir.post_execution_validators.map((validator) => constraintFrom("post_validator", validator, sources)),
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
export function attachProvenance(ir, sources) {
|
|
106
|
+
const baseIR = {
|
|
107
|
+
...ir,
|
|
108
|
+
provenance: undefined,
|
|
109
|
+
compiled_ir_hash: undefined,
|
|
110
|
+
};
|
|
111
|
+
const compiledHash = hashObject(baseIR);
|
|
112
|
+
const enterprise = sources.find((source) => source.type === "enterprise");
|
|
113
|
+
const manifest = {
|
|
114
|
+
schema_version: PROVENANCE_SCHEMA_VERSION,
|
|
115
|
+
policy_bundle_id: enterprise?.source_id ?? "unknown",
|
|
116
|
+
policy_bundle_version: enterprise?.version ?? "unknown",
|
|
117
|
+
sources,
|
|
118
|
+
cascade_layers: buildCascadeLayers(sources),
|
|
119
|
+
constraint_winners: buildConstraintWinners(ir, sources),
|
|
120
|
+
compiled_ir_hash: compiledHash,
|
|
121
|
+
};
|
|
122
|
+
return {
|
|
123
|
+
...ir,
|
|
124
|
+
compiled_ir_hash: compiledHash,
|
|
125
|
+
provenance: manifest,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Intent DNA —
|
|
2
|
+
* Intent DNA — Governance Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Currently exports type definitions only — no runtime implementation.
|
|
4
|
+
* Exports the v1.7.1 enterprise evidence contract plus reserved remote governance interfaces.
|
|
6
5
|
*/
|
|
7
6
|
export type { AuditReport, RemoteDNAPolicy, PolicyContent, PolicyScope, PolicyUpdate, GovernanceConfig, GovernanceClient, GovernanceResponse, GovernanceSubscription, } from "./types.js";
|
|
7
|
+
export { CASCADE_LAYER_NAMES, RUNTIME_DECISION_EVENT_SCHEMA_VERSION, validateRuntimeDecisionEvent, } from "./runtime-decision-event.js";
|
|
8
|
+
export type { CascadeDecisionTrace, CascadeLayerFingerprint, CascadeLayerName, CascadeLayers, EnforcementPoint, ExplicitPlaceholder, HarnessRuntimeContext, PolicyBundle, RuntimeDecision, RuntimeDecisionEvent, RuntimeDecisionEventValidationResult, RuntimeEvidenceRef, WinningConstraint, } from "./runtime-decision-event.js";
|
package/dist/governance/index.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Intent DNA —
|
|
2
|
+
* Intent DNA — Governance Module
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* Currently exports type definitions only — no runtime implementation.
|
|
4
|
+
* Exports the v1.7.1 enterprise evidence contract plus reserved remote governance interfaces.
|
|
6
5
|
*/
|
|
7
|
-
export {};
|
|
6
|
+
export { CASCADE_LAYER_NAMES, RUNTIME_DECISION_EVENT_SCHEMA_VERSION, validateRuntimeDecisionEvent, } from "./runtime-decision-event.js";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export declare const RUNTIME_DECISION_EVENT_SCHEMA_VERSION: "intentdna.runtime_decision_event.v1.7.1";
|
|
2
|
+
export declare const CASCADE_LAYER_NAMES: readonly ["species", "enterprise", "project", "personal", "role", "context", "task"];
|
|
3
|
+
export type ExplicitPlaceholder = "unknown" | "unsupported";
|
|
4
|
+
export type RuntimeDecision = "allow" | "warn" | "block" | "escalate" | "validate";
|
|
5
|
+
export type EnforcementPoint = "hook" | "sdk" | "ci" | "plugin_runtime" | "mcp_external_write" | ExplicitPlaceholder;
|
|
6
|
+
export type CascadeLayerName = typeof CASCADE_LAYER_NAMES[number];
|
|
7
|
+
export type RuntimeEvidenceRefType = "trace" | "audit" | "artifact" | "verifier" | "handoff" | "legacy" | ExplicitPlaceholder;
|
|
8
|
+
export interface PolicyBundle {
|
|
9
|
+
policy_bundle_id: string;
|
|
10
|
+
policy_bundle_version: string;
|
|
11
|
+
source_ref: string;
|
|
12
|
+
fingerprint: string;
|
|
13
|
+
}
|
|
14
|
+
export interface CascadeLayerFingerprint {
|
|
15
|
+
layer: CascadeLayerName;
|
|
16
|
+
source_id: string;
|
|
17
|
+
source_ref: string;
|
|
18
|
+
fingerprint: string;
|
|
19
|
+
status: "active" | ExplicitPlaceholder;
|
|
20
|
+
}
|
|
21
|
+
export type CascadeLayers = Record<CascadeLayerName, CascadeLayerFingerprint>;
|
|
22
|
+
export interface WinningConstraint {
|
|
23
|
+
constraint_id: string;
|
|
24
|
+
source_layer: CascadeLayerName | ExplicitPlaceholder;
|
|
25
|
+
source_dna_id: string;
|
|
26
|
+
gene_id: string;
|
|
27
|
+
codon_type: string;
|
|
28
|
+
action: RuntimeDecision;
|
|
29
|
+
provenance_reason: string;
|
|
30
|
+
}
|
|
31
|
+
export interface CascadeDecisionTrace {
|
|
32
|
+
cascade_layers: CascadeLayers;
|
|
33
|
+
winning_constraint: WinningConstraint;
|
|
34
|
+
compiled_ir_hash: string;
|
|
35
|
+
decision_reason: string;
|
|
36
|
+
}
|
|
37
|
+
export interface RuntimeEvidenceRef {
|
|
38
|
+
type: RuntimeEvidenceRefType;
|
|
39
|
+
ref: string;
|
|
40
|
+
}
|
|
41
|
+
export interface HarnessRuntimeContext {
|
|
42
|
+
agent_id: string;
|
|
43
|
+
agent_role: string;
|
|
44
|
+
agent_type: string;
|
|
45
|
+
session_agent: string;
|
|
46
|
+
harness_adapter: string;
|
|
47
|
+
runtime: string;
|
|
48
|
+
}
|
|
49
|
+
export interface RuntimeDecisionEvent {
|
|
50
|
+
schema_version: typeof RUNTIME_DECISION_EVENT_SCHEMA_VERSION;
|
|
51
|
+
event_id: string;
|
|
52
|
+
timestamp: string;
|
|
53
|
+
org_id: string;
|
|
54
|
+
team_id: string;
|
|
55
|
+
user_id: string;
|
|
56
|
+
project_id: string;
|
|
57
|
+
agent_id: string;
|
|
58
|
+
agent_role: string;
|
|
59
|
+
agent_type: string;
|
|
60
|
+
session_agent: string;
|
|
61
|
+
harness_adapter: string;
|
|
62
|
+
runtime: string;
|
|
63
|
+
policy_bundle_id: string;
|
|
64
|
+
policy_bundle_version: string;
|
|
65
|
+
cascade_layers: CascadeLayers;
|
|
66
|
+
winning_constraint: WinningConstraint;
|
|
67
|
+
compiled_ir_hash: string;
|
|
68
|
+
decision: RuntimeDecision;
|
|
69
|
+
decision_reason: string;
|
|
70
|
+
enforcement_point: EnforcementPoint;
|
|
71
|
+
harness_runtime_context: HarnessRuntimeContext;
|
|
72
|
+
session_id: string;
|
|
73
|
+
run_id: string;
|
|
74
|
+
step_id: string;
|
|
75
|
+
tool_name: string;
|
|
76
|
+
action_kind: string;
|
|
77
|
+
resource_ref: string;
|
|
78
|
+
evidence_refs: RuntimeEvidenceRef[];
|
|
79
|
+
}
|
|
80
|
+
export interface RuntimeDecisionEventValidationResult {
|
|
81
|
+
valid: boolean;
|
|
82
|
+
classification: "enterprise_evidence" | "legacy_diagnostic";
|
|
83
|
+
errors: string[];
|
|
84
|
+
}
|
|
85
|
+
export declare function validateRuntimeDecisionEvent(event: unknown): RuntimeDecisionEventValidationResult;
|