@oxdeai/conformance 1.3.1 → 2.0.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/LICENSE +213 -0
  3. package/README.md +217 -5
  4. package/dist/evidence-metadata.json +3489 -0
  5. package/dist/scripts/build-evidence-metadata.d.mts +1 -0
  6. package/dist/scripts/build-evidence-metadata.mjs +7 -0
  7. package/dist/scripts/build-evidence-metadata.mjs.map +1 -0
  8. package/dist/scripts/extract-vectors.js +228 -22
  9. package/dist/scripts/extract-vectors.js.map +1 -1
  10. package/dist/src/evidenceScope.d.mts +68 -0
  11. package/dist/src/evidenceScope.mjs +127 -0
  12. package/dist/src/evidenceScope.mjs.map +1 -0
  13. package/dist/src/fixtures/conformance-engine-secret.fixture.d.ts +5 -0
  14. package/dist/src/fixtures/conformance-engine-secret.fixture.js +7 -0
  15. package/dist/src/fixtures/conformance-engine-secret.fixture.js.map +1 -0
  16. package/dist/src/fixtures/ed25519.test-only.fixture.d.ts +1 -1
  17. package/dist/src/fixtures/ed25519.test-only.fixture.js +2 -1
  18. package/dist/src/fixtures/ed25519.test-only.fixture.js.map +1 -1
  19. package/dist/src/fixtures/krl-ed25519.test-only.fixture.d.ts +13 -0
  20. package/dist/src/fixtures/krl-ed25519.test-only.fixture.js +19 -0
  21. package/dist/src/fixtures/krl-ed25519.test-only.fixture.js.map +1 -0
  22. package/dist/src/trustedTimeConformance.d.ts +32 -0
  23. package/dist/src/trustedTimeConformance.js +484 -0
  24. package/dist/src/trustedTimeConformance.js.map +1 -0
  25. package/dist/src/validate-trusted-time.d.ts +1 -0
  26. package/dist/src/validate-trusted-time.js +14 -0
  27. package/dist/src/validate-trusted-time.js.map +1 -0
  28. package/dist/src/validate.d.ts +1 -0
  29. package/dist/src/validate.js +723 -31
  30. package/dist/src/validate.js.map +1 -1
  31. package/package.json +38 -8
  32. package/vectors/audit-chain.json +2 -2
  33. package/vectors/authorization-payload.json +46 -34
  34. package/vectors/authorization-signature-verification.json +39 -1
  35. package/vectors/authorization-verification.json +54 -1
  36. package/vectors/clock-semantics-verification.json +136 -0
  37. package/vectors/delegation-chain-verification.json +366 -0
  38. package/vectors/delegation-parent-hash.json +54 -0
  39. package/vectors/delegation-signature-verification.json +317 -0
  40. package/vectors/delegation-verification.json +252 -0
  41. package/vectors/envelope-verification.json +3 -3
  42. package/vectors/key-lifecycle-verification.json +131 -0
  43. package/vectors/profile-c-state-verification.json +217 -0
  44. package/vectors/signed-krl-verification.json +103 -0
  45. package/vectors/trusted-time.json +50 -0
  46. package/dist/extract-vectors.js +0 -416
  47. package/scripts/extract-vectors.ts +0 -466
  48. package/src/fixtures/README.md +0 -11
  49. package/src/fixtures/ed25519.test-only.fixture.ts +0 -14
  50. package/src/validate.ts +0 -828
  51. package/tsconfig.json +0 -14
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Fixed engine secret used to generate and validate conformance vectors.
3
+ * This is a test-only constant — never use in production.
4
+ */
5
+ export declare const CONFORMANCE_ENGINE_SECRET = "test-secret-must-be-at-least-32-chars!!";
@@ -0,0 +1,7 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Fixed engine secret used to generate and validate conformance vectors.
4
+ * This is a test-only constant — never use in production.
5
+ */
6
+ export const CONFORMANCE_ENGINE_SECRET = "test-secret-must-be-at-least-32-chars!!";
7
+ //# sourceMappingURL=conformance-engine-secret.fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance-engine-secret.fixture.js","sourceRoot":"","sources":["../../../src/fixtures/conformance-engine-secret.fixture.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC;;;GAGG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,yCAAyC,CAAC"}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * TEST ONLY — DO NOT USE IN PRODUCTION.
2
+ * TEST ONLY - DO NOT USE IN PRODUCTION.
3
3
  *
4
4
  * Deterministic Ed25519 fixture for conformance vectors/validation.
5
5
  * This keypair is intentionally public test material and not a deployable secret.
@@ -1,5 +1,6 @@
1
+ // SPDX-License-Identifier: Apache-2.0
1
2
  /**
2
- * TEST ONLY — DO NOT USE IN PRODUCTION.
3
+ * TEST ONLY - DO NOT USE IN PRODUCTION.
3
4
  *
4
5
  * Deterministic Ed25519 fixture for conformance vectors/validation.
5
6
  * This keypair is intentionally public test material and not a deployable secret.
@@ -1 +1 @@
1
- {"version":3,"file":"ed25519.test-only.fixture.js","sourceRoot":"","sources":["../../../src/fixtures/ed25519.test-only.fixture.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,0DAA0D,GAAG;;0BAEhD,CAAC;AAE3B,MAAM,CAAC,MAAM,yDAAyD,GAAG;;yBAEhD,CAAC"}
1
+ {"version":3,"file":"ed25519.test-only.fixture.js","sourceRoot":"","sources":["../../../src/fixtures/ed25519.test-only.fixture.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,0DAA0D,GAAG;;0BAEhD,CAAC;AAE3B,MAAM,CAAC,MAAM,yDAAyD,GAAG;;yBAEhD,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * KRL SIGNING KEY — TEST ONLY — DO NOT USE IN PRODUCTION.
3
+ *
4
+ * Distinct Ed25519 fixture for SignedKRLV1 conformance vectors.
5
+ * Intentionally separate from the AuthorizationV1 / DelegationV1 signing key
6
+ * fixture (ed25519.test-only.fixture.ts) to exercise the trust-domain
7
+ * separation between KRL signing keys and authorization signing keys.
8
+ *
9
+ * kid: "krl-2026-01"
10
+ * issuer (in conformance vectors): "krl.issuer"
11
+ */
12
+ export declare const KRL_TEST_ONLY_ED25519_PRIVATE_KEY_PEM_DO_NOT_USE_IN_PRODUCTION = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIKPuSm5iCBRfVLXFwMLVWzdRDAI882fQ1XeZqwvBDayT\n-----END PRIVATE KEY-----";
13
+ export declare const KRL_TEST_ONLY_ED25519_PUBLIC_KEY_PEM_DO_NOT_USE_IN_PRODUCTION = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAoNLRsm0uJ8Zb4z9BErD/xcPyN8kK1Y3zTrpdxDGiT2A=\n-----END PUBLIC KEY-----";
@@ -0,0 +1,19 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * KRL SIGNING KEY — TEST ONLY — DO NOT USE IN PRODUCTION.
4
+ *
5
+ * Distinct Ed25519 fixture for SignedKRLV1 conformance vectors.
6
+ * Intentionally separate from the AuthorizationV1 / DelegationV1 signing key
7
+ * fixture (ed25519.test-only.fixture.ts) to exercise the trust-domain
8
+ * separation between KRL signing keys and authorization signing keys.
9
+ *
10
+ * kid: "krl-2026-01"
11
+ * issuer (in conformance vectors): "krl.issuer"
12
+ */
13
+ export const KRL_TEST_ONLY_ED25519_PRIVATE_KEY_PEM_DO_NOT_USE_IN_PRODUCTION = `-----BEGIN PRIVATE KEY-----
14
+ MC4CAQAwBQYDK2VwBCIEIKPuSm5iCBRfVLXFwMLVWzdRDAI882fQ1XeZqwvBDayT
15
+ -----END PRIVATE KEY-----`;
16
+ export const KRL_TEST_ONLY_ED25519_PUBLIC_KEY_PEM_DO_NOT_USE_IN_PRODUCTION = `-----BEGIN PUBLIC KEY-----
17
+ MCowBQYDK2VwAyEAoNLRsm0uJ8Zb4z9BErD/xcPyN8kK1Y3zTrpdxDGiT2A=
18
+ -----END PUBLIC KEY-----`;
19
+ //# sourceMappingURL=krl-ed25519.test-only.fixture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"krl-ed25519.test-only.fixture.js","sourceRoot":"","sources":["../../../src/fixtures/krl-ed25519.test-only.fixture.ts"],"names":[],"mappings":"AAAA,sCAAsC;AACtC;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,8DAA8D,GAAG;;0BAEpD,CAAC;AAE3B,MAAM,CAAC,MAAM,6DAA6D,GAAG;;yBAEpD,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { evidenceScope } from "./evidenceScope.mjs";
2
+ export declare const TRUSTED_TIME_SCHEMA_VERSION = "1.0.0";
3
+ export declare const TRUSTED_TIME_CATEGORIES: readonly ["intent_freshness", "authorization_issuance", "replay", "velocity", "authorization_verification", "determinism", "malformed_configuration", "protocol_seconds_validation", "tool_window"];
4
+ type Category = (typeof TRUSTED_TIME_CATEGORIES)[number];
5
+ type RecordValue = Record<string, unknown>;
6
+ export type TrustedTimeVector = {
7
+ id: string;
8
+ category: Category;
9
+ status: "active" | "pending";
10
+ description: string;
11
+ blocked_by?: string;
12
+ comparison_group?: string;
13
+ input: RecordValue;
14
+ expected: RecordValue;
15
+ };
16
+ export type TrustedTimeFile = {
17
+ schema_version: string;
18
+ description: string;
19
+ vectors: TrustedTimeVector[];
20
+ };
21
+ export type TrustedTimeSummary = {
22
+ active: number;
23
+ passed: number;
24
+ failed: number;
25
+ pending: number;
26
+ failures: string[];
27
+ evidenceScope: ReturnType<typeof evidenceScope>;
28
+ };
29
+ export declare function parseTrustedTimeFile(raw: unknown): TrustedTimeFile;
30
+ export declare function runTrustedTimeConformance(raw: unknown, log?: (line: string) => void): TrustedTimeSummary;
31
+ export declare function trustedTimeExitCode(summary: TrustedTimeSummary): 0 | 1;
32
+ export {};
@@ -0,0 +1,484 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ import { evidenceScope } from "./evidenceScope.mjs";
3
+ import { PolicyEngine, signAuthorizationEd25519, verifyAuthorization, verifyTrustedTime, } from "@oxdeai/core";
4
+ import { TEST_ONLY_ED25519_PRIVATE_KEY_PEM_DO_NOT_USE_IN_PRODUCTION, TEST_ONLY_ED25519_PUBLIC_KEY_PEM_DO_NOT_USE_IN_PRODUCTION, } from "./fixtures/ed25519.test-only.fixture.js";
5
+ import { CONFORMANCE_ENGINE_SECRET } from "./fixtures/conformance-engine-secret.fixture.js";
6
+ export const TRUSTED_TIME_SCHEMA_VERSION = "1.0.0";
7
+ export const TRUSTED_TIME_CATEGORIES = [
8
+ "intent_freshness", "authorization_issuance", "replay", "velocity",
9
+ "authorization_verification", "determinism", "malformed_configuration",
10
+ "protocol_seconds_validation", "tool_window",
11
+ ];
12
+ const CATEGORY_SET = new Set(TRUSTED_TIME_CATEGORIES);
13
+ // The package root currently exposes ReasonCode as a public type rather than a
14
+ // runtime value. `satisfies` keeps this registry compile-time checked against
15
+ // that public union while providing strict runtime validation for vector data.
16
+ const PUBLIC_REASON_CODES = [
17
+ "KILL_SWITCH", "ALLOWLIST_ACTION", "ALLOWLIST_ASSET", "ALLOWLIST_TARGET",
18
+ "POLICY_VERSION_MISMATCH", "STATE_INVALID", "INTENT_AMOUNT_INVALID", "BUDGET_EXCEEDED",
19
+ "PER_ACTION_CAP_EXCEEDED", "VELOCITY_EXCEEDED", "CONCURRENCY_LIMIT_EXCEEDED",
20
+ "RECURSION_DEPTH_EXCEEDED", "REPLAY_NONCE", "REPLAY_DETECTED", "AUTH_EXPIRED",
21
+ "AUTH_SIGNATURE_INVALID", "AUTH_INTENT_MISMATCH", "INTERNAL_ERROR",
22
+ "CONCURRENCY_RELEASE_INVALID", "TOOL_CALL_LIMIT_EXCEEDED",
23
+ "INTENT_FRESHNESS_FUTURE", "INTENT_STALE",
24
+ ];
25
+ const ALL_PUBLIC_REASON_CODES_COVERED = true;
26
+ void ALL_PUBLIC_REASON_CODES_COVERED;
27
+ const REASON_CODES = new Set(PUBLIC_REASON_CODES);
28
+ const VERIFICATION_CODES = new Set(["AUTH_EXPIRED", "AUTH_ISSUED_AT_IMPLAUSIBLE"]);
29
+ const COMMON_KEYS = new Set(["id", "category", "status", "description", "blocked_by", "comparison_group", "input", "expected"]);
30
+ const INPUT_KEYS = {
31
+ intent_freshness: new Set(["intent_timestamp", "evaluation_time", "max_clock_skew_seconds", "max_intent_age_seconds"]),
32
+ authorization_issuance: new Set(["intent_timestamp", "evaluation_time", "effective_ttl", "authorization_action"]),
33
+ replay: new Set(["replay_window_seconds", "max_nonces_per_agent", "initial_nonce_state", "steps"]),
34
+ velocity: new Set(["window_seconds", "max_actions", "initial_velocity", "steps"]),
35
+ authorization_verification: new Set(["authorization_issued_at", "authorization_expiry", "verifier_time", "max_future_issued_at_skew_seconds"]),
36
+ determinism: new Set(["kind", "repeat", "intent_timestamp", "evaluation_time", "effective_ttl", "window_seconds", "max_actions", "initial_velocity", "steps"]),
37
+ malformed_configuration: new Set(["configuration_field", "configuration_value"]),
38
+ protocol_seconds_validation: new Set(["case", "intent_timestamp", "evaluation_time", "effective_ttl"]),
39
+ tool_window: new Set(["window_seconds", "max_calls", "max_calls_by_tool", "initial_tool_calls", "steps"]),
40
+ };
41
+ const EXPECTED_KEYS = {
42
+ intent_freshness: new Set(["decision", "reasons"]),
43
+ authorization_issuance: new Set(["decision", "reasons", "authorization_issued_at", "authorization_expiry"]),
44
+ replay: new Set(["steps"]),
45
+ velocity: new Set(["steps"]),
46
+ authorization_verification: new Set(["status", "violation_codes"]),
47
+ determinism: new Set(["decision", "reasons", "authorization_issued_at", "authorization_expiry", "identical_output"]),
48
+ malformed_configuration: new Set(["throws", "error_includes"]),
49
+ protocol_seconds_validation: new Set(["decision", "reasons", "throws", "error_includes"]),
50
+ tool_window: new Set(["steps"]),
51
+ };
52
+ function object(value, where) {
53
+ if (typeof value !== "object" || value === null || Array.isArray(value))
54
+ throw new Error(`${where}: expected object`);
55
+ return value;
56
+ }
57
+ function exactKeys(value, allowed, where) {
58
+ for (const key of Object.keys(value))
59
+ if (!allowed.has(key))
60
+ throw new Error(`${where}: unknown field ${key}`);
61
+ }
62
+ function text(value, where) {
63
+ if (typeof value !== "string" || value.length === 0)
64
+ throw new Error(`${where}: expected non-empty string`);
65
+ return value;
66
+ }
67
+ function array(value, where) {
68
+ if (!Array.isArray(value))
69
+ throw new Error(`${where}: expected array`);
70
+ return value;
71
+ }
72
+ function safeSeconds(value, where, positive = false) {
73
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < (positive ? 1 : 0)) {
74
+ throw new Error(`${where}: expected ${positive ? "positive" : "non-negative"} safe-integer protocol seconds`);
75
+ }
76
+ return value;
77
+ }
78
+ function nonNegativeCount(value, where) {
79
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0)
80
+ throw new Error(`${where}: expected non-negative safe integer`);
81
+ return value;
82
+ }
83
+ function validateReasons(value, id, where) {
84
+ for (const [index, reason] of array(value, `${id} ${where}`).entries()) {
85
+ if (typeof reason !== "string" || !REASON_CODES.has(reason))
86
+ throw new Error(`${id} ${where}[${index}]: unsupported ReasonCode ${String(reason)}`);
87
+ }
88
+ }
89
+ function validateDecisionExpected(expected, id) {
90
+ if (expected.decision !== "ALLOW" && expected.decision !== "DENY")
91
+ throw new Error(`${id} expected.decision: expected ALLOW or DENY`);
92
+ validateReasons(expected.reasons, id, "expected.reasons");
93
+ if (expected.decision === "ALLOW" && expected.reasons.length !== 0)
94
+ throw new Error(`${id} expected.reasons: ALLOW must have no reasons`);
95
+ }
96
+ function validateNonceState(value, id, where) {
97
+ for (const [i, raw] of array(value, `${id} ${where}`).entries()) {
98
+ const item = object(raw, `${id} ${where}[${i}]`);
99
+ exactKeys(item, new Set(["nonce", "nonce_first_seen_time"]), `${id} ${where}[${i}]`);
100
+ text(item.nonce, `${id} ${where}[${i}].nonce`);
101
+ safeSeconds(item.nonce_first_seen_time, `${id} ${where}[${i}].nonce_first_seen_time`);
102
+ }
103
+ }
104
+ function validateToolCalls(value, id, where) {
105
+ for (const [i, raw] of array(value, `${id} ${where}`).entries()) {
106
+ const item = object(raw, `${id} ${where}[${i}]`);
107
+ exactKeys(item, new Set(["tool_call_time", "tool"]), `${id} ${where}[${i}]`);
108
+ if (typeof item.tool_call_time !== "number" || !Number.isSafeInteger(item.tool_call_time)) {
109
+ throw new Error(`${id} ${where}[${i}].tool_call_time: expected safe integer`);
110
+ }
111
+ text(item.tool, `${id} ${where}[${i}].tool`);
112
+ }
113
+ }
114
+ function validateSteps(vector) {
115
+ const steps = array(vector.input.steps, `${vector.id} input.steps`);
116
+ if (steps.length === 0)
117
+ throw new Error(`${vector.id} input.steps: must not be empty`);
118
+ for (const [i, raw] of steps.entries()) {
119
+ const step = object(raw, `${vector.id} input.steps[${i}]`);
120
+ exactKeys(step, new Set(["intent_timestamp", "evaluation_time", "nonce"]), `${vector.id} input.steps[${i}]`);
121
+ safeSeconds(step.intent_timestamp, `${vector.id} input.steps[${i}].intent_timestamp`);
122
+ safeSeconds(step.evaluation_time, `${vector.id} input.steps[${i}].evaluation_time`);
123
+ text(step.nonce, `${vector.id} input.steps[${i}].nonce`);
124
+ }
125
+ if (vector.category === "determinism")
126
+ return;
127
+ const expectedSteps = array(vector.expected.steps, `${vector.id} expected.steps`);
128
+ if (expectedSteps.length !== steps.length)
129
+ throw new Error(`${vector.id} expected.steps: expected ${steps.length} entries`);
130
+ for (const [i, raw] of expectedSteps.entries()) {
131
+ const step = object(raw, `${vector.id} expected.steps[${i}]`);
132
+ const allowed = vector.category === "replay"
133
+ ? new Set(["decision", "reasons", "nonce_state"])
134
+ : vector.category === "tool_window"
135
+ ? new Set(["decision", "reasons", "tool_calls"])
136
+ : new Set(["decision", "reasons", "velocity_window_start", "velocity_count"]);
137
+ exactKeys(step, allowed, `${vector.id} expected.steps[${i}]`);
138
+ validateDecisionExpected(step, `${vector.id} step ${i + 1}`);
139
+ if (vector.category === "replay")
140
+ validateNonceState(step.nonce_state, vector.id, `expected.steps[${i}].nonce_state`);
141
+ else if (vector.category === "tool_window")
142
+ validateToolCalls(step.tool_calls, vector.id, `expected.steps[${i}].tool_calls`);
143
+ else {
144
+ // Negative starts are permitted only as an expected preservation value for the malformed-state vector.
145
+ if (typeof step.velocity_window_start !== "number" || !Number.isSafeInteger(step.velocity_window_start))
146
+ throw new Error(`${vector.id} expected.steps[${i}].velocity_window_start: expected safe integer`);
147
+ nonNegativeCount(step.velocity_count, `${vector.id} expected.steps[${i}].velocity_count`);
148
+ }
149
+ }
150
+ }
151
+ export function parseTrustedTimeFile(raw) {
152
+ const file = object(raw, "trusted-time");
153
+ exactKeys(file, new Set(["schema_version", "description", "vectors"]), "trusted-time");
154
+ if (file.schema_version !== TRUSTED_TIME_SCHEMA_VERSION)
155
+ throw new Error(`trusted-time schema_version: expected ${TRUSTED_TIME_SCHEMA_VERSION}`);
156
+ text(file.description, "trusted-time description");
157
+ const seen = new Set();
158
+ const vectors = array(file.vectors, "trusted-time vectors");
159
+ for (const rawVector of vectors) {
160
+ const vector = object(rawVector, "trusted-time vector");
161
+ exactKeys(vector, COMMON_KEYS, `trusted-time vector ${String(vector.id ?? "(missing)")}`);
162
+ const id = text(vector.id, "trusted-time vector id");
163
+ if (seen.has(id))
164
+ throw new Error(`${id}: duplicate vector ID`);
165
+ seen.add(id);
166
+ if (!CATEGORY_SET.has(String(vector.category)))
167
+ throw new Error(`${id}: unknown category ${String(vector.category)}`);
168
+ if (vector.status !== "active" && vector.status !== "pending")
169
+ throw new Error(`${id}: malformed status ${String(vector.status)}`);
170
+ text(vector.description, `${id} description`);
171
+ if (vector.status === "pending")
172
+ text(vector.blocked_by, `${id} blocked_by`);
173
+ else if (vector.blocked_by !== undefined)
174
+ throw new Error(`${id}: active vector must not define blocked_by`);
175
+ if (vector.comparison_group !== undefined)
176
+ text(vector.comparison_group, `${id} comparison_group`);
177
+ const input = object(vector.input, `${id} input`);
178
+ const expected = object(vector.expected, `${id} expected`);
179
+ exactKeys(input, INPUT_KEYS[vector.category], `${id} input`);
180
+ exactKeys(expected, EXPECTED_KEYS[vector.category], `${id} expected`);
181
+ if (vector.category === "intent_freshness") {
182
+ safeSeconds(input.intent_timestamp, `${id} input.intent_timestamp`);
183
+ safeSeconds(input.evaluation_time, `${id} input.evaluation_time`);
184
+ safeSeconds(input.max_clock_skew_seconds, `${id} input.max_clock_skew_seconds`);
185
+ safeSeconds(input.max_intent_age_seconds, `${id} input.max_intent_age_seconds`);
186
+ validateDecisionExpected(expected, id);
187
+ }
188
+ else if (vector.category === "authorization_issuance") {
189
+ safeSeconds(input.intent_timestamp, `${id} input.intent_timestamp`);
190
+ safeSeconds(input.evaluation_time, `${id} input.evaluation_time`);
191
+ safeSeconds(input.effective_ttl, `${id} input.effective_ttl`, true);
192
+ if (input.authorization_action !== "EXECUTE" && input.authorization_action !== "RELEASE")
193
+ throw new Error(`${id} input.authorization_action: unknown action`);
194
+ validateDecisionExpected(expected, id);
195
+ safeSeconds(expected.authorization_issued_at, `${id} expected.authorization_issued_at`);
196
+ safeSeconds(expected.authorization_expiry, `${id} expected.authorization_expiry`);
197
+ }
198
+ else if (vector.category === "replay") {
199
+ safeSeconds(input.replay_window_seconds, `${id} input.replay_window_seconds`, true);
200
+ nonNegativeCount(input.max_nonces_per_agent, `${id} input.max_nonces_per_agent`);
201
+ if (input.initial_nonce_state !== undefined)
202
+ validateNonceState(input.initial_nonce_state, id, "input.initial_nonce_state");
203
+ validateSteps(vector);
204
+ }
205
+ else if (vector.category === "velocity") {
206
+ safeSeconds(input.window_seconds, `${id} input.window_seconds`, true);
207
+ nonNegativeCount(input.max_actions, `${id} input.max_actions`);
208
+ if (input.initial_velocity !== undefined)
209
+ validateInitialVelocity(input.initial_velocity, id);
210
+ validateSteps(vector);
211
+ }
212
+ else if (vector.category === "authorization_verification") {
213
+ safeSeconds(input.authorization_issued_at, `${id} input.authorization_issued_at`);
214
+ safeSeconds(input.authorization_expiry, `${id} input.authorization_expiry`);
215
+ safeSeconds(input.verifier_time, `${id} input.verifier_time`);
216
+ safeSeconds(input.max_future_issued_at_skew_seconds, `${id} input.max_future_issued_at_skew_seconds`);
217
+ if (expected.status !== "ok" && expected.status !== "invalid")
218
+ throw new Error(`${id} expected.status: unsupported verifier status`);
219
+ for (const code of array(expected.violation_codes, `${id} expected.violation_codes`))
220
+ if (typeof code !== "string" || !VERIFICATION_CODES.has(code))
221
+ throw new Error(`${id} expected.violation_codes: unsupported verification code ${String(code)}`);
222
+ }
223
+ else if (vector.category === "determinism") {
224
+ nonNegativeCount(input.repeat, `${id} input.repeat`);
225
+ if (input.repeat < 2)
226
+ throw new Error(`${id} input.repeat: must be at least 2`);
227
+ if (input.kind === "velocity_sequence") {
228
+ safeSeconds(input.window_seconds, `${id} input.window_seconds`, true);
229
+ nonNegativeCount(input.max_actions, `${id} input.max_actions`);
230
+ if (input.initial_velocity !== undefined)
231
+ validateInitialVelocity(input.initial_velocity, id);
232
+ validateSteps(vector);
233
+ }
234
+ else {
235
+ if (input.kind !== undefined)
236
+ throw new Error(`${id} input.kind: unsupported determinism kind`);
237
+ safeSeconds(input.intent_timestamp, `${id} input.intent_timestamp`);
238
+ safeSeconds(input.evaluation_time, `${id} input.evaluation_time`);
239
+ safeSeconds(input.effective_ttl, `${id} input.effective_ttl`, true);
240
+ validateDecisionExpected(expected, id);
241
+ }
242
+ if (expected.identical_output !== true)
243
+ throw new Error(`${id} expected.identical_output: must be true`);
244
+ }
245
+ else if (vector.category === "malformed_configuration") {
246
+ text(input.configuration_field, `${id} input.configuration_field`);
247
+ if (expected.throws !== true)
248
+ throw new Error(`${id} expected.throws: must be true`);
249
+ text(expected.error_includes, `${id} expected.error_includes`);
250
+ }
251
+ else if (vector.category === "protocol_seconds_validation") {
252
+ text(input.case, `${id} input.case`);
253
+ if (expected.reasons !== undefined)
254
+ validateDecisionExpected(expected, id);
255
+ if (expected.throws !== undefined && expected.throws !== true)
256
+ throw new Error(`${id} expected.throws: must be true`);
257
+ if (expected.error_includes !== undefined)
258
+ text(expected.error_includes, `${id} expected.error_includes`);
259
+ }
260
+ else if (vector.category === "tool_window") {
261
+ safeSeconds(input.window_seconds, `${id} input.window_seconds`, true);
262
+ nonNegativeCount(input.max_calls, `${id} input.max_calls`);
263
+ if (input.max_calls_by_tool !== undefined)
264
+ nonNegativeCount(input.max_calls_by_tool, `${id} input.max_calls_by_tool`);
265
+ if (input.initial_tool_calls !== undefined)
266
+ validateToolCalls(input.initial_tool_calls, id, "input.initial_tool_calls");
267
+ validateSteps(vector);
268
+ }
269
+ }
270
+ return file;
271
+ }
272
+ function validateInitialVelocity(raw, id) {
273
+ const value = object(raw, `${id} input.initial_velocity`);
274
+ exactKeys(value, new Set(["velocity_window_start", "velocity_count"]), `${id} input.initial_velocity`);
275
+ if (typeof value.velocity_window_start !== "number" || !Number.isSafeInteger(value.velocity_window_start))
276
+ throw new Error(`${id} input.initial_velocity.velocity_window_start: expected safe integer`);
277
+ nonNegativeCount(value.velocity_count, `${id} input.initial_velocity.velocity_count`);
278
+ }
279
+ function makeState() {
280
+ return {
281
+ policy_version: "v1.0.0", period_id: "period-1",
282
+ kill_switch: { global: false, agents: {} }, allowlists: {},
283
+ budget: { budget_limit: { "agent-1": 5000000n }, spent_in_period: { "agent-1": 0n } },
284
+ max_amount_per_action: { "agent-1": 2000000n },
285
+ velocity: { config: { window_seconds: 60, max_actions: 10 }, counters: {} },
286
+ replay: { window_seconds: 600, max_nonces_per_agent: 256, nonces: {} },
287
+ concurrency: { max_concurrent: { "agent-1": 10 }, active: {}, active_auths: {} },
288
+ recursion: { max_depth: { "agent-1": 10 } },
289
+ tool_limits: { window_seconds: 600, max_calls: { "agent-1": 100 }, calls: {} },
290
+ };
291
+ }
292
+ function makeEngine(ttl = 60, skew = 300, age = 300) {
293
+ return new PolicyEngine({ policy_version: "v1.0.0", engine_secret: CONFORMANCE_ENGINE_SECRET, authorization_ttl_seconds: ttl, maxClockSkewSeconds: skew, maxIntentAgeSeconds: age, policyId: "a".repeat(64) });
294
+ }
295
+ function intent(id, timestamp, nonce, action = "EXECUTE") {
296
+ const nonceValue = /^\d+$/.test(nonce)
297
+ ? BigInt(nonce)
298
+ : BigInt([...nonce].reduce((n, c) => n + c.charCodeAt(0), 10_000));
299
+ return {
300
+ intent_id: `${id}-${nonce}`, agent_id: "agent-1", action_type: "PAYMENT", amount: 100n,
301
+ asset: "wallet", target: "merchant-1", timestamp, metadata_hash: "0".repeat(64),
302
+ nonce: nonceValue, signature: "sig", depth: 0,
303
+ ...(action === "RELEASE" ? { type: "RELEASE", authorization_id: "release-id" } : { type: "EXECUTE" }),
304
+ };
305
+ }
306
+ function normalize(value) {
307
+ return JSON.parse(JSON.stringify(value, (_key, item) => typeof item === "bigint" ? `${item}n` : item));
308
+ }
309
+ function equal(actual, expected) { return JSON.stringify(normalize(actual)) === JSON.stringify(normalize(expected)); }
310
+ function runSequence(vector, category) {
311
+ const input = vector.input;
312
+ let state = makeState();
313
+ if (category === "replay") {
314
+ state.replay.window_seconds = input.replay_window_seconds;
315
+ state.replay.max_nonces_per_agent = input.max_nonces_per_agent;
316
+ state.replay.nonces["agent-1"] = (input.initial_nonce_state ?? []).map(x => ({ nonce: x.nonce, ts: x.nonce_first_seen_time }));
317
+ }
318
+ else if (category === "velocity") {
319
+ state.velocity.config = { window_seconds: input.window_seconds, max_actions: input.max_actions };
320
+ if (input.initial_velocity) {
321
+ const initial = input.initial_velocity;
322
+ state.velocity.counters["agent-1"] = { window_start: initial.velocity_window_start, count: initial.velocity_count };
323
+ }
324
+ }
325
+ else {
326
+ state.tool_limits.window_seconds = input.window_seconds;
327
+ state.tool_limits.max_calls["agent-1"] = input.max_calls;
328
+ if (input.max_calls_by_tool !== undefined) {
329
+ state.tool_limits.max_calls_by_tool = { "agent-1": { search: input.max_calls_by_tool } };
330
+ }
331
+ state.tool_limits.calls["agent-1"] = (input.initial_tool_calls ?? []).map(x => ({ ts: x.tool_call_time, tool: x.tool }));
332
+ }
333
+ const observations = [];
334
+ for (const [index, rawStep] of input.steps.entries()) {
335
+ const before = structuredClone(state);
336
+ const stepIntent = {
337
+ ...intent(vector.id, rawStep.intent_timestamp, rawStep.nonce),
338
+ ...(category === "tool_window" ? { tool: "search", tool_call: true } : {}),
339
+ };
340
+ const out = makeEngine().evaluatePure(stepIntent, state, rawStep.evaluation_time);
341
+ if (out.decision === "ALLOW")
342
+ state = out.nextState;
343
+ else
344
+ state = before;
345
+ const base = { decision: out.decision, reasons: [...out.reasons] };
346
+ observations.push(category === "replay"
347
+ ? { ...base, nonce_state: (state.replay.nonces["agent-1"] ?? []).map(x => ({ nonce: x.nonce, nonce_first_seen_time: x.ts })) }
348
+ : category === "velocity"
349
+ ? { ...base, velocity_window_start: state.velocity.counters["agent-1"]?.window_start, velocity_count: state.velocity.counters["agent-1"]?.count }
350
+ : { ...base, tool_calls: (state.tool_limits.calls["agent-1"] ?? []).map(x => ({ tool_call_time: x.ts, tool: x.tool })) });
351
+ void index;
352
+ }
353
+ return observations;
354
+ }
355
+ const KEYSET = { issuer: "issuer", version: "1", keys: [{ kid: "k1", alg: "Ed25519", public_key: TEST_ONLY_ED25519_PUBLIC_KEY_PEM_DO_NOT_USE_IN_PRODUCTION }] };
356
+ function runVector(vector) {
357
+ const input = vector.input;
358
+ if (vector.category === "intent_freshness") {
359
+ return verifyTrustedTime({ intentTimestamp: input.intent_timestamp, evaluationTime: input.evaluation_time, maxClockSkewSeconds: input.max_clock_skew_seconds, maxIntentAgeSeconds: input.max_intent_age_seconds });
360
+ }
361
+ if (vector.category === "authorization_issuance") {
362
+ const action = input.authorization_action;
363
+ const state = makeState();
364
+ if (action === "RELEASE") {
365
+ state.concurrency.active["agent-1"] = 1;
366
+ state.concurrency.active_auths["agent-1"] = { "release-id": { expires_at: input.evaluation_time + 600 } };
367
+ }
368
+ const out = makeEngine(input.effective_ttl).evaluatePure(intent(vector.id, input.intent_timestamp, "1", action), state, input.evaluation_time);
369
+ return out.decision === "ALLOW" ? { decision: out.decision, reasons: out.reasons, authorization_issued_at: out.authorization.issued_at, authorization_expiry: out.authorization.expiry } : { decision: out.decision, reasons: out.reasons };
370
+ }
371
+ if (vector.category === "replay" || vector.category === "velocity")
372
+ return { steps: runSequence(vector, vector.category) };
373
+ if (vector.category === "tool_window") {
374
+ const first = { steps: runSequence(vector, "tool_window") };
375
+ const second = { steps: runSequence(vector, "tool_window") };
376
+ if (!equal(first, second))
377
+ throw new Error(`${vector.id}: repeated tool-window execution was not deterministic`);
378
+ return first;
379
+ }
380
+ if (vector.category === "authorization_verification") {
381
+ const unsigned = { auth_id: vector.id, issuer: "issuer", audience: "aud", intent_hash: "1".repeat(64), state_hash: "2".repeat(64), policy_id: "3".repeat(64), decision: "ALLOW", issued_at: input.authorization_issued_at, expiry: input.authorization_expiry, kid: "k1" };
382
+ const auth = signAuthorizationEd25519(unsigned, TEST_ONLY_ED25519_PRIVATE_KEY_PEM_DO_NOT_USE_IN_PRODUCTION);
383
+ const result = verifyAuthorization(auth, { now: input.verifier_time, mode: "strict", expectedIssuer: "issuer", expectedAudience: "aud", trustedKeySets: KEYSET, maxFutureIssuedAtSkewSeconds: input.max_future_issued_at_skew_seconds });
384
+ return { status: result.status, violation_codes: result.violations.map(v => v.code) };
385
+ }
386
+ if (vector.category === "malformed_configuration") {
387
+ try {
388
+ if (input.configuration_field === "authorization_ttl_seconds")
389
+ makeEngine(input.configuration_value);
390
+ else
391
+ verifyAuthorization({}, { maxFutureIssuedAtSkewSeconds: input.configuration_value });
392
+ return { throws: false, error_includes: "" };
393
+ }
394
+ catch (error) {
395
+ return { throws: true, error_includes: error instanceof Error ? error.message : String(error) };
396
+ }
397
+ }
398
+ if (vector.category === "protocol_seconds_validation") {
399
+ try {
400
+ if (input.case === "intent_timestamp") {
401
+ return verifyTrustedTime({ intentTimestamp: input.intent_timestamp, evaluationTime: input.evaluation_time, maxClockSkewSeconds: 300, maxIntentAgeSeconds: 300 });
402
+ }
403
+ const out = makeEngine(input.effective_ttl ?? 60).evaluatePure(intent(vector.id, input.intent_timestamp, "1"), makeState(), input.evaluation_time);
404
+ return { decision: out.decision, reasons: out.reasons };
405
+ }
406
+ catch (error) {
407
+ return { throws: true, error_includes: error instanceof Error ? error.message : String(error) };
408
+ }
409
+ }
410
+ if (vector.category === "determinism") {
411
+ const outputs = [];
412
+ for (let i = 0; i < input.repeat; i++) {
413
+ if (input.kind === "velocity_sequence")
414
+ outputs.push({ steps: runSequence(vector, "velocity") });
415
+ else {
416
+ const out = makeEngine(input.effective_ttl).evaluatePure(intent(vector.id, input.intent_timestamp, "1"), makeState(), input.evaluation_time);
417
+ outputs.push(out.decision === "ALLOW" ? { decision: out.decision, reasons: out.reasons, authorization_issued_at: out.authorization.issued_at, authorization_expiry: out.authorization.expiry, full: normalize(out) } : normalize(out));
418
+ }
419
+ }
420
+ const first = outputs[0];
421
+ const identical = outputs.every(x => equal(x, first));
422
+ if (input.kind === "velocity_sequence")
423
+ return { identical_output: identical };
424
+ const base = first;
425
+ return { decision: base.decision, reasons: base.reasons, authorization_issued_at: base.authorization_issued_at, authorization_expiry: base.authorization_expiry, identical_output: identical };
426
+ }
427
+ throw new Error(`${vector.id}: active category ${vector.category} has no executor`);
428
+ }
429
+ function matches(actual, expected) {
430
+ if (expected.throws === true)
431
+ return actual.throws === true && String(actual.error_includes).includes(String(expected.error_includes));
432
+ return equal(actual, expected);
433
+ }
434
+ function mismatchDetail(vector, actual) {
435
+ if (vector.category !== "replay" && vector.category !== "velocity" && vector.category !== "tool_window")
436
+ return undefined;
437
+ const actualSteps = actual.steps;
438
+ const expectedSteps = vector.expected.steps;
439
+ const inputSteps = vector.input.steps;
440
+ for (let index = 0; index < expectedSteps.length; index++) {
441
+ if (!equal(actualSteps[index], expectedSteps[index])) {
442
+ const input = inputSteps[index];
443
+ return `step ${index + 1} intent_timestamp=${String(input.intent_timestamp)} evaluation_time=${String(input.evaluation_time)} expected=${JSON.stringify(expectedSteps[index])} actual=${JSON.stringify(actualSteps[index])}`;
444
+ }
445
+ }
446
+ return undefined;
447
+ }
448
+ export function runTrustedTimeConformance(raw, log = console.log) {
449
+ const file = parseTrustedTimeFile(raw);
450
+ const passedCaseIds = [];
451
+ const summary = { active: 0, passed: 0, failed: 0, pending: 0, failures: [] };
452
+ for (const vector of file.vectors) {
453
+ if (vector.status === "pending") {
454
+ summary.pending++;
455
+ log(`PENDING ${vector.id} blocked_by=${vector.blocked_by}`);
456
+ continue;
457
+ }
458
+ summary.active++;
459
+ try {
460
+ const actual = runVector(vector);
461
+ if (!matches(actual, vector.expected))
462
+ throw new Error(mismatchDetail(vector, actual) ?? `expected=${JSON.stringify(vector.expected)} actual=${JSON.stringify(actual)}`);
463
+ passedCaseIds.push(vector.id);
464
+ summary.passed++;
465
+ log(`PASS ${vector.id}`);
466
+ }
467
+ catch (error) {
468
+ summary.failed++;
469
+ const detail = error instanceof Error ? error.message : String(error);
470
+ const message = `${vector.id}: ${detail}`;
471
+ summary.failures.push(message);
472
+ log(`FAIL ${message}`);
473
+ }
474
+ }
475
+ return { ...summary, evidenceScope: evidenceScope({ consumer: "packages/conformance/src/trustedTimeConformance.ts", runtime: "TypeScript", selections: [{
476
+ representation: "packages/conformance/vectors/trusted-time.json", data: raw,
477
+ consumer: "packages/conformance/src/trustedTimeConformance.ts", runtime: "TypeScript",
478
+ caseIds: file.vectors.filter(v => v.status === "active").map(v => v.id), passedCaseIds,
479
+ }] }) };
480
+ }
481
+ export function trustedTimeExitCode(summary) {
482
+ return summary.failed === 0 ? 0 : 1;
483
+ }
484
+ //# sourceMappingURL=trustedTimeConformance.js.map