@cleave-protocol/ai-sdk 0.0.0-stage → 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Cleave
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,267 @@
1
- # Temporary Holding Version
1
+ # @cleave-protocol/ai-sdk
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Cleave swap tools for [Vercel AI SDK](https://ai-sdk.dev) agents.
4
+
5
+ Cleave is a DEX aggregator on Ethereum, Arbitrum, Base, BNB Chain, Polygon
6
+ and Optimism. It splits a trade across pools and paths, and swaps between
7
+ chains in one step. Quotes include Cleave's fees in the price.
8
+
9
+ These tools plan swaps and return unsigned calls. The user's own wallet
10
+ signs and sends them. They sign nothing; the only thing they send is a gas
11
+ order the wallet has signed.
12
+
13
+ ## Install
14
+
15
+ ```sh
16
+ npm install @cleave-protocol/ai-sdk ai zod
17
+ ```
18
+
19
+ No API key is needed.
20
+
21
+ ## Usage
22
+
23
+ ```ts
24
+ import { generateText, isStepCount } from 'ai';
25
+ import { cleaveTools } from '@cleave-protocol/ai-sdk';
26
+
27
+ const { text } = await generateText({
28
+ model: 'openai/gpt-5-mini',
29
+ prompt:
30
+ 'Plan a swap of 25 USDC to WETH on Base for wallet 0x00000000000000000000000000000000000A11cE.',
31
+ tools: cleaveTools(),
32
+ stopWhen: isStepCount(5),
33
+ });
34
+
35
+ console.log(text);
36
+ ```
37
+
38
+ On `ai` 6, use `stepCountIs` in place of `isStepCount`.
39
+
40
+ `cleaveTools()` includes `getGas`. Its second call passes a gas order the
41
+ wallet has signed to Cleave's relayer. That call waits for your approval
42
+ by default; see [Approving a gas order](#approving-a-gas-order).
43
+
44
+ Each tool can also be added on its own:
45
+
46
+ ```ts
47
+ import { findToken, planSwap } from '@cleave-protocol/ai-sdk';
48
+
49
+ const tools = { planSwap: planSwap(), findToken: findToken() };
50
+ ```
51
+
52
+ ## Tools
53
+
54
+ The tools match Cleave's MCP server tools and call the public
55
+ [Agent REST API](https://cleave.fi/docs/agents/rest).
56
+
57
+ | Tool | MCP tool | What it does |
58
+ | --- | --- | --- |
59
+ | `planSwap` | `cleave_plan_swap` | Plans a swap on one chain, a cross-chain swap or a bridge, and returns unsigned calls in signing order |
60
+ | `trackSwap` | `cleave_track_swap` | Reports the state of a sent plan or gas order, and what arrived |
61
+ | `findToken` | `cleave_find_token` | Finds a token by symbol or address and flags look-alikes |
62
+ | `getGas` | `cleave_get_gas` | Offers native gas for USDC, then passes the order the wallet signed to Cleave's relayer |
63
+
64
+ The usual flow:
65
+
66
+ 1. `findToken`, when a symbol is ambiguous.
67
+ 2. `planSwap`, with the wallet, the tokens and the amount.
68
+ 3. The wallet signs the returned `calls` in order, before `validUntil`.
69
+ 4. `trackSwap`, with the chain, the transaction hash and the plan's
70
+ `requestId`.
71
+
72
+ Check with `trackSwap` before sending a plan again: a plan still in flight
73
+ that is sent twice can pay twice.
74
+
75
+ A plan states its notices first, then the outcome kind, the costs charged
76
+ by others, the expected time and `validUntil`. A `block` notice means no
77
+ calls. A `warn` notice, or a recipient other than the wallet, sets
78
+ `requiresConfirmation`. The token to receive must be on Cleave's token
79
+ list.
80
+
81
+ Before any wallet signs, show the user the warnings, recipient, amounts
82
+ and slippage, and get their confirmation. Do not raise slippage above the
83
+ default 50 bps without the user's consent. Your signer enforces this: the
84
+ tools only plan.
85
+
86
+ `findToken` returns up to ten candidates for a symbol. When the user can
87
+ give a verified contract address, prefer it to a symbol.
88
+
89
+ Field-by-field answers are documented at
90
+ [cleave.fi/docs/agents/mcp](https://cleave.fi/docs/agents/mcp).
91
+
92
+ ## Keys and signing
93
+
94
+ - The tools take no private keys.
95
+ - They sign nothing; the only thing they send is a gas order the wallet
96
+ has signed.
97
+ - `planSwap` returns unsigned calls as `{ chainId, from, to, data, value }`.
98
+ - `getGas` returns an offer with one USDC authorization to sign. Called
99
+ again with that offer, the wallet and the wallet's signature, it passes
100
+ the signed order to Cleave's relayer. That second call has
101
+ `needsApproval` set, so the AI SDK asks for your approval before it runs.
102
+
103
+ ## Gas top-ups
104
+
105
+ `getGas` takes two calls.
106
+
107
+ 1. The offer: `payChainId`, `chainId`, `recipient` and `amount`.
108
+ - `typedData`, the USDC authorization to sign, comes back only when
109
+ `wallet` is passed.
110
+ - An order from another chain needs `wallet` or `refundTo`. On one
111
+ chain, a refund goes to `refundTo`, else `wallet`, else the recipient.
112
+ - `amount` is USDC with at most 6 decimals, from 2 to 50 USDC; gas
113
+ delivered on Ethereum needs at least 5 USDC.
114
+ 2. The order: `offer`, `wallet` and `signature`. The tool passes the
115
+ signed order to Cleave's relayer.
116
+
117
+ An authorization that is never used never debits the wallet. Once an
118
+ order is funded, follow `trackSwap` with its `requestId`
119
+ (`g:<payChainId>:<orderHash>`) and report the status it gives; do not
120
+ assume a refund.
121
+
122
+ ### Approving a gas order
123
+
124
+ The second `getGas` call has `needsApproval` set, so `generateText` stops
125
+ with a `tool-approval-request` instead of running it. Your app shows the
126
+ request to the user and resumes with their answer:
127
+
128
+ ```ts
129
+ import { generateText, isStepCount } from 'ai';
130
+ import type { ModelMessage, ToolApprovalResponse } from 'ai';
131
+ import { cleaveTools } from '@cleave-protocol/ai-sdk';
132
+
133
+ const tools = cleaveTools();
134
+ const messages: ModelMessage[] = [
135
+ { role: 'user', content: 'Submit the gas order my wallet just signed.' },
136
+ ];
137
+
138
+ const first = await generateText({
139
+ model: 'openai/gpt-5-mini',
140
+ tools,
141
+ messages,
142
+ stopWhen: isStepCount(5),
143
+ });
144
+ messages.push(...first.response.messages);
145
+
146
+ const approvals: ToolApprovalResponse[] = [];
147
+ for (const part of first.content) {
148
+ if (part.type === 'tool-approval-request') {
149
+ approvals.push({
150
+ type: 'tool-approval-response',
151
+ approvalId: part.approvalId,
152
+ approved: await confirmWithUser(part.toolCall.input),
153
+ });
154
+ }
155
+ }
156
+
157
+ if (approvals.length > 0) {
158
+ messages.push({ role: 'tool', content: approvals });
159
+ const second = await generateText({
160
+ model: 'openai/gpt-5-mini',
161
+ tools,
162
+ messages,
163
+ stopWhen: isStepCount(5),
164
+ });
165
+ console.log(second.text);
166
+ }
167
+ ```
168
+
169
+ `confirmWithUser` stands for your own prompt to the user.
170
+
171
+ - This approval comes after the wallet has signed. Before the wallet
172
+ signs, show the user the order from the offer: the payer, the
173
+ recipient, the refund address, the amount and the fee.
174
+ - Calling a tool's `.execute()` directly skips the AI SDK's approval step.
175
+ - `getGasApproval: false` turns the approval off.
176
+
177
+ ## Options
178
+
179
+ Every factory, and `cleaveTools`, takes the same optional settings:
180
+
181
+ ```ts
182
+ cleaveTools({
183
+ baseUrl: 'https://api.cleave.fi',
184
+ apiKey: process.env.CLEAVE_API_KEY,
185
+ timeoutMs: 30000,
186
+ fetch: globalThis.fetch,
187
+ getGasApproval: true,
188
+ });
189
+ ```
190
+
191
+ | Option | Default | Meaning |
192
+ | --- | --- | --- |
193
+ | `baseUrl` | `https://api.cleave.fi` | API origin |
194
+ | `apiKey` | none | Optional partner key, sent to `baseUrl` as `Authorization: Bearer`. An unknown or revoked key fails every call with `unauthorized`; there is no keyless fallback |
195
+ | `timeoutMs` | `30000` | Time limit for each call, combined with the AI SDK's abort signal |
196
+ | `fetch` | `globalThis.fetch` | A custom fetch |
197
+ | `getGasApproval` | `true` | Whether `getGas` asks for approval before passing a signed order |
198
+
199
+ ## Errors
200
+
201
+ A refusal from the API comes back as the tool's result, unchanged, so the
202
+ model can read it:
203
+
204
+ ```json
205
+ {
206
+ "apiVersion": "1",
207
+ "requestId": "...",
208
+ "error": {
209
+ "code": "not_found",
210
+ "reason": "unknown_order",
211
+ "message": "...",
212
+ "detail": "...",
213
+ "retryable": false
214
+ }
215
+ }
216
+ ```
217
+
218
+ `retryable: true` means the same call can succeed later; `retryAfterSec`,
219
+ when present, says when.
220
+
221
+ `CleaveApiError` is thrown when there is no API answer to return:
222
+
223
+ - a network error, a timeout or an abort: `status` is `0` and `cause`
224
+ holds the original error;
225
+ - an answer that is not JSON: `status` is the HTTP status.
226
+
227
+ `requestId` carries the `x-request-id` header when the API sent one.
228
+
229
+ Aborting a call, or a timeout, does not cancel a gas order the API has
230
+ already accepted. If a `getGas` submission fails this way, track
231
+ `g:<payChainId>:<orderHash>` from the first offer before signing a new
232
+ one.
233
+
234
+ ## Server side only
235
+
236
+ Run these tools on a server, in a bot or in agent code. The Agent REST API
237
+ refuses requests that carry an `Origin` header, as browsers send, with
238
+ `403` and code `forbidden_origin`.
239
+
240
+ ## Limits
241
+
242
+ - Without a key, each client IP can make 40 requests per 10 seconds and
243
+ 120 per minute.
244
+ - From a recognised hosted platform, where many agents share one address,
245
+ a request with a valid `wallet` in its body is counted by that wallet
246
+ instead. `findToken` and `trackSwap` take no `wallet`, so they stay
247
+ counted by IP.
248
+ - With `apiKey`, calls count against the partner key's own limits.
249
+ - All keyless agents share one planning capacity, used by `planSwap` and
250
+ by `getGas` offers; a cross-chain plan or offer counts twice. When it is
251
+ busy, the answer is `rate_limited` with reason `agent_limit` and a short
252
+ `retryAfterSec`.
253
+
254
+ ## Links
255
+
256
+ - [Cleave for agents](https://cleave.fi/docs/agents)
257
+ - [Agent REST API](https://cleave.fi/docs/agents/rest)
258
+ - [Gas top-up](https://cleave.fi/docs/agents/gas)
259
+ - [MCP server](https://cleave.fi/docs/agents/mcp): `https://api.cleave.fi/mcp`
260
+
261
+ Use of the API is subject to Cleave's
262
+ [Terms of Use](https://cleave.fi/terms) and
263
+ [Privacy Policy](https://cleave.fi/privacy).
264
+
265
+ ## License
266
+
267
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ var index_exports = {};
21
+ __export(index_exports, {
22
+ CHAIN_IDS: () => CHAIN_IDS,
23
+ CleaveApiError: () => CleaveApiError,
24
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
25
+ DEFAULT_TIMEOUT_MS: () => DEFAULT_TIMEOUT_MS,
26
+ cleaveTools: () => cleaveTools,
27
+ findToken: () => findToken,
28
+ getGas: () => getGas,
29
+ planSwap: () => planSwap,
30
+ trackSwap: () => trackSwap
31
+ });
32
+ module.exports = __toCommonJS(index_exports);
33
+
34
+ var import_ai = require("ai");
35
+
36
+ var DEFAULT_BASE_URL = "https://api.cleave.fi";
37
+ var DEFAULT_TIMEOUT_MS = 3e4;
38
+ var USER_AGENT = `cleave-ai-sdk/${"0.1.0"}`;
39
+ var CleaveApiError = class extends Error {
40
+ status;
41
+ requestId;
42
+ constructor(message, status, requestId = null, options) {
43
+ super(message, options);
44
+ this.name = "CleaveApiError";
45
+ this.status = status;
46
+ this.requestId = requestId;
47
+ }
48
+ };
49
+ function untilAborted(signal) {
50
+ return new Promise((_, reject) => {
51
+ if (signal.aborted) reject(signal.reason);
52
+ else signal.addEventListener("abort", () => reject(signal.reason), { once: true });
53
+ });
54
+ }
55
+ async function callTool(endpoint, input, options, abortSignal) {
56
+ const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
57
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
58
+ const headers = { "content-type": "application/json", accept: "application/json", "user-agent": USER_AGENT };
59
+ if (options.apiKey !== void 0 && options.apiKey !== "") headers.authorization = `Bearer ${options.apiKey}`;
60
+ const doFetch = options.fetch ?? globalThis.fetch;
61
+ const timeout = AbortSignal.timeout(timeoutMs);
62
+ const signal = abortSignal === void 0 ? timeout : AbortSignal.any([abortSignal, timeout]);
63
+ const stopped = untilAborted(signal);
64
+ stopped.catch(() => void 0);
65
+ let response;
66
+ let text;
67
+ try {
68
+ response = await Promise.race([doFetch(`${base}/agent/v1/${endpoint}`, { method: "POST", headers, body: JSON.stringify(input), signal }), stopped]);
69
+ text = await Promise.race([response.text(), stopped]);
70
+ } catch (err) {
71
+ const message = abortSignal?.aborted ? "The Cleave API call was aborted" : timeout.aborted ? `Cleave API did not answer within ${timeoutMs} ms` : "Cleave API could not be reached";
72
+ throw new CleaveApiError(message, 0, null, { cause: err });
73
+ }
74
+ const requestId = response.headers.get("x-request-id");
75
+ let body;
76
+ try {
77
+ body = JSON.parse(text);
78
+ } catch (err) {
79
+ throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without a JSON body`, response.status, requestId, { cause: err });
80
+ }
81
+ if (typeof body !== "object" || body === null || Array.isArray(body)) throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without a JSON object`, response.status, requestId);
82
+ if (!response.ok && !("error" in body)) throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without an error body`, response.status, requestId);
83
+ return body;
84
+ }
85
+
86
+ var import_v4 = require("zod/v4");
87
+ var CHAIN_IDS = [1, 10, 56, 137, 8453, 42161];
88
+ var chain = (description) => import_v4.z.literal(CHAIN_IDS).meta({ type: "integer" }).describe(description);
89
+ var address = (description) => import_v4.z.string().regex(/^0x[0-9a-fA-F]{40}$/).describe(description);
90
+ var token = (description) => import_v4.z.string().min(1).max(64).describe(description);
91
+ var amount = (description) => import_v4.z.string().regex(/^[0-9]+(\.[0-9]+)?$/).describe(description);
92
+ var planSwapInput = import_v4.z.strictObject({
93
+ fromChainId: chain("Chain id of the token to sell: 1 Ethereum, 10 Optimism, 56 BNB Chain, 137 Polygon, 8453 Base, 42161 Arbitrum"),
94
+ toChainId: chain("Chain id of the token to receive; defaults to fromChainId").optional(),
95
+ sellToken: token("The token to sell: a listed symbol or a contract address"),
96
+ buyToken: token("The token to receive: a listed symbol or a contract address"),
97
+ amount: amount('Decimal amount of sellToken in token units, such as "25.5"'),
98
+ wallet: address("The wallet that signs and pays"),
99
+ recipient: address("Who receives; defaults to wallet").optional(),
100
+ maxSlippageBps: import_v4.z.number().int().min(0).max(5e3).describe("Slippage limit in basis points, 0 to 5000").optional()
101
+ });
102
+ var trackSwapInput = import_v4.z.strictObject({
103
+ chainId: chain("Chain id of the sent transaction").optional(),
104
+ txHash: import_v4.z.string().regex(/^0x[0-9a-fA-F]{64}$/).describe("Hash of the sent transaction").optional(),
105
+ requestId: import_v4.z.string().min(1).max(79).regex(/^([A-Za-z0-9-]{1,64}|g:[0-9]{1,10}:0x[0-9a-fA-F]{64})$/).describe("The plan's requestId, or for a gas order its requestId alone (g:<payChainId>:<orderHash>)").optional()
106
+ });
107
+ var findTokenInput = import_v4.z.strictObject({
108
+ chainId: chain("Chain id to search on"),
109
+ query: import_v4.z.string().min(1).max(64).describe("A symbol or a contract address")
110
+ });
111
+ var getGasInput = import_v4.z.strictObject({
112
+ payChainId: chain("Chain id the USDC is paid on (first call)").optional(),
113
+ chainId: chain("Chain id the native coin is delivered on (first call)").optional(),
114
+ recipient: address("Who receives the native coin (first call)").optional(),
115
+ amount: amount('USDC to pay, as a decimal string such as "5" (first call)').optional(),
116
+ wallet: address("The paying wallet; with it, the offer carries the authorization to sign. Required on the second call").optional(),
117
+ refundTo: address("Where a refund goes on the pay chain (first call)").optional(),
118
+ offer: import_v4.z.string().min(1).max(4096).describe("The offer string from the first call (second call)").optional(),
119
+ signature: import_v4.z.string().regex(/^0x[0-9a-fA-F]+$/).describe("The wallet's signature of the offer's typedData (second call)").optional()
120
+ });
121
+
122
+ var DESCRIPTIONS = {
123
+ planSwap: "Plans a token swap on one chain, a cross-chain swap or bridge, or a native-gas top-up on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism, and returns unsigned transactions for the given wallet to sign. It fits when the user wants to exchange tokens, move them between these chains, or get ETH, BNB or POL for gas. It sends nothing and moves no funds. The answer leads with blocking notices and warnings, then gives the route's outcome kind (exact or at least a minimum; with a swap on arrival, the token, else a named fallback; where a transfer can go unfilled, a refund of the bridged token on the source chain hours later), every third-party cost except network gas, the expected time, validUntil and the calls in signing order. A blocking notice returns no calls; a warning, or a recipient other than the wallet, sets requiresConfirmation, and the connection's settings can turn either into a refusal. Calls sent after validUntil are stale: they can revert or, on some routes, end in a refund hours later. Amounts are decimal strings in token units; tokens are listed symbols or contract addresses; the recipient defaults to the wallet. Limits set in the connection (a pinned wallet, a per-plan USD cap, listed tokens only) bound every argument. For an ambiguous symbol, cleave_find_token lists the candidates; after sending, cleave_track_swap reports progress. A wallet with no gas can get it with cleave_get_gas.",
124
+ trackSwap: "Reports the status of a swap, bridge or gas top-up that was planned with cleave_plan_swap and then sent, or ordered with cleave_get_gas: its state (for example pending, in flight, delivered, refund pending or refunded), what arrived against the plan and on which chain, and the next action or when to check again. It takes the chain and transaction hash, plus the plan's requestId when known, or for a gas top-up the requestId alone; a transaction to a contract outside Cleave's published list is reported as such. It fits when the user asks whether such a transfer arrived, and before any re-send, because re-sending a plan that is still in flight can pay twice. It reports states only and returns no transactions.",
125
+ findToken: "Finds a token by symbol or contract address on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism and returns its address, decimals and whether it is on Cleave's token list, with a flag when it resembles a listed token. A symbol shared by several listed tokens returns every candidate by address instead of picking one. An unlisted token is found only by its address and comes back flagged, without a name or symbol. It fits before cleave_plan_swap when the user names a token by symbol and the address is unknown.",
126
+ getGas: "Gets native gas (ETH, BNB or POL) on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism for a wallet that holds USDC on Ethereum, Arbitrum, Base, Optimism or Polygon but has no gas to send transactions. Called with the paying chain, the receiving chain, the recipient and a USDC amount, it returns an offer: the fee, the minimum native amount, the deadline, the order and one USDC authorization for the wallet to sign; signing costs no gas. Called again with the offer and the signature, it passes the signed order to Cleave's relayer, which executes it through a contract made for that one order, and returns a requestId for cleave_track_swap. On one chain the recipient gets at least the minimum or nothing moves; from another chain it gets the native coin at the minimum, else USDC on arrival if the arrival swap fails. An order not executed by its deadline is refunded to the refund address, or, if paid from another chain and not filled, refunded hours later. When the wallet already holds some gas, cleave_plan_swap plans a swap to the native coin."
127
+ };
128
+ var NAMES = " In this package, cleave_plan_swap, cleave_track_swap, cleave_find_token and cleave_get_gas are the tools planSwap, trackSwap, findToken and getGas.";
129
+ var PLAN_SUFFIX = " This package has no connection settings, and the token to receive must be on Cleave's token list. Before any wallet signs, show the user the warnings, recipient, amounts and slippage, and get their confirmation. Do not raise slippage above the default 50 bps without the user's consent.";
130
+ var GAS_SUFFIX = " If a submission fails or times out, the order may still have been accepted: track g:<payChainId>:<orderHash> before signing a new offer.";
131
+ function planSwap(options = {}) {
132
+ return (0, import_ai.tool)({
133
+ title: "Plan a swap, bridge or gas top-up",
134
+ description: DESCRIPTIONS.planSwap + PLAN_SUFFIX + NAMES,
135
+ inputSchema: planSwapInput,
136
+ execute: (input, { abortSignal }) => callTool("plan_swap", input, options, abortSignal)
137
+ });
138
+ }
139
+ function trackSwap(options = {}) {
140
+ return (0, import_ai.tool)({
141
+ title: "Track a planned swap or transfer",
142
+ description: DESCRIPTIONS.trackSwap + NAMES,
143
+ inputSchema: trackSwapInput,
144
+ execute: (input, { abortSignal }) => callTool("track_swap", input, options, abortSignal)
145
+ });
146
+ }
147
+ function findToken(options = {}) {
148
+ return (0, import_ai.tool)({
149
+ title: "Find a token's contract address",
150
+ description: DESCRIPTIONS.findToken + NAMES,
151
+ inputSchema: findTokenInput,
152
+ execute: (input, { abortSignal }) => callTool("find_token", input, options, abortSignal)
153
+ });
154
+ }
155
+ function getGas(options = {}) {
156
+ return (0, import_ai.tool)({
157
+ title: "Get native gas with USDC",
158
+ description: DESCRIPTIONS.getGas + GAS_SUFFIX + NAMES,
159
+ inputSchema: getGasInput,
160
+ needsApproval: options.getGasApproval === false ? false : (input) => input.signature !== void 0,
161
+ execute: (input, { abortSignal }) => callTool("get_gas", input, options, abortSignal)
162
+ });
163
+ }
164
+ function cleaveTools(options = {}) {
165
+ return { planSwap: planSwap(options), trackSwap: trackSwap(options), findToken: findToken(options), getGas: getGas(options) };
166
+ }
167
+ 0 && (module.exports = {
168
+ CHAIN_IDS,
169
+ CleaveApiError,
170
+ DEFAULT_BASE_URL,
171
+ DEFAULT_TIMEOUT_MS,
172
+ cleaveTools,
173
+ findToken,
174
+ getGas,
175
+ planSwap,
176
+ trackSwap
177
+ });
@@ -0,0 +1,70 @@
1
+ import { Tool } from 'ai';
2
+ import { z } from 'zod/v4';
3
+
4
+ declare const DEFAULT_BASE_URL = "https://api.cleave.fi";
5
+ declare const DEFAULT_TIMEOUT_MS = 30000;
6
+ interface CleaveToolsOptions {
7
+ baseUrl?: string;
8
+ apiKey?: string;
9
+ timeoutMs?: number;
10
+ fetch?: typeof globalThis.fetch;
11
+ getGasApproval?: boolean;
12
+ }
13
+ type CleaveResult = Record<string, unknown>;
14
+ declare class CleaveApiError extends Error {
15
+ readonly status: number;
16
+ readonly requestId: string | null;
17
+ constructor(message: string, status: number, requestId?: string | null, options?: {
18
+ cause?: unknown;
19
+ });
20
+ }
21
+
22
+ declare const CHAIN_IDS: readonly [1, 10, 56, 137, 8453, 42161];
23
+ declare const planSwapInput: z.ZodObject<{
24
+ fromChainId: z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>;
25
+ toChainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
26
+ sellToken: z.ZodString;
27
+ buyToken: z.ZodString;
28
+ amount: z.ZodString;
29
+ wallet: z.ZodString;
30
+ recipient: z.ZodOptional<z.ZodString>;
31
+ maxSlippageBps: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strict>;
33
+ declare const trackSwapInput: z.ZodObject<{
34
+ chainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
35
+ txHash: z.ZodOptional<z.ZodString>;
36
+ requestId: z.ZodOptional<z.ZodString>;
37
+ }, z.core.$strict>;
38
+ declare const findTokenInput: z.ZodObject<{
39
+ chainId: z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>;
40
+ query: z.ZodString;
41
+ }, z.core.$strict>;
42
+ declare const getGasInput: z.ZodObject<{
43
+ payChainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
44
+ chainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
45
+ recipient: z.ZodOptional<z.ZodString>;
46
+ amount: z.ZodOptional<z.ZodString>;
47
+ wallet: z.ZodOptional<z.ZodString>;
48
+ refundTo: z.ZodOptional<z.ZodString>;
49
+ offer: z.ZodOptional<z.ZodString>;
50
+ signature: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strict>;
52
+ type PlanSwapInput = z.infer<typeof planSwapInput>;
53
+ type TrackSwapInput = z.infer<typeof trackSwapInput>;
54
+ type FindTokenInput = z.infer<typeof findTokenInput>;
55
+ type GetGasInput = z.infer<typeof getGasInput>;
56
+
57
+ type CleaveTools = {
58
+ planSwap: Tool<PlanSwapInput, CleaveResult>;
59
+ trackSwap: Tool<TrackSwapInput, CleaveResult>;
60
+ findToken: Tool<FindTokenInput, CleaveResult>;
61
+ getGas: Tool<GetGasInput, CleaveResult>;
62
+ };
63
+ declare function planSwap(options?: CleaveToolsOptions): Tool<PlanSwapInput, CleaveResult>;
64
+ declare function trackSwap(options?: CleaveToolsOptions): Tool<TrackSwapInput, CleaveResult>;
65
+ declare function findToken(options?: CleaveToolsOptions): Tool<FindTokenInput, CleaveResult>;
66
+ declare function getGas(options?: CleaveToolsOptions): Tool<GetGasInput, CleaveResult>;
67
+ declare function cleaveTools(options?: CleaveToolsOptions): CleaveTools;
68
+
69
+ export { CHAIN_IDS, CleaveApiError, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, cleaveTools, findToken, getGas, planSwap, trackSwap };
70
+ export type { CleaveResult, CleaveTools, CleaveToolsOptions, FindTokenInput, GetGasInput, PlanSwapInput, TrackSwapInput };
@@ -0,0 +1,70 @@
1
+ import { Tool } from 'ai';
2
+ import { z } from 'zod/v4';
3
+
4
+ declare const DEFAULT_BASE_URL = "https://api.cleave.fi";
5
+ declare const DEFAULT_TIMEOUT_MS = 30000;
6
+ interface CleaveToolsOptions {
7
+ baseUrl?: string;
8
+ apiKey?: string;
9
+ timeoutMs?: number;
10
+ fetch?: typeof globalThis.fetch;
11
+ getGasApproval?: boolean;
12
+ }
13
+ type CleaveResult = Record<string, unknown>;
14
+ declare class CleaveApiError extends Error {
15
+ readonly status: number;
16
+ readonly requestId: string | null;
17
+ constructor(message: string, status: number, requestId?: string | null, options?: {
18
+ cause?: unknown;
19
+ });
20
+ }
21
+
22
+ declare const CHAIN_IDS: readonly [1, 10, 56, 137, 8453, 42161];
23
+ declare const planSwapInput: z.ZodObject<{
24
+ fromChainId: z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>;
25
+ toChainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
26
+ sellToken: z.ZodString;
27
+ buyToken: z.ZodString;
28
+ amount: z.ZodString;
29
+ wallet: z.ZodString;
30
+ recipient: z.ZodOptional<z.ZodString>;
31
+ maxSlippageBps: z.ZodOptional<z.ZodNumber>;
32
+ }, z.core.$strict>;
33
+ declare const trackSwapInput: z.ZodObject<{
34
+ chainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
35
+ txHash: z.ZodOptional<z.ZodString>;
36
+ requestId: z.ZodOptional<z.ZodString>;
37
+ }, z.core.$strict>;
38
+ declare const findTokenInput: z.ZodObject<{
39
+ chainId: z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>;
40
+ query: z.ZodString;
41
+ }, z.core.$strict>;
42
+ declare const getGasInput: z.ZodObject<{
43
+ payChainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
44
+ chainId: z.ZodOptional<z.ZodLiteral<1 | 10 | 56 | 137 | 8453 | 42161>>;
45
+ recipient: z.ZodOptional<z.ZodString>;
46
+ amount: z.ZodOptional<z.ZodString>;
47
+ wallet: z.ZodOptional<z.ZodString>;
48
+ refundTo: z.ZodOptional<z.ZodString>;
49
+ offer: z.ZodOptional<z.ZodString>;
50
+ signature: z.ZodOptional<z.ZodString>;
51
+ }, z.core.$strict>;
52
+ type PlanSwapInput = z.infer<typeof planSwapInput>;
53
+ type TrackSwapInput = z.infer<typeof trackSwapInput>;
54
+ type FindTokenInput = z.infer<typeof findTokenInput>;
55
+ type GetGasInput = z.infer<typeof getGasInput>;
56
+
57
+ type CleaveTools = {
58
+ planSwap: Tool<PlanSwapInput, CleaveResult>;
59
+ trackSwap: Tool<TrackSwapInput, CleaveResult>;
60
+ findToken: Tool<FindTokenInput, CleaveResult>;
61
+ getGas: Tool<GetGasInput, CleaveResult>;
62
+ };
63
+ declare function planSwap(options?: CleaveToolsOptions): Tool<PlanSwapInput, CleaveResult>;
64
+ declare function trackSwap(options?: CleaveToolsOptions): Tool<TrackSwapInput, CleaveResult>;
65
+ declare function findToken(options?: CleaveToolsOptions): Tool<FindTokenInput, CleaveResult>;
66
+ declare function getGas(options?: CleaveToolsOptions): Tool<GetGasInput, CleaveResult>;
67
+ declare function cleaveTools(options?: CleaveToolsOptions): CleaveTools;
68
+
69
+ export { CHAIN_IDS, CleaveApiError, DEFAULT_BASE_URL, DEFAULT_TIMEOUT_MS, cleaveTools, findToken, getGas, planSwap, trackSwap };
70
+ export type { CleaveResult, CleaveTools, CleaveToolsOptions, FindTokenInput, GetGasInput, PlanSwapInput, TrackSwapInput };
package/dist/index.js ADDED
@@ -0,0 +1,144 @@
1
+ import { tool } from "ai";
2
+
3
+ var DEFAULT_BASE_URL = "https://api.cleave.fi";
4
+ var DEFAULT_TIMEOUT_MS = 3e4;
5
+ var USER_AGENT = `cleave-ai-sdk/${"0.1.0"}`;
6
+ var CleaveApiError = class extends Error {
7
+ status;
8
+ requestId;
9
+ constructor(message, status, requestId = null, options) {
10
+ super(message, options);
11
+ this.name = "CleaveApiError";
12
+ this.status = status;
13
+ this.requestId = requestId;
14
+ }
15
+ };
16
+ function untilAborted(signal) {
17
+ return new Promise((_, reject) => {
18
+ if (signal.aborted) reject(signal.reason);
19
+ else signal.addEventListener("abort", () => reject(signal.reason), { once: true });
20
+ });
21
+ }
22
+ async function callTool(endpoint, input, options, abortSignal) {
23
+ const base = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
24
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
25
+ const headers = { "content-type": "application/json", accept: "application/json", "user-agent": USER_AGENT };
26
+ if (options.apiKey !== void 0 && options.apiKey !== "") headers.authorization = `Bearer ${options.apiKey}`;
27
+ const doFetch = options.fetch ?? globalThis.fetch;
28
+ const timeout = AbortSignal.timeout(timeoutMs);
29
+ const signal = abortSignal === void 0 ? timeout : AbortSignal.any([abortSignal, timeout]);
30
+ const stopped = untilAborted(signal);
31
+ stopped.catch(() => void 0);
32
+ let response;
33
+ let text;
34
+ try {
35
+ response = await Promise.race([doFetch(`${base}/agent/v1/${endpoint}`, { method: "POST", headers, body: JSON.stringify(input), signal }), stopped]);
36
+ text = await Promise.race([response.text(), stopped]);
37
+ } catch (err) {
38
+ const message = abortSignal?.aborted ? "The Cleave API call was aborted" : timeout.aborted ? `Cleave API did not answer within ${timeoutMs} ms` : "Cleave API could not be reached";
39
+ throw new CleaveApiError(message, 0, null, { cause: err });
40
+ }
41
+ const requestId = response.headers.get("x-request-id");
42
+ let body;
43
+ try {
44
+ body = JSON.parse(text);
45
+ } catch (err) {
46
+ throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without a JSON body`, response.status, requestId, { cause: err });
47
+ }
48
+ if (typeof body !== "object" || body === null || Array.isArray(body)) throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without a JSON object`, response.status, requestId);
49
+ if (!response.ok && !("error" in body)) throw new CleaveApiError(`Cleave API answered HTTP ${response.status} without an error body`, response.status, requestId);
50
+ return body;
51
+ }
52
+
53
+ import { z } from "zod/v4";
54
+ var CHAIN_IDS = [1, 10, 56, 137, 8453, 42161];
55
+ var chain = (description) => z.literal(CHAIN_IDS).meta({ type: "integer" }).describe(description);
56
+ var address = (description) => z.string().regex(/^0x[0-9a-fA-F]{40}$/).describe(description);
57
+ var token = (description) => z.string().min(1).max(64).describe(description);
58
+ var amount = (description) => z.string().regex(/^[0-9]+(\.[0-9]+)?$/).describe(description);
59
+ var planSwapInput = z.strictObject({
60
+ fromChainId: chain("Chain id of the token to sell: 1 Ethereum, 10 Optimism, 56 BNB Chain, 137 Polygon, 8453 Base, 42161 Arbitrum"),
61
+ toChainId: chain("Chain id of the token to receive; defaults to fromChainId").optional(),
62
+ sellToken: token("The token to sell: a listed symbol or a contract address"),
63
+ buyToken: token("The token to receive: a listed symbol or a contract address"),
64
+ amount: amount('Decimal amount of sellToken in token units, such as "25.5"'),
65
+ wallet: address("The wallet that signs and pays"),
66
+ recipient: address("Who receives; defaults to wallet").optional(),
67
+ maxSlippageBps: z.number().int().min(0).max(5e3).describe("Slippage limit in basis points, 0 to 5000").optional()
68
+ });
69
+ var trackSwapInput = z.strictObject({
70
+ chainId: chain("Chain id of the sent transaction").optional(),
71
+ txHash: z.string().regex(/^0x[0-9a-fA-F]{64}$/).describe("Hash of the sent transaction").optional(),
72
+ requestId: z.string().min(1).max(79).regex(/^([A-Za-z0-9-]{1,64}|g:[0-9]{1,10}:0x[0-9a-fA-F]{64})$/).describe("The plan's requestId, or for a gas order its requestId alone (g:<payChainId>:<orderHash>)").optional()
73
+ });
74
+ var findTokenInput = z.strictObject({
75
+ chainId: chain("Chain id to search on"),
76
+ query: z.string().min(1).max(64).describe("A symbol or a contract address")
77
+ });
78
+ var getGasInput = z.strictObject({
79
+ payChainId: chain("Chain id the USDC is paid on (first call)").optional(),
80
+ chainId: chain("Chain id the native coin is delivered on (first call)").optional(),
81
+ recipient: address("Who receives the native coin (first call)").optional(),
82
+ amount: amount('USDC to pay, as a decimal string such as "5" (first call)').optional(),
83
+ wallet: address("The paying wallet; with it, the offer carries the authorization to sign. Required on the second call").optional(),
84
+ refundTo: address("Where a refund goes on the pay chain (first call)").optional(),
85
+ offer: z.string().min(1).max(4096).describe("The offer string from the first call (second call)").optional(),
86
+ signature: z.string().regex(/^0x[0-9a-fA-F]+$/).describe("The wallet's signature of the offer's typedData (second call)").optional()
87
+ });
88
+
89
+ var DESCRIPTIONS = {
90
+ planSwap: "Plans a token swap on one chain, a cross-chain swap or bridge, or a native-gas top-up on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism, and returns unsigned transactions for the given wallet to sign. It fits when the user wants to exchange tokens, move them between these chains, or get ETH, BNB or POL for gas. It sends nothing and moves no funds. The answer leads with blocking notices and warnings, then gives the route's outcome kind (exact or at least a minimum; with a swap on arrival, the token, else a named fallback; where a transfer can go unfilled, a refund of the bridged token on the source chain hours later), every third-party cost except network gas, the expected time, validUntil and the calls in signing order. A blocking notice returns no calls; a warning, or a recipient other than the wallet, sets requiresConfirmation, and the connection's settings can turn either into a refusal. Calls sent after validUntil are stale: they can revert or, on some routes, end in a refund hours later. Amounts are decimal strings in token units; tokens are listed symbols or contract addresses; the recipient defaults to the wallet. Limits set in the connection (a pinned wallet, a per-plan USD cap, listed tokens only) bound every argument. For an ambiguous symbol, cleave_find_token lists the candidates; after sending, cleave_track_swap reports progress. A wallet with no gas can get it with cleave_get_gas.",
91
+ trackSwap: "Reports the status of a swap, bridge or gas top-up that was planned with cleave_plan_swap and then sent, or ordered with cleave_get_gas: its state (for example pending, in flight, delivered, refund pending or refunded), what arrived against the plan and on which chain, and the next action or when to check again. It takes the chain and transaction hash, plus the plan's requestId when known, or for a gas top-up the requestId alone; a transaction to a contract outside Cleave's published list is reported as such. It fits when the user asks whether such a transfer arrived, and before any re-send, because re-sending a plan that is still in flight can pay twice. It reports states only and returns no transactions.",
92
+ findToken: "Finds a token by symbol or contract address on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism and returns its address, decimals and whether it is on Cleave's token list, with a flag when it resembles a listed token. A symbol shared by several listed tokens returns every candidate by address instead of picking one. An unlisted token is found only by its address and comes back flagged, without a name or symbol. It fits before cleave_plan_swap when the user names a token by symbol and the address is unknown.",
93
+ getGas: "Gets native gas (ETH, BNB or POL) on Ethereum, Arbitrum, Base, BNB Chain, Polygon or Optimism for a wallet that holds USDC on Ethereum, Arbitrum, Base, Optimism or Polygon but has no gas to send transactions. Called with the paying chain, the receiving chain, the recipient and a USDC amount, it returns an offer: the fee, the minimum native amount, the deadline, the order and one USDC authorization for the wallet to sign; signing costs no gas. Called again with the offer and the signature, it passes the signed order to Cleave's relayer, which executes it through a contract made for that one order, and returns a requestId for cleave_track_swap. On one chain the recipient gets at least the minimum or nothing moves; from another chain it gets the native coin at the minimum, else USDC on arrival if the arrival swap fails. An order not executed by its deadline is refunded to the refund address, or, if paid from another chain and not filled, refunded hours later. When the wallet already holds some gas, cleave_plan_swap plans a swap to the native coin."
94
+ };
95
+ var NAMES = " In this package, cleave_plan_swap, cleave_track_swap, cleave_find_token and cleave_get_gas are the tools planSwap, trackSwap, findToken and getGas.";
96
+ var PLAN_SUFFIX = " This package has no connection settings, and the token to receive must be on Cleave's token list. Before any wallet signs, show the user the warnings, recipient, amounts and slippage, and get their confirmation. Do not raise slippage above the default 50 bps without the user's consent.";
97
+ var GAS_SUFFIX = " If a submission fails or times out, the order may still have been accepted: track g:<payChainId>:<orderHash> before signing a new offer.";
98
+ function planSwap(options = {}) {
99
+ return tool({
100
+ title: "Plan a swap, bridge or gas top-up",
101
+ description: DESCRIPTIONS.planSwap + PLAN_SUFFIX + NAMES,
102
+ inputSchema: planSwapInput,
103
+ execute: (input, { abortSignal }) => callTool("plan_swap", input, options, abortSignal)
104
+ });
105
+ }
106
+ function trackSwap(options = {}) {
107
+ return tool({
108
+ title: "Track a planned swap or transfer",
109
+ description: DESCRIPTIONS.trackSwap + NAMES,
110
+ inputSchema: trackSwapInput,
111
+ execute: (input, { abortSignal }) => callTool("track_swap", input, options, abortSignal)
112
+ });
113
+ }
114
+ function findToken(options = {}) {
115
+ return tool({
116
+ title: "Find a token's contract address",
117
+ description: DESCRIPTIONS.findToken + NAMES,
118
+ inputSchema: findTokenInput,
119
+ execute: (input, { abortSignal }) => callTool("find_token", input, options, abortSignal)
120
+ });
121
+ }
122
+ function getGas(options = {}) {
123
+ return tool({
124
+ title: "Get native gas with USDC",
125
+ description: DESCRIPTIONS.getGas + GAS_SUFFIX + NAMES,
126
+ inputSchema: getGasInput,
127
+ needsApproval: options.getGasApproval === false ? false : (input) => input.signature !== void 0,
128
+ execute: (input, { abortSignal }) => callTool("get_gas", input, options, abortSignal)
129
+ });
130
+ }
131
+ function cleaveTools(options = {}) {
132
+ return { planSwap: planSwap(options), trackSwap: trackSwap(options), findToken: findToken(options), getGas: getGas(options) };
133
+ }
134
+ export {
135
+ CHAIN_IDS,
136
+ CleaveApiError,
137
+ DEFAULT_BASE_URL,
138
+ DEFAULT_TIMEOUT_MS,
139
+ cleaveTools,
140
+ findToken,
141
+ getGas,
142
+ planSwap,
143
+ trackSwap
144
+ };
package/package.json CHANGED
@@ -1,6 +1,85 @@
1
1
  {
2
2
  "name": "@cleave-protocol/ai-sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Cleave agent tools for the Vercel AI SDK: plan swaps as unsigned calls, track them, find tokens, and order gas top-ups, submitting the gas orders a wallet has signed.",
5
+ "keywords": [
6
+ "ai",
7
+ "ai-sdk",
8
+ "vercel-ai-sdk",
9
+ "tools",
10
+ "agents",
11
+ "dex",
12
+ "dex-aggregator",
13
+ "swap",
14
+ "cross-chain",
15
+ "bridge",
16
+ "ethereum",
17
+ "arbitrum",
18
+ "base",
19
+ "bnb",
20
+ "polygon",
21
+ "optimism"
22
+ ],
23
+ "homepage": "https://cleave.fi/docs/agents",
24
+ "bugs": {
25
+ "url": "https://github.com/cleave-protocol/ai-sdk/issues"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/cleave-protocol/ai-sdk.git"
30
+ },
31
+ "license": "MIT",
32
+ "type": "module",
33
+ "sideEffects": false,
34
+ "main": "./dist/index.cjs",
35
+ "module": "./dist/index.js",
36
+ "types": "./dist/index.d.ts",
37
+ "exports": {
38
+ ".": {
39
+ "import": {
40
+ "types": "./dist/index.d.ts",
41
+ "default": "./dist/index.js"
42
+ },
43
+ "require": {
44
+ "types": "./dist/index.d.cts",
45
+ "default": "./dist/index.cjs"
46
+ }
47
+ },
48
+ "./package.json": "./package.json"
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "README.md",
53
+ "LICENSE"
54
+ ],
55
+ "engines": {
56
+ "node": ">=20.3.0"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "typecheck": "tsc",
61
+ "test": "vitest run",
62
+ "lint": "publint && attw --pack .",
63
+ "probe": "npm run build && node scripts/probe.mjs",
64
+ "rename": "node scripts/rename.mjs",
65
+ "prepublishOnly": "npm run typecheck && npm test && npm run build && npm run lint"
66
+ },
67
+ "peerDependencies": {
68
+ "ai": "^6.0.0 || ^7.0.0",
69
+ "zod": "^3.25.76 || ^4.1.8"
70
+ },
71
+ "devDependencies": {
72
+ "@arethetypeswrong/cli": "^0.18.5",
73
+ "@types/node": "^22.20.5",
74
+ "ai": "^7.0.159",
75
+ "publint": "^0.3.25",
76
+ "tsup": "^9.0.0",
77
+ "typescript": "~5.9.3",
78
+ "vitest": "^5.0.5",
79
+ "zod": "^4.6.7"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public",
83
+ "provenance": true
84
+ }
85
+ }