@botanary/agent 0.1.0-alpha.5 → 0.1.0-alpha.6

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 CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.6
4
+
5
+ - Treat an empty mandate recipient list as unrestricted while preserving exact membership checks for non-empty recipient allowlists.
6
+
3
7
  ## 0.1.0-alpha.5
4
8
 
5
9
  - Add `prepareAction({ mandate, action })` for exact Base Sepolia transfers from a confirmed mandate projection.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var h=class extends Error{constructor(t,n,i=null,o=null){super(t);this.status=n;this.details=i;this.retryAfterSeconds=o;this.name="BotanaryApiError"}requestId;code;operationId;userOpHash;requirementId;ambiguous=!1};import{encodeFunctionData as E,encodePacked as ve,decodeFunctionData as F,parseAbi as U,getAddress as I,zeroAddress as Pe,zeroHash as Re}from"viem";import{entryPoint07Address as ee,getUserOperationHash as Se}from"viem/account-abstraction";import{publicKeyToAddress as Ie}from"viem/accounts";function d(){let r=new h("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)?d():r}function p(r,e){(typeof r!="string"||!/^0x(?:[0-9a-fA-F]{2})*$/.test(r)||e!==void 0&&r.length!==2+2*e)&&d()}function w(r){(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||/^(?:bsk|ags|dcs|dca|dcr)_/.test(r))&&d()}function L(r){(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r)||BigInt(r)>=2n**256n)&&d()}function H(r){let e=y(r);return p(e.address,20),p(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)))&&d(),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 d();let t=BigInt(r);return t>=2n**BigInt(e)?d():t}function C(r){let e=y(r);for(let t of["sender","entryPoint"])p(e[t],20);for(let t of["nonce","callGasLimit","verificationGasLimit","preVerificationGas","maxFeePerGas","maxPriorityFeePerGas"])x(e[t]);for(let t of["callData","signature"])p(e[t]);for(let t of["paymasterVerificationGasLimit","paymasterPostOpGasLimit"])e[t]!=null&&x(e[t],128);(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d();for(let t of["factory","paymaster"])e[t]!=null&&p(e[t],20);for(let t of["factoryData","paymasterData"])e[t]!=null&&p(e[t]);return r}function T(r){let e=y(r);p(e.payload,32),p(e.validator,20),(!Number.isSafeInteger(e.index)||Number(e.index)<0||Number(e.index)>=2**32)&&d();let t=y(e.authorization);return p(t.from,20),p(t.to,20),p(t.nonce,32),L(t.value),L(t.validAfter),L(t.validBefore),BigInt(t.validBefore)<=BigInt(t.validAfter)&&d(),{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")&&d();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)))&&d(),t.providerStatus!==void 0&&(!Number.isSafeInteger(t.providerStatus)||Number(t.providerStatus)<100||Number(t.providerStatus)>599)&&d(),t.txHash!==void 0&&p(t.txHash,32),t.status==="settled"&&p(t.txHash,32),t.status==="ready"?T(t.ready):t.ready!==void 0&&d(),t}var te=U(["function execute(bytes32 mode, bytes executionCalldata) payable"]),M=U(["function executeUnderGrant(bytes32 permissionId, address target, uint256 value, bytes callData)","function executeUnderGrantWithAllowance(bytes32 permissionId, address target, uint256 value, address token, uint256 allowance, bytes callData)"]),_e=U(["function transfer(address to, uint256 amount) returns (bool)"]),ne=U(["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 d();let t=BigInt(r);return t>=2n**256n||e&&t===0n?d():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))&&d(),w(e.accountId),p(e.accountAddress,20),p(e.tokenAddress,20),b(e.amount),(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d(),e.action==="transfer"?(p(e.recipient,20),(e.tokenOutAddress!==void 0||e.maxSlippageBps!==void 0)&&d()):e.action==="swap"?(p(e.tokenOutAddress,20),(e.recipient!==void 0||e.maxSlippageBps!==void 0&&(!Number.isInteger(e.maxSlippageBps)||Number(e.maxSlippageBps)<1||Number(e.maxSlippageBps)>1e3))&&d()):d(),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),p(r.permissionId,32),p(r.executorAddress,20),b(r.maxGasCostWei),e.action==="swap"){let t=r;p(t.routerAddress,20),b(t.minAmountOut),t.verifyRoute!==void 0&&typeof t.verifyRoute!="function"&&d()}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),o=C(n.userOp),s=Object.freeze({...o,sender:I(o.sender),entryPoint:I(o.entryPoint)});p(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)&&d();let a=x(s.nonce);a>>64n!==Ee&&d();let c=x(s.callGasLimit,128),u=x(s.verificationGasLimit,128),l=x(s.preVerificationGas),k=x(s.maxFeePerGas,128),m=x(s.maxPriorityFeePerGas,128);(m>k||(c+u+l)*k>b(t.maxGasCostWei))&&d();let g=s.callData,v;if(e.action==="transfer"){(!f(i.recipient,e.recipient)||i.tokenOutAddress!==void 0||i.maxSlippageBps!==void 0)&&d();let A=f(e.tokenAddress,Pe);v=E({abi:M,functionName:"executeUnderGrant",args:[t.permissionId,A?e.recipient:e.tokenAddress,A?b(e.amount):0n,A?"0x":E({abi:_e,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))&&d();let q=F({abi:te,data:g}).args[1],P=F({abi:M,data:`0x${q.slice(106)}`});P.functionName!=="executeUnderGrantWithAllowance"&&d();let S=P.args[5],_=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(_)!==!0&&d();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))&&d()}v=E({abi:M,functionName:"executeUnderGrantWithAllowance",args:[t.permissionId,A.routerAddress,0n,e.tokenAddress,b(e.amount),S]})}let R=E({abi:te,functionName:"execute",args:[Re,ve(["address","uint256","bytes"],[t.executorAddress,0n,v])]});f(g,R)||d();let $=Se({entryPointAddress:ee,entryPointVersion:"0.7",chainId:e.chainId,userOperation:{sender:s.sender,nonce:a,callData:s.callData,callGasLimit:c,verificationGasLimit:u,preVerificationGas:l,maxFeePerGas:k,maxPriorityFeePerGas:m,signature:"0x"}});f($,n.userOpHash)||d();let G=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:$,execution:G}}catch{return d()}}function j(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&&p(t.txHash,32),t}catch{let t=new h("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 he,encodeAbiParameters as Fe}from"viem";import{randomBytes as Oe}from"node:crypto";import{hexToBytes as Be}from"viem";var ae="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function W(r){let e=0,t=0,n="";for(let i of r)for(e=e<<8|i,t+=8;t>=5;)n+=ae[e>>>t-5&31],t-=5;return t>0&&(n+=ae[e<<5-t&31]),n}var oe=3,de=5;function V(r){let e=Be(r);return W(e.slice(0,oe))}function K(){return W(Oe(de))}var ce=600*1e3,O=class{#r;#e;#d;#a;#i=null;constructor(e,t){this.#r=e,this.#e=t?.ttlMs??ce,this.#d=t?.now??Date.now,this.#a=t?.nonce??K}current(){let e=this.#d();return(!this.#i||this.#i.expiresAt<=e)&&this.#c(e),this.#s()}regenerate(){return this.#c(this.#d()),this.#s()}#c(e){this.#i={nonce:this.#a(),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 Ne 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",le="(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})",ut=`${le}+`,He=new RegExp(`^${le}+$`),Ce=new RegExp("^(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$"),Te=/^\.{1,2}$/,Ue=/[/\\#?]/,De=/[\u0000-\u001f\u007f]/,$e=6;function Ge(r){let e=r;for(let t=0;t<$e;t+=1){if(Te.test(e)||Ue.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 ge(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 a=i[s];if(!He.test(a)||Ge(a))return null}if(n!==""&&!Ce.test(n.slice(1)))return null;let o;try{o=new URL(t+n,pe)}catch{return null}return o.origin!==pe||o.hash!==""||o.pathname!==t||o.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=Ne({baseUrl:n.origin+"/v1",fetch:qe(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 o=ge(t);if(!o?.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=o.path.slice(3),a=ue.find(m=>m.method===e&&new RegExp("^"+m.path.replace(/\{[^}]+\}/g,"[A-Za-z0-9_-]+")+"$").test(s));if(!a)throw new Error("This route is outside the reviewed agent API.");let c={};a.path.split("/").forEach((m,g)=>{m.startsWith("{")&&(c[m.slice(1,-1)]=s.split("/")[g])});let u=Object.fromEntries(new URLSearchParams(o.query)),l=new AbortController,k=setTimeout(()=>l.abort(),3e4);try{let m=await this.#r.request(e,a.path,{params:{path:c,query:u},...n===void 0?{}:{body:n},headers:i?{authorization:`Bearer ${i}`}:{},credentials:"omit",redirect:"error",cache:"no-store",signal:l.signal,parseAs:"text"}),g=m.response,v=m.error;if(g.ok)try{v=m.data?JSON.parse(m.data):{}}catch{throw new Error("Invalid JSON response.")}if(!g.ok){let R=Z(Z(v)?.error),G=(typeof R?.message=="string"?R.message:`Agent request failed with ${g.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=g.headers.get("retry-after"),A=B&&/^\d+(?:\.\d+)?$/.test(B)?Number(B):null,N=Z(R?.details),q=N&&Object.fromEntries(Object.entries(N).filter(([S,_])=>["missingModule","declineReason","chainId"].includes(S)&&(typeof _=="number"||typeof _=="string"&&/^[a-zA-Z0-9_-]{1,100}$/.test(_)))),P=new h(G.slice(0,1e3),g.status,q,A);throw P.requestId=me(g.headers.get("x-request-id")),P.code=me(R?.code)??"http_error",P.ambiguous=e==="post"&&g.status>=500,P}return v}catch(m){if(m instanceof h)throw m;let g=new h("The agent request did not complete. Check the existing operation before retrying.",0);throw g.code=l.signal.aborted?"timeout":"transport_error",g.ambiguous=e==="post",g}finally{clearTimeout(k)}}};function Z(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:null}function me(r){return typeof r=="string"&&/^[a-zA-Z0-9_-]{1,128}$/.test(r)&&!/^(?:ags|bsk|dcs|dca|dcr)_/.test(r)?r:void 0}function qe(r){return async(e,t)=>{let n=e instanceof Request?e:new Request(e,t),i=n.signal,o=()=>{},s=new Promise((c,u)=>{o=()=>u(new Error("Request aborted.")),i.aborted?o():i.addEventListener("abort",o,{once:!0})}),a;try{let c=await Promise.race([r(n),s]);if(!c.body)return c;a=c.body.getReader();let u=[],l=0;for(;;){let g=await Promise.race([a.read(),s]);if(g.done)break;if(l+=g.value.byteLength,l>4*1024*1024)throw new Error("Response too large.");u.push(g.value)}let k=new Uint8Array(l),m=0;for(let g of u)k.set(g,m),m+=g.byteLength;return new Response(k,{status:c.status,statusText:c.statusText,headers:c.headers})}finally{i.removeEventListener("abort",o),a&&a.cancel().catch(()=>{})}}}import{recoverAddress as Me}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 o=n(),s=await r(e);for(;["pending","pending_approval","submitted","queued"].includes(String(s.status))&&n()-o<t;)await i(1500),s=await r(e);return s}var je=3e4,fe=300*1e3,ye="https://api.app.botanary.xyz",Y=class{#r;#e;#d;#a;#i=null;#c=null;#s=null;#u=null;#p=null;#n=0;#g;constructor(e,t={}){if(this.#r=e,this.#d=t.baseUrl??ye,this.#e=t.transport??new D(this.#d,t.fetch),this.#a=t.provenance??{},t.maxGasCostWei!==void 0&&(!/^[1-9][0-9]{0,77}$/.test(t.maxGasCostWei)||BigInt(t.maxGasCostWei)>=2n**256n))throw new Error("Use a positive integer agent gas ceiling in wei.");this.#g=t.maxGasCostWei??null}get apiBaseUrl(){return this.#d}get identityMaterialized(){return this.#i!==null||this.#u!==null}setProvenance(e){this.#a=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.#o()}if(e!==this.#n)throw this.#o();let i=H(n);return this.#i=i,i})();this.#u=t;try{return await t}finally{this.#u===t&&(this.#u=null)}}#o(){let e=new h("The agent signer could not complete the request.",0);return e.code="signer_error",e}async#l(e){p(e,32);let t=this.#n,n=await this.identity();if(t!==this.#n)throw this.#o();try{let i=await this.#r.signHash(e);p(i,65);let o=await Me({hash:e,signature:i});if(t!==this.#n||o.toLowerCase()!==n.address.toLowerCase())throw this.#o();return i}catch{throw this.#o()}}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}`,o=Le(ze(i));return await this.#e.post("/v1/agents/pair",{code:e.code,publicKey:n.publicKey,timestamp:t,signature:await this.#l(o),...this.#a.client?{client:this.#a.client}:{},...this.#a.profile?{profile:this.#a.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(p(i.nonce,32),e!==this.#n)throw this.#o();let o=await this.#l(i.nonce),s=y(await this.#e.post("/v1/agents/session",{address:n.address,nonce:i.nonce,signature:o}));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)&&d(),e!==this.#n)throw this.#o();return this.#s={token:s.token,safeUntil:Date.parse(s.expiresAt)-je},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),o=this.#n,s=await this.#t(u=>this.#e.post(`/v1/delegations/${i.delegationId}/actions/exact`,n,u),!1),a=await se(s,n,i),c;return Object.freeze({status:"prepared",userOpHash:a.userOpHash,execution:a.execution,submit:()=>(c||(c=(async()=>{if(o!==this.#n)throw this.#o();let u=await this.ensureSession();if(o!==this.#n)throw this.#o();return j(await this.spendUnderGrant(a.userOp,a.userOpHash,i.permissionId,u,"delegated_action"),a.userOpHash)})()),c),reconcile:()=>this.getUserOpByHash(n.chainId,a.userOpHash)})}async prepareAction(e){let{mandate:t,action:n}=structuredClone(e),i=/^[1-9][0-9]{0,77}$/.test(n.amountRaw)?BigInt(n.amountRaw):0n,o=t.policy.perActionMaxRaw&&/^[1-9][0-9]{0,77}$/.test(t.policy.perActionMaxRaw)?BigInt(t.policy.perActionMaxRaw):null,s=t.chainState.remaining?.find(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase())?.remainingRaw??null,a=u=>Object.freeze({status:"refused",reason:u,transactionSubmitted:!1,requestedAmountRaw:n.amountRaw,perActionMaxRaw:t.policy.perActionMaxRaw,remainingRaw:s});if(t.chainId!==84532||!["submitted","unresolved","confirmed"].includes(t.createStatus)||t.chainState.status!=="active"||t.apiAccess!=="active")return a(t.revokeStatus==="confirmed"||t.chainState.status==="revoked"?"mandate_revoked":Date.parse(t.policy.expiresAt)<=Date.now()?"mandate_expired":"mandate_inactive");if(i===0n||!t.policy.budgets.some(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase()))return a("mandate_inactive");if(o!==null&&i>o)return a("over_per_action_max");if(!t.policy.recipients.some(u=>u.toLowerCase()===n.recipient.toLowerCase()))return a("wrong_recipient");if((await this.identity()).address.toLowerCase()!==t.agentAddress.toLowerCase())return a("mandate_inactive");if(!this.#g)throw new Error("Configure maxGasCostWei before preparing mandate actions.");return this.prepareExactAction({accountId:t.accountId,accountAddress:t.accountAddress,chainId:84532,action:"transfer",tokenAddress:n.tokenAddress,recipient:n.recipient,amount:n.amountRaw},{delegationId:t.id,permissionId:t.permissionId,executorAddress:t.grantExecutor,maxGasCostWei:this.#g})}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,o){C(e),p(t,32),p(n,32);let s=await this.#l(t),a=he(["bytes1","bytes32","bytes"],["0x00",n,s]);try{return await this.#e.post("/v1/userops",{userOp:{...e,signature:a},userOpHash:t,intentType:o},i)}catch(c){throw c instanceof h&&(c.userOpHash=t),c}}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)&&d(),p(t,32),j(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)||d(),this.#m(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.#m({requirementId:e,status:"ready",...T(n.ready)},t):n.status!=="pending_approval"?n:this.#m({requirementId:e,status:"pending_approval",approvalId:e,expiresAt:""},t)}async#m(e,t=fe){let n;if(e.status==="pending_approval"){let a=await X(c=>this.#t(u=>this.#e.get(`/v1/apis/calls/requirements/${c}`,u)),e.requirementId,t,()=>Date.now(),c=>new Promise(u=>setTimeout(u,c)));if(a.status==="approved"&&a.payload&&a.authorization&&a.index!==void 0&&a.validator)n={payload:a.payload,authorization:a.authorization,index:a.index,validator:a.validator};else{let c=a.status==="declined"?"owner_declined":a.status==="expired"?"approval_timed_out":"approval_still_pending",u=c==="owner_declined"?"The owner declined this call. Nothing was signed and nothing was charged.":c==="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(a.status)||d(),{status:a.status==="pending"?"pending_approval":"declined",declineReason:c,message:u,requirementId:e.requirementId,approvalId:e.status==="pending_approval"?e.approvalId:void 0}}}else n=e;n=T(n);let i=await this.identity(),o=await this.#l(n.payload),s=he(["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,o])]);try{return z(await this.#t(a=>this.#e.post("/v1/apis/calls/relay",{requirementId:e.requirementId,signature:s},a),!1),e.requirementId)}catch(a){throw a instanceof h&&(a.requirementId=e.requirementId),a}}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.#c=null,this.#s=null}async#h(){if(!this.#c){let e=await this.identity();this.#c??=new O(e.fingerprint)}return this.#c}async#t(e,t=!0){let n=await this.ensureSession();try{return await e(n)}catch(i){if(t&&i instanceof h&&i.status===401){let o=this.#s&&this.#s.token!==n?await this.ensureSession():await this.mintSession();return await e(o)}throw i}}};function We(r,e={}){let t={address:r.address,publicKey:r.publicKey,fingerprint:V(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 Ve}from"viem";async function Ke(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)||d();let o=n.publicKey.toLowerCase(),s=n.address.toLowerCase(),a=await e.agents.challenge({publicKey:o,name:t.trim()});(!a||a.appId!==i.appId||a.environment!==i.environment||a.publicKey!==o||a.address!==s||!/^dach_[A-Za-z0-9_-]{43}$/.test(a.id)||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||Date.parse(a.expiresAt)>Date.now()+36e4)&&d();let c=["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: ${o}`,`Address: ${s}`,`Nonce: ${a.id}`,`Expires: ${a.expiresAt}`,"This proof does not authorize an account connection or an on-chain grant."].join(`
1
+ var h=class extends Error{constructor(t,n,i=null,o=null){super(t);this.status=n;this.details=i;this.retryAfterSeconds=o;this.name="BotanaryApiError"}requestId;code;operationId;userOpHash;requirementId;ambiguous=!1};import{encodeFunctionData as E,encodePacked as ve,decodeFunctionData as F,parseAbi as U,getAddress as I,zeroAddress as Pe,zeroHash as Re}from"viem";import{entryPoint07Address as ee,getUserOperationHash as Se}from"viem/account-abstraction";import{publicKeyToAddress as Ie}from"viem/accounts";function d(){let r=new h("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)?d():r}function p(r,e){(typeof r!="string"||!/^0x(?:[0-9a-fA-F]{2})*$/.test(r)||e!==void 0&&r.length!==2+2*e)&&d()}function w(r){(typeof r!="string"||!/^[A-Za-z0-9_-]{1,128}$/.test(r)||/^(?:bsk|ags|dcs|dca|dcr)_/.test(r))&&d()}function L(r){(typeof r!="string"||!/^(0|[1-9][0-9]{0,77})$/.test(r)||BigInt(r)>=2n**256n)&&d()}function H(r){let e=y(r);return p(e.address,20),p(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)))&&d(),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 d();let t=BigInt(r);return t>=2n**BigInt(e)?d():t}function C(r){let e=y(r);for(let t of["sender","entryPoint"])p(e[t],20);for(let t of["nonce","callGasLimit","verificationGasLimit","preVerificationGas","maxFeePerGas","maxPriorityFeePerGas"])x(e[t]);for(let t of["callData","signature"])p(e[t]);for(let t of["paymasterVerificationGasLimit","paymasterPostOpGasLimit"])e[t]!=null&&x(e[t],128);(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d();for(let t of["factory","paymaster"])e[t]!=null&&p(e[t],20);for(let t of["factoryData","paymasterData"])e[t]!=null&&p(e[t]);return r}function T(r){let e=y(r);p(e.payload,32),p(e.validator,20),(!Number.isSafeInteger(e.index)||Number(e.index)<0||Number(e.index)>=2**32)&&d();let t=y(e.authorization);return p(t.from,20),p(t.to,20),p(t.nonce,32),L(t.value),L(t.validAfter),L(t.validBefore),BigInt(t.validBefore)<=BigInt(t.validAfter)&&d(),{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")&&d();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)))&&d(),t.providerStatus!==void 0&&(!Number.isSafeInteger(t.providerStatus)||Number(t.providerStatus)<100||Number(t.providerStatus)>599)&&d(),t.txHash!==void 0&&p(t.txHash,32),t.status==="settled"&&p(t.txHash,32),t.status==="ready"?T(t.ready):t.ready!==void 0&&d(),t}var te=U(["function execute(bytes32 mode, bytes executionCalldata) payable"]),M=U(["function executeUnderGrant(bytes32 permissionId, address target, uint256 value, bytes callData)","function executeUnderGrantWithAllowance(bytes32 permissionId, address target, uint256 value, address token, uint256 allowance, bytes callData)"]),_e=U(["function transfer(address to, uint256 amount) returns (bool)"]),ne=U(["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 d();let t=BigInt(r);return t>=2n**256n||e&&t===0n?d():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))&&d(),w(e.accountId),p(e.accountAddress,20),p(e.tokenAddress,20),b(e.amount),(!Number.isSafeInteger(e.chainId)||Number(e.chainId)<=0)&&d(),e.action==="transfer"?(p(e.recipient,20),(e.tokenOutAddress!==void 0||e.maxSlippageBps!==void 0)&&d()):e.action==="swap"?(p(e.tokenOutAddress,20),(e.recipient!==void 0||e.maxSlippageBps!==void 0&&(!Number.isInteger(e.maxSlippageBps)||Number(e.maxSlippageBps)<1||Number(e.maxSlippageBps)>1e3))&&d()):d(),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),p(r.permissionId,32),p(r.executorAddress,20),b(r.maxGasCostWei),e.action==="swap"){let t=r;p(t.routerAddress,20),b(t.minAmountOut),t.verifyRoute!==void 0&&typeof t.verifyRoute!="function"&&d()}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),o=C(n.userOp),s=Object.freeze({...o,sender:I(o.sender),entryPoint:I(o.entryPoint)});p(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)&&d();let a=x(s.nonce);a>>64n!==Ee&&d();let c=x(s.callGasLimit,128),u=x(s.verificationGasLimit,128),l=x(s.preVerificationGas),k=x(s.maxFeePerGas,128),m=x(s.maxPriorityFeePerGas,128);(m>k||(c+u+l)*k>b(t.maxGasCostWei))&&d();let g=s.callData,v;if(e.action==="transfer"){(!f(i.recipient,e.recipient)||i.tokenOutAddress!==void 0||i.maxSlippageBps!==void 0)&&d();let A=f(e.tokenAddress,Pe);v=E({abi:M,functionName:"executeUnderGrant",args:[t.permissionId,A?e.recipient:e.tokenAddress,A?b(e.amount):0n,A?"0x":E({abi:_e,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))&&d();let q=F({abi:te,data:g}).args[1],P=F({abi:M,data:`0x${q.slice(106)}`});P.functionName!=="executeUnderGrantWithAllowance"&&d();let S=P.args[5],_=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(_)!==!0&&d();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))&&d()}v=E({abi:M,functionName:"executeUnderGrantWithAllowance",args:[t.permissionId,A.routerAddress,0n,e.tokenAddress,b(e.amount),S]})}let R=E({abi:te,functionName:"execute",args:[Re,ve(["address","uint256","bytes"],[t.executorAddress,0n,v])]});f(g,R)||d();let $=Se({entryPointAddress:ee,entryPointVersion:"0.7",chainId:e.chainId,userOperation:{sender:s.sender,nonce:a,callData:s.callData,callGasLimit:c,verificationGasLimit:u,preVerificationGas:l,maxFeePerGas:k,maxPriorityFeePerGas:m,signature:"0x"}});f($,n.userOpHash)||d();let G=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:$,execution:G}}catch{return d()}}function j(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&&p(t.txHash,32),t}catch{let t=new h("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 he,encodeAbiParameters as Fe}from"viem";import{randomBytes as Oe}from"node:crypto";import{hexToBytes as Be}from"viem";var ae="0123456789ABCDEFGHJKMNPQRSTVWXYZ";function W(r){let e=0,t=0,n="";for(let i of r)for(e=e<<8|i,t+=8;t>=5;)n+=ae[e>>>t-5&31],t-=5;return t>0&&(n+=ae[e<<5-t&31]),n}var oe=3,de=5;function V(r){let e=Be(r);return W(e.slice(0,oe))}function K(){return W(Oe(de))}var ce=600*1e3,O=class{#r;#e;#d;#a;#i=null;constructor(e,t){this.#r=e,this.#e=t?.ttlMs??ce,this.#d=t?.now??Date.now,this.#a=t?.nonce??K}current(){let e=this.#d();return(!this.#i||this.#i.expiresAt<=e)&&this.#c(e),this.#s()}regenerate(){return this.#c(this.#d()),this.#s()}#c(e){this.#i={nonce:this.#a(),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 Ne 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",le="(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@]|%[0-9A-Fa-f]{2})",ut=`${le}+`,He=new RegExp(`^${le}+$`),Ce=new RegExp("^(?:[A-Za-z0-9\\-._~!$&'()*+,;=:@/?]|%[0-9A-Fa-f]{2})*$"),Te=/^\.{1,2}$/,Ue=/[/\\#?]/,De=/[\u0000-\u001f\u007f]/,$e=6;function Ge(r){let e=r;for(let t=0;t<$e;t+=1){if(Te.test(e)||Ue.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 ge(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 a=i[s];if(!He.test(a)||Ge(a))return null}if(n!==""&&!Ce.test(n.slice(1)))return null;let o;try{o=new URL(t+n,pe)}catch{return null}return o.origin!==pe||o.hash!==""||o.pathname!==t||o.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=Ne({baseUrl:n.origin+"/v1",fetch:qe(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 o=ge(t);if(!o?.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=o.path.slice(3),a=ue.find(m=>m.method===e&&new RegExp("^"+m.path.replace(/\{[^}]+\}/g,"[A-Za-z0-9_-]+")+"$").test(s));if(!a)throw new Error("This route is outside the reviewed agent API.");let c={};a.path.split("/").forEach((m,g)=>{m.startsWith("{")&&(c[m.slice(1,-1)]=s.split("/")[g])});let u=Object.fromEntries(new URLSearchParams(o.query)),l=new AbortController,k=setTimeout(()=>l.abort(),3e4);try{let m=await this.#r.request(e,a.path,{params:{path:c,query:u},...n===void 0?{}:{body:n},headers:i?{authorization:`Bearer ${i}`}:{},credentials:"omit",redirect:"error",cache:"no-store",signal:l.signal,parseAs:"text"}),g=m.response,v=m.error;if(g.ok)try{v=m.data?JSON.parse(m.data):{}}catch{throw new Error("Invalid JSON response.")}if(!g.ok){let R=Z(Z(v)?.error),G=(typeof R?.message=="string"?R.message:`Agent request failed with ${g.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=g.headers.get("retry-after"),A=B&&/^\d+(?:\.\d+)?$/.test(B)?Number(B):null,N=Z(R?.details),q=N&&Object.fromEntries(Object.entries(N).filter(([S,_])=>["missingModule","declineReason","chainId"].includes(S)&&(typeof _=="number"||typeof _=="string"&&/^[a-zA-Z0-9_-]{1,100}$/.test(_)))),P=new h(G.slice(0,1e3),g.status,q,A);throw P.requestId=me(g.headers.get("x-request-id")),P.code=me(R?.code)??"http_error",P.ambiguous=e==="post"&&g.status>=500,P}return v}catch(m){if(m instanceof h)throw m;let g=new h("The agent request did not complete. Check the existing operation before retrying.",0);throw g.code=l.signal.aborted?"timeout":"transport_error",g.ambiguous=e==="post",g}finally{clearTimeout(k)}}};function Z(r){return typeof r=="object"&&r!==null&&!Array.isArray(r)?r:null}function me(r){return typeof r=="string"&&/^[a-zA-Z0-9_-]{1,128}$/.test(r)&&!/^(?:ags|bsk|dcs|dca|dcr)_/.test(r)?r:void 0}function qe(r){return async(e,t)=>{let n=e instanceof Request?e:new Request(e,t),i=n.signal,o=()=>{},s=new Promise((c,u)=>{o=()=>u(new Error("Request aborted.")),i.aborted?o():i.addEventListener("abort",o,{once:!0})}),a;try{let c=await Promise.race([r(n),s]);if(!c.body)return c;a=c.body.getReader();let u=[],l=0;for(;;){let g=await Promise.race([a.read(),s]);if(g.done)break;if(l+=g.value.byteLength,l>4*1024*1024)throw new Error("Response too large.");u.push(g.value)}let k=new Uint8Array(l),m=0;for(let g of u)k.set(g,m),m+=g.byteLength;return new Response(k,{status:c.status,statusText:c.statusText,headers:c.headers})}finally{i.removeEventListener("abort",o),a&&a.cancel().catch(()=>{})}}}import{recoverAddress as Me}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 o=n(),s=await r(e);for(;["pending","pending_approval","submitted","queued"].includes(String(s.status))&&n()-o<t;)await i(1500),s=await r(e);return s}var je=3e4,fe=300*1e3,ye="https://api.app.botanary.xyz",Y=class{#r;#e;#d;#a;#i=null;#c=null;#s=null;#u=null;#p=null;#n=0;#g;constructor(e,t={}){if(this.#r=e,this.#d=t.baseUrl??ye,this.#e=t.transport??new D(this.#d,t.fetch),this.#a=t.provenance??{},t.maxGasCostWei!==void 0&&(!/^[1-9][0-9]{0,77}$/.test(t.maxGasCostWei)||BigInt(t.maxGasCostWei)>=2n**256n))throw new Error("Use a positive integer agent gas ceiling in wei.");this.#g=t.maxGasCostWei??null}get apiBaseUrl(){return this.#d}get identityMaterialized(){return this.#i!==null||this.#u!==null}setProvenance(e){this.#a=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.#o()}if(e!==this.#n)throw this.#o();let i=H(n);return this.#i=i,i})();this.#u=t;try{return await t}finally{this.#u===t&&(this.#u=null)}}#o(){let e=new h("The agent signer could not complete the request.",0);return e.code="signer_error",e}async#l(e){p(e,32);let t=this.#n,n=await this.identity();if(t!==this.#n)throw this.#o();try{let i=await this.#r.signHash(e);p(i,65);let o=await Me({hash:e,signature:i});if(t!==this.#n||o.toLowerCase()!==n.address.toLowerCase())throw this.#o();return i}catch{throw this.#o()}}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}`,o=Le(ze(i));return await this.#e.post("/v1/agents/pair",{code:e.code,publicKey:n.publicKey,timestamp:t,signature:await this.#l(o),...this.#a.client?{client:this.#a.client}:{},...this.#a.profile?{profile:this.#a.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(p(i.nonce,32),e!==this.#n)throw this.#o();let o=await this.#l(i.nonce),s=y(await this.#e.post("/v1/agents/session",{address:n.address,nonce:i.nonce,signature:o}));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)&&d(),e!==this.#n)throw this.#o();return this.#s={token:s.token,safeUntil:Date.parse(s.expiresAt)-je},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),o=this.#n,s=await this.#t(u=>this.#e.post(`/v1/delegations/${i.delegationId}/actions/exact`,n,u),!1),a=await se(s,n,i),c;return Object.freeze({status:"prepared",userOpHash:a.userOpHash,execution:a.execution,submit:()=>(c||(c=(async()=>{if(o!==this.#n)throw this.#o();let u=await this.ensureSession();if(o!==this.#n)throw this.#o();return j(await this.spendUnderGrant(a.userOp,a.userOpHash,i.permissionId,u,"delegated_action"),a.userOpHash)})()),c),reconcile:()=>this.getUserOpByHash(n.chainId,a.userOpHash)})}async prepareAction(e){let{mandate:t,action:n}=structuredClone(e),i=/^[1-9][0-9]{0,77}$/.test(n.amountRaw)?BigInt(n.amountRaw):0n,o=t.policy.perActionMaxRaw&&/^[1-9][0-9]{0,77}$/.test(t.policy.perActionMaxRaw)?BigInt(t.policy.perActionMaxRaw):null,s=t.chainState.remaining?.find(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase())?.remainingRaw??null,a=u=>Object.freeze({status:"refused",reason:u,transactionSubmitted:!1,requestedAmountRaw:n.amountRaw,perActionMaxRaw:t.policy.perActionMaxRaw,remainingRaw:s});if(t.chainId!==84532||!["submitted","unresolved","confirmed"].includes(t.createStatus)||t.chainState.status!=="active"||t.apiAccess!=="active")return a(t.revokeStatus==="confirmed"||t.chainState.status==="revoked"?"mandate_revoked":Date.parse(t.policy.expiresAt)<=Date.now()?"mandate_expired":"mandate_inactive");if(i===0n||!t.policy.budgets.some(u=>u.token.toLowerCase()===n.tokenAddress.toLowerCase()))return a("mandate_inactive");if(o!==null&&i>o)return a("over_per_action_max");if(t.policy.recipients.length>0&&!t.policy.recipients.some(u=>u.toLowerCase()===n.recipient.toLowerCase()))return a("wrong_recipient");if((await this.identity()).address.toLowerCase()!==t.agentAddress.toLowerCase())return a("mandate_inactive");if(!this.#g)throw new Error("Configure maxGasCostWei before preparing mandate actions.");return this.prepareExactAction({accountId:t.accountId,accountAddress:t.accountAddress,chainId:84532,action:"transfer",tokenAddress:n.tokenAddress,recipient:n.recipient,amount:n.amountRaw},{delegationId:t.id,permissionId:t.permissionId,executorAddress:t.grantExecutor,maxGasCostWei:this.#g})}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,o){C(e),p(t,32),p(n,32);let s=await this.#l(t),a=he(["bytes1","bytes32","bytes"],["0x00",n,s]);try{return await this.#e.post("/v1/userops",{userOp:{...e,signature:a},userOpHash:t,intentType:o},i)}catch(c){throw c instanceof h&&(c.userOpHash=t),c}}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)&&d(),p(t,32),j(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)||d(),this.#m(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.#m({requirementId:e,status:"ready",...T(n.ready)},t):n.status!=="pending_approval"?n:this.#m({requirementId:e,status:"pending_approval",approvalId:e,expiresAt:""},t)}async#m(e,t=fe){let n;if(e.status==="pending_approval"){let a=await X(c=>this.#t(u=>this.#e.get(`/v1/apis/calls/requirements/${c}`,u)),e.requirementId,t,()=>Date.now(),c=>new Promise(u=>setTimeout(u,c)));if(a.status==="approved"&&a.payload&&a.authorization&&a.index!==void 0&&a.validator)n={payload:a.payload,authorization:a.authorization,index:a.index,validator:a.validator};else{let c=a.status==="declined"?"owner_declined":a.status==="expired"?"approval_timed_out":"approval_still_pending",u=c==="owner_declined"?"The owner declined this call. Nothing was signed and nothing was charged.":c==="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(a.status)||d(),{status:a.status==="pending"?"pending_approval":"declined",declineReason:c,message:u,requirementId:e.requirementId,approvalId:e.status==="pending_approval"?e.approvalId:void 0}}}else n=e;n=T(n);let i=await this.identity(),o=await this.#l(n.payload),s=he(["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,o])]);try{return z(await this.#t(a=>this.#e.post("/v1/apis/calls/relay",{requirementId:e.requirementId,signature:s},a),!1),e.requirementId)}catch(a){throw a instanceof h&&(a.requirementId=e.requirementId),a}}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.#c=null,this.#s=null}async#h(){if(!this.#c){let e=await this.identity();this.#c??=new O(e.fingerprint)}return this.#c}async#t(e,t=!0){let n=await this.ensureSession();try{return await e(n)}catch(i){if(t&&i instanceof h&&i.status===401){let o=this.#s&&this.#s.token!==n?await this.ensureSession():await this.mintSession();return await e(o)}throw i}}};function We(r,e={}){let t={address:r.address,publicKey:r.publicKey,fingerprint:V(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 Ve}from"viem";async function Ke(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)||d();let o=n.publicKey.toLowerCase(),s=n.address.toLowerCase(),a=await e.agents.challenge({publicKey:o,name:t.trim()});(!a||a.appId!==i.appId||a.environment!==i.environment||a.publicKey!==o||a.address!==s||!/^dach_[A-Za-z0-9_-]{43}$/.test(a.id)||typeof a.expiresAt!="string"||!Number.isFinite(Date.parse(a.expiresAt))||Date.parse(a.expiresAt)<=Date.now()||Date.parse(a.expiresAt)>Date.now()+36e4)&&d();let c=["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: ${o}`,`Address: ${s}`,`Nonce: ${a.id}`,`Expires: ${a.expiresAt}`,"This proof does not authorize an account connection or an on-chain grant."].join(`
2
2
  `);a.message!==c&&d();let u;try{if(u=await r.sign(c),p(u,65),(await Ve({message:c,signature:u})).toLowerCase()!==s)throw Q()}catch{throw Q()}let l=await e.agents.complete({challengeId:a.id,signature:u});return(!l||l.appId!==i.appId||l.environment!==i.environment||l.publicKey!==o||l.address!==s||l.grantStatus!=="unknown"||!["awaiting_connection","active","disabled","app_disabled","key_revoked","connection_revoked","unavailable"].includes(l.apiAccess)||l.apiAccess==="active"&&l.connectionId===null||l.apiAccess==="awaiting_connection"&&l.connectionId!==null)&&d(),l.connectionId!==null&&w(l.connectionId),l}function Q(){let r=new h("The agent signer could not complete registration.",0);return r.code="signer_error",r}export{Y as AgentRuntime,h as BotanaryApiError,ye as DEFAULT_API_BASE_URL,ce as DEFAULT_PAIRING_CODE_TTL_MS,oe as FINGERPRINT_BYTES,de as NONCE_BYTES,O as PairingCodeManager,X as awaitTerminalWith,We as createViemAgentSigner,W as crockfordBase32,V as deriveFingerprint,K as generatePairingNonce,Ke as registerAgent};
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@botanary/agent",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.6",
4
4
  "description": "Botanary agent session and execution runtime with an injected signer",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "engines": {
8
8
  "node": ">=22"
9
9
  },
10
+ "packageManager": "pnpm@11.5.1",
10
11
  "sideEffects": false,
11
12
  "files": [
12
13
  "dist",
@@ -23,6 +24,18 @@
23
24
  "types": "./dist/generated/schema.d.ts"
24
25
  }
25
26
  },
27
+ "scripts": {
28
+ "generate": "node scripts/generate.mjs",
29
+ "check:generated": "node scripts/generate.mjs --check",
30
+ "check:inventory": "node ../protected-boundary/check-inventory.mjs agent",
31
+ "typecheck": "tsc --noEmit",
32
+ "build": "node ../protected-boundary/release-build.mjs agent",
33
+ "build:tsc": "tsc",
34
+ "audit:artifact": "node ../protected-boundary/audit-artifact.mjs agent",
35
+ "test": "vitest run",
36
+ "prepack": "pnpm check:generated && pnpm typecheck && pnpm build",
37
+ "test:packed": "node scripts/verify-packed.mjs"
38
+ },
26
39
  "dependencies": {
27
40
  "openapi-fetch": "0.17.0",
28
41
  "viem": "2.54.6"
@@ -37,16 +50,5 @@
37
50
  },
38
51
  "publishConfig": {
39
52
  "access": "public"
40
- },
41
- "scripts": {
42
- "generate": "node scripts/generate.mjs",
43
- "check:generated": "node scripts/generate.mjs --check",
44
- "check:inventory": "node ../protected-boundary/check-inventory.mjs agent",
45
- "typecheck": "tsc --noEmit",
46
- "build": "node ../protected-boundary/release-build.mjs agent",
47
- "build:tsc": "tsc",
48
- "audit:artifact": "node ../protected-boundary/audit-artifact.mjs agent",
49
- "test": "vitest run",
50
- "test:packed": "node scripts/verify-packed.mjs"
51
53
  }
52
- }
54
+ }