@bondedhq/sdk 0.0.0-stage → 0.1.1

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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +483 -2
  3. package/dist/actions.d.ts +76 -0
  4. package/dist/actions.js +115 -0
  5. package/dist/agent-url.d.ts +101 -0
  6. package/dist/agent-url.js +234 -0
  7. package/dist/agentkit.d.ts +104 -0
  8. package/dist/agentkit.js +206 -0
  9. package/dist/ai-sdk.d.ts +39 -0
  10. package/dist/ai-sdk.js +86 -0
  11. package/dist/breaches.d.ts +50 -0
  12. package/dist/breaches.js +57 -0
  13. package/dist/done.d.ts +14 -0
  14. package/dist/done.js +16 -0
  15. package/dist/eliza.d.ts +118 -0
  16. package/dist/eliza.js +281 -0
  17. package/dist/episode.d.ts +108 -0
  18. package/dist/episode.js +65 -0
  19. package/dist/errors.d.ts +93 -0
  20. package/dist/errors.js +97 -0
  21. package/dist/guarded.d.ts +80 -0
  22. package/dist/guarded.js +97 -0
  23. package/dist/index.d.ts +20 -0
  24. package/dist/index.js +19 -0
  25. package/dist/json-schema-zod3.d.ts +9 -0
  26. package/dist/json-schema-zod3.js +54 -0
  27. package/dist/langchain.d.ts +39 -0
  28. package/dist/langchain.js +53 -0
  29. package/dist/mandate.d.ts +36 -0
  30. package/dist/mandate.js +26 -0
  31. package/dist/mcp-config-cli.d.ts +3 -0
  32. package/dist/mcp-config-cli.js +44 -0
  33. package/dist/mcp.d.ts +131 -0
  34. package/dist/mcp.js +111 -0
  35. package/dist/module.d.ts +37 -0
  36. package/dist/module.js +32 -0
  37. package/dist/revert.d.ts +12 -0
  38. package/dist/revert.js +71 -0
  39. package/dist/sentinel.d.ts +43 -0
  40. package/dist/sentinel.js +94 -0
  41. package/dist/signature.d.ts +34 -0
  42. package/dist/signature.js +52 -0
  43. package/dist/solana-agent-kit.d.ts +141 -0
  44. package/dist/solana-agent-kit.js +333 -0
  45. package/dist/vault-tools.d.ts +67 -0
  46. package/dist/vault-tools.js +335 -0
  47. package/dist/vault.d.ts +116 -0
  48. package/dist/vault.js +326 -0
  49. package/package.json +134 -3
@@ -0,0 +1,80 @@
1
+ import { type EvmMandate } from "@bondedhq/shared";
2
+ import { type ApproveParams, type CallParams, type SwapParams, type TransferParams, type VaultAction } from "./actions.js";
3
+ import type { DryRunResult } from "./breaches.js";
4
+ import { type SentinelVerdict } from "./errors.js";
5
+ import { type MessageSigner, type SentinelInput } from "./sentinel.js";
6
+ import type { ActionResult, BondedVault } from "./vault.js";
7
+ /** A token the agent trades, so Sentinel sees symbols and whole units as it was trained on. */
8
+ export interface TokenInfo {
9
+ symbol: string;
10
+ address: string;
11
+ decimals: number;
12
+ }
13
+ export interface SentinelGuardOptions {
14
+ /** The Bonded API that serves `POST /sentinel/check`. */
15
+ apiUrl: string;
16
+ /** The mandate in plain words, or the mandate itself (described with `describeMandate`). */
17
+ mandate: string | EvmMandate;
18
+ /** Names token addresses in the check. */
19
+ tokens?: readonly TokenInfo[];
20
+ /** Defaults to the vault's `agentId()`. */
21
+ agentId?: bigint | number | string;
22
+ /** Defaults to the public client's chain. */
23
+ chainId?: number;
24
+ /** Signs the check. Defaults to the vault's wallet (the agent key). */
25
+ account?: MessageSigner;
26
+ /**
27
+ * When Sentinel can't answer (down, busy, rate limited): "refuse" the action (the default),
28
+ * or "proceed" without its advice. The vault enforces the mandate either way.
29
+ */
30
+ onUnavailable?: "refuse" | "proceed";
31
+ fetch?: typeof fetch;
32
+ /** Give up on Sentinel after this long, in milliseconds. Default 15,000. */
33
+ timeoutMs?: number;
34
+ }
35
+ export interface GuardOptions {
36
+ /** Ask Sentinel before each write that passes the dry run. Off unless set. */
37
+ sentinel?: SentinelGuardOptions;
38
+ }
39
+ /** What the agent read before this action, for Sentinel. */
40
+ export interface GuardContext {
41
+ untrustedInputs?: readonly SentinelInput[];
42
+ }
43
+ /** What a guarded write did, and the checks it passed. */
44
+ export interface GuardedResult extends ActionResult {
45
+ dryRun: DryRunResult;
46
+ /** Sentinel's answer, when it was asked and answered. */
47
+ sentinel?: SentinelVerdict;
48
+ }
49
+ /** The checks an action passed, without sending it. */
50
+ export interface GuardCheck {
51
+ dryRun: DryRunResult;
52
+ sentinel?: SentinelVerdict;
53
+ }
54
+ /**
55
+ * A vault that checks every write before sending it: first a dry run of `execute` against the
56
+ * chain's current state, refused if the vault would record a breach (`PredictedBreachError`);
57
+ * then, if configured, Sentinel, refused if it advises against (`SentinelRefusedError`).
58
+ *
59
+ * This turns a Monitor-mode vault into one that doesn't knowingly breach. It is a client-side
60
+ * check: the state can change between the dry run and the transaction, and the vault's own
61
+ * verdict on chain is what counts.
62
+ */
63
+ export declare class GuardedVault {
64
+ readonly vault: BondedVault;
65
+ private readonly options;
66
+ /** The chain id and agent id Sentinel checks are signed for, read once. */
67
+ private identity;
68
+ constructor(vault: BondedVault, options?: GuardOptions);
69
+ swap(params: SwapParams, context?: GuardContext): Promise<GuardedResult>;
70
+ transfer(params: TransferParams, context?: GuardContext): Promise<GuardedResult>;
71
+ approve(params: ApproveParams, context?: GuardContext): Promise<GuardedResult>;
72
+ call(params: CallParams, context?: GuardContext): Promise<GuardedResult>;
73
+ execute(action: VaultAction, context?: GuardContext): Promise<GuardedResult>;
74
+ /** Runs the guard's checks on an action and throws if one refuses. Sends nothing. */
75
+ check(action: VaultAction, context?: GuardContext): Promise<GuardCheck>;
76
+ private askSentinel;
77
+ }
78
+ /** Wraps a vault so every write is dry-run (and optionally checked by Sentinel) first. */
79
+ export declare function guarded(vault: BondedVault, options?: GuardOptions): GuardedVault;
80
+ //# sourceMappingURL=guarded.d.ts.map
@@ -0,0 +1,97 @@
1
+ import { describeMandate } from "@bondedhq/shared";
2
+ import { describeAction, } from "./actions.js";
3
+ import { BondedError, PredictedBreachError, SentinelError, SentinelRefusedError, } from "./errors.js";
4
+ import { checkWithSentinel } from "./sentinel.js";
5
+ /**
6
+ * A vault that checks every write before sending it: first a dry run of `execute` against the
7
+ * chain's current state, refused if the vault would record a breach (`PredictedBreachError`);
8
+ * then, if configured, Sentinel, refused if it advises against (`SentinelRefusedError`).
9
+ *
10
+ * This turns a Monitor-mode vault into one that doesn't knowingly breach. It is a client-side
11
+ * check: the state can change between the dry run and the transaction, and the vault's own
12
+ * verdict on chain is what counts.
13
+ */
14
+ export class GuardedVault {
15
+ vault;
16
+ options;
17
+ /** The chain id and agent id Sentinel checks are signed for, read once. */
18
+ identity;
19
+ constructor(vault, options = {}) {
20
+ this.vault = vault;
21
+ this.options = options;
22
+ }
23
+ swap(params, context) {
24
+ return this.execute({ type: "swap", ...params }, context);
25
+ }
26
+ transfer(params, context) {
27
+ return this.execute({ type: "transfer", ...params }, context);
28
+ }
29
+ approve(params, context) {
30
+ return this.execute({ type: "approve", ...params }, context);
31
+ }
32
+ call(params, context) {
33
+ return this.execute({ type: "call", ...params }, context);
34
+ }
35
+ async execute(action, context = {}) {
36
+ const checks = await this.check(action, context);
37
+ const result = await this.vault.execute(action);
38
+ return { ...result, ...checks };
39
+ }
40
+ /** Runs the guard's checks on an action and throws if one refuses. Sends nothing. */
41
+ async check(action, context = {}) {
42
+ const dryRun = await this.vault.dryRun(action);
43
+ if (!dryRun.ok)
44
+ throw new PredictedBreachError(dryRun);
45
+ const sentinel = await this.askSentinel(action, context);
46
+ if (sentinel?.blocked)
47
+ throw new SentinelRefusedError(sentinel);
48
+ return sentinel ? { dryRun, sentinel } : { dryRun };
49
+ }
50
+ async askSentinel(action, context) {
51
+ const s = this.options.sentinel;
52
+ if (!s)
53
+ return undefined;
54
+ const wallet = this.vault.walletClient;
55
+ const account = s.account ??
56
+ (wallet
57
+ ? { signMessage: (args) => wallet.signMessage({ account: wallet.account, ...args }) }
58
+ : undefined);
59
+ if (!account)
60
+ throw new BondedError("CONFIG", "Sentinel checks are signed by the agent key; none is set");
61
+ this.identity ??= Promise.all([
62
+ s.chainId ?? this.vault.publicClient.getChainId(),
63
+ s.agentId ?? this.vault.status().then((status) => status.agentId),
64
+ ]);
65
+ const [chainId, agentId] = await this.identity.catch((error) => {
66
+ this.identity = undefined;
67
+ throw error;
68
+ });
69
+ try {
70
+ return await checkWithSentinel({
71
+ apiUrl: s.apiUrl,
72
+ chainId,
73
+ agentId,
74
+ vault: this.vault.address,
75
+ mandate: typeof s.mandate === "string" ? s.mandate : describeMandate(s.mandate, s.tokens),
76
+ proposedAction: describeAction(action, s.tokens),
77
+ untrustedInputs: context.untrustedInputs ?? [],
78
+ account,
79
+ signal: AbortSignal.timeout(s.timeoutMs ?? 15_000),
80
+ ...(s.fetch ? { fetch: s.fetch } : {}),
81
+ });
82
+ }
83
+ catch (error) {
84
+ // Only unavailability may be skipped; a refused request (bad signature, wrong vault) is a bug.
85
+ const unavailable = error instanceof SentinelError &&
86
+ (error.status === undefined || error.status === 429 || error.status >= 500);
87
+ if (unavailable && s.onUnavailable === "proceed")
88
+ return undefined;
89
+ throw error;
90
+ }
91
+ }
92
+ }
93
+ /** Wraps a vault so every write is dry-run (and optionally checked by Sentinel) first. */
94
+ export function guarded(vault, options = {}) {
95
+ return new GuardedVault(vault, options);
96
+ }
97
+ //# sourceMappingURL=guarded.js.map
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @bondedhq/sdk: trade through a Bonded Mandate Vault from any TypeScript agent.
3
+ *
4
+ * Framework plugins live in subpaths, each with its framework as an optional peer dependency:
5
+ * `@bondedhq/sdk/agentkit`, `@bondedhq/sdk/solana-agent-kit`, `@bondedhq/sdk/eliza`,
6
+ * `@bondedhq/sdk/langchain`, `@bondedhq/sdk/ai-sdk`. The episode's tools over MCP are in
7
+ * `@bondedhq/sdk/mcp`, and the Agent URL handler in `@bondedhq/sdk/agent-url`.
8
+ */
9
+ export { BondedVault, type ActionResult, type BondedVaultConfig, type VaultStatus, } from "./vault.js";
10
+ export { demoRouter, describeAction, type ApproveParams, type CallParams, type SwapParams, type SwapRouter, type TransferParams, type VaultAction, type VaultCall, } from "./actions.js";
11
+ export { breachReason, type DryRunResult, type VaultBreach } from "./breaches.js";
12
+ export { guarded, GuardedVault, type GuardCheck, type GuardContext, type GuardedResult, type GuardOptions, type SentinelGuardOptions, type TokenInfo, } from "./guarded.js";
13
+ export { checkWithSentinel, type MessageSigner, type SentinelCheckRequest, type SentinelInput, } from "./sentinel.js";
14
+ export { buildMandate, describeMandate, type BuiltMandate, type EvmMandate, type EvmMandateParams, type EvmMandateRules, } from "./mandate.js";
15
+ export { BlockedByMandateError, BondedError, CallRevertedError, NotAgentError, PredictedBreachError, SentinelError, SentinelRefusedError, VaultFrozenError, type SentinelVerdict, } from "./errors.js";
16
+ export type { BreachType } from "@bondedhq/shared";
17
+ export { BONDED_VAULT_TOOLS, bondedVaultFromEnv, bondedVaultTools, type BondedVaultTool, type BondedVaultToolName, type BondedVaultToolsOptions, } from "./vault-tools.js";
18
+ export { bondedEpisodeEnv, episodeVault, fromBondedEnv, isBondedEpisode, type BondedEpisode, } from "./episode.js";
19
+ export { checkUnprotectedAllowances, deploymentTokens, unprotectedAllowancesWarning, type AllowanceCheck, type AllowanceFinding, type TokenRef, installMandateModule, type InstallMandateModuleInput, type ModuleAccountType, type ModuleInstallPlan, } from "./module.js";
20
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @bondedhq/sdk: trade through a Bonded Mandate Vault from any TypeScript agent.
3
+ *
4
+ * Framework plugins live in subpaths, each with its framework as an optional peer dependency:
5
+ * `@bondedhq/sdk/agentkit`, `@bondedhq/sdk/solana-agent-kit`, `@bondedhq/sdk/eliza`,
6
+ * `@bondedhq/sdk/langchain`, `@bondedhq/sdk/ai-sdk`. The episode's tools over MCP are in
7
+ * `@bondedhq/sdk/mcp`, and the Agent URL handler in `@bondedhq/sdk/agent-url`.
8
+ */
9
+ export { BondedVault, } from "./vault.js";
10
+ export { demoRouter, describeAction, } from "./actions.js";
11
+ export { breachReason } from "./breaches.js";
12
+ export { guarded, GuardedVault, } from "./guarded.js";
13
+ export { checkWithSentinel, } from "./sentinel.js";
14
+ export { buildMandate, describeMandate, } from "./mandate.js";
15
+ export { BlockedByMandateError, BondedError, CallRevertedError, NotAgentError, PredictedBreachError, SentinelError, SentinelRefusedError, VaultFrozenError, } from "./errors.js";
16
+ export { BONDED_VAULT_TOOLS, bondedVaultFromEnv, bondedVaultTools, } from "./vault-tools.js";
17
+ export { bondedEpisodeEnv, episodeVault, fromBondedEnv, isBondedEpisode, } from "./episode.js";
18
+ export { checkUnprotectedAllowances, deploymentTokens, unprotectedAllowancesWarning, installMandateModule, } from "./module.js";
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ import { z } from "zod/v3";
2
+ /**
3
+ * A zod 3 schema for a tool's JSON Schema, for frameworks whose tools are typed with zod 3
4
+ * (Coinbase AgentKit 0.10). It covers what the Arena's tools use (objects of strings with
5
+ * patterns and lengths, numbers, booleans, arrays, enums); anything else is accepted as is, and
6
+ * the gateway checks every call against the real schema anyway.
7
+ */
8
+ export declare function zod3FromJsonSchema(schema: unknown): z.ZodTypeAny;
9
+ //# sourceMappingURL=json-schema-zod3.d.ts.map
@@ -0,0 +1,54 @@
1
+ import { z } from "zod/v3";
2
+ /**
3
+ * A zod 3 schema for a tool's JSON Schema, for frameworks whose tools are typed with zod 3
4
+ * (Coinbase AgentKit 0.10). It covers what the Arena's tools use (objects of strings with
5
+ * patterns and lengths, numbers, booleans, arrays, enums); anything else is accepted as is, and
6
+ * the gateway checks every call against the real schema anyway.
7
+ */
8
+ export function zod3FromJsonSchema(schema) {
9
+ const s = (schema ?? {});
10
+ const described = (t) => typeof s.description === "string" ? t.describe(s.description) : t;
11
+ if (Array.isArray(s.enum) && s.enum.length > 0 && s.enum.every((v) => typeof v === "string"))
12
+ return described(z.enum(s.enum));
13
+ if ("const" in s && ["string", "number", "boolean"].includes(typeof s.const))
14
+ return described(z.literal(s.const));
15
+ switch (s.type) {
16
+ case "object": {
17
+ const properties = (s.properties ?? {});
18
+ const required = new Set(Array.isArray(s.required) ? s.required : []);
19
+ const shape = {};
20
+ for (const [key, value] of Object.entries(properties)) {
21
+ const t = zod3FromJsonSchema(value);
22
+ shape[key] = required.has(key) ? t : t.optional();
23
+ }
24
+ const object = z.object(shape);
25
+ return described(s.additionalProperties === false ? object.strict() : object.passthrough());
26
+ }
27
+ case "string": {
28
+ let t = z.string();
29
+ if (typeof s.minLength === "number")
30
+ t = t.min(s.minLength);
31
+ if (typeof s.maxLength === "number")
32
+ t = t.max(s.maxLength);
33
+ if (typeof s.pattern === "string")
34
+ t = t.regex(new RegExp(s.pattern));
35
+ return described(t);
36
+ }
37
+ case "integer":
38
+ case "number": {
39
+ let t = s.type === "integer" ? z.number().int() : z.number();
40
+ if (typeof s.minimum === "number")
41
+ t = t.min(s.minimum);
42
+ if (typeof s.maximum === "number")
43
+ t = t.max(s.maximum);
44
+ return described(t);
45
+ }
46
+ case "boolean":
47
+ return described(z.boolean());
48
+ case "array":
49
+ return described(z.array(zod3FromJsonSchema(s.items)));
50
+ default:
51
+ return described(z.any());
52
+ }
53
+ }
54
+ //# sourceMappingURL=json-schema-zod3.js.map
@@ -0,0 +1,39 @@
1
+ import { DynamicStructuredTool } from "@langchain/core/tools";
2
+ import { type EpisodeTools, type EpisodeToolsSource } from "./mcp.js";
3
+ import { type BondedVaultToolsOptions } from "./vault-tools.js";
4
+ export type { BondedVaultToolsOptions } from "./vault-tools.js";
5
+ /**
6
+ * The episode's tools as LangChain `StructuredTool`s, for LangChain and LangGraph agents
7
+ * (`createReactAgent`, `ToolNode`, `model.bindTools`). Each takes the tool's own JSON Schema and
8
+ * returns what the Arena returned, as text; a failed call returns `Error: ...` so the model can
9
+ * react, as it would to a production tool.
10
+ *
11
+ * With no argument it reads the episode from the runner's `BONDED_*` variables. Switch to them
12
+ * only while being rated:
13
+ *
14
+ * ```ts
15
+ * const tools = isBondedEpisode() ? await bondedLangChainTools() : productionTools;
16
+ * const agent = createReactAgent({ llm, tools });
17
+ * ```
18
+ *
19
+ * Peer dependency: `@langchain/core` 1.
20
+ */
21
+ export declare function bondedLangChainTools(from?: EpisodeTools | EpisodeToolsSource): Promise<DynamicStructuredTool[]>;
22
+ /**
23
+ * The agent's Mandate Vault as LangChain tools, for production: `swap`, `transfer`, `approve`
24
+ * and `vault_status`, every write sent through `BondedVault` so the vault checks it against the
25
+ * mandate. Each write is dry-run first and refused on a predicted breach (`guard`, on by
26
+ * default); with `sentinel`, Sentinel is asked too, with what `context()` says the agent read.
27
+ * A refusal or failure comes back as `Error: ...` text, so the model can react.
28
+ *
29
+ * The names and arguments match the episode's write tools, so the agent's prompts work the same
30
+ * while it is rated:
31
+ *
32
+ * ```ts
33
+ * const tools = isBondedEpisode()
34
+ * ? await bondedLangChainTools()
35
+ * : [...productionTools, ...bondedVaultLangChainTools({ vault })];
36
+ * ```
37
+ */
38
+ export declare function bondedVaultLangChainTools(options: BondedVaultToolsOptions): DynamicStructuredTool[];
39
+ //# sourceMappingURL=langchain.d.ts.map
@@ -0,0 +1,53 @@
1
+ import { DynamicStructuredTool } from "@langchain/core/tools";
2
+ import { resolveTools, resultText } from "./mcp.js";
3
+ import { bondedVaultTools } from "./vault-tools.js";
4
+ /**
5
+ * The episode's tools as LangChain `StructuredTool`s, for LangChain and LangGraph agents
6
+ * (`createReactAgent`, `ToolNode`, `model.bindTools`). Each takes the tool's own JSON Schema and
7
+ * returns what the Arena returned, as text; a failed call returns `Error: ...` so the model can
8
+ * react, as it would to a production tool.
9
+ *
10
+ * With no argument it reads the episode from the runner's `BONDED_*` variables. Switch to them
11
+ * only while being rated:
12
+ *
13
+ * ```ts
14
+ * const tools = isBondedEpisode() ? await bondedLangChainTools() : productionTools;
15
+ * const agent = createReactAgent({ llm, tools });
16
+ * ```
17
+ *
18
+ * Peer dependency: `@langchain/core` 1.
19
+ */
20
+ export async function bondedLangChainTools(from) {
21
+ const episode = await resolveTools(from);
22
+ return episode.tools.map((t) => new DynamicStructuredTool({
23
+ name: t.name,
24
+ description: t.description,
25
+ schema: t.inputSchema,
26
+ func: async (args) => resultText(await t.call(args ?? {})),
27
+ }));
28
+ }
29
+ /**
30
+ * The agent's Mandate Vault as LangChain tools, for production: `swap`, `transfer`, `approve`
31
+ * and `vault_status`, every write sent through `BondedVault` so the vault checks it against the
32
+ * mandate. Each write is dry-run first and refused on a predicted breach (`guard`, on by
33
+ * default); with `sentinel`, Sentinel is asked too, with what `context()` says the agent read.
34
+ * A refusal or failure comes back as `Error: ...` text, so the model can react.
35
+ *
36
+ * The names and arguments match the episode's write tools, so the agent's prompts work the same
37
+ * while it is rated:
38
+ *
39
+ * ```ts
40
+ * const tools = isBondedEpisode()
41
+ * ? await bondedLangChainTools()
42
+ * : [...productionTools, ...bondedVaultLangChainTools({ vault })];
43
+ * ```
44
+ */
45
+ export function bondedVaultLangChainTools(options) {
46
+ return bondedVaultTools(options).map((t) => new DynamicStructuredTool({
47
+ name: t.name,
48
+ description: t.description,
49
+ schema: t.inputSchema,
50
+ func: async (args) => resultText(await t.call(args ?? {})),
51
+ }));
52
+ }
53
+ //# sourceMappingURL=langchain.js.map
@@ -0,0 +1,36 @@
1
+ import { type EvmMandate, type EvmMandateParams, type EvmMandateRules } from "@bondedhq/shared";
2
+ import type { Hex } from "viem";
3
+ export type { EvmMandate, EvmMandateParams, EvmMandateRules };
4
+ export { describeMandate } from "@bondedhq/shared";
5
+ /** A mandate ready for `MandateVaultFactory.createVault`. */
6
+ export interface BuiltMandate {
7
+ /** The canonical mandate JSON: keep it, the API and raters check it against the hash. */
8
+ mandate: EvmMandate;
9
+ /**
10
+ * The hash stored on chain. For a vault: keccak256 of the canonical JSON. For a module: the
11
+ * commitment to the enforced params (`mandateCommitment`), which MandateModuleFactory checks.
12
+ * Ratings, quotes and cover use this value.
13
+ */
14
+ mandateHash: Hex;
15
+ /** The struct `createVault` (or, for a module, `createModule`) takes. */
16
+ params: EvmMandateParams;
17
+ }
18
+ /**
19
+ * Plain rules (dollars, percentages, function signatures) to the mandate a vault enforces.
20
+ * The same builder the web app's onboarding uses, so both hash the same rules the same way.
21
+ * Pass `{ for: "module" }` for a MandateModule on a smart account.
22
+ *
23
+ * ```ts
24
+ * const { params, mandate } = buildMandate({
25
+ * chainId: 46630, agentId: 12n, mode: "enforce",
26
+ * baseAsset: usdg, assets: [tsla],
27
+ * targets: [{ address: router, functions: ["swap(address,address,uint256,address)"] }],
28
+ * maxTradeUsd: 5_000, maxDailyUsd: 25_000, maxDrawdownPct: 15, maxSlippagePct: 3,
29
+ * description: "Rebalance a tech basket against tUSDG.",
30
+ * });
31
+ * ```
32
+ */
33
+ export declare function buildMandate(rules: EvmMandateRules, options?: {
34
+ for?: "vault" | "module";
35
+ }): BuiltMandate;
36
+ //# sourceMappingURL=mandate.d.ts.map
@@ -0,0 +1,26 @@
1
+ import { buildEvmMandate, mandateHash, toEvmMandateParams, withMandateCommitment, } from "@bondedhq/shared";
2
+ export { describeMandate } from "@bondedhq/shared";
3
+ /**
4
+ * Plain rules (dollars, percentages, function signatures) to the mandate a vault enforces.
5
+ * The same builder the web app's onboarding uses, so both hash the same rules the same way.
6
+ * Pass `{ for: "module" }` for a MandateModule on a smart account.
7
+ *
8
+ * ```ts
9
+ * const { params, mandate } = buildMandate({
10
+ * chainId: 46630, agentId: 12n, mode: "enforce",
11
+ * baseAsset: usdg, assets: [tsla],
12
+ * targets: [{ address: router, functions: ["swap(address,address,uint256,address)"] }],
13
+ * maxTradeUsd: 5_000, maxDailyUsd: 25_000, maxDrawdownPct: 15, maxSlippagePct: 3,
14
+ * description: "Rebalance a tech basket against tUSDG.",
15
+ * });
16
+ * ```
17
+ */
18
+ export function buildMandate(rules, options = {}) {
19
+ const mandate = buildEvmMandate(rules);
20
+ if (options.for === "module") {
21
+ const params = withMandateCommitment(toEvmMandateParams(mandate));
22
+ return { mandate, mandateHash: params.mandateHash, params };
23
+ }
24
+ return { mandate, mandateHash: mandateHash(mandate), params: toEvmMandateParams(mandate) };
25
+ }
26
+ //# sourceMappingURL=mandate.js.map
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=mcp-config-cli.d.ts.map
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env node
2
+ import { BondedError } from "./errors.js";
3
+ import { fromBondedEnv } from "./episode.js";
4
+ import { mcpConfigJson } from "./mcp.js";
5
+ /**
6
+ * `bonded-mcp-config [--style http|url|stdio] [--name <name>]`: prints the MCP config for the
7
+ * episode the Bonded runner set up (`BONDED_*` variables), for agents that read an `mcpServers`
8
+ * file. The URL is per episode: run it at the start of every episode.
9
+ */
10
+ const USAGE = "usage: bonded-mcp-config [--style http|url|stdio] [--name <name>]";
11
+ function main(argv) {
12
+ const options = {};
13
+ for (let i = 0; i < argv.length; i++) {
14
+ const flag = argv[i];
15
+ const value = argv[i + 1];
16
+ if (flag === "--help" || flag === "-h") {
17
+ process.stdout.write(`${USAGE}\n`);
18
+ return 0;
19
+ }
20
+ if (flag === "--style" && (value === "http" || value === "url" || value === "stdio")) {
21
+ options.style = value;
22
+ i++;
23
+ }
24
+ else if (flag === "--name" && value) {
25
+ options.name = value;
26
+ i++;
27
+ }
28
+ else {
29
+ process.stderr.write(`${USAGE}\n`);
30
+ return 2;
31
+ }
32
+ }
33
+ try {
34
+ process.stdout.write(mcpConfigJson(fromBondedEnv(), options));
35
+ return 0;
36
+ }
37
+ catch (error) {
38
+ process.stderr.write(`${error instanceof BondedError ? error.message : String(error)}\n` +
39
+ "bonded-mcp-config reads the episode the Bonded runner sets up; run it inside an episode.\n");
40
+ return 1;
41
+ }
42
+ }
43
+ process.exitCode = main(process.argv.slice(2));
44
+ //# sourceMappingURL=mcp-config-cli.js.map
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,131 @@
1
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2
+ import type { DoneRequest } from "@bondedhq/shared";
3
+ export { isBondedEpisode } from "./episode.js";
4
+ export { finishEpisode } from "./done.js";
5
+ /**
6
+ * Where an episode's tools are. A `BondedEpisode` (from `fromBondedEnv`, or the one an Agent URL
7
+ * handler hands you) has these, and so does the runner protocol's `EpisodeMessage`.
8
+ */
9
+ export interface EpisodeToolsSource {
10
+ endpoints: {
11
+ /** MCP over streamable HTTP. The URL carries the episode's capability: it is per episode. */
12
+ mcp: string;
13
+ /** `POST` here when finished. */
14
+ done: string;
15
+ };
16
+ }
17
+ /** A JSON Schema for a tool's arguments: always an object. */
18
+ export interface ToolInputSchema {
19
+ type: "object";
20
+ properties?: Record<string, unknown>;
21
+ required?: string[];
22
+ [key: string]: unknown;
23
+ }
24
+ /**
25
+ * The result of one tool call, as the Arena returns it. Failures are returned, not thrown, so the
26
+ * agent's model can read them and react, as it would to a production tool's error. `text` is what
27
+ * a model should read: the output as JSON, or the error.
28
+ */
29
+ export type EpisodeToolResult = {
30
+ ok: true;
31
+ output: unknown;
32
+ text: string;
33
+ } | {
34
+ ok: false;
35
+ error: string;
36
+ text: string;
37
+ };
38
+ /** One of the episode's tools, framework-neutral. */
39
+ export interface EpisodeTool {
40
+ name: string;
41
+ description: string;
42
+ inputSchema: ToolInputSchema;
43
+ /** Calls the tool at the gateway. */
44
+ call(args?: Record<string, unknown>): Promise<EpisodeToolResult>;
45
+ }
46
+ /** Usage an agent reports at the end of an episode (recorded, never trusted for limits). */
47
+ export type EpisodeUsage = NonNullable<DoneRequest["usage"]>;
48
+ /** An MCP client connected to one episode, with its tools listed. */
49
+ export interface EpisodeTools {
50
+ /** The official MCP SDK client, for anything not covered here. */
51
+ readonly client: Client;
52
+ /** The episode's tools, listed once at connect. */
53
+ readonly tools: EpisodeTool[];
54
+ /** MCP `tools/list`, as the server answers it. */
55
+ listTools(): ReturnType<Client["listTools"]>;
56
+ /** Calls a tool by name. */
57
+ callTool(name: string, args?: Record<string, unknown>): Promise<EpisodeToolResult>;
58
+ /** Tells the gateway the agent is finished (`POST {done}`). Later tool calls are refused. */
59
+ done(body?: DoneRequest): Promise<void>;
60
+ close(): Promise<void>;
61
+ }
62
+ export interface ConnectOptions {
63
+ /** Closes the connection when it fires (e.g. the episode's deadline). */
64
+ signal?: AbortSignal;
65
+ /** The `fetch` to use (default: the global one). */
66
+ fetch?: typeof fetch;
67
+ /** How the client names itself to the server. */
68
+ clientName?: string;
69
+ }
70
+ /**
71
+ * Connects to the episode's MCP endpoint and lists its tools. With no argument it reads the
72
+ * episode from the runner's `BONDED_*` variables (`fromBondedEnv`); in Agent URL mode pass the
73
+ * episode your handler received.
74
+ *
75
+ * ```ts
76
+ * const episode = await connectEpisodeTools();
77
+ * for (const tool of episode.tools) console.log(tool.name, tool.inputSchema);
78
+ * const prices = await episode.callTool("get_prices");
79
+ * await episode.done({ finalMessage: "bought tNVDA" });
80
+ * ```
81
+ */
82
+ export declare function connectEpisodeTools(episode?: EpisodeToolsSource, options?: ConnectOptions): Promise<EpisodeTools>;
83
+ /** The text a string-returning framework (LangChain, AgentKit) gives its model. */
84
+ export declare function resultText(result: EpisodeToolResult): string;
85
+ /** Tools from a connection, or a fresh connection to the episode. */
86
+ export declare function resolveTools(from: EpisodeTools | EpisodeToolsSource | undefined): Promise<EpisodeTools>;
87
+ /** The `mcp-remote` bridge, pinned, for MCP clients that only start local (stdio) servers. */
88
+ export declare const MCP_REMOTE_PACKAGE = "mcp-remote@0.14.3";
89
+ export interface McpConfigOptions {
90
+ /** The server's name in the config. Default `bonded-episode`. */
91
+ name?: string;
92
+ /**
93
+ * - `http` (default): `{ type: "http", url }`, for clients that speak streamable HTTP
94
+ * (Claude Code's `.mcp.json`, Cursor's `mcp.json`, and most current agents).
95
+ * - `url`: `{ url }` only, for clients that infer the transport from the URL (older Cursor).
96
+ * - `stdio`: `npx mcp-remote <url>`, for clients that only launch local servers (Claude Desktop).
97
+ */
98
+ style?: "http" | "url" | "stdio";
99
+ }
100
+ export type McpServerEntry = {
101
+ type: "http";
102
+ url: string;
103
+ } | {
104
+ url: string;
105
+ } | {
106
+ command: string;
107
+ args: string[];
108
+ };
109
+ /**
110
+ * The `mcpServers` block an MCP-aware agent accepts, pointing at this episode's MCP endpoint.
111
+ * The URL carries the episode's capability and stops working when the episode ends, so write the
112
+ * config afresh for every episode (e.g. at the start of the runner's agent command):
113
+ *
114
+ * ```sh
115
+ * bonded-runner --command 'bonded-mcp-config > .mcp.json && my-agent --mcp-config .mcp.json'
116
+ * ```
117
+ */
118
+ export declare function mcpConfig(episode?: {
119
+ endpoints: {
120
+ mcp: string;
121
+ };
122
+ }, options?: McpConfigOptions): {
123
+ mcpServers: Record<string, McpServerEntry>;
124
+ };
125
+ /** `mcpConfig` as pretty-printed JSON, ready to write to a file. */
126
+ export declare function mcpConfigJson(episode?: {
127
+ endpoints: {
128
+ mcp: string;
129
+ };
130
+ }, options?: McpConfigOptions): string;
131
+ //# sourceMappingURL=mcp.d.ts.map