@haven_ai/sdk 0.1.33-alpha.0 → 0.1.35-alpha.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/README.md +12 -1
- package/dist/index.cjs +284 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +383 -7
- package/dist/index.d.ts +383 -7
- package/dist/index.js +266 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,10 +4,21 @@ TypeScript SDK for [Haven](https://github.com/d-hinders/Haven-AI) — agent wall
|
|
|
4
4
|
|
|
5
5
|
Haven lets AI agents request and sign payments within strict, user-approved on-chain guardrails. This SDK makes it straightforward to integrate Haven payment requests into any agent without giving Haven custody of user or agent keys.
|
|
6
6
|
|
|
7
|
+
## Are you an AI agent whose user has no Haven account yet?
|
|
8
|
+
|
|
9
|
+
Read **`/for-agents.md`** on the Haven host your user gave you — or
|
|
10
|
+
[the copy in this repository](https://github.com/d-hinders/Haven-AI/blob/dev/packages/frontend/public/for-agents.md)
|
|
11
|
+
if you do not have that host yet.
|
|
12
|
+
|
|
13
|
+
Your user creates the account and the passkey: those are theirs, they need a
|
|
14
|
+
human, and you should never ask for their password. You can do everything else
|
|
15
|
+
— including running the connector command from the setup prompt they paste you,
|
|
16
|
+
and managing the account from the shell with `@haven_ai/cli`.
|
|
17
|
+
|
|
7
18
|
## Install
|
|
8
19
|
|
|
9
20
|
```bash
|
|
10
|
-
npm install @haven_ai/sdk
|
|
21
|
+
npm install @haven_ai/sdk@alpha
|
|
11
22
|
```
|
|
12
23
|
|
|
13
24
|
## Quick Start
|
package/dist/index.cjs
CHANGED
|
@@ -9,6 +9,33 @@ var accounts = require('viem/accounts');
|
|
|
9
9
|
|
|
10
10
|
// src/client.ts
|
|
11
11
|
|
|
12
|
+
// src/connector-channel.ts
|
|
13
|
+
var CONNECTOR_PACKAGE_NAME = "@haven_ai/connect";
|
|
14
|
+
var HAVEN_CONNECTOR_CHANNEL = "alpha";
|
|
15
|
+
var CHANNEL_PATTERN = /^[a-z][a-z0-9-]{0,31}$/;
|
|
16
|
+
function isConnectorChannel(value) {
|
|
17
|
+
return CHANNEL_PATTERN.test(value);
|
|
18
|
+
}
|
|
19
|
+
function resolveConnectorChannel(raw, fallback = HAVEN_CONNECTOR_CHANNEL) {
|
|
20
|
+
const trimmed = (raw ?? "").trim();
|
|
21
|
+
if (trimmed === "") return fallback;
|
|
22
|
+
if (!isConnectorChannel(trimmed)) {
|
|
23
|
+
throw new Error(
|
|
24
|
+
`HAVEN_CONNECTOR_CHANNEL is set to ${JSON.stringify(raw)}, which is not a valid npm dist-tag (lowercase letter first, then letters, digits or hyphens). Refusing to start rather than fall back to the default channel, because falling back would hand out the production connector while looking configured.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
return trimmed;
|
|
28
|
+
}
|
|
29
|
+
function connectorSpec(channel = HAVEN_CONNECTOR_CHANNEL) {
|
|
30
|
+
return `${CONNECTOR_PACKAGE_NAME}@${channel}`;
|
|
31
|
+
}
|
|
32
|
+
function connectorRerunCommand(args, options) {
|
|
33
|
+
const channel = options?.channel ?? HAVEN_CONNECTOR_CHANNEL;
|
|
34
|
+
const flags = options?.npxFlags ? `${options.npxFlags} ` : "";
|
|
35
|
+
const command = `npx ${flags}${connectorSpec(channel)}`;
|
|
36
|
+
return args ? `${command} ${args}` : command;
|
|
37
|
+
}
|
|
38
|
+
|
|
12
39
|
// src/types.ts
|
|
13
40
|
var DEFAULT_CONFIRMATION_TIMEOUT_MS = 9e4;
|
|
14
41
|
var AgentPaymentPhase = {
|
|
@@ -329,7 +356,10 @@ var SignerRefusalCode = {
|
|
|
329
356
|
/** `SUPPORTED_SWEEP_BINDING_VERSIONS` in `@haven_ai/signer` does not include the received version. */
|
|
330
357
|
UnsupportedSweepBindingVersion: "UNSUPPORTED_SWEEP_BINDING_VERSION"
|
|
331
358
|
};
|
|
332
|
-
|
|
359
|
+
function signerUpdateFallback(channel = HAVEN_CONNECTOR_CHANNEL) {
|
|
360
|
+
return `Update @haven_ai/signer by rerunning \`${connectorRerunCommand(void 0, { channel })}\`, which reinstalls the pinned MCP runtime, then retry the same signing call. Nothing was signed or spent \u2014 the quote or payment this version came from is unaffected and does not need to be re-quoted.`;
|
|
361
|
+
}
|
|
362
|
+
var SIGNER_UPDATE_FALLBACK = signerUpdateFallback();
|
|
333
363
|
var HavenUnsupportedSignerVersionError = class extends HavenError {
|
|
334
364
|
constructor(message, code, supportedVersions, receivedVersion, fallback) {
|
|
335
365
|
super(message, code);
|
|
@@ -617,6 +647,7 @@ function normalizePaymentRequired(value) {
|
|
|
617
647
|
const resourceUrl = candidate.resource?.url ?? first.resource;
|
|
618
648
|
if (!resourceUrl) return null;
|
|
619
649
|
const resource = {
|
|
650
|
+
...candidate.resource && typeof candidate.resource === "object" ? candidate.resource : {},
|
|
620
651
|
url: resourceUrl,
|
|
621
652
|
description: candidate.resource?.description ?? first.description,
|
|
622
653
|
mimeType: candidate.resource?.mimeType ?? first.mimeType
|
|
@@ -691,6 +722,17 @@ function x402PaymentHeaderNamesFor(paymentHeader) {
|
|
|
691
722
|
function x402PaymentHeaderNamesSent(paymentHeader) {
|
|
692
723
|
return x402PaymentHeaderNamesFor(paymentHeader).join(", ");
|
|
693
724
|
}
|
|
725
|
+
function x402V2PaymentEnvelope(paymentRequired, accepted, payload) {
|
|
726
|
+
const resource = paymentRequired.resource;
|
|
727
|
+
const extensions = paymentRequired.extensions;
|
|
728
|
+
return {
|
|
729
|
+
x402Version: paymentRequired.x402Version,
|
|
730
|
+
...resource && typeof resource === "object" && !Array.isArray(resource) ? { resource } : {},
|
|
731
|
+
accepted,
|
|
732
|
+
payload,
|
|
733
|
+
...extensions && typeof extensions === "object" && !Array.isArray(extensions) ? { extensions } : {}
|
|
734
|
+
};
|
|
735
|
+
}
|
|
694
736
|
function parsePaymentRequired(response) {
|
|
695
737
|
const v2Header = response.headers.get("PAYMENT-REQUIRED");
|
|
696
738
|
if (v2Header) {
|
|
@@ -870,7 +912,15 @@ async function validateStandardX402PaymentHeader(paymentHeader, context) {
|
|
|
870
912
|
throw new Error("context");
|
|
871
913
|
}
|
|
872
914
|
} else {
|
|
873
|
-
if (!hasOnlyKeys(decoded, ["x402Version", "accepted", "payload"]
|
|
915
|
+
if (!hasOnlyKeys(decoded, ["x402Version", "accepted", "payload"], ["resource", "extensions"])) {
|
|
916
|
+
throw new Error("shape");
|
|
917
|
+
}
|
|
918
|
+
if ("resource" in decoded && (!decoded.resource || typeof decoded.resource !== "object" || Array.isArray(decoded.resource))) {
|
|
919
|
+
throw new Error("shape");
|
|
920
|
+
}
|
|
921
|
+
if ("extensions" in decoded && (!decoded.extensions || typeof decoded.extensions !== "object" || Array.isArray(decoded.extensions))) {
|
|
922
|
+
throw new Error("shape");
|
|
923
|
+
}
|
|
874
924
|
const accepted = selectStandardPaymentOption([decoded.accepted]);
|
|
875
925
|
if (!accepted || !matchesHeaderContext(accepted, context)) throw new Error("context");
|
|
876
926
|
}
|
|
@@ -922,8 +972,8 @@ async function validateStandardX402PaymentHeader(paymentHeader, context) {
|
|
|
922
972
|
throw new X402PaymentHeaderValidationError();
|
|
923
973
|
}
|
|
924
974
|
}
|
|
925
|
-
function hasOnlyKeys(value,
|
|
926
|
-
return Object.keys(value).every((key) =>
|
|
975
|
+
function hasOnlyKeys(value, required, optional = []) {
|
|
976
|
+
return Object.keys(value).every((key) => required.includes(key) || optional.includes(key)) && required.every((key) => key in value);
|
|
927
977
|
}
|
|
928
978
|
function sameAddress2(left, right) {
|
|
929
979
|
return left.toLowerCase() === right.toLowerCase();
|
|
@@ -2128,11 +2178,7 @@ var X402FundingLeg = class {
|
|
|
2128
2178
|
);
|
|
2129
2179
|
if (paymentRequired.x402Version < 2) return header;
|
|
2130
2180
|
const payment = decodeBase64Json(header);
|
|
2131
|
-
return encodeBase64Json(
|
|
2132
|
-
x402Version: paymentRequired.x402Version,
|
|
2133
|
-
accepted: option,
|
|
2134
|
-
payload: payment.payload
|
|
2135
|
-
});
|
|
2181
|
+
return encodeBase64Json(x402V2PaymentEnvelope(paymentRequired, option, payment.payload));
|
|
2136
2182
|
}
|
|
2137
2183
|
// ── Receipt mapping ──────────────────────────────────────────────
|
|
2138
2184
|
receiptFromAuthorization(paymentRequired, option, paymentHeader, raw, execResult) {
|
|
@@ -2343,6 +2389,16 @@ var X402Erc7710 = class {
|
|
|
2343
2389
|
const amountAtomic = x402AuthorizationAmount(option);
|
|
2344
2390
|
const raw = await this.post("/x402", {
|
|
2345
2391
|
url: options.resourceUrl ?? paymentRequired.resource?.url,
|
|
2392
|
+
// #2373: the full 402 challenge, persisted verbatim by the backend
|
|
2393
|
+
// (#1355) so the settle handoff can echo its resource/extensions into
|
|
2394
|
+
// the X-PAYMENT envelope (#2361). This scheme decomposes the challenge
|
|
2395
|
+
// into the fields below for AUTHORITY; the stored copy exists for the
|
|
2396
|
+
// echo, which cannot be reconstructed from the decomposition — omitting
|
|
2397
|
+
// it is how every erc7710 payment failed a merchant that enforces the
|
|
2398
|
+
// spec's extensions-echo MUST. Same ≤64KB guard and omission behaviour
|
|
2399
|
+
// as the 3009 path (client.ts): an oversized challenge omits the field
|
|
2400
|
+
// rather than failing the payment, and the settle echo then omits too.
|
|
2401
|
+
...new TextEncoder().encode(JSON.stringify(paymentRequired)).length <= 65536 ? { paymentRequired } : {},
|
|
2346
2402
|
// payTo = the MERCHANT is what selects direct settlement server-side.
|
|
2347
2403
|
// The explicit settlementScheme must AGREE with that shape (#1360) —
|
|
2348
2404
|
// disagreement is a 400 by design, so that a stale delegate address
|
|
@@ -4258,10 +4314,146 @@ var havenTools = {
|
|
|
4258
4314
|
openai: openaiTools
|
|
4259
4315
|
};
|
|
4260
4316
|
|
|
4317
|
+
// src/agent-guidance.ts
|
|
4318
|
+
var AGENT_SECRET_HYGIENE_SENTENCE = "Do not print private keys, API keys, credential file contents, or config secrets in chat or logs.";
|
|
4319
|
+
var AGENT_LOCAL_KEY_SENTENCE = "The Haven connector generates the signing key locally and sends Haven only the public signing address plus proof.";
|
|
4320
|
+
var AGENT_NETWORK_ACCESS_SENTENCE = "Network access is expected: this command downloads the npm package and contacts the Haven API, so if your environment is sandboxed, run it with network access enabled or request network access escalation; that changes the execution environment, not the command, and is not a third command modification.";
|
|
4321
|
+
var AGENT_JSON_MODE_SENTENCE = "If you are an AI agent running this command yourself rather than a human pasting it, you should append --json: the connector then emits one machine-readable, secret-free result object on stdout with progress on stderr, and returns promptly instead of blocking while it waits for the budget approval.";
|
|
4322
|
+
var AGENT_APPROVAL_RELAY_JSON_SENTENCE = "When a --json outcome reports approval.required: true, your first action must be to relay the approval instruction to me in your own reply \u2014 if the outcome carries approval.url, give me that link; otherwise tell me to return to Haven and approve this agent's budget \u2014 before verifying the connection, restarting anything, or any other step. Never build that link yourself: relay the one the outcome gave you or none at all. Any restart the outcome asks for is a separate instruction to give me afterwards, once the approval is done.";
|
|
4323
|
+
var AGENT_APPROVAL_RELAY_PROSE_SENTENCE = "If you ran the command without --json, the connector waits for the approval itself and prints its next steps when it finishes: relay the budget-approval instruction to me \u2014 the approval link if those steps printed one, otherwise that you need to return to Haven and approve this agent's budget \u2014 only if those printed next steps still ask for it. If they report the budget as already approved, there is nothing for me to approve.";
|
|
4324
|
+
var AGENT_WIRING_COLLISION_RELAY_SENTENCE = "If the connector refuses with wiring_collision, this machine is already wired to a different agent: relay that refusal to me with the superseded_agent_ids and suggested_name it carries, and let me choose whether to replace the existing wiring or add this agent alongside it. Never pick for me by adding --replace or --name yourself.";
|
|
4325
|
+
var AGENT_COMMAND_MODIFICATION_SENTENCE = "Only two changes to the command above are permitted, and no others: appending --json, and \u2014 only if the connector refuses because it could not determine the agent runtime \u2014 re-running it once with --runtime <name> added, naming the harness you are running in, using one of the values that refusal lists. Never invent a runtime name and never change anything else.";
|
|
4326
|
+
var AGENT_README_SECTION_MD = `## Are you an AI agent whose user has no Haven account yet?
|
|
4327
|
+
|
|
4328
|
+
Read **\`/for-agents.md\`** on the Haven host your user gave you \u2014 or
|
|
4329
|
+
[the copy in this repository](https://github.com/d-hinders/Haven-AI/blob/dev/packages/frontend/public/for-agents.md)
|
|
4330
|
+
if you do not have that host yet.
|
|
4331
|
+
|
|
4332
|
+
Your user creates the account and the passkey: those are theirs, they need a
|
|
4333
|
+
human, and you should never ask for their password. You can do everything else
|
|
4334
|
+
\u2014 including running the connector command from the setup prompt they paste you,
|
|
4335
|
+
and managing the account from the shell with \`@haven_ai/cli\`.`;
|
|
4336
|
+
var HAVEN_AGENT_RUNBOOK_MD = `# Haven for agents \u2014 set your user up to let you pay
|
|
4337
|
+
|
|
4338
|
+
You are an AI agent whose user has no Haven account yet, or has one but has not connected you.
|
|
4339
|
+
|
|
4340
|
+
> Links below are paths on this same host: resolve them against the URL you fetched this file from.
|
|
4341
|
+
|
|
4342
|
+
## What Haven is, in three sentences
|
|
4343
|
+
|
|
4344
|
+
Your user gives you a **budget on their own account** \u2014 not their wallet, and not a key to their funds. The budget is a delegation they sign, enforced on-chain: a payment over it, to the wrong recipient, or past its expiry is refused at execution time, not by a dashboard promise. Haven constructs and relays the payments; you get an agent credential and a signing key made on your machine, and your user can revoke the budget without you and without Haven.
|
|
4345
|
+
|
|
4346
|
+
## The sequence
|
|
4347
|
+
|
|
4348
|
+
Four of the six steps are your user's \u2014 each needs a human signature or a human decision. The rest is yours. If they already have a funded account, start at step 3.
|
|
4349
|
+
|
|
4350
|
+
1. **HUMAN \u2014 create the account.** Name, email, password, then a passkey (Face ID / Touch ID) or a wallet. Never offer to enter any of it: you must not have their password, and the passkey is bound to their device. With a terminal, \`npx @haven_ai/cli login --api <api-url>\` afterwards gets you a scoped session for steps 3-4 (that flag or \`HAVEN_API_URL\`, on the first command \u2014 the session then remembers the backend; **the CLI's built-in default is Haven's hosted production backend**, so on any other deployment an omitted flag connects you somewhere real and wrong rather than failing) \u2014 they approve a code in the browser, you never hold their password. It can set up agents and read the account; it cannot sign, approve a budget, move funds, or rotate any agent's keys.
|
|
4351
|
+
2. **HUMAN \u2014 fund it.** USDC only, no ETH: Haven sponsors the gas. \`haven wallets funding\` prints the address, the amount **and which chain** in one place; without a CLI session, the dashboard's funding card shows the address and amount and its Receive-funds screen names the chain. Read the chain off whichever you used and put it in your message \u2014 never assume one: a testnet deployment and production both call themselves Haven.
|
|
4352
|
+
3. **HUMAN \u2014 create the agent, set its budget**, and paste you the **setup prompt** it hands back. With a CLI session (step 1) you can do this step yourself: \`haven agents connect --name <n> --budget <amount> --token USDC --period <minutes>\` prints the same connector command and approval link; add \`--run\` to do step 4 too.
|
|
4353
|
+
4. **YOU \u2014 run the connector command** in that prompt (below). It makes your signing key locally, registering only the public address.
|
|
4354
|
+
5. **HUMAN \u2014 approve the budget** with their passkey, in the Haven tab they created the agent in: it advances to the approval step by itself once your run registers.
|
|
4355
|
+
6. **YOU \u2014 verify, then pay.**
|
|
4356
|
+
|
|
4357
|
+
## Budget changes later (second token, raise, revoke)
|
|
4358
|
+
|
|
4359
|
+
The first budget rides the setup above. After the agent exists, a CLI session can construct the LATER changes \u2014 a second token, a bigger amount, a recipient pin, a stop \u2014 and hand your user a link; the human still signs, every time:
|
|
4360
|
+
|
|
4361
|
+
\`\`\`
|
|
4362
|
+
haven budget grant <agentId> --amount <n> --token USDC --period <minutes> [--recipient <address>] [--wait]
|
|
4363
|
+
haven budget revoke <agentId> <delegationHash> [--wait]
|
|
4364
|
+
\`\`\`
|
|
4365
|
+
|
|
4366
|
+
The CLI never signs: it prints a dashboard link, your user opens it and signs with their passkey or wallet. \`--wait\` polls until the human's signature lands. The hash for \`revoke\` is in \`haven agents show <agentId>\`.
|
|
4367
|
+
|
|
4368
|
+
## Hand-off scripts
|
|
4369
|
+
|
|
4370
|
+
Send these as your own message, \`<host>\` replaced by the host you fetched this file from. Say what you cannot do, not only what they must.
|
|
4371
|
+
|
|
4372
|
+
**Before signup** (step 1):
|
|
4373
|
+
|
|
4374
|
+
> I can do everything except the parts that need your signature. Please open \`<host>/signup?next=/agents&via=agent\` \u2014 name, email, password, then a passkey (Face ID / Touch ID) or a wallet. That is your account's key: I should not have it, and I will never ask for your password. Already have one? \`<host>/login?next=/agents\`.
|
|
4375
|
+
|
|
4376
|
+
**At the passkey step:**
|
|
4377
|
+
|
|
4378
|
+
> The passkey must be made on your own device \u2014 it is what keeps the account yours, so nobody, Haven included, can move your funds without you. If this browser cannot, open \`<host>/onboarding?next=/agents\` on your phone and finish there.
|
|
4379
|
+
|
|
4380
|
+
**At funding** (step 2):
|
|
4381
|
+
|
|
4382
|
+
> Your Haven account needs USDC before I can pay for anything \u2014 USDC only, no ETH: Haven sponsors the gas. Before you send anything, let me get you the exact address **and network** \u2014 \`haven wallets funding\` prints both, and the funding card on \`<host>/dashboard\` shows the same. Please do not send to an address or a chain I have not confirmed with you; a small amount first is fine.
|
|
4383
|
+
|
|
4384
|
+
**At the budget** (step 3):
|
|
4385
|
+
|
|
4386
|
+
> On \`<host>/agents\`, create an agent for me and set a budget \u2014 say 25 USDC per day. That is the limit I cannot exceed. It hands back a setup prompt: paste it to me and I run it here.
|
|
4387
|
+
|
|
4388
|
+
**At budget approval** (step 5) \u2014 the moment your run reports that approval is required, before anything else. Send the first if your run carried \`approval.url\`, the second if it did not.
|
|
4389
|
+
|
|
4390
|
+
> Setup is done on my side. Approve the budget here: <approval.url>. Approve it with your passkey; nothing can be spent until you do.
|
|
4391
|
+
|
|
4392
|
+
> Setup is done on my side. Go back to the Haven tab where you created the agent \u2014 it should now be asking you to approve the budget. Approve it with your passkey; nothing can be spent until you do.
|
|
4393
|
+
|
|
4394
|
+
## What you run
|
|
4395
|
+
|
|
4396
|
+
The setup prompt carries a **connector command** shaped like this:
|
|
4397
|
+
|
|
4398
|
+
\`\`\`
|
|
4399
|
+
npx -y @haven_ai/connect@<channel> --setup EXAMPLE-SETUP-TOKEN-NOT-REAL --api <api-url> --ack-local-tools
|
|
4400
|
+
\`\`\`
|
|
4401
|
+
|
|
4402
|
+
Run the prompt's version, not this one: the token is one-time, the API URL is the backend's own, and \`<channel>\` is the npm tag your prompt names \u2014 never a tag you pick. The rules below come from that prompt, so "me" in them is your user, not Haven.
|
|
4403
|
+
|
|
4404
|
+
- ${AGENT_JSON_MODE_SENTENCE}
|
|
4405
|
+
- ${AGENT_APPROVAL_RELAY_JSON_SENTENCE}
|
|
4406
|
+
- ${AGENT_APPROVAL_RELAY_PROSE_SENTENCE}
|
|
4407
|
+
- ${AGENT_COMMAND_MODIFICATION_SENTENCE}
|
|
4408
|
+
- ${AGENT_SECRET_HYGIENE_SENTENCE}
|
|
4409
|
+
|
|
4410
|
+
If the approval wait times out, nothing is lost \u2014 your agent is registered and the budget is still waiting to be approved. Send your user the \`approval.url\` your run reported, or, if it carried none, ask them to finish it in that same Haven tab. The outcome carries no setup id, so never assemble an approval link out of parts \u2014 relay the whole one it gave you or none at all.
|
|
4411
|
+
|
|
4412
|
+
## How to verify
|
|
4413
|
+
|
|
4414
|
+
Call \`haven_get_agent\`, one of the Haven MCP tools the connector wires into your runtime in step 4. It returns identity plus \`spend_authority_readiness\`:
|
|
4415
|
+
|
|
4416
|
+
- \`ready\` \u2014 a budget is live; you can pay.
|
|
4417
|
+
- \`needs_approval\` \u2014 the connector finished, nobody approved yet. Ask your user again, in their Haven tab; there is no queue to wait in.
|
|
4418
|
+
- \`revoked\` \u2014 the credential is not active; ask your user to create a new agent.
|
|
4419
|
+
|
|
4420
|
+
\`ready\` covers hosted identity and the budget only, not your local signer. Check that with \`npx -y @haven_ai/connect@<channel> --doctor\`, the same tag your prompt named \u2014 a separate command, so the two-changes rule does not bind it.
|
|
4421
|
+
|
|
4422
|
+
## If you cannot open a browser
|
|
4423
|
+
|
|
4424
|
+
Nothing here needs you to. Steps 1-3 are links: hand your user the full \`<host>/\u2026\` URL and ask them to say when it is done. Step 5 is a link only when your run reported one in \`approval.url\` \u2014 otherwise it is the tab they already have open, as above. Then poll \`haven_get_agent\` until it reads \`ready\`. Do not route around the sign-in wall \u2014 it makes the account theirs, not yours.
|
|
4425
|
+
|
|
4426
|
+
## Vocabulary
|
|
4427
|
+
|
|
4428
|
+
| Term | What it is |
|
|
4429
|
+
|---|---|
|
|
4430
|
+
| **setup prompt** | The text the dashboard hands your user to paste to you. Carries the command and its rules. |
|
|
4431
|
+
| **connector command** | The \`npx -y @haven_ai/connect@\u2026\` line you run. One-time token, one use. |
|
|
4432
|
+
| **agent credential** | Your API key (\`sk_agent_\u2026\`), written to \`~/.haven\`. It identifies you; alone it cannot move money. |
|
|
4433
|
+
| **delegate key** | Your signing key, made on this machine and never sent anywhere. |
|
|
4434
|
+
| **budget** | The on-chain delegation your user signed. It authorises the payment; Haven constructs and relays it. |
|
|
4435
|
+
|
|
4436
|
+
Next: [your agent hit a 402](/402.md) \xB7 [everything agent-readable](/llms.txt)
|
|
4437
|
+
`;
|
|
4438
|
+
var AGENT_ONBOARDING_PROMPT = `I have a Haven account and I am signed in at {{HAVEN_ORIGIN}}. Please set up Haven so you can pay for things within a budget I approve.
|
|
4439
|
+
|
|
4440
|
+
Start by reading {{HAVEN_ORIGIN}}/for-agents.md \u2014 it is written for you and explains which steps are mine.
|
|
4441
|
+
|
|
4442
|
+
Then:
|
|
4443
|
+
|
|
4444
|
+
1. Run \`npx @haven_ai/cli login\`. It prints a link and a code and does not need my password \u2014 it must never ask for it. Give me the link straight away and wait for me to approve it in my browser.
|
|
4445
|
+
2. Once I have approved, run \`haven agents connect --name <a name you choose> --budget <amount> --token USDC --period <minutes>\` with the budget I tell you. If I have not given you one, ask me before running it. Add \`--run\` to complete the connection in the same step.
|
|
4446
|
+
3. ${AGENT_APPROVAL_RELAY_JSON_SENTENCE}
|
|
4447
|
+
4. Once I have approved the budget, verify with the \`haven_get_agent\` tool: \`ready\` means you can pay, \`needs_approval\` means my approval has not landed yet.
|
|
4448
|
+
|
|
4449
|
+
Two things only I can do: approving that budget with my passkey, and funding the account with USDC on Base \u2014 no ETH, Haven sponsors the gas. Tell me if either is missing rather than working around it.
|
|
4450
|
+
|
|
4451
|
+
${AGENT_SECRET_HYGIENE_SENTENCE}`;
|
|
4452
|
+
|
|
4261
4453
|
// src/skill-content.ts
|
|
4262
4454
|
var HAVEN_SKILL_MD = `---
|
|
4263
4455
|
name: haven-pay
|
|
4264
|
-
description: Pay for things from the user's Haven wallet within their agent rules. Use when the user asks to send, pay, tip, or transfer crypto
|
|
4456
|
+
description: Pay for things from the user's Haven wallet within their agent rules, and set Haven up when it is not yet connected. Use when the user asks to send, pay, tip, or transfer crypto; when a request hits an HTTP 402 (x402) paywall; or when they ask to create a Haven account, create an agent, or connect one.
|
|
4265
4457
|
---
|
|
4266
4458
|
|
|
4267
4459
|
# Haven: pay from a Haven wallet
|
|
@@ -4287,6 +4479,59 @@ the source of truth.
|
|
|
4287
4479
|
- A request returns HTTP 402 (x402): use the Haven pay tools to settle it,
|
|
4288
4480
|
then retry the original request.
|
|
4289
4481
|
|
|
4482
|
+
## Onboarding and setup
|
|
4483
|
+
|
|
4484
|
+
You are in this mode when there is no Haven agent credential on this machine,
|
|
4485
|
+
or when your user asks you to create a Haven account, create an agent, or
|
|
4486
|
+
connect one \u2014 for themselves or for someone else.
|
|
4487
|
+
|
|
4488
|
+
**None of the tools below creates authority.** They spend a budget a human
|
|
4489
|
+
already signed. There is no tool here that opens an account, mints a
|
|
4490
|
+
credential, or approves a budget, so reaching for one of them to "set Haven
|
|
4491
|
+
up" cannot work; the steps are the ones in this section instead.
|
|
4492
|
+
|
|
4493
|
+
Start by reading \`/for-agents.md\` on the Haven host \u2014 the origin of the
|
|
4494
|
+
\`api_url\` in your \`agent.json\` if you have one, otherwise the host your user
|
|
4495
|
+
names. It is the full runbook: six steps, which four are your user's, and what
|
|
4496
|
+
to say at each hand-off.
|
|
4497
|
+
|
|
4498
|
+
Two of those steps you can do yourself, from the shell with \`@haven_ai/cli\`
|
|
4499
|
+
(installs the \`haven\` command):
|
|
4500
|
+
|
|
4501
|
+
- \`haven login\` \u2014 a device-code browser flow. It prints a code and a link
|
|
4502
|
+
for your user to approve, so you never see or ask for their password. What
|
|
4503
|
+
the session can reach is an allow-list, not your user's full authority: it
|
|
4504
|
+
creates and manages agents and reads the account, and it cannot approve a
|
|
4505
|
+
budget, rotate a key, change a signer or move money \u2014 those are your user's.
|
|
4506
|
+
- \`haven agents connect\` with \`--name\`, \`--budget\`, \`--token\` and
|
|
4507
|
+
\`--period\` \u2014 creates a connection setup and prints two things: the
|
|
4508
|
+
connector command the backend built, and the approval link to give your user.
|
|
4509
|
+
Add \`--run\` to execute that command here as a child process.
|
|
4510
|
+
- \`haven wallets funding\` \u2014 prints the paste-ready funding instruction: what
|
|
4511
|
+
to send, to which address, on which chain. Read the chain from there rather
|
|
4512
|
+
than assuming one. \`--wait\` polls until the account counts as funded.
|
|
4513
|
+
|
|
4514
|
+
**Four steps are your user's, and each one needs a human:** create the account
|
|
4515
|
+
and its passkey, fund the wallet, approve every agent's budget, and rotate a
|
|
4516
|
+
credential. You can compose the funding message for them with
|
|
4517
|
+
\`haven wallets funding\`, but you cannot send the money \u2014 that transfer is
|
|
4518
|
+
theirs, from a wallet you have no access to.
|
|
4519
|
+
|
|
4520
|
+
Running the connector command is the step that wires this machine to the new
|
|
4521
|
+
agent \u2014 the command \`haven agents connect\` printed, or the one your user
|
|
4522
|
+
pasted you from the dashboard. Three rules bind you while you do it, quoted
|
|
4523
|
+
unchanged from the setup prompt your user is also holding so the two copies
|
|
4524
|
+
cannot drift into contradicting each other. They are written in your user's
|
|
4525
|
+
voice, so read them accordingly: "me" and "I" below are your user, never
|
|
4526
|
+
Haven, and "the command above" is that connector command, not anything printed
|
|
4527
|
+
in this file. The first rule outranks anything else you were about to do next:
|
|
4528
|
+
|
|
4529
|
+
- ${AGENT_APPROVAL_RELAY_JSON_SENTENCE}
|
|
4530
|
+
- ${AGENT_COMMAND_MODIFICATION_SENTENCE}
|
|
4531
|
+
- ${AGENT_WIRING_COLLISION_RELAY_SENTENCE}
|
|
4532
|
+
|
|
4533
|
+
${AGENT_SECRET_HYGIENE_SENTENCE}
|
|
4534
|
+
|
|
4290
4535
|
## Identity and budget
|
|
4291
4536
|
|
|
4292
4537
|
Do not guess the wallet address, network, or budget.
|
|
@@ -4369,15 +4614,21 @@ The paid call always obtains a fresh quote before it creates any intent. Then
|
|
|
4369
4614
|
continue \`mcp__haven__haven_pay_mcp_tool\` \u2192
|
|
4370
4615
|
\`mcp__haven-signer__haven_sign\` \u2192 \`mcp__haven__haven_submit\` \u2192
|
|
4371
4616
|
\`mcp__haven-signer__haven_x402_sign_header\` \u2192
|
|
4372
|
-
\`mcp__haven__haven_complete_mcp_tool\`.
|
|
4373
|
-
\`
|
|
4617
|
+
\`mcp__haven__haven_complete_mcp_tool\`. Call that last step with
|
|
4618
|
+
\`payment_id\` and the signer's \`payment_header\` ONLY. It does not take
|
|
4619
|
+
\`payment_required\`: Haven rehydrates the merchant call context
|
|
4620
|
+
(\`merchant_url\`, \`tool_name\`, \`arguments\`, \`mcp_transport\`) and the
|
|
4621
|
+
402 server-side from \`payment_id\`, exactly as at settle. Pass that context
|
|
4622
|
+
explicitly only as a version-skew fallback when Haven has no stored context
|
|
4623
|
+
for the id \u2014 \`merchant_url\` and \`tool_name\` both or none together, never
|
|
4624
|
+
just one.
|
|
4374
4625
|
The returned \`expires_at\` is the signing window; if a tool returns
|
|
4375
4626
|
\`PAYMENT_WINDOW_EXPIRED\`, re-run the same quote/prepare tool with the same
|
|
4376
4627
|
\`idempotency_key\`. Do not call the merchant yourself \u2014 Haven completes the
|
|
4377
4628
|
merchant leg for you.
|
|
4378
4629
|
|
|
4379
4630
|
**Direct transfer / non-MCP paywall:** \`mcp__haven__haven_pay\` with
|
|
4380
|
-
|
|
4631
|
+
\`to\`, \`amount\`, and \`token\` for a plain transfer. For an arbitrary,
|
|
4381
4632
|
non-MCP x402 paywall: \`mcp__haven__haven_quote_x402\` to get a quote, then
|
|
4382
4633
|
\`mcp__haven__haven_pay_x402_quote\` \u2014 follow the result's guidance fields
|
|
4383
4634
|
first and sign in the local Haven signer. On THIS path Haven does not talk to
|
|
@@ -4428,7 +4679,7 @@ check on in-flight payments. Do not poll in a tight loop.
|
|
|
4428
4679
|
- Never ask the user for private keys. Signing happens only in the local Haven
|
|
4429
4680
|
signer; the hosted Haven tools never receive the signing key. If a tool
|
|
4430
4681
|
reports a missing or invalid credential, tell the user to re-run the Haven
|
|
4431
|
-
|
|
4682
|
+
connector command.
|
|
4432
4683
|
|
|
4433
4684
|
## Failure handling
|
|
4434
4685
|
|
|
@@ -4563,10 +4814,20 @@ function sameUrl(a, b) {
|
|
|
4563
4814
|
}
|
|
4564
4815
|
}
|
|
4565
4816
|
|
|
4817
|
+
exports.AGENT_APPROVAL_RELAY_JSON_SENTENCE = AGENT_APPROVAL_RELAY_JSON_SENTENCE;
|
|
4818
|
+
exports.AGENT_APPROVAL_RELAY_PROSE_SENTENCE = AGENT_APPROVAL_RELAY_PROSE_SENTENCE;
|
|
4819
|
+
exports.AGENT_COMMAND_MODIFICATION_SENTENCE = AGENT_COMMAND_MODIFICATION_SENTENCE;
|
|
4820
|
+
exports.AGENT_JSON_MODE_SENTENCE = AGENT_JSON_MODE_SENTENCE;
|
|
4821
|
+
exports.AGENT_LOCAL_KEY_SENTENCE = AGENT_LOCAL_KEY_SENTENCE;
|
|
4822
|
+
exports.AGENT_NETWORK_ACCESS_SENTENCE = AGENT_NETWORK_ACCESS_SENTENCE;
|
|
4823
|
+
exports.AGENT_ONBOARDING_PROMPT = AGENT_ONBOARDING_PROMPT;
|
|
4566
4824
|
exports.AGENT_PAYMENT_FAILURE_CODE_VALUES = AGENT_PAYMENT_FAILURE_CODE_VALUES;
|
|
4567
4825
|
exports.AGENT_PAYMENT_NEXT_ACTION_VALUES = AGENT_PAYMENT_NEXT_ACTION_VALUES;
|
|
4568
4826
|
exports.AGENT_PAYMENT_PHASE_VALUES = AGENT_PAYMENT_PHASE_VALUES;
|
|
4569
4827
|
exports.AGENT_PAYMENT_RAIL_VALUES = AGENT_PAYMENT_RAIL_VALUES;
|
|
4828
|
+
exports.AGENT_README_SECTION_MD = AGENT_README_SECTION_MD;
|
|
4829
|
+
exports.AGENT_SECRET_HYGIENE_SENTENCE = AGENT_SECRET_HYGIENE_SENTENCE;
|
|
4830
|
+
exports.AGENT_WIRING_COLLISION_RELAY_SENTENCE = AGENT_WIRING_COLLISION_RELAY_SENTENCE;
|
|
4570
4831
|
exports.AgentPaymentFailureCode = AgentPaymentFailureCode;
|
|
4571
4832
|
exports.AgentPaymentFailureCodeDescriptions = AgentPaymentFailureCodeDescriptions;
|
|
4572
4833
|
exports.AgentPaymentFailureCodeSchema = AgentPaymentFailureCodeSchema;
|
|
@@ -4580,9 +4841,12 @@ exports.AgentPaymentRail = AgentPaymentRail;
|
|
|
4580
4841
|
exports.AgentPaymentRailDescriptions = AgentPaymentRailDescriptions;
|
|
4581
4842
|
exports.AgentPaymentRailSchema = AgentPaymentRailSchema;
|
|
4582
4843
|
exports.AgentPaymentWarningCode = AgentPaymentWarningCode;
|
|
4844
|
+
exports.CONNECTOR_PACKAGE_NAME = CONNECTOR_PACKAGE_NAME;
|
|
4583
4845
|
exports.DEFAULT_CONFIRMATION_TIMEOUT_MS = DEFAULT_CONFIRMATION_TIMEOUT_MS;
|
|
4584
4846
|
exports.DISCOVERY_MAX_BYTES = DISCOVERY_MAX_BYTES;
|
|
4585
4847
|
exports.ERC7710_ASSET_TRANSFER_METHOD = ERC7710_ASSET_TRANSFER_METHOD;
|
|
4848
|
+
exports.HAVEN_AGENT_RUNBOOK_MD = HAVEN_AGENT_RUNBOOK_MD;
|
|
4849
|
+
exports.HAVEN_CONNECTOR_CHANNEL = HAVEN_CONNECTOR_CHANNEL;
|
|
4586
4850
|
exports.HAVEN_MINIMUM_NODE_VERSION = HAVEN_MINIMUM_NODE_VERSION;
|
|
4587
4851
|
exports.HAVEN_SKILL_BODY_MD = HAVEN_SKILL_BODY_MD;
|
|
4588
4852
|
exports.HAVEN_SKILL_MD = HAVEN_SKILL_MD;
|
|
@@ -4620,6 +4884,8 @@ exports.buildSweepTypedData = buildSweepTypedData;
|
|
|
4620
4884
|
exports.buildX402ExpectedMessage = buildX402ExpectedMessage;
|
|
4621
4885
|
exports.compareNodeVersions = compareNodeVersions;
|
|
4622
4886
|
exports.composeDescription = composeDescription;
|
|
4887
|
+
exports.connectorRerunCommand = connectorRerunCommand;
|
|
4888
|
+
exports.connectorSpec = connectorSpec;
|
|
4623
4889
|
exports.decodeBase64Json = decodeBase64Json;
|
|
4624
4890
|
exports.decodeBase64Utf8 = decodeBase64Utf8;
|
|
4625
4891
|
exports.discoverMerchantMcpUrl = discoverMerchantMcpUrl;
|
|
@@ -4627,12 +4893,14 @@ exports.encodeBase64Json = encodeBase64Json;
|
|
|
4627
4893
|
exports.encodeBase64Utf8 = encodeBase64Utf8;
|
|
4628
4894
|
exports.encodePaymentProof = encodePaymentProof;
|
|
4629
4895
|
exports.havenTools = havenTools;
|
|
4896
|
+
exports.isConnectorChannel = isConnectorChannel;
|
|
4630
4897
|
exports.isErc7710Option = isErc7710Option;
|
|
4631
4898
|
exports.isSupportedNodeVersion = isSupportedNodeVersion;
|
|
4632
4899
|
exports.isSweepableChain = isSweepableChain;
|
|
4633
4900
|
exports.normalizePaymentRequired = normalizePaymentRequired;
|
|
4634
4901
|
exports.parsePaymentRequired = parsePaymentRequired;
|
|
4635
4902
|
exports.parsePaymentRequiredResponse = parsePaymentRequiredResponse;
|
|
4903
|
+
exports.resolveConnectorChannel = resolveConnectorChannel;
|
|
4636
4904
|
exports.resolveTokenFromAddress = resolveTokenFromAddress;
|
|
4637
4905
|
exports.sameUrl = sameUrl;
|
|
4638
4906
|
exports.selectErc7710PaymentOption = selectErc7710PaymentOption;
|
|
@@ -4641,6 +4909,7 @@ exports.selectStandardPaymentOption = selectStandardPaymentOption;
|
|
|
4641
4909
|
exports.selectX402SettlementScheme = selectX402SettlementScheme;
|
|
4642
4910
|
exports.signHash = signHash;
|
|
4643
4911
|
exports.signUserOpTypedDataForDelegation = signUserOpTypedDataForDelegation;
|
|
4912
|
+
exports.signerUpdateFallback = signerUpdateFallback;
|
|
4644
4913
|
exports.sweepUsdcAddress = sweepUsdcAddress;
|
|
4645
4914
|
exports.sweepUsdcDomain = sweepUsdcDomain;
|
|
4646
4915
|
exports.toStandardPaymentRequirements = toStandardPaymentRequirements;
|
|
@@ -4652,5 +4921,6 @@ exports.verifySignature = verifySignature;
|
|
|
4652
4921
|
exports.x402AssetTransferMethod = x402AssetTransferMethod;
|
|
4653
4922
|
exports.x402AuthorizationAmount = x402AuthorizationAmount;
|
|
4654
4923
|
exports.x402FacilitatorAddresses = x402FacilitatorAddresses;
|
|
4924
|
+
exports.x402V2PaymentEnvelope = x402V2PaymentEnvelope;
|
|
4655
4925
|
//# sourceMappingURL=index.cjs.map
|
|
4656
4926
|
//# sourceMappingURL=index.cjs.map
|