@aikdna/kdna-core 0.12.2 → 0.12.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/README.md +35 -21
- package/package.json +3 -3
- package/schema/checksums.schema.json +15 -8
- package/schema/load-plan.schema.json +5 -0
- package/src/types.d.ts +2 -0
- package/src/v1/index.js +43 -0
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# @aikdna/kdna-core
|
|
2
2
|
|
|
3
|
-
Core library for
|
|
3
|
+
Core library for local `.kdna` judgment assets.
|
|
4
4
|
|
|
5
|
-
KDNA Core v1 defines the
|
|
6
|
-
|
|
7
|
-
runtimes.
|
|
5
|
+
KDNA Core v1 defines the `.kdna` file format, schemas, secure container
|
|
6
|
+
reader, LoadPlan contract, and runtime projection helpers used by the CLI,
|
|
7
|
+
Studio export, skills, MCP integrations, and downstream agent runtimes.
|
|
8
8
|
|
|
9
9
|
## Installation
|
|
10
10
|
|
|
@@ -26,9 +26,10 @@ Use the `./v1` entrypoint for current KDNA Core v1 tooling:
|
|
|
26
26
|
const {
|
|
27
27
|
inspect,
|
|
28
28
|
validate,
|
|
29
|
+
planLoad,
|
|
30
|
+
loadAuthorized,
|
|
29
31
|
pack,
|
|
30
32
|
unpack,
|
|
31
|
-
loadV1,
|
|
32
33
|
buildChecksumsV1
|
|
33
34
|
} = require('@aikdna/kdna-core/v1');
|
|
34
35
|
|
|
@@ -37,7 +38,12 @@ if (!validation.overall_valid) {
|
|
|
37
38
|
throw new Error(validation.problems.join('\n'));
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
const
|
|
41
|
+
const plan = planLoad('./asset.kdna');
|
|
42
|
+
if (plan.can_load_now !== true) {
|
|
43
|
+
throw new Error(`Asset is not loadable yet: ${plan.required_action}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const compact = loadAuthorized('./asset.kdna', {
|
|
41
47
|
profile: 'compact',
|
|
42
48
|
as: 'prompt'
|
|
43
49
|
});
|
|
@@ -46,26 +52,35 @@ const compact = loadV1('./asset.kdna', {
|
|
|
46
52
|
## Supported Runtime Flow
|
|
47
53
|
|
|
48
54
|
```text
|
|
49
|
-
|
|
50
|
-
→ buildChecksumsV1
|
|
51
|
-
→ pack
|
|
55
|
+
.kdna file
|
|
52
56
|
→ validate
|
|
53
|
-
→
|
|
57
|
+
→ planLoad
|
|
58
|
+
→ loadAuthorized
|
|
54
59
|
→ agent/runtime context
|
|
55
60
|
```
|
|
56
61
|
|
|
57
|
-
|
|
62
|
+
For authoring and test fixtures, `pack()` can turn a local working folder into
|
|
63
|
+
a `.kdna` file before validation:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
local working folder
|
|
67
|
+
→ pack
|
|
68
|
+
→ .kdna file
|
|
69
|
+
→ validate
|
|
70
|
+
→ planLoad
|
|
71
|
+
→ loadAuthorized
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## v1 Container
|
|
58
75
|
|
|
59
|
-
A v1
|
|
76
|
+
A v1 `.kdna` container contains:
|
|
60
77
|
|
|
61
78
|
- `mimetype`
|
|
62
79
|
- `kdna.json`
|
|
63
80
|
- `payload.kdnab`
|
|
64
81
|
- `checksums.json`
|
|
65
82
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
A v1 `.kdna` container is a zip package of the same files. `validate()` checks:
|
|
83
|
+
`validate()` checks:
|
|
69
84
|
|
|
70
85
|
- format
|
|
71
86
|
- manifest schema
|
|
@@ -75,7 +90,7 @@ A v1 `.kdna` container is a zip package of the same files. `validate()` checks:
|
|
|
75
90
|
|
|
76
91
|
## Load Profiles
|
|
77
92
|
|
|
78
|
-
`
|
|
93
|
+
`loadAuthorized()` supports:
|
|
79
94
|
|
|
80
95
|
- `index`
|
|
81
96
|
- `compact`
|
|
@@ -89,15 +104,14 @@ Output formats:
|
|
|
89
104
|
|
|
90
105
|
## Boundary
|
|
91
106
|
|
|
92
|
-
KDNA Core v1 is content-neutral. It
|
|
93
|
-
|
|
94
|
-
encryption, licensing, and entitlement work is gated outside the current v1
|
|
95
|
-
baseline.
|
|
107
|
+
KDNA Core v1 is content-neutral. It validates file structure and loadability;
|
|
108
|
+
it does not rank judgment quality or endorse specific assets.
|
|
96
109
|
|
|
97
110
|
## Legacy API
|
|
98
111
|
|
|
99
112
|
The package root still exports compatibility APIs for older KDNA paths. New
|
|
100
|
-
tooling should prefer `@aikdna/kdna-core/v1
|
|
113
|
+
tooling should prefer `@aikdna/kdna-core/v1` and the `planLoad` /
|
|
114
|
+
`loadAuthorized` path.
|
|
101
115
|
|
|
102
116
|
## License
|
|
103
117
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aikdna/kdna-core",
|
|
3
|
-
"version": "0.12.
|
|
4
|
-
"description": "KDNA
|
|
3
|
+
"version": "0.12.4",
|
|
4
|
+
"description": "KDNA Core library for validating, planning, and loading local .kdna judgment assets.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"module": "src/index.mjs",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"kdna",
|
|
36
36
|
"kdna-core",
|
|
37
37
|
"ai-agent",
|
|
38
|
-
"
|
|
38
|
+
"judgment-assets"
|
|
39
39
|
],
|
|
40
40
|
"license": "Apache-2.0",
|
|
41
41
|
"repository": {
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
"type": "object",
|
|
7
7
|
"required": ["algorithm"],
|
|
8
8
|
"additionalProperties": true,
|
|
9
|
+
"$defs": {
|
|
10
|
+
"sha256Digest": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"pattern": "^sha256:[a-f0-9]{64}$"
|
|
13
|
+
},
|
|
14
|
+
"sha256Value": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
9
19
|
"properties": {
|
|
10
20
|
"algorithm": {
|
|
11
21
|
"type": "string",
|
|
@@ -13,19 +23,16 @@
|
|
|
13
23
|
"enum": ["sha256", "sha512", "blake2b-256"]
|
|
14
24
|
},
|
|
15
25
|
"manifest_digest": {
|
|
16
|
-
"type": "string",
|
|
17
26
|
"description": "Digest of the kdna.json entry.",
|
|
18
|
-
"
|
|
27
|
+
"$ref": "#/$defs/sha256Digest"
|
|
19
28
|
},
|
|
20
29
|
"payload_digest": {
|
|
21
|
-
"type": "string",
|
|
22
30
|
"description": "Digest of the payload.kdnab entry.",
|
|
23
|
-
"
|
|
31
|
+
"$ref": "#/$defs/sha256Digest"
|
|
24
32
|
},
|
|
25
33
|
"asset_digest": {
|
|
26
|
-
"type": "string",
|
|
27
34
|
"description": "Digest of the whole container (per-entry digests combined deterministically).",
|
|
28
|
-
"
|
|
35
|
+
"$ref": "#/$defs/sha256Digest"
|
|
29
36
|
},
|
|
30
37
|
"entries": {
|
|
31
38
|
"type": "object",
|
|
@@ -34,8 +41,8 @@
|
|
|
34
41
|
"type": "object",
|
|
35
42
|
"required": ["algorithm", "value"],
|
|
36
43
|
"properties": {
|
|
37
|
-
"algorithm": { "
|
|
38
|
-
"value": { "
|
|
44
|
+
"algorithm": { "const": "sha256" },
|
|
45
|
+
"value": { "$ref": "#/$defs/sha256Value" }
|
|
39
46
|
}
|
|
40
47
|
}
|
|
41
48
|
}
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"required_action",
|
|
15
15
|
"can_load_now",
|
|
16
16
|
"projection_policy",
|
|
17
|
+
"input_fingerprint",
|
|
17
18
|
"checks",
|
|
18
19
|
"issues",
|
|
19
20
|
"source"
|
|
@@ -73,6 +74,10 @@
|
|
|
73
74
|
"type": "string",
|
|
74
75
|
"enum": ["minimal", "remote", "none"]
|
|
75
76
|
},
|
|
77
|
+
"input_fingerprint": {
|
|
78
|
+
"type": ["string", "null"],
|
|
79
|
+
"pattern": "^sha256:[a-f0-9]{64}$"
|
|
80
|
+
},
|
|
76
81
|
"checks": {
|
|
77
82
|
"type": "object",
|
|
78
83
|
"required": [
|
package/src/types.d.ts
CHANGED
|
@@ -549,6 +549,7 @@ export interface KDNAV1LoadPlan {
|
|
|
549
549
|
| string;
|
|
550
550
|
can_load_now: boolean;
|
|
551
551
|
projection_policy: 'minimal' | 'remote' | 'none' | string;
|
|
552
|
+
input_fingerprint: string | null;
|
|
552
553
|
checks: Record<string, boolean>;
|
|
553
554
|
issues: KDNAV1LoadPlanIssue[];
|
|
554
555
|
source: {
|
|
@@ -560,6 +561,7 @@ export function planLoad(inputPath: string, options?: { password?: string; hasPa
|
|
|
560
561
|
export function buildChecksumsV1(sourceDir: string): KDNAV1Checksums;
|
|
561
562
|
export function pack(sourceDir: string, outputPath: string): void;
|
|
562
563
|
export function unpack(inputPath: string, outputDir: string): void;
|
|
564
|
+
export function loadAuthorized(inputPath: string, options?: { profile?: 'index' | 'compact' | 'scenario' | 'full' | string; as?: 'json' | 'prompt' | string }): Record<string, any>;
|
|
563
565
|
export function loadV1(inputPath: string, options?: { profile?: 'index' | 'compact' | 'scenario' | 'full' | string; as?: 'json' | 'prompt' | string }): Record<string, any>;
|
|
564
566
|
export const FORBIDDEN_OUTPUT_TERMS: readonly string[];
|
|
565
567
|
|
package/src/v1/index.js
CHANGED
|
@@ -747,6 +747,7 @@ function verifyDigests(checksums, map, problems, result) {
|
|
|
747
747
|
manifest_digest: 'kdna.json',
|
|
748
748
|
payload_digest: 'payload.kdnab',
|
|
749
749
|
};
|
|
750
|
+
const entryDigests = {};
|
|
750
751
|
let stillValid = true;
|
|
751
752
|
for (const [digestKey, entryName] of Object.entries(entryMap)) {
|
|
752
753
|
const declared = checksums[digestKey];
|
|
@@ -758,12 +759,33 @@ function verifyDigests(checksums, map, problems, result) {
|
|
|
758
759
|
}
|
|
759
760
|
const entryBytes = map[entryName];
|
|
760
761
|
const actual = crypto.createHash('sha256').update(entryBytes).digest('hex');
|
|
762
|
+
entryDigests[entryName] = actual;
|
|
761
763
|
const expected = declared.replace(/^sha256:/, '');
|
|
762
764
|
if (actual !== expected) {
|
|
763
765
|
problems.push(`checksums: ${digestKey} mismatch (declared ${expected.slice(0, 8)}..., actual ${actual.slice(0, 8)}...)`);
|
|
764
766
|
stillValid = false;
|
|
765
767
|
}
|
|
766
768
|
}
|
|
769
|
+
if (checksums.asset_digest) {
|
|
770
|
+
const expectedAssetDigest = checksums.asset_digest.replace(/^sha256:/, '');
|
|
771
|
+
const missingAssetInputs = ['kdna.json', 'payload.kdnab'].filter((entryName) => !entryDigests[entryName]);
|
|
772
|
+
if (missingAssetInputs.length) {
|
|
773
|
+
problems.push(`checksums: asset_digest cannot be verified without ${missingAssetInputs.join(', ')}`);
|
|
774
|
+
stillValid = false;
|
|
775
|
+
} else {
|
|
776
|
+
const combined = Object.keys(entryDigests)
|
|
777
|
+
.sort()
|
|
778
|
+
.map((name) => `${name}:${entryDigests[name]}`)
|
|
779
|
+
.join('\n');
|
|
780
|
+
const actualAssetDigest = crypto.createHash('sha256').update(combined).digest('hex');
|
|
781
|
+
if (actualAssetDigest !== expectedAssetDigest) {
|
|
782
|
+
problems.push(
|
|
783
|
+
`checksums: asset_digest mismatch (declared ${expectedAssetDigest.slice(0, 8)}..., actual ${actualAssetDigest.slice(0, 8)}...)`,
|
|
784
|
+
);
|
|
785
|
+
stillValid = false;
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
}
|
|
767
789
|
if (!stillValid) result.checksums_valid = false;
|
|
768
790
|
return stillValid;
|
|
769
791
|
}
|
|
@@ -986,6 +1008,25 @@ function finalizeLoadPlan(plan) {
|
|
|
986
1008
|
return plan;
|
|
987
1009
|
}
|
|
988
1010
|
|
|
1011
|
+
function inputFingerprint(inputPath, v1) {
|
|
1012
|
+
const absPath = path.resolve(inputPath);
|
|
1013
|
+
if (v1.kind === 'file') {
|
|
1014
|
+
return `sha256:${crypto.createHash('sha256').update(fs.readFileSync(absPath)).digest('hex')}`;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
const hash = crypto.createHash('sha256');
|
|
1018
|
+
for (const name of Object.keys(v1.map).sort()) {
|
|
1019
|
+
const bytes = v1.map[name];
|
|
1020
|
+
hash.update(name);
|
|
1021
|
+
hash.update('\0');
|
|
1022
|
+
hash.update(String(bytes.length));
|
|
1023
|
+
hash.update('\0');
|
|
1024
|
+
hash.update(bytes);
|
|
1025
|
+
hash.update('\0');
|
|
1026
|
+
}
|
|
1027
|
+
return `sha256:${hash.digest('hex')}`;
|
|
1028
|
+
}
|
|
1029
|
+
|
|
989
1030
|
function baseLoadPlan(inputPath, v1, validation) {
|
|
990
1031
|
const manifest = v1.manifest;
|
|
991
1032
|
const accessInfo = normalizeAccess(manifest.access);
|
|
@@ -1008,6 +1049,7 @@ function baseLoadPlan(inputPath, v1, validation) {
|
|
|
1008
1049
|
required_action: 'block',
|
|
1009
1050
|
can_load_now: false,
|
|
1010
1051
|
projection_policy: 'none',
|
|
1052
|
+
input_fingerprint: inputFingerprint(inputPath, v1),
|
|
1011
1053
|
checks: {
|
|
1012
1054
|
format_valid: validation.format_valid,
|
|
1013
1055
|
schema_valid: validation.schema_valid,
|
|
@@ -1060,6 +1102,7 @@ function planLoad(inputPath, opts = {}) {
|
|
|
1060
1102
|
required_action: 'block',
|
|
1061
1103
|
can_load_now: false,
|
|
1062
1104
|
projection_policy: 'none',
|
|
1105
|
+
input_fingerprint: null,
|
|
1063
1106
|
checks: {
|
|
1064
1107
|
format_valid: false,
|
|
1065
1108
|
schema_valid: false,
|