@402flow/sdk 0.1.0-alpha.27 → 0.1.0-alpha.28

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 (2) hide show
  1. package/README.md +67 -584
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,8 +1,18 @@
1
1
  # @402flow/sdk
2
2
 
3
- Paid HTTP SDK for AI agents with an inspectable prepare/execute flow.
3
+ Paid API SDK for AI agents, tool hosts, and governed automation.
4
4
 
5
- It uses the 402flow control plane as the governance and execution backend, but the main developer surface is a small client for preparing and executing paid HTTP requests.
5
+ It gives AI agents, tool hosts, and automation services easy access to paid APIs while organizations keep policy, approvals, receipts, and spend controls outside the agent runtime.
6
+
7
+ Use `fetchPaid(...)` when the exact request is already known.
8
+ Use `preparePaidRequest(...)` when the agent needs merchant-published hints and an authoritative `nextAction` before paying.
9
+
10
+ ## Why This SDK
11
+
12
+ - Inspectable paid request flow. Agents and tool hosts can prepare, revise, and execute paid HTTP requests explicitly instead of hiding everything inside one opaque pay-and-fetch call.
13
+ - Control-plane governance. Policy, approvals, receipts, and audit stay centralized instead of being reimplemented in every host.
14
+ - Agent-ready request shaping. `nextAction` gives models and tools a stable contract for revise, execute, or passthrough.
15
+ - Provider-neutral execution. Use the native SDK path or delegate the paid call to Dexter, pay.sh, or a host-owned executor without losing governance value.
6
16
 
7
17
  ## Install
8
18
 
@@ -10,647 +20,120 @@ It uses the 402flow control plane as the governance and execution backend, but t
10
20
  npm install @402flow/sdk
11
21
  ```
12
22
 
13
- The published package supports Node 20+.
23
+ Optional official adapters for third-party payers:
14
24
 
15
- ## Overview
25
+ ```bash
26
+ npm install @402flow/sdk @402flow/sdk-third-party-executors
27
+ ```
16
28
 
17
- `@402flow/sdk` is built for AI agents and other callers that need to inspect, prepare, and execute paid HTTP requests without embedding payment-protocol or governance logic in the host.
29
+ The published package supports Node 20+.
18
30
 
19
- The SDK has one core client, `AgentPayClient`, and three main calls:
31
+ ## Core Surface
20
32
 
21
- | API | Use when | What it does |
33
+ | API | Use it when | What it returns |
22
34
  | --- | --- | --- |
23
- | `fetchPaid(...)` | You already know the request shape | Probes if needed, resolves payment through the control plane, and returns passthrough or success |
24
- | `preparePaidRequest(...)` | You want to inspect before paying | Returns normalized payment terms, request hints, validation issues, and `nextAction` |
35
+ | `fetchPaid(...)` | You already know the request shape | Probe, authorize, pay, and return the merchant response in one path |
36
+ | `preparePaidRequest(...)` | You want to inspect before paying | Payment terms, parameter hints, validation issues, and an authoritative `nextAction` |
25
37
  | `executePreparedRequest(...)` | You already prepared the request | Executes the exact prepared request without re-probing first |
38
+ | `AgentHarness` | Your model host wants a `preparedId` tool contract | The same flow behind a canonical three-tool surface |
26
39
 
27
- Use `fetchPaid(...)` for the simplest direct path.
28
- Use `preparePaidRequest(...)` plus `executePreparedRequest(...)` when the caller needs an explicit inspect, revise, then execute loop.
40
+ ## Quick Start: Host-Controlled Request
29
41
 
30
- The package also includes `AgentHarness`, an optional preparedId-based wrapper for tool hosts. That host-facing wrapper adds a human-readable `costSummary` on prepared results and ships canonical `defaultHarnessInstructions` plus `defaultHarnessToolSpecs` exports so adapters can reuse the same orchestration contract.
31
-
32
- ## Runtime Notes
33
-
34
- Two constraints matter early in real integrations:
35
-
36
- 1. paid prepare and execute flows require replayable request bodies
37
- 2. the current replayable body types are `string` and `URLSearchParams`
38
-
39
- That means JSON payloads should be sent as strings, and form-style payloads should be sent as `URLSearchParams`.
40
-
41
- The SDK exports small helpers for that:
42
+ This first example shows the deterministic application path. Your code already knows which merchant route and request parameters it wants to send, and the SDK handles probing, policy, payment, and receipts around that request.
42
43
 
43
44
  ```ts
44
45
  import {
45
- createFormUrlEncodedBody,
46
+ AgentPayClient,
46
47
  createJsonRequestBody,
47
48
  } from '@402flow/sdk';
48
49
 
49
- const jsonBody = createJsonRequestBody({
50
- prompt: 'foggy coastline',
51
- });
52
-
53
- const formBody = createFormUrlEncodedBody({
54
- prompt: 'foggy coastline',
55
- style: 'noir',
56
- tags: ['coast', 'mist'],
57
- });
58
- ```
59
-
60
- `FormData`, `Blob`, streams, and framework-specific body wrappers are not currently accepted in paid flows because the SDK has to replay the exact request body through preparation and execution. Convert those upstream into a stable string or `URLSearchParams` first.
61
-
62
- ## Create A Client
63
-
64
- Create one `AgentPayClient` per agent identity. The client binds the 402flow control-plane location and the organization plus agent selectors up front, and each request only carries request-specific context.
65
-
66
- ### Bootstrap key
67
-
68
- For most SDK integrations, bootstrap-key auth is the recommended mode. The SDK exchanges it for a short-lived runtime token, caches that token, and refreshes it automatically before expiry.
69
-
70
- ```ts
71
- import { AgentPayClient, createJsonRequestBody } from '@402flow/sdk';
72
-
73
50
  const client = new AgentPayClient({
74
- controlPlaneBaseUrl: 'https://402flow.ai',
75
- organization: 'acme-labs',
76
- agent: 'reporting-worker',
51
+ controlPlaneBaseUrl:
52
+ process.env.X402FLOW_CONTROL_PLANE_BASE_URL ?? 'https://api-staging.402flow.ai',
53
+ organization: process.env.X402FLOW_ORGANIZATION ?? 'acme-labs',
54
+ agent: process.env.X402FLOW_AGENT ?? 'research-worker',
77
55
  auth: {
78
56
  type: 'bootstrapKey',
79
57
  bootstrapKey: process.env.X402FLOW_BOOTSTRAP_KEY ?? '',
80
58
  },
81
59
  });
82
- ```
83
-
84
- ### Runtime token
85
60
 
86
- ```ts
87
- import { AgentPayClient } from '@402flow/sdk';
88
-
89
- const client = new AgentPayClient({
90
- controlPlaneBaseUrl: 'https://402flow.ai',
91
- organization: 'acme-labs',
92
- agent: 'reporting-worker',
93
- auth: {
94
- type: 'runtimeToken',
95
- runtimeToken: process.env.X402FLOW_RUNTIME_TOKEN ?? '',
96
- },
97
- });
98
- ```
99
-
100
- ## Fast Path: `fetchPaid()`
101
-
102
- Call `fetchPaid()` when you already know the merchant URL, method, headers, and body.
103
-
104
- ```ts
105
- try {
106
- const result = await client.fetchPaid(
107
- 'https://merchant.example.com/reports/daily',
108
- {
109
- method: 'POST',
110
- headers: {
111
- 'content-type': 'application/json',
112
- },
113
- body: createJsonRequestBody({
114
- date: '2026-03-25',
115
- }),
116
- },
117
- {
118
- description: 'sync daily paid report',
119
- idempotencyKey: 'daily-report-2026-03-25',
120
- },
121
- );
122
-
123
- const merchantBody = await result.response.json();
124
- console.log('merchant response body:', merchantBody);
125
- } catch (error) {
126
- console.error('paid request failed', error);
127
- throw error;
128
- }
129
- ```
130
-
131
- If the merchant does not require payment for that exact request, the SDK returns a passthrough response. If the merchant returns a payable challenge, the SDK resolves payment through the control plane and returns a durable paid outcome.
132
-
133
- `result.response` is always the merchant HTTP response. SDK-owned payment metadata such as `paidRequestId`, `paymentAttemptId`, `receiptId`, and `receipt` stays on the SDK result instead of being injected into the merchant JSON body.
134
-
135
- ### Optional Attribution
136
-
137
- Most SDK users do not need to send attribution at all.
138
-
139
- The optional `attribution` field is for callers that already know where this paid endpoint came from and want that provenance to survive into control-plane audit, reporting, and later policy analysis.
140
-
141
- Common cases:
142
-
143
- 1. your app found the endpoint through your own registry or catalog
144
- 2. your app imported the endpoint from a saved workspace config
145
- 3. your agent is calling a merchant directly and you want to mark that path as `direct`
146
- 4. your host selected the endpoint from a discovery surface such as Bazaar, Dexter, pay.sh, x402scan, or another catalog
147
-
148
- This field does not make payment execution work. It improves lifecycle explainability.
149
-
150
- If you do not already know the endpoint provenance, omit it.
151
-
152
- ```ts
153
61
  const result = await client.fetchPaid(
154
- 'https://merchant.example.com/reports/daily',
62
+ 'https://demo-merchant-staging.402flow.ai/demo-merchant/research-brief/solana-devnet',
155
63
  {
156
64
  method: 'POST',
157
65
  headers: {
158
66
  'content-type': 'application/json',
159
67
  },
160
68
  body: createJsonRequestBody({
161
- date: '2026-03-25',
69
+ topic: 'sdk integration rollout',
70
+ audience: 'platform engineers',
71
+ format: 'bullets',
162
72
  }),
163
73
  },
164
74
  {
165
- description: 'sync daily paid report',
166
- attribution: {
167
- discoverySource: 'direct',
168
- },
169
- },
170
- );
171
- ```
172
-
173
- For the first attribution slice, `discoverySource` is the main field callers should set when they have a clear answer. The control plane derives an early endpoint-level `resourceIdentity` from the request method and URL, so callers do not need to compute that themselves.
174
-
175
- ### Interpreting Merchant Responses
176
-
177
- The SDK gives you a stable place for payment metadata, but it does not invent a universal fulfilled-response schema for merchant content.
178
-
179
- In practice:
180
-
181
- 1. the SDK result carries durable payment metadata such as `receiptId` and `receipt`
182
- 2. `result.response` carries the merchant fulfillment payload
183
- 3. the merchant contract decides where the useful paid content lives inside that payload
184
-
185
- If you need request-shape guidance before execution, use `preparePaidRequest()` and inspect:
186
-
187
- 1. `prepared.hints` for authoritative request fields, examples, notes, and query/body guidance when the challenge publishes them
188
- 2. `prepared.challengeDetails` for raw merchant challenge data such as resource metadata, accepted payment candidates, and extensions like Bazaar discovery metadata
189
- 3. optional caller-supplied `externalMetadata` as advisory context only
190
-
191
- If you do not have enough contract information to interpret a merchant response safely, return the raw merchant body and explain what is still missing instead of inventing a payload shape.
192
-
193
- ## Preparation Flow
194
-
195
- Use `preparePaidRequest()` when the caller needs a first-class pre-execution result before paying.
196
-
197
- ```ts
198
- import { createJsonRequestBody } from '@402flow/sdk';
199
-
200
- const prepared = await client.preparePaidRequest(
201
- 'https://merchant.example.com/images/generate',
202
- {
203
- method: 'POST',
204
- headers: {
205
- 'content-type': 'application/json',
206
- },
207
- body: createJsonRequestBody({
208
- prompt: 'foggy coastline',
209
- }),
75
+ description: 'generate a staged research brief',
76
+ idempotencyKey: 'sdk-readme-solana-devnet-brief',
210
77
  },
211
78
  );
212
79
 
213
- if (prepared.kind === 'passthrough') {
214
- console.log('merchant did not require payment', prepared.probe?.responseStatus);
215
- } else {
216
- console.log('protocol:', prepared.protocol);
217
- console.log('payment requirement:', prepared.paymentRequirement);
218
- console.log('request hints:', prepared.hints);
219
- }
220
-
221
- console.log('next action:', prepared.nextAction);
222
- console.log('validation issues:', prepared.validationIssues);
80
+ console.log(await result.response.json());
81
+ console.log(result.receiptId);
223
82
  ```
224
83
 
225
- This flow is useful when:
84
+ This is why the request body is filled in directly in code here. `fetchPaid(...)` is the simplest integration path when your application already knows the parameters.
226
85
 
227
- 1. an agent needs request-shape hints before attempting execution
228
- 2. the caller wants normalized payment terms before paying
229
- 3. the caller wants to merge optional `externalMetadata` it already has from another system
86
+ Use `fetchPaid(...)` when the request is already shaped and you want the shortest path.
87
+ Use `preparePaidRequest(...)` when the caller needs to inspect what the merchant published, construct the right request, and execute only when `nextAction === 'execute'`.
230
88
 
231
- The common loop is:
89
+ ## Quick Start: Agent-Driven Request Construction
232
90
 
233
- 1. prepare the request
234
- 2. inspect `kind`, `paymentRequirement`, `hints`, `validationIssues`, and `nextAction`
235
- 3. revise if needed
236
- 4. execute only once the request is understood
91
+ If you want the agent to decide which parameters to send, do not hardcode those decisions into the SDK call site. Instead, expose the SDK through `AgentHarness` or your own tool wrapper and let the agent react to `nextAction`, `validationIssues`, and `hints`.
237
92
 
238
- If your system already has endpoint metadata, you can pass it in as optional context:
93
+ The typical loop is:
239
94
 
240
- ```ts
241
- import { createJsonRequestBody } from '@402flow/sdk';
95
+ 1. the agent proposes a request
96
+ 2. the SDK returns `nextAction`, `validationIssues`, and merchant-published `hints`
97
+ 3. the agent revises the request until `nextAction === 'execute'`
98
+ 4. the host executes the prepared request and reads the stored result before summarizing the outcome
242
99
 
243
- const prepared = await client.preparePaidRequest(
244
- 'https://merchant.example.com/images/generate',
245
- {
246
- method: 'POST',
247
- headers: {
248
- 'content-type': 'application/json',
249
- },
250
- body: createJsonRequestBody({
251
- prompt: 'foggy coastline',
252
- }),
253
- },
254
- {
255
- externalMetadata: {
256
- requestBodyType: 'json',
257
- requestBodyFields: [
258
- {
259
- name: 'prompt',
260
- type: 'string',
261
- required: true,
262
- },
263
- ],
264
- },
265
- },
266
- );
267
- ```
268
-
269
- `externalMetadata` is optional caller context. It improves preparation when the caller already has structured endpoint knowledge, but it is not required for normal SDK use.
270
-
271
- `attribution` is separate from `externalMetadata`.
272
-
273
- 1. `externalMetadata` helps the SDK understand request shape before execution
274
- 2. `attribution` helps the control plane explain where the paid endpoint came from after execution
275
-
276
- Use `externalMetadata` for request hints. Use `attribution` for provenance. Either may be omitted.
277
-
278
- ### What `ready` Means
279
-
280
- `ready` means this exact request can proceed through governed paid execution as-is; it does not mean the SDK has inferred the best task parameters for you.
100
+ That is the path to use when the model is supposed to fill request parameters properly instead of relying on host code that already knows the answer.
281
101
 
282
- That distinction matters:
102
+ ## AgentHarness
283
103
 
284
- 1. `ready` is about protocol and payment executability
285
- 2. `validationIssues` and `hints` are about request-shape guidance
286
- 3. choosing semantically correct task parameters still belongs to the caller or agent
104
+ `AgentHarness` is the optional model-host wrapper for the same inspect-then-execute loop.
287
105
 
288
- ### Execute A Prepared Request
106
+ It stores prepared state behind a `preparedId`, exposes a canonical three-tool contract, and keeps the rule that matters most stable across hosts:
289
107
 
290
- If preparation returns `kind === 'ready'`, execute that exact prepared request with `executePreparedRequest(prepared, ...)`.
291
-
292
- ```ts
293
- import { createJsonRequestBody } from '@402flow/sdk';
294
-
295
- const prepared = await client.preparePaidRequest(
296
- 'https://merchant.example.com/images/generate',
297
- {
298
- method: 'POST',
299
- headers: {
300
- 'content-type': 'application/json',
301
- },
302
- body: createJsonRequestBody({
303
- prompt: 'foggy coastline',
304
- }),
305
- },
306
-
307
- const result = await client.executePreparedRequest(prepared, {
308
- description: 'generate paid image',
309
- attribution: {
310
- discoverySource: 'manual',
311
- },
312
- });
313
- );
314
-
315
- if (prepared.kind === 'ready') {
316
- const result = await client.executePreparedRequest(prepared, {
317
- description: 'generate image',
318
- idempotencyKey: 'image-generate-foggy-coastline',
319
- });
320
-
321
- console.log('paid response status:', result.response.status);
322
- }
323
- ```
324
-
325
- If preparation does not return `kind === 'ready'`, that is not necessarily an error. It means this exact request did not currently resolve to a payable executable path. The caller can accept that result, run a normal non-paid path, or revise and prepare again.
326
-
327
- ### Delegated Execution With Custom Executors
328
-
329
- `executePreparedRequest()` now supports governed delegated execution through a caller-supplied executor interface.
330
-
331
- This lets the SDK keep authorization, policy, receipts, and final outcome normalization in the 402flow control plane while handing the final paid merchant call to a provider-specific executor owned by the host app or a separate integration package.
332
-
333
- The core SDK stays provider-neutral. That means third-party payment clients such as Dexter or pay.sh should live in the host app's own dependency graph, not inside `@402flow/sdk` itself.
334
-
335
- The flow is:
336
-
337
- 1. prepare the exact request locally with `preparePaidRequest()`
338
- 2. ask the control plane to authorize delegated execution
339
- 3. if authorized, let the supplied executor perform the actual paid merchant request
340
- 4. finalize the normalized executor result back through the control plane
341
- 5. return the same stable SDK result or `FetchPaidError` contract the caller already uses elsewhere
342
-
343
- If the control plane denies authorization, the delegated executor is never invoked.
344
-
345
- ```ts
346
- import {
347
- createJsonRequestBody,
348
- type PreparedRequestExecutorInput,
349
- type PreparedRequestExecutor,
350
- } from '@402flow/sdk';
351
-
352
- async function callDexter(
353
- prepared: PreparedRequestExecutorInput['prepared'],
354
- ) {
355
- // Replace this with your real Dexter SDK call.
356
- return {
357
- status: 200,
358
- body: { ok: true },
359
- settlementReference: 'settlement-ref-1',
360
- paymentReference: 'payment-ref-1',
361
- };
362
- }
363
-
364
- function mapDexterResult(
365
- prepared: PreparedRequestExecutorInput['prepared'],
366
- result: Awaited<ReturnType<typeof callDexter>>,
367
- ) {
368
- return {
369
- protocol: prepared.protocol,
370
- executionStatus: 'succeeded' as const,
371
- settlementEvidenceClass: 'settled' as const,
372
- merchantOutcome: 'success_response' as const,
373
- merchantResponse: {
374
- status: result.status,
375
- headers: {
376
- 'content-type': 'application/json',
377
- },
378
- body: JSON.stringify(result.body),
379
- },
380
- settlementReference: result.settlementReference,
381
- paymentReference: result.paymentReference,
382
- };
383
- }
384
-
385
- const dexterExecutor: PreparedRequestExecutor = {
386
- provider: 'dexter',
387
- async execute({ prepared }) {
388
- const dexterResult = await callDexter(prepared);
389
- return mapDexterResult(prepared, dexterResult);
390
- },
391
- };
392
-
393
- const prepared = await client.preparePaidRequest(
394
- 'https://merchant.example.com/images/generate',
395
- {
396
- method: 'POST',
397
- headers: {
398
- 'content-type': 'application/json',
399
- },
400
- body: createJsonRequestBody({
401
- prompt: 'foggy coastline',
402
- }),
403
- },
404
- );
405
-
406
- if (prepared.kind === 'ready') {
407
- const result = await client.executePreparedRequest(prepared, {
408
- description: 'generate image through delegated execution',
409
- executionProvider: 'dexter',
410
- executor: dexterExecutor,
411
- });
412
-
413
- console.log('paid response status:', result.response.status);
414
- }
415
- ```
416
-
417
- This snippet shows the intended split directly: your host-owned code does the provider call and maps it into the delegated execution contract, while the SDK still owns authorize, finalize, and the outward result shape. For a real host-owned Dexter integration that performs the paid call, see `third-party-executors/examples/dexter-delegated-executor.mjs`.
418
-
419
- Responsibility split:
420
-
421
- 1. the SDK asks the control plane for delegated authorization
422
- 2. if authorized, the SDK invokes your executor
423
- 3. your executor performs the provider-specific paid request and returns `SdkDelegatedExecutionResult`
424
- 4. the SDK finalizes that result with the control plane
425
- 5. the SDK returns the same outward `PaidResponse` or `FetchPaidError` contract as the direct path
426
-
427
- If your host app wants to execute through Dexter, pay.sh, or another provider, that integration should install and own the third-party SDK directly. `@402flow/sdk` only owns the executor contract and the governed authorize/finalize flow.
428
-
429
- Official supported adapters live in the separate `@402flow/sdk-third-party-executors` package so the main `@402flow/sdk` install path stays provider-neutral.
430
-
431
- ```bash
432
- npm install @402flow/sdk @402flow/sdk-third-party-executors
433
- ```
434
-
435
- If you pin versions explicitly, pin both packages to the same version:
436
-
437
- ```bash
438
- npm install @402flow/sdk@<version> @402flow/sdk-third-party-executors@<version>
439
- ```
440
-
441
- The adapter package is versioned and supported in lockstep with `@402flow/sdk`, so keep the two package versions aligned. In this repo, the published adapter package source lives under `third-party-executors/`.
442
-
443
- Current official adapters:
444
-
445
- | Adapter | Current scope | Key dependencies |
446
- | --- | --- | --- |
447
- | Dexter | Host-owned delegated execution against Dexter's paid request client | `@dexterai/x402` |
448
- | pay.sh | Host-owned x402 Solana exact delegated execution | `@x402/core`, `@x402/svm`, `@solana/kit` |
449
-
450
- The pay.sh adapter intentionally does not depend on `@solana/pay` today. The current proof targets pay.sh's x402 Solana exact flow, so challenge parsing, signed payment payload creation, and paid response parsing come from `@x402/core` plus `@x402/svm`, while `@solana/kit` only supplies the signer. `@solana/pay` becomes relevant when the repo adds a separate Solana Pay or MPP-specific adapter path.
451
-
452
- For a repo-wide verification pass from the SDK root, run:
453
-
454
- ```bash
455
- npm run install:all
456
- npm run check:all
457
- ```
458
-
459
- `npm run install:all` installs both the main SDK package and the separate `third-party-executors` package from the SDK root. `npm run check` still validates only the main SDK package. `npm run check:all` runs the main SDK checks first, then runs the separate `third-party-executors` package checks from the top level.
460
-
461
- For a repo-local host-owned Dexter example, run:
462
-
463
- ```bash
464
- cd third-party-executors
465
- npm install
466
- export DEXTER_EVM_PRIVATE_KEY="..."
467
-
468
- npm run example:dexter-delegated-executor -- \
469
- "https://merchant.example.com/paid-endpoint" \
470
- '{"topic":"sdk integration rollout","audience":"platform engineers","format":"bullets"}'
471
- ```
472
-
473
- For a repo-local host-owned pay.sh x402 example, run:
474
-
475
- ```bash
476
- cd third-party-executors
477
- npm install
478
- npm run example:pay-sh-delegated-executor -- --help
479
- ```
480
-
481
- The pay.sh example expects the standard SDK auth environment plus a local Solana signer path in `PAY_SH_SOLANA_KEYPAIR_PATH`. Run the `--help` form first to see the full required environment and argument contract.
482
-
483
- ## Prepared Result Semantics
484
-
485
- `preparePaidRequest()` separates request checking from paid execution.
486
-
487
- The preparation result distinguishes four important things:
488
-
489
- 1. `paymentRequirement`: normalized payment terms derived from the merchant challenge when available
490
- 2. `hints`: request-shape hints such as body fields, query params, path params, descriptions, examples, and notes
491
- 3. `validationIssues`: structured remediation diagnostics derived from the current request and defensible preparation inputs
492
- 4. `nextAction`: the authoritative machine contract for what to do next, such as `execute`, `revise_request`, or `treat_as_passthrough`
493
-
494
- Each prepared hint carries `attribution` so callers can distinguish live merchant-authoritative data from advisory caller-supplied metadata.
495
-
496
- In this model, payment terms come from the merchant challenge, optional request-shape enrichment comes from `externalMetadata`, and live confirmation is represented by the prepared `probe` result.
497
-
498
- ## Result And Error Semantics
499
-
500
- `fetchPaid()` and `executePreparedRequest()` either:
501
-
502
- 1. return a passthrough response when the request did not require payment
503
- 2. return `success` with a receipt when the paid request completed successfully
504
- 3. throw `FetchPaidError` for all non-success paid outcomes
505
-
506
- `FetchPaidError` kinds are:
507
-
508
- 1. `denied`
509
- 2. `preflight_failed`
510
- 3. `execution_pending`
511
- 4. `execution_failed`
512
- 5. `paid_fulfillment_failed`
513
- 6. `execution_inconclusive`
514
- 7. `request_failed`
515
-
516
- Receipt notes:
517
-
518
- 1. `receipt.status = 'confirmed'` means the control plane has chain-backed settlement attribution for the paid attempt
519
- 2. `receipt.status = 'provisional'` means the paid outcome was supportable by merchant-provided evidence, but final settlement attribution is still pending reconciliation
520
- 3. callers should treat provisional receipts as payment-attempt evidence, not as proof of final settlement
521
- 4. `idempotencyKey` is optional for normal SDK use, which keeps low-value one-off integrations simple
522
- 5. if you safely retry the same logical paid request with the same `idempotencyKey`, the SDK returns the same durable paid outcome instead of creating a second paid attempt
523
- 6. if you omit `idempotencyKey`, the request still works, but you should not assume replay-safe duplicate suppression across retries
524
- 7. use `idempotencyKey` for retrying callers, automation loops, and higher-load integrations where duplicate suppression matters
525
-
526
- ## Receipt Lookup
527
-
528
- ```ts
529
- const receipt = await client.lookupReceipt('receipt-id');
530
-
531
- console.log(receipt.receipt.status);
532
- ```
533
-
534
- ## Canonical Host Metadata
535
-
536
- If you are building a tool host, do not copy orchestration rules into ad hoc prompts. Import the canonical host-agnostic metadata from the SDK and adapt it to your model provider.
108
+ `nextAction` is authoritative.
537
109
 
538
110
  ```ts
539
111
  import {
112
+ AgentHarness,
540
113
  defaultHarnessInstructions,
541
114
  defaultHarnessToolSpecs,
542
115
  } from '@402flow/sdk';
543
116
 
544
- console.log(defaultHarnessInstructions);
545
- console.log(defaultHarnessToolSpecs);
546
- ```
547
-
548
- `defaultHarnessToolSpecs` defines the canonical three-tool contract:
549
-
550
- 1. `prepare_paid_request`
551
- 2. `execute_prepared_request`
552
- 3. `get_execution_result`
553
-
554
- Those descriptions encode the orchestration rules, including:
555
-
556
- 1. `nextAction` is authoritative
557
- 2. execute only after `nextAction === 'execute'`
558
- 3. read the stored execution result before summarizing the outcome
559
-
560
- Keep provider-specific tool objects in your host adapter, not in the SDK core package.
561
-
562
- ## Tiny OpenAI Tools Host
563
-
564
- If you want a minimal real host integration instead of the larger evaluation harness, use the tiny OpenAI Responses example in `examples/openai-tools-quickstart.mjs`.
565
-
566
- For repo-local example runs, create `.env` from `.env.example` in the SDK root. The SDK examples load that file directly, so scenario runs stay self-contained in this repo whether they point at a local control plane or a hosted environment.
117
+ const harness = new AgentHarness({ client });
567
118
 
568
- It keeps the host story narrow:
569
-
570
- 1. create an `AgentPayClient`
571
- 2. wrap it with optional `AgentHarness` so tool calls can pass `preparedId`
572
- 3. build provider-specific tool objects from `defaultHarnessToolSpecs`
573
- 4. expose `prepare_paid_request`, `execute_prepared_request`, and `get_execution_result`
574
-
575
- Run it with one prompt. Either populate `.env` from `.env.example`, or export the same values in your shell for a one-off run:
576
-
577
- ```bash
578
- cp .env.example .env
579
- export OPENAI_API_KEY="..."
580
- export X402FLOW_CONTROL_PLANE_BASE_URL="https://402flow.ai"
581
- export X402FLOW_ORGANIZATION="acme-labs"
582
- export X402FLOW_AGENT="reporting-worker"
583
- export X402FLOW_BOOTSTRAP_KEY="..."
584
-
585
- npm run example:openai-tools-quickstart -- \
586
- "Prepare and execute a paid POST request to http://127.0.0.1:4123/demo-merchant/research-brief/solana-devnet with JSON body {\"topic\":\"sdk integration rollout\",\"audience\":\"platform engineers\",\"format\":\"bullets\"}"
587
- ```
588
-
589
- Use this when you want the shortest real host integration path. For now, this quickstart defaults to the self-hosted first-party demo merchant in `agent-pay` so SDK adoption starts from the canonical proving ground. Switch the merchant URL to the public AWS demo-merchant host once that deployment is live.
590
-
591
- ## Optional `AgentHarness`
592
-
593
- `AgentHarness` is an optional preparedId-based wrapper for tool hosts that do not want to manage in-flight prepared request objects themselves. It is a convenience layer on top of `AgentPayClient`, not a required abstraction.
594
-
595
- Key behavior:
596
-
597
- 1. preparations are stored in memory behind a `preparedId`
598
- 2. a newer active preparation for the same method plus origin plus pathname supersedes the older one
599
- 3. duplicate execute calls for the same consumed `preparedId` are rejected locally as already consumed
600
- 4. hosts should call `getExecutionResult(preparedId)` after execution to read the durable stored outcome
601
- 5. host-facing prepare results include `costSummary`, for example `Costs 0.001000 USDC on Base Sepolia (exact).`
602
-
603
- For harness usage, presets, transcripts, and scenario packs, see:
604
-
605
- 1. [docs/evaluation-harness.md](docs/evaluation-harness.md)
606
- 2. [docs/harness-scenarios.md](docs/harness-scenarios.md)
607
-
608
- For the canonical cross-repo core proving sweep (first-party + mock), run:
609
-
610
- ```bash
611
- pnpm scenario:core
612
- ```
613
-
614
- `pnpm scenario:core` runs first-party + mock in a single pass and keeps one combined artifact set under `tmp/`.
615
-
616
- Additional scenario commands:
617
-
618
- 1. `pnpm scenario:core`: core proving sweep (first-party + mock)
619
- 2. `pnpm scenario:first-party`: first-party self-hosted demo-merchant scenarios only
620
- 3. `pnpm scenario:third-party`: third-party merchant compatibility scenarios only
621
- 4. `pnpm scenario:mock`: fixture-only mock outcomes, no live merchant required
622
-
623
- `pnpm scenario:all` now literally runs all scenarios (first-party + third-party + mock).
624
-
625
- As the public AWS demo-merchant host comes online, keep these command boundaries and flip first-party scenario fixtures with one env variable:
626
-
627
- ```bash
628
- export X402FLOW_FIRST_PARTY_MERCHANT_BASE_URL="https://demo-merchant.402flow.ai"
119
+ console.log(defaultHarnessInstructions);
120
+ console.log(defaultHarnessToolSpecs.map((spec) => spec.name));
121
+ // [ 'prepare_paid_request', 'execute_prepared_request', 'get_execution_result' ]
629
122
  ```
630
123
 
631
- When unset, first-party fixtures default to the self-hosted demo merchant at `http://127.0.0.1:4123`.
124
+ Use this path when you want the model to construct a correct request instead of guessing its way into a paid call.
632
125
 
633
- ## Publish
126
+ ## Governed Third-Party Execution
634
127
 
635
- Publish the main SDK package first. Publish `@402flow/sdk-third-party-executors` second, after the matching SDK version is available.
128
+ 402flow can execute paid x402 requests natively, or you can delegate final payment execution to Dexter, pay.sh, or another executor. In both cases, 402flow authorizes the attempt before execution and finalizes the normalized result afterward, keeping policy, approvals, receipts, and audit centralized.
636
129
 
637
- Main SDK package:
638
130
 
639
- ```bash
640
- npm run install:all
641
- npm run check:all
642
- npm run pack:check
643
- npm publish --access public
644
- ```
131
+ Official adapters live in `@402flow/sdk-third-party-executors`, and the repo-local source for those adapters lives under `third-party-executors/`.
645
132
 
646
- `npm publish` now also runs `npm run check:all` through the root `prepublishOnly` hook, so the repo-wide test pass is enforced before publish even if you skip that step manually.
133
+ ## Further Reading
647
134
 
648
- Adapter package:
649
-
650
- ```bash
651
- cd third-party-executors
652
- npm install
653
- npm run check
654
- npm run pack:check
655
- npm publish --access public
656
- ```
135
+ - [Detailed SDK guide](docs/sdk-guide.md)
136
+ - [Evaluation harness](docs/evaluation-harness.md)
137
+ - [Harness scenarios](docs/harness-scenarios.md)
138
+ - [Dexter and pay.sh executors](third-party-executors/README.md)
139
+ - [Publishing and release checks](docs/releasing.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@402flow/sdk",
3
- "version": "0.1.0-alpha.27",
3
+ "version": "0.1.0-alpha.28",
4
4
  "description": "Paid HTTP SDK for AI agents with an inspectable prepare/execute flow.",
5
5
  "private": false,
6
6
  "type": "module",