@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 CHANGED
@@ -1,10 +1,10 @@
1
1
  # @aikdna/kdna-core
2
2
 
3
- Core library for KDNA judgment assets.
3
+ Core library for local `.kdna` judgment assets.
4
4
 
5
- KDNA Core v1 defines the official `.kdna` source/container contract used by
6
- the CLI, Studio export, skills, MCP integrations, and downstream agent
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 compact = loadV1('./asset.kdna', {
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
- v1 source directory
50
- → buildChecksumsV1
51
- → pack
55
+ .kdna file
52
56
  → validate
53
- → loadV1
57
+ → planLoad
58
+ → loadAuthorized
54
59
  → agent/runtime context
55
60
  ```
56
61
 
57
- ## v1 Source Directory
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 source directory contains:
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
- ## v1 Container
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
- `loadV1()` supports:
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 does not recommend assets, assign quality
93
- badges, run a marketplace, or define a public registry. Future signature,
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.2",
4
- "description": "KDNA core library — load, validate, lint, and render KDNA domain judgment assets. Supports KDNA Container format (payload.kdnab via CBOR).",
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
- "domain-cognition"
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
- "pattern": "^[a-z0-9-]+:.+$"
27
+ "$ref": "#/$defs/sha256Digest"
19
28
  },
20
29
  "payload_digest": {
21
- "type": "string",
22
30
  "description": "Digest of the payload.kdnab entry.",
23
- "pattern": "^[a-z0-9-]+:.+$"
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
- "pattern": "^[a-z0-9-]+:.+$"
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": { "type": "string" },
38
- "value": { "type": "string" }
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,