intentdna 1.6.5 → 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/compile.js +2 -47
- package/dist/cli/commands/context.d.ts +8 -0
- package/dist/cli/commands/context.js +63 -0
- package/dist/cli/commands/feedback.d.ts +3 -2
- package/dist/cli/commands/feedback.js +16 -5
- package/dist/cli/commands/init.js +11 -63
- package/dist/cli/commands/run.js +5 -4
- package/dist/cli/commands/show.js +2 -38
- package/dist/cli/commands/sync.d.ts +9 -6
- package/dist/cli/commands/sync.js +191 -186
- package/dist/cli/commands/templates.d.ts +10 -1
- package/dist/cli/commands/templates.js +50 -1
- package/dist/cli/commands/validate.js +15 -9
- package/dist/cli/commands/verify.d.ts +32 -0
- package/dist/cli/commands/verify.js +270 -31
- package/dist/cli/index.js +78 -11
- package/dist/compiler/activate.js +11 -6
- package/dist/compiler/cascade.d.ts +5 -1
- package/dist/compiler/cascade.js +74 -1
- package/dist/compiler/compile.js +38 -0
- package/dist/compiler/diagnostics.d.ts +17 -0
- package/dist/compiler/diagnostics.js +30 -0
- package/dist/compiler/index.d.ts +7 -0
- package/dist/compiler/index.js +13 -13
- package/dist/compiler/input-resolver.d.ts +32 -0
- package/dist/compiler/input-resolver.js +281 -0
- 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.d.ts +10 -1
- package/dist/hooks/cli.js +199 -35
- package/dist/hooks/state.d.ts +5 -10
- package/dist/hooks/state.js +170 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-compile.js +18 -49
- package/dist/mcp/tools-context.d.ts +2 -0
- package/dist/mcp/tools-context.js +85 -0
- package/dist/mcp/tools-enforce.d.ts +2 -2
- package/dist/mcp/tools-enforce.js +19 -49
- package/dist/mcp/tools-observability.js +26 -2
- 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 +87 -2
- package/dist/report/report-package.d.ts +64 -0
- package/dist/report/report-package.js +90 -0
- package/dist/runtime/agent-md.d.ts +1 -0
- package/dist/runtime/agent-md.js +21 -3
- package/dist/runtime/claude-sdk.d.ts +9 -4
- package/dist/runtime/claude-sdk.js +9 -0
- package/dist/runtime/context-sources.d.ts +14 -0
- package/dist/runtime/context-sources.js +60 -0
- package/dist/runtime/plugin-adapter.d.ts +5 -1
- package/dist/runtime/plugin-adapter.js +2 -0
- package/dist/runtime/skill-adapter.d.ts +32 -4
- package/dist/runtime/skill-adapter.js +184 -9
- package/dist/runtime/workflow-runner.d.ts +1 -1
- package/dist/runtime/workflow-runner.js +1 -1
- package/dist/schema/types.d.ts +84 -0
- package/dist/schema/validate.js +156 -2
- package/dist/schema/validators/controllers.js +16 -0
- package/dist/signals/index.d.ts +10 -0
- package/dist/signals/index.js +90 -5
- package/dist/templates/catalog.d.ts +19 -0
- package/dist/templates/catalog.js +57 -0
- package/dist/templates/flutter-rewrite.dna.yaml +2 -2
- package/package.json +1 -1
- package/spec/README.md +1 -1
- package/spec/foundation-hardening.md +2 -1
- package/spec/schema-spec.md +78 -1
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
4
|
+
import { dirname, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { validateDNA } from "../schema/validate.js";
|
|
7
|
+
import { parseYAML } from "../schema/yaml-parser.js";
|
|
8
|
+
import { DNADiagnosticsError, hasDiagnosticErrors } from "./diagnostics.js";
|
|
9
|
+
const DNA_CONFIG_CANDIDATES = [
|
|
10
|
+
".dna/config.yaml",
|
|
11
|
+
".dna/config.yml",
|
|
12
|
+
".dna/config.json",
|
|
13
|
+
".dna.yaml",
|
|
14
|
+
".dna.yml",
|
|
15
|
+
".dna.json",
|
|
16
|
+
];
|
|
17
|
+
async function fileExists(path) {
|
|
18
|
+
try {
|
|
19
|
+
await stat(path);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseDNAContent(filePath, raw) {
|
|
27
|
+
const isYAML = filePath.endsWith(".yaml") || filePath.endsWith(".yml");
|
|
28
|
+
return (isYAML ? parseYAML(raw) : JSON.parse(raw));
|
|
29
|
+
}
|
|
30
|
+
export async function parseDNAFile(filePath) {
|
|
31
|
+
const raw = await readFile(filePath, "utf-8");
|
|
32
|
+
return parseDNAContent(filePath, raw);
|
|
33
|
+
}
|
|
34
|
+
export function resolveSpeciesReference(ref) {
|
|
35
|
+
if (!ref.startsWith("species:"))
|
|
36
|
+
return null;
|
|
37
|
+
const name = ref.slice("species:".length);
|
|
38
|
+
const thisDir = dirname(fileURLToPath(import.meta.url));
|
|
39
|
+
return resolve(thisDir, "..", "species", `${name}.dna.json`);
|
|
40
|
+
}
|
|
41
|
+
export async function detectDNAConfigs(projectDir = process.cwd()) {
|
|
42
|
+
const configsDir = resolve(projectDir, ".dna", "configs");
|
|
43
|
+
try {
|
|
44
|
+
const files = await readdir(configsDir);
|
|
45
|
+
const configs = files
|
|
46
|
+
.filter((file) => file.endsWith(".yaml") || file.endsWith(".yml"))
|
|
47
|
+
.sort()
|
|
48
|
+
.map((file) => resolve(configsDir, file));
|
|
49
|
+
if (configs.length > 0)
|
|
50
|
+
return configs;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Directory does not exist.
|
|
54
|
+
}
|
|
55
|
+
for (const candidate of DNA_CONFIG_CANDIDATES) {
|
|
56
|
+
const path = resolve(projectDir, candidate);
|
|
57
|
+
if (await fileExists(path))
|
|
58
|
+
return [path];
|
|
59
|
+
}
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
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) {
|
|
110
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
111
|
+
const ordered = [];
|
|
112
|
+
const seen = new Set();
|
|
113
|
+
const diagnostics = [];
|
|
114
|
+
const provenance = new Map();
|
|
115
|
+
const stack = [];
|
|
116
|
+
const visit = async (file, ref, inheritedBy, trust) => {
|
|
117
|
+
const abs = resolve(cwd, file);
|
|
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;
|
|
130
|
+
try {
|
|
131
|
+
loaded = await readDNAWithRaw(abs);
|
|
132
|
+
}
|
|
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;
|
|
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);
|
|
190
|
+
}
|
|
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;
|
|
195
|
+
}
|
|
196
|
+
export async function checkNamespaceCollisions(configPaths) {
|
|
197
|
+
const diagnostics = [];
|
|
198
|
+
const namespaces = new Map();
|
|
199
|
+
for (const configPath of configPaths) {
|
|
200
|
+
try {
|
|
201
|
+
const data = await parseDNAFile(configPath);
|
|
202
|
+
const namespace = typeof data.namespace === "string" ? data.namespace : undefined;
|
|
203
|
+
if (!namespace)
|
|
204
|
+
continue;
|
|
205
|
+
const previous = namespaces.get(namespace);
|
|
206
|
+
if (previous) {
|
|
207
|
+
diagnostics.push({
|
|
208
|
+
severity: "error",
|
|
209
|
+
code: "namespace_conflict",
|
|
210
|
+
message: `Namespace "${namespace}" conflict: ${previous} and ${configPath}. Each template must have a unique namespace.`,
|
|
211
|
+
file: configPath,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
namespaces.set(namespace, configPath);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
// Parse/validation errors are reported by the load step.
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return diagnostics;
|
|
223
|
+
}
|
|
224
|
+
export async function assertNoNamespaceCollisions(configPaths) {
|
|
225
|
+
const diagnostics = await checkNamespaceCollisions(configPaths);
|
|
226
|
+
if (diagnostics.length > 0) {
|
|
227
|
+
throw new DNADiagnosticsError(diagnostics[0].message, diagnostics);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
export async function loadDNAWithDiagnostics(filePath) {
|
|
231
|
+
const diagnostics = [];
|
|
232
|
+
try {
|
|
233
|
+
const dna = await parseDNAFile(filePath);
|
|
234
|
+
const result = validateDNA(dna);
|
|
235
|
+
for (const warning of result.warnings ?? []) {
|
|
236
|
+
diagnostics.push({
|
|
237
|
+
severity: "warning",
|
|
238
|
+
code: "schema_warning",
|
|
239
|
+
message: warning.message,
|
|
240
|
+
file: filePath,
|
|
241
|
+
path: warning.path,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
for (const error of result.errors) {
|
|
245
|
+
diagnostics.push({
|
|
246
|
+
severity: "error",
|
|
247
|
+
code: "schema_error",
|
|
248
|
+
message: error.message,
|
|
249
|
+
file: filePath,
|
|
250
|
+
path: error.path,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
return { dna: result.valid ? dna : undefined, diagnostics };
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
diagnostics.push({
|
|
257
|
+
severity: "error",
|
|
258
|
+
code: "parse_error",
|
|
259
|
+
message: error instanceof Error ? error.message : String(error),
|
|
260
|
+
file: filePath,
|
|
261
|
+
});
|
|
262
|
+
return { diagnostics };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
export async function resolveDNAInputs(files, options) {
|
|
266
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
267
|
+
const inputFiles = files.length > 0 ? files : await detectDNAConfigs(cwd);
|
|
268
|
+
const expandedFiles = await expandDNAInputFiles(inputFiles, { cwd });
|
|
269
|
+
const diagnostics = await checkNamespaceCollisions(expandedFiles);
|
|
270
|
+
const dnas = [];
|
|
271
|
+
for (const file of expandedFiles) {
|
|
272
|
+
const result = await loadDNAWithDiagnostics(file);
|
|
273
|
+
diagnostics.push(...result.diagnostics);
|
|
274
|
+
if (result.dna)
|
|
275
|
+
dnas.push(result.dna);
|
|
276
|
+
}
|
|
277
|
+
if (hasDiagnosticErrors(diagnostics)) {
|
|
278
|
+
throw new DNADiagnosticsError("DNA input resolution failed", diagnostics);
|
|
279
|
+
}
|
|
280
|
+
return { files: expandedFiles, dnas, diagnostics };
|
|
281
|
+
}
|
|
@@ -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;
|