@aikdna/kdna-core 0.18.0 → 0.19.0
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/CHANGELOG.md +128 -94
- package/README.md +109 -215
- package/package.json +7 -2
- package/schema/agent-host-capabilities.schema.json +62 -0
- package/schema/agent-host-receipt.schema.json +371 -0
- package/schema/agent-host-request.schema.json +223 -0
- package/schema/{bundle-profile-v1.schema.json → bundle-profile.schema.json} +9 -5
- package/schema/checksums.schema.json +8 -8
- package/schema/consumption-plan.schema.json +428 -0
- package/schema/digest-evidence.schema.json +115 -26
- package/schema/{kdna-envelope-external-grant-v1.schema.json → external-grant-envelope.schema.json} +17 -4
- package/schema/{kdna-key-grant-v1.schema.json → external-key-grant.schema.json} +39 -7
- package/schema/judgment-trace.schema.json +1251 -0
- package/schema/load-plan.schema.json +5 -3
- package/schema/manifest.schema.json +70 -10
- package/schema/{payload-profile-v1.schema.json → payload-profile.schema.json} +30 -6
- package/schema/{runtime-capsule-2.schema.json → runtime-capsule.schema.json} +7 -31
- package/src/asset-reader.js +8 -2
- package/src/{v1 → container}/index.js +385 -198
- package/src/container/index.mjs +20 -0
- package/src/container-dispatcher.js +6 -6
- package/src/crypto-profile.js +37 -107
- package/src/external-key-grant.js +41 -15
- package/src/index.js +11 -8
- package/src/index.mjs +49 -45
- package/src/loader-compatibility.js +59 -0
- package/src/loader.js +1 -1
- package/src/public-api.js +5 -5
- package/src/remote-runtime.d.ts +13 -0
- package/src/remote-runtime.js +27 -0
- package/src/remote-runtime.mjs +3 -0
- package/src/runtime-api.js +5 -5
- package/src/{capsule-v2.js → runtime-capsule.js} +96 -254
- package/src/{execution-contract-v1.js → runtime-contract.js} +184 -152
- package/src/types.d.ts +200 -277
- package/src/workpack-pure.js +1 -1
- package/schema/agent-host-2-receipt.schema.json +0 -206
- package/schema/agent-host-2-request.schema.json +0 -135
- package/schema/agent-host-capabilities-1.schema.json +0 -40
- package/schema/consumption-plan-1.schema.json +0 -280
- package/schema/judgment-trace-1.schema.json +0 -748
- package/schema/runtime-capsule-1.schema.json +0 -68
- package/src/lint-pure.js +0 -433
- package/src/v1/index.mjs +0 -21
|
@@ -2,24 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
const crypto = require('node:crypto');
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const JsonSchema2020 = require('ajv/dist/2020');
|
|
6
6
|
const addFormats = require('ajv-formats');
|
|
7
7
|
|
|
8
|
-
const { canonicalizeJcs, computeCapsuleDeliveryDigest } = require('./capsule
|
|
8
|
+
const { canonicalizeJcs, computeCapsuleDeliveryDigest } = require('./runtime-capsule');
|
|
9
9
|
const digestEvidenceSchema = require('../schema/digest-evidence.schema.json');
|
|
10
|
-
const
|
|
11
|
-
const planSchema = require('../schema/consumption-plan
|
|
12
|
-
const capabilitiesSchema = require('../schema/agent-host-capabilities
|
|
13
|
-
const requestSchema = require('../schema/agent-host-
|
|
14
|
-
const receiptSchema = require('../schema/agent-host-
|
|
15
|
-
const traceSchema = require('../schema/judgment-trace
|
|
16
|
-
|
|
17
|
-
const PLAN_DIGEST_PROFILE = 'kdna
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
10
|
+
const runtimeCapsuleSchema = require('../schema/runtime-capsule.schema.json');
|
|
11
|
+
const planSchema = require('../schema/consumption-plan.schema.json');
|
|
12
|
+
const capabilitiesSchema = require('../schema/agent-host-capabilities.schema.json');
|
|
13
|
+
const requestSchema = require('../schema/agent-host-request.schema.json');
|
|
14
|
+
const receiptSchema = require('../schema/agent-host-receipt.schema.json');
|
|
15
|
+
const traceSchema = require('../schema/judgment-trace.schema.json');
|
|
16
|
+
|
|
17
|
+
const PLAN_DIGEST_PROFILE = 'kdna.canonicalization.consumption-plan-jcs';
|
|
18
|
+
const PLAN_DIGEST_PROFILE_VERSION = '0.1.0';
|
|
19
|
+
const CAPSULE_DELIVERY_PROFILE = 'kdna.canonicalization.runtime-capsule-jcs';
|
|
20
|
+
const CAPSULE_DELIVERY_PROFILE_VERSION = '0.1.0';
|
|
21
|
+
const CAPSULE_DIGEST_PROFILE = 'kdna.digest-evidence';
|
|
22
|
+
const HOST_PROTOCOL = 'kdna.agent-host';
|
|
23
|
+
const HOST_PROTOCOL_VERSION = '0.1.0';
|
|
24
|
+
const CAPSULE_CONTRACT_VERSION = '0.1.0';
|
|
25
|
+
const DEFAULT_CORE_CAPSULE_CONTRACT_VERSIONS = Object.freeze([
|
|
26
|
+
CAPSULE_CONTRACT_VERSION,
|
|
27
|
+
]);
|
|
23
28
|
const JSON_MAX_BYTES = 2 * 1024 * 1024;
|
|
24
29
|
const JSON_MAX_DEPTH = 64;
|
|
25
30
|
const PRE_HOST_INTERNAL_REQUEST_ID = 'host_000000000000000000000000';
|
|
@@ -31,10 +36,10 @@ const PRE_HOST_BUDGET_ERROR = Object.freeze({
|
|
|
31
36
|
|
|
32
37
|
let validators;
|
|
33
38
|
|
|
34
|
-
class
|
|
39
|
+
class KDNARuntimeContractError extends Error {
|
|
35
40
|
constructor(code, message, details) {
|
|
36
41
|
super(message);
|
|
37
|
-
this.name = '
|
|
42
|
+
this.name = 'KDNARuntimeContractError';
|
|
38
43
|
this.code = code;
|
|
39
44
|
if (details !== undefined) this.details = details;
|
|
40
45
|
}
|
|
@@ -42,11 +47,11 @@ class KDNAExecutionContractError extends Error {
|
|
|
42
47
|
|
|
43
48
|
function schemaValidators() {
|
|
44
49
|
if (validators) return validators;
|
|
45
|
-
const ajv = new
|
|
50
|
+
const ajv = new JsonSchema2020({ allErrors: true, strict: false });
|
|
46
51
|
addFormats(ajv);
|
|
47
52
|
for (const schema of [
|
|
48
53
|
digestEvidenceSchema,
|
|
49
|
-
|
|
54
|
+
runtimeCapsuleSchema,
|
|
50
55
|
planSchema,
|
|
51
56
|
capabilitiesSchema,
|
|
52
57
|
requestSchema,
|
|
@@ -80,7 +85,7 @@ function invalid(code, message, details) {
|
|
|
80
85
|
function throwInvalid(result) {
|
|
81
86
|
if (result.valid) return;
|
|
82
87
|
const first = result.errors[0] || {};
|
|
83
|
-
throw new
|
|
88
|
+
throw new KDNARuntimeContractError(
|
|
84
89
|
result.code || 'KDNA_EXECUTION_CONTRACT_INVALID',
|
|
85
90
|
first.message || 'KDNA execution contract is invalid.',
|
|
86
91
|
first.details,
|
|
@@ -111,7 +116,7 @@ function jcsCharacterCount(value) {
|
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
function snapshotJsonValue(value) {
|
|
114
|
-
return
|
|
119
|
+
return parseRuntimeContractJson(canonicalizeJcs(value));
|
|
115
120
|
}
|
|
116
121
|
|
|
117
122
|
function validateSchema(kind, value) {
|
|
@@ -130,7 +135,7 @@ function validateSchema(kind, value) {
|
|
|
130
135
|
) {
|
|
131
136
|
return invalid(
|
|
132
137
|
'KDNA_ASSET_FILE_REQUIRED',
|
|
133
|
-
'ConsumptionPlan
|
|
138
|
+
'ConsumptionPlan requires a packaged .kdna Runtime asset.',
|
|
134
139
|
details,
|
|
135
140
|
);
|
|
136
141
|
}
|
|
@@ -252,14 +257,14 @@ function snapshotPublicInput(input, requiredKeys, optionalKeys = []) {
|
|
|
252
257
|
* Duplicate member names are rejected after JSON escape decoding, so
|
|
253
258
|
* {"a": 1, "\\u0061": 2} is also rejected.
|
|
254
259
|
*/
|
|
255
|
-
function
|
|
260
|
+
function parseRuntimeContractJson(input, options = {}) {
|
|
256
261
|
if (
|
|
257
262
|
options === null ||
|
|
258
263
|
typeof options !== 'object' ||
|
|
259
264
|
Array.isArray(options) ||
|
|
260
265
|
Object.getPrototypeOf(options) !== Object.prototype
|
|
261
266
|
) {
|
|
262
|
-
throw new
|
|
267
|
+
throw new KDNARuntimeContractError(
|
|
263
268
|
'KDNA_JSON_OPTIONS_INVALID',
|
|
264
269
|
'Execution-contract JSON parser options must be a plain object.',
|
|
265
270
|
);
|
|
@@ -274,7 +279,7 @@ function parseExecutionContractJsonV1(input, options = {}) {
|
|
|
274
279
|
!Object.prototype.hasOwnProperty.call(descriptors[key], 'value'),
|
|
275
280
|
)
|
|
276
281
|
) {
|
|
277
|
-
throw new
|
|
282
|
+
throw new KDNARuntimeContractError(
|
|
278
283
|
'KDNA_JSON_OPTIONS_INVALID',
|
|
279
284
|
'Execution-contract JSON parser options contain an unknown or accessor property.',
|
|
280
285
|
);
|
|
@@ -289,7 +294,7 @@ function parseExecutionContractJsonV1(input, options = {}) {
|
|
|
289
294
|
maxDepth < 1 ||
|
|
290
295
|
maxDepth > JSON_MAX_DEPTH
|
|
291
296
|
) {
|
|
292
|
-
throw new
|
|
297
|
+
throw new KDNARuntimeContractError(
|
|
293
298
|
'KDNA_JSON_OPTIONS_INVALID',
|
|
294
299
|
`maxBytes and maxDepth must be positive safe integers no greater than ${JSON_MAX_BYTES} and ${JSON_MAX_DEPTH}.`,
|
|
295
300
|
);
|
|
@@ -299,13 +304,13 @@ function parseExecutionContractJsonV1(input, options = {}) {
|
|
|
299
304
|
text = input;
|
|
300
305
|
} else if (Buffer.isBuffer(input) || input instanceof Uint8Array) {
|
|
301
306
|
if (input.byteLength > maxBytes) {
|
|
302
|
-
throw new
|
|
307
|
+
throw new KDNARuntimeContractError(
|
|
303
308
|
'KDNA_JSON_LIMIT_EXCEEDED',
|
|
304
309
|
'Execution-contract JSON exceeds the configured byte limit.',
|
|
305
310
|
);
|
|
306
311
|
}
|
|
307
312
|
if (input.byteLength >= 3 && input[0] === 0xef && input[1] === 0xbb && input[2] === 0xbf) {
|
|
308
|
-
throw new
|
|
313
|
+
throw new KDNARuntimeContractError(
|
|
309
314
|
'KDNA_JSON_BOM_FORBIDDEN',
|
|
310
315
|
'Execution-contract JSON must not begin with a UTF-8 BOM.',
|
|
311
316
|
);
|
|
@@ -313,27 +318,27 @@ function parseExecutionContractJsonV1(input, options = {}) {
|
|
|
313
318
|
try {
|
|
314
319
|
text = new TextDecoder('utf-8', { fatal: true }).decode(input);
|
|
315
320
|
} catch {
|
|
316
|
-
throw new
|
|
321
|
+
throw new KDNARuntimeContractError(
|
|
317
322
|
'KDNA_JSON_INVALID_UNICODE',
|
|
318
323
|
'Execution-contract JSON is not valid UTF-8.',
|
|
319
324
|
);
|
|
320
325
|
}
|
|
321
326
|
} else {
|
|
322
|
-
throw new
|
|
327
|
+
throw new KDNARuntimeContractError(
|
|
323
328
|
'KDNA_JSON_INPUT_INVALID',
|
|
324
329
|
'Execution-contract JSON input must be a string, Buffer, or Uint8Array.',
|
|
325
330
|
);
|
|
326
331
|
}
|
|
327
332
|
|
|
328
333
|
if (text.charCodeAt(0) === 0xfeff) {
|
|
329
|
-
throw new
|
|
334
|
+
throw new KDNARuntimeContractError(
|
|
330
335
|
'KDNA_JSON_BOM_FORBIDDEN',
|
|
331
336
|
'Execution-contract JSON must not begin with a BOM.',
|
|
332
337
|
);
|
|
333
338
|
}
|
|
334
339
|
|
|
335
340
|
if (Buffer.byteLength(text, 'utf8') > maxBytes) {
|
|
336
|
-
throw new
|
|
341
|
+
throw new KDNARuntimeContractError(
|
|
337
342
|
'KDNA_JSON_LIMIT_EXCEEDED',
|
|
338
343
|
'Execution-contract JSON exceeds the configured byte limit.',
|
|
339
344
|
);
|
|
@@ -359,7 +364,7 @@ class StrictJsonParser {
|
|
|
359
364
|
}
|
|
360
365
|
|
|
361
366
|
syntax(message) {
|
|
362
|
-
throw new
|
|
367
|
+
throw new KDNARuntimeContractError('KDNA_JSON_SYNTAX_INVALID', message, {
|
|
363
368
|
code_unit_offset: this.index,
|
|
364
369
|
});
|
|
365
370
|
}
|
|
@@ -381,7 +386,7 @@ class StrictJsonParser {
|
|
|
381
386
|
const char = this.text[this.index];
|
|
382
387
|
if (char === '{' || char === '[') {
|
|
383
388
|
if (depth >= this.maxDepth) {
|
|
384
|
-
throw new
|
|
389
|
+
throw new KDNARuntimeContractError(
|
|
385
390
|
'KDNA_JSON_LIMIT_EXCEEDED',
|
|
386
391
|
'Execution-contract JSON exceeds the configured nesting limit.',
|
|
387
392
|
);
|
|
@@ -418,7 +423,7 @@ class StrictJsonParser {
|
|
|
418
423
|
if (this.text[this.index] !== '"') this.syntax('Expected a quoted JSON member name.');
|
|
419
424
|
const key = this.parseString();
|
|
420
425
|
if (keys.has(key)) {
|
|
421
|
-
throw new
|
|
426
|
+
throw new KDNARuntimeContractError(
|
|
422
427
|
'KDNA_JSON_DUPLICATE_KEY',
|
|
423
428
|
`Execution-contract JSON contains duplicate member name ${JSON.stringify(key)}.`,
|
|
424
429
|
{ key, code_unit_offset: this.index },
|
|
@@ -529,7 +534,7 @@ class StrictJsonParser {
|
|
|
529
534
|
}
|
|
530
535
|
|
|
531
536
|
invalidUnicode() {
|
|
532
|
-
throw new
|
|
537
|
+
throw new KDNARuntimeContractError(
|
|
533
538
|
'KDNA_JSON_INVALID_UNICODE',
|
|
534
539
|
'Execution-contract JSON contains an unpaired UTF-16 surrogate.',
|
|
535
540
|
{ code_unit_offset: this.index },
|
|
@@ -544,7 +549,7 @@ class StrictJsonParser {
|
|
|
544
549
|
this.index += token.length;
|
|
545
550
|
const number = Number(token);
|
|
546
551
|
if (!Number.isFinite(number)) {
|
|
547
|
-
throw new
|
|
552
|
+
throw new KDNARuntimeContractError(
|
|
548
553
|
'KDNA_JSON_NUMBER_INVALID',
|
|
549
554
|
'Execution-contract JSON number is outside the finite IEEE-754 range.',
|
|
550
555
|
);
|
|
@@ -553,7 +558,7 @@ class StrictJsonParser {
|
|
|
553
558
|
}
|
|
554
559
|
}
|
|
555
560
|
|
|
556
|
-
function
|
|
561
|
+
function computeConsumptionPlanDigest(plan) {
|
|
557
562
|
try {
|
|
558
563
|
if (plan === null || typeof plan !== 'object' || Array.isArray(plan)) {
|
|
559
564
|
throw new TypeError('plan must be an object');
|
|
@@ -562,8 +567,8 @@ function computeConsumptionPlanDigestV1(plan) {
|
|
|
562
567
|
delete projection.integrity;
|
|
563
568
|
return digestJcs(projection);
|
|
564
569
|
} catch (error) {
|
|
565
|
-
if (error instanceof
|
|
566
|
-
throw new
|
|
570
|
+
if (error instanceof KDNARuntimeContractError) throw error;
|
|
571
|
+
throw new KDNARuntimeContractError(
|
|
567
572
|
'KDNA_PLAN_INPUT_INVALID',
|
|
568
573
|
'ConsumptionPlan digest input must be a finite, acyclic JSON object.',
|
|
569
574
|
{ cause: error && typeof error.message === 'string' ? error.message : String(error) },
|
|
@@ -574,7 +579,7 @@ function computeConsumptionPlanDigestV1(plan) {
|
|
|
574
579
|
function validatePlanSemantics(plan, trustedPlanDigest) {
|
|
575
580
|
let computed;
|
|
576
581
|
try {
|
|
577
|
-
computed =
|
|
582
|
+
computed = computeConsumptionPlanDigest(plan);
|
|
578
583
|
} catch {
|
|
579
584
|
return 'KDNA_PLAN_INPUT_INVALID';
|
|
580
585
|
}
|
|
@@ -585,7 +590,7 @@ function validatePlanSemantics(plan, trustedPlanDigest) {
|
|
|
585
590
|
return null;
|
|
586
591
|
}
|
|
587
592
|
|
|
588
|
-
function
|
|
593
|
+
function validateConsumptionPlan(plan, context) {
|
|
589
594
|
const contextResult = snapshotContext(context, ['trustedPlanDigest']);
|
|
590
595
|
if (!contextResult.valid) return contextResult;
|
|
591
596
|
const safeContext = contextResult.value;
|
|
@@ -605,12 +610,12 @@ function validateConsumptionPlanV1(plan, context) {
|
|
|
605
610
|
return code ? invalid(code, 'ConsumptionPlan integrity correlation failed.') : ok(safePlan);
|
|
606
611
|
}
|
|
607
612
|
|
|
608
|
-
function
|
|
613
|
+
function buildConsumptionPlan(input) {
|
|
609
614
|
let plan;
|
|
610
615
|
try {
|
|
611
616
|
plan = {
|
|
612
|
-
type: 'kdna.consumption
|
|
613
|
-
|
|
617
|
+
type: 'kdna.consumption-plan',
|
|
618
|
+
contract_version: '0.1.0',
|
|
614
619
|
plan_id: input.plan_id,
|
|
615
620
|
created_at: input.created_at,
|
|
616
621
|
mode: 'single',
|
|
@@ -618,26 +623,31 @@ function buildConsumptionPlanV1(input) {
|
|
|
618
623
|
asset_ref: clone(input.asset_ref),
|
|
619
624
|
projection_request: {
|
|
620
625
|
profile: input.projection_profile,
|
|
621
|
-
accepted_capsule_versions: [
|
|
626
|
+
accepted_capsule_versions: [CAPSULE_CONTRACT_VERSION],
|
|
622
627
|
required_digest_profile: CAPSULE_DIGEST_PROFILE,
|
|
628
|
+
required_digest_profile_version: '0.1.0',
|
|
623
629
|
require_packaged_asset: true,
|
|
624
630
|
},
|
|
625
631
|
host_request: { accepted_protocols: [HOST_PROTOCOL] },
|
|
626
632
|
result_request: { shape: 'structured_judgment' },
|
|
627
633
|
budget: clone(input.budget),
|
|
628
634
|
trace_policy: clone(input.trace_policy),
|
|
629
|
-
integrity: {
|
|
635
|
+
integrity: {
|
|
636
|
+
profile: PLAN_DIGEST_PROFILE,
|
|
637
|
+
profile_version: PLAN_DIGEST_PROFILE_VERSION,
|
|
638
|
+
plan_digest: '',
|
|
639
|
+
},
|
|
630
640
|
constraints: clone(input.constraints),
|
|
631
641
|
};
|
|
632
|
-
plan.integrity.plan_digest =
|
|
642
|
+
plan.integrity.plan_digest = computeConsumptionPlanDigest(plan);
|
|
633
643
|
} catch (error) {
|
|
634
|
-
if (error instanceof
|
|
635
|
-
throw new
|
|
644
|
+
if (error instanceof KDNARuntimeContractError) throw error;
|
|
645
|
+
throw new KDNARuntimeContractError(
|
|
636
646
|
'KDNA_PLAN_INPUT_INVALID',
|
|
637
|
-
'Cannot build ConsumptionPlan
|
|
647
|
+
'Cannot build ConsumptionPlan from the supplied input.',
|
|
638
648
|
);
|
|
639
649
|
}
|
|
640
|
-
const result =
|
|
650
|
+
const result = validateConsumptionPlan(plan, {
|
|
641
651
|
trustedPlanDigest: plan.integrity.plan_digest,
|
|
642
652
|
});
|
|
643
653
|
throwInvalid(result);
|
|
@@ -653,7 +663,7 @@ function blocked(issueCode) {
|
|
|
653
663
|
};
|
|
654
664
|
}
|
|
655
665
|
|
|
656
|
-
function
|
|
666
|
+
function negotiateRuntimePair(plan, context) {
|
|
657
667
|
const contextResult = snapshotContext(context, [
|
|
658
668
|
'trustedPlanDigest',
|
|
659
669
|
'capabilities',
|
|
@@ -661,7 +671,7 @@ function negotiateExecutionPairV1(plan, context) {
|
|
|
661
671
|
]);
|
|
662
672
|
if (!contextResult.valid) return blocked(contextResult.code);
|
|
663
673
|
const safeContext = contextResult.value;
|
|
664
|
-
const planResult =
|
|
674
|
+
const planResult = validateConsumptionPlan(plan, {
|
|
665
675
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
666
676
|
});
|
|
667
677
|
if (!planResult.valid) return blocked(planResult.code);
|
|
@@ -677,10 +687,10 @@ function negotiateExecutionPairV1(plan, context) {
|
|
|
677
687
|
}
|
|
678
688
|
|
|
679
689
|
const hasCapsule =
|
|
680
|
-
safePlan.projection_request.accepted_capsule_versions.includes(
|
|
681
|
-
safeContext.coreCapsuleVersions.includes(
|
|
682
|
-
safeCapabilities.capsule_versions.includes(
|
|
683
|
-
if (!hasCapsule) return blocked('
|
|
690
|
+
safePlan.projection_request.accepted_capsule_versions.includes(CAPSULE_CONTRACT_VERSION) &&
|
|
691
|
+
safeContext.coreCapsuleVersions.includes(CAPSULE_CONTRACT_VERSION) &&
|
|
692
|
+
safeCapabilities.capsule_versions.includes(CAPSULE_CONTRACT_VERSION);
|
|
693
|
+
if (!hasCapsule) return blocked('KDNA_CAPSULE_CONTRACT_VERSION_UNSUPPORTED');
|
|
684
694
|
|
|
685
695
|
const hasProtocol =
|
|
686
696
|
safePlan.host_request.accepted_protocols.includes(HOST_PROTOCOL) &&
|
|
@@ -694,7 +704,7 @@ function negotiateExecutionPairV1(plan, context) {
|
|
|
694
704
|
|
|
695
705
|
return {
|
|
696
706
|
state: 'selected',
|
|
697
|
-
capsule_version:
|
|
707
|
+
capsule_version: CAPSULE_CONTRACT_VERSION,
|
|
698
708
|
host_protocol: HOST_PROTOCOL,
|
|
699
709
|
issue_code: null,
|
|
700
710
|
};
|
|
@@ -702,7 +712,7 @@ function negotiateExecutionPairV1(plan, context) {
|
|
|
702
712
|
|
|
703
713
|
function expectedAgainst(source) {
|
|
704
714
|
if (source === 'kdna.json.content_digest') return 'manifest_declaration';
|
|
705
|
-
if (source === 'checksums.json.entry_set_digest'
|
|
715
|
+
if (source === 'checksums.json.entry_set_digest') {
|
|
706
716
|
return 'checksum_declaration';
|
|
707
717
|
}
|
|
708
718
|
return 'external_expected';
|
|
@@ -742,8 +752,9 @@ function validateRequestSemantics(request, plan, enforceBudget = true) {
|
|
|
742
752
|
if (
|
|
743
753
|
request.plan_ref.plan_id !== plan.plan_id ||
|
|
744
754
|
request.plan_ref.plan_digest_profile !== plan.integrity.profile ||
|
|
755
|
+
request.plan_ref.plan_digest_profile_version !== plan.integrity.profile_version ||
|
|
745
756
|
request.plan_ref.plan_digest !== plan.integrity.plan_digest ||
|
|
746
|
-
|
|
757
|
+
computeConsumptionPlanDigest(plan) !== plan.integrity.plan_digest
|
|
747
758
|
) {
|
|
748
759
|
return 'KDNA_HOST_PLAN_REF_MISMATCH';
|
|
749
760
|
}
|
|
@@ -762,14 +773,18 @@ function validateRequestSemantics(request, plan, enforceBudget = true) {
|
|
|
762
773
|
if (request.capsule.access !== plan.asset_ref.access) return 'KDNA_HOST_ASSET_REF_MISMATCH';
|
|
763
774
|
|
|
764
775
|
if (
|
|
765
|
-
request.runtime_contract.capsule_version !== request.capsule.
|
|
766
|
-
request.capsule.
|
|
776
|
+
request.runtime_contract.capsule_version !== request.capsule.contract_version ||
|
|
777
|
+
request.capsule.contract_version !== CAPSULE_CONTRACT_VERSION
|
|
767
778
|
) {
|
|
768
|
-
return '
|
|
779
|
+
return 'KDNA_HOST_CAPSULE_CONTRACT_VERSION_MISMATCH';
|
|
769
780
|
}
|
|
770
781
|
if (
|
|
771
782
|
request.runtime_contract.capsule_digest_profile !== CAPSULE_DELIVERY_PROFILE ||
|
|
772
|
-
request.
|
|
783
|
+
request.runtime_contract.capsule_digest_profile_version !==
|
|
784
|
+
CAPSULE_DELIVERY_PROFILE_VERSION ||
|
|
785
|
+
request.capsule.digests.profile !== plan.projection_request.required_digest_profile ||
|
|
786
|
+
request.capsule.digests.profile_version !==
|
|
787
|
+
plan.projection_request.required_digest_profile_version
|
|
773
788
|
) {
|
|
774
789
|
return 'KDNA_HOST_PROJECTION_CONTRACT_MISMATCH';
|
|
775
790
|
}
|
|
@@ -777,6 +792,8 @@ function validateRequestSemantics(request, plan, enforceBudget = true) {
|
|
|
777
792
|
const expectedProjectionContract = {
|
|
778
793
|
profile: plan.projection_request.profile,
|
|
779
794
|
required_digest_profile: plan.projection_request.required_digest_profile,
|
|
795
|
+
required_digest_profile_version:
|
|
796
|
+
plan.projection_request.required_digest_profile_version,
|
|
780
797
|
require_packaged_asset: plan.projection_request.require_packaged_asset,
|
|
781
798
|
};
|
|
782
799
|
if (
|
|
@@ -813,7 +830,7 @@ function validateRequestSemantics(request, plan, enforceBudget = true) {
|
|
|
813
830
|
return null;
|
|
814
831
|
}
|
|
815
832
|
|
|
816
|
-
function
|
|
833
|
+
function validateAgentHostRequest(request, context) {
|
|
817
834
|
const contextResult = snapshotContext(context, [
|
|
818
835
|
'plan',
|
|
819
836
|
'trustedPlanDigest',
|
|
@@ -825,42 +842,47 @@ function validateAgentHost2RequestV1(request, context) {
|
|
|
825
842
|
const schemaResult = validateSchema('request', request);
|
|
826
843
|
if (!schemaResult.valid) return schemaResult;
|
|
827
844
|
const safeRequest = schemaResult.value;
|
|
828
|
-
const planResult =
|
|
845
|
+
const planResult = validateConsumptionPlan(safeContext.plan, {
|
|
829
846
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
830
847
|
});
|
|
831
848
|
if (!planResult.valid) return planResult;
|
|
832
849
|
const safePlan = planResult.value;
|
|
833
|
-
const negotiation =
|
|
850
|
+
const negotiation = negotiateRuntimePair(safePlan, safeContext);
|
|
834
851
|
if (negotiation.state !== 'selected') {
|
|
835
|
-
return invalid(negotiation.issue_code, 'No strict ConsumptionPlan
|
|
852
|
+
return invalid(negotiation.issue_code, 'No strict ConsumptionPlan / Agent Host pair was selected.');
|
|
836
853
|
}
|
|
837
854
|
try {
|
|
838
855
|
const code = validateRequestSemantics(safeRequest, safePlan, true);
|
|
839
|
-
return code ? invalid(code, 'Agent Host
|
|
856
|
+
return code ? invalid(code, 'Agent Host request correlation failed.') : ok(safeRequest);
|
|
840
857
|
} catch (error) {
|
|
841
|
-
return invalid('KDNA_INPUT_INVALID', 'Agent Host
|
|
858
|
+
return invalid('KDNA_INPUT_INVALID', 'Agent Host request is not a safe JSON value.', {
|
|
842
859
|
cause: error && typeof error.message === 'string' ? error.message : String(error),
|
|
843
860
|
});
|
|
844
861
|
}
|
|
845
862
|
}
|
|
846
863
|
|
|
847
|
-
function
|
|
864
|
+
function assembleAgentHostRequest(input, plan) {
|
|
848
865
|
return {
|
|
849
866
|
protocol: HOST_PROTOCOL,
|
|
867
|
+
protocol_version: HOST_PROTOCOL_VERSION,
|
|
850
868
|
request_id: input.request_id,
|
|
851
869
|
plan_ref: {
|
|
852
870
|
plan_id: plan.plan_id,
|
|
853
871
|
plan_digest_profile: plan.integrity.profile,
|
|
872
|
+
plan_digest_profile_version: plan.integrity.profile_version,
|
|
854
873
|
plan_digest: plan.integrity.plan_digest,
|
|
855
874
|
},
|
|
856
875
|
runtime_contract: {
|
|
857
|
-
capsule_version:
|
|
876
|
+
capsule_version: CAPSULE_CONTRACT_VERSION,
|
|
858
877
|
capsule_digest_profile: CAPSULE_DELIVERY_PROFILE,
|
|
878
|
+
capsule_digest_profile_version: CAPSULE_DELIVERY_PROFILE_VERSION,
|
|
859
879
|
capsule_delivery_digest: computeCapsuleDeliveryDigest(input.capsule),
|
|
860
880
|
},
|
|
861
881
|
projection_contract: {
|
|
862
882
|
profile: plan.projection_request.profile,
|
|
863
883
|
required_digest_profile: plan.projection_request.required_digest_profile,
|
|
884
|
+
required_digest_profile_version:
|
|
885
|
+
plan.projection_request.required_digest_profile_version,
|
|
864
886
|
require_packaged_asset: plan.projection_request.require_packaged_asset,
|
|
865
887
|
},
|
|
866
888
|
result_contract: clone(plan.result_request),
|
|
@@ -874,7 +896,7 @@ function assembleAgentHost2RequestV1(input, plan) {
|
|
|
874
896
|
};
|
|
875
897
|
}
|
|
876
898
|
|
|
877
|
-
function
|
|
899
|
+
function buildAgentHostRequest(input, context) {
|
|
878
900
|
const contextResult = snapshotContext(context, [
|
|
879
901
|
'plan',
|
|
880
902
|
'trustedPlanDigest',
|
|
@@ -885,34 +907,38 @@ function buildAgentHost2RequestV1(input, context) {
|
|
|
885
907
|
const inputResult = snapshotPublicInput(input, ['request_id', 'capsule']);
|
|
886
908
|
throwInvalid(inputResult);
|
|
887
909
|
const safeContext = contextResult.value;
|
|
888
|
-
const planResult =
|
|
910
|
+
const planResult = validateConsumptionPlan(safeContext.plan, {
|
|
889
911
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
890
912
|
});
|
|
891
913
|
throwInvalid(planResult);
|
|
892
914
|
const plan = planResult.value;
|
|
893
|
-
const negotiation =
|
|
915
|
+
const negotiation = negotiateRuntimePair(plan, safeContext);
|
|
894
916
|
if (negotiation.state !== 'selected') {
|
|
895
|
-
throw new
|
|
917
|
+
throw new KDNARuntimeContractError(
|
|
896
918
|
negotiation.issue_code,
|
|
897
|
-
'Cannot build Host
|
|
919
|
+
'Cannot build Agent Host request without a selected strict execution pair.',
|
|
898
920
|
);
|
|
899
921
|
}
|
|
900
922
|
let request;
|
|
901
923
|
try {
|
|
902
|
-
request =
|
|
924
|
+
request = assembleAgentHostRequest(inputResult.value, plan);
|
|
903
925
|
} catch {
|
|
904
|
-
throw new
|
|
926
|
+
throw new KDNARuntimeContractError(
|
|
905
927
|
'KDNA_HOST_REQUEST_INPUT_INVALID',
|
|
906
|
-
'Cannot build Agent Host
|
|
928
|
+
'Cannot build Agent Host request from the supplied input.',
|
|
907
929
|
);
|
|
908
930
|
}
|
|
909
|
-
const result =
|
|
931
|
+
const result = validateAgentHostRequest(request, { ...safeContext, plan });
|
|
910
932
|
throwInvalid(result);
|
|
911
933
|
return request;
|
|
912
934
|
}
|
|
913
935
|
|
|
914
936
|
function validateReceiptSemantics(receipt, request) {
|
|
915
|
-
if (
|
|
937
|
+
if (
|
|
938
|
+
receipt.protocol !== request.protocol ||
|
|
939
|
+
receipt.protocol_version !== request.protocol_version ||
|
|
940
|
+
receipt.request_id !== request.request_id
|
|
941
|
+
) {
|
|
916
942
|
return 'KDNA_HOST_REQUEST_ID_MISMATCH';
|
|
917
943
|
}
|
|
918
944
|
const runtimeReceipt = receipt.runtime_receipt;
|
|
@@ -947,7 +973,7 @@ function validateReceiptSemantics(receipt, request) {
|
|
|
947
973
|
runtimeReceipt.capsule_version !== request.runtime_contract.capsule_version ||
|
|
948
974
|
runtimeReceipt.capsule_digest_profile !== request.runtime_contract.capsule_digest_profile
|
|
949
975
|
) {
|
|
950
|
-
return '
|
|
976
|
+
return 'KDNA_HOST_CAPSULE_CONTRACT_VERSION_MISMATCH';
|
|
951
977
|
}
|
|
952
978
|
|
|
953
979
|
const outcomeUsage = receipt.outcome?.usage ?? null;
|
|
@@ -971,7 +997,7 @@ function validateReceiptSemantics(receipt, request) {
|
|
|
971
997
|
return null;
|
|
972
998
|
}
|
|
973
999
|
|
|
974
|
-
function
|
|
1000
|
+
function validateAgentHostReceipt(receipt, context) {
|
|
975
1001
|
const contextResult = snapshotContext(context, ['request']);
|
|
976
1002
|
if (!contextResult.valid) return contextResult;
|
|
977
1003
|
const safeContext = contextResult.value;
|
|
@@ -982,16 +1008,16 @@ function validateAgentHost2ReceiptV1(receipt, context) {
|
|
|
982
1008
|
if (!requestSchemaResult.valid) {
|
|
983
1009
|
return invalid(
|
|
984
1010
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
985
|
-
'Receipt validation requires a schema-valid independent Host
|
|
1011
|
+
'Receipt validation requires a schema-valid independent Agent Host request.',
|
|
986
1012
|
requestSchemaResult.errors,
|
|
987
1013
|
);
|
|
988
1014
|
}
|
|
989
1015
|
const safeRequest = requestSchemaResult.value;
|
|
990
1016
|
try {
|
|
991
1017
|
const code = validateReceiptSemantics(safeReceipt, safeRequest);
|
|
992
|
-
return code ? invalid(code, 'Agent Host
|
|
1018
|
+
return code ? invalid(code, 'Agent Host receipt correlation failed.') : ok(safeReceipt);
|
|
993
1019
|
} catch (error) {
|
|
994
|
-
return invalid('KDNA_INPUT_INVALID', 'Agent Host
|
|
1020
|
+
return invalid('KDNA_INPUT_INVALID', 'Agent Host receipt is not a safe JSON value.', {
|
|
995
1021
|
cause: error && typeof error.message === 'string' ? error.message : String(error),
|
|
996
1022
|
});
|
|
997
1023
|
}
|
|
@@ -1038,11 +1064,11 @@ function expectedBudgetEvidence(plan, request, receipt) {
|
|
|
1038
1064
|
return { limits: clone(plan.budget), actual, comparison };
|
|
1039
1065
|
}
|
|
1040
1066
|
|
|
1041
|
-
function
|
|
1067
|
+
function deriveBudgetEvidence(plan, context) {
|
|
1042
1068
|
const contextResult = snapshotContext(context, ['trustedPlanDigest', 'request', 'receipt']);
|
|
1043
1069
|
throwInvalid(contextResult);
|
|
1044
1070
|
const safeContext = contextResult.value;
|
|
1045
|
-
const planResult =
|
|
1071
|
+
const planResult = validateConsumptionPlan(plan, {
|
|
1046
1072
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
1047
1073
|
});
|
|
1048
1074
|
throwInvalid(planResult);
|
|
@@ -1050,29 +1076,29 @@ function deriveBudgetEvidenceV1(plan, context) {
|
|
|
1050
1076
|
if (safeContext.request !== null) {
|
|
1051
1077
|
const requestResult = validateSchema('request', safeContext.request);
|
|
1052
1078
|
if (!requestResult.valid) {
|
|
1053
|
-
throw new
|
|
1079
|
+
throw new KDNARuntimeContractError(
|
|
1054
1080
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
1055
|
-
'Budget derivation requires a schema-valid independent Host
|
|
1081
|
+
'Budget derivation requires a schema-valid independent Agent Host request.',
|
|
1056
1082
|
requestResult.errors,
|
|
1057
1083
|
);
|
|
1058
1084
|
}
|
|
1059
1085
|
safeContext.request = requestResult.value;
|
|
1060
1086
|
const requestCode = validateRequestSemantics(safeContext.request, safePlan, false);
|
|
1061
1087
|
if (requestCode !== null) {
|
|
1062
|
-
throw new
|
|
1088
|
+
throw new KDNARuntimeContractError(
|
|
1063
1089
|
requestCode,
|
|
1064
|
-
'Budget derivation requires an independently correlated Host
|
|
1090
|
+
'Budget derivation requires an independently correlated Agent Host request.',
|
|
1065
1091
|
);
|
|
1066
1092
|
}
|
|
1067
1093
|
}
|
|
1068
1094
|
if (safeContext.receipt !== null) {
|
|
1069
1095
|
if (safeContext.request === null) {
|
|
1070
|
-
throw new
|
|
1096
|
+
throw new KDNARuntimeContractError(
|
|
1071
1097
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
1072
1098
|
'Budget derivation cannot use a receipt without its independent request.',
|
|
1073
1099
|
);
|
|
1074
1100
|
}
|
|
1075
|
-
const receiptResult =
|
|
1101
|
+
const receiptResult = validateAgentHostReceipt(safeContext.receipt, {
|
|
1076
1102
|
request: safeContext.request,
|
|
1077
1103
|
});
|
|
1078
1104
|
throwInvalid(receiptResult);
|
|
@@ -1081,13 +1107,13 @@ function deriveBudgetEvidenceV1(plan, context) {
|
|
|
1081
1107
|
return expectedBudgetEvidence(safePlan, safeContext.request, safeContext.receipt);
|
|
1082
1108
|
}
|
|
1083
1109
|
|
|
1084
|
-
function
|
|
1085
|
-
const planResult =
|
|
1110
|
+
function preparePreHostBudgetBlocked(capsule, safeContext) {
|
|
1111
|
+
const planResult = validateConsumptionPlan(safeContext.plan, {
|
|
1086
1112
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
1087
1113
|
});
|
|
1088
1114
|
if (!planResult.valid) return planResult;
|
|
1089
1115
|
const plan = planResult.value;
|
|
1090
|
-
const negotiation =
|
|
1116
|
+
const negotiation = negotiateRuntimePair(plan, safeContext);
|
|
1091
1117
|
if (negotiation.state !== 'selected') {
|
|
1092
1118
|
return invalid(
|
|
1093
1119
|
negotiation.issue_code,
|
|
@@ -1097,7 +1123,7 @@ function preparePreHostBudgetBlockedV1(capsule, safeContext) {
|
|
|
1097
1123
|
|
|
1098
1124
|
let projectedCandidate;
|
|
1099
1125
|
try {
|
|
1100
|
-
projectedCandidate =
|
|
1126
|
+
projectedCandidate = assembleAgentHostRequest(
|
|
1101
1127
|
{ request_id: PRE_HOST_INTERNAL_REQUEST_ID, capsule },
|
|
1102
1128
|
plan,
|
|
1103
1129
|
);
|
|
@@ -1130,12 +1156,12 @@ function preparePreHostBudgetBlockedV1(capsule, safeContext) {
|
|
|
1130
1156
|
}
|
|
1131
1157
|
|
|
1132
1158
|
/**
|
|
1133
|
-
* Build the
|
|
1159
|
+
* Build the sole supported pre-Host blocked Trace path for a successfully projected Capsule
|
|
1134
1160
|
* that exceeds an enforce-before-Host projection or task limit. This function
|
|
1135
1161
|
* never exposes the internal request-shaped correlation candidate and cannot
|
|
1136
|
-
* produce a deliverable Agent Host
|
|
1162
|
+
* produce a deliverable Agent Host request.
|
|
1137
1163
|
*/
|
|
1138
|
-
function
|
|
1164
|
+
function buildPreHostBudgetBlockedTrace(input, context) {
|
|
1139
1165
|
const inputResult = snapshotPublicInput(
|
|
1140
1166
|
input,
|
|
1141
1167
|
['trace_id', 'timestamp', 'capsule'],
|
|
@@ -1151,10 +1177,10 @@ function buildPreHostBudgetBlockedTraceV1(input, context) {
|
|
|
1151
1177
|
throwInvalid(contextResult);
|
|
1152
1178
|
const safeInput = inputResult.value;
|
|
1153
1179
|
const safeContext = contextResult.value;
|
|
1154
|
-
const prepared =
|
|
1180
|
+
const prepared = preparePreHostBudgetBlocked(safeInput.capsule, safeContext);
|
|
1155
1181
|
throwInvalid(prepared);
|
|
1156
1182
|
|
|
1157
|
-
return
|
|
1183
|
+
return buildJudgmentTrace(
|
|
1158
1184
|
{
|
|
1159
1185
|
trace_id: safeInput.trace_id,
|
|
1160
1186
|
timestamp: safeInput.timestamp,
|
|
@@ -1178,7 +1204,7 @@ function buildPreHostBudgetBlockedTraceV1(input, context) {
|
|
|
1178
1204
|
* independent Plan/Host capability facts without requiring callers to
|
|
1179
1205
|
* construct or retain an over-budget request object.
|
|
1180
1206
|
*/
|
|
1181
|
-
function
|
|
1207
|
+
function validatePreHostBudgetBlockedTrace(trace, context) {
|
|
1182
1208
|
const contextResult = snapshotContext(context, [
|
|
1183
1209
|
'plan',
|
|
1184
1210
|
'trustedPlanDigest',
|
|
@@ -1188,9 +1214,9 @@ function validatePreHostBudgetBlockedTraceV1(trace, context) {
|
|
|
1188
1214
|
]);
|
|
1189
1215
|
if (!contextResult.valid) return contextResult;
|
|
1190
1216
|
const safeContext = contextResult.value;
|
|
1191
|
-
const prepared =
|
|
1217
|
+
const prepared = preparePreHostBudgetBlocked(safeContext.capsule, safeContext);
|
|
1192
1218
|
if (!prepared.valid) return prepared;
|
|
1193
|
-
const traceResult =
|
|
1219
|
+
const traceResult = validateJudgmentTrace(trace, {
|
|
1194
1220
|
...safeContext,
|
|
1195
1221
|
plan: prepared.value.plan,
|
|
1196
1222
|
request: prepared.value.projectedCandidate,
|
|
@@ -1240,9 +1266,10 @@ function unavailableDigestEvidence() {
|
|
|
1240
1266
|
});
|
|
1241
1267
|
return {
|
|
1242
1268
|
profile: CAPSULE_DIGEST_PROFILE,
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1269
|
+
profile_version: '0.1.0',
|
|
1270
|
+
asset: item('kdna.digest-basis.container-bytes'),
|
|
1271
|
+
content: item('kdna.digest-basis.content-tree'),
|
|
1272
|
+
runtime_entry_set: item('kdna.digest-basis.runtime-entry-set'),
|
|
1246
1273
|
};
|
|
1247
1274
|
}
|
|
1248
1275
|
|
|
@@ -1324,8 +1351,9 @@ function validateTraceSemantics(
|
|
|
1324
1351
|
if (
|
|
1325
1352
|
trace.plan_ref.plan_id !== plan.plan_id ||
|
|
1326
1353
|
trace.plan_ref.plan_digest_profile !== plan.integrity.profile ||
|
|
1354
|
+
trace.plan_ref.plan_digest_profile_version !== plan.integrity.profile_version ||
|
|
1327
1355
|
trace.plan_ref.plan_digest !== plan.integrity.plan_digest ||
|
|
1328
|
-
trace.plan_ref.plan_digest !==
|
|
1356
|
+
trace.plan_ref.plan_digest !== computeConsumptionPlanDigest(plan)
|
|
1329
1357
|
) {
|
|
1330
1358
|
return 'KDNA_TRACE_PLAN_REF_MISMATCH';
|
|
1331
1359
|
}
|
|
@@ -1350,7 +1378,7 @@ function validateTraceSemantics(
|
|
|
1350
1378
|
if (validateRequestSemantics(request, plan, false) !== null) {
|
|
1351
1379
|
return 'KDNA_TRACE_REQUEST_MISMATCH';
|
|
1352
1380
|
}
|
|
1353
|
-
const negotiation =
|
|
1381
|
+
const negotiation = negotiateRuntimePair(plan, {
|
|
1354
1382
|
trustedPlanDigest: plan.integrity.plan_digest,
|
|
1355
1383
|
capabilities,
|
|
1356
1384
|
coreCapsuleVersions,
|
|
@@ -1390,7 +1418,7 @@ function validateTraceSemantics(
|
|
|
1390
1418
|
receipt.runtime_receipt.host_recomputed_capsule_delivery_digest ||
|
|
1391
1419
|
delivery.host_echoed !== receipt.runtime_receipt.echoed_capsule_delivery_digest ||
|
|
1392
1420
|
delivery.host_recomputed === p ||
|
|
1393
|
-
delivery.delivered_capsule_version !==
|
|
1421
|
+
delivery.delivered_capsule_version !== CAPSULE_CONTRACT_VERSION ||
|
|
1394
1422
|
delivery.request_id !== request.request_id ||
|
|
1395
1423
|
trace.execution.delivery_status !== 'rejected_before_execution'
|
|
1396
1424
|
) {
|
|
@@ -1402,7 +1430,7 @@ function validateTraceSemantics(
|
|
|
1402
1430
|
receipt !== null ||
|
|
1403
1431
|
delivery.host_recomputed !== null ||
|
|
1404
1432
|
delivery.host_echoed !== null ||
|
|
1405
|
-
delivery.delivered_capsule_version !==
|
|
1433
|
+
delivery.delivered_capsule_version !== CAPSULE_CONTRACT_VERSION ||
|
|
1406
1434
|
delivery.request_id !== request.request_id ||
|
|
1407
1435
|
trace.execution.delivery_status !== 'rejected_before_execution'
|
|
1408
1436
|
) {
|
|
@@ -1456,7 +1484,7 @@ function validateTraceSemantics(
|
|
|
1456
1484
|
return 'KDNA_TRACE_NEGOTIATION_EVIDENCE_MISMATCH';
|
|
1457
1485
|
}
|
|
1458
1486
|
if (runtime.negotiation_state === 'blocked') {
|
|
1459
|
-
const negotiation =
|
|
1487
|
+
const negotiation = negotiateRuntimePair(plan, {
|
|
1460
1488
|
trustedPlanDigest: plan.integrity.plan_digest,
|
|
1461
1489
|
capabilities,
|
|
1462
1490
|
coreCapsuleVersions,
|
|
@@ -1490,7 +1518,7 @@ function validateTraceSemantics(
|
|
|
1490
1518
|
return 'KDNA_TRACE_HOST_RECEIPT_MISMATCH';
|
|
1491
1519
|
}
|
|
1492
1520
|
|
|
1493
|
-
const negotiation =
|
|
1521
|
+
const negotiation = negotiateRuntimePair(plan, {
|
|
1494
1522
|
trustedPlanDigest: plan.integrity.plan_digest,
|
|
1495
1523
|
capabilities,
|
|
1496
1524
|
coreCapsuleVersions,
|
|
@@ -1563,7 +1591,7 @@ function validateTraceSemantics(
|
|
|
1563
1591
|
return validateTerminalState(trace, receipt);
|
|
1564
1592
|
}
|
|
1565
1593
|
|
|
1566
|
-
function
|
|
1594
|
+
function validateJudgmentTrace(trace, context) {
|
|
1567
1595
|
const contextResult = snapshotContext(context, [
|
|
1568
1596
|
'plan',
|
|
1569
1597
|
'trustedPlanDigest',
|
|
@@ -1580,7 +1608,7 @@ function validateJudgmentTraceV1(trace, context) {
|
|
|
1580
1608
|
const schemaResult = validateSchema('trace', trace);
|
|
1581
1609
|
if (!schemaResult.valid) return schemaResult;
|
|
1582
1610
|
const safeTrace = schemaResult.value;
|
|
1583
|
-
const planResult =
|
|
1611
|
+
const planResult = validateConsumptionPlan(safeContext.plan, {
|
|
1584
1612
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
1585
1613
|
});
|
|
1586
1614
|
if (!planResult.valid) return planResult;
|
|
@@ -1612,7 +1640,7 @@ function validateJudgmentTraceV1(trace, context) {
|
|
|
1612
1640
|
'Trace validation cannot correlate a receipt without its independent request.',
|
|
1613
1641
|
);
|
|
1614
1642
|
}
|
|
1615
|
-
const receiptResult =
|
|
1643
|
+
const receiptResult = validateAgentHostReceipt(safeContext.receipt, {
|
|
1616
1644
|
request: safeContext.request,
|
|
1617
1645
|
});
|
|
1618
1646
|
if (!receiptResult.valid) {
|
|
@@ -1649,7 +1677,7 @@ function validateJudgmentTraceV1(trace, context) {
|
|
|
1649
1677
|
}
|
|
1650
1678
|
}
|
|
1651
1679
|
|
|
1652
|
-
function
|
|
1680
|
+
function buildJudgmentTrace(input, context) {
|
|
1653
1681
|
const contextResult = snapshotContext(context, [
|
|
1654
1682
|
'plan',
|
|
1655
1683
|
'trustedPlanDigest',
|
|
@@ -1662,7 +1690,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1662
1690
|
throwInvalid(contextResult);
|
|
1663
1691
|
const safeContext = contextResult.value;
|
|
1664
1692
|
throwInvalid(validateTrustedDeliveryObservation(safeContext));
|
|
1665
|
-
const planResult =
|
|
1693
|
+
const planResult = validateConsumptionPlan(safeContext.plan, {
|
|
1666
1694
|
trustedPlanDigest: safeContext.trustedPlanDigest,
|
|
1667
1695
|
});
|
|
1668
1696
|
throwInvalid(planResult);
|
|
@@ -1670,7 +1698,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1670
1698
|
|
|
1671
1699
|
const capabilitiesResult = validateSchema('capabilities', safeContext.capabilities);
|
|
1672
1700
|
if (!capabilitiesResult.valid) {
|
|
1673
|
-
throw new
|
|
1701
|
+
throw new KDNARuntimeContractError(
|
|
1674
1702
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
1675
1703
|
'Trace construction requires schema-valid independent Host capabilities.',
|
|
1676
1704
|
capabilitiesResult.errors,
|
|
@@ -1680,7 +1708,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1680
1708
|
if (safeContext.request !== null) {
|
|
1681
1709
|
const requestResult = validateSchema('request', safeContext.request);
|
|
1682
1710
|
if (!requestResult.valid) {
|
|
1683
|
-
throw new
|
|
1711
|
+
throw new KDNARuntimeContractError(
|
|
1684
1712
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
1685
1713
|
'Trace construction requires a schema-valid independent Host request.',
|
|
1686
1714
|
requestResult.errors,
|
|
@@ -1693,14 +1721,14 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1693
1721
|
false,
|
|
1694
1722
|
);
|
|
1695
1723
|
if (requestCode !== null) {
|
|
1696
|
-
throw new
|
|
1724
|
+
throw new KDNARuntimeContractError(
|
|
1697
1725
|
requestCode,
|
|
1698
1726
|
'Trace construction requires an independently correlated Host request.',
|
|
1699
1727
|
);
|
|
1700
1728
|
}
|
|
1701
|
-
const negotiation =
|
|
1729
|
+
const negotiation = negotiateRuntimePair(safeContext.plan, safeContext);
|
|
1702
1730
|
if (negotiation.state !== 'selected') {
|
|
1703
|
-
throw new
|
|
1731
|
+
throw new KDNARuntimeContractError(
|
|
1704
1732
|
negotiation.issue_code,
|
|
1705
1733
|
'Trace construction requires a selected strict execution pair.',
|
|
1706
1734
|
);
|
|
@@ -1708,12 +1736,12 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1708
1736
|
}
|
|
1709
1737
|
if (safeContext.receipt !== null) {
|
|
1710
1738
|
if (safeContext.request === null) {
|
|
1711
|
-
throw new
|
|
1739
|
+
throw new KDNARuntimeContractError(
|
|
1712
1740
|
'KDNA_VALIDATION_CONTEXT_INVALID',
|
|
1713
1741
|
'Trace construction cannot use a receipt without its independent request.',
|
|
1714
1742
|
);
|
|
1715
1743
|
}
|
|
1716
|
-
const receiptResult =
|
|
1744
|
+
const receiptResult = validateAgentHostReceipt(safeContext.receipt, {
|
|
1717
1745
|
request: safeContext.request,
|
|
1718
1746
|
});
|
|
1719
1747
|
throwInvalid(receiptResult);
|
|
@@ -1724,7 +1752,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1724
1752
|
const request = safeContext.request;
|
|
1725
1753
|
const receipt = safeContext.receipt;
|
|
1726
1754
|
const observation = safeContext.trustedDeliveryObservation;
|
|
1727
|
-
const negotiation =
|
|
1755
|
+
const negotiation = negotiateRuntimePair(plan, safeContext);
|
|
1728
1756
|
const hasRequest = request !== null;
|
|
1729
1757
|
const hasReceipt = receipt !== null;
|
|
1730
1758
|
const comparison = hasReceipt
|
|
@@ -1738,12 +1766,13 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1738
1766
|
let trace;
|
|
1739
1767
|
try {
|
|
1740
1768
|
trace = {
|
|
1741
|
-
type: 'kdna.judgment
|
|
1742
|
-
|
|
1769
|
+
type: 'kdna.judgment-trace',
|
|
1770
|
+
contract_version: '0.1.0',
|
|
1743
1771
|
trace_id: input.trace_id,
|
|
1744
1772
|
plan_ref: {
|
|
1745
1773
|
plan_id: plan.plan_id,
|
|
1746
1774
|
plan_digest_profile: plan.integrity.profile,
|
|
1775
|
+
plan_digest_profile_version: plan.integrity.profile_version,
|
|
1747
1776
|
plan_digest: plan.integrity.plan_digest,
|
|
1748
1777
|
comparison: 'matched',
|
|
1749
1778
|
},
|
|
@@ -1768,6 +1797,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1768
1797
|
digest_evidence: hasRequest ? clone(request.capsule.digests) : unavailableDigestEvidence(),
|
|
1769
1798
|
capsule_delivery_evidence: {
|
|
1770
1799
|
basis: CAPSULE_DELIVERY_PROFILE,
|
|
1800
|
+
basis_version: CAPSULE_DELIVERY_PROFILE_VERSION,
|
|
1771
1801
|
observed: p,
|
|
1772
1802
|
sender_computed: hasRequest,
|
|
1773
1803
|
host_recomputed: hasReceipt
|
|
@@ -1777,7 +1807,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1777
1807
|
? receipt.runtime_receipt.echoed_capsule_delivery_digest
|
|
1778
1808
|
: null,
|
|
1779
1809
|
delivered_capsule_version:
|
|
1780
|
-
hasReceipt || observation === 'not_observed' ?
|
|
1810
|
+
hasReceipt || observation === 'not_observed' ? CAPSULE_CONTRACT_VERSION : null,
|
|
1781
1811
|
host_boundary_comparison: comparison,
|
|
1782
1812
|
request_id: hasReceipt || observation === 'not_observed' ? request.request_id : null,
|
|
1783
1813
|
},
|
|
@@ -1810,7 +1840,7 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1810
1840
|
? {
|
|
1811
1841
|
shape: request.result_contract.shape,
|
|
1812
1842
|
result_digest: digestJcs(receipt.outcome),
|
|
1813
|
-
basis: 'kdna
|
|
1843
|
+
basis: 'kdna.canonicalization.result-jcs',
|
|
1814
1844
|
stored: input.result_stored ?? true,
|
|
1815
1845
|
}
|
|
1816
1846
|
: null,
|
|
@@ -1818,33 +1848,35 @@ function buildJudgmentTraceV1(input, context) {
|
|
|
1818
1848
|
warnings: clone(input.warnings || []),
|
|
1819
1849
|
};
|
|
1820
1850
|
} catch {
|
|
1821
|
-
throw new
|
|
1851
|
+
throw new KDNARuntimeContractError(
|
|
1822
1852
|
'KDNA_TRACE_INPUT_INVALID',
|
|
1823
|
-
'Cannot build JudgmentTrace
|
|
1853
|
+
'Cannot build JudgmentTrace from the supplied input and independent context.',
|
|
1824
1854
|
);
|
|
1825
1855
|
}
|
|
1826
1856
|
|
|
1827
|
-
const result =
|
|
1857
|
+
const result = validateJudgmentTrace(trace, safeContext);
|
|
1828
1858
|
throwInvalid(result);
|
|
1829
1859
|
return trace;
|
|
1830
1860
|
}
|
|
1831
1861
|
|
|
1832
1862
|
module.exports = {
|
|
1833
|
-
|
|
1863
|
+
KDNARuntimeContractError,
|
|
1834
1864
|
PLAN_DIGEST_PROFILE,
|
|
1865
|
+
PLAN_DIGEST_PROFILE_VERSION,
|
|
1835
1866
|
HOST_PROTOCOL,
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1867
|
+
HOST_PROTOCOL_VERSION,
|
|
1868
|
+
DEFAULT_CORE_CAPSULE_CONTRACT_VERSIONS,
|
|
1869
|
+
parseRuntimeContractJson,
|
|
1870
|
+
computeConsumptionPlanDigest,
|
|
1871
|
+
buildConsumptionPlan,
|
|
1872
|
+
validateConsumptionPlan,
|
|
1873
|
+
negotiateRuntimePair,
|
|
1874
|
+
buildAgentHostRequest,
|
|
1875
|
+
validateAgentHostRequest,
|
|
1876
|
+
validateAgentHostReceipt,
|
|
1877
|
+
deriveBudgetEvidence,
|
|
1878
|
+
buildPreHostBudgetBlockedTrace,
|
|
1879
|
+
validatePreHostBudgetBlockedTrace,
|
|
1880
|
+
buildJudgmentTrace,
|
|
1881
|
+
validateJudgmentTrace,
|
|
1850
1882
|
};
|