@atrib/agent 0.1.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 (39) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +333 -0
  3. package/dist/adapters/cloudflare-agent.d.ts +46 -0
  4. package/dist/adapters/cloudflare-agent.d.ts.map +1 -0
  5. package/dist/adapters/cloudflare-agent.js +124 -0
  6. package/dist/adapters/cloudflare-agent.js.map +1 -0
  7. package/dist/adapters/langchain-mcp.d.ts +172 -0
  8. package/dist/adapters/langchain-mcp.d.ts.map +1 -0
  9. package/dist/adapters/langchain-mcp.js +145 -0
  10. package/dist/adapters/langchain-mcp.js.map +1 -0
  11. package/dist/adapters/mcp-client.d.ts +94 -0
  12. package/dist/adapters/mcp-client.d.ts.map +1 -0
  13. package/dist/adapters/mcp-client.js +91 -0
  14. package/dist/adapters/mcp-client.js.map +1 -0
  15. package/dist/adapters/vercel-ai-sdk-mcp.d.ts +129 -0
  16. package/dist/adapters/vercel-ai-sdk-mcp.d.ts.map +1 -0
  17. package/dist/adapters/vercel-ai-sdk-mcp.js +115 -0
  18. package/dist/adapters/vercel-ai-sdk-mcp.js.map +1 -0
  19. package/dist/index.d.ts +18 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +27 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/middleware.d.ts +53 -0
  24. package/dist/middleware.d.ts.map +1 -0
  25. package/dist/middleware.js +268 -0
  26. package/dist/middleware.js.map +1 -0
  27. package/dist/policy.d.ts +28 -0
  28. package/dist/policy.d.ts.map +1 -0
  29. package/dist/policy.js +196 -0
  30. package/dist/policy.js.map +1 -0
  31. package/dist/session.d.ts +50 -0
  32. package/dist/session.d.ts.map +1 -0
  33. package/dist/session.js +141 -0
  34. package/dist/session.js.map +1 -0
  35. package/dist/transaction.d.ts +52 -0
  36. package/dist/transaction.d.ts.map +1 -0
  37. package/dist/transaction.js +123 -0
  38. package/dist/transaction.js.map +1 -0
  39. package/package.json +50 -0
@@ -0,0 +1,28 @@
1
+ import type { CreatorPolicyEntry, SessionPolicyRecord } from '@atrib/verify';
2
+ export type { CreatorPolicyEntry, SessionPolicyRecord };
3
+ /** A fetched policy document shape for the fetch/parse layer (§4.2). */
4
+ export interface PolicyDocument {
5
+ spec_version: string;
6
+ edge_weights?: Record<string, number>;
7
+ modifiers?: unknown[];
8
+ constraints?: {
9
+ minimum_share?: number;
10
+ maximum_share?: number;
11
+ minimum_own_share?: number;
12
+ maximum_total_share?: number;
13
+ };
14
+ [key: string]: unknown;
15
+ }
16
+ /** Options for session initialization. */
17
+ export interface PolicyInitOptions {
18
+ contextId: string;
19
+ merchantDomain?: string | undefined;
20
+ /** Server URLs for all tools the agent intends to call. */
21
+ serverUrls?: string[] | undefined;
22
+ }
23
+ /**
24
+ * Run session initialization: fetch policies and negotiate (§5.4.2).
25
+ * Must complete within 3 seconds.
26
+ */
27
+ export declare function initializeSessionPolicy(options: PolicyInitOptions): Promise<SessionPolicyRecord>;
28
+ //# sourceMappingURL=policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.d.ts","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAI5E,YAAY,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,CAAA;AAMvD,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAA;IACrB,WAAW,CAAC,EAAE;QACZ,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAC7B,CAAA;IACD,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACvB;AAgDD,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAA;CAClC;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,mBAAmB,CAAC,CA4D9B"}
package/dist/policy.js ADDED
@@ -0,0 +1,196 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Policy negotiation and session policy record (§4.5, §5.4.2, §5.4.6).
4
+ */
5
+ import { sha256, hexEncode } from '@atrib/mcp';
6
+ import canonicalize from 'canonicalize';
7
+ const POLICY_FETCH_TIMEOUT_MS = 1000;
8
+ const INIT_BUDGET_MS = 3000;
9
+ const POLICY_PATH = '/.well-known/atrib-policy.json';
10
+ /**
11
+ * Fetch a policy document from a URL with timeout.
12
+ * Returns null on any error (404, timeout, parse error).
13
+ */
14
+ async function fetchPolicy(url) {
15
+ try {
16
+ // Validate URL scheme to prevent SSRF via crafted merchantDomain/serverUrls
17
+ const parsed = new URL(url);
18
+ if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:')
19
+ return null;
20
+ const controller = new AbortController();
21
+ const timeoutId = setTimeout(() => controller.abort(), POLICY_FETCH_TIMEOUT_MS);
22
+ let response;
23
+ try {
24
+ response = await fetch(url, { signal: controller.signal });
25
+ }
26
+ finally {
27
+ clearTimeout(timeoutId);
28
+ }
29
+ if (!response.ok)
30
+ return null;
31
+ const doc = (await response.json());
32
+ // §4.5.2 Rule 6: reject contradictory constraints
33
+ if (doc.constraints) {
34
+ const { minimum_share, maximum_share } = doc.constraints;
35
+ if (minimum_share !== undefined &&
36
+ maximum_share !== undefined &&
37
+ minimum_share > maximum_share) {
38
+ return null;
39
+ }
40
+ // Reject negative values
41
+ for (const val of Object.values(doc.constraints)) {
42
+ if (typeof val === 'number' && val < 0)
43
+ return null;
44
+ }
45
+ }
46
+ return doc;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Run session initialization: fetch policies and negotiate (§5.4.2).
54
+ * Must complete within 3 seconds.
55
+ */
56
+ export async function initializeSessionPolicy(options) {
57
+ const warnings = [];
58
+ const startTime = Date.now();
59
+ // Step 1: Fetch merchant policy
60
+ let merchantPolicyDoc = null;
61
+ let merchantPolicyUrl = 'default';
62
+ if (options.merchantDomain) {
63
+ merchantPolicyUrl = `${options.merchantDomain}${POLICY_PATH}`;
64
+ merchantPolicyDoc = await fetchPolicy(merchantPolicyUrl);
65
+ if (!merchantPolicyDoc) {
66
+ merchantPolicyUrl = 'default';
67
+ warnings.push(`merchant policy not found at ${options.merchantDomain}${POLICY_PATH}`);
68
+ }
69
+ }
70
+ // Step 2: Fetch creator policies concurrently (§5.4.2 step 4)
71
+ const creatorEntries = [];
72
+ if (options.serverUrls && options.serverUrls.length > 0) {
73
+ const fetches = options.serverUrls.map(async (serverUrl) => {
74
+ const policyUrl = `${serverUrl}${POLICY_PATH}`;
75
+ const doc = await fetchPolicy(policyUrl);
76
+ return { serverUrl, policyUrl, doc };
77
+ });
78
+ const results = await Promise.allSettled(fetches);
79
+ for (const result of results) {
80
+ if (result.status === 'fulfilled') {
81
+ const { serverUrl, policyUrl, doc } = result.value;
82
+ creatorEntries.push({
83
+ server_url: serverUrl,
84
+ policy_url: policyUrl,
85
+ status: doc ? 'compatible' : 'not_found',
86
+ ...(doc ? { policy: doc } : {}),
87
+ });
88
+ }
89
+ }
90
+ }
91
+ // Check init budget
92
+ if (Date.now() - startTime > INIT_BUDGET_MS) {
93
+ warnings.push('negotiation_skipped: session initialization exceeded 3-second budget');
94
+ return buildSessionPolicyRecord(options.contextId, 'default', creatorEntries, {}, warnings);
95
+ }
96
+ // Step 3: Conflict resolution (§4.5.2)
97
+ const { agreedPolicy, minimumFloors, negotiationWarnings } = negotiatePolicies(merchantPolicyDoc, merchantPolicyUrl, creatorEntries);
98
+ warnings.push(...negotiationWarnings);
99
+ return buildSessionPolicyRecord(options.contextId, agreedPolicy, creatorEntries, minimumFloors, warnings);
100
+ }
101
+ /** Run the 7-rule conflict resolution (§4.5.2). */
102
+ function negotiatePolicies(merchantPolicy, merchantPolicyUrl, creatorEntries) {
103
+ const warnings = [];
104
+ const minimumFloors = {};
105
+ const merchantCap = merchantPolicy?.constraints?.maximum_total_share;
106
+ // Collect creator floors
107
+ let floorSum = 0;
108
+ for (const entry of creatorEntries) {
109
+ if (entry.policy?.constraints?.minimum_own_share !== undefined) {
110
+ const floor = entry.policy.constraints.minimum_own_share;
111
+ minimumFloors[entry.server_url] = floor;
112
+ floorSum += floor;
113
+ }
114
+ }
115
+ // Rule 5: Creator floors summing to >1.0 are irreconcilable
116
+ if (floorSum > 1.0) {
117
+ warnings.push(`creator minimum floors sum to ${floorSum} (>1.0), falling back to default policy. ` +
118
+ `Contributors: ${Object.keys(minimumFloors).join(', ')}`);
119
+ for (const entry of creatorEntries) {
120
+ if (entry.status === 'compatible' && entry.policy) {
121
+ entry.status = 'conflict_defaulted';
122
+ }
123
+ }
124
+ return { agreedPolicy: 'default', minimumFloors: {}, negotiationWarnings: warnings };
125
+ }
126
+ // Rule 3: Check if single creator floor exceeds merchant cap (BEFORE scaling)
127
+ // Must check before Rule 2 scaling. a single floor > cap is irreconcilable
128
+ if (merchantCap !== undefined) {
129
+ for (const entry of creatorEntries) {
130
+ const floor = entry.policy?.constraints?.minimum_own_share;
131
+ if (floor !== undefined && floor > merchantCap) {
132
+ warnings.push(`creator ${entry.server_url} minimum floor ${floor} exceeds merchant cap ${merchantCap}, ` +
133
+ `falling back to default`);
134
+ // Only mark creators whose floor actually exceeds the cap as
135
+ // conflict_defaulted. creators with floors that fit are not at fault.
136
+ for (const e of creatorEntries) {
137
+ const eFloor = e.policy?.constraints?.minimum_own_share;
138
+ if (e.status !== 'not_found' && eFloor !== undefined && eFloor > merchantCap) {
139
+ e.status = 'conflict_defaulted';
140
+ }
141
+ }
142
+ return { agreedPolicy: 'default', minimumFloors: {}, negotiationWarnings: warnings };
143
+ }
144
+ }
145
+ }
146
+ // Rule 1 & 2: Merchant cap + creator floor scaling
147
+ // Multiple floors that individually fit the cap but sum > cap get scaled down proportionally
148
+ if (merchantCap !== undefined && floorSum > merchantCap) {
149
+ const scaleFactor = merchantCap / floorSum;
150
+ for (const [url, floor] of Object.entries(minimumFloors)) {
151
+ minimumFloors[url] = floor * scaleFactor;
152
+ }
153
+ warnings.push(`creator minimum floors scaled by ${scaleFactor.toFixed(4)} to fit merchant cap of ${merchantCap}`);
154
+ for (const entry of creatorEntries) {
155
+ if (entry.status === 'compatible' && minimumFloors[entry.server_url] !== undefined) {
156
+ entry.status = 'floor_scaled';
157
+ }
158
+ }
159
+ }
160
+ // Rule 4: Edge weight disagreements. merchant governs (advisory only for creators)
161
+ const agreedPolicy = merchantPolicyUrl !== 'default' ? merchantPolicyUrl : 'default';
162
+ return { agreedPolicy, minimumFloors, negotiationWarnings: warnings };
163
+ }
164
+ /** Build the session policy record (§4.5.3). */
165
+ function buildSessionPolicyRecord(contextId, agreedPolicy, creatorEntries, minimumFloors, warnings) {
166
+ // Build the record without record_id first, then compute it
167
+ const record = {
168
+ spec_version: 'atrib/1.0',
169
+ record_id: '', // placeholder
170
+ context_id: contextId,
171
+ created_at: Date.now(),
172
+ merchant_policy: agreedPolicy === 'default' ? 'default' : agreedPolicy,
173
+ creator_policies: creatorEntries.map(({ server_url, policy_url, status }) => ({
174
+ server_url,
175
+ policy_url,
176
+ status,
177
+ })),
178
+ agreed_policy: agreedPolicy,
179
+ applied_constraints: {
180
+ minimum_floors: minimumFloors,
181
+ },
182
+ warnings,
183
+ };
184
+ // Compute record_id = sha256 of JCS canonical form, excluding record_id
185
+ // and warnings (warnings are mutable. runtime warnings are appended after
186
+ // negotiation, so including them would make record_id stale).
187
+ const { record_id: _, warnings: _w, ...forHashing } = record;
188
+ const canonical = canonicalize(forHashing);
189
+ if (canonical) {
190
+ const encoder = new TextEncoder();
191
+ const hash = sha256(encoder.encode(canonical));
192
+ record.record_id = `sha256:${hexEncode(hash)}`;
193
+ }
194
+ return record;
195
+ }
196
+ //# sourceMappingURL=policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { GapNode } from '@atrib/verify';
2
+ /** The latest attribution context from the most recent tool response. */
3
+ export interface LatestContext {
4
+ recordHash: Uint8Array;
5
+ creatorKey: Uint8Array;
6
+ }
7
+ /** Mutable session state, evolved by each tool call/response. */
8
+ export interface SessionState {
9
+ contextId: string;
10
+ sessionToken: string;
11
+ latestContext: LatestContext | null;
12
+ initialized: boolean;
13
+ /** The record_id from the session policy record, set after init. */
14
+ policyRecordId: string | null;
15
+ /** Task IDs from tasks/create responses (§5.7). */
16
+ taskIds: Set<string>;
17
+ /** Unsigned hops recorded during this session (§1.6). */
18
+ gapNodes: GapNode[];
19
+ warnings: string[];
20
+ }
21
+ /** Options for creating a session. */
22
+ export interface SessionOptions {
23
+ /** Session token for cross-trace linking. Auto-generated if absent. */
24
+ sessionToken?: string | undefined;
25
+ }
26
+ /**
27
+ * Create a new session state.
28
+ */
29
+ export declare function createSession(options: SessionOptions): SessionState;
30
+ /**
31
+ * Build the outbound _meta for a tools/call request (§5.4.3).
32
+ * Attaches attribution context, traceparent, session_token, and policy record ID.
33
+ */
34
+ /**
35
+ * Build the outbound _meta for a tools/call request (§5.4.3).
36
+ * Attaches attribution context, traceparent, session_token, and policy record ID.
37
+ *
38
+ * Merges with the caller's existing _meta:
39
+ * - `baggage` and `tracestate` are APPENDED (W3C semantics, multi-vendor safe)
40
+ * - `traceparent` is overwritten only if the caller has not supplied one
41
+ * - `atrib` and `X-Atrib-Chain` are set (atrib-owned keys)
42
+ */
43
+ export declare function buildOutboundMeta(session: SessionState, existing?: Record<string, unknown>): Record<string, string>;
44
+ /**
45
+ * Read inbound attribution context from a tool response (§5.4.4).
46
+ * Updates session state with the latest context.
47
+ * Returns true if an attribution token was found.
48
+ */
49
+ export declare function accumulateInboundContext(session: SessionState, responseMeta: Record<string, unknown> | undefined): boolean;
50
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAE5C,yEAAyE;AACzE,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,UAAU,CAAA;CACvB;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,aAAa,GAAG,IAAI,CAAA;IACnC,WAAW,EAAE,OAAO,CAAA;IACpB,oEAAoE;IACpE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,mDAAmD;IACnD,OAAO,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACpB,yDAAyD;IACzD,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED,sCAAsC;AACtC,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAClC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,YAAY,CA0BnE;AAED;;;GAGG;AACH;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GACrC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmDxB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,YAAY,EACrB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GAChD,OAAO,CAwCT"}
@@ -0,0 +1,141 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Agent session state management (§5.4).
4
+ *
5
+ * Tracks the latest attribution context across sequential tool calls
6
+ * within a session. The session is initialized lazily on the first
7
+ * outbound tool call.
8
+ */
9
+ import { base64urlEncode, hexEncode, decodeToken, mergeTracestate, mergeBaggageAtribSession, } from '@atrib/mcp';
10
+ /**
11
+ * Create a new session state.
12
+ */
13
+ export function createSession(options) {
14
+ // Generate context_id (random 16-byte hex if no OTel trace available)
15
+ const contextIdBytes = new Uint8Array(16);
16
+ crypto.getRandomValues(contextIdBytes);
17
+ const contextId = hexEncode(contextIdBytes);
18
+ // Generate or use provided session_token
19
+ let sessionToken;
20
+ if (options.sessionToken) {
21
+ sessionToken = options.sessionToken;
22
+ }
23
+ else {
24
+ const tokenBytes = new Uint8Array(16);
25
+ crypto.getRandomValues(tokenBytes);
26
+ sessionToken = base64urlEncode(tokenBytes);
27
+ }
28
+ return {
29
+ contextId,
30
+ sessionToken,
31
+ latestContext: null,
32
+ initialized: false,
33
+ policyRecordId: null,
34
+ taskIds: new Set(),
35
+ gapNodes: [],
36
+ warnings: [],
37
+ };
38
+ }
39
+ /**
40
+ * Build the outbound _meta for a tools/call request (§5.4.3).
41
+ * Attaches attribution context, traceparent, session_token, and policy record ID.
42
+ */
43
+ /**
44
+ * Build the outbound _meta for a tools/call request (§5.4.3).
45
+ * Attaches attribution context, traceparent, session_token, and policy record ID.
46
+ *
47
+ * Merges with the caller's existing _meta:
48
+ * - `baggage` and `tracestate` are APPENDED (W3C semantics, multi-vendor safe)
49
+ * - `traceparent` is overwritten only if the caller has not supplied one
50
+ * - `atrib` and `X-Atrib-Chain` are set (atrib-owned keys)
51
+ */
52
+ export function buildOutboundMeta(session, existing = {}) {
53
+ const meta = {};
54
+ // §1.5.3.1: Set X-atrib-Context on every outbound request.
55
+ // The value is the raw 32-char hex context_id (not the propagation token).
56
+ meta['X-atrib-Context'] = session.contextId;
57
+ // §1.5.4: traceparent. only set if caller has not provided one.
58
+ // If they have one, the trace-id should already match session.contextId
59
+ // (since the session adopted it), so we leave it untouched.
60
+ if (typeof existing.traceparent === 'string') {
61
+ meta.traceparent = existing.traceparent;
62
+ }
63
+ else {
64
+ const parentIdBytes = new Uint8Array(8);
65
+ crypto.getRandomValues(parentIdBytes);
66
+ const parentId = hexEncode(parentIdBytes);
67
+ meta.traceparent = `00-${session.contextId}-${parentId}-01`;
68
+ }
69
+ // §5.4.3: baggage gets atrib-session prepended (most-recent vendor first
70
+ // per W3C). mergeBaggageAtribSession dedupes prior atrib-session entries
71
+ // and enforces the W3C 64-list-member and 8192-byte limits, evicting
72
+ // entries from the rightmost end if needed.
73
+ //
74
+ // policy is added as a separate, non-evictable entry. We add it after the
75
+ // session merge to ensure both atrib entries are leftmost.
76
+ const existingBaggage = typeof existing.baggage === 'string' ? existing.baggage : '';
77
+ let baggage = mergeBaggageAtribSession(session.sessionToken, existingBaggage);
78
+ if (session.policyRecordId) {
79
+ baggage = `atrib-policy=${session.policyRecordId},${baggage}`;
80
+ }
81
+ meta.baggage = baggage;
82
+ // Attach latest attribution token if available
83
+ if (session.latestContext) {
84
+ const token = `${base64urlEncode(session.latestContext.recordHash)}.${base64urlEncode(session.latestContext.creatorKey)}`;
85
+ meta.atrib = token;
86
+ meta['X-Atrib-Chain'] = token;
87
+ // §5.4.3: tracestate gets `atrib=<token>` PREPENDED so atrib appears
88
+ // leftmost (W3C "most recent vendor first" convention). mergeTracestate
89
+ // dedupes any prior atrib entry and enforces the W3C 32-list-member
90
+ // maximum, evicting from the rightmost end if needed.
91
+ const existingTracestate = typeof existing.tracestate === 'string' ? existing.tracestate : '';
92
+ meta.tracestate = mergeTracestate(`atrib=${token}`, existingTracestate);
93
+ }
94
+ else if (typeof existing.tracestate === 'string') {
95
+ // Preserve caller's tracestate if no atrib token to add
96
+ meta.tracestate = existing.tracestate;
97
+ }
98
+ return meta;
99
+ }
100
+ /**
101
+ * Read inbound attribution context from a tool response (§5.4.4).
102
+ * Updates session state with the latest context.
103
+ * Returns true if an attribution token was found.
104
+ */
105
+ export function accumulateInboundContext(session, responseMeta) {
106
+ if (!responseMeta)
107
+ return false;
108
+ // Priority: _meta.atrib > tracestate atrib= > X-Atrib-Chain
109
+ let decoded = null;
110
+ if (typeof responseMeta.atrib === 'string') {
111
+ decoded = decodeToken(responseMeta.atrib);
112
+ }
113
+ if (!decoded && typeof responseMeta.tracestate === 'string') {
114
+ // W3C Trace Context list-member grammar allows OWS around `=`. Be lenient
115
+ // about whitespace and accept the entry whether it's leftmost or not.
116
+ for (const entry of responseMeta.tracestate.split(',')) {
117
+ const trimmed = entry.trim();
118
+ const match = trimmed.match(/^atrib\s*=\s*(.*)$/);
119
+ if (match && match[1] !== undefined) {
120
+ decoded = decodeToken(match[1].trim());
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ if (!decoded) {
126
+ const xAtribChain = responseMeta['X-Atrib-Chain'] ?? responseMeta['x-atrib-chain'];
127
+ if (typeof xAtribChain === 'string') {
128
+ decoded = decodeToken(xAtribChain);
129
+ }
130
+ }
131
+ if (decoded) {
132
+ session.latestContext = {
133
+ recordHash: decoded.recordHash,
134
+ creatorKey: decoded.creatorKey,
135
+ };
136
+ return true;
137
+ }
138
+ // No token → tool doesn't have @atrib/mcp. Gap node in graph. Session continues.
139
+ return false;
140
+ }
141
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Transaction detection (§5.4.5).
3
+ *
4
+ * Detects transaction events from response shapes for ACP, UCP, x402, MPP,
5
+ * AP2, and heuristic tool name matching.
6
+ *
7
+ * Protocol shape sources (verified 2026-04-06):
8
+ * - ACP: github.com/agentic-commerce-protocol/agentic-commerce-protocol
9
+ * rfcs/rfc.agentic_checkout.md
10
+ * - UCP: github.com/universal-commerce-protocol/ucp
11
+ * docs/specification/checkout-rest.md (version 2026-01-11)
12
+ * - AP2: github.com/google-agentic-commerce/ap2 (v0.1). A2A Message with a
13
+ * DataPart containing the key `ap2.mandates.PaymentMandate`. AP2 does
14
+ * NOT currently use W3C Verifiable Credentials despite earlier drafts
15
+ * of this code and the atrib spec assuming it would.
16
+ * - a2a-x402: github.com/google-agentic-commerce/a2a-x402. extension that
17
+ * layers x402 crypto payments over A2A. Detection signal is
18
+ * `status.message.metadata["x402.payment.status"] === "payment-completed"`
19
+ * with at least one `success: true` entry in
20
+ * `status.message.metadata["x402.payment.receipts"]`. Both shapes are
21
+ * reported as `protocol: 'AP2'` since a2a-x402 IS the AP2 crypto path.
22
+ * - x402: github.com/coinbase/x402. response header `PAYMENT-RESPONSE` (v2)
23
+ * or `X-PAYMENT-RESPONSE` (v1 legacy). Value is base64-encoded JSON
24
+ * with shape { success: bool, transaction, network, payer, requirements }.
25
+ * - MPP: IETF draft-ryan-httpauth-payment-01 ("The 'Payment' HTTP Authentication
26
+ * Scheme"), per Section 5.3. Response header is `Payment-Receipt` on a
27
+ * 200 success after the client retries with Authorization: Payment.
28
+ * Value is base64url-nopad JSON with required field { status: "success",
29
+ * method, timestamp, reference }.
30
+ *
31
+ * x402 and MPP are DIFFERENT protocols that use DIFFERENT headers. Earlier
32
+ * versions of this code conflated them on a fictitious shared `Payment-Receipt`
33
+ * header. see DECISIONS.md D016 for the verification trail.
34
+ */
35
+ export type TransactionProtocol = 'ACP' | 'UCP' | 'x402' | 'MPP' | 'AP2' | 'heuristic';
36
+ export interface TransactionDetection {
37
+ detected: boolean;
38
+ protocol: TransactionProtocol | null;
39
+ /**
40
+ * For Path 2 content_id derivation (§5.4.5):
41
+ * - ACP/UCP: the order permalink URL from the response (if present)
42
+ * - x402/MPP: not available here (caller must use HTTP endpoint URL)
43
+ * - AP2: not available here (caller uses MCP server URL)
44
+ * - Heuristic: not available here (caller uses MCP server URL)
45
+ */
46
+ checkoutUrl: string | null;
47
+ }
48
+ /**
49
+ * Detect whether a tool call response contains a transaction signal (§5.4.5).
50
+ */
51
+ export declare function detectTransaction(toolName: string, response: unknown, headers?: Record<string, string | undefined>): TransactionDetection;
52
+ //# sourceMappingURL=transaction.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,WAAW,CAAA;AAEtF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,OAAO,CAAA;IACjB,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAA;IACpC;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAC3B;AAWD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,OAAO,EACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAC3C,oBAAoB,CAkItB"}
@@ -0,0 +1,123 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ const HEURISTIC_KEYWORDS = [
3
+ 'create_order',
4
+ 'complete_checkout',
5
+ 'process_payment',
6
+ 'place_order',
7
+ 'purchase',
8
+ 'checkout',
9
+ ];
10
+ /**
11
+ * Detect whether a tool call response contains a transaction signal (§5.4.5).
12
+ */
13
+ export function detectTransaction(toolName, response, headers) {
14
+ const resp = response;
15
+ // ACP / UCP completion response shape:
16
+ // { id: "...", status: "completed", order: { id, permalink_url? }, ... }
17
+ // UCP additionally has a top-level `ucp: { version, capabilities }` envelope.
18
+ // Both shapes are produced by POST /checkout_sessions/{id}/complete (ACP)
19
+ // or POST /checkout-sessions/{id}/complete (UCP).
20
+ if (resp) {
21
+ const status = resp['status'];
22
+ const order = resp['order'];
23
+ if (status === 'completed' && order && typeof order['id'] === 'string') {
24
+ const ucpEnvelope = resp['ucp'];
25
+ const isUcp = !!ucpEnvelope && typeof ucpEnvelope['version'] === 'string';
26
+ const checkoutUrl = typeof order['permalink_url'] === 'string' ? order['permalink_url'] : null;
27
+ return { detected: true, protocol: isUcp ? 'UCP' : 'ACP', checkoutUrl };
28
+ }
29
+ // ACP webhook event shapes (server → merchant): order_create / order_update.
30
+ // { type: "order_create", data: { type: "order", checkout_session_id, permalink_url, status, refunds } }
31
+ if (resp['type'] === 'order_create' || resp['type'] === 'order_update') {
32
+ const data = resp['data'];
33
+ const checkoutUrl = typeof data?.['permalink_url'] === 'string' ? data['permalink_url'] : null;
34
+ return { detected: true, protocol: 'ACP', checkoutUrl };
35
+ }
36
+ }
37
+ // x402 and MPP: distinct protocols, distinct response headers.
38
+ //
39
+ // x402 v2 → PAYMENT-RESPONSE (renamed from v1 X-PAYMENT-RESPONSE)
40
+ // MPP → Payment-Receipt (per draft-ryan-httpauth-payment-01 §5.3)
41
+ //
42
+ // HTTP header names are case-insensitive per RFC 7230, so we accept any
43
+ // letter casing. JS object keys are not case-insensitive, so we lower-case
44
+ // the lookup table once and probe by lowercase key.
45
+ if (headers) {
46
+ const lower = {};
47
+ for (const [k, v] of Object.entries(headers)) {
48
+ if (typeof v === 'string')
49
+ lower[k.toLowerCase()] = v;
50
+ }
51
+ // x402. accept v2 and v1 names
52
+ if (lower['payment-response'] || lower['x-payment-response']) {
53
+ return { detected: true, protocol: 'x402', checkoutUrl: null };
54
+ }
55
+ // MPP. IETF draft Payment-Receipt header
56
+ if (lower['payment-receipt']) {
57
+ return { detected: true, protocol: 'MPP', checkoutUrl: null };
58
+ }
59
+ }
60
+ // AP2 v0.1. PaymentMandate Message inside an A2A DataPart.
61
+ // Source: github.com/google-agentic-commerce/ap2 docs/specification.md
62
+ // Shape: { ..., parts: [{ kind: "data", data: { "ap2.mandates.PaymentMandate": {...} } }, ...] }
63
+ if (resp) {
64
+ const parts = resp['parts'];
65
+ if (Array.isArray(parts)) {
66
+ for (const part of parts) {
67
+ if (part && typeof part === 'object') {
68
+ const data = part['data'];
69
+ if (data &&
70
+ typeof data === 'object' &&
71
+ 'ap2.mandates.PaymentMandate' in data) {
72
+ return { detected: true, protocol: 'AP2', checkoutUrl: null };
73
+ }
74
+ }
75
+ }
76
+ }
77
+ // a2a-x402 extension. payment-completed via A2A task status metadata.
78
+ // Source: github.com/google-agentic-commerce/a2a-x402 spec/v0.1/spec.md
79
+ // Shape: { kind: "task", status: { message: { metadata: { "x402.payment.status": "payment-completed", "x402.payment.receipts": [{success, transaction, ...}] } } } }
80
+ // Guard on kind === "task" to prevent false positives from responses
81
+ // that incidentally contain matching metadata keys.
82
+ if (resp['kind'] === 'task') {
83
+ const status = resp['status'];
84
+ const statusMessage = status?.['message'];
85
+ const metadata = statusMessage?.['metadata'];
86
+ if (metadata && metadata['x402.payment.status'] === 'payment-completed') {
87
+ const receipts = metadata['x402.payment.receipts'];
88
+ if (Array.isArray(receipts) &&
89
+ receipts.some((r) => r !== null &&
90
+ typeof r === 'object' &&
91
+ r['success'] === true)) {
92
+ return { detected: true, protocol: 'AP2', checkoutUrl: null };
93
+ }
94
+ }
95
+ }
96
+ // Legacy: W3C Verifiable Credential PaymentMandate. AP2 v0.1 does NOT use
97
+ // VCs, but research forks and earlier drafts may. Kept as a backward-
98
+ // compatible fallback. Accepts both VC v2 array form and v1 string form.
99
+ const respType = resp['type'];
100
+ const credentialSubject = resp['credentialSubject'];
101
+ const subjectType = credentialSubject?.['type'];
102
+ const isVcArray = Array.isArray(respType) &&
103
+ respType.includes('VerifiableCredential') &&
104
+ respType.some((t) => typeof t === 'string' && /paymentmandate/i.test(t));
105
+ const isVcStringLegacy = respType === 'VerifiableCredential';
106
+ const subjectIsPaymentMandate = (typeof subjectType === 'string' && /paymentmandate/i.test(subjectType)) ||
107
+ (Array.isArray(subjectType) &&
108
+ subjectType.some((t) => typeof t === 'string' && /paymentmandate/i.test(t)));
109
+ // For v2 array form: the PaymentMandate type is in the type array itself,
110
+ // so no credentialSubject check needed. For v1 string form: type is just
111
+ // "VerifiableCredential" so we need credentialSubject to confirm it's a PaymentMandate.
112
+ if (isVcArray || (isVcStringLegacy && subjectIsPaymentMandate)) {
113
+ return { detected: true, protocol: 'AP2', checkoutUrl: null };
114
+ }
115
+ }
116
+ // Heuristic: tool name keywords (last resort)
117
+ const lowerName = toolName.toLowerCase();
118
+ if (HEURISTIC_KEYWORDS.some((k) => lowerName.includes(k))) {
119
+ return { detected: true, protocol: 'heuristic', checkoutUrl: null };
120
+ }
121
+ return { detected: false, protocol: null, checkoutUrl: null };
122
+ }
123
+ //# sourceMappingURL=transaction.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transaction.js","sourceRoot":"","sources":["../src/transaction.ts"],"names":[],"mappings":"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"}