@botanary/agent 0.1.0-alpha.1 → 0.1.0-alpha.12
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 +36 -0
- package/README.md +43 -5
- package/dist/errors.d.ts +1 -38
- package/dist/exact-action.d.ts +61 -8
- package/dist/fingerprint.d.ts +0 -15
- package/dist/generated/routes.d.ts +0 -1
- package/dist/generated/schema.d.ts +8 -1092
- package/dist/http-path.d.ts +0 -81
- package/dist/index.d.ts +1 -2
- 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 +3 -128
- package/dist/signer.d.ts +0 -7
- package/dist/transport.d.ts +0 -3
- package/dist/validation.d.ts +4 -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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-alpha.11
|
|
4
|
+
|
|
5
|
+
- Redact credential material from transport errors and validate typed runtime responses with strict timestamps.
|
|
6
|
+
- Reject invalid display amounts before transport and enforce the documented pairing-code configuration and wire format.
|
|
7
|
+
|
|
8
|
+
## 0.1.0-alpha.7
|
|
9
|
+
|
|
10
|
+
- Republish the empty-recipient unrestricted-mandate fix through the exact protected pnpm artifact path.
|
|
11
|
+
|
|
12
|
+
## 0.1.0-alpha.6
|
|
13
|
+
|
|
14
|
+
- Treat an empty mandate recipient list as unrestricted while preserving exact membership checks for non-empty recipient allowlists.
|
|
15
|
+
|
|
16
|
+
## 0.1.0-alpha.5
|
|
17
|
+
|
|
18
|
+
- Add `prepareAction({ mandate, action })` for exact Base Sepolia transfers from a confirmed mandate projection.
|
|
19
|
+
- Allow a mandate with strict active on-chain state to prepare an action while its durable creation operation still awaits final settlement.
|
|
20
|
+
- Return deterministic typed refusals for over-limit, wrong-recipient, inactive, expired, and revoked mandates before signing or transport.
|
|
21
|
+
- Require a trusted per-runtime native gas ceiling for the mandate facade and preserve the existing exact action hash, nonce-lane, one-shot submission, and reconciliation checks.
|
|
22
|
+
|
|
23
|
+
## Unreleased - protected artifact build
|
|
24
|
+
|
|
25
|
+
- No public method changed. `AgentRuntime` still contains no planning, tool selection, or
|
|
26
|
+
operation/grant/API-payment compilation and no route or provider selection - the hosted platform
|
|
27
|
+
re-resolves both swap legs from its own per-chain manifest. The only local trust logic is the exact
|
|
28
|
+
challenge/digest verifier behind `prepareExactAction` and `registerAgent`, plus one-shot signing and
|
|
29
|
+
submission fencing; both signer-defense units carry a written threat argument in
|
|
30
|
+
`packages/protected-boundary/inventory.json`.
|
|
31
|
+
- Ship an explicit release build: `dist/index.js` is bundled and minified with esbuild, comments
|
|
32
|
+
removed, `viem` and `openapi-fetch` kept external, `node:` built-ins left in place, and no
|
|
33
|
+
JavaScript source maps or declaration maps are emitted.
|
|
34
|
+
- `pnpm run audit:artifact` proves the exact tarball contains only allowlisted declarations, bundled
|
|
35
|
+
runtime, README, changelog, manifest and licence.
|
|
36
|
+
- `pnpm check:inventory` classifies all 13 runtime exports and fails CI on drift.
|
|
37
|
+
- No new live-chain or deployed-platform evidence.
|
|
38
|
+
|
|
3
39
|
## 0.1.0-alpha.1
|
|
4
40
|
|
|
5
41
|
- Recover durable API payment status without signing or forwarding an existing submission again.
|
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# @botanary/agent
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
This package has not been verified against a deployed developer platform or a settled testnet transaction.
|
|
3
|
+
Version 0.1.0-alpha.12, canonical API contract 2026-09-12. MIT licensed. Node 22 or later.
|
|
5
4
|
|
|
6
5
|
The runtime is extracted from `botanary-mcp`. MCP keeps desktop keychain adapters, profile selection,
|
|
7
6
|
owner sessions and approval handles. The CLI consumes that desktop adapter through MCP. The shared
|
|
@@ -69,6 +68,31 @@ No mainnet grant capability is added here.
|
|
|
69
68
|
|
|
70
69
|
## Exact execution
|
|
71
70
|
|
|
71
|
+
For the developer-first mandate flow, configure one trusted native gas ceiling when constructing the
|
|
72
|
+
runtime, then pass the confirmed mandate projection directly. The facade accepts transfer only and
|
|
73
|
+
maps `amountRaw` without floating-point conversion:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const runtime = new AgentRuntime(agentSigner, { maxGasCostWei: '10000000000000000' });
|
|
77
|
+
const prepared = await runtime.prepareAction({
|
|
78
|
+
mandate,
|
|
79
|
+
action: { type: 'transfer', tokenAddress, recipient, amountRaw: '1000000' },
|
|
80
|
+
});
|
|
81
|
+
if (prepared.status === 'refused') {
|
|
82
|
+
// No transaction was submitted. Render reason and remainingRaw as policy evidence.
|
|
83
|
+
return prepared;
|
|
84
|
+
}
|
|
85
|
+
remember(prepared.userOpHash);
|
|
86
|
+
return prepared.submit();
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The projection must report confirmed creation, active API access, observed active chain authority,
|
|
90
|
+
the exact account, agent, permission, executor, policy, and remaining limit. The package rechecks the
|
|
91
|
+
runtime agent identity. Over-limit, wrong-recipient, inactive, expired, and revoked requests return a
|
|
92
|
+
typed refusal before build or signing. An allowed request continues through the same exact call,
|
|
93
|
+
nonce-lane, gas-ceiling, hash, signature, one-shot submission, and reconciliation checks described
|
|
94
|
+
below. OpenAI integration, key loading, persistence, and retries remain host responsibilities.
|
|
95
|
+
|
|
72
96
|
An already connected agent with an owner-signed grant can prepare an action. Obtain the delegation ID,
|
|
73
97
|
permission ID and pinned executor address from that approved grant and its network deployment. The
|
|
74
98
|
app key is never an execution credential. Amounts and the maximum native gas cost are integer strings.
|
|
@@ -118,6 +142,20 @@ API access disablement and on-chain permission revocation are separate. Disablin
|
|
|
118
142
|
not revoke a previously signed grant. Never interpret `grantStatus: unknown` as revoked authority.
|
|
119
143
|
|
|
120
144
|
From this package directory: `pnpm install --frozen-lockfile`, `pnpm check:generated`, `pnpm typecheck`,
|
|
121
|
-
`pnpm test`,
|
|
122
|
-
|
|
123
|
-
|
|
145
|
+
`pnpm test`, `pnpm build`, `pnpm check:inventory`, `pnpm run audit:artifact`, and `pnpm test:packed`.
|
|
146
|
+
Install the independent SDK package before the packed check. The CLI and MCP package integration
|
|
147
|
+
checks live in their private repositories.
|
|
148
|
+
|
|
149
|
+
## What ships in the npm tarball
|
|
150
|
+
|
|
151
|
+
Allowlisted declarations (no declaration maps), one bundled and minified `dist/index.js` (no source
|
|
152
|
+
maps, no comments; `viem`, `openapi-fetch` and `node:` built-ins external), this README, the
|
|
153
|
+
changelog and `LICENSE`. `pnpm run audit:artifact` verifies the exact tarball.
|
|
154
|
+
|
|
155
|
+
Every runtime export is classified in
|
|
156
|
+
[`docs/specs/2026-09-10-protected-sdk-integration-boundary.inventory.md`](../../docs/specs/2026-09-10-protected-sdk-integration-boundary.inventory.md).
|
|
157
|
+
`AgentRuntime` carries no planning, tool selection, compilation, or route/provider selection - the
|
|
158
|
+
hosted platform re-resolves both swap legs from its own manifest. The only local trust logic is the
|
|
159
|
+
exact challenge/digest verifier behind `prepareExactAction` and `registerAgent` plus one-shot
|
|
160
|
+
signing and submission fencing, each with a written threat argument. No important logic is
|
|
161
|
+
distributed; minification is not concealment.
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,25 +1,6 @@
|
|
|
1
|
-
/** Thrown for any non-2xx response, carrying the HTTP status alongside the SERVER's own message (never a
|
|
2
|
-
* generic one - whoever built this agent needs the reason, §6-40 one layer up from the chain). The
|
|
3
|
-
* status is what lets `AgentRuntime` tell "this session token is stale, re-mint and retry" (401) apart
|
|
4
|
-
* from every other failure, which it should surface as-is. */
|
|
5
1
|
export declare class BotanaryApiError extends Error {
|
|
6
2
|
readonly status: number;
|
|
7
|
-
/**
|
|
8
|
-
* The backend's own `error.details` blob, carried through rather than flattened into the message.
|
|
9
|
-
* Some refusals are ACTIONABLE and say so in structured fields - `missingModule` names a setup module
|
|
10
|
-
* the account lacks - and a tool that can only read prose has to pattern-match on wording to notice.
|
|
11
|
-
* Keeping the object means a handler keys off a field the backend guarantees instead of a sentence it
|
|
12
|
-
* is free to reword. Null whenever the response carried none.
|
|
13
|
-
*/
|
|
14
3
|
readonly details: Record<string, unknown> | null;
|
|
15
|
-
/**
|
|
16
|
-
* The `Retry-After` header's value in SECONDS, when the server sent one (429/503 carry it). Null
|
|
17
|
-
* otherwise. Kept as a first-class field because a caller that wants to survive a rate limit -
|
|
18
|
-
* `wallet/login.ts`'s poll loop is the one that must - cannot see response headers otherwise, and
|
|
19
|
-
* guessing a backoff when the server told you the exact number is how a retry storm starts.
|
|
20
|
-
* Only the numeric (delta-seconds) form is parsed; the HTTP-date form yields null, and the caller
|
|
21
|
-
* falls back to its own backoff rather than mis-parsing a date as a duration.
|
|
22
|
-
*/
|
|
23
4
|
readonly retryAfterSeconds: number | null;
|
|
24
5
|
requestId?: string;
|
|
25
6
|
code?: string;
|
|
@@ -27,23 +8,5 @@ export declare class BotanaryApiError extends Error {
|
|
|
27
8
|
userOpHash?: string;
|
|
28
9
|
requirementId?: string;
|
|
29
10
|
ambiguous: boolean;
|
|
30
|
-
constructor(message: string, status: number,
|
|
31
|
-
/**
|
|
32
|
-
* The backend's own `error.details` blob, carried through rather than flattened into the message.
|
|
33
|
-
* Some refusals are ACTIONABLE and say so in structured fields - `missingModule` names a setup module
|
|
34
|
-
* the account lacks - and a tool that can only read prose has to pattern-match on wording to notice.
|
|
35
|
-
* Keeping the object means a handler keys off a field the backend guarantees instead of a sentence it
|
|
36
|
-
* is free to reword. Null whenever the response carried none.
|
|
37
|
-
*/
|
|
38
|
-
details?: Record<string, unknown> | null,
|
|
39
|
-
/**
|
|
40
|
-
* The `Retry-After` header's value in SECONDS, when the server sent one (429/503 carry it). Null
|
|
41
|
-
* otherwise. Kept as a first-class field because a caller that wants to survive a rate limit -
|
|
42
|
-
* `wallet/login.ts`'s poll loop is the one that must - cannot see response headers otherwise, and
|
|
43
|
-
* guessing a backoff when the server told you the exact number is how a retry storm starts.
|
|
44
|
-
* Only the numeric (delta-seconds) form is parsed; the HTTP-date form yields null, and the caller
|
|
45
|
-
* falls back to its own backoff rather than mis-parsing a date as a duration.
|
|
46
|
-
*/
|
|
47
|
-
retryAfterSeconds?: number | null);
|
|
11
|
+
constructor(message: string, status: number, details?: Record<string, unknown> | null, retryAfterSeconds?: number | null);
|
|
48
12
|
}
|
|
49
|
-
//# sourceMappingURL=errors.d.ts.map
|
package/dist/exact-action.d.ts
CHANGED
|
@@ -7,7 +7,6 @@ export interface AgentActionBinding {
|
|
|
7
7
|
delegationId: string;
|
|
8
8
|
permissionId: Hex;
|
|
9
9
|
executorAddress: Hex;
|
|
10
|
-
/** Maximum native gas cost for this operation, in wei. */
|
|
11
10
|
maxGasCostWei: string;
|
|
12
11
|
}
|
|
13
12
|
export interface SwapRouteFacts {
|
|
@@ -23,26 +22,80 @@ export interface SwapRouteFacts {
|
|
|
23
22
|
export interface AgentSwapBinding extends AgentActionBinding {
|
|
24
23
|
routerAddress: Hex;
|
|
25
24
|
minAmountOut: string;
|
|
26
|
-
/** For a reviewed router ABI other than swapExactIn. Must verify all supplied facts. */
|
|
27
25
|
verifyRoute?: (facts: Readonly<SwapRouteFacts>) => boolean | Promise<boolean>;
|
|
28
26
|
}
|
|
29
27
|
export interface PreparedAgentAction {
|
|
28
|
+
readonly status: 'prepared';
|
|
30
29
|
readonly userOpHash: Hex;
|
|
31
30
|
readonly execution: Readonly<ExactAgentExecution>;
|
|
32
|
-
/** One attempt per prepared action, including concurrent calls and transport failures. */
|
|
33
31
|
submit(): Promise<Record<string, unknown>>;
|
|
34
|
-
/** Read by the original hash. A 404 is still an unknown network outcome. */
|
|
35
32
|
reconcile(): Promise<Record<string, unknown>>;
|
|
36
33
|
}
|
|
37
|
-
|
|
34
|
+
export interface AgentMandateProjection {
|
|
35
|
+
readonly id: string;
|
|
36
|
+
readonly accountId: string;
|
|
37
|
+
readonly accountAddress: Hex;
|
|
38
|
+
readonly agentAddress: Hex;
|
|
39
|
+
readonly chainId: 84532;
|
|
40
|
+
readonly permissionId: Hex;
|
|
41
|
+
readonly grantExecutor: Hex;
|
|
42
|
+
readonly apiAccess: string;
|
|
43
|
+
readonly createStatus: string;
|
|
44
|
+
readonly revokeStatus: string | null;
|
|
45
|
+
readonly policy: {
|
|
46
|
+
readonly budgets: readonly {
|
|
47
|
+
readonly token: Hex;
|
|
48
|
+
readonly amountRaw: string;
|
|
49
|
+
}[];
|
|
50
|
+
readonly perActionMaxRaw: string | null;
|
|
51
|
+
readonly recipients: readonly Hex[];
|
|
52
|
+
readonly maxActions: number | null;
|
|
53
|
+
readonly expiresAt: string;
|
|
54
|
+
};
|
|
55
|
+
readonly chainState: {
|
|
56
|
+
readonly status: string;
|
|
57
|
+
readonly remaining?: readonly {
|
|
58
|
+
readonly token: Hex;
|
|
59
|
+
readonly remainingRaw: string;
|
|
60
|
+
}[];
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export interface MandateTransferInput {
|
|
64
|
+
readonly mandate: AgentMandateProjection;
|
|
65
|
+
readonly action: {
|
|
66
|
+
readonly type: 'transfer';
|
|
67
|
+
readonly tokenAddress: Hex;
|
|
68
|
+
readonly recipient: Hex;
|
|
69
|
+
readonly amountRaw: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export interface MandateSwapInput {
|
|
73
|
+
readonly mandate: AgentMandateProjection;
|
|
74
|
+
readonly action: {
|
|
75
|
+
readonly type: 'swap';
|
|
76
|
+
readonly tokenAddress: Hex;
|
|
77
|
+
readonly tokenOutAddress: Hex;
|
|
78
|
+
readonly amountRaw: string;
|
|
79
|
+
readonly routerAddress: Hex;
|
|
80
|
+
readonly minAmountOutRaw: string;
|
|
81
|
+
readonly maxSlippageBps?: number;
|
|
82
|
+
readonly verifyRoute?: (facts: SwapRouteFacts) => Promise<boolean> | boolean;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export type MandateActionInput = MandateTransferInput | MandateSwapInput;
|
|
86
|
+
export interface AgentPolicyRefusal {
|
|
87
|
+
readonly status: 'refused';
|
|
88
|
+
readonly reason: 'over_per_action_max' | 'wrong_recipient' | 'unbudgeted_swap_leg' | 'mandate_inactive' | 'mandate_revoked' | 'mandate_expired';
|
|
89
|
+
readonly transactionSubmitted: false;
|
|
90
|
+
readonly requestedAmountRaw: string;
|
|
91
|
+
readonly perActionMaxRaw: string | null;
|
|
92
|
+
readonly remainingRaw: string | null;
|
|
93
|
+
}
|
|
38
94
|
export declare function exactActionInput(value: ExactAgentAction): ExactAgentAction;
|
|
39
95
|
export declare function actionBinding(value: AgentActionBinding | AgentSwapBinding, action: ExactAgentAction): AgentActionBinding | AgentSwapBinding;
|
|
40
|
-
/** Validate the actual call and recompute its chain-bound v0.7 hash before handing it to a signer. */
|
|
41
96
|
export declare function validateExactAction(value: unknown, input: ExactAgentAction, binding: AgentActionBinding | AgentSwapBinding): Promise<{
|
|
42
97
|
userOp: UnsignedUserOp;
|
|
43
98
|
userOpHash: Hex;
|
|
44
99
|
execution: Readonly<ExactAgentExecution>;
|
|
45
100
|
}>;
|
|
46
|
-
/** Receipt validation happens after submission, so failure must retain the reconciliation handle. */
|
|
47
101
|
export declare function exactActionReceipt(value: unknown, hash: Hex): Record<string, unknown>;
|
|
48
|
-
//# sourceMappingURL=exact-action.d.ts.map
|
package/dist/fingerprint.d.ts
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
1
|
import type { Hex } from 'viem';
|
|
2
|
-
/** Encode `bytes` as Crockford Base32. Not meant to round-trip (no decoder here, and slicing the
|
|
3
|
-
* result to a shorter display length - see below - deliberately drops the tail) - this is a display
|
|
4
|
-
* transform for short human-facing codes, not a data-interchange encoding. */
|
|
5
2
|
export declare function crockfordBase32(bytes: Uint8Array): string;
|
|
6
|
-
/** Bytes of address fingerprint / pairing nonce - chosen for "short to type" over "cryptographically
|
|
7
|
-
* weighty", since neither is a secret (see README's "why intercepting the code alone grants nothing").
|
|
8
|
-
* FINGERPRINT_BYTES: ~24 bits, purely a human recognition aid - the full address is always available
|
|
9
|
-
* via get_identity for exact verification, so collisions here cost nothing but a moment's "hm, two
|
|
10
|
-
* agents look similar", not a security property.
|
|
11
|
-
* NONCE_BYTES: ~40 bits of fresh randomness per pairing code, the thing that actually makes each code
|
|
12
|
-
* single-use in practice (combined with the short TTL - see pairing-code.ts). */
|
|
13
3
|
export declare const FINGERPRINT_BYTES = 3;
|
|
14
4
|
export declare const NONCE_BYTES = 5;
|
|
15
|
-
/** A short, deterministic, human-typable id for an address - always the same for the same address.
|
|
16
|
-
* Purely a display/recognition aid (see module doc); the address itself is the real identifier. */
|
|
17
5
|
export declare function deriveFingerprint(address: Hex): string;
|
|
18
|
-
/** A fresh random nonce for one pairing code. Never derived from the key - pure randomness, so a new
|
|
19
|
-
* call can never be predicted from a previous one. */
|
|
20
6
|
export declare function generatePairingNonce(): string;
|
|
21
|
-
//# sourceMappingURL=fingerprint.d.ts.map
|