@botanary/agent 0.1.0-alpha.1 → 0.1.0-alpha.2
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/CHANGELOG.md +16 -0
- package/README.md +20 -5
- package/dist/errors.d.ts +1 -38
- package/dist/exact-action.d.ts +0 -8
- package/dist/fingerprint.d.ts +0 -15
- package/dist/generated/routes.d.ts +0 -1
- package/dist/generated/schema.d.ts +0 -1090
- package/dist/http-path.d.ts +0 -81
- package/dist/index.d.ts +0 -1
- package/dist/index.js +2 -7
- package/dist/pairing-code.d.ts +0 -23
- package/dist/registration.d.ts +0 -3
- package/dist/runtime.d.ts +0 -127
- package/dist/signer.d.ts +0 -7
- package/dist/transport.d.ts +0 -3
- package/dist/validation.d.ts +0 -3
- package/dist/views.d.ts +0 -54
- package/package.json +6 -2
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -40
- package/dist/errors.js.map +0 -1
- package/dist/exact-action.d.ts.map +0 -1
- package/dist/exact-action.js +0 -178
- package/dist/exact-action.js.map +0 -1
- package/dist/fingerprint.d.ts.map +0 -1
- package/dist/fingerprint.js +0 -47
- package/dist/fingerprint.js.map +0 -1
- package/dist/generated/routes.d.ts.map +0 -1
- package/dist/generated/routes.js +0 -92
- package/dist/generated/routes.js.map +0 -1
- package/dist/generated/schema.d.ts.map +0 -1
- package/dist/generated/schema.js +0 -2
- package/dist/generated/schema.js.map +0 -1
- package/dist/http-path.d.ts.map +0 -1
- package/dist/http-path.js +0 -201
- package/dist/http-path.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/pairing-code.d.ts.map +0 -1
- package/dist/pairing-code.js +0 -60
- package/dist/pairing-code.js.map +0 -1
- package/dist/registration.d.ts.map +0 -1
- package/dist/registration.js +0 -62
- package/dist/registration.js.map +0 -1
- package/dist/runtime.d.ts.map +0 -1
- package/dist/runtime.js +0 -559
- package/dist/runtime.js.map +0 -1
- package/dist/signer.d.ts.map +0 -1
- package/dist/signer.js +0 -13
- package/dist/signer.js.map +0 -1
- package/dist/transport.d.ts.map +0 -1
- package/dist/transport.js +0 -135
- package/dist/transport.js.map +0 -1
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -108
- package/dist/validation.js.map +0 -1
- package/dist/views.d.ts.map +0 -1
- package/dist/views.js +0 -2
- package/dist/views.js.map +0 -1
package/dist/http-path.d.ts
CHANGED
|
@@ -1,92 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ONE PARSE. The string that is validated is the string that is requested.
|
|
3
|
-
*
|
|
4
|
-
* ------------------------------------------------------------------------------------------------
|
|
5
|
-
* THE BUG THIS EXISTS TO CLOSE
|
|
6
|
-
* ------------------------------------------------------------------------------------------------
|
|
7
|
-
* `wallet_api_get` and `wallet_api_write` both take a free-form, model-supplied `path` and check it
|
|
8
|
-
* against a generated manifest by compiling each manifest template into a regex, with a `{param}`
|
|
9
|
-
* segment becoming `[^/]+`. The compiled string was then re-assembled and handed to `fetch`, whose
|
|
10
|
-
* WHATWG URL parser re-parses it - and that parser does NOT agree with `[^/]+` about what a path
|
|
11
|
-
* segment is. It normalises `\` to `/`, resolves `.` and `..` (including their percent-encoded forms
|
|
12
|
-
* `%2e` / `%2e%2e`), and truncates everything from the first `#`. So:
|
|
13
|
-
*
|
|
14
|
-
* isKnownWritePath('/notifications/..\\userops#/read', 'POST') -> true
|
|
15
|
-
* ...and the request actually issued was POST /v1/userops
|
|
16
|
-
*
|
|
17
|
-
* Two different strings: the one the allowlist inspected, and the one that went on the wire. Every
|
|
18
|
-
* denylist entry in both manifests - the `/userops` relay boundary, `/approvals/{digest}/relay`,
|
|
19
|
-
* `/solana/transactions`, `/agent-wallets/partner-provision`, and on the read side `/auth/session`,
|
|
20
|
-
* which hands back the owner's live session token - was reachable through that gap.
|
|
21
|
-
*
|
|
22
|
-
* ------------------------------------------------------------------------------------------------
|
|
23
|
-
* WHY THIS CLOSES THE CLASS RATHER THAN THE INSTANCES
|
|
24
|
-
* ------------------------------------------------------------------------------------------------
|
|
25
|
-
* Blocklisting the tokens (`..`, `\`, `#`) loses: there is always another encoding (`%2e%2e`, `%5c`,
|
|
26
|
-
* `%23`, `%252e%252e`, and whatever the next parser quirk turns out to be). The root cause is not any
|
|
27
|
-
* particular character - it is that VALIDATION AND REQUEST OPERATED ON DIFFERENT STRINGS. So this
|
|
28
|
-
* module removes the difference instead of enumerating it:
|
|
29
|
-
*
|
|
30
|
-
* 1. The caller's path is canonicalised ONCE, up front, before anything looks at a manifest.
|
|
31
|
-
* 2. Canonicalisation is a FIXED-POINT test against the very parser `fetch` uses: the path is
|
|
32
|
-
* re-parsed with `new URL(...)`, and if `pathname + search` is not byte-identical to the input,
|
|
33
|
-
* the path is refused. Anything the URL parser would rewrite - a traversal in any encoding, a
|
|
34
|
-
* backslash, a fragment, a character it would percent-encode, a `//` that changes the host - is
|
|
35
|
-
* by definition not a fixed point, and is rejected without this module ever having to know that
|
|
36
|
-
* particular trick's name.
|
|
37
|
-
* 3. A structural charset allowlist runs first, per segment, so the fixed-point test is a second
|
|
38
|
-
* opinion rather than the only one: a segment may contain only unreserved/sub-delim characters
|
|
39
|
-
* and well-formed `%XX` triplets, and must not decode - through as many layers of encoding as it
|
|
40
|
-
* carries - to a separator, a fragment marker, or `.`/`..`.
|
|
41
|
-
* 4. The single canonical result is what the caller then matches AND requests. There is no second
|
|
42
|
-
* re-assembly step for the two to drift apart in.
|
|
43
|
-
*
|
|
44
|
-
* The `{param}` compilation is tightened to match: `PARAM_SEGMENT_PATTERN` is the same charset, so a
|
|
45
|
-
* parameter structurally cannot hold a separator or a fragment marker even before rule 2 speaks.
|
|
46
|
-
*/
|
|
47
|
-
/**
|
|
48
|
-
* What a `{param}` in a manifest template compiles to. The old `[^/]+` was the bug: to a regex, `\`,
|
|
49
|
-
* `#` and `..` are ordinary characters, and to the URL parser they are separators. This charset holds
|
|
50
|
-
* no separator and no fragment marker in any form the URL parser recognises.
|
|
51
|
-
*/
|
|
52
1
|
export declare const PARAM_SEGMENT_PATTERN = "(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})+";
|
|
53
|
-
/** The canonical form of a caller-supplied path: one object, carried from validation to `fetch`. */
|
|
54
2
|
export interface CanonicalHttpPath {
|
|
55
|
-
/** The path, no query string. Byte-identical to what will be requested. */
|
|
56
3
|
readonly path: string;
|
|
57
|
-
/** The query string including its leading `?`, or `''`. Byte-identical to what will be requested. */
|
|
58
4
|
readonly query: string;
|
|
59
|
-
/** `path + query` - exactly the string that goes on the wire after the base URL. */
|
|
60
5
|
readonly full: string;
|
|
61
6
|
}
|
|
62
|
-
/**
|
|
63
|
-
* Canonicalise a caller-supplied request path, or return null if it is not canonical.
|
|
64
|
-
*
|
|
65
|
-
* "Canonical" means: an absolute path (never protocol-relative, which would change the HOST), every
|
|
66
|
-
* segment drawn from the safe charset and free of any encoded separator or traversal, an optional
|
|
67
|
-
* query drawn from its own charset, no fragment at all - and, as the final word, a string the WHATWG
|
|
68
|
-
* URL parser reproduces unchanged. Callers must use the returned strings, never their own input.
|
|
69
|
-
*/
|
|
70
7
|
export declare function canonicalizeHttpPath(raw: unknown): CanonicalHttpPath | null;
|
|
71
|
-
/**
|
|
72
|
-
* Strip the `/v1` prefix, if present, so `/balance` and `/v1/balance` resolve to the same manifest
|
|
73
|
-
* entry. The OpenAPI contract declares paths WITHOUT the version prefix (`/v1` lives in
|
|
74
|
-
* `servers[].url`), so the generated manifests are un-prefixed - this is where the two conventions
|
|
75
|
-
* meet. Operates on an ALREADY-CANONICAL path; the old code did this to the raw input, which is how a
|
|
76
|
-
* `/v1`-prefixed bypass got a second bite.
|
|
77
|
-
*/
|
|
78
8
|
export declare function stripVersionPrefix(canonicalPath: string): string;
|
|
79
|
-
/**
|
|
80
|
-
* Compile one manifest template (`/agents/{id}`) into a matcher for a concrete canonical path
|
|
81
|
-
* (`/agents/agt_1`). Shared by both allowlists so they cannot drift into two different ideas of what a
|
|
82
|
-
* path parameter may contain - which is how one of them ends up with the other's hole.
|
|
83
|
-
*/
|
|
84
9
|
export declare function compileRoutePattern(template: string): RegExp;
|
|
85
|
-
/** Cap what a refusal echoes back, so a pathological input cannot flood a tool result. */
|
|
86
10
|
export declare function describePath(raw: unknown): string;
|
|
87
|
-
/**
|
|
88
|
-
* The refusal text every caller shares, so the reason a path was rejected reads the same wherever it
|
|
89
|
-
* is rejected - and names the real cause rather than the character that happened to trip it.
|
|
90
|
-
*/
|
|
91
11
|
export declare function nonCanonicalPathMessage(raw: unknown, tool: string): string;
|
|
92
|
-
//# sourceMappingURL=http-path.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,3 @@ export type * from './views.js';
|
|
|
8
8
|
export { PairingCodeManager, DEFAULT_PAIRING_CODE_TTL_MS, type PairingCode } from './pairing-code.js';
|
|
9
9
|
export { crockfordBase32, deriveFingerprint, generatePairingNonce, FINGERPRINT_BYTES, NONCE_BYTES } from './fingerprint.js';
|
|
10
10
|
export type { ExactAgentAction, ExactAgentExecution, AgentActionBinding, AgentSwapBinding, SwapRouteFacts, PreparedAgentAction } from './exact-action.js';
|
|
11
|
-
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,2 @@
|
|
|
1
|
-
export { AgentRuntime, awaitTerminalWith, DEFAULT_API_BASE_URL } from './runtime.js';
|
|
2
|
-
|
|
3
|
-
export { createViemAgentSigner } from './signer.js';
|
|
4
|
-
export { registerAgent } from './registration.js';
|
|
5
|
-
export { PairingCodeManager, DEFAULT_PAIRING_CODE_TTL_MS } from './pairing-code.js';
|
|
6
|
-
export { crockfordBase32, deriveFingerprint, generatePairingNonce, FINGERPRINT_BYTES, NONCE_BYTES } from './fingerprint.js';
|
|
7
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
var m=class extends Error{constructor(t,n,i=null,c=null){super(t);this.status=n;this.details=i;this.retryAfterSeconds=c;this.name="BotanaryApiError"}requestId;code;operationId;userOpHash;requirementId;ambiguous=!1};import{encodeFunctionData as E,encodePacked as Pe,decodeFunctionData as F,parseAbi as C,getAddress as I,zeroAddress as ve,zeroHash as Re}from"viem";import{entryPoint07Address as ee,getUserOperationHash as Se}from"viem/account-abstraction";import{publicKeyToAddress as Ie}from"viem/accounts";function a(){let r=new m("Botanary returned an invalid response. Nothing was signed.",0);throw r.code="invalid_response",r}function y(r){return typeof r!="object"||r===null||Array.isArray(r)?a():r}function u(r,e){(typeof r!="string"||!/^0x(?:[0-9a-fA-F]{2})*$/.test(r)||e!==void 0&&r.length!==2+2*e)&&a()}function w(r){(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||/^(?:bsk|ags|dcs|dca|dcr)_/.test(r))&&a()}function L(r){(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r)||BigInt(r)>=2n**256n)&&a()}function H(r){let e=y(r);return u(e.address,20),u(e.publicKey,65),(!e.publicKey.startsWith("0x04")||Ie(e.publicKey).toLowerCase()!==e.address.toLowerCase()||typeof e.fingerprint!="string"||!/^[A-Za-z0-9]{1,32}$/.test(e.fingerprint)||typeof e.backend!="string"||!/^[A-Za-z0-9_-]{1,64}$/.test(e.backend)||typeof e.createdAt!="string"||!Number.isFinite(Date.parse(e.createdAt)))&&a(),Object.freeze({address:e.address,publicKey:e.publicKey,fingerprint:e.fingerprint,backend:e.backend,createdAt:e.createdAt})}function x(r,e=256){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77}|0x[0-9a-fA-F]{1,64})$/.test(r))return a();let t=BigInt(r);return t>=2n**BigInt(e)?a():t}function T(r){let e=y(r);for(let t of["sender","entryPoint"])u(e[t],20);for(let t of["nonce","callGasLimit","verificationGasLimit","preVerificationGas","maxFeePerGas","maxPriorityFeePerGas"])x(e[t]);for(let t of["callData","signature"])u(e[t]);for(let t of["paymasterVerificationGasLimit","paymasterPostOpGasLimit"])e[t]!=null&&x(e[t],128);(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&a();for(let t of["factory","paymaster"])e[t]!=null&&u(e[t],20);for(let t of["factoryData","paymasterData"])e[t]!=null&&u(e[t]);return r}function U(r){let e=y(r);u(e.payload,32),u(e.validator,20),(!Number.isSafeInteger(e.index)||Number(e.index)<0||Number(e.index)>=2**32)&&a();let t=y(e.authorization);return u(t.from,20),u(t.to,20),u(t.nonce,32),L(t.value),L(t.validAfter),L(t.validBefore),BigInt(t.validBefore)<=BigInt(t.validAfter)&&a(),{payload:e.payload,validator:e.validator,index:Number(e.index),authorization:{from:t.from,to:t.to,nonce:t.nonce,value:t.value,validAfter:t.validAfter,validBefore:t.validBefore}}}function z(r,e){let t=y(r);(t.requirementId!==e||typeof t.simulated!="boolean")&&a();let n={ready:["not_submitted"],pending_approval:["not_submitted"],submitted:["pending"],settled:["settled"],declined:["not_submitted","not_settled"]};return(typeof t.status!="string"||!Object.hasOwn(n,t.status)||!n[t.status].includes(String(t.paymentStatus)))&&a(),t.providerStatus!==void 0&&(!Number.isSafeInteger(t.providerStatus)||Number(t.providerStatus)<100||Number(t.providerStatus)>599)&&a(),t.txHash!==void 0&&u(t.txHash,32),t.status==="settled"&&u(t.txHash,32),t.status==="ready"?U(t.ready):t.ready!==void 0&&a(),t}var te=C(["function execute(bytes32 mode, bytes executionCalldata) payable"]),j=C(["function executeUnderGrant(bytes32 permissionId, address target, uint256 value, bytes callData)","function executeUnderGrantWithAllowance(bytes32 permissionId, address target, uint256 value, address token, uint256 allowance, bytes callData)"]),Oe=C(["function transfer(address to, uint256 amount) returns (bool)"]),ne=C(["function swapExactIn(address tokenIn, address tokenOut, uint256 amountIn, uint256 minOut, address to) returns (uint256 amountOut)"]),Ee=BigInt("0x000100000000008bdaba73cd9815d79069c247eb4bda0000");function f(r,e){return typeof r=="string"&&r.toLowerCase()===e.toLowerCase()}function b(r,e=!0){if(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r))return a();let t=BigInt(r);return t>=2n**256n||e&&t===0n?a():t}function re(r){let e=y(r),t=["accountId","accountAddress","chainId","action","tokenAddress","amount","recipient","tokenOutAddress","maxSlippageBps"];return Object.keys(e).some(n=>!t.includes(n))&&a(),w(e.accountId),u(e.accountAddress,20),u(e.tokenAddress,20),b(e.amount),(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&a(),e.action==="transfer"?(u(e.recipient,20),(e.tokenOutAddress!==void 0||e.maxSlippageBps!==void 0)&&a()):e.action==="swap"?(u(e.tokenOutAddress,20),(e.recipient!==void 0||e.maxSlippageBps!==void 0&&(!Number.isInteger(e.maxSlippageBps)||Number(e.maxSlippageBps)<1||Number(e.maxSlippageBps)>1e3))&&a()):a(),Object.freeze({...r,accountAddress:I(r.accountAddress),tokenAddress:I(r.tokenAddress),...r.recipient!==void 0?{recipient:I(r.recipient)}:{},...r.tokenOutAddress!==void 0?{tokenOutAddress:I(r.tokenOutAddress)}:{}})}function ie(r,e){if(w(r.delegationId),u(r.permissionId,32),u(r.executorAddress,20),b(r.maxGasCostWei),e.action==="swap"){let t=r;u(t.routerAddress,20),b(t.minAmountOut),t.verifyRoute!==void 0&&typeof t.verifyRoute!="function"&&a()}return Object.freeze({...r,executorAddress:I(r.executorAddress),...e.action==="swap"?{routerAddress:I(r.routerAddress)}:{}})}async function se(r,e,t){try{let n=y(r),i=y(n.execution),c=T(n.userOp),s=Object.freeze({...c,sender:I(c.sender),entryPoint:I(c.entryPoint)});u(n.userOpHash,32),(n.intentType!=="delegated_action"||y(n.simulation).willSucceed!==!0||i.accountId!==e.accountId||!f(i.accountAddress,e.accountAddress)||i.chainId!==e.chainId||i.action!==e.action||i.amount!==e.amount||!f(i.tokenAddress,e.tokenAddress)||i.delegationId!==t.delegationId||!f(i.permissionId,t.permissionId)||i.gasMethod!=="native"||!f(s.sender,e.accountAddress)||s.chainId!==e.chainId||!f(s.entryPoint,ee)||s.factory!=null||s.factoryData!=null||s.paymaster!=null||s.paymasterData!=null||s.paymasterVerificationGasLimit!=null||s.paymasterPostOpGasLimit!=null)&&a();let o=x(s.nonce);o>>64n!==Ee&&a();let d=x(s.callGasLimit,128),g=x(s.verificationGasLimit,128),p=x(s.preVerificationGas),k=x(s.maxFeePerGas,128),h=x(s.maxPriorityFeePerGas,128);(h>k||(d+g+p)*k>b(t.maxGasCostWei))&&a();let l=s.callData,P;if(e.action==="transfer"){(!f(i.recipient,e.recipient)||i.tokenOutAddress!==void 0||i.maxSlippageBps!==void 0)&&a();let A=f(e.tokenAddress,ve);P=E({abi:j,functionName:"executeUnderGrant",args:[t.permissionId,A?e.recipient:e.tokenAddress,A?b(e.amount):0n,A?"0x":E({abi:Oe,functionName:"transfer",args:[e.recipient,b(e.amount)]})]})}else{let A=t;(!f(i.tokenOutAddress,e.tokenOutAddress)||i.recipient!==void 0||i.maxSlippageBps!==(e.maxSlippageBps??50))&&a();let G=F({abi:te,data:l}).args[1],v=F({abi:j,data:`0x${G.slice(106)}`});v.functionName!=="executeUnderGrantWithAllowance"&&a();let S=v.args[5],O=Object.freeze({chainId:e.chainId,accountAddress:e.accountAddress,routerAddress:A.routerAddress,tokenAddress:e.tokenAddress,tokenOutAddress:e.tokenOutAddress,amount:e.amount,minAmountOut:A.minAmountOut,callData:S});if(A.verifyRoute)await A.verifyRoute(O)!==!0&&a();else{let J=F({abi:ne,data:S}),[Ae,we,xe,be,ke]=J.args;(!f(Ae,e.tokenAddress)||!f(we,e.tokenOutAddress)||xe!==b(e.amount)||be<b(A.minAmountOut)||!f(ke,e.accountAddress)||!f(E({abi:ne,functionName:"swapExactIn",args:J.args}),S))&&a()}P=E({abi:j,functionName:"executeUnderGrantWithAllowance",args:[t.permissionId,A.routerAddress,0n,e.tokenAddress,b(e.amount),S]})}let R=E({abi:te,functionName:"execute",args:[Re,Pe(["address","uint256","bytes"],[t.executorAddress,0n,P])]});f(l,R)||a();let N=Se({entryPointAddress:ee,entryPointVersion:"0.7",chainId:e.chainId,userOperation:{sender:s.sender,nonce:o,callData:s.callData,callGasLimit:d,verificationGasLimit:g,preVerificationGas:p,maxFeePerGas:k,maxPriorityFeePerGas:h,signature:"0x"}});f(N,n.userOpHash)||a();let $=Object.freeze({...e,delegationId:t.delegationId,permissionId:t.permissionId,gasMethod:"native",...e.action==="swap"?{maxSlippageBps:e.maxSlippageBps??50}:{}});return{userOp:Object.freeze({sender:s.sender,chainId:s.chainId,entryPoint:s.entryPoint,nonce:s.nonce,callData:s.callData,callGasLimit:s.callGasLimit,verificationGasLimit:s.verificationGasLimit,preVerificationGas:s.preVerificationGas,maxFeePerGas:s.maxFeePerGas,maxPriorityFeePerGas:s.maxPriorityFeePerGas,signature:"0x"}),userOpHash:N,execution:$}}catch{return a()}}function M(r,e){try{let t=y(r);if(w(t.id),!f(t.userOpHash,e)||!["pending","included","failed"].includes(String(t.status)))throw new Error;return t.txHash!=null&&u(t.txHash,32),t}catch{let t=new m("Botanary returned an invalid receipt. Reconcile using the original operation hash.",0);throw t.code="invalid_response",t.userOpHash=e,t.ambiguous=!0,t}}import{keccak256 as Le,toHex as ze,encodePacked as me,encodeAbiParameters as Fe}from"viem";import{randomBytes as _e}from"node:crypto";import{hexToBytes as Be}from"viem";var oe="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function V(r){let e=0,t=0,n="";for(let i of r)for(e=e<<8|i,t+=8;t>=5;)n+=oe[e>>>t-5&31],t-=5;return t>0&&(n+=oe[e<<5-t&31]),n}var ae=3,ce=5;function K(r){let e=Be(r);return V(e.slice(0,ae))}function W(){return V(_e(ce))}var de=600*1e3,_=class{#r;#e;#c;#o;#i=null;constructor(e,t){this.#r=e,this.#e=t?.ttlMs??de,this.#c=t?.now??Date.now,this.#o=t?.nonce??W}current(){let e=this.#c();return(!this.#i||this.#i.expiresAt<=e)&&this.#d(e),this.#s()}regenerate(){return this.#d(this.#c()),this.#s()}#d(e){this.#i={nonce:this.#o(),issuedAt:e,expiresAt:e+this.#e}}#s(){let e=this.#i;if(!e)throw new Error("unreachable: no pairing code minted");return{code:`${this.#r}-${e.nonce}`,fingerprint:this.#r,nonce:e.nonce,issuedAt:new Date(e.issuedAt).toISOString(),expiresAt:new Date(e.expiresAt).toISOString()}}};import qe from"openapi-fetch";var ue=[{method:"post",path:"/agents/pair"},{method:"post",path:"/agents/session/nonce"},{method:"post",path:"/agents/session"},{method:"get",path:"/agents/me"},{method:"get",path:"/agents/requests"},{method:"post",path:"/agents/requests"},{method:"get",path:"/balance"},{method:"get",path:"/chains"},{method:"get",path:"/gas/methods"},{method:"get",path:"/account"},{method:"post",path:"/delegations/{delegationId}/actions"},{method:"post",path:"/delegations/{delegationId}/actions/exact"},{method:"post",path:"/userops"},{method:"get",path:"/userops/{userOpId}"},{method:"get",path:"/userops/by-hash/{chainId}/{userOpHash}"},{method:"get",path:"/apis/providers"},{method:"get",path:"/apis/providers/{providerId}"},{method:"get",path:"/apis/budget"},{method:"post",path:"/apis/calls/requirements"},{method:"get",path:"/apis/calls/requirements/{id}"},{method:"post",path:"/apis/calls/relay"},{method:"get",path:"/apis/calls/relay/{id}"}];var pe="https://botanary-canonical-path.invalid",ge="(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})",ut=`${ge}+`,He=new RegExp(`^${ge}+$`),Te=new RegExp("^(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$"),Ue=/^\.{1,2}$/,Ce=/[/\\#?]/,De=/[\u0000-\u001f\u007f]/,Ne=6;function $e(r){let e=r;for(let t=0;t<Ne;t+=1){if(Ue.test(e)||Ce.test(e)||De.test(e))return!0;if(!e.includes("%"))return!1;let n;try{n=decodeURIComponent(e)}catch{return!0}if(n===e)return!1;e=n}return!0}function le(r){if(typeof r!="string"||r.length===0||!r.startsWith("/")||r.startsWith("//"))return null;let e=r.indexOf("?"),t=e===-1?r:r.slice(0,e),n=e===-1?"":r.slice(e),i=t.split("/");for(let s=1;s<i.length;s+=1){let o=i[s];if(!He.test(o)||$e(o))return null}if(n!==""&&!Te.test(n.slice(1)))return null;let c;try{c=new URL(t+n,pe)}catch{return null}return c.origin!==pe||c.hash!==""||c.pathname!==t||c.search!==n?null:{path:t,query:n,full:t+n}}var D=class{#r;constructor(e,t=fetch){let n=new URL(e);if(n.username||n.password||n.search||n.hash||n.pathname!=="/"||n.protocol!=="https:"&&!(n.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(n.hostname)))throw new Error("Agent API origin must be HTTPS, or HTTP on loopback.");this.#r=qe({baseUrl:n.origin+"/v1",fetch:Ge(t)})}get(e,t){return this.#e("get",e,void 0,t)}post(e,t,n){return this.#e("post",e,t,n)}async#e(e,t,n,i){let c=le(t);if(!c?.path.startsWith("/v1/"))throw new Error("Invalid agent API path.");if(i!==void 0&&!/^ags_[a-f0-9]{64}$/.test(i))throw new Error("Expected an agent session credential.");let s=c.path.slice(3),o=ue.find(h=>h.method===e&&new RegExp("^"+h.path.replace(/\{[^}]+\}/g,"[A-Za-z0-9_-]+")+"$").test(s));if(!o)throw new Error("This route is outside the reviewed agent API.");let d={};o.path.split("/").forEach((h,l)=>{h.startsWith("{")&&(d[h.slice(1,-1)]=s.split("/")[l])});let g=Object.fromEntries(new URLSearchParams(c.query)),p=new AbortController,k=setTimeout(()=>p.abort(),3e4);try{let h=await this.#r.request(e,o.path,{params:{path:d,query:g},...n===void 0?{}:{body:n},headers:i?{authorization:`Bearer ${i}`}:{},credentials:"omit",redirect:"error",cache:"no-store",signal:p.signal,parseAs:"text"}),l=h.response,P=h.error;if(l.ok)try{P=h.data?JSON.parse(h.data):{}}catch{throw new Error("Invalid JSON response.")}if(!l.ok){let R=Z(Z(P)?.error),$=(typeof R?.message=="string"?R.message:`Agent request failed with ${l.status}.`).replace(/(?:bsk_(?:test|live)|ags|dcs|dca|dcr)_[A-Za-z0-9_-]+/g,"[redacted]").replace(/0x[a-fA-F0-9]{64,}/g,"[redacted]"),B=l.headers.get("retry-after"),A=B&&/^\d+(?:\.\d+)?$/.test(B)?Number(B):null,q=Z(R?.details),G=q&&Object.fromEntries(Object.entries(q).filter(([S,O])=>["missingModule","declineReason","chainId"].includes(S)&&(typeof O=="number"||typeof O=="string"&&/^[a-zA-Z0-9_-]{1,100}$/.test(O)))),v=new m($.slice(0,1e3),l.status,G,A);throw v.requestId=he(l.headers.get("x-request-id")),v.code=he(R?.code)??"http_error",v.ambiguous=e==="post"&&l.status>=500,v}return P}catch(h){if(h instanceof m)throw h;let l=new m("The agent request did not complete. Check the existing operation before retrying.",0);throw l.code=p.signal.aborted?"timeout":"transport_error",l.ambiguous=e==="post",l}finally{clearTimeout(k)}}};function Z(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:null}function he(r){return typeof r=="string"&&/^[a-zA-Z0-9_-]{1,128}$/.test(r)&&!/^(?:ags|bsk|dcs|dca|dcr)_/.test(r)?r:void 0}function Ge(r){return async(e,t)=>{let n=e instanceof Request?e:new Request(e,t),i=n.signal,c=()=>{},s=new Promise((d,g)=>{c=()=>g(new Error("Request aborted.")),i.aborted?c():i.addEventListener("abort",c,{once:!0})}),o;try{let d=await Promise.race([r(n),s]);if(!d.body)return d;o=d.body.getReader();let g=[],p=0;for(;;){let l=await Promise.race([o.read(),s]);if(l.done)break;if(p+=l.value.byteLength,p>4*1024*1024)throw new Error("Response too large.");g.push(l.value)}let k=new Uint8Array(p),h=0;for(let l of g)k.set(l,h),h+=l.byteLength;return new Response(k,{status:d.status,statusText:d.statusText,headers:d.headers})}finally{i.removeEventListener("abort",c),o&&o.cancel().catch(()=>{})}}}import{recoverAddress as je}from"viem";async function X(r,e,t,n,i){if(!Number.isFinite(t)||t<0||t>36e5)throw new Error("Invalid wait budget.");let c=n(),s=await r(e);for(;["pending","pending_approval","submitted","queued"].includes(String(s.status))&&n()-c<t;)await i(1500),s=await r(e);return s}var Me=3e4,fe=300*1e3,ye="https://api.app.botanary.xyz",Y=class{#r;#e;#c;#o;#i=null;#d=null;#s=null;#u=null;#p=null;#n=0;constructor(e,t={}){this.#r=e,this.#c=t.baseUrl??ye,this.#e=t.transport??new D(this.#c,t.fetch),this.#o=t.provenance??{}}get apiBaseUrl(){return this.#c}get identityMaterialized(){return this.#i!==null||this.#u!==null}setProvenance(e){this.#o=e}replaceSigner(e){if(this.identityMaterialized)throw new Error("Cannot replace a materialized agent signer.");this.#r=e,this.reset()}async identity(){if(this.#i)return this.#i;if(this.#u)return this.#u;let e=this.#n,t=(async()=>{let n;try{n=await this.#r.ensure()}catch{throw this.#a()}if(e!==this.#n)throw this.#a();let i=H(n);return this.#i=i,i})();this.#u=t;try{return await t}finally{this.#u===t&&(this.#u=null)}}#a(){let e=new m("The agent signer could not complete the request.",0);return e.code="signer_error",e}async#g(e){u(e,32);let t=this.#n,n=await this.identity();if(t!==this.#n)throw this.#a();try{let i=await this.#r.signHash(e);u(i,65);let c=await je({hash:e,signature:i});if(t!==this.#n||c.toLowerCase()!==n.address.toLowerCase())throw this.#a();return i}catch{throw this.#a()}}async pairingCode(){return(await this.#h()).current()}async regeneratePairingCode(){return(await this.#h()).regenerate()}async pair(){let e=await this.pairingCode(),t=Math.floor(Date.now()/1e3),n=await this.identity(),i=`${e.code}|${t}`,c=Le(ze(i));return await this.#e.post("/v1/agents/pair",{code:e.code,publicKey:n.publicKey,timestamp:t,signature:await this.#g(c),...this.#o.client?{client:this.#o.client}:{},...this.#o.profile?{profile:this.#o.profile}:{}}),e}async mintSession(){if(this.#p)return this.#p;let e=this.#n,t=(async()=>{let n=await this.identity(),i=y(await this.#e.post("/v1/agents/session/nonce",{address:n.address}));if(u(i.nonce,32),e!==this.#n)throw this.#a();let c=await this.#g(i.nonce),s=y(await this.#e.post("/v1/agents/session",{address:n.address,nonce:i.nonce,signature:c}));if((typeof s.token!="string"||!/^ags_[a-f0-9]{64}$/.test(s.token)||typeof s.expiresAt!="string"||!Number.isFinite(Date.parse(s.expiresAt))||Date.parse(s.expiresAt)<=Date.now()||Date.parse(s.expiresAt)>Date.now()+366e4)&&a(),e!==this.#n)throw this.#a();return this.#s={token:s.token,safeUntil:Date.parse(s.expiresAt)-Me},s.token})();this.#p=t;try{return await t}finally{this.#p===t&&(this.#p=null)}}async ensureSession(){return this.#s&&this.#s.safeUntil>Date.now()?this.#s.token:this.mintSession()}async me(){return this.#t(e=>this.#e.get("/v1/agents/me",e))}async getBalance(){return this.#t(e=>this.#e.get("/v1/balance",e))}async listRequests(){return this.#t(e=>this.#e.get("/v1/agents/requests",e))}async listChains(){return this.#t(e=>this.#e.get("/v1/chains",e))}async getGasMethods(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId)),e.accountId&&t.set("accountId",e.accountId),e.key&&t.set("key",e.key);let n=t.toString();return this.#t(i=>this.#e.get(`/v1/gas/methods${n?`?${n}`:""}`,i))}async getAccount(e){let t=new URLSearchParams;e.chainId!==void 0&&t.set("chainId",String(e.chainId));let n=t.toString();return this.#t(i=>this.#e.get(`/v1/account${n?`?${n}`:""}`,i))}async prepareExactAction(e,t){let n=re(e),i=ie(t,n),c=this.#n,s=await this.#t(g=>this.#e.post(`/v1/delegations/${i.delegationId}/actions/exact`,n,g),!1),o=await se(s,n,i),d;return Object.freeze({userOpHash:o.userOpHash,execution:o.execution,submit:()=>(d||(d=(async()=>{if(c!==this.#n)throw this.#a();let g=await this.ensureSession();if(c!==this.#n)throw this.#a();return M(await this.spendUnderGrant(o.userOp,o.userOpHash,i.permissionId,g,"delegated_action"),o.userOpHash)})()),d),reconcile:()=>this.getUserOpByHash(n.chainId,o.userOpHash)})}async buildDelegatedAction(e){let t={recipient:e.recipient,amount:e.amount,...e.token?{token:e.token}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async buildDelegatedSwap(e){let t={action:"swap",tokenIn:e.tokenIn,tokenOut:e.tokenOut,amountIn:e.amountIn,...e.maxSlippageBps!=null?{maxSlippageBps:e.maxSlippageBps}:{}};return this.#t(n=>this.#e.post(`/v1/delegations/${e.delegationId}/actions`,t,n),!1)}async raiseRequest(e,t){return this.#t(n=>this.#e.post("/v1/agents/requests",{calls:e,reason:t},n),!1)}async spendUnderGrant(e,t,n,i,c){T(e),u(t,32),u(n,32);let s=await this.#g(t),o=me(["bytes1","bytes32","bytes"],["0x00",n,s]);try{return await this.#e.post("/v1/userops",{userOp:{...e,signature:o},userOpHash:t,intentType:c},i)}catch(d){throw d instanceof m&&(d.userOpHash=t),d}}async getUserOp(e){return w(e),this.#t(t=>this.#e.get(`/v1/userops/${e}`,t))}async getUserOpByHash(e,t){return(!Number.isSafeInteger(e)||e<1)&&a(),u(t,32),M(await this.#t(n=>this.#e.get(`/v1/userops/by-hash/${e}/${t}`,n)),t)}async awaitTerminal(e,t){return X(n=>this.getUserOp(n),e,t,()=>Date.now(),n=>new Promise(i=>setTimeout(i,n)))}async listApis(e){return this.#t(t=>this.#e.get(`/v1/apis/providers?chainId=${e}`,t))}async getApiProvider(e,t){let n=t!==void 0?`?chainId=${t}`:"";return this.#t(i=>this.#e.get(`/v1/apis/providers/${e}${n}`,i))}async callApi(e){let t=await this.#t(n=>this.#e.post("/v1/apis/calls/requirements",e,n),!1);return w(t?.requirementId),["ready","pending_approval"].includes(t.status)||a(),this.#l(t)}async resumeApi(e,t=fe){w(e);let n=z(await this.#t(i=>this.#e.get(`/v1/apis/calls/relay/${e}`,i)),e);return n.status==="ready"?this.#l({requirementId:e,status:"ready",...U(n.ready)},t):n.status!=="pending_approval"?n:this.#l({requirementId:e,status:"pending_approval",approvalId:e,expiresAt:""},t)}async#l(e,t=fe){let n;if(e.status==="pending_approval"){let o=await X(d=>this.#t(g=>this.#e.get(`/v1/apis/calls/requirements/${d}`,g)),e.requirementId,t,()=>Date.now(),d=>new Promise(g=>setTimeout(g,d)));if(o.status==="approved"&&o.payload&&o.authorization&&o.index!==void 0&&o.validator)n={payload:o.payload,authorization:o.authorization,index:o.index,validator:o.validator};else{let d=o.status==="declined"?"owner_declined":o.status==="expired"?"approval_timed_out":"approval_still_pending",g=d==="owner_declined"?"The owner declined this call. Nothing was signed and nothing was charged.":d==="approval_timed_out"?"The owner did not respond before the approval window closed. Nothing was signed and nothing was charged.":"The owner has not answered yet. Nothing was signed or charged. Resume this requirement or ask the owner to check their pending approvals.";return["pending","declined","expired"].includes(o.status)||a(),{status:o.status==="pending"?"pending_approval":"declined",declineReason:d,message:g,requirementId:e.requirementId,approvalId:e.status==="pending_approval"?e.approvalId:void 0}}}else n=e;n=U(n);let i=await this.identity(),c=await this.#g(n.payload),s=me(["bytes1","address","bytes"],["0x01",n.validator,Fe([{type:"address"},{type:"tuple",components:[{name:"from",type:"address"},{name:"to",type:"address"},{name:"value",type:"uint256"},{name:"validAfter",type:"uint256"},{name:"validBefore",type:"uint256"},{name:"nonce",type:"bytes32"}]},{type:"uint32"},{type:"bytes"}],[i.address,{from:n.authorization.from,to:n.authorization.to,value:BigInt(n.authorization.value),validAfter:BigInt(n.authorization.validAfter),validBefore:BigInt(n.authorization.validBefore),nonce:n.authorization.nonce},n.index,c])]);try{return z(await this.#t(o=>this.#e.post("/v1/apis/calls/relay",{requirementId:e.requirementId,signature:s},o),!1),e.requirementId)}catch(o){throw o instanceof m&&(o.requirementId=e.requirementId),o}}async getApiBudget(e){return this.#t(t=>this.#e.get(`/v1/apis/budget?chainId=${e}`,t))}reset(){this.#n+=1,this.#u=null,this.#p=null,this.#i=null,this.#d=null,this.#s=null}async#h(){if(!this.#d){let e=await this.identity();this.#d??=new _(e.fingerprint)}return this.#d}async#t(e,t=!0){let n=await this.ensureSession();try{return await e(n)}catch(i){if(t&&i instanceof m&&i.status===401){let c=this.#s&&this.#s.token!==n?await this.ensureSession():await this.mintSession();return await e(c)}throw i}}};function Ve(r,e={}){let t={address:r.address,publicKey:r.publicKey,fingerprint:K(r.address),backend:e.backend??"injected",createdAt:e.createdAt??new Date().toISOString()};return{ensure:async()=>({...t}),sign:n=>r.signMessage({message:n}),signHash:n=>r.sign({hash:n})}}import{recoverMessageAddress as Ke}from"viem";async function We(r,e,t){if(typeof t!="string"||t.trim().length<1||t.trim().length>80)throw new Error("Agent name must contain 1 to 80 characters.");let n;try{n=H(await r.ensure())}catch{throw Q()}let i=await e.app.context();w(i.appId),w(i.keyId),["test","live"].includes(i.environment)||a();let c=n.publicKey.toLowerCase(),s=n.address.toLowerCase(),o=await e.agents.challenge({publicKey:c,name:t.trim()});(!o||o.appId!==i.appId||o.environment!==i.environment||o.publicKey!==c||o.address!==s||!/^dach_[A-Za-z0-9_-]{43}$/.test(o.id)||typeof o.expiresAt!="string"||!Number.isFinite(Date.parse(o.expiresAt))||Date.parse(o.expiresAt)<=Date.now()||Date.parse(o.expiresAt)>Date.now()+36e4)&&a();let d=["Botanary agent registration","Purpose: register-agent-key","Audience: botanary:developer-platform:2026-09-07",`Application: ${i.appId}`,`Environment: ${i.environment}`,`API key: ${i.keyId}`,`Public key: ${c}`,`Address: ${s}`,`Nonce: ${o.id}`,`Expires: ${o.expiresAt}`,"This proof does not authorize an account connection or an on-chain grant."].join(`
|
|
2
|
+
`);o.message!==d&&a();let g;try{if(g=await r.sign(d),u(g,65),(await Ke({message:d,signature:g})).toLowerCase()!==s)throw Q()}catch{throw Q()}let p=await e.agents.complete({challengeId:o.id,signature:g});return(!p||p.appId!==i.appId||p.environment!==i.environment||p.publicKey!==c||p.address!==s||p.grantStatus!=="unknown"||!["awaiting_connection","active","disabled","app_disabled","key_revoked","connection_revoked","unavailable"].includes(p.apiAccess)||p.apiAccess==="active"&&p.connectionId===null||p.apiAccess==="awaiting_connection"&&p.connectionId!==null)&&a(),p.connectionId!==null&&w(p.connectionId),p}function Q(){let r=new m("The agent signer could not complete registration.",0);return r.code="signer_error",r}export{Y as AgentRuntime,m as BotanaryApiError,ye as DEFAULT_API_BASE_URL,de as DEFAULT_PAIRING_CODE_TTL_MS,ae as FINGERPRINT_BYTES,ce as NONCE_BYTES,_ as PairingCodeManager,X as awaitTerminalWith,Ve as createViemAgentSigner,V as crockfordBase32,K as deriveFingerprint,W as generatePairingNonce,We as registerAgent};
|
package/dist/pairing-code.d.ts
CHANGED
|
@@ -1,41 +1,18 @@
|
|
|
1
|
-
/** 10 minutes: long enough to alt-tab to the Botanary app and type ~13 characters, short enough that a
|
|
2
|
-
* code nobody redeemed stops being worth anything on its own quickly. See README for the full
|
|
3
|
-
* "why intercepting the code alone grants nothing" argument - the TTL is one layer of it, not the
|
|
4
|
-
* whole thing. */
|
|
5
1
|
export declare const DEFAULT_PAIRING_CODE_TTL_MS: number;
|
|
6
2
|
export interface PairingCode {
|
|
7
|
-
/** What the agent shows the user, and what they type into Connected agents: `${fingerprint}-${nonce}`. */
|
|
8
3
|
code: string;
|
|
9
|
-
/** The stable, non-secret address fingerprint half (see fingerprint.ts). */
|
|
10
4
|
fingerprint: string;
|
|
11
|
-
/** The fresh-per-code random half - this is what makes the code single-use/short-lived. */
|
|
12
5
|
nonce: string;
|
|
13
6
|
issuedAt: string;
|
|
14
7
|
expiresAt: string;
|
|
15
8
|
}
|
|
16
|
-
/**
|
|
17
|
-
* Owns exactly one pairing attempt at a time for this process, keyed off a fixed address fingerprint.
|
|
18
|
-
* `current()` is idempotent while the code is still valid - Flow 7d step 1 is "the agent ... shows the
|
|
19
|
-
* short code, and waits", which only makes sense if the code stays put while the owner is busy typing
|
|
20
|
-
* it, rather than rotating out from under them on every tool call. It mints automatically once the
|
|
21
|
-
* previous code expires, and `regenerate()` lets the owner (via the agent) burn a code early - e.g. if
|
|
22
|
-
* they suspect it was seen by someone else - without waiting out the TTL.
|
|
23
|
-
*
|
|
24
|
-
* Deliberately in-memory only: a pairing code is not part of the identity (see store.ts) and is not
|
|
25
|
-
* meant to survive a restart - a fresh process showing a fresh code is the correct behavior, not a bug.
|
|
26
|
-
*/
|
|
27
9
|
export declare class PairingCodeManager {
|
|
28
10
|
#private;
|
|
29
11
|
constructor(fingerprint: string, options?: {
|
|
30
12
|
ttlMs?: number;
|
|
31
|
-
/** injectable for deterministic tests */
|
|
32
13
|
now?: () => number;
|
|
33
|
-
/** injectable for deterministic tests */
|
|
34
14
|
nonce?: () => string;
|
|
35
15
|
});
|
|
36
|
-
/** The active code, minting a fresh one if there is none yet or the previous one expired. */
|
|
37
16
|
current(): PairingCode;
|
|
38
|
-
/** Always mints a fresh code, invalidating whatever was active. */
|
|
39
17
|
regenerate(): PairingCode;
|
|
40
18
|
}
|
|
41
|
-
//# sourceMappingURL=pairing-code.d.ts.map
|
package/dist/registration.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AgentSigner } from './signer.js';
|
|
2
2
|
import type { components } from './generated/schema.js';
|
|
3
3
|
type Schemas = components['schemas'];
|
|
4
|
-
/** Structurally compatible with @botanary/sdk. Only the server client handles its app credential. */
|
|
5
4
|
export interface AgentRegistrationClient {
|
|
6
5
|
app: {
|
|
7
6
|
context(): Promise<{
|
|
@@ -15,7 +14,5 @@ export interface AgentRegistrationClient {
|
|
|
15
14
|
complete(input: Schemas['DeveloperAgentRegistrationInput']): Promise<Schemas['DeveloperAgentRegistration']>;
|
|
16
15
|
};
|
|
17
16
|
}
|
|
18
|
-
/** Registration proves possession of this agent key. It creates neither owner consent nor a grant. */
|
|
19
17
|
export declare function registerAgent(signer: AgentSigner, client: AgentRegistrationClient, name: string): Promise<Schemas['DeveloperAgentRegistration']>;
|
|
20
18
|
export {};
|
|
21
|
-
//# sourceMappingURL=registration.d.ts.map
|
package/dist/runtime.d.ts
CHANGED
|
@@ -4,17 +4,8 @@ import { type PairingCode } from './pairing-code.js';
|
|
|
4
4
|
import { type AgentTransport } from './transport.js';
|
|
5
5
|
import type { AgentIdentity, AgentSigner } from './signer.js';
|
|
6
6
|
import type { AgentSelf, AgentRequestView, UnsignedCallView, UserOpBuildResult, UnsignedUserOp } from './views.js';
|
|
7
|
-
/**
|
|
8
|
-
* Poll `get` until the op reports a terminal status or `budgetMs` is spent, then return whatever the
|
|
9
|
-
* backend last said - VERBATIM. A budget lapse returns the real `pending` answer; this helper never
|
|
10
|
-
* invents a status, and never blocks forever.
|
|
11
|
-
*
|
|
12
|
-
* Pure and injectable (clock + sleep) so its behaviour is testable without timers or a network.
|
|
13
|
-
*/
|
|
14
7
|
export declare function awaitTerminalWith(get: (opId: string) => Promise<Record<string, unknown>>, opId: string, budgetMs: number, now: () => number, sleep: (ms: number) => Promise<void>): Promise<Record<string, unknown>>;
|
|
15
|
-
/** Default API origin. The shared runtime reads no environment variables; its host supplies overrides. */
|
|
16
8
|
export declare const DEFAULT_API_BASE_URL = "https://api.app.botanary.xyz";
|
|
17
|
-
/** Per-instance agent identity and sessions. The signer owns all private-key storage. */
|
|
18
9
|
export declare class AgentRuntime {
|
|
19
10
|
#private;
|
|
20
11
|
constructor(signer: AgentSigner, options?: {
|
|
@@ -33,91 +24,31 @@ export declare class AgentRuntime {
|
|
|
33
24
|
profile?: string;
|
|
34
25
|
}): void;
|
|
35
26
|
protected replaceSigner(signer: AgentSigner): void;
|
|
36
|
-
/** The agent's public identity, creating it on first call if none exists yet. Cached for the life of
|
|
37
|
-
* this runtime (the address/fingerprint cannot change without a forget(), which calls reset()). */
|
|
38
27
|
identity(): Promise<AgentIdentity>;
|
|
39
28
|
pairingCode(): Promise<PairingCode>;
|
|
40
29
|
regeneratePairingCode(): Promise<PairingCode>;
|
|
41
30
|
pair(): Promise<PairingCode>;
|
|
42
|
-
/** Mint a FRESH session unconditionally (bypasses the cache - this is what re-minting after expiry or
|
|
43
|
-
* a 401 calls). Also (re)populates the cache, so an ordinary tool call right after this one reuses it
|
|
44
|
-
* via `ensureSession()` instead of minting again. */
|
|
45
31
|
mintSession(): Promise<string>;
|
|
46
|
-
/** The token every read/build/spend call below actually uses: the cached session if it is still safe
|
|
47
|
-
* to reuse, otherwise a freshly minted one. This is what makes "every tool call does not re-mint" true
|
|
48
|
-
* without any caller having to think about session lifetime. */
|
|
49
32
|
ensureSession(): Promise<string>;
|
|
50
|
-
/** `GET /agents/me` - this agent's own identity, its bound account, and its live grant if any (or an
|
|
51
|
-
* honest `grant: null`). Re-mints the session once and retries on a 401 (a token that expired between
|
|
52
|
-
* `ensureSession()`'s check and the request landing, or one the backend otherwise no longer honors). */
|
|
53
33
|
me(): Promise<AgentSelf>;
|
|
54
|
-
/** `GET /balance` - the account address and its token/holdings breakdown, exactly as the backend
|
|
55
|
-
* reports it. */
|
|
56
34
|
getBalance(): Promise<Record<string, unknown>>;
|
|
57
|
-
/** `GET /agents/requests` - every approval request this agent has filed, with the owner's verdict
|
|
58
|
-
* (`status`, `declineReason`) and deadline. The read half of `request_approval`: without it an agent
|
|
59
|
-
* files a request it can never learn the outcome of. */
|
|
60
35
|
listRequests(): Promise<unknown>;
|
|
61
|
-
/** `GET /chains` - every chain this build serves, each with the `chainTier` that decides what it can
|
|
62
|
-
* do (`watch` read-only, `basic` no AgentGuard, `botanary` full authority). */
|
|
63
36
|
listChains(): Promise<unknown>;
|
|
64
|
-
/** `GET /gas/methods` - only the gas methods actually AVAILABLE on the given chain (the backend
|
|
65
|
-
* filters unavailable/sponsored ones out itself). Every parameter is optional and passed through
|
|
66
|
-
* untouched; `key` is the Stellar/Solana key-keyed lane, `chainId` the EVM numeric one. */
|
|
67
37
|
getGasMethods(params: {
|
|
68
38
|
chainId?: number;
|
|
69
39
|
accountId?: string;
|
|
70
40
|
key?: string;
|
|
71
41
|
}): Promise<unknown>;
|
|
72
|
-
/** `GET /account` - SINGULAR: the one account this agent's session resolves to, optionally for a
|
|
73
|
-
* specific chain. Carries `address` and `deploymentStatus`. */
|
|
74
42
|
getAccount(params: {
|
|
75
43
|
chainId?: number;
|
|
76
44
|
}): Promise<unknown>;
|
|
77
|
-
/** Build and verify a bound action. Submission is explicit and attempted at most once per handle. */
|
|
78
45
|
prepareExactAction(input: ExactAgentAction, binding: AgentActionBinding | AgentSwapBinding): Promise<PreparedAgentAction>;
|
|
79
|
-
/**
|
|
80
|
-
* `POST /delegations/{delegationId}/actions` - an UNSIGNED delegated-action op, built in the
|
|
81
|
-
* delegation's OWN Smart Sessions nonce lane with fixed native gas (the session validator cannot
|
|
82
|
-
* gas-estimate a stub signature). This is the ONLY build lane a session-key signature can validate:
|
|
83
|
-
* `POST /money/send/build` (this package's earlier, WRONG choice) builds in the Kernel account's ROOT
|
|
84
|
-
* nonce lane for the OWNER's key - an op built there, then signed with this agent's session key and
|
|
85
|
-
* wrapped in `spendUnderGrant`'s USE envelope, would route validation to the root ECDSA validator,
|
|
86
|
-
* which cannot parse a `(bytes1, bytes32, bytes)` envelope as a 65-byte ECDSA signature. `delegationId`
|
|
87
|
-
* is this agent's OWN grant id (`GET /agents/me`'s `grant.id` - never invented, never another agent's:
|
|
88
|
-
* the backend refuses that with a named 403, see the workspace's delegation.controller.ts). `amount` is
|
|
89
|
-
* DISPLAY units (e.g. `12.5` for 12.5 USDC), matching `DelegatedActionInput.amount` - never wei.
|
|
90
|
-
* `token`, when given, disambiguates which of the delegation's budgeted tokens to move - a contract
|
|
91
|
-
* address or CAIP-19 asset ref (Task A11), required whenever the grant budgets more than one
|
|
92
|
-
* stablecoin. Omitted, the backend defaults to it ONLY when exactly one token is budgeted; a
|
|
93
|
-
* multi-budget grant given neither refuses rather than silently picking the first (`propose_payment`
|
|
94
|
-
* always resolves and passes one - see `agent-actions.ts`).
|
|
95
|
-
*/
|
|
96
46
|
buildDelegatedAction(params: {
|
|
97
47
|
delegationId: string;
|
|
98
48
|
recipient: string;
|
|
99
49
|
amount: number;
|
|
100
50
|
token?: string;
|
|
101
51
|
}): Promise<UserOpBuildResult>;
|
|
102
|
-
/**
|
|
103
|
-
* `POST /delegations/{delegationId}/actions` with `action: 'swap'` - the SAME endpoint, the same session
|
|
104
|
-
* nonce lane and the same fixed gas as `buildDelegatedAction` above; only the body's shape differs. The
|
|
105
|
-
* backend discriminates on `action` explicitly and NEVER infers it from the presence of `tokenIn`, so a
|
|
106
|
-
* transfer body with a typo stays a 422 on the wrong field rather than silently becoming a swap.
|
|
107
|
-
*
|
|
108
|
-
* Symbols, not addresses: unlike `buildDelegatedAction`'s `token` (Task A11), this shape was NOT widened
|
|
109
|
-
* to accept an address or asset ref - the backend re-resolves both legs from ITS OWN per-chain manifest,
|
|
110
|
-
* which is the same resolution `buildGrant` used when it compiled the venue's `EQ` rule, so the two
|
|
111
|
-
* cannot disagree. `propose_swap` (`agent-actions.ts`) still takes an address/asset ref from ITS OWN
|
|
112
|
-
* caller (`assetIn`/`assetOut`) and resolves each to a verified symbol before calling this - so the
|
|
113
|
-
* package-external argument names moved even though this wire shape did not. `amountIn` is DISPLAY
|
|
114
|
-
* units (e.g. `25` for 25 USDC), matching `amount` on the transfer shape.
|
|
115
|
-
*
|
|
116
|
-
* The op this produces is ONE call into `GrantExecutor.executeUnderGrantWithAllowance` - approve, route,
|
|
117
|
-
* zero, assert no residual - which the grant's session authorises only when the grant named a
|
|
118
|
-
* `swapVenue`. A grant without one has no such action at all and the backend declines
|
|
119
|
-
* `grant_swap_not_authorised` rather than handing back an op Smart Sessions would refuse in validation.
|
|
120
|
-
*/
|
|
121
52
|
buildDelegatedSwap(params: {
|
|
122
53
|
delegationId: string;
|
|
123
54
|
tokenIn: string;
|
|
@@ -125,79 +56,21 @@ export declare class AgentRuntime {
|
|
|
125
56
|
amountIn: number;
|
|
126
57
|
maxSlippageBps?: number;
|
|
127
58
|
}): Promise<UserOpBuildResult>;
|
|
128
|
-
/** `POST /agents/requests` - the "ask" half of the design (Flow 7d step 5): raise a request naming the
|
|
129
|
-
* calls that were declined and why, returning which bound was crossed and the deadline exactly as the
|
|
130
|
-
* backend computed them. */
|
|
131
59
|
raiseRequest(calls: UnsignedCallView[], reason: string): Promise<AgentRequestView>;
|
|
132
|
-
/**
|
|
133
|
-
* Sign a built op as the grant's session key and relay it. The USE envelope is EXACTLY the three lines
|
|
134
|
-
* `botanary-fe/src/lib/wallet/signing.ts:49` already uses - `SmartSessionMode.USE` is `0x00`, and the
|
|
135
|
-
* packing is `(bytes1, bytes32, bytes)`. Reproduced rather than re-derived: a second encoding of the same
|
|
136
|
-
* envelope is a second thing to get byte-exact, and only one of them would be tested.
|
|
137
|
-
*
|
|
138
|
-
* `intentType` is the SAME value the build response itself reported (`UserOpBuildResult.intentType`,
|
|
139
|
-
* `'delegated_action'` for `buildDelegatedAction`'s builds) - never assumed or hardcoded here, so the
|
|
140
|
-
* relay always describes what was actually built, which is what `OrchestratorService.submit` uses to
|
|
141
|
-
* label the audit trail entry.
|
|
142
|
-
*
|
|
143
|
-
* Returns the backend's OWN relay response (status/txHash/error, whatever `POST /userops` actually
|
|
144
|
-
* said) rather than nothing - a caller (`propose_payment`) reporting success has to report what the
|
|
145
|
-
* backend reported, never a string this package made up.
|
|
146
|
-
*/
|
|
147
60
|
spendUnderGrant(userOp: UnsignedUserOp, userOpHash: Hex, permissionId: Hex, token: string, intentType: string): Promise<Record<string, unknown>>;
|
|
148
|
-
/** `GET /userops/{opId}` - the receipt for an op THIS agent relayed. Reachable only because that route
|
|
149
|
-
* now carries `@AgentAllowed()`; before that it 403'd an `ags_` token before the handler ran. */
|
|
150
61
|
getUserOp(opId: string): Promise<Record<string, unknown>>;
|
|
151
|
-
/** Read the original agent submission without building or signing another operation. */
|
|
152
62
|
getUserOpByHash(chainId: number, hash: Hex): Promise<Record<string, unknown>>;
|
|
153
|
-
/** Follow an op to a terminal status, bounded. See `awaitTerminalWith`. */
|
|
154
63
|
awaitTerminal(opId: string, budgetMs: number): Promise<Record<string, unknown>>;
|
|
155
|
-
/** `GET /apis/providers` - the ranked, searchable catalog of API providers on a chain, with each
|
|
156
|
-
* provider's endpoint count, verified count, minimum and maximum price in atomic units (nullable).
|
|
157
|
-
* Does NOT call an endpoint or spend anything. Returns
|
|
158
|
-
* `{ providers, total, hidden, reasons, reasonsTruncated }`.
|
|
159
|
-
*
|
|
160
|
-
* The route is `/apis/providers`, not the `/apis/list` this file used to call: the backend's
|
|
161
|
-
* api-catalog controller serves `providers` and `providers/:providerId` and has no `list` route at
|
|
162
|
-
* all, so the old path 404s. botanary-mcp@0.6.1 was published with this corrected route but the
|
|
163
|
-
* change was never committed, which is how the repository came to describe a dead endpoint while
|
|
164
|
-
* the published package worked. */
|
|
165
64
|
listApis(chainId: number): Promise<unknown>;
|
|
166
|
-
/** `GET /apis/providers/{providerId}` - one provider with a page of its endpoints, each with its
|
|
167
|
-
* live price as measured or declared (nullable), and whether it is payable on this chain. Returns
|
|
168
|
-
* the `ApiProviderDetail` shape, with an `endpoints` array. */
|
|
169
65
|
getApiProvider(providerId: string, chainId?: number): Promise<unknown>;
|
|
170
|
-
/**
|
|
171
|
-
* Call a paid third-party API endpoint and pay for it from the owner's wallet, within the budget the
|
|
172
|
-
* owner committed on chain. Build -> sign -> relay, exactly like every other spending lane here.
|
|
173
|
-
*
|
|
174
|
-
* This used to `POST /v1/apis/calls`, a route that has never existed - the contract has
|
|
175
|
-
* `/apis/calls/requirements` and `/apis/calls/relay`, and there is no composite. There could not be
|
|
176
|
-
* one: a single endpoint would have to sign, and the backend holds no key. So the two halves are both
|
|
177
|
-
* real calls, with the signature produced HERE, from this agent's own key.
|
|
178
|
-
*
|
|
179
|
-
* `payload` is a 32-byte EIP-712 digest over an EIP-3009 `TransferWithAuthorization`. Signing it
|
|
180
|
-
* authorises exactly one transfer, of a value and to a recipient the digest already fixes; USDC
|
|
181
|
-
* verifies the result via ERC-1271 against the owner's account, which routes to
|
|
182
|
-
* `X402PaymentValidator` and is refused unless this agent key is inside a live budget.
|
|
183
|
-
*/
|
|
184
66
|
callApi(params: {
|
|
185
67
|
chainId: number;
|
|
186
68
|
providerId: string;
|
|
187
69
|
endpointId: string;
|
|
188
70
|
url: string;
|
|
189
|
-
/** Raw POST body, forwarded byte for byte. The price is quoted for THIS body and the paid request
|
|
190
|
-
* replays exactly it, so a caller cannot get a cheap quote and then send something expensive. */
|
|
191
71
|
body?: string;
|
|
192
72
|
}): Promise<Record<string, unknown>>;
|
|
193
|
-
/** Read durable submission status first. A submitted payment is never signed or forwarded again. */
|
|
194
73
|
resumeApi(requirementId: string, budgetMs?: number): Promise<Record<string, unknown>>;
|
|
195
|
-
/** `GET /apis/budget` - remaining authorizations, per-call maximum, expiry and epoch for this agent
|
|
196
|
-
* on a chain. Does NOT tell you which endpoints this agent may reach. */
|
|
197
74
|
getApiBudget(chainId: number): Promise<unknown>;
|
|
198
|
-
/** Drops cached identity/pairing/session state. Call after `store.forget()` so the next call re-derives
|
|
199
|
-
* from storage (and, in the ordinary case, creates a brand new identity) instead of continuing to serve
|
|
200
|
-
* an in-memory identity - or a session minted for it - whose key is now gone. */
|
|
201
75
|
reset(): void;
|
|
202
76
|
}
|
|
203
|
-
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/signer.d.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import type { Hex } from 'viem';
|
|
2
|
-
/** Public metadata only. The runtime never accepts or exports a private key. */
|
|
3
2
|
export interface AgentIdentity {
|
|
4
3
|
address: Hex;
|
|
5
4
|
publicKey: Hex;
|
|
6
5
|
fingerprint: string;
|
|
7
6
|
createdAt: string;
|
|
8
|
-
/** A non-secret description of the injected signer, such as an HSM or desktop keychain. */
|
|
9
7
|
backend: string;
|
|
10
8
|
}
|
|
11
|
-
/** A server, HSM or desktop adapter supplies signing without exposing key material. */
|
|
12
9
|
export interface AgentSigner {
|
|
13
10
|
ensure(): Promise<AgentIdentity>;
|
|
14
|
-
/** EIP-191 personal message signing, used for app registration proofs. */
|
|
15
11
|
sign(message: string): Promise<Hex>;
|
|
16
|
-
/** Raw secp256k1 digest signing, used by existing agent sessions and grant execution. */
|
|
17
12
|
signHash(hash: Hex): Promise<Hex>;
|
|
18
13
|
}
|
|
19
|
-
/** Adapts an already-owned viem-compatible account. Key loading remains the caller's responsibility. */
|
|
20
14
|
export declare function createViemAgentSigner(account: {
|
|
21
15
|
address: Hex;
|
|
22
16
|
publicKey: Hex;
|
|
@@ -30,4 +24,3 @@ export declare function createViemAgentSigner(account: {
|
|
|
30
24
|
backend?: string;
|
|
31
25
|
createdAt?: string;
|
|
32
26
|
}): AgentSigner;
|
|
33
|
-
//# sourceMappingURL=signer.d.ts.map
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
/** Injection seam for an existing host transport. It receives only public payloads and agent tokens. */
|
|
2
1
|
export interface AgentTransport {
|
|
3
2
|
get<T>(path: string, token?: string): Promise<T>;
|
|
4
3
|
post<T>(path: string, body: unknown, token?: string): Promise<T>;
|
|
5
4
|
}
|
|
6
|
-
/** No mutation retries. A failed relay must be reconciled using its existing operation/hash. */
|
|
7
5
|
export declare class AgentApiClient implements AgentTransport {
|
|
8
6
|
#private;
|
|
9
7
|
constructor(baseUrl: string, fetchImpl?: typeof fetch);
|
|
10
8
|
get<T>(path: string, token?: string): Promise<T>;
|
|
11
9
|
post<T>(path: string, body: unknown, token?: string): Promise<T>;
|
|
12
10
|
}
|
|
13
|
-
//# sourceMappingURL=transport.d.ts.map
|
package/dist/validation.d.ts
CHANGED
|
@@ -6,10 +6,7 @@ export declare function record(value: unknown): Record<string, unknown>;
|
|
|
6
6
|
export declare function hex(value: unknown, bytes?: number): asserts value is Hex;
|
|
7
7
|
export declare function resourceId(value: unknown): asserts value is string;
|
|
8
8
|
export declare function publicIdentity(value: unknown): AgentIdentity;
|
|
9
|
-
/** The public v0.7 contract permits decimal or hex integer quantities. Bytes remain hex-only. */
|
|
10
9
|
export declare function userOpQuantity(value: unknown, bits?: number): bigint;
|
|
11
10
|
export declare function unsignedUserOp(value: unknown): UnsignedUserOp;
|
|
12
11
|
export declare function readyRequirement(value: unknown): Pick<ApiCallRequirementsReady, 'payload' | 'authorization' | 'index' | 'validator'>;
|
|
13
|
-
/** Bind a recovery response to the requested handle; never treat provider success as payment proof. */
|
|
14
12
|
export declare function apiCallStatus(value: unknown, requirementId: string): Record<string, unknown>;
|
|
15
|
-
//# sourceMappingURL=validation.d.ts.map
|
package/dist/views.d.ts
CHANGED
|
@@ -4,18 +4,12 @@ type HexFields<T> = {
|
|
|
4
4
|
[K in keyof T]: K extends 'sender' | 'entryPoint' | 'callData' | 'signature' | 'factory' | 'factoryData' | 'paymaster' | 'paymasterData' ? Hex | Extract<T[K], null | undefined> : T[K];
|
|
5
5
|
};
|
|
6
6
|
export type UnsignedUserOp = HexFields<components['schemas']['UnsignedUserOp']>;
|
|
7
|
-
/** An UNSIGNED userOp build response shape (`POST /delegations/{delegationId}/actions`, the ONLY build
|
|
8
|
-
* lane whose op a session-key signature can actually validate - see `buildDelegatedAction`'s own doc
|
|
9
|
-
* comment) - the UserOp wire fields are generated from the reviewed API contract. Compatibility views below
|
|
10
|
-
* retain the historical MCP fields while the transport contract supplies the complete wire types. */
|
|
11
7
|
export interface UserOpBuildResult {
|
|
12
8
|
intentType: string;
|
|
13
9
|
userOp: UnsignedUserOp;
|
|
14
10
|
userOpHash: Hex;
|
|
15
11
|
[key: string]: unknown;
|
|
16
12
|
}
|
|
17
|
-
/** One entry of `AgentSelf.grant.policySet.spentToDate` (backend's `SpendMeter`) - what tells a caller
|
|
18
|
-
* how much of a budgeted token is left before it tries to spend more than a grant allows. */
|
|
19
13
|
export interface SpendMeterView {
|
|
20
14
|
token: {
|
|
21
15
|
symbol: string;
|
|
@@ -28,27 +22,15 @@ export interface SpendMeterView {
|
|
|
28
22
|
remaining: number;
|
|
29
23
|
expiresAt?: string | null;
|
|
30
24
|
}
|
|
31
|
-
/** The SWAP venue a grant pins, when it was granted swap authority at all (backend's
|
|
32
|
-
* `PolicySet.swapVenue`). ABSENT - not null - on a transfer-only grant, which is exactly what makes it
|
|
33
|
-
* answerable: the compiled Smart Sessions session is not readable back from a `permissionId`, so this
|
|
34
|
-
* recorded fact is the only way either side can know whether the session carries an allowance action.
|
|
35
|
-
* `maxAllowance` is DISPLAY units (the backend scales it by the token's own resolution), so it compares
|
|
36
|
-
* directly against `propose_swap`'s `amountIn`. */
|
|
37
25
|
export interface GrantSwapVenueView {
|
|
38
|
-
/** The one router this grant may approve AND call - the allowance entrypoint has one `(token, target)`. */
|
|
39
26
|
router: string;
|
|
40
|
-
/** The one token this grant may sell. */
|
|
41
27
|
token: {
|
|
42
28
|
symbol: string;
|
|
43
29
|
chainId?: number | null;
|
|
44
30
|
address?: string | null;
|
|
45
31
|
};
|
|
46
|
-
/** Ceiling on a SINGLE swap's allowance, in display units. Not a budget: the grant's own budget meters
|
|
47
|
-
* separately, and both apply. */
|
|
48
32
|
maxAllowance: number;
|
|
49
33
|
}
|
|
50
|
-
/** The live grant `GET /agents/me` reports, when one exists - mirrors the backend's `Delegation` (only
|
|
51
|
-
* the fields this package reads). */
|
|
52
34
|
export interface AgentGrantView {
|
|
53
35
|
id: string;
|
|
54
36
|
permissionId: Hex;
|
|
@@ -57,11 +39,6 @@ export interface AgentGrantView {
|
|
|
57
39
|
chain: string;
|
|
58
40
|
chainId: number;
|
|
59
41
|
policySet: {
|
|
60
|
-
/** `token.assetRef` is stamped on at READ time by the backend's `withGrantAssetRefs`
|
|
61
|
-
* (`grant-asset-refs.ts`) - CAIP-19, e.g. `eip155:8453/erc20:0x...` - and is what
|
|
62
|
-
* `findBudgetMeter`/`checkSendBounds` match a resolved send against, never the symbol. `null`
|
|
63
|
-
* means this build could not resolve an on-chain address for the token (honestly undecodable,
|
|
64
|
-
* never guessed); absent (older backend) means "not reported". */
|
|
65
42
|
budgets: Array<{
|
|
66
43
|
token: {
|
|
67
44
|
symbol: string;
|
|
@@ -83,16 +60,12 @@ export interface AgentGrantView {
|
|
|
83
60
|
name?: string | null;
|
|
84
61
|
}>;
|
|
85
62
|
expiresAt?: string | null;
|
|
86
|
-
/** Absent unless this grant was given swap authority - see `GrantSwapVenueView`. */
|
|
87
63
|
swapVenue?: GrantSwapVenueView | null;
|
|
88
64
|
[key: string]: unknown;
|
|
89
65
|
};
|
|
90
66
|
spentToDate: SpendMeterView[];
|
|
91
67
|
[key: string]: unknown;
|
|
92
68
|
}
|
|
93
|
-
/** `GET /agents/me`'s response - who this agent is, what account it is bound to, and its live grant if
|
|
94
|
-
* any. `grant: null` is an honest, explicit answer (see agents.controller.ts's own doc comment), never
|
|
95
|
-
* an error and never fabricated by this package. */
|
|
96
69
|
export interface AgentSelf {
|
|
97
70
|
id: string;
|
|
98
71
|
name: string;
|
|
@@ -100,32 +73,20 @@ export interface AgentSelf {
|
|
|
100
73
|
fingerprint: string;
|
|
101
74
|
connectedAt: string;
|
|
102
75
|
accountId: string;
|
|
103
|
-
/** The bound account's own on-chain address - the one identifier the owner actually recognises, since
|
|
104
|
-
* `accountId` is an opaque row id that appears nowhere in the Botanary app. Without it, an agent
|
|
105
|
-
* paired to a DIFFERENT login than the one the owner has open reports itself connected and gives
|
|
106
|
-
* them nothing to notice the mismatch with. Optional here because an older backend does not send it;
|
|
107
|
-
* this package never fabricates one. */
|
|
108
76
|
accountAddress?: Hex | null;
|
|
109
77
|
grant: AgentGrantView | null;
|
|
110
|
-
/** Whether the grant's own chain has every setup module the account needs. Optional because an older
|
|
111
|
-
* backend does not send it - absent means "not reported", which this package treats as "no evidence
|
|
112
|
-
* of a problem" rather than inventing either answer. */
|
|
113
78
|
accountSetup?: {
|
|
114
79
|
ready: boolean;
|
|
115
80
|
missing: string[];
|
|
116
81
|
chainId: number;
|
|
117
82
|
} | null;
|
|
118
83
|
}
|
|
119
|
-
/** A submitted call, exactly the shape `POST /agents/requests`'s `calls` field and a money/swap build's
|
|
120
|
-
* own callData/value/to already share. */
|
|
121
84
|
export interface UnsignedCallView {
|
|
122
85
|
to: string;
|
|
123
86
|
data: string;
|
|
124
87
|
value: string;
|
|
125
88
|
chainId: number;
|
|
126
89
|
}
|
|
127
|
-
/** `POST /agents/requests`'s response - the raised request, including which bound was crossed and the
|
|
128
|
-
* deadline, exactly as the backend reports them (never fabricated here). */
|
|
129
90
|
export interface AgentRequestView {
|
|
130
91
|
id: string;
|
|
131
92
|
agentId: string;
|
|
@@ -138,12 +99,6 @@ export interface AgentRequestView {
|
|
|
138
99
|
status: string;
|
|
139
100
|
[key: string]: unknown;
|
|
140
101
|
}
|
|
141
|
-
/** The exact EIP-3009 tuple this lane's `X402PaymentValidator` verifies, field order load-bearing - it
|
|
142
|
-
* must match `IX402PaymentValidator.Authorization` in botanary-contracts exactly (reorder a field and
|
|
143
|
-
* every signature stops verifying; see that interface's own doc comment). Decimal-string value fields,
|
|
144
|
-
* mirroring the backend's own `X402Authorization` wire shape (`src/modules/x402/x402-authorization.ts`)
|
|
145
|
-
* rather than a bigint: JSON has no integer wide enough for a uint256, and this package never imports
|
|
146
|
-
* across the BE boundary to reuse that type directly (deploy-isolated - see the workspace AGENTS.md). */
|
|
147
102
|
export interface X402AuthorizationView {
|
|
148
103
|
from: Hex;
|
|
149
104
|
to: Hex;
|
|
@@ -153,10 +108,6 @@ export interface X402AuthorizationView {
|
|
|
153
108
|
nonce: Hex;
|
|
154
109
|
[key: string]: unknown;
|
|
155
110
|
}
|
|
156
|
-
/** `POST /apis/calls/requirements`'s `ready` branch (Task 13) - everything `callApi` needs to build the
|
|
157
|
-
* Kernel ERC-1271 envelope. `validator` is the `X402PaymentValidator` address on THIS call's chain,
|
|
158
|
-
* read from here and never hardcoded: deployment addresses are nonce-shifted between chains, so the
|
|
159
|
-
* same address means something different on another chain. */
|
|
160
111
|
export interface ApiCallRequirementsReady {
|
|
161
112
|
requirementId: string;
|
|
162
113
|
status: 'ready';
|
|
@@ -166,10 +117,6 @@ export interface ApiCallRequirementsReady {
|
|
|
166
117
|
validator: Hex;
|
|
167
118
|
[key: string]: unknown;
|
|
168
119
|
}
|
|
169
|
-
/** `POST /apis/calls/requirements`'s `pending_approval` branch (Task 11's confirmation threshold) -
|
|
170
|
-
* carries NONE of `payload`/`authorization`/`index`/`validator`, structurally, not merely empty: the
|
|
171
|
-
* digest is deliberately withheld until the owner answers. `callApi` must never try to sign off this
|
|
172
|
-
* shape - see its own handling below. */
|
|
173
120
|
export interface ApiCallRequirementsPending {
|
|
174
121
|
requirementId: string;
|
|
175
122
|
status: 'pending_approval';
|
|
@@ -179,4 +126,3 @@ export interface ApiCallRequirementsPending {
|
|
|
179
126
|
}
|
|
180
127
|
export type ApiCallRequirementsResponse = ApiCallRequirementsReady | ApiCallRequirementsPending;
|
|
181
128
|
export {};
|
|
182
|
-
//# sourceMappingURL=views.d.ts.map
|