@bondedhq/shared 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +17 -2
- package/dist/abis.d.ts +8639 -0
- package/dist/abis.js +11234 -0
- package/dist/actuarial.d.ts +158 -0
- package/dist/actuarial.js +210 -0
- package/dist/agent-url.d.ts +172 -0
- package/dist/agent-url.js +248 -0
- package/dist/agent.d.ts +184 -0
- package/dist/agent.js +133 -0
- package/dist/allowances.d.ts +54 -0
- package/dist/allowances.js +68 -0
- package/dist/bounty-example.d.ts +6 -0
- package/dist/bounty-example.js +23 -0
- package/dist/bounty-spec.d.ts +36 -0
- package/dist/bounty-spec.js +111 -0
- package/dist/canonical.d.ts +8 -0
- package/dist/canonical.js +44 -0
- package/dist/chains.d.ts +58 -0
- package/dist/chains.js +123 -0
- package/dist/deployments.d.ts +32 -0
- package/dist/deployments.js +41 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +33 -0
- package/dist/leaderboard.d.ts +105 -0
- package/dist/leaderboard.js +85 -0
- package/dist/llm.d.ts +121 -0
- package/dist/llm.js +105 -0
- package/dist/mandate-rules.d.ts +54 -0
- package/dist/mandate-rules.js +69 -0
- package/dist/module-install.d.ts +145 -0
- package/dist/module-install.js +133 -0
- package/dist/notifications.d.ts +48 -0
- package/dist/notifications.js +45 -0
- package/dist/observed-rates.d.ts +125 -0
- package/dist/observed-rates.js +158 -0
- package/dist/problems.d.ts +44 -0
- package/dist/problems.js +148 -0
- package/dist/quote.d.ts +123 -0
- package/dist/quote.js +167 -0
- package/dist/report-fixes.d.ts +89 -0
- package/dist/report-fixes.js +159 -0
- package/dist/runner.d.ts +376 -0
- package/dist/runner.js +353 -0
- package/dist/schemas/attack.d.ts +121 -0
- package/dist/schemas/attack.js +142 -0
- package/dist/schemas/attestation.d.ts +284 -0
- package/dist/schemas/attestation.js +175 -0
- package/dist/schemas/common.d.ts +22 -0
- package/dist/schemas/common.js +53 -0
- package/dist/schemas/mandate-commitment.d.ts +13 -0
- package/dist/schemas/mandate-commitment.js +37 -0
- package/dist/schemas/mandate.d.ts +170 -0
- package/dist/schemas/mandate.js +113 -0
- package/dist/self-serve.d.ts +133 -0
- package/dist/self-serve.js +110 -0
- package/dist/sentinel-cascade.d.ts +64 -0
- package/dist/sentinel-cascade.js +64 -0
- package/dist/sentinel.d.ts +133 -0
- package/dist/sentinel.js +101 -0
- package/dist/suggested-mandate.d.ts +81 -0
- package/dist/suggested-mandate.js +112 -0
- package/dist/tee.d.ts +61 -0
- package/dist/tee.js +93 -0
- package/dist/tiers.d.ts +19 -0
- package/dist/tiers.js +23 -0
- package/dist/troubleshooting-doc.d.ts +11 -0
- package/dist/troubleshooting-doc.js +46 -0
- package/package.json +59 -3
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* A mandate is the machine-readable set of rules an agent must follow.
|
|
4
|
+
* The full JSON lives off-chain; its hash and enforcement parameters live on-chain
|
|
5
|
+
* in the agent's Mandate Vault. See docs/SPECIFICATION.md Section 6.1.
|
|
6
|
+
*/
|
|
7
|
+
/** monitor: the agent keeps full autonomy, breaches are recorded and the agent is frozen.
|
|
8
|
+
* enforce: breaches are blocked (the call reverts). */
|
|
9
|
+
export declare const MandateMode: z.ZodEnum<{
|
|
10
|
+
monitor: "monitor";
|
|
11
|
+
enforce: "enforce";
|
|
12
|
+
}>;
|
|
13
|
+
export type MandateMode = z.infer<typeof MandateMode>;
|
|
14
|
+
/** Numeric codes used by the Solidity MandateVault. Order must match `BondedTypes.MandateMode`. */
|
|
15
|
+
export declare const MANDATE_MODE_CODE: Record<MandateMode, number>;
|
|
16
|
+
export declare const MandateLimits: z.ZodObject<{
|
|
17
|
+
maxTxNotionalUsd: z.ZodNumber;
|
|
18
|
+
maxDailyNotionalUsd: z.ZodNumber;
|
|
19
|
+
maxDrawdownBps: z.ZodNumber;
|
|
20
|
+
maxSlippageBps: z.ZodNumber;
|
|
21
|
+
}, z.core.$strip>;
|
|
22
|
+
export type MandateLimits = z.infer<typeof MandateLimits>;
|
|
23
|
+
export declare const EvmTarget: z.ZodObject<{
|
|
24
|
+
address: z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>;
|
|
25
|
+
selectors: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>>;
|
|
26
|
+
}, z.core.$strip>;
|
|
27
|
+
export type EvmTarget = z.infer<typeof EvmTarget>;
|
|
28
|
+
export declare const EvmMandate: z.ZodObject<{
|
|
29
|
+
chain: z.ZodObject<{
|
|
30
|
+
family: z.ZodLiteral<"evm">;
|
|
31
|
+
chainId: z.ZodNumber;
|
|
32
|
+
}, z.core.$strip>;
|
|
33
|
+
baseAsset: z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>;
|
|
34
|
+
assets: z.ZodArray<z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>>;
|
|
35
|
+
targets: z.ZodArray<z.ZodObject<{
|
|
36
|
+
address: z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>;
|
|
37
|
+
selectors: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>>;
|
|
38
|
+
}, z.core.$strip>>;
|
|
39
|
+
destinations: z.ZodArray<z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>>;
|
|
40
|
+
version: z.ZodLiteral<1>;
|
|
41
|
+
agentId: z.ZodString;
|
|
42
|
+
mode: z.ZodEnum<{
|
|
43
|
+
monitor: "monitor";
|
|
44
|
+
enforce: "enforce";
|
|
45
|
+
}>;
|
|
46
|
+
limits: z.ZodObject<{
|
|
47
|
+
maxTxNotionalUsd: z.ZodNumber;
|
|
48
|
+
maxDailyNotionalUsd: z.ZodNumber;
|
|
49
|
+
maxDrawdownBps: z.ZodNumber;
|
|
50
|
+
maxSlippageBps: z.ZodNumber;
|
|
51
|
+
}, z.core.$strip>;
|
|
52
|
+
freezeOnBreach: z.ZodBoolean;
|
|
53
|
+
description: z.ZodString;
|
|
54
|
+
}, z.core.$strip>;
|
|
55
|
+
export type EvmMandate = z.infer<typeof EvmMandate>;
|
|
56
|
+
/** Unvalidated mandate as written by a user (addresses in any case). */
|
|
57
|
+
export type EvmMandateInput = z.input<typeof EvmMandate>;
|
|
58
|
+
export declare const SolanaMandate: z.ZodObject<{
|
|
59
|
+
chain: z.ZodObject<{
|
|
60
|
+
family: z.ZodLiteral<"solana">;
|
|
61
|
+
cluster: z.ZodEnum<{
|
|
62
|
+
localnet: "localnet";
|
|
63
|
+
devnet: "devnet";
|
|
64
|
+
testnet: "testnet";
|
|
65
|
+
"mainnet-beta": "mainnet-beta";
|
|
66
|
+
}>;
|
|
67
|
+
}, z.core.$strip>;
|
|
68
|
+
baseAsset: z.ZodString;
|
|
69
|
+
assets: z.ZodArray<z.ZodString>;
|
|
70
|
+
programs: z.ZodArray<z.ZodString>;
|
|
71
|
+
destinations: z.ZodArray<z.ZodString>;
|
|
72
|
+
version: z.ZodLiteral<1>;
|
|
73
|
+
agentId: z.ZodString;
|
|
74
|
+
mode: z.ZodEnum<{
|
|
75
|
+
monitor: "monitor";
|
|
76
|
+
enforce: "enforce";
|
|
77
|
+
}>;
|
|
78
|
+
limits: z.ZodObject<{
|
|
79
|
+
maxTxNotionalUsd: z.ZodNumber;
|
|
80
|
+
maxDailyNotionalUsd: z.ZodNumber;
|
|
81
|
+
maxDrawdownBps: z.ZodNumber;
|
|
82
|
+
maxSlippageBps: z.ZodNumber;
|
|
83
|
+
}, z.core.$strip>;
|
|
84
|
+
freezeOnBreach: z.ZodBoolean;
|
|
85
|
+
description: z.ZodString;
|
|
86
|
+
}, z.core.$strip>;
|
|
87
|
+
export type SolanaMandate = z.infer<typeof SolanaMandate>;
|
|
88
|
+
export type SolanaMandateInput = z.input<typeof SolanaMandate>;
|
|
89
|
+
export declare const Mandate: z.ZodUnion<readonly [z.ZodObject<{
|
|
90
|
+
chain: z.ZodObject<{
|
|
91
|
+
family: z.ZodLiteral<"evm">;
|
|
92
|
+
chainId: z.ZodNumber;
|
|
93
|
+
}, z.core.$strip>;
|
|
94
|
+
baseAsset: z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>;
|
|
95
|
+
assets: z.ZodArray<z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>>;
|
|
96
|
+
targets: z.ZodArray<z.ZodObject<{
|
|
97
|
+
address: z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>;
|
|
98
|
+
selectors: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>>;
|
|
99
|
+
}, z.core.$strip>>;
|
|
100
|
+
destinations: z.ZodArray<z.ZodPipe<z.ZodString & z.ZodType<`0x${string}`, string, z.core.$ZodTypeInternals<`0x${string}`, string>>, z.ZodTransform<`0x${string}`, `0x${string}`>>>;
|
|
101
|
+
version: z.ZodLiteral<1>;
|
|
102
|
+
agentId: z.ZodString;
|
|
103
|
+
mode: z.ZodEnum<{
|
|
104
|
+
monitor: "monitor";
|
|
105
|
+
enforce: "enforce";
|
|
106
|
+
}>;
|
|
107
|
+
limits: z.ZodObject<{
|
|
108
|
+
maxTxNotionalUsd: z.ZodNumber;
|
|
109
|
+
maxDailyNotionalUsd: z.ZodNumber;
|
|
110
|
+
maxDrawdownBps: z.ZodNumber;
|
|
111
|
+
maxSlippageBps: z.ZodNumber;
|
|
112
|
+
}, z.core.$strip>;
|
|
113
|
+
freezeOnBreach: z.ZodBoolean;
|
|
114
|
+
description: z.ZodString;
|
|
115
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
116
|
+
chain: z.ZodObject<{
|
|
117
|
+
family: z.ZodLiteral<"solana">;
|
|
118
|
+
cluster: z.ZodEnum<{
|
|
119
|
+
localnet: "localnet";
|
|
120
|
+
devnet: "devnet";
|
|
121
|
+
testnet: "testnet";
|
|
122
|
+
"mainnet-beta": "mainnet-beta";
|
|
123
|
+
}>;
|
|
124
|
+
}, z.core.$strip>;
|
|
125
|
+
baseAsset: z.ZodString;
|
|
126
|
+
assets: z.ZodArray<z.ZodString>;
|
|
127
|
+
programs: z.ZodArray<z.ZodString>;
|
|
128
|
+
destinations: z.ZodArray<z.ZodString>;
|
|
129
|
+
version: z.ZodLiteral<1>;
|
|
130
|
+
agentId: z.ZodString;
|
|
131
|
+
mode: z.ZodEnum<{
|
|
132
|
+
monitor: "monitor";
|
|
133
|
+
enforce: "enforce";
|
|
134
|
+
}>;
|
|
135
|
+
limits: z.ZodObject<{
|
|
136
|
+
maxTxNotionalUsd: z.ZodNumber;
|
|
137
|
+
maxDailyNotionalUsd: z.ZodNumber;
|
|
138
|
+
maxDrawdownBps: z.ZodNumber;
|
|
139
|
+
maxSlippageBps: z.ZodNumber;
|
|
140
|
+
}, z.core.$strip>;
|
|
141
|
+
freezeOnBreach: z.ZodBoolean;
|
|
142
|
+
description: z.ZodString;
|
|
143
|
+
}, z.core.$strip>]>;
|
|
144
|
+
export type Mandate = z.infer<typeof Mandate>;
|
|
145
|
+
export type MandateInput = z.input<typeof Mandate>;
|
|
146
|
+
/**
|
|
147
|
+
* keccak256 of the canonical JSON of a *parsed* mandate. Parsing first normalises
|
|
148
|
+
* addresses and selectors, so equivalent mandates always hash the same.
|
|
149
|
+
*/
|
|
150
|
+
export declare function mandateHash(mandate: MandateInput): `0x${string}`;
|
|
151
|
+
/** USD amounts on-chain use 6 decimals, matching USDC. */
|
|
152
|
+
export declare const USD_DECIMALS = 6;
|
|
153
|
+
/** Parameters passed to MandateVaultFactory.createVault (Solidity struct `MandateParams`). */
|
|
154
|
+
export interface EvmMandateParams {
|
|
155
|
+
mandateHash: `0x${string}`;
|
|
156
|
+
mode: number;
|
|
157
|
+
baseAsset: `0x${string}`;
|
|
158
|
+
assets: `0x${string}`[];
|
|
159
|
+
targets: `0x${string}`[];
|
|
160
|
+
/** selectors[i] lists the allowed selectors for targets[i]; empty means any function. */
|
|
161
|
+
selectors: `0x${string}`[][];
|
|
162
|
+
destinations: `0x${string}`[];
|
|
163
|
+
maxTxNotionalUsd6: bigint;
|
|
164
|
+
maxDailyNotionalUsd6: bigint;
|
|
165
|
+
maxDrawdownBps: number;
|
|
166
|
+
maxSlippageBps: number;
|
|
167
|
+
freezeOnBreach: boolean;
|
|
168
|
+
}
|
|
169
|
+
export declare function toEvmMandateParams(input: EvmMandateInput): EvmMandateParams;
|
|
170
|
+
//# sourceMappingURL=mandate.d.ts.map
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { canonicalHash } from "../canonical.js";
|
|
3
|
+
import { evmAddress, isUnique, selector, solanaAddress, uintString } from "./common.js";
|
|
4
|
+
/**
|
|
5
|
+
* A mandate is the machine-readable set of rules an agent must follow.
|
|
6
|
+
* The full JSON lives off-chain; its hash and enforcement parameters live on-chain
|
|
7
|
+
* in the agent's Mandate Vault. See docs/SPECIFICATION.md Section 6.1.
|
|
8
|
+
*/
|
|
9
|
+
/** monitor: the agent keeps full autonomy, breaches are recorded and the agent is frozen.
|
|
10
|
+
* enforce: breaches are blocked (the call reverts). */
|
|
11
|
+
export const MandateMode = z.enum(["monitor", "enforce"]);
|
|
12
|
+
/** Numeric codes used by the Solidity MandateVault. Order must match `BondedTypes.MandateMode`. */
|
|
13
|
+
export const MANDATE_MODE_CODE = { monitor: 0, enforce: 1 };
|
|
14
|
+
export const MandateLimits = z
|
|
15
|
+
.object({
|
|
16
|
+
/** Largest notional value of a single action, in whole US dollars. */
|
|
17
|
+
maxTxNotionalUsd: z.number().int().positive(),
|
|
18
|
+
/** Largest total notional value over a rolling 24h window, in whole US dollars. */
|
|
19
|
+
maxDailyNotionalUsd: z.number().int().positive(),
|
|
20
|
+
/** Largest allowed fall in vault value over 24h, in basis points (1500 = 15%). */
|
|
21
|
+
maxDrawdownBps: z.number().int().min(1).max(10_000),
|
|
22
|
+
/** Largest net loss one action may cause, as a share of its outflow, in basis points
|
|
23
|
+
* (300 = 3%). A bigger loss on an allowed call is a T3 breach (e.g. a honeypot swap). */
|
|
24
|
+
maxSlippageBps: z.number().int().min(0).max(10_000),
|
|
25
|
+
})
|
|
26
|
+
.refine((limits) => limits.maxDailyNotionalUsd >= limits.maxTxNotionalUsd, {
|
|
27
|
+
message: "maxDailyNotionalUsd must be at least maxTxNotionalUsd",
|
|
28
|
+
path: ["maxDailyNotionalUsd"],
|
|
29
|
+
});
|
|
30
|
+
const common = {
|
|
31
|
+
version: z.literal(1),
|
|
32
|
+
/** ERC-8004 agent id in the chain's Bonded registry, as a decimal string. */
|
|
33
|
+
agentId: uintString,
|
|
34
|
+
mode: MandateMode,
|
|
35
|
+
limits: MandateLimits,
|
|
36
|
+
/** Freeze the agent's session key after the first breach (recommended). */
|
|
37
|
+
freezeOnBreach: z.boolean(),
|
|
38
|
+
/** Plain-language summary of what the agent is meant to do. */
|
|
39
|
+
description: z.string().min(1).max(1_000),
|
|
40
|
+
};
|
|
41
|
+
export const EvmTarget = z.object({
|
|
42
|
+
address: evmAddress,
|
|
43
|
+
/** Allowed function selectors. Omitted means any function on this contract. */
|
|
44
|
+
selectors: z.array(selector).min(1).refine(isUnique, "duplicate selectors").optional(),
|
|
45
|
+
});
|
|
46
|
+
export const EvmMandate = z
|
|
47
|
+
.object({
|
|
48
|
+
...common,
|
|
49
|
+
chain: z.object({ family: z.literal("evm"), chainId: z.number().int().positive() }),
|
|
50
|
+
/** Asset the vault is denominated in (deposits, NAV, premiums, payouts). */
|
|
51
|
+
baseAsset: evmAddress,
|
|
52
|
+
/** Tokens the vault may hold. Must include baseAsset. */
|
|
53
|
+
assets: z.array(evmAddress).min(1).refine(isUnique, "duplicate assets"),
|
|
54
|
+
/** Contracts (and optionally functions) the agent may call. */
|
|
55
|
+
targets: z
|
|
56
|
+
.array(EvmTarget)
|
|
57
|
+
.refine((targets) => isUnique(targets.map((t) => t.address)), "duplicate targets"),
|
|
58
|
+
/** Allowed recipients of outbound transfers. The vault itself is always allowed. */
|
|
59
|
+
destinations: z.array(evmAddress).refine(isUnique, "duplicate destinations"),
|
|
60
|
+
})
|
|
61
|
+
.refine((mandate) => mandate.assets.some((asset) => asset === mandate.baseAsset), {
|
|
62
|
+
message: "assets must include baseAsset",
|
|
63
|
+
path: ["assets"],
|
|
64
|
+
});
|
|
65
|
+
export const SolanaMandate = z
|
|
66
|
+
.object({
|
|
67
|
+
...common,
|
|
68
|
+
chain: z.object({
|
|
69
|
+
family: z.literal("solana"),
|
|
70
|
+
cluster: z.enum(["localnet", "devnet", "testnet", "mainnet-beta"]),
|
|
71
|
+
}),
|
|
72
|
+
/** SPL mint the vault is denominated in. */
|
|
73
|
+
baseAsset: solanaAddress,
|
|
74
|
+
/** SPL mints the vault may hold. Must include baseAsset. */
|
|
75
|
+
assets: z.array(solanaAddress).min(1).refine(isUnique, "duplicate assets"),
|
|
76
|
+
/** Programs the agent may invoke through the vault. */
|
|
77
|
+
programs: z.array(solanaAddress).refine(isUnique, "duplicate programs"),
|
|
78
|
+
/** Allowed recipients (token account owners) of outbound transfers. The vault is always allowed. */
|
|
79
|
+
destinations: z.array(solanaAddress).refine(isUnique, "duplicate destinations"),
|
|
80
|
+
})
|
|
81
|
+
.refine((mandate) => mandate.assets.includes(mandate.baseAsset), {
|
|
82
|
+
message: "assets must include baseAsset",
|
|
83
|
+
path: ["assets"],
|
|
84
|
+
});
|
|
85
|
+
export const Mandate = z.union([EvmMandate, SolanaMandate]);
|
|
86
|
+
/**
|
|
87
|
+
* keccak256 of the canonical JSON of a *parsed* mandate. Parsing first normalises
|
|
88
|
+
* addresses and selectors, so equivalent mandates always hash the same.
|
|
89
|
+
*/
|
|
90
|
+
export function mandateHash(mandate) {
|
|
91
|
+
return canonicalHash(Mandate.parse(mandate));
|
|
92
|
+
}
|
|
93
|
+
/** USD amounts on-chain use 6 decimals, matching USDC. */
|
|
94
|
+
export const USD_DECIMALS = 6;
|
|
95
|
+
const USD_UNIT = 10n ** BigInt(USD_DECIMALS);
|
|
96
|
+
export function toEvmMandateParams(input) {
|
|
97
|
+
const mandate = EvmMandate.parse(input);
|
|
98
|
+
return {
|
|
99
|
+
mandateHash: mandateHash(mandate),
|
|
100
|
+
mode: MANDATE_MODE_CODE[mandate.mode],
|
|
101
|
+
baseAsset: mandate.baseAsset,
|
|
102
|
+
assets: mandate.assets,
|
|
103
|
+
targets: mandate.targets.map((target) => target.address),
|
|
104
|
+
selectors: mandate.targets.map((target) => target.selectors ?? []),
|
|
105
|
+
destinations: mandate.destinations,
|
|
106
|
+
maxTxNotionalUsd6: BigInt(mandate.limits.maxTxNotionalUsd) * USD_UNIT,
|
|
107
|
+
maxDailyNotionalUsd6: BigInt(mandate.limits.maxDailyNotionalUsd) * USD_UNIT,
|
|
108
|
+
maxDrawdownBps: mandate.limits.maxDrawdownBps,
|
|
109
|
+
maxSlippageBps: mandate.limits.maxSlippageBps,
|
|
110
|
+
freezeOnBreach: mandate.freezeOnBreach,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=mandate.js.map
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { AgentMode } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* Self-serve requests (docs/SPECIFICATION.md, Day 14 workstream 7): the messages an agent's owner signs
|
|
4
|
+
* in their wallet (EIP-191 personal_sign) so the API can act for them without an operator token.
|
|
5
|
+
* The web app builds them and the API rebuilds them byte for byte, so both use this module.
|
|
6
|
+
*/
|
|
7
|
+
/** Bonded's built-in agent designs a self-serve rating can use (docs/AGENTS.md). */
|
|
8
|
+
export declare const SELF_SERVE_KINDS: readonly ["vulnerable", "partial", "hardened"];
|
|
9
|
+
export type SelfServeKind = (typeof SELF_SERVE_KINDS)[number];
|
|
10
|
+
/** What each design is, in a line, for the onboarding page. */
|
|
11
|
+
export declare const SELF_SERVE_KIND_LABELS: Record<SelfServeKind, string>;
|
|
12
|
+
/**
|
|
13
|
+
* The operator's own agent (bring your own agent, docs/BRING-YOUR-OWN-AGENT.md): any code, run
|
|
14
|
+
* wherever the operator runs it, rated through the Arena Gateway by `@bondedhq/runner`.
|
|
15
|
+
*/
|
|
16
|
+
export declare const REMOTE_KIND = "remote";
|
|
17
|
+
/** A built-in design, or the operator's own agent. */
|
|
18
|
+
export type SelfServeAgentKind = SelfServeKind | typeof REMOTE_KIND;
|
|
19
|
+
/**
|
|
20
|
+
* How the operator's agent acts during a rating: the runner protocol's `AgentMode` (tools: reads
|
|
21
|
+
* and writes are tool calls over MCP or HTTP; chain: reads are tool calls, writes are transactions
|
|
22
|
+
* it signs and sends to its vault).
|
|
23
|
+
*/
|
|
24
|
+
export declare const REMOTE_MODES: ("tools" | "chain")[];
|
|
25
|
+
export type RemoteMode = AgentMode;
|
|
26
|
+
/**
|
|
27
|
+
* How Bonded reaches the operator's agent for each episode: through `@bondedhq/runner`, which the
|
|
28
|
+
* operator runs next to the agent (the default), or by calling the agent's own HTTPS endpoint
|
|
29
|
+
* (Agent URL mode: no install).
|
|
30
|
+
*/
|
|
31
|
+
export declare const REMOTE_CONNECTIONS: readonly ["runner", "url"];
|
|
32
|
+
export type RemoteConnection = (typeof REMOTE_CONNECTIONS)[number];
|
|
33
|
+
/** What the operator declares about their agent when registering it. */
|
|
34
|
+
export interface RemoteAgentSpec {
|
|
35
|
+
/**
|
|
36
|
+
* The operator's own hash of the code that runs (an image digest or a git commit, as 32 bytes).
|
|
37
|
+
* Optional: without it, the hash the runner computes (or the version the Agent URL reports) is
|
|
38
|
+
* used and recorded on each rating, with where it came from.
|
|
39
|
+
*/
|
|
40
|
+
codeHash?: string;
|
|
41
|
+
mode: RemoteMode;
|
|
42
|
+
/** How Bonded reaches the agent (default "runner"). */
|
|
43
|
+
connection?: RemoteConnection;
|
|
44
|
+
/** Where the agent runs in a TEE, and the compose hash published for that deployment. */
|
|
45
|
+
tee?: {
|
|
46
|
+
url: string;
|
|
47
|
+
composeHash: string;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* What protects the agent's funds: a Bonded Mandate Vault (the default), or a MandateModule
|
|
52
|
+
* installed on the smart account the agent already uses (ERC-7579, e.g. Safe through Safe7579).
|
|
53
|
+
* For a module, `vault` in a request is the module's address, and the mandate hash the rating
|
|
54
|
+
* attests is `mandateCommitment`, the hash the module enforces.
|
|
55
|
+
*/
|
|
56
|
+
export declare const PROTECTIONS: readonly ["vault", "module"];
|
|
57
|
+
export type Protection = (typeof PROTECTIONS)[number];
|
|
58
|
+
/** A signed request is accepted for this long after it was signed, in seconds. */
|
|
59
|
+
export declare const SELF_SERVE_MAX_AGE_SECONDS: number;
|
|
60
|
+
/** Largest cover a quote without the operator token can ask for, in tUSDG (testnet cap). */
|
|
61
|
+
export declare const SELF_SERVE_MAX_COVER_USD = 20000;
|
|
62
|
+
export type SelfServeRequest = {
|
|
63
|
+
action: "register";
|
|
64
|
+
chainId: number;
|
|
65
|
+
agentId: string;
|
|
66
|
+
vault: string;
|
|
67
|
+
kind: SelfServeKind;
|
|
68
|
+
mandateHash: string;
|
|
69
|
+
/** Rate the agent with Sentinel in front of it (docs/SENTINEL.md). */
|
|
70
|
+
sentinel?: boolean;
|
|
71
|
+
/** "module" when `vault` is a MandateModule on a smart account. */
|
|
72
|
+
protection?: Protection;
|
|
73
|
+
timestamp: number;
|
|
74
|
+
} | {
|
|
75
|
+
action: "register";
|
|
76
|
+
chainId: number;
|
|
77
|
+
agentId: string;
|
|
78
|
+
vault: string;
|
|
79
|
+
kind: typeof REMOTE_KIND;
|
|
80
|
+
mandateHash: string;
|
|
81
|
+
remote: RemoteAgentSpec;
|
|
82
|
+
/** "module" when `vault` is a MandateModule on a smart account. */
|
|
83
|
+
protection?: Protection;
|
|
84
|
+
timestamp: number;
|
|
85
|
+
} | {
|
|
86
|
+
action: "rate";
|
|
87
|
+
chainId: number;
|
|
88
|
+
agentId: string;
|
|
89
|
+
timestamp: number;
|
|
90
|
+
}
|
|
91
|
+
/** Create a runner token for the agent (it replaces the agent's current one). */
|
|
92
|
+
| {
|
|
93
|
+
action: "runner-token";
|
|
94
|
+
chainId: number;
|
|
95
|
+
agentId: string;
|
|
96
|
+
timestamp: number;
|
|
97
|
+
}
|
|
98
|
+
/** Revoke the agent's runner token: a connected runner is disconnected. */
|
|
99
|
+
| {
|
|
100
|
+
action: "revoke-runner-token";
|
|
101
|
+
chainId: number;
|
|
102
|
+
agentId: string;
|
|
103
|
+
timestamp: number;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Sign in with a wallet, for a short session (previews, connection checks, Agent URLs and
|
|
107
|
+
* notification settings). Not tied to an agent: a preview needs no on-chain registration.
|
|
108
|
+
*/
|
|
109
|
+
| {
|
|
110
|
+
action: "sign-in";
|
|
111
|
+
chainId: number;
|
|
112
|
+
wallet: string;
|
|
113
|
+
timestamp: number;
|
|
114
|
+
};
|
|
115
|
+
/** The exact text the owner signs. Addresses and hashes are lowercased so case can't differ. */
|
|
116
|
+
export declare function selfServeMessage(request: SelfServeRequest): string;
|
|
117
|
+
/**
|
|
118
|
+
* Bring your own agent, round 2: what a wallet signs for the preview, Agent URL, check and
|
|
119
|
+
* notification calls, in the same format as `selfServeMessage` (a title, the chain, the agent or
|
|
120
|
+
* the wallet, any fields as "<name> <value>" in order, then the time). A preview's calls are made
|
|
121
|
+
* with the session that "start preview" opens; a registered agent's are each signed by its owner.
|
|
122
|
+
*/
|
|
123
|
+
export type ByoaAction = "sign-in" | "runner-token" | "agent-url" | "check" | "notifications";
|
|
124
|
+
/** The exact text signed for a round-2 call. URLs and other fields are signed exactly as sent. */
|
|
125
|
+
export declare function byoaMessage(input: {
|
|
126
|
+
action: ByoaAction;
|
|
127
|
+
chainId: number;
|
|
128
|
+
agentId?: string;
|
|
129
|
+
wallet?: string;
|
|
130
|
+
fields?: Record<string, string>;
|
|
131
|
+
timestamp: number;
|
|
132
|
+
}): string;
|
|
133
|
+
//# sourceMappingURL=self-serve.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { AgentMode } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* Self-serve requests (docs/SPECIFICATION.md, Day 14 workstream 7): the messages an agent's owner signs
|
|
4
|
+
* in their wallet (EIP-191 personal_sign) so the API can act for them without an operator token.
|
|
5
|
+
* The web app builds them and the API rebuilds them byte for byte, so both use this module.
|
|
6
|
+
*/
|
|
7
|
+
/** Bonded's built-in agent designs a self-serve rating can use (docs/AGENTS.md). */
|
|
8
|
+
export const SELF_SERVE_KINDS = ["vulnerable", "partial", "hardened"];
|
|
9
|
+
/** What each design is, in a line, for the onboarding page. */
|
|
10
|
+
export const SELF_SERVE_KIND_LABELS = {
|
|
11
|
+
vulnerable: "Vulnerable: acts on instructions it reads; no guard",
|
|
12
|
+
partial: "Partial (Agent A): the full guard except it checks addresses by their ends only",
|
|
13
|
+
hardened: "Hardened: treats what it reads as data; full guard",
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The operator's own agent (bring your own agent, docs/BRING-YOUR-OWN-AGENT.md): any code, run
|
|
17
|
+
* wherever the operator runs it, rated through the Arena Gateway by `@bondedhq/runner`.
|
|
18
|
+
*/
|
|
19
|
+
export const REMOTE_KIND = "remote";
|
|
20
|
+
/**
|
|
21
|
+
* How the operator's agent acts during a rating: the runner protocol's `AgentMode` (tools: reads
|
|
22
|
+
* and writes are tool calls over MCP or HTTP; chain: reads are tool calls, writes are transactions
|
|
23
|
+
* it signs and sends to its vault).
|
|
24
|
+
*/
|
|
25
|
+
export const REMOTE_MODES = AgentMode.options;
|
|
26
|
+
/**
|
|
27
|
+
* How Bonded reaches the operator's agent for each episode: through `@bondedhq/runner`, which the
|
|
28
|
+
* operator runs next to the agent (the default), or by calling the agent's own HTTPS endpoint
|
|
29
|
+
* (Agent URL mode: no install).
|
|
30
|
+
*/
|
|
31
|
+
export const REMOTE_CONNECTIONS = ["runner", "url"];
|
|
32
|
+
/**
|
|
33
|
+
* What protects the agent's funds: a Bonded Mandate Vault (the default), or a MandateModule
|
|
34
|
+
* installed on the smart account the agent already uses (ERC-7579, e.g. Safe through Safe7579).
|
|
35
|
+
* For a module, `vault` in a request is the module's address, and the mandate hash the rating
|
|
36
|
+
* attests is `mandateCommitment`, the hash the module enforces.
|
|
37
|
+
*/
|
|
38
|
+
export const PROTECTIONS = ["vault", "module"];
|
|
39
|
+
/** A signed request is accepted for this long after it was signed, in seconds. */
|
|
40
|
+
export const SELF_SERVE_MAX_AGE_SECONDS = 10 * 60;
|
|
41
|
+
/** Largest cover a quote without the operator token can ask for, in tUSDG (testnet cap). */
|
|
42
|
+
export const SELF_SERVE_MAX_COVER_USD = 20_000;
|
|
43
|
+
const TITLES = {
|
|
44
|
+
register: "register agent for ratings",
|
|
45
|
+
rate: "rate agent",
|
|
46
|
+
"runner-token": "create runner token",
|
|
47
|
+
"revoke-runner-token": "revoke runner token",
|
|
48
|
+
"sign-in": "start preview",
|
|
49
|
+
};
|
|
50
|
+
/** The exact text the owner signs. Addresses and hashes are lowercased so case can't differ. */
|
|
51
|
+
export function selfServeMessage(request) {
|
|
52
|
+
if (request.action === "sign-in")
|
|
53
|
+
return byoaMessage({
|
|
54
|
+
action: "sign-in",
|
|
55
|
+
chainId: request.chainId,
|
|
56
|
+
wallet: request.wallet,
|
|
57
|
+
timestamp: request.timestamp,
|
|
58
|
+
});
|
|
59
|
+
const lines = [
|
|
60
|
+
`Bonded: ${TITLES[request.action]}`,
|
|
61
|
+
`chain ${request.chainId}`,
|
|
62
|
+
`agent ${request.agentId}`,
|
|
63
|
+
];
|
|
64
|
+
if (request.action === "register") {
|
|
65
|
+
lines.push(`vault ${request.vault.toLowerCase()}`, `design ${request.kind}`, `mandate ${request.mandateHash.toLowerCase()}`);
|
|
66
|
+
// Only for a module, so a vault registration reads as it always has.
|
|
67
|
+
if (request.protection === "module")
|
|
68
|
+
lines.push("protection module");
|
|
69
|
+
if (request.kind === REMOTE_KIND) {
|
|
70
|
+
const { codeHash, mode, connection, tee } = request.remote;
|
|
71
|
+
// Only when declared, so a registration signed with a code hash reads as it always has;
|
|
72
|
+
// without one, the runner's or the Agent URL's reported hash is used and recorded.
|
|
73
|
+
if (codeHash)
|
|
74
|
+
lines.push(`code ${codeHash.toLowerCase()}`);
|
|
75
|
+
lines.push(`mode ${mode}`);
|
|
76
|
+
// Only for Agent URL mode, so a runner registration reads as it always has.
|
|
77
|
+
if (connection === "url")
|
|
78
|
+
lines.push("connection url");
|
|
79
|
+
// The TEE's URL is signed exactly as sent; only its compose hash is lowercased.
|
|
80
|
+
if (tee)
|
|
81
|
+
lines.push(`tee ${tee.url}`, `compose ${tee.composeHash.toLowerCase()}`);
|
|
82
|
+
}
|
|
83
|
+
// Only when chosen, so a registration signed before this option reads the same.
|
|
84
|
+
else if (request.sentinel)
|
|
85
|
+
lines.push("sentinel on");
|
|
86
|
+
}
|
|
87
|
+
lines.push(`time ${request.timestamp}`);
|
|
88
|
+
return lines.join("\n");
|
|
89
|
+
}
|
|
90
|
+
const BYOA_TITLES = {
|
|
91
|
+
"sign-in": "start preview",
|
|
92
|
+
// The existing action: the same text `selfServeMessage` makes for it.
|
|
93
|
+
"runner-token": "create runner token",
|
|
94
|
+
"agent-url": "register agent url",
|
|
95
|
+
check: "start connection check",
|
|
96
|
+
notifications: "set notifications",
|
|
97
|
+
};
|
|
98
|
+
/** The exact text signed for a round-2 call. URLs and other fields are signed exactly as sent. */
|
|
99
|
+
export function byoaMessage(input) {
|
|
100
|
+
const lines = [`Bonded: ${BYOA_TITLES[input.action]}`, `chain ${input.chainId}`];
|
|
101
|
+
if (input.agentId !== undefined)
|
|
102
|
+
lines.push(`agent ${input.agentId}`);
|
|
103
|
+
if (input.wallet !== undefined)
|
|
104
|
+
lines.push(`wallet ${input.wallet.toLowerCase()}`);
|
|
105
|
+
for (const [name, value] of Object.entries(input.fields ?? {}))
|
|
106
|
+
lines.push(`${name} ${value}`);
|
|
107
|
+
lines.push(`time ${input.timestamp}`);
|
|
108
|
+
return lines.join("\n");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=self-serve.js.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Frozen before the final evaluation; never contains a provider credential or attack text. */
|
|
3
|
+
export declare const sentinelCascadePolicySchema: z.ZodObject<{
|
|
4
|
+
version: z.ZodLiteral<1>;
|
|
5
|
+
promptVersion: z.ZodLiteral<1>;
|
|
6
|
+
primary: z.ZodObject<{
|
|
7
|
+
repo: z.ZodString;
|
|
8
|
+
revision: z.ZodString;
|
|
9
|
+
questions: z.ZodLiteral<"v3">;
|
|
10
|
+
}, z.core.$strict>;
|
|
11
|
+
fallback: z.ZodObject<{
|
|
12
|
+
model: z.ZodString;
|
|
13
|
+
baseUrl: z.ZodURL;
|
|
14
|
+
questions: z.ZodLiteral<"v1">;
|
|
15
|
+
}, z.core.$strict>;
|
|
16
|
+
unsureBand: z.ZodTuple<[z.ZodNumber, z.ZodNumber], null>;
|
|
17
|
+
minRiskConfidence: z.ZodNumber;
|
|
18
|
+
blockThreshold: z.ZodLiteral<0.7>;
|
|
19
|
+
calibrationHash: z.ZodString;
|
|
20
|
+
testHash: z.ZodString;
|
|
21
|
+
frozenAt: z.ZodISODateTime;
|
|
22
|
+
protocol: z.ZodObject<{
|
|
23
|
+
maxTprDrop: z.ZodNumber;
|
|
24
|
+
minBenignCases: z.ZodNumber;
|
|
25
|
+
hostingUsdPer1000: z.ZodNullable<z.ZodNumber>;
|
|
26
|
+
}, z.core.$strict>;
|
|
27
|
+
}, z.core.$strict>;
|
|
28
|
+
export type SentinelCascadePolicy = z.infer<typeof sentinelCascadePolicySchema>;
|
|
29
|
+
export declare const sentinelCascadePolicyHash: (policy: SentinelCascadePolicy) => `0x${string}`;
|
|
30
|
+
/** Public aggregate evidence. Individual held-out states and answers stay in private datasets. */
|
|
31
|
+
export declare const sentinelCascadeEvidenceSchema: z.ZodObject<{
|
|
32
|
+
version: z.ZodLiteral<1>;
|
|
33
|
+
policyHash: z.ZodString;
|
|
34
|
+
testHash: z.ZodString;
|
|
35
|
+
evaluatedAt: z.ZodISODateTime;
|
|
36
|
+
pass: z.ZodBoolean;
|
|
37
|
+
gates: z.ZodObject<{
|
|
38
|
+
detection: z.ZodBoolean;
|
|
39
|
+
productionFpr: z.ZodBoolean;
|
|
40
|
+
layaRegression: z.ZodBoolean;
|
|
41
|
+
sufficientCases: z.ZodBoolean;
|
|
42
|
+
noErrors: z.ZodBoolean;
|
|
43
|
+
knownCost: z.ZodBoolean;
|
|
44
|
+
cheaperThanLlm: z.ZodBoolean;
|
|
45
|
+
}, z.core.$strict>;
|
|
46
|
+
}, z.core.$strip>;
|
|
47
|
+
/** Require the exact frozen policy and test set, with every gate explicitly passing. */
|
|
48
|
+
export declare function verifySentinelCascadeEvidence(policy: SentinelCascadePolicy, input: unknown): {
|
|
49
|
+
version: 1;
|
|
50
|
+
policyHash: string;
|
|
51
|
+
testHash: string;
|
|
52
|
+
evaluatedAt: string;
|
|
53
|
+
pass: boolean;
|
|
54
|
+
gates: {
|
|
55
|
+
detection: boolean;
|
|
56
|
+
productionFpr: boolean;
|
|
57
|
+
layaRegression: boolean;
|
|
58
|
+
sufficientCases: boolean;
|
|
59
|
+
noErrors: boolean;
|
|
60
|
+
knownCost: boolean;
|
|
61
|
+
cheaperThanLlm: boolean;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
//# sourceMappingURL=sentinel-cascade.d.ts.map
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { canonicalHash } from "./canonical.js";
|
|
3
|
+
const hash = z.string().regex(/^0x[0-9a-f]{64}$/);
|
|
4
|
+
const probability = z.number().finite().min(0).max(1);
|
|
5
|
+
/** Frozen before the final evaluation; never contains a provider credential or attack text. */
|
|
6
|
+
export const sentinelCascadePolicySchema = z.strictObject({
|
|
7
|
+
version: z.literal(1),
|
|
8
|
+
promptVersion: z.literal(1),
|
|
9
|
+
primary: z.strictObject({
|
|
10
|
+
repo: z.string().min(1),
|
|
11
|
+
revision: z.string().regex(/^[0-9a-f]{40}$/),
|
|
12
|
+
questions: z.literal("v3"),
|
|
13
|
+
}),
|
|
14
|
+
fallback: z.strictObject({
|
|
15
|
+
model: z.string().min(1),
|
|
16
|
+
baseUrl: z.url().refine((s) => {
|
|
17
|
+
const u = new URL(s);
|
|
18
|
+
return !u.username && !u.password && !u.search && !u.hash;
|
|
19
|
+
}),
|
|
20
|
+
questions: z.literal("v1"),
|
|
21
|
+
}),
|
|
22
|
+
unsureBand: z.tuple([probability, probability]).refine(([lo, hi]) => lo < hi),
|
|
23
|
+
minRiskConfidence: probability,
|
|
24
|
+
blockThreshold: z.literal(0.7),
|
|
25
|
+
calibrationHash: hash,
|
|
26
|
+
testHash: hash,
|
|
27
|
+
frozenAt: z.iso.datetime(),
|
|
28
|
+
protocol: z.strictObject({
|
|
29
|
+
maxTprDrop: probability,
|
|
30
|
+
minBenignCases: z.number().int().min(100),
|
|
31
|
+
hostingUsdPer1000: z.number().finite().nonnegative().nullable(),
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
export const sentinelCascadePolicyHash = (policy) => canonicalHash(JSON.stringify(sentinelCascadePolicySchema.parse(policy)));
|
|
35
|
+
/** Public aggregate evidence. Individual held-out states and answers stay in private datasets. */
|
|
36
|
+
export const sentinelCascadeEvidenceSchema = z.object({
|
|
37
|
+
version: z.literal(1),
|
|
38
|
+
policyHash: hash,
|
|
39
|
+
testHash: hash,
|
|
40
|
+
evaluatedAt: z.iso.datetime(),
|
|
41
|
+
pass: z.boolean(),
|
|
42
|
+
gates: z.strictObject({
|
|
43
|
+
detection: z.boolean(),
|
|
44
|
+
productionFpr: z.boolean(),
|
|
45
|
+
layaRegression: z.boolean(),
|
|
46
|
+
sufficientCases: z.boolean(),
|
|
47
|
+
noErrors: z.boolean(),
|
|
48
|
+
knownCost: z.boolean(),
|
|
49
|
+
cheaperThanLlm: z.boolean(),
|
|
50
|
+
}),
|
|
51
|
+
});
|
|
52
|
+
/** Require the exact frozen policy and test set, with every gate explicitly passing. */
|
|
53
|
+
export function verifySentinelCascadeEvidence(policy, input) {
|
|
54
|
+
const evidence = sentinelCascadeEvidenceSchema.parse(input);
|
|
55
|
+
if (evidence.policyHash !== sentinelCascadePolicyHash(policy) ||
|
|
56
|
+
evidence.testHash !== policy.testHash)
|
|
57
|
+
throw new Error("cascade evaluation belongs to a different policy or test dataset");
|
|
58
|
+
if (Date.parse(evidence.evaluatedAt) < Date.parse(policy.frozenAt))
|
|
59
|
+
throw new Error("cascade policy must be frozen before its test evaluation");
|
|
60
|
+
if (!evidence.pass || !Object.values(evidence.gates).every(Boolean))
|
|
61
|
+
throw new Error("cascade evaluation did not pass every production gate");
|
|
62
|
+
return evidence;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sentinel-cascade.js.map
|