@402flow/sdk 0.1.0-alpha.3 → 0.1.0-alpha.30
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.
- package/README.md +123 -28
- package/dist/agent-harness.d.ts +164 -0
- package/dist/agent-harness.js +487 -0
- package/dist/agent-harness.js.map +1 -0
- package/dist/challenge-detection.d.ts +17 -5
- package/dist/challenge-detection.js +65 -102
- package/dist/challenge-detection.js.map +1 -1
- package/dist/contracts.d.ts +5939 -500
- package/dist/contracts.js +337 -20
- package/dist/contracts.js.map +1 -1
- package/dist/executors.d.ts +27 -0
- package/dist/executors.js +2 -0
- package/dist/executors.js.map +1 -0
- package/dist/harness-instructions.d.ts +15 -0
- package/dist/harness-instructions.js +28 -0
- package/dist/harness-instructions.js.map +1 -0
- package/dist/http-utils.d.ts +1 -0
- package/dist/http-utils.js +12 -0
- package/dist/http-utils.js.map +1 -0
- package/dist/index.d.ts +117 -8
- package/dist/index.js +1186 -56
- package/dist/index.js.map +1 -1
- package/dist/version.d.ts +4 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/package.json +21 -11
- package/dist/challenge-types.d.ts +0 -57
- package/dist/challenge-types.js +0 -111
- package/dist/challenge-types.js.map +0 -1
- package/dist/x402-v1.d.ts +0 -4
- package/dist/x402-v1.js +0 -53
- package/dist/x402-v1.js.map +0 -1
- package/dist/x402-v2.d.ts +0 -4
- package/dist/x402-v2.js +0 -52
- package/dist/x402-v2.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
# @402flow/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Paid API SDK for AI agents, tool hosts, and governed automation.
|
|
4
|
+
|
|
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.
|
|
4
16
|
|
|
5
17
|
## Install
|
|
6
18
|
|
|
@@ -8,50 +20,133 @@ Node.js SDK for making paid requests through the 402flow control plane.
|
|
|
8
20
|
npm install @402flow/sdk
|
|
9
21
|
```
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
This is the normal install path. Use `@402flow/sdk` by itself when you want the native 402flow payment flow.
|
|
24
|
+
|
|
25
|
+
Optional official adapters for third-party payers:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install @402flow/sdk @402flow/sdk-third-party-executors
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Install `@402flow/sdk-third-party-executors` only when you want delegated execution through Dexter or pay.sh instead of the native 402flow path.
|
|
32
|
+
|
|
33
|
+
The published package supports Node 20+.
|
|
34
|
+
|
|
35
|
+
## Core Surface
|
|
36
|
+
|
|
37
|
+
| API | Use it when | What it returns |
|
|
38
|
+
| --- | --- | --- |
|
|
39
|
+
| `fetchPaid(...)` | You already know the request shape | Probe the merchant when no challenge is supplied, then authorize, pay, and return the merchant response |
|
|
40
|
+
| `preparePaidRequest(...)` | You want to inspect before paying | Payment terms, parameter hints, validation issues, and an authoritative `nextAction` |
|
|
41
|
+
| `executePreparedRequest(...)` | You already prepared the request | Executes the exact prepared request without re-probing first |
|
|
42
|
+
| `AgentHarness` | Your model host wants a `preparedId` tool contract | The same flow behind a process-local in-memory three-tool surface |
|
|
12
43
|
|
|
13
|
-
|
|
44
|
+
## Quick Start: Host-Controlled Request
|
|
45
|
+
|
|
46
|
+
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.
|
|
14
47
|
|
|
15
48
|
```ts
|
|
16
|
-
import {
|
|
49
|
+
import {
|
|
50
|
+
AgentPayClient,
|
|
51
|
+
createJsonRequestBody,
|
|
52
|
+
} from '@402flow/sdk';
|
|
17
53
|
|
|
18
54
|
const client = new AgentPayClient({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
55
|
+
controlPlaneBaseUrl:
|
|
56
|
+
process.env.X402FLOW_CONTROL_PLANE_BASE_URL ?? 'https://api-staging.402flow.ai',
|
|
57
|
+
organization: process.env.X402FLOW_ORGANIZATION ?? 'acme-labs',
|
|
58
|
+
agent: process.env.X402FLOW_AGENT ?? 'research-worker',
|
|
59
|
+
auth: {
|
|
60
|
+
type: 'bootstrapKey',
|
|
61
|
+
bootstrapKey: process.env.X402FLOW_BOOTSTRAP_KEY ?? '',
|
|
62
|
+
},
|
|
24
63
|
});
|
|
64
|
+
|
|
65
|
+
const result = await client.fetchPaid(
|
|
66
|
+
'https://demo-merchant-staging.402flow.ai/demo-merchant/research-brief/solana-devnet',
|
|
67
|
+
{
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: {
|
|
70
|
+
'content-type': 'application/json',
|
|
71
|
+
},
|
|
72
|
+
body: createJsonRequestBody({
|
|
73
|
+
topic: 'sdk integration rollout',
|
|
74
|
+
audience: 'platform engineers',
|
|
75
|
+
format: 'bullets',
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
description: 'generate a staged research brief',
|
|
80
|
+
idempotencyKey: 'sdk-readme-solana-devnet-brief',
|
|
81
|
+
},
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
console.log(await result.response.json());
|
|
85
|
+
console.log(result.receiptId);
|
|
25
86
|
```
|
|
26
87
|
|
|
27
|
-
|
|
88
|
+
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.
|
|
89
|
+
|
|
90
|
+
Important probe semantics: when you do not supply a merchant challenge, both `fetchPaid(...)` and `preparePaidRequest(...)` send the original request to the merchant first to discover whether payment is required. That initial merchant probe happens before any control-plane authorization or payment attempt. For non-idempotent `POST` routes, use this only against endpoints that are explicitly safe to probe or after you already have the merchant challenge from another step.
|
|
91
|
+
|
|
92
|
+
Use `fetchPaid(...)` when the request is already shaped and you want the shortest path.
|
|
93
|
+
Use `preparePaidRequest(...)` when the caller needs to inspect what the merchant published, construct the right request, and execute only when `nextAction === 'execute'`.
|
|
94
|
+
|
|
95
|
+
## Quick Start: Agent-Driven Request Construction
|
|
96
|
+
|
|
97
|
+
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`.
|
|
98
|
+
|
|
99
|
+
The typical loop is:
|
|
100
|
+
|
|
101
|
+
1. the agent proposes a request
|
|
102
|
+
2. the SDK returns `nextAction`, `validationIssues`, and merchant-published `hints`
|
|
103
|
+
3. the agent revises the request until `nextAction === 'execute'`
|
|
104
|
+
4. the host executes the prepared request and reads the stored result before summarizing the outcome
|
|
105
|
+
|
|
106
|
+
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.
|
|
107
|
+
|
|
108
|
+
## AgentHarness
|
|
109
|
+
|
|
110
|
+
`AgentHarness` is the optional model-host wrapper for the same inspect-then-execute loop.
|
|
111
|
+
|
|
112
|
+
It stores process-local in-memory prepared state behind a `preparedId`, exposes a canonical three-tool contract, and keeps the rule that matters most stable across hosts:
|
|
113
|
+
|
|
114
|
+
`nextAction` is authoritative.
|
|
28
115
|
|
|
29
116
|
```ts
|
|
30
|
-
import {
|
|
117
|
+
import {
|
|
118
|
+
AgentHarness,
|
|
119
|
+
defaultHarnessInstructions,
|
|
120
|
+
defaultHarnessToolSpecs,
|
|
121
|
+
} from '@402flow/sdk';
|
|
31
122
|
|
|
32
|
-
const
|
|
33
|
-
controlPlaneBaseUrl: 'https://402flow.ai',
|
|
34
|
-
auth: {
|
|
35
|
-
type: 'bootstrapKey',
|
|
36
|
-
bootstrapKey: process.env.AGENT_PAY_BOOTSTRAP_KEY ?? '',
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
```
|
|
123
|
+
const harness = new AgentHarness({ client });
|
|
40
124
|
|
|
41
|
-
|
|
125
|
+
console.log(defaultHarnessInstructions);
|
|
126
|
+
console.log(defaultHarnessToolSpecs.map((spec) => spec.name));
|
|
127
|
+
// [ 'prepare_paid_request', 'execute_prepared_request', 'get_execution_result' ]
|
|
128
|
+
```
|
|
42
129
|
|
|
43
|
-
|
|
130
|
+
Use this path when you want the model to construct a correct request instead of guessing its way into a paid call.
|
|
44
131
|
|
|
45
|
-
|
|
132
|
+
`AgentHarness` is a convenience wrapper for single-process hosts. It is not a durable cross-process orchestration store.
|
|
46
133
|
|
|
47
|
-
|
|
134
|
+
## Governed Third-Party Execution
|
|
48
135
|
|
|
49
|
-
|
|
136
|
+
402flow can execute paid x402 requests natively, or you can delegate final payment execution to Dexter, pay.sh, or another executor. Once a payable challenge is already known, 402flow authorizes the paid attempt before execution and finalizes the normalized result afterward, keeping policy, approvals, receipts, and audit centralized.
|
|
50
137
|
|
|
51
|
-
|
|
138
|
+
Official adapters live in `@402flow/sdk-third-party-executors`, and the repo-local source for those adapters lives under `third-party-executors/`. Import the provider-specific subpath you actually use:
|
|
52
139
|
|
|
53
|
-
|
|
140
|
+
```ts
|
|
141
|
+
import { createDexterExecutor } from '@402flow/sdk-third-party-executors/dexter';
|
|
142
|
+
// or:
|
|
143
|
+
import { createPayShExecutor } from '@402flow/sdk-third-party-executors/pay-sh';
|
|
144
|
+
```
|
|
54
145
|
|
|
55
|
-
##
|
|
146
|
+
## Further Reading
|
|
56
147
|
|
|
57
|
-
|
|
148
|
+
- [Detailed SDK guide](docs/sdk-guide.md)
|
|
149
|
+
- [Evaluation harness](docs/evaluation-harness.md)
|
|
150
|
+
- [Harness scenarios](docs/harness-scenarios.md)
|
|
151
|
+
- [Dexter and pay.sh executors](third-party-executors/README.md)
|
|
152
|
+
- [Publishing and release checks](docs/releasing.md)
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AgentHarness provides a small in-memory orchestration layer on top of
|
|
3
|
+
* AgentPayClient for hosts that prefer preparedId-based handoffs over holding
|
|
4
|
+
* full prepared request objects.
|
|
5
|
+
*
|
|
6
|
+
* The intended host-facing flow is:
|
|
7
|
+
*
|
|
8
|
+
* 1. prepare a candidate request and receive a preparedId plus the preparation summary
|
|
9
|
+
* 2. execute later by preparedId once nextAction is execute
|
|
10
|
+
* 3. read back the stored execution result by preparedId
|
|
11
|
+
*
|
|
12
|
+
* This is useful for tool-driven hosts such as OpenAI tools, MCP servers,
|
|
13
|
+
* Claude tools, LangGraph nodes, or custom orchestrators where passing a small
|
|
14
|
+
* opaque id between turns is easier than preserving the full prepared object.
|
|
15
|
+
*
|
|
16
|
+
* This file implements only a convenience layer. The core SDK contract remains
|
|
17
|
+
* AgentPayClient with preparePaidRequest() and executePreparedRequest().
|
|
18
|
+
*
|
|
19
|
+
* Important behavior:
|
|
20
|
+
* - State is kept only in memory inside this process.
|
|
21
|
+
* - Prepared requests expire after a TTL.
|
|
22
|
+
* - A newer active preparation for the same method + origin + pathname supersedes
|
|
23
|
+
* the older one.
|
|
24
|
+
* - Execution is rejected locally unless the stored preparation is still active,
|
|
25
|
+
* kind === 'ready', and nextAction === 'execute'.
|
|
26
|
+
*/
|
|
27
|
+
import type { PaidRequestChallenge, SdkExternalMetadata, SdkMerchantResponse, SdkPreparedChallengeDetails, SdkPreparedNextAction, SdkPreparedPaidRequest, SdkPreparedPaymentRequirement, SdkPreparedRequestHints, SdkPreparedValidationIssue } from './contracts.js';
|
|
28
|
+
import type { AgentPayClient, ExecutePreparedRequest, FetchPaidFailureResponse, PaidResponse } from './index.js';
|
|
29
|
+
export type AgentHarnessPreparedState = 'active' | 'consumed' | 'expired' | 'superseded';
|
|
30
|
+
/** Local rejection reasons produced by the harness before the SDK is called. */
|
|
31
|
+
export type AgentHarnessRejectionCode = 'missing_prepared_id' | 'unknown_prepared_id' | 'expired_prepared_id' | 'prepared_request_superseded' | 'prepared_request_consumed' | 'prepared_request_not_ready' | 'prepared_request_not_executable';
|
|
32
|
+
/** Typed error used internally to convert local state failures into stable results. */
|
|
33
|
+
export declare class AgentHarnessError extends Error {
|
|
34
|
+
readonly code: AgentHarnessRejectionCode;
|
|
35
|
+
readonly preparedId: string | undefined;
|
|
36
|
+
constructor(code: AgentHarnessRejectionCode, message: string, preparedId?: string);
|
|
37
|
+
}
|
|
38
|
+
/** Host-facing input for the harness prepare step. */
|
|
39
|
+
export type AgentHarnessPrepareInput = {
|
|
40
|
+
url: string;
|
|
41
|
+
method?: string;
|
|
42
|
+
headers?: Record<string, string>;
|
|
43
|
+
body?: string;
|
|
44
|
+
externalMetadata?: SdkExternalMetadata;
|
|
45
|
+
};
|
|
46
|
+
/** Host-facing input for the harness execute step. */
|
|
47
|
+
export type AgentHarnessExecuteInput = {
|
|
48
|
+
preparedId: string;
|
|
49
|
+
executionContext?: ExecutePreparedRequest;
|
|
50
|
+
};
|
|
51
|
+
/** Exact immutable execution payload stored behind a preparedId. */
|
|
52
|
+
export type AgentHarnessExecutionBinding = {
|
|
53
|
+
method: string;
|
|
54
|
+
url: string;
|
|
55
|
+
headers: Record<string, string>;
|
|
56
|
+
body?: string;
|
|
57
|
+
bodyHash?: string;
|
|
58
|
+
challenge?: {
|
|
59
|
+
protocol: PaidRequestChallenge['protocol'];
|
|
60
|
+
headers: Record<string, string>;
|
|
61
|
+
body?: unknown;
|
|
62
|
+
};
|
|
63
|
+
merchantOrigin: string;
|
|
64
|
+
};
|
|
65
|
+
/** Summary returned to the host after preparation succeeds. */
|
|
66
|
+
export type AgentHarnessPreparedSummary = {
|
|
67
|
+
preparedId: string;
|
|
68
|
+
state: 'active';
|
|
69
|
+
kind: SdkPreparedPaidRequest['kind'];
|
|
70
|
+
protocol: SdkPreparedPaidRequest['protocol'];
|
|
71
|
+
costSummary?: string;
|
|
72
|
+
challengeDetails?: SdkPreparedChallengeDetails;
|
|
73
|
+
paymentRequirement?: SdkPreparedPaymentRequirement;
|
|
74
|
+
hints: SdkPreparedRequestHints;
|
|
75
|
+
probe?: SdkPreparedPaidRequest['probe'];
|
|
76
|
+
validationIssues: SdkPreparedValidationIssue[];
|
|
77
|
+
nextAction: SdkPreparedNextAction;
|
|
78
|
+
expiresAt: string;
|
|
79
|
+
};
|
|
80
|
+
/** Stored summary for an SDK-backed paid execution outcome. */
|
|
81
|
+
export type AgentHarnessExecutedResult = {
|
|
82
|
+
preparedId: string;
|
|
83
|
+
harnessDisposition: 'executed';
|
|
84
|
+
sdkOutcomeKind: PaidResponse['kind'] | FetchPaidFailureResponse['kind'];
|
|
85
|
+
status: number;
|
|
86
|
+
merchantResponse: SdkMerchantResponse;
|
|
87
|
+
receiptId?: string;
|
|
88
|
+
paidRequestId?: string;
|
|
89
|
+
paymentAttemptId?: string;
|
|
90
|
+
reason?: string;
|
|
91
|
+
policyReviewEventId?: string;
|
|
92
|
+
};
|
|
93
|
+
/** Stored summary for a harness-local rejection outcome. */
|
|
94
|
+
export type AgentHarnessRejectedResult = {
|
|
95
|
+
preparedId: string;
|
|
96
|
+
harnessDisposition: 'rejected';
|
|
97
|
+
rejectionCode: AgentHarnessRejectionCode;
|
|
98
|
+
message: string;
|
|
99
|
+
};
|
|
100
|
+
export type AgentHarnessExecutionResult = AgentHarnessExecutedResult | AgentHarnessRejectedResult;
|
|
101
|
+
/** Lookup shape returned when a host asks for the durable result of a preparedId. */
|
|
102
|
+
export type AgentHarnessExecutionLookup = {
|
|
103
|
+
preparedId: string;
|
|
104
|
+
state: AgentHarnessPreparedState;
|
|
105
|
+
supersededByPreparedId?: string;
|
|
106
|
+
executionResult?: AgentHarnessExecutionResult;
|
|
107
|
+
};
|
|
108
|
+
/** Full internal record shape exposed for debugging and test inspection. */
|
|
109
|
+
export type AgentHarnessPreparedRecord = {
|
|
110
|
+
preparedId: string;
|
|
111
|
+
state: AgentHarnessPreparedState;
|
|
112
|
+
createdAt: string;
|
|
113
|
+
expiresAt: string;
|
|
114
|
+
supersededByPreparedId?: string;
|
|
115
|
+
prepared: SdkPreparedPaidRequest;
|
|
116
|
+
executionBinding: AgentHarnessExecutionBinding;
|
|
117
|
+
executionResult?: AgentHarnessExecutionResult;
|
|
118
|
+
};
|
|
119
|
+
/** Minimal client surface AgentHarness needs from the core SDK. */
|
|
120
|
+
export type AgentHarnessClient = Pick<AgentPayClient, 'preparePaidRequest' | 'executePreparedRequest'>;
|
|
121
|
+
/** Configuration for the in-memory wrapper, including TTL and id generation hooks. */
|
|
122
|
+
export type AgentHarnessOptions = {
|
|
123
|
+
client: AgentHarnessClient;
|
|
124
|
+
preparedTtlMs?: number;
|
|
125
|
+
now?: () => Date;
|
|
126
|
+
createPreparedId?: () => string;
|
|
127
|
+
};
|
|
128
|
+
/**
|
|
129
|
+
* Optional in-memory preparedId wrapper over AgentPayClient.
|
|
130
|
+
*/
|
|
131
|
+
export declare class AgentHarness {
|
|
132
|
+
private readonly client;
|
|
133
|
+
private readonly preparedTtlMs;
|
|
134
|
+
private readonly now;
|
|
135
|
+
private readonly createPreparedId;
|
|
136
|
+
private readonly preparedRecords;
|
|
137
|
+
constructor(options: AgentHarnessOptions);
|
|
138
|
+
/**
|
|
139
|
+
* Prepare a candidate request through the core SDK and store the immutable
|
|
140
|
+
* prepared result behind a generated preparedId for later execution.
|
|
141
|
+
*/
|
|
142
|
+
preparePaidRequest(input: AgentHarnessPrepareInput): Promise<AgentHarnessPreparedSummary>;
|
|
143
|
+
/**
|
|
144
|
+
* Execute a previously stored ready preparation. Local state failures are
|
|
145
|
+
* converted into deterministic rejected results rather than thrown to the host.
|
|
146
|
+
*/
|
|
147
|
+
executePreparedRequest(input: AgentHarnessExecuteInput): Promise<AgentHarnessExecutionResult>;
|
|
148
|
+
/**
|
|
149
|
+
* Return the stored in-memory outcome for a preparedId without re-running any
|
|
150
|
+
* merchant or control-plane call.
|
|
151
|
+
*/
|
|
152
|
+
getExecutionResult(preparedId: string): AgentHarnessExecutionLookup;
|
|
153
|
+
/**
|
|
154
|
+
* Return the full stored record for debugging, tests, or host inspection.
|
|
155
|
+
*/
|
|
156
|
+
getPreparedRecord(preparedId: string): AgentHarnessPreparedRecord;
|
|
157
|
+
private runExecution;
|
|
158
|
+
private createExecutionPromise;
|
|
159
|
+
private handleExecutionRejection;
|
|
160
|
+
private getRecordForExecution;
|
|
161
|
+
private getKnownRecord;
|
|
162
|
+
private refreshRecordState;
|
|
163
|
+
private supersedeActiveRecords;
|
|
164
|
+
}
|