@vallum/standards 0.0.0-prerelease

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.
@@ -0,0 +1,125 @@
1
+ import { type AgentTransactionManifest } from "@vallum/manifest";
2
+ import { type AgentActionPolicy, type AgentPolicyDecision } from "@vallum/policy-gateway";
3
+ export declare const A2A_TASK_PROTOCOL_VERSION: "1.0";
4
+ export declare const A2A_TASK_MEDIA_TYPE: "application/a2a+json";
5
+ export type A2ARole = "ROLE_USER" | "ROLE_AGENT";
6
+ export type A2ATaskState = "TASK_STATE_SUBMITTED" | "TASK_STATE_WORKING" | "TASK_STATE_INPUT_REQUIRED" | "TASK_STATE_COMPLETED" | "TASK_STATE_CANCELED" | "TASK_STATE_FAILED" | "TASK_STATE_REJECTED";
7
+ export interface A2APart {
8
+ readonly text?: string;
9
+ readonly data?: Record<string, unknown>;
10
+ readonly file?: {
11
+ readonly uri?: string;
12
+ readonly bytes?: string;
13
+ readonly filename?: string;
14
+ readonly mediaType?: string;
15
+ };
16
+ }
17
+ export interface A2AMessage {
18
+ readonly messageId: string;
19
+ readonly role: A2ARole;
20
+ readonly parts: readonly A2APart[];
21
+ readonly taskId?: string;
22
+ readonly contextId?: string;
23
+ readonly metadata?: Record<string, unknown>;
24
+ }
25
+ export interface A2AArtifact {
26
+ readonly artifactId: string;
27
+ readonly name?: string;
28
+ readonly parts: readonly A2APart[];
29
+ readonly metadata?: Record<string, unknown>;
30
+ }
31
+ export interface A2ATaskStatus {
32
+ readonly state: A2ATaskState;
33
+ readonly timestamp: string;
34
+ readonly message?: A2AMessage;
35
+ }
36
+ export interface A2ATask {
37
+ readonly id: string;
38
+ readonly contextId: string;
39
+ readonly status: A2ATaskStatus;
40
+ readonly history: readonly A2AMessage[];
41
+ readonly artifacts?: readonly A2AArtifact[];
42
+ readonly agenticVallum?: {
43
+ readonly manifestId: string;
44
+ readonly agentId: string;
45
+ readonly counterpartyId: string;
46
+ readonly action: {
47
+ readonly packageId: string;
48
+ readonly module?: string;
49
+ readonly functionName: string;
50
+ readonly templateId?: string;
51
+ readonly templateVersion?: string;
52
+ };
53
+ readonly policyDecision: AgentPolicyDecision;
54
+ };
55
+ readonly metadata?: Record<string, unknown>;
56
+ }
57
+ export type A2ATaskErrorCode = "A2A_VERSION_NOT_SUPPORTED" | "A2A_MESSAGE_INVALID" | "A2A_MANIFEST_REQUIRED" | "A2A_POLICY_REQUIRED" | "A2A_MANIFEST_INVALID" | "A2A_TASK_NOT_FOUND" | "A2A_TASK_TERMINAL" | "A2A_CONTEXT_MISMATCH";
58
+ export declare class A2ATaskError extends Error {
59
+ readonly code: A2ATaskErrorCode;
60
+ readonly status: 400 | 404 | 409;
61
+ constructor(code: A2ATaskErrorCode, message: string, status?: 400 | 404 | 409);
62
+ }
63
+ export interface A2AProcessMessageContext {
64
+ readonly task: A2ATask;
65
+ readonly message: A2AMessage;
66
+ readonly manifest?: AgentTransactionManifest;
67
+ readonly policyDecision?: AgentPolicyDecision;
68
+ }
69
+ export interface A2AProcessMessageResult {
70
+ readonly state: A2ATaskState;
71
+ readonly message?: A2AMessage;
72
+ readonly artifacts?: readonly A2AArtifact[];
73
+ readonly metadata?: Record<string, unknown>;
74
+ }
75
+ export interface SendA2AMessageOptions {
76
+ readonly store: LocalA2ATaskStore;
77
+ readonly protocolVersion?: string;
78
+ readonly message: A2AMessage;
79
+ readonly manifest?: unknown;
80
+ readonly policy?: AgentActionPolicy;
81
+ readonly now?: Date;
82
+ readonly contextId?: string;
83
+ readonly processMessage?: (context: A2AProcessMessageContext) => Promise<A2AProcessMessageResult> | A2AProcessMessageResult;
84
+ }
85
+ export interface SendA2AMessageResult {
86
+ readonly task: A2ATask;
87
+ readonly policyDecision?: AgentPolicyDecision;
88
+ }
89
+ export interface GetA2ATaskOptions {
90
+ readonly store: LocalA2ATaskStore;
91
+ readonly id: string;
92
+ readonly includeArtifacts?: boolean;
93
+ readonly historyLength?: number;
94
+ }
95
+ export interface GetA2ATaskResult {
96
+ readonly task: A2ATask;
97
+ }
98
+ export interface ListA2ATasksOptions {
99
+ readonly store: LocalA2ATaskStore;
100
+ readonly contextId?: string;
101
+ readonly state?: A2ATaskState;
102
+ readonly includeArtifacts?: boolean;
103
+ readonly historyLength?: number;
104
+ readonly pageSize?: number;
105
+ }
106
+ export interface ListA2ATasksResult {
107
+ readonly tasks: readonly A2ATask[];
108
+ }
109
+ export interface CancelA2ATaskOptions {
110
+ readonly store: LocalA2ATaskStore;
111
+ readonly id: string;
112
+ readonly now?: Date;
113
+ readonly includeArtifacts?: boolean;
114
+ }
115
+ export declare class LocalA2ATaskStore {
116
+ #private;
117
+ put(task: A2ATask): A2ATask;
118
+ get(id: string): A2ATask | undefined;
119
+ list(): readonly A2ATask[];
120
+ }
121
+ export declare function sendA2AMessage(options: SendA2AMessageOptions): Promise<SendA2AMessageResult>;
122
+ export declare function getA2ATask(options: GetA2ATaskOptions): GetA2ATaskResult;
123
+ export declare function listA2ATasks(options: ListA2ATasksOptions): ListA2ATasksResult;
124
+ export declare function cancelA2ATask(options: CancelA2ATaskOptions): GetA2ATaskResult;
125
+ export declare function redactA2ATaskForLog(task: A2ATask): unknown;
@@ -0,0 +1,327 @@
1
+ import { validateAgentTransactionManifest, } from "@vallum/manifest";
2
+ import { evaluateAgentActionPolicy, } from "@vallum/policy-gateway";
3
+ export const A2A_TASK_PROTOCOL_VERSION = "1.0";
4
+ export const A2A_TASK_MEDIA_TYPE = "application/a2a+json";
5
+ export class A2ATaskError extends Error {
6
+ code;
7
+ status;
8
+ constructor(code, message, status = 400) {
9
+ super(message);
10
+ this.name = "A2ATaskError";
11
+ this.code = code;
12
+ this.status = status;
13
+ }
14
+ }
15
+ export class LocalA2ATaskStore {
16
+ #tasks = new Map();
17
+ put(task) {
18
+ const safeTask = redactA2ATask(task);
19
+ this.#tasks.set(safeTask.id, clone(safeTask));
20
+ return clone(safeTask);
21
+ }
22
+ get(id) {
23
+ const task = this.#tasks.get(id);
24
+ return task ? clone(task) : undefined;
25
+ }
26
+ list() {
27
+ return [...this.#tasks.values()].map((task) => clone(task));
28
+ }
29
+ }
30
+ export async function sendA2AMessage(options) {
31
+ assertSupportedVersion(options.protocolVersion);
32
+ assertValidMessage(options.message, "$.message");
33
+ if (options.message.role !== "ROLE_USER") {
34
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "A2A client messages must use ROLE_USER.");
35
+ }
36
+ const now = options.now ?? new Date();
37
+ const message = redactA2AMessage(options.message);
38
+ if (message.taskId) {
39
+ return continueTask(options, message, now);
40
+ }
41
+ return createTask(options, message, now);
42
+ }
43
+ export function getA2ATask(options) {
44
+ const task = options.store.get(options.id);
45
+ if (!task)
46
+ throw new A2ATaskError("A2A_TASK_NOT_FOUND", "A2A task was not found.", 404);
47
+ return { task: taskView(task, options) };
48
+ }
49
+ export function listA2ATasks(options) {
50
+ const tasks = options.store.list()
51
+ .filter((task) => options.contextId === undefined || task.contextId === options.contextId)
52
+ .filter((task) => options.state === undefined || task.status.state === options.state)
53
+ .sort((left, right) => Date.parse(right.status.timestamp) - Date.parse(left.status.timestamp))
54
+ .slice(0, options.pageSize ?? Number.POSITIVE_INFINITY)
55
+ .map((task) => taskView(task, options));
56
+ return { tasks };
57
+ }
58
+ export function cancelA2ATask(options) {
59
+ const task = options.store.get(options.id);
60
+ if (!task)
61
+ throw new A2ATaskError("A2A_TASK_NOT_FOUND", "A2A task was not found.", 404);
62
+ if (isTerminalTaskState(task.status.state)) {
63
+ throw new A2ATaskError("A2A_TASK_TERMINAL", "Terminal A2A tasks cannot be canceled.", 409);
64
+ }
65
+ const canceled = {
66
+ ...task,
67
+ status: {
68
+ state: "TASK_STATE_CANCELED",
69
+ timestamp: timestamp(options.now),
70
+ },
71
+ artifacts: undefined,
72
+ };
73
+ options.store.put(canceled);
74
+ return { task: taskView(canceled, options) };
75
+ }
76
+ export function redactA2ATaskForLog(task) {
77
+ return redactDeep(task);
78
+ }
79
+ function createTask(options, message, now) {
80
+ if (options.manifest === undefined) {
81
+ throw new A2ATaskError("A2A_MANIFEST_REQUIRED", "New A2A tasks require an Vallum manifest.");
82
+ }
83
+ if (!options.policy) {
84
+ throw new A2ATaskError("A2A_POLICY_REQUIRED", "New A2A tasks require an Vallum policy.");
85
+ }
86
+ const manifestResult = validateAgentTransactionManifest(options.manifest, { now });
87
+ if (!manifestResult.ok) {
88
+ throw new A2ATaskError("A2A_MANIFEST_INVALID", "A2A task manifest failed validation.");
89
+ }
90
+ const manifest = manifestResult.manifest;
91
+ const policyDecision = evaluateAgentActionPolicy(options.policy, manifest, { now });
92
+ const baseTask = {
93
+ id: randomId("task"),
94
+ contextId: message.contextId ?? options.contextId ?? randomId("ctx"),
95
+ status: {
96
+ state: policyDecision.allowed ? "TASK_STATE_SUBMITTED" : "TASK_STATE_REJECTED",
97
+ timestamp: timestamp(now),
98
+ ...(!policyDecision.allowed ? { message: rejectionMessage(policyDecision, now) } : {}),
99
+ },
100
+ history: policyDecision.allowed ? [message] : [message, rejectionMessage(policyDecision, now)],
101
+ agenticVallum: {
102
+ manifestId: manifest.idempotencyKey,
103
+ agentId: manifest.agent.id,
104
+ counterpartyId: manifest.counterparty.id,
105
+ action: {
106
+ packageId: manifest.action.packageId,
107
+ ...(manifest.action.module ? { module: manifest.action.module } : {}),
108
+ functionName: manifest.action.functionName,
109
+ ...(manifest.action.templateId ? { templateId: manifest.action.templateId } : {}),
110
+ ...(manifest.action.templateVersion ? { templateVersion: manifest.action.templateVersion } : {}),
111
+ },
112
+ policyDecision,
113
+ },
114
+ };
115
+ if (!policyDecision.allowed) {
116
+ return { task: options.store.put(baseTask), policyDecision };
117
+ }
118
+ return processAndStoreTask(options, baseTask, message, now, manifest, policyDecision);
119
+ }
120
+ async function continueTask(options, message, now) {
121
+ const task = options.store.get(message.taskId ?? "");
122
+ if (!task)
123
+ throw new A2ATaskError("A2A_TASK_NOT_FOUND", "A2A task was not found.", 404);
124
+ if (isTerminalTaskState(task.status.state)) {
125
+ throw new A2ATaskError("A2A_TASK_TERMINAL", "Terminal A2A tasks cannot accept follow-up messages.", 409);
126
+ }
127
+ if (message.contextId !== undefined && message.contextId !== task.contextId) {
128
+ throw new A2ATaskError("A2A_CONTEXT_MISMATCH", "Follow-up A2A message context does not match the task.", 409);
129
+ }
130
+ const nextTask = {
131
+ ...task,
132
+ history: [...task.history, message],
133
+ status: {
134
+ state: task.status.state,
135
+ timestamp: timestamp(now),
136
+ ...(task.status.message ? { message: task.status.message } : {}),
137
+ },
138
+ };
139
+ return processAndStoreTask(options, nextTask, message, now, undefined, task.agenticVallum?.policyDecision);
140
+ }
141
+ async function processAndStoreTask(options, task, message, now, manifest, policyDecision) {
142
+ const outcome = options.processMessage
143
+ ? await options.processMessage({ task, message, manifest, policyDecision })
144
+ : {
145
+ state: "TASK_STATE_COMPLETED",
146
+ artifacts: [{
147
+ artifactId: randomId("artifact"),
148
+ parts: [{ text: "A2A task accepted by local Vallum runtime." }],
149
+ }],
150
+ };
151
+ assertValidTaskState(outcome.state);
152
+ if (outcome.message)
153
+ assertValidMessage(outcome.message, "$.processMessage.message");
154
+ for (const [index, artifact] of (outcome.artifacts ?? []).entries()) {
155
+ assertValidArtifact(artifact, `$.processMessage.artifacts[${index}]`);
156
+ }
157
+ if (isArtifactForbiddenState(outcome.state) && (outcome.artifacts?.length ?? 0) > 0) {
158
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "Terminal failed, rejected, and canceled tasks cannot attach artifacts.");
159
+ }
160
+ const statusMessage = outcome.message ? redactA2AMessage(outcome.message) : undefined;
161
+ const nextTask = {
162
+ ...task,
163
+ status: {
164
+ state: outcome.state,
165
+ timestamp: timestamp(now),
166
+ ...(statusMessage ? { message: statusMessage } : {}),
167
+ },
168
+ history: statusMessage ? [...task.history, statusMessage] : task.history,
169
+ ...(outcome.artifacts ? { artifacts: outcome.artifacts.map(redactA2AArtifact) } : {}),
170
+ ...(outcome.metadata ? { metadata: redactRecord(outcome.metadata) } : {}),
171
+ };
172
+ return { task: options.store.put(nextTask), policyDecision };
173
+ }
174
+ function rejectionMessage(policyDecision, now) {
175
+ return {
176
+ messageId: `policy-${timestamp(now)}`,
177
+ role: "ROLE_AGENT",
178
+ parts: [{ text: `Vallum policy denied the A2A task: ${policyDecision.reasonCode}` }],
179
+ metadata: {
180
+ reasonCode: policyDecision.reasonCode,
181
+ },
182
+ };
183
+ }
184
+ function assertSupportedVersion(version = A2A_TASK_PROTOCOL_VERSION) {
185
+ if (version !== A2A_TASK_PROTOCOL_VERSION) {
186
+ throw new A2ATaskError("A2A_VERSION_NOT_SUPPORTED", "A2A protocol version is unsupported.");
187
+ }
188
+ }
189
+ function assertValidTaskState(state) {
190
+ if (![
191
+ "TASK_STATE_SUBMITTED",
192
+ "TASK_STATE_WORKING",
193
+ "TASK_STATE_INPUT_REQUIRED",
194
+ "TASK_STATE_COMPLETED",
195
+ "TASK_STATE_CANCELED",
196
+ "TASK_STATE_FAILED",
197
+ "TASK_STATE_REJECTED",
198
+ ].includes(state)) {
199
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "A2A task state is invalid.");
200
+ }
201
+ }
202
+ function assertValidMessage(message, path) {
203
+ if (!message || typeof message !== "object") {
204
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "A2A message must be an object.");
205
+ }
206
+ if (typeof message.messageId !== "string" || message.messageId.trim() === "") {
207
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "A2A messageId is required.");
208
+ }
209
+ if (message.role !== "ROLE_USER" && message.role !== "ROLE_AGENT") {
210
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", "A2A message role is invalid.");
211
+ }
212
+ requireOptionalNonEmptyString(message.taskId, `${path}.taskId`);
213
+ requireOptionalNonEmptyString(message.contextId, `${path}.contextId`);
214
+ if (!Array.isArray(message.parts) || message.parts.length === 0) {
215
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path}.parts must contain at least one part.`);
216
+ }
217
+ message.parts.forEach((part, index) => assertValidPart(part, `${path}.parts[${index}]`));
218
+ }
219
+ function assertValidArtifact(artifact, path) {
220
+ if (typeof artifact.artifactId !== "string" || artifact.artifactId.trim() === "") {
221
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path}.artifactId is required.`);
222
+ }
223
+ if (!Array.isArray(artifact.parts) || artifact.parts.length === 0) {
224
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path}.parts must contain at least one part.`);
225
+ }
226
+ artifact.parts.forEach((part, index) => assertValidPart(part, `${path}.parts[${index}]`));
227
+ }
228
+ function assertValidPart(part, path) {
229
+ if (!part || typeof part !== "object" || Array.isArray(part)) {
230
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path} must be an object.`);
231
+ }
232
+ const variants = [
233
+ typeof part.text === "string" && part.text.trim() !== "",
234
+ isRecord(part.data),
235
+ isRecord(part.file),
236
+ ].filter(Boolean);
237
+ if (variants.length !== 1) {
238
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path} must contain exactly one text, data, or file part.`);
239
+ }
240
+ }
241
+ function taskView(task, options) {
242
+ const history = typeof options.historyLength === "number"
243
+ ? task.history.slice(-Math.max(0, options.historyLength))
244
+ : task.history;
245
+ return clone({
246
+ ...task,
247
+ history,
248
+ ...(options.includeArtifacts ? { artifacts: task.artifacts } : { artifacts: undefined }),
249
+ });
250
+ }
251
+ function redactA2AMessage(message) {
252
+ return {
253
+ ...message,
254
+ parts: message.parts.map(redactA2APart),
255
+ ...(message.metadata ? { metadata: redactRecord(message.metadata) } : {}),
256
+ };
257
+ }
258
+ function redactA2ATask(task) {
259
+ return redactDeep(task);
260
+ }
261
+ function redactA2AArtifact(artifact) {
262
+ return {
263
+ ...artifact,
264
+ parts: artifact.parts.map(redactA2APart),
265
+ ...(artifact.metadata ? { metadata: redactRecord(artifact.metadata) } : {}),
266
+ };
267
+ }
268
+ function redactA2APart(part) {
269
+ return redactDeep(part);
270
+ }
271
+ function redactRecord(value) {
272
+ return redactDeep(value);
273
+ }
274
+ function redactDeep(value, key = "") {
275
+ if (isSecretField(key))
276
+ return "[REDACTED]";
277
+ if (Array.isArray(value))
278
+ return value.map((item) => redactDeep(item));
279
+ if (isRecord(value)) {
280
+ return Object.fromEntries(Object.entries(value).map(([childKey, child]) => [childKey, redactDeep(child, childKey)]));
281
+ }
282
+ if (typeof value === "string")
283
+ return redactString(value);
284
+ return value;
285
+ }
286
+ function redactString(value) {
287
+ if (/private\s+prompt\s*:/i.test(value))
288
+ return "[REDACTED]";
289
+ if (/(mnemonic|private key|raw keypair|payment credential)/i.test(value))
290
+ return "[REDACTED]";
291
+ return value
292
+ .replace(/Bearer\s+[A-Za-z0-9._-]+/gi, "[REDACTED]")
293
+ .replace(/sk-[A-Za-z0-9._-]+/gi, "[REDACTED]")
294
+ .replace(/signer_ref_[A-Za-z0-9_-]+/gi, "[REDACTED]")
295
+ .replace(/([?&](?:token|signature|X-Amz-Signature)=)[^&#\s]+/gi, "$1[REDACTED]");
296
+ }
297
+ function isTerminalTaskState(state) {
298
+ return [
299
+ "TASK_STATE_COMPLETED",
300
+ "TASK_STATE_CANCELED",
301
+ "TASK_STATE_FAILED",
302
+ "TASK_STATE_REJECTED",
303
+ ].includes(state);
304
+ }
305
+ function isArtifactForbiddenState(state) {
306
+ return ["TASK_STATE_CANCELED", "TASK_STATE_FAILED", "TASK_STATE_REJECTED"].includes(state);
307
+ }
308
+ function requireOptionalNonEmptyString(value, path) {
309
+ if (value !== undefined && value.trim() === "") {
310
+ throw new A2ATaskError("A2A_MESSAGE_INVALID", `${path} must be non-empty when present.`);
311
+ }
312
+ }
313
+ function randomId(prefix) {
314
+ return `${prefix}_${crypto.randomUUID()}`;
315
+ }
316
+ function timestamp(now) {
317
+ return (now ?? new Date()).toISOString();
318
+ }
319
+ function clone(value) {
320
+ return JSON.parse(JSON.stringify(value));
321
+ }
322
+ function isRecord(value) {
323
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
324
+ }
325
+ function isSecretField(key) {
326
+ return /^(seed|mnemonic|privateKey|private_key|rawKeypair|raw_keypair|rawTransactionBytes|raw_transaction_bytes|userSignature|user_signature|sponsorKey|sponsor_key|appApiKey|app_api_key|bearerToken|bearer_token|paymentCredential|payment_credential|signerSecret|signer_secret|signerRef|signer_ref|walletId|wallet_id|privatePrompt|private_prompt|prompt|paymentPayload|payment_payload|bytes)$/i.test(key);
327
+ }
package/dist/ap2.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { type AP2ManifestMappingContext, type AP2MandateBundle, type AgentTransactionManifest } from "@vallum/manifest";
2
+ import { type AgentActionPolicy, type AgentPolicyDecision } from "@vallum/policy-gateway";
3
+ import { type AP2MandateReceiptState, type AP2ReceiptBundle } from "@vallum/receipts";
4
+ export type { AP2ManifestMappingContext, AP2MandateBundle, AP2ReceiptBundle };
5
+ export interface RunAP2MockMandateFlowOptions {
6
+ readonly mandateBundle: AP2MandateBundle;
7
+ readonly manifestContext: AP2ManifestMappingContext;
8
+ readonly policy: AgentActionPolicy;
9
+ readonly issueReceipts: (manifest: AgentTransactionManifest) => Promise<AP2ReceiptBundle> | AP2ReceiptBundle;
10
+ readonly execute: (manifest: AgentTransactionManifest) => Promise<Record<string, unknown>> | Record<string, unknown>;
11
+ readonly now?: Date;
12
+ }
13
+ export type AP2MockMandateFlowResult = {
14
+ readonly approved: true;
15
+ readonly manifest: AgentTransactionManifest;
16
+ readonly receipt: AP2MandateReceiptState;
17
+ readonly toolResult: Record<string, unknown>;
18
+ readonly logSafeMandateMetadata: unknown;
19
+ } | {
20
+ readonly approved: false;
21
+ readonly manifest: AgentTransactionManifest;
22
+ readonly denial: Exclude<AgentPolicyDecision, {
23
+ allowed: true;
24
+ }>;
25
+ readonly stage: "policy";
26
+ } | {
27
+ readonly approved: false;
28
+ readonly manifest: AgentTransactionManifest;
29
+ readonly paymentFailure: AP2PaymentFailure;
30
+ readonly receipt: AP2MandateReceiptState;
31
+ readonly logSafeMandateMetadata: unknown;
32
+ };
33
+ export interface AP2PaymentFailure {
34
+ readonly reasonCode: "AP2_CHECKOUT_RECEIPT_FAILED" | "AP2_PAYMENT_RECEIPT_FAILED";
35
+ readonly message: string;
36
+ }
37
+ export declare function runAP2MockMandateFlow(options: RunAP2MockMandateFlowOptions): Promise<AP2MockMandateFlowResult>;
38
+ export declare function redactAP2MandateLogMetadata(bundle: AP2MandateBundle): unknown;
package/dist/ap2.js ADDED
@@ -0,0 +1,56 @@
1
+ import { ap2PaymentMandateId, mapAP2MandatesToManifest, } from "@vallum/manifest";
2
+ import { evaluateAgentActionPolicy, } from "@vallum/policy-gateway";
3
+ import { createAP2MandateReceiptState, redactAP2MandateMetadata, } from "@vallum/receipts";
4
+ export async function runAP2MockMandateFlow(options) {
5
+ const manifest = mapAP2MandatesToManifest(options.mandateBundle, {
6
+ ...options.manifestContext,
7
+ now: options.now ?? options.manifestContext.now,
8
+ });
9
+ const policyDecision = evaluateAgentActionPolicy(options.policy, manifest, { now: options.now });
10
+ if (!policyDecision.allowed)
11
+ return { approved: false, manifest, denial: policyDecision, stage: "policy" };
12
+ const receipts = await options.issueReceipts(manifest);
13
+ const receipt = createAP2MandateReceiptState({
14
+ manifestId: manifest.idempotencyKey,
15
+ checkoutHash: options.mandateBundle.checkoutMandate.checkout_hash,
16
+ paymentMandateId: ap2PaymentMandateId(options.mandateBundle.paymentMandate),
17
+ disputeEvidenceReference: options.mandateBundle.disputeEvidenceReference,
18
+ checkoutReceipt: receipts.checkoutReceipt,
19
+ paymentReceipt: receipts.paymentReceipt,
20
+ });
21
+ const logSafeMandateMetadata = redactAP2MandateLogMetadata(options.mandateBundle);
22
+ if (receipts.checkoutReceipt.status !== "Success") {
23
+ return {
24
+ approved: false,
25
+ manifest,
26
+ receipt,
27
+ paymentFailure: {
28
+ reasonCode: "AP2_CHECKOUT_RECEIPT_FAILED",
29
+ message: receipts.checkoutReceipt.error ?? "AP2 checkout receipt failed.",
30
+ },
31
+ logSafeMandateMetadata,
32
+ };
33
+ }
34
+ if (receipts.paymentReceipt.status !== "Success") {
35
+ return {
36
+ approved: false,
37
+ manifest,
38
+ receipt,
39
+ paymentFailure: {
40
+ reasonCode: "AP2_PAYMENT_RECEIPT_FAILED",
41
+ message: receipts.paymentReceipt.error ?? "AP2 payment receipt failed.",
42
+ },
43
+ logSafeMandateMetadata,
44
+ };
45
+ }
46
+ return {
47
+ approved: true,
48
+ manifest,
49
+ receipt,
50
+ toolResult: await options.execute(manifest),
51
+ logSafeMandateMetadata,
52
+ };
53
+ }
54
+ export function redactAP2MandateLogMetadata(bundle) {
55
+ return redactAP2MandateMetadata(bundle);
56
+ }
@@ -0,0 +1,7 @@
1
+ export * from "./ap2.js";
2
+ export * from "./a2a.js";
3
+ export * from "./a2aHttp.js";
4
+ export * from "./a2aNodeServer.js";
5
+ export * from "./a2aPush.js";
6
+ export * from "./a2aTask.js";
7
+ export * from "./x402.js";
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./ap2.js";
2
+ export * from "./a2a.js";
3
+ export * from "./a2aHttp.js";
4
+ export * from "./a2aNodeServer.js";
5
+ export * from "./a2aPush.js";
6
+ export * from "./a2aTask.js";
7
+ export * from "./x402.js";
package/dist/x402.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { type AgentTransactionManifest, type X402ManifestMappingContext, type X402PaymentRequired, type X402PaymentRequirements } from "@vallum/manifest";
2
+ import { type AgentActionPolicy, type AgentPolicyDecision } from "@vallum/policy-gateway";
3
+ import { type X402ExternalPaymentReceiptState, type X402SettleEvidence, type X402VerifyEvidence } from "@vallum/receipts";
4
+ export type { X402ManifestMappingContext, X402PaymentRequired, X402PaymentRequirements };
5
+ export interface X402MockFacilitator {
6
+ readonly verify: (request: X402FacilitatorRequest) => Promise<X402VerifyEvidence> | X402VerifyEvidence;
7
+ readonly settle: (request: X402FacilitatorRequest) => Promise<X402SettleEvidence> | X402SettleEvidence;
8
+ }
9
+ export interface X402FacilitatorRequest {
10
+ readonly x402Version: 2;
11
+ readonly paymentPayload: X402PaymentPayload;
12
+ readonly paymentRequirements: X402PaymentRequirements;
13
+ }
14
+ export interface X402PaymentPayload {
15
+ readonly x402Version: 2;
16
+ readonly resource?: X402PaymentRequired["resource"];
17
+ readonly accepted: X402PaymentRequirements;
18
+ readonly payload: Record<string, unknown>;
19
+ readonly extensions?: Record<string, unknown>;
20
+ }
21
+ export interface RunX402MockFacilitatorFlowOptions {
22
+ readonly paymentRequired: X402PaymentRequired;
23
+ readonly manifestContext: X402ManifestMappingContext;
24
+ readonly policy: AgentActionPolicy;
25
+ readonly facilitator: X402MockFacilitator;
26
+ readonly invokeTool: () => Promise<Record<string, unknown>> | Record<string, unknown>;
27
+ readonly now?: Date;
28
+ readonly paymentPayload?: Record<string, unknown>;
29
+ }
30
+ export type X402MockFacilitatorFlowResult = {
31
+ readonly approved: true;
32
+ readonly manifest: AgentTransactionManifest;
33
+ readonly receipt: X402ExternalPaymentReceiptState;
34
+ readonly toolResult: Record<string, unknown>;
35
+ readonly logSafePaymentMetadata: unknown;
36
+ } | {
37
+ readonly approved: false;
38
+ readonly manifest: AgentTransactionManifest;
39
+ readonly denial: Exclude<AgentPolicyDecision, {
40
+ allowed: true;
41
+ }>;
42
+ readonly stage: "policy";
43
+ } | {
44
+ readonly approved: false;
45
+ readonly manifest: AgentTransactionManifest;
46
+ readonly paymentFailure: X402PaymentFailure;
47
+ readonly receipt: X402ExternalPaymentReceiptState;
48
+ readonly logSafePaymentMetadata: unknown;
49
+ };
50
+ export interface X402PaymentFailure {
51
+ readonly stage: "verify" | "settle";
52
+ readonly reasonCode: "X402_VERIFY_FAILED" | "X402_SETTLE_FAILED";
53
+ readonly message: string;
54
+ }
55
+ export interface X402PaymentLogMetadataInput {
56
+ readonly paymentId: string;
57
+ readonly paymentPayload: X402PaymentPayload;
58
+ readonly verify?: X402VerifyEvidence;
59
+ readonly settle?: X402SettleEvidence;
60
+ }
61
+ export declare function redactX402PaymentLogMetadata(input: X402PaymentLogMetadataInput): unknown;
62
+ export declare function runX402MockFacilitatorFlow(options: RunX402MockFacilitatorFlowOptions): Promise<X402MockFacilitatorFlowResult>;