agent-passport-system 1.27.0 → 1.29.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 (56) hide show
  1. package/README.md +45 -7
  2. package/dist/src/core/anchor-state.d.ts +46 -0
  3. package/dist/src/core/anchor-state.d.ts.map +1 -0
  4. package/dist/src/core/anchor-state.js +80 -0
  5. package/dist/src/core/anchor-state.js.map +1 -0
  6. package/dist/src/core/canonical-jcs.d.ts +23 -0
  7. package/dist/src/core/canonical-jcs.d.ts.map +1 -0
  8. package/dist/src/core/canonical-jcs.js +125 -0
  9. package/dist/src/core/canonical-jcs.js.map +1 -0
  10. package/dist/src/core/data-narrowing.d.ts +43 -0
  11. package/dist/src/core/data-narrowing.d.ts.map +1 -0
  12. package/dist/src/core/data-narrowing.js +97 -0
  13. package/dist/src/core/data-narrowing.js.map +1 -0
  14. package/dist/src/core/data-source-attribution.d.ts +17 -0
  15. package/dist/src/core/data-source-attribution.d.ts.map +1 -0
  16. package/dist/src/core/data-source-attribution.js +219 -0
  17. package/dist/src/core/data-source-attribution.js.map +1 -0
  18. package/dist/src/core/denial-domains.d.ts +43 -0
  19. package/dist/src/core/denial-domains.d.ts.map +1 -0
  20. package/dist/src/core/denial-domains.js +153 -0
  21. package/dist/src/core/denial-domains.js.map +1 -0
  22. package/dist/src/core/fidelity-probe.d.ts +170 -0
  23. package/dist/src/core/fidelity-probe.d.ts.map +1 -0
  24. package/dist/src/core/fidelity-probe.js +189 -0
  25. package/dist/src/core/fidelity-probe.js.map +1 -0
  26. package/dist/src/core/gateway-identity.d.ts +59 -0
  27. package/dist/src/core/gateway-identity.d.ts.map +1 -0
  28. package/dist/src/core/gateway-identity.js +195 -0
  29. package/dist/src/core/gateway-identity.js.map +1 -0
  30. package/dist/src/core/gateway-wiring.d.ts +32 -0
  31. package/dist/src/core/gateway-wiring.d.ts.map +1 -0
  32. package/dist/src/core/gateway-wiring.js +71 -0
  33. package/dist/src/core/gateway-wiring.js.map +1 -0
  34. package/dist/src/core/gateway.d.ts +12 -1
  35. package/dist/src/core/gateway.d.ts.map +1 -1
  36. package/dist/src/core/gateway.js +113 -3
  37. package/dist/src/core/gateway.js.map +1 -1
  38. package/dist/src/core/governance-posture.d.ts +72 -0
  39. package/dist/src/core/governance-posture.d.ts.map +1 -0
  40. package/dist/src/core/governance-posture.js +173 -0
  41. package/dist/src/core/governance-posture.js.map +1 -0
  42. package/dist/src/core/reputation-authority.d.ts +13 -4
  43. package/dist/src/core/reputation-authority.d.ts.map +1 -1
  44. package/dist/src/core/reputation-authority.js +29 -5
  45. package/dist/src/core/reputation-authority.js.map +1 -1
  46. package/dist/src/index.d.ts +17 -0
  47. package/dist/src/index.d.ts.map +1 -1
  48. package/dist/src/index.js +17 -0
  49. package/dist/src/index.js.map +1 -1
  50. package/dist/src/types/data-contribution.d.ts +28 -0
  51. package/dist/src/types/data-contribution.d.ts.map +1 -1
  52. package/dist/src/types/gateway.d.ts +45 -1
  53. package/dist/src/types/gateway.d.ts.map +1 -1
  54. package/dist/src/types/reputation-authority.d.ts +4 -0
  55. package/dist/src/types/reputation-authority.d.ts.map +1 -1
  56. package/package.json +3 -3
package/README.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/agent-passport-system)](https://www.npmjs.com/package/agent-passport-system)
4
4
  [![license](https://img.shields.io/npm/l/agent-passport-system)](https://github.com/aeoess/agent-passport-system/blob/main/LICENSE)
5
- [![tests](https://img.shields.io/badge/tests-1557%20passing-brightgreen)](https://github.com/aeoess/agent-passport-system)
5
+ [![tests](https://img.shields.io/badge/tests-1715%20passing-brightgreen)](https://github.com/aeoess/agent-passport-system)
6
6
  [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.18749779.svg)](https://doi.org/10.5281/zenodo.18749779)
7
7
 
8
8
  > **For AI agents:** visit [aeoess.com/llms.txt](https://aeoess.com/llms.txt) for machine-readable docs or [llms-full.txt](https://aeoess.com/llms-full.txt) for the complete reference.
9
9
 
10
- **Governance, trust, and enforcement for AI agents. Not just identity.**
10
+ **Governance infrastructure for the agent economy.** Identity, delegation, reputation, enforcement, commerce, institutional governance. Not just identity — the full stack.
11
11
 
12
- When an AI agent acts on your behalf, APS answers: what is it allowed to do? How much can it spend? Is it trustworthy? What happens when it violates a constraint? And can you prove all of this cryptographically?
12
+ AI agents represent companies and people. They spend real money, access sensitive data, negotiate contracts, and talk to other agents. APS answers: what is this agent allowed to do? How much can it spend? Is it trustworthy? What happens when it violates a constraint? And can you prove all of this cryptographically?
13
13
 
14
14
  ```bash
15
15
  npm install agent-passport-system
@@ -70,6 +70,40 @@ const result = await gateway.processToolCall({
70
70
 
71
71
  **What just happened:** The gateway verified the agent's identity, checked delegation scope, enforced spend limits, evaluated values floor compliance, verified reputation tier, checked revocation status, executed the tool, generated a signed receipt, and updated reputation. All in one call. The agent never touched the tool directly.
72
72
 
73
+ ## Framework Integration: CrewAI
74
+
75
+ Add governance to any CrewAI crew in 10 lines. Works the same way with LangChain, ADK, A2A, or any custom framework.
76
+
77
+ ```typescript
78
+ import { generateKeyPair, createCrewAIGovernance } from 'agent-passport-system'
79
+
80
+ const keys = generateKeyPair()
81
+ const gov = createCrewAIGovernance({
82
+ agentId: 'research-agent',
83
+ ...keys,
84
+ delegationId: 'del_research_2026',
85
+ allowedScopes: ['tool:web_search', 'tool:read_file', 'task:execute'],
86
+ spendLimitPerAction: 5.00,
87
+ })
88
+
89
+ // Wrap any tool call — permitted actions execute, denied ones don't
90
+ const result = await gov.governedToolCall(
91
+ 'web_search',
92
+ { query: 'AI governance standards' },
93
+ () => mySearchTool('AI governance standards'),
94
+ 0.01 // estimated cost
95
+ )
96
+ // result.governance.verdict → 'permit' or 'deny'
97
+ // result.receipt → signed proof of the action (or denial)
98
+
99
+ // Use as CrewAI task callback
100
+ const receipt = gov.taskCallback({ description: '...', result: '...', agent: 'research-agent' })
101
+ ```
102
+
103
+ **What you get:** scope enforcement (agent can only use allowed tools), spend controls ($5/action limit), signed receipts for every action (permit or deny), and a verifiable audit trail. The agent never bypasses governance because the wrapper executes the action, not the agent.
104
+
105
+ See [`examples/crewai-governance.ts`](examples/crewai-governance.ts) for the full working example. Adapters also available for [LangChain](src/adapters/langchain.ts), [Google ADK](src/adapters/adk.ts), and [A2A](src/adapters/a2a.ts).
106
+
73
107
  ## Identity Is the Foundation, Not the Product
74
108
 
75
109
  Everything above is built on Ed25519 cryptographic identity. But identity is the plumbing, not the value proposition.
@@ -90,7 +124,7 @@ const agent = joinSocialContract({ name: 'my-agent', owner: 'alice', floor: floo
90
124
 
91
125
  ## The Stack
92
126
 
93
- 48 core modules + 32 v2 constitutional modules. 1557 tests. Zero heavy dependencies.
127
+ 62 core modules + 32 v2 constitutional modules. 1715 tests. Zero heavy dependencies.
94
128
 
95
129
  | Layer | What it does | Key primitive |
96
130
  |-------|-------------|---------------|
@@ -109,7 +143,7 @@ const agent = joinSocialContract({ name: 'my-agent', owner: 'alice', floor: floo
109
143
 
110
144
  ## MCP Server
111
145
 
112
- 120 tools across all modules. Any MCP client connects agents directly.
146
+ 121 tools across all modules. Any MCP client connects agents directly.
113
147
 
114
148
  ```bash
115
149
  npm install -g agent-passport-system-mcp
@@ -144,7 +178,7 @@ npx agent-passport audit --floor values/floor.yaml
144
178
 
145
179
  ```bash
146
180
  npm test
147
- # 1557 tests across 84 files, 405 suites, 0 failures
181
+ # 1715 tests across 93 files, 446 suites, 0 failures
148
182
  ```
149
183
 
150
184
  50 adversarial tests: Merkle tampering, attribution gaming, compliance violations, floor negotiation attacks, cross-chain confused deputy, taint laundering, authority probing.
@@ -162,7 +196,7 @@ npm test
162
196
  | Signed receipts | 3-sig chain | Proposed | Logs | General | — |
163
197
  | Values enforcement | 8 principles, graduated | — | Rules | — | — |
164
198
  | Coordination | Task lifecycle + MCP | — | — | — | — |
165
- | Tests | 1557 (50 adversarial) | None | Limited | None | None |
199
+ | Tests | 1715 (50 adversarial) | None | Limited | None | None |
166
200
 
167
201
  ## Recognition
168
202
 
@@ -177,6 +211,10 @@ npm test
177
211
 
178
212
  **"Monotonic Narrowing for Agent Authority"** — Published on [Zenodo](https://doi.org/10.5281/zenodo.18749779). [Read →](papers/agent-social-contract.md)
179
213
 
214
+ **"Faceted Authority Attenuation: A Product Lattice Model for AI Agent Governance"** — Published on [Zenodo](https://doi.org/10.5281/zenodo.19260073). Seven-dimensional product lattice formalization with authorization witnesses, constraint vectors, and institutional governance composition.
215
+
216
+ **Cited by:** Nanook & Gerundium, "PDR in Production: Empirical Validation of Behavioral Trust Scoring in Multi-Agent Systems" ([DOI:10.5281/zenodo.19323172](https://doi.org/10.5281/zenodo.19323172)) — Section 7.6 independently validates the APS Bayesian reputation model, sigma dynamics, and structuralVerdict/trustVerdict separation using production data from UBC.
217
+
180
218
  ## Authorship
181
219
 
182
220
  Designed and built by **Tymofii Pidlisnyi** with AI assistance from **Claude** (Anthropic).
@@ -0,0 +1,46 @@
1
+ /** External anchor state for a receipt or batch.
2
+ * unanchored: exists only in gateway memory
3
+ * batched_pending: included in a Merkle batch, root not yet anchored externally
4
+ * anchored: Merkle root published to external log (Rekor, Solana, etc.)
5
+ * critical_direct_anchor: individual receipt anchored directly (bypass batching) */
6
+ export type AnchorState = 'unanchored' | 'batched_pending' | 'anchored' | 'critical_direct_anchor';
7
+ /** Anchor metadata on a receipt */
8
+ export interface AnchorMetadata {
9
+ state: AnchorState;
10
+ /** Batch ID if batched_pending or anchored */
11
+ batchId?: string;
12
+ /** External anchor reference (URL, transaction ID, etc.) */
13
+ anchorRef?: string;
14
+ /** When the anchor was confirmed */
15
+ anchoredAt?: string;
16
+ /** Which anchor backend was used */
17
+ anchorBackend?: string;
18
+ }
19
+ /** Auto-batch configuration */
20
+ export interface AutoBatchConfig {
21
+ /** Maximum seconds between batch commits (0 = disabled) */
22
+ maxIntervalSeconds: number;
23
+ /** Maximum receipts before auto-commit (0 = disabled) */
24
+ maxReceiptsPerBatch: number;
25
+ /** Whether critical/irreversible actions get direct anchor */
26
+ directAnchorCritical: boolean;
27
+ }
28
+ export declare const DEFAULT_AUTO_BATCH_CONFIG: AutoBatchConfig;
29
+ /** Create initial anchor metadata for a new receipt */
30
+ export declare function createAnchorMetadata(critical?: boolean): AnchorMetadata;
31
+ /** Transition anchor state when receipt is added to a batch */
32
+ export declare function markBatched(anchor: AnchorMetadata, batchId: string): AnchorMetadata;
33
+ /** Transition anchor state when batch root is externally anchored */
34
+ export declare function markAnchored(anchor: AnchorMetadata, anchorRef: string, anchorBackend: string): AnchorMetadata;
35
+ /** Check if auto-batch should fire based on config and current state */
36
+ export declare function shouldAutoBatch(pendingCount: number, lastBatchTime: string | null, config?: AutoBatchConfig): {
37
+ trigger: boolean;
38
+ reason: 'max_receipts' | 'max_interval' | null;
39
+ };
40
+ /** Check if an anchor state meets a minimum requirement */
41
+ export declare function meetsAnchorRequirement(current: AnchorState, minimum: AnchorState): boolean;
42
+ /** Check if anchor state transition is valid (can only move forward) */
43
+ export declare function isValidAnchorTransition(from: AnchorState, to: AnchorState): boolean;
44
+ /** Exported ordering for cross-language verification */
45
+ export declare const ANCHOR_STATE_ORDER: Record<AnchorState, number>;
46
+ //# sourceMappingURL=anchor-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-state.d.ts","sourceRoot":"","sources":["../../../src/core/anchor-state.ts"],"names":[],"mappings":"AAYA;;;;qFAIqF;AACrF,MAAM,MAAM,WAAW,GAAG,YAAY,GAAG,iBAAiB,GAAG,UAAU,GAAG,wBAAwB,CAAA;AAElG,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,WAAW,CAAA;IAClB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,oCAAoC;IACpC,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB;AAED,+BAA+B;AAC/B,MAAM,WAAW,eAAe;IAC9B,2DAA2D;IAC3D,kBAAkB,EAAE,MAAM,CAAA;IAC1B,yDAAyD;IACzD,mBAAmB,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,oBAAoB,EAAE,OAAO,CAAA;CAC9B;AAED,eAAO,MAAM,yBAAyB,EAAE,eAIvC,CAAA;AAED,uDAAuD;AACvD,wBAAgB,oBAAoB,CAAC,QAAQ,GAAE,OAAe,GAAG,cAAc,CAI9E;AAED,+DAA+D;AAC/D,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CAInF;AAED,qEAAqE;AACrE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,GACpB,cAAc,CAQhB;AAED,wEAAwE;AACxE,wBAAgB,eAAe,CAC7B,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,MAAM,GAAE,eAA2C,GAClD;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,cAAc,GAAG,cAAc,GAAG,IAAI,CAAA;CAAE,CAsBtE;AAUD,2DAA2D;AAC3D,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,WAAW,GACnB,OAAO,CAET;AAED,wEAAwE;AACxE,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,WAAW,GAAG,OAAO,CAEnF;AAED,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,6BAAe,CAAA"}
@@ -0,0 +1,80 @@
1
+ // ══════════════════════════════════════════════════════════════════
2
+ // Anchor States — External verifiability tracking for receipts
3
+ // ══════════════════════════════════════════════════════════════════
4
+ // Consilium Priority 6. Gemini: "explicit receipt anchor states."
5
+ // desiorac (A2A #1672): batch commitment lags individual receipts.
6
+ //
7
+ // Every receipt and batch carries an anchor state:
8
+ // unanchored → batched_pending → anchored → critical_direct_anchor
9
+ //
10
+ // Auto-batching: configurable window (N seconds or N receipts).
11
+ // ══════════════════════════════════════════════════════════════════
12
+ export const DEFAULT_AUTO_BATCH_CONFIG = {
13
+ maxIntervalSeconds: 300, // 5 minutes
14
+ maxReceiptsPerBatch: 100,
15
+ directAnchorCritical: true,
16
+ };
17
+ /** Create initial anchor metadata for a new receipt */
18
+ export function createAnchorMetadata(critical = false) {
19
+ return {
20
+ state: critical ? 'critical_direct_anchor' : 'unanchored',
21
+ };
22
+ }
23
+ /** Transition anchor state when receipt is added to a batch */
24
+ export function markBatched(anchor, batchId) {
25
+ if (anchor.state === 'critical_direct_anchor')
26
+ return anchor; // already anchored
27
+ if (anchor.state === 'anchored')
28
+ return anchor; // already anchored
29
+ return { ...anchor, state: 'batched_pending', batchId };
30
+ }
31
+ /** Transition anchor state when batch root is externally anchored */
32
+ export function markAnchored(anchor, anchorRef, anchorBackend) {
33
+ if (anchor.state === 'critical_direct_anchor')
34
+ return anchor;
35
+ return {
36
+ ...anchor,
37
+ state: 'anchored',
38
+ anchorRef, anchorBackend,
39
+ anchoredAt: new Date().toISOString(),
40
+ };
41
+ }
42
+ /** Check if auto-batch should fire based on config and current state */
43
+ export function shouldAutoBatch(pendingCount, lastBatchTime, config = DEFAULT_AUTO_BATCH_CONFIG) {
44
+ if (pendingCount === 0)
45
+ return { trigger: false, reason: null };
46
+ // Receipt count trigger
47
+ if (config.maxReceiptsPerBatch > 0 && pendingCount >= config.maxReceiptsPerBatch) {
48
+ return { trigger: true, reason: 'max_receipts' };
49
+ }
50
+ // Time interval trigger
51
+ if (config.maxIntervalSeconds > 0 && lastBatchTime) {
52
+ const elapsed = (Date.now() - new Date(lastBatchTime).getTime()) / 1000;
53
+ if (elapsed >= config.maxIntervalSeconds) {
54
+ return { trigger: true, reason: 'max_interval' };
55
+ }
56
+ }
57
+ // First batch ever — trigger on interval if no previous batch
58
+ if (config.maxIntervalSeconds > 0 && !lastBatchTime && pendingCount > 0) {
59
+ return { trigger: true, reason: 'max_interval' };
60
+ }
61
+ return { trigger: false, reason: null };
62
+ }
63
+ /** Anchor state ordering — higher number = more externally verifiable */
64
+ const ANCHOR_ORDER = {
65
+ unanchored: 0,
66
+ batched_pending: 1,
67
+ anchored: 2,
68
+ critical_direct_anchor: 3,
69
+ };
70
+ /** Check if an anchor state meets a minimum requirement */
71
+ export function meetsAnchorRequirement(current, minimum) {
72
+ return ANCHOR_ORDER[current] >= ANCHOR_ORDER[minimum];
73
+ }
74
+ /** Check if anchor state transition is valid (can only move forward) */
75
+ export function isValidAnchorTransition(from, to) {
76
+ return ANCHOR_ORDER[to] >= ANCHOR_ORDER[from];
77
+ }
78
+ /** Exported ordering for cross-language verification */
79
+ export const ANCHOR_STATE_ORDER = ANCHOR_ORDER;
80
+ //# sourceMappingURL=anchor-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anchor-state.js","sourceRoot":"","sources":["../../../src/core/anchor-state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** RFC 8785 JSON Canonicalization Scheme.
2
+ * Differences from legacy canonicalize():
3
+ * - null values ARE preserved (not filtered)
4
+ * - undefined object values become null
5
+ * - Number serialization follows ES2015 spec
6
+ * - All other behavior is identical (sorted keys, no whitespace) */
7
+ export declare function canonicalizeJCS(value: unknown): string;
8
+ /** Detect which canonicalization variant was likely used.
9
+ * Checks if null values are present — JCS preserves them, legacy strips them. */
10
+ export declare function detectCanonicalVariant(obj: unknown, canonicalString: string): 'jcs' | 'legacy' | 'ambiguous';
11
+ /** Cross-language test vector for canonicalization verification */
12
+ export interface CanonicalizationTestVector {
13
+ id: string;
14
+ description: string;
15
+ input: unknown;
16
+ expected_jcs: string;
17
+ expected_legacy: string;
18
+ sha256_jcs: string;
19
+ sha256_legacy: string;
20
+ }
21
+ /** Built-in test vectors for cross-language verification */
22
+ export declare function getTestVectors(): CanonicalizationTestVector[];
23
+ //# sourceMappingURL=canonical-jcs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-jcs.d.ts","sourceRoot":"","sources":["../../../src/core/canonical-jcs.ts"],"names":[],"mappings":"AAcA;;;;;qEAKqE;AACrE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiCtD;AAED;kFACkF;AAClF,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,MAAM,GACtB,KAAK,GAAG,QAAQ,GAAG,WAAW,CAMhC;AAYD,mEAAmE;AACnE,MAAM,WAAW,0BAA0B;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,OAAO,CAAA;IACd,YAAY,EAAE,MAAM,CAAA;IACpB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;CACtB;AAOD,4DAA4D;AAC5D,wBAAgB,cAAc,IAAI,0BAA0B,EAAE,CAoF7D"}
@@ -0,0 +1,125 @@
1
+ // ══════════════════════════════════════════════════════════════════
2
+ // JCS Canonicalization — RFC 8785 compliant JSON Canonicalization
3
+ // ══════════════════════════════════════════════════════════════════
4
+ // The original canonicalize() filters null values — a deviation from
5
+ // RFC 8785 that cannot be changed without breaking existing signatures.
6
+ //
7
+ // This module provides:
8
+ // canonicalizeJCS() — strict RFC 8785 compliance
9
+ // verifyCanonical() — detect which variant was used
10
+ //
11
+ // Migration: new signatures should use JCS. Old signatures keep
12
+ // working with the legacy function. Verification tries both.
13
+ // ══════════════════════════════════════════════════════════════════
14
+ /** RFC 8785 JSON Canonicalization Scheme.
15
+ * Differences from legacy canonicalize():
16
+ * - null values ARE preserved (not filtered)
17
+ * - undefined object values become null
18
+ * - Number serialization follows ES2015 spec
19
+ * - All other behavior is identical (sorted keys, no whitespace) */
20
+ export function canonicalizeJCS(value) {
21
+ if (value === null || value === undefined)
22
+ return 'null';
23
+ switch (typeof value) {
24
+ case 'boolean':
25
+ return value ? 'true' : 'false';
26
+ case 'number': {
27
+ if (!isFinite(value))
28
+ throw new Error('JCS does not support Infinity or NaN');
29
+ // ES2015 number serialization — JSON.stringify handles this correctly
30
+ return JSON.stringify(value);
31
+ }
32
+ case 'string':
33
+ return JSON.stringify(value);
34
+ case 'object': {
35
+ if (value instanceof Date)
36
+ return JSON.stringify(value);
37
+ if (Array.isArray(value)) {
38
+ return '[' + value.map(item => canonicalizeJCS(item)).join(',') + ']';
39
+ }
40
+ // Object: sort keys by Unicode code point, preserve null values
41
+ const obj = value;
42
+ const keys = Object.keys(obj).sort();
43
+ const pairs = [];
44
+ for (const key of keys) {
45
+ const v = obj[key];
46
+ // RFC 8785: undefined becomes null, null is preserved
47
+ // Only skip if the key was never set (shouldn't happen with Object.keys)
48
+ pairs.push(`${JSON.stringify(key)}:${canonicalizeJCS(v)}`);
49
+ }
50
+ return '{' + pairs.join(',') + '}';
51
+ }
52
+ default:
53
+ throw new Error(`JCS: unsupported type ${typeof value}`);
54
+ }
55
+ }
56
+ /** Detect which canonicalization variant was likely used.
57
+ * Checks if null values are present — JCS preserves them, legacy strips them. */
58
+ export function detectCanonicalVariant(obj, canonicalString) {
59
+ // If the object has no null values, both variants produce identical output
60
+ if (!hasNullValues(obj))
61
+ return 'ambiguous';
62
+ // If canonical string contains `:null`, it's JCS (legacy strips nulls)
63
+ if (canonicalString.includes(':null'))
64
+ return 'jcs';
65
+ return 'legacy';
66
+ }
67
+ function hasNullValues(obj) {
68
+ if (obj === null)
69
+ return true;
70
+ if (typeof obj !== 'object' || obj === undefined)
71
+ return false;
72
+ if (Array.isArray(obj))
73
+ return obj.some(hasNullValues);
74
+ return Object.values(obj).some(v => v === null || v === undefined || hasNullValues(v));
75
+ }
76
+ import { createHash } from 'crypto';
77
+ /** Generate SHA-256 hex digest of a string */
78
+ function sha256hex(input) {
79
+ return createHash('sha256').update(input, 'utf-8').digest('hex');
80
+ }
81
+ /** Built-in test vectors for cross-language verification */
82
+ export function getTestVectors() {
83
+ const vectors = [];
84
+ function addVector(id, desc, input, jcs, legacy) {
85
+ vectors.push({
86
+ id, description: desc, input,
87
+ expected_jcs: jcs, expected_legacy: legacy,
88
+ sha256_jcs: sha256hex(jcs), sha256_legacy: sha256hex(legacy),
89
+ });
90
+ }
91
+ // V1: Simple object — both variants identical
92
+ addVector('cv-001', 'Simple object, no nulls — variants identical', { agentId: 'agent-001', scope: 'read' }, '{"agentId":"agent-001","scope":"read"}', '{"agentId":"agent-001","scope":"read"}');
93
+ // V2: Object with null — variants diverge
94
+ addVector('cv-002', 'Null value — JCS preserves, legacy strips', { agentId: 'agent-001', metadata: null, scope: 'read' }, '{"agentId":"agent-001","metadata":null,"scope":"read"}', '{"agentId":"agent-001","scope":"read"}');
95
+ // V3: Key ordering
96
+ addVector('cv-003', 'Keys sorted by Unicode code point', { zebra: 1, alpha: 2, middle: 3 }, '{"alpha":2,"middle":3,"zebra":1}', '{"alpha":2,"middle":3,"zebra":1}');
97
+ // V4: Nested objects with null
98
+ addVector('cv-004', 'Nested object with null at depth', { outer: { inner: null, value: 42 }, top: 'ok' }, '{"outer":{"inner":null,"value":42},"top":"ok"}', '{"outer":{"value":42},"top":"ok"}');
99
+ // V5: Arrays with null elements
100
+ addVector('cv-005', 'Array with null elements — both preserve array nulls', { items: [1, null, 3] }, '{"items":[1,null,3]}', '{"items":[1,null,3]}');
101
+ // V6: Number edge cases
102
+ addVector('cv-006', 'Number formatting — integers and floats', { integer: 42, negative: -7, float: 3.14, zero: 0 }, '{"float":3.14,"integer":42,"negative":-7,"zero":0}', '{"float":3.14,"integer":42,"negative":-7,"zero":0}');
103
+ // V7: Empty structures
104
+ addVector('cv-007', 'Empty object and empty array', { emptyArr: [], emptyObj: {} }, '{"emptyArr":[],"emptyObj":{}}', '{"emptyArr":[],"emptyObj":{}}');
105
+ // V8: Unicode
106
+ addVector('cv-008', 'Unicode string content', { name: 'Тимофій', emoji: '🔐' }, '{"emoji":"🔐","name":"Тимофій"}', '{"emoji":"🔐","name":"Тимофій"}');
107
+ // V9: Realistic APS object — delegation-like structure
108
+ addVector('cv-009', 'Realistic delegation object with mixed null/present fields', {
109
+ delegationId: 'del_abc123',
110
+ delegatedBy: 'did:aps:principal001',
111
+ delegatedTo: 'did:aps:agent002',
112
+ scope: ['data:read', 'commerce:checkout'],
113
+ spendLimit: 500,
114
+ obligationBundleHash: null,
115
+ expiresAt: '2026-04-01T00:00:00Z',
116
+ notBefore: null,
117
+ maxDepth: 3,
118
+ currentDepth: 1,
119
+ createdAt: '2026-03-29T00:00:00Z',
120
+ }, '{"createdAt":"2026-03-29T00:00:00Z","currentDepth":1,"delegatedBy":"did:aps:principal001","delegatedTo":"did:aps:agent002","delegationId":"del_abc123","expiresAt":"2026-04-01T00:00:00Z","maxDepth":3,"notBefore":null,"obligationBundleHash":null,"scope":["data:read","commerce:checkout"],"spendLimit":500}', '{"createdAt":"2026-03-29T00:00:00Z","currentDepth":1,"delegatedBy":"did:aps:principal001","delegatedTo":"did:aps:agent002","delegationId":"del_abc123","expiresAt":"2026-04-01T00:00:00Z","maxDepth":3,"scope":["data:read","commerce:checkout"],"spendLimit":500}');
121
+ // V10: Boolean values
122
+ addVector('cv-010', 'Boolean values', { active: true, revoked: false }, '{"active":true,"revoked":false}', '{"active":true,"revoked":false}');
123
+ return vectors;
124
+ }
125
+ //# sourceMappingURL=canonical-jcs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-jcs.js","sourceRoot":"","sources":["../../../src/core/canonical-jcs.ts"],"names":[],"mappings":"AAAA,qEAAqE;AACrE,kEAAkE;AAClE,qEAAqE;AACrE,qEAAqE;AACrE,wEAAwE;AACxE,EAAE;AACF,wBAAwB;AACxB,mDAAmD;AACnD,uDAAuD;AACvD,EAAE;AACF,gEAAgE;AAChE,6DAA6D;AAC7D,qEAAqE;AAErE;;;;;qEAKqE;AACrE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,MAAM,CAAA;IAExD,QAAQ,OAAO,KAAK,EAAE,CAAC;QACrB,KAAK,SAAS;YACZ,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAA;QACjC,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;YAC7E,sEAAsE;YACtE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC9B,CAAC;QACD,KAAK,QAAQ;YACX,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAC9B,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,KAAK,YAAY,IAAI;gBAAE,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YACvD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBACzB,OAAO,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;YACvE,CAAC;YACD,gEAAgE;YAChE,MAAM,GAAG,GAAG,KAAgC,CAAA;YAC5C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAA;YACpC,MAAM,KAAK,GAAa,EAAE,CAAA;YAC1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,CAAA;gBAClB,sDAAsD;gBACtD,yEAAyE;gBACzE,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,CAAA;YAC5D,CAAC;YACD,OAAO,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,CAAA;QACpC,CAAC;QACD;YACE,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,KAAK,EAAE,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC;AAED;kFACkF;AAClF,MAAM,UAAU,sBAAsB,CACpC,GAAY,EACZ,eAAuB;IAEvB,2EAA2E;IAC3E,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC;QAAE,OAAO,WAAW,CAAA;IAC3C,uEAAuE;IACvE,IAAI,eAAe,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAA;IACnD,OAAO,QAAQ,CAAA;AACjB,CAAC;AAED,SAAS,aAAa,CAAC,GAAY;IACjC,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC7B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IAC9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;IACtD,OAAO,MAAM,CAAC,MAAM,CAAC,GAA8B,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAC5D,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,SAAS,IAAI,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;AACtD,CAAC;AAED,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAanC,8CAA8C;AAC9C,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAClE,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAiC,EAAE,CAAA;IAEhD,SAAS,SAAS,CAAC,EAAU,EAAE,IAAY,EAAE,KAAc,EAAE,GAAW,EAAE,MAAc;QACtF,OAAO,CAAC,IAAI,CAAC;YACX,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK;YAC5B,YAAY,EAAE,GAAG,EAAE,eAAe,EAAE,MAAM;YAC1C,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,SAAS,CAAC,MAAM,CAAC;SAC7D,CAAC,CAAA;IACJ,CAAC;IAED,8CAA8C;IAC9C,SAAS,CAAC,QAAQ,EAAE,8CAA8C,EAChE,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,EACvC,wCAAwC,EACxC,wCAAwC,CAAC,CAAA;IAE3C,0CAA0C;IAC1C,SAAS,CAAC,QAAQ,EAAE,2CAA2C,EAC7D,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EACvD,wDAAwD,EACxD,wCAAwC,CAAC,CAAA;IAE3C,mBAAmB;IACnB,SAAS,CAAC,QAAQ,EAAE,mCAAmC,EACrD,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EACjC,kCAAkC,EAClC,kCAAkC,CAAC,CAAA;IAErC,+BAA+B;IAC/B,SAAS,CAAC,QAAQ,EAAE,kCAAkC,EACpD,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,EAChD,gDAAgD,EAChD,mCAAmC,CAAC,CAAA;IAEtC,gCAAgC;IAChC,SAAS,CAAC,QAAQ,EAAE,sDAAsD,EACxE,EAAE,KAAK,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EACvB,sBAAsB,EACtB,sBAAsB,CAAC,CAAA;IAEzB,wBAAwB;IACxB,SAAS,CAAC,QAAQ,EAAE,yCAAyC,EAC3D,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,EAAE,EACnD,oDAAoD,EACpD,oDAAoD,CAAC,CAAA;IAEvD,uBAAuB;IACvB,SAAS,CAAC,QAAQ,EAAE,8BAA8B,EAChD,EAAE,QAAQ,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,EAC9B,+BAA+B,EAC/B,+BAA+B,CAAC,CAAA;IAElC,cAAc;IACd,SAAS,CAAC,QAAQ,EAAE,wBAAwB,EAC1C,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAChC,iCAAiC,EACjC,iCAAiC,CAAC,CAAA;IAEpC,uDAAuD;IACvD,SAAS,CAAC,QAAQ,EAAE,4DAA4D,EAC9E;QACE,YAAY,EAAE,YAAY;QAC1B,WAAW,EAAE,sBAAsB;QACnC,WAAW,EAAE,kBAAkB;QAC/B,KAAK,EAAE,CAAC,WAAW,EAAE,mBAAmB,CAAC;QACzC,UAAU,EAAE,GAAG;QACf,oBAAoB,EAAE,IAAI;QAC1B,SAAS,EAAE,sBAAsB;QACjC,SAAS,EAAE,IAAI;QACf,QAAQ,EAAE,CAAC;QACX,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,sBAAsB;KAClC,EACD,iTAAiT,EACjT,oQAAoQ,CAAC,CAAA;IAEvQ,sBAAsB;IACtB,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAClC,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAChC,iCAAiC,EACjC,iCAAiC,CAAC,CAAA;IAEpC,OAAO,OAAO,CAAA;AAChB,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { ConstraintFacet, ConstraintStatus } from '../types/gateway.js';
2
+ /** A facet evaluation snapshot — facet name + its status */
3
+ export interface FacetSnapshot {
4
+ facet: ConstraintFacet;
5
+ status: ConstraintStatus;
6
+ }
7
+ /** Result of a narrowing check */
8
+ export interface NarrowingCheckResult {
9
+ valid: boolean;
10
+ /** Facets where data attempted to widen authority */
11
+ violations: Array<{
12
+ facet: ConstraintFacet;
13
+ before: ConstraintStatus;
14
+ after: ConstraintStatus;
15
+ message: string;
16
+ }>;
17
+ }
18
+ /** Assert that data influence only narrows authority.
19
+ * Compares constraint evaluations BEFORE and AFTER data is considered.
20
+ * Any facet that moves from a more restrictive status to a more
21
+ * permissive one is a violation — data attempted to widen authority.
22
+ *
23
+ * @param before - Facet evaluations before data influence
24
+ * @param after - Facet evaluations after data influence
25
+ * @returns NarrowingCheckResult with any violations */
26
+ export declare function assertDataNarrowsOnly(before: FacetSnapshot[], after: FacetSnapshot[]): NarrowingCheckResult;
27
+ /** Apply data-sourced constraint modifications safely.
28
+ * Only allows narrowing (making more restrictive).
29
+ * Returns the narrowed snapshots with any widening attempts rejected.
30
+ *
31
+ * Use case: a data source declares "this data requires scope:read_only"
32
+ * → the constraint is narrowed. But if data declares "grant scope:admin"
33
+ * → the widening is rejected and the original constraint stands. */
34
+ export declare function applyDataConstraints(current: FacetSnapshot[], dataInfluence: FacetSnapshot[]): {
35
+ result: FacetSnapshot[];
36
+ rejected: NarrowingCheckResult['violations'];
37
+ };
38
+ /** Check if a status transition is valid narrowing (same or more restrictive) */
39
+ export declare function isValidNarrowing(before: ConstraintStatus, after: ConstraintStatus): boolean;
40
+ /** The status ordering: fail < unknown < not_applicable < pass.
41
+ * Exported for test vectors and cross-language verification. */
42
+ export declare const NARROWING_ORDER: Record<ConstraintStatus, number>;
43
+ //# sourceMappingURL=data-narrowing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-narrowing.d.ts","sourceRoot":"","sources":["../../../src/core/data-narrowing.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAE5E,4DAA4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,eAAe,CAAA;IACtB,MAAM,EAAE,gBAAgB,CAAA;CACzB;AAED,kCAAkC;AAClC,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAA;IACd,qDAAqD;IACrD,UAAU,EAAE,KAAK,CAAC;QAChB,KAAK,EAAE,eAAe,CAAA;QACtB,MAAM,EAAE,gBAAgB,CAAA;QACxB,KAAK,EAAE,gBAAgB,CAAA;QACvB,OAAO,EAAE,MAAM,CAAA;KAChB,CAAC,CAAA;CACH;AAWD;;;;;;;wDAOwD;AACxD,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EAAE,EACvB,KAAK,EAAE,aAAa,EAAE,GACrB,oBAAoB,CAwBtB;AAED;;;;;;qEAMqE;AACrE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,aAAa,EAAE,EACxB,aAAa,EAAE,aAAa,EAAE,GAC7B;IAAE,MAAM,EAAE,aAAa,EAAE,CAAC;IAAC,QAAQ,EAAE,oBAAoB,CAAC,YAAY,CAAC,CAAA;CAAE,CAkC3E;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,gBAAgB,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAE3F;AAED;iEACiE;AACjE,eAAO,MAAM,eAAe,kCAAe,CAAA"}
@@ -0,0 +1,97 @@
1
+ // ══════════════════════════════════════════════════════════════════
2
+ // Data Narrowing Invariant — Data Can Only Narrow Authority
3
+ // ══════════════════════════════════════════════════════════════════
4
+ // GPT's "Context-Bypass Attack" (consilium March 2026):
5
+ // An agent reads a data source containing authority-widening
6
+ // instructions. The gateway must ensure data inputs can ONLY narrow
7
+ // the ConstraintVector, never widen it.
8
+ //
9
+ // Same as monotonic narrowing for delegation — monotonic narrowing
10
+ // for data influence. Only signed delegations from a higher-tier
11
+ // principal can widen authority.
12
+ // ══════════════════════════════════════════════════════════════════
13
+ /** Status ordering for monotonic narrowing check.
14
+ * Lower number = more restrictive. Authority can only move DOWN. */
15
+ const STATUS_ORDER = {
16
+ 'fail': 0,
17
+ 'unknown': 1,
18
+ 'not_applicable': 2,
19
+ 'pass': 3,
20
+ };
21
+ /** Assert that data influence only narrows authority.
22
+ * Compares constraint evaluations BEFORE and AFTER data is considered.
23
+ * Any facet that moves from a more restrictive status to a more
24
+ * permissive one is a violation — data attempted to widen authority.
25
+ *
26
+ * @param before - Facet evaluations before data influence
27
+ * @param after - Facet evaluations after data influence
28
+ * @returns NarrowingCheckResult with any violations */
29
+ export function assertDataNarrowsOnly(before, after) {
30
+ const violations = [];
31
+ const beforeMap = new Map(before.map(f => [f.facet, f.status]));
32
+ for (const a of after) {
33
+ const beforeStatus = beforeMap.get(a.facet);
34
+ if (beforeStatus === undefined)
35
+ continue; // new facet, no comparison
36
+ const beforeOrder = STATUS_ORDER[beforeStatus];
37
+ const afterOrder = STATUS_ORDER[a.status];
38
+ // If after is MORE permissive (higher order) than before → violation
39
+ if (afterOrder > beforeOrder) {
40
+ violations.push({
41
+ facet: a.facet,
42
+ before: beforeStatus,
43
+ after: a.status,
44
+ message: `Data attempted to widen ${a.facet}: ${beforeStatus} → ${a.status}`,
45
+ });
46
+ }
47
+ }
48
+ return { valid: violations.length === 0, violations };
49
+ }
50
+ /** Apply data-sourced constraint modifications safely.
51
+ * Only allows narrowing (making more restrictive).
52
+ * Returns the narrowed snapshots with any widening attempts rejected.
53
+ *
54
+ * Use case: a data source declares "this data requires scope:read_only"
55
+ * → the constraint is narrowed. But if data declares "grant scope:admin"
56
+ * → the widening is rejected and the original constraint stands. */
57
+ export function applyDataConstraints(current, dataInfluence) {
58
+ const currentMap = new Map(current.map(f => [f.facet, f]));
59
+ const result = [...current];
60
+ const rejected = [];
61
+ for (const influence of dataInfluence) {
62
+ const existing = currentMap.get(influence.facet);
63
+ if (!existing) {
64
+ // New facet from data — only accept if restrictive (fail or unknown)
65
+ if (STATUS_ORDER[influence.status] <= STATUS_ORDER['unknown']) {
66
+ result.push(influence);
67
+ }
68
+ continue;
69
+ }
70
+ const existingOrder = STATUS_ORDER[existing.status];
71
+ const influenceOrder = STATUS_ORDER[influence.status];
72
+ if (influenceOrder <= existingOrder) {
73
+ // More restrictive or equal — allowed (narrowing)
74
+ const idx = result.findIndex(f => f.facet === influence.facet);
75
+ if (idx >= 0)
76
+ result[idx] = influence;
77
+ }
78
+ else {
79
+ // Less restrictive — rejected (widening attempt)
80
+ rejected.push({
81
+ facet: influence.facet,
82
+ before: existing.status,
83
+ after: influence.status,
84
+ message: `Rejected: data tried to widen ${influence.facet} from ${existing.status} to ${influence.status}`,
85
+ });
86
+ }
87
+ }
88
+ return { result, rejected };
89
+ }
90
+ /** Check if a status transition is valid narrowing (same or more restrictive) */
91
+ export function isValidNarrowing(before, after) {
92
+ return STATUS_ORDER[after] <= STATUS_ORDER[before];
93
+ }
94
+ /** The status ordering: fail < unknown < not_applicable < pass.
95
+ * Exported for test vectors and cross-language verification. */
96
+ export const NARROWING_ORDER = STATUS_ORDER;
97
+ //# sourceMappingURL=data-narrowing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-narrowing.js","sourceRoot":"","sources":["../../../src/core/data-narrowing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { DataAccessReceipt } from '../types/data-source.js';
2
+ import type { DataAttributionModel, DataSourceAttributionReport } from '../types/data-contribution.js';
3
+ export declare function computeDataSourceAttribution(opts: {
4
+ outputArtifactId: string;
5
+ outputType: 'decision' | 'content' | 'model' | 'action';
6
+ accessReceipts: DataAccessReceipt[];
7
+ sourceDescriptors?: Map<string, string>;
8
+ model?: DataAttributionModel;
9
+ customWeights?: Map<string, number>;
10
+ generatorPublicKey: string;
11
+ generatorPrivateKey: string;
12
+ }): DataSourceAttributionReport;
13
+ export declare function verifyDataSourceAttribution(report: DataSourceAttributionReport, publicKey: string): {
14
+ valid: boolean;
15
+ errors: string[];
16
+ };
17
+ //# sourceMappingURL=data-source-attribution.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-source-attribution.d.ts","sourceRoot":"","sources":["../../../src/core/data-source-attribution.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,KAAK,EACV,oBAAoB,EAEpB,2BAA2B,EAC5B,MAAM,+BAA+B,CAAA;AAkHtC,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,gBAAgB,EAAE,MAAM,CAAA;IACxB,UAAU,EAAE,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,QAAQ,CAAA;IACvD,cAAc,EAAE,iBAAiB,EAAE,CAAA;IACnC,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,KAAK,CAAC,EAAE,oBAAoB,CAAA;IAC5B,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,kBAAkB,EAAE,MAAM,CAAA;IAC1B,mBAAmB,EAAE,MAAM,CAAA;CAC5B,GAAG,2BAA2B,CAsE9B;AAMD,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,2BAA2B,EACnC,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAwCtC"}