@edge-protocol/sdk 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DOCS.md +357 -297
- package/README.md +122 -33
- package/dist/core/PolicyEngine.d.ts +0 -68
- package/dist/core/PolicyEngine.d.ts.map +1 -1
- package/dist/core/PolicyEngine.js +7 -85
- package/dist/core/PolicyEngine.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# @edge-protocol/sdk
|
|
2
2
|
|
|
3
3
|
[](https://npmjs.com/package/@edge-protocol/sdk)
|
|
4
|
-
[](https://npmjs.com/package/@edge-protocol/sdk)
|
|
5
|
+
[](https://github.com/fluturecode/edge)
|
|
6
|
+
[](https://sui.io)
|
|
7
|
+
[](LICENSE)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Give agents your rules, not your keys.
|
|
10
10
|
|
|
11
|
-
[Live Demo](https://edge-web-cyan.vercel.app) · [Full Docs](
|
|
11
|
+
[Live Demo](https://edge-web-cyan.vercel.app) · [Full Docs](DOCS.md) · [GitHub](https://github.com/fluturecode/edge)
|
|
12
12
|
|
|
13
13
|
---
|
|
14
14
|
|
|
15
15
|
As autonomous agents begin managing real assets onchain, they need a trust layer that governs how they interact with them. Raw private keys are a security nightmare. Requiring human approval for every transaction defeats the purpose of automation.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
EdgePass is the policy layer — scoped, programmatic spend authority issued directly to agent runtimes, with cryptographic guardrails enforced by the Sui VM. Not a payment rail. Not a wallet. The boundary between what an agent can do and what it cannot.
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
@@ -38,11 +38,11 @@ Every EdgePass is a native Sui Move object encoding five distinct governance dim
|
|
|
38
38
|
|
|
39
39
|
| Dimension | What it controls |
|
|
40
40
|
|-----------|-----------------|
|
|
41
|
-
|
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
|
45
|
-
|
|
|
41
|
+
| BUDGET | Maximum global spending ceiling |
|
|
42
|
+
| VELOCITY | Auto-approve threshold before escalation fires |
|
|
43
|
+
| SCOPE | Explicit allowlist of approved merchants / contracts |
|
|
44
|
+
| TIME | Hard cryptographic expiration date |
|
|
45
|
+
| ESCALATION | Programmatic fallback when a limit is exceeded |
|
|
46
46
|
|
|
47
47
|
---
|
|
48
48
|
|
|
@@ -58,7 +58,8 @@ EdgePass.fromTemplate('defi', { owner }) // $10k · auto <$500 · esca
|
|
|
58
58
|
EdgePass.fromTemplate('enterprise', { owner }) // $50k · auto <$1k · escalate >$5k · 30d
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
Example — brand licensing agent:
|
|
62
|
+
|
|
62
63
|
```typescript
|
|
63
64
|
EdgePass.fromTemplate('enterprise', {
|
|
64
65
|
approvedMerchants: ['nike-licensing.sui', 'brand-registry.sui'],
|
|
@@ -70,6 +71,54 @@ EdgePass.fromTemplate('enterprise', {
|
|
|
70
71
|
|
|
71
72
|
---
|
|
72
73
|
|
|
74
|
+
## 🔮 Simulate Before You Execute
|
|
75
|
+
|
|
76
|
+
Plan an agent's session without touching the chain. Zero network calls.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
const plan = sdk.simulate(pass, [
|
|
80
|
+
{ merchant: 'Shuttle Express', amount: 45n * MIST_PER_SUI },
|
|
81
|
+
{ merchant: 'ShadyTokens.xyz', amount: 1n },
|
|
82
|
+
{ merchant: 'Stage Access VIP', amount: 220n * MIST_PER_SUI },
|
|
83
|
+
]);
|
|
84
|
+
|
|
85
|
+
console.log(plan.summary);
|
|
86
|
+
// { approvedCount: 1, blockedCount: 1, escalatedCount: 1, totalDecisions: 3 }
|
|
87
|
+
|
|
88
|
+
console.log(plan.utilizationPct); // projected budget usage after approved decisions
|
|
89
|
+
console.log(plan.remainingBudget); // projected remaining in MIST
|
|
90
|
+
|
|
91
|
+
// Show plan to user, then execute approved decisions
|
|
92
|
+
for (const decision of plan.approved) {
|
|
93
|
+
await sdk.execute(pass, decision.request, signer);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## 💰 Budget Intelligence
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const status = sdk.budgetStatus(pass);
|
|
103
|
+
// {
|
|
104
|
+
// budget: 500000000000n,
|
|
105
|
+
// spent: 218000000000n,
|
|
106
|
+
// remaining: 282000000000n,
|
|
107
|
+
// utilizationPct: 43.6,
|
|
108
|
+
// isNearLimit: false, // true when > 80%
|
|
109
|
+
// isExhausted: false,
|
|
110
|
+
// }
|
|
111
|
+
|
|
112
|
+
sdk.utilizationPct(pass) // 43.6
|
|
113
|
+
sdk.isNearLimit(pass) // false (default threshold: 80%)
|
|
114
|
+
sdk.isNearLimit(pass, 0.5) // true if > 50% spent
|
|
115
|
+
sdk.remainingBudget(pass) // 282000000000n MIST
|
|
116
|
+
sdk.timeRemaining(pass) // ms until expiry
|
|
117
|
+
sdk.isExpiringSoon(pass) // true if expires within 1 hour
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
73
122
|
## 🤖 Agent Framework Integration
|
|
74
123
|
|
|
75
124
|
### Vercel AI SDK / Mastra
|
|
@@ -91,14 +140,29 @@ export const autonomousPurchaseTool = tool({
|
|
|
91
140
|
amount: BigInt(Math.floor(amountSUI * 1e9)),
|
|
92
141
|
}, agentSigner);
|
|
93
142
|
|
|
94
|
-
if (outcome.status === 'blocked') return { success: false, error: `Blocked
|
|
95
|
-
if (outcome.status === 'escalated') return { success: false, error: `
|
|
143
|
+
if (outcome.status === 'blocked') return { success: false, error: `Blocked: ${outcome.reason}` };
|
|
144
|
+
if (outcome.status === 'escalated') return { success: false, error: `Escalated: ${outcome.reason}` };
|
|
96
145
|
|
|
97
146
|
return { success: true, digest: outcome.digest };
|
|
98
147
|
}
|
|
99
148
|
});
|
|
100
149
|
```
|
|
101
150
|
|
|
151
|
+
### `withPolicy` — Wrap Any Tool
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
const safePurchase = EdgePass.withPolicy(pass, signer, sdk, async (request) => {
|
|
155
|
+
return await purchaseItem(request.merchant, request.amount);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// safePurchase enforces EdgePass policy automatically
|
|
159
|
+
// blocked/escalated never reach your tool logic
|
|
160
|
+
const { outcome, result } = await safePurchase({
|
|
161
|
+
merchant: 'Hydra Bar',
|
|
162
|
+
amount: 32n * MIST_PER_SUI,
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
102
166
|
### Native Agent System Prompt
|
|
103
167
|
|
|
104
168
|
```typescript
|
|
@@ -118,6 +182,41 @@ The transaction was executed on-chain and logged to Walrus.
|
|
|
118
182
|
|
|
119
183
|
---
|
|
120
184
|
|
|
185
|
+
## ⚛️ React Hook
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
import { useEdgePass } from '@edge-protocol/sdk/react';
|
|
189
|
+
|
|
190
|
+
function AgentDashboard({ passId, signer }) {
|
|
191
|
+
const { pass, execute, simulate, budgetStatus, loading } = useEdgePass({
|
|
192
|
+
passId,
|
|
193
|
+
network: 'mainnet',
|
|
194
|
+
enokiApiKey: process.env.NEXT_PUBLIC_ENOKI_API_KEY!,
|
|
195
|
+
signer,
|
|
196
|
+
autoRefresh: true, // re-fetch after every approved execute
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
if (loading) return <Spinner />;
|
|
200
|
+
if (!pass) return <div>Pass not found</div>;
|
|
201
|
+
|
|
202
|
+
return (
|
|
203
|
+
<div>
|
|
204
|
+
<progress value={budgetStatus?.utilizationPct} max={100} />
|
|
205
|
+
<button onClick={() => execute({ merchant: 'Hydra Bar', amount: 32n * MIST_PER_SUI })}>
|
|
206
|
+
Purchase
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Three hooks available:
|
|
214
|
+
- `useEdgePass` — full featured: fetch, execute, simulate, budgetStatus, refresh
|
|
215
|
+
- `useBudgetStatus` — lightweight budget display
|
|
216
|
+
- `useSimulate` — reactive simulation when requests change
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
121
220
|
## 📊 Execution Results
|
|
122
221
|
|
|
123
222
|
Every `sdk.execute()` returns a structured outcome — not a flat string:
|
|
@@ -127,6 +226,7 @@ type TransactionOutcome =
|
|
|
127
226
|
| { status: 'approved'; digest: string; auto: true; }
|
|
128
227
|
| { status: 'escalated'; reason: string; auto: false; }
|
|
129
228
|
| { status: 'blocked'; reason: string; auto: false; }
|
|
229
|
+
| { status: 'error'; reason: string; code?: string; auto: false; }
|
|
130
230
|
|
|
131
231
|
// Approved
|
|
132
232
|
{ status: 'approved', digest: '4REcPLezK8gF...', auto: true }
|
|
@@ -164,7 +264,7 @@ await sdk.execute(pass, request, signer);
|
|
|
164
264
|
|
|
165
265
|
## 🔌 Pluggable Escalation Handlers
|
|
166
266
|
|
|
167
|
-
Route escalation alerts to
|
|
267
|
+
Route escalation alerts to Slack, Telegram, or your dashboard:
|
|
168
268
|
|
|
169
269
|
```typescript
|
|
170
270
|
sdk.on('escalated', async ({ outcome, request }) => {
|
|
@@ -179,22 +279,8 @@ sdk.on('escalated', async ({ outcome, request }) => {
|
|
|
179
279
|
|
|
180
280
|
---
|
|
181
281
|
|
|
182
|
-
## 📜 Cryptographic Audit Trail
|
|
183
|
-
|
|
184
|
-
Every execution writes an immutable receipt to Walrus — decentralized, tamper-evident, permanent. No database. No server. Cryptographically committed.
|
|
185
|
-
|
|
186
|
-
```typescript
|
|
187
|
-
const outcome = await sdk.execute(pass, request, signer);
|
|
188
|
-
// audit receipt automatically written to Walrus
|
|
189
|
-
// verifiable at walruscan.com/mainnet/blob/{blobId}
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
---
|
|
193
|
-
|
|
194
282
|
## 🔍 Preview Without Executing
|
|
195
283
|
|
|
196
|
-
Zero network calls. Sub-millisecond. Use for UI previews:
|
|
197
|
-
|
|
198
284
|
```typescript
|
|
199
285
|
const preview = sdk.validate(pass, { merchant, amount });
|
|
200
286
|
// { allowed: boolean, requiresEscalation: boolean, reason: string }
|
|
@@ -230,8 +316,6 @@ assert!(amount <= pass.escalate_threshold, EAmountExceedsEscalationThreshold);
|
|
|
230
316
|
|
|
231
317
|
If any assertion fails, the entire transaction reverts. A compromised agent cannot bypass the contract. **The chain is the trust boundary.**
|
|
232
318
|
|
|
233
|
-
For production: always execute via the Move contract. The TypeScript layer is a preview — the chain is the guarantee.
|
|
234
|
-
|
|
235
319
|
---
|
|
236
320
|
|
|
237
321
|
## 🧪 Testing
|
|
@@ -273,6 +357,11 @@ pnpm add @edge-protocol/sdk
|
|
|
273
357
|
yarn add @edge-protocol/sdk
|
|
274
358
|
```
|
|
275
359
|
|
|
360
|
+
React hook (requires React 18+):
|
|
361
|
+
```bash
|
|
362
|
+
import { useEdgePass } from '@edge-protocol/sdk/react';
|
|
363
|
+
```
|
|
364
|
+
|
|
276
365
|
---
|
|
277
366
|
|
|
278
367
|
## Competitive Positioning
|
|
@@ -302,6 +391,6 @@ Edge (policy layer) → x402 (payment rail) → Settlement
|
|
|
302
391
|
|
|
303
392
|
*The best infrastructure is invisible.*
|
|
304
393
|
|
|
305
|
-
Built for
|
|
394
|
+
Built for Sui Overflow 2026 · MIT License
|
|
306
395
|
|
|
307
396
|
[GitHub](https://github.com/fluturecode/edge) · [npm](https://npmjs.com/package/@edge-protocol/sdk)
|
|
@@ -1,81 +1,13 @@
|
|
|
1
1
|
import { EdgePassObject, TransactionRequest, PolicyValidation, SimulationResult, BudgetStatus } from '../utils/types';
|
|
2
2
|
export declare class PolicyEngine {
|
|
3
|
-
/**
|
|
4
|
-
* Validates a transaction request against an EdgePass policy.
|
|
5
|
-
*
|
|
6
|
-
* Rules (in order):
|
|
7
|
-
* 1. Pass must be active
|
|
8
|
-
* 2. Pass must not be expired
|
|
9
|
-
* 3. Merchant must be in approved list
|
|
10
|
-
* 4. Amount must not exceed remaining budget
|
|
11
|
-
* 5. Amount must not exceed maxPerTransaction (if set)
|
|
12
|
-
* 6. If amount > escalateThreshold → escalate
|
|
13
|
-
* 7. If amount ≤ autoThreshold → auto-approve
|
|
14
|
-
*/
|
|
15
3
|
static validate(pass: EdgePassObject, request: TransactionRequest): PolicyValidation;
|
|
16
|
-
/**
|
|
17
|
-
* Simulate a sequence of transactions against an EdgePass.
|
|
18
|
-
* Zero network calls. Returns predicted outcomes for all decisions
|
|
19
|
-
* including projected budget state after each step.
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* const plan = sdk.simulate(pass, claudeDecisions);
|
|
23
|
-
* console.log(plan.approved.length); // decisions that will execute
|
|
24
|
-
* console.log(plan.blocked.length); // decisions that will be rejected
|
|
25
|
-
* console.log(plan.utilizationPct); // projected budget usage
|
|
26
|
-
*
|
|
27
|
-
* // Show plan to user, then execute approved decisions
|
|
28
|
-
* for (const decision of plan.approved) {
|
|
29
|
-
* await sdk.execute(pass, decision.request, signer);
|
|
30
|
-
* }
|
|
31
|
-
*/
|
|
32
4
|
static simulate(pass: EdgePassObject, requests: TransactionRequest[]): SimulationResult;
|
|
33
|
-
/**
|
|
34
|
-
* Returns true if the pass is active and not expired.
|
|
35
|
-
*/
|
|
36
5
|
static isValid(pass: EdgePassObject): boolean;
|
|
37
|
-
/**
|
|
38
|
-
* Returns the remaining budget in MIST.
|
|
39
|
-
*/
|
|
40
6
|
static remainingBudget(pass: EdgePassObject): bigint;
|
|
41
|
-
/**
|
|
42
|
-
* Returns budget utilization as a percentage (0-100).
|
|
43
|
-
*
|
|
44
|
-
* @example
|
|
45
|
-
* const pct = sdk.utilizationPct(pass);
|
|
46
|
-
* if (pct > 80) warnUser('Running low on budget');
|
|
47
|
-
*/
|
|
48
7
|
static utilizationPct(pass: EdgePassObject): number;
|
|
49
|
-
/**
|
|
50
|
-
* Returns true if budget utilization exceeds the given threshold.
|
|
51
|
-
* Default threshold is 80%.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* if (sdk.isNearLimit(pass)) notifyUser('Budget nearly exhausted');
|
|
55
|
-
* if (sdk.isNearLimit(pass, 0.5)) notifyUser('Halfway through budget');
|
|
56
|
-
*/
|
|
57
8
|
static isNearLimit(pass: EdgePassObject, threshold?: number): boolean;
|
|
58
|
-
/**
|
|
59
|
-
* Returns a complete budget status snapshot.
|
|
60
|
-
*
|
|
61
|
-
* @example
|
|
62
|
-
* const status = sdk.budgetStatus(pass);
|
|
63
|
-
* if (status.isExhausted) stopAgent();
|
|
64
|
-
* if (status.isNearLimit) notifyUser(`${status.utilizationPct}% spent`);
|
|
65
|
-
*/
|
|
66
9
|
static budgetStatus(pass: EdgePassObject, nearLimitThreshold?: number): BudgetStatus;
|
|
67
|
-
/**
|
|
68
|
-
* Returns the time remaining on the pass in milliseconds.
|
|
69
|
-
* Returns 0 if expired.
|
|
70
|
-
*/
|
|
71
10
|
static timeRemaining(pass: EdgePassObject): number;
|
|
72
|
-
/**
|
|
73
|
-
* Returns true if the pass will expire within the given window.
|
|
74
|
-
* Default window is 1 hour.
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* if (sdk.isExpiringSoon(pass)) notifyUser('Pass expires in less than 1 hour');
|
|
78
|
-
*/
|
|
79
11
|
static isExpiringSoon(pass: EdgePassObject, withinMs?: number): boolean;
|
|
80
12
|
}
|
|
81
13
|
//# sourceMappingURL=PolicyEngine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PolicyEngine.d.ts","sourceRoot":"","sources":["../../src/core/PolicyEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAEhB,gBAAgB,EAChB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,qBAAa,YAAY;IAEvB
|
|
1
|
+
{"version":3,"file":"PolicyEngine.d.ts","sourceRoot":"","sources":["../../src/core/PolicyEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,gBAAgB,EAEhB,gBAAgB,EAChB,YAAY,EACb,MAAM,gBAAgB,CAAC;AAExB,qBAAa,YAAY;IAEvB,MAAM,CAAC,QAAQ,CACb,IAAI,EAAE,cAAc,EACpB,OAAO,EAAE,kBAAkB,GAC1B,gBAAgB;IA8BnB,MAAM,CAAC,QAAQ,CACb,IAAI,EAAE,cAAc,EACpB,QAAQ,EAAE,kBAAkB,EAAE,GAC7B,gBAAgB;IA2DnB,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO;IAI7C,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAIpD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAKnD,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,cAAc,EAAE,SAAS,SAAM,GAAG,OAAO;IAIlE,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,cAAc,EAAE,kBAAkB,SAAM,GAAG,YAAY;IAajF,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM;IAIlD,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,EAAE,QAAQ,SAAiB,GAAG,OAAO;CAIhF"}
|
|
@@ -2,68 +2,32 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PolicyEngine = void 0;
|
|
4
4
|
class PolicyEngine {
|
|
5
|
-
/**
|
|
6
|
-
* Validates a transaction request against an EdgePass policy.
|
|
7
|
-
*
|
|
8
|
-
* Rules (in order):
|
|
9
|
-
* 1. Pass must be active
|
|
10
|
-
* 2. Pass must not be expired
|
|
11
|
-
* 3. Merchant must be in approved list
|
|
12
|
-
* 4. Amount must not exceed remaining budget
|
|
13
|
-
* 5. Amount must not exceed maxPerTransaction (if set)
|
|
14
|
-
* 6. If amount > escalateThreshold → escalate
|
|
15
|
-
* 7. If amount ≤ autoThreshold → auto-approve
|
|
16
|
-
*/
|
|
17
5
|
static validate(pass, request) {
|
|
18
|
-
// Rule 1 — pass must be active
|
|
19
6
|
if (!pass.active) {
|
|
20
7
|
return { allowed: false, requiresEscalation: false, reason: 'EdgePass is inactive' };
|
|
21
8
|
}
|
|
22
|
-
// Rule 2 — pass must not be expired
|
|
23
9
|
if (Date.now() > pass.expiresAt) {
|
|
24
10
|
return { allowed: false, requiresEscalation: false, reason: 'EdgePass has expired' };
|
|
25
11
|
}
|
|
26
|
-
// Rule 3 — merchant must be approved
|
|
27
12
|
if (!pass.config.approvedMerchants.includes(request.merchant)) {
|
|
28
13
|
return { allowed: false, requiresEscalation: false, reason: `Merchant "${request.merchant}" is not approved` };
|
|
29
14
|
}
|
|
30
|
-
// Rule 4 — must not exceed remaining budget
|
|
31
15
|
const remaining = pass.config.budget - pass.spent;
|
|
32
16
|
if (request.amount > remaining) {
|
|
33
17
|
return { allowed: false, requiresEscalation: false, reason: `Insufficient budget. Remaining: ${remaining} MIST` };
|
|
34
18
|
}
|
|
35
|
-
// Rule 5 — must not exceed per-transaction limit (if set)
|
|
36
19
|
if (pass.config.maxPerTransaction !== undefined && request.amount > pass.config.maxPerTransaction) {
|
|
37
20
|
return { allowed: false, requiresEscalation: false, reason: `Amount exceeds per-transaction limit of ${pass.config.maxPerTransaction} MIST` };
|
|
38
21
|
}
|
|
39
|
-
// Rule 6 — escalate if above escalation threshold
|
|
40
22
|
if (request.amount > pass.config.escalateThreshold) {
|
|
41
23
|
return { allowed: true, requiresEscalation: true, reason: `Amount exceeds escalation threshold of ${pass.config.escalateThreshold} MIST` };
|
|
42
24
|
}
|
|
43
|
-
// Rule 7 — auto-approve
|
|
44
25
|
return { allowed: true, requiresEscalation: false, reason: 'Auto-approved' };
|
|
45
26
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Simulate a sequence of transactions against an EdgePass.
|
|
48
|
-
* Zero network calls. Returns predicted outcomes for all decisions
|
|
49
|
-
* including projected budget state after each step.
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* const plan = sdk.simulate(pass, claudeDecisions);
|
|
53
|
-
* console.log(plan.approved.length); // decisions that will execute
|
|
54
|
-
* console.log(plan.blocked.length); // decisions that will be rejected
|
|
55
|
-
* console.log(plan.utilizationPct); // projected budget usage
|
|
56
|
-
*
|
|
57
|
-
* // Show plan to user, then execute approved decisions
|
|
58
|
-
* for (const decision of plan.approved) {
|
|
59
|
-
* await sdk.execute(pass, decision.request, signer);
|
|
60
|
-
* }
|
|
61
|
-
*/
|
|
62
27
|
static simulate(pass, requests) {
|
|
63
28
|
const decisions = [];
|
|
64
29
|
let projectedSpent = pass.spent;
|
|
65
30
|
for (const request of requests) {
|
|
66
|
-
// Create a projected pass with current projected spent for validation
|
|
67
31
|
const projectedPass = {
|
|
68
32
|
...pass,
|
|
69
33
|
spent: projectedSpent,
|
|
@@ -76,28 +40,26 @@ class PolicyEngine {
|
|
|
76
40
|
}
|
|
77
41
|
else if (validation.requiresEscalation) {
|
|
78
42
|
outcome = 'escalated';
|
|
79
|
-
// Escalated decisions don't spend budget until approved
|
|
80
43
|
}
|
|
81
44
|
else {
|
|
82
45
|
outcome = 'approved';
|
|
83
46
|
nextSpent = projectedSpent + request.amount;
|
|
84
47
|
}
|
|
85
|
-
|
|
48
|
+
decisions.push({
|
|
86
49
|
request,
|
|
87
50
|
outcome,
|
|
88
51
|
reason: validation.reason,
|
|
89
52
|
projectedSpent: nextSpent,
|
|
90
53
|
projectedRemaining: pass.config.budget - nextSpent,
|
|
91
|
-
};
|
|
92
|
-
decisions.push(decision);
|
|
54
|
+
});
|
|
93
55
|
projectedSpent = nextSpent;
|
|
94
56
|
}
|
|
95
57
|
const approved = decisions.filter(d => d.outcome === 'approved');
|
|
96
58
|
const blocked = decisions.filter(d => d.outcome === 'blocked');
|
|
97
59
|
const escalated = decisions.filter(d => d.outcome === 'escalated');
|
|
98
|
-
const totalSpend = approved.reduce((sum, d) => sum + d.request.amount,
|
|
60
|
+
const totalSpend = approved.reduce((sum, d) => sum + d.request.amount, BigInt(0));
|
|
99
61
|
const remainingBudget = pass.config.budget - pass.spent - totalSpend;
|
|
100
|
-
const utilizationPct = Number((pass.spent + totalSpend) *
|
|
62
|
+
const utilizationPct = Number((pass.spent + totalSpend) * BigInt(100) / pass.config.budget);
|
|
101
63
|
return {
|
|
102
64
|
decisions,
|
|
103
65
|
approved,
|
|
@@ -114,49 +76,20 @@ class PolicyEngine {
|
|
|
114
76
|
},
|
|
115
77
|
};
|
|
116
78
|
}
|
|
117
|
-
/**
|
|
118
|
-
* Returns true if the pass is active and not expired.
|
|
119
|
-
*/
|
|
120
79
|
static isValid(pass) {
|
|
121
80
|
return pass.active && Date.now() <= pass.expiresAt;
|
|
122
81
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Returns the remaining budget in MIST.
|
|
125
|
-
*/
|
|
126
82
|
static remainingBudget(pass) {
|
|
127
83
|
return pass.config.budget - pass.spent;
|
|
128
84
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Returns budget utilization as a percentage (0-100).
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* const pct = sdk.utilizationPct(pass);
|
|
134
|
-
* if (pct > 80) warnUser('Running low on budget');
|
|
135
|
-
*/
|
|
136
85
|
static utilizationPct(pass) {
|
|
137
|
-
if (pass.config.budget ===
|
|
86
|
+
if (pass.config.budget === BigInt(0))
|
|
138
87
|
return 0;
|
|
139
|
-
return Number(pass.spent *
|
|
88
|
+
return Number(pass.spent * BigInt(100) / pass.config.budget);
|
|
140
89
|
}
|
|
141
|
-
/**
|
|
142
|
-
* Returns true if budget utilization exceeds the given threshold.
|
|
143
|
-
* Default threshold is 80%.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* if (sdk.isNearLimit(pass)) notifyUser('Budget nearly exhausted');
|
|
147
|
-
* if (sdk.isNearLimit(pass, 0.5)) notifyUser('Halfway through budget');
|
|
148
|
-
*/
|
|
149
90
|
static isNearLimit(pass, threshold = 0.8) {
|
|
150
91
|
return PolicyEngine.utilizationPct(pass) >= threshold * 100;
|
|
151
92
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Returns a complete budget status snapshot.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* const status = sdk.budgetStatus(pass);
|
|
157
|
-
* if (status.isExhausted) stopAgent();
|
|
158
|
-
* if (status.isNearLimit) notifyUser(`${status.utilizationPct}% spent`);
|
|
159
|
-
*/
|
|
160
93
|
static budgetStatus(pass, nearLimitThreshold = 0.8) {
|
|
161
94
|
const remaining = pass.config.budget - pass.spent;
|
|
162
95
|
const utilizationPct = PolicyEngine.utilizationPct(pass);
|
|
@@ -166,23 +99,12 @@ class PolicyEngine {
|
|
|
166
99
|
remaining,
|
|
167
100
|
utilizationPct,
|
|
168
101
|
isNearLimit: utilizationPct >= nearLimitThreshold * 100,
|
|
169
|
-
isExhausted: remaining ===
|
|
102
|
+
isExhausted: remaining === BigInt(0),
|
|
170
103
|
};
|
|
171
104
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Returns the time remaining on the pass in milliseconds.
|
|
174
|
-
* Returns 0 if expired.
|
|
175
|
-
*/
|
|
176
105
|
static timeRemaining(pass) {
|
|
177
106
|
return Math.max(0, pass.expiresAt - Date.now());
|
|
178
107
|
}
|
|
179
|
-
/**
|
|
180
|
-
* Returns true if the pass will expire within the given window.
|
|
181
|
-
* Default window is 1 hour.
|
|
182
|
-
*
|
|
183
|
-
* @example
|
|
184
|
-
* if (sdk.isExpiringSoon(pass)) notifyUser('Pass expires in less than 1 hour');
|
|
185
|
-
*/
|
|
186
108
|
static isExpiringSoon(pass, withinMs = 60 * 60 * 1000) {
|
|
187
109
|
const remaining = PolicyEngine.timeRemaining(pass);
|
|
188
110
|
return remaining > 0 && remaining <= withinMs;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PolicyEngine.js","sourceRoot":"","sources":["../../src/core/PolicyEngine.ts"],"names":[],"mappings":";;;AASA,MAAa,YAAY;IAEvB
|
|
1
|
+
{"version":3,"file":"PolicyEngine.js","sourceRoot":"","sources":["../../src/core/PolicyEngine.ts"],"names":[],"mappings":";;;AASA,MAAa,YAAY;IAEvB,MAAM,CAAC,QAAQ,CACb,IAAoB,EACpB,OAA2B;QAG3B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QACvF,CAAC;QAED,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,CAAC;QACvF,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9D,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,OAAO,CAAC,QAAQ,mBAAmB,EAAE,CAAC;QACjH,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;QAClD,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC;YAC/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,mCAAmC,SAAS,OAAO,EAAE,CAAC;QACpH,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,iBAAiB,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YAClG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,2CAA2C,IAAI,CAAC,MAAM,CAAC,iBAAiB,OAAO,EAAE,CAAC;QAChJ,CAAC;QAED,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;YACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,0CAA0C,IAAI,CAAC,MAAM,CAAC,iBAAiB,OAAO,EAAE,CAAC;QAC7I,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,CAAC;IAC/E,CAAC;IAED,MAAM,CAAC,QAAQ,CACb,IAAoB,EACpB,QAA8B;QAE9B,MAAM,SAAS,GAAwB,EAAE,CAAC;QAC1C,IAAI,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC;QAEhC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,aAAa,GAAmB;gBACpC,GAAG,IAAI;gBACP,KAAK,EAAE,cAAc;aACtB,CAAC;YAEF,MAAM,UAAU,GAAG,YAAY,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAEjE,IAAI,OAA6C,CAAC;YAClD,IAAI,SAAS,GAAG,cAAc,CAAC;YAE/B,IAAI,CAAC,UAAU,CAAC,OAAO,EAAE,CAAC;gBACxB,OAAO,GAAG,SAAS,CAAC;YACtB,CAAC;iBAAM,IAAI,UAAU,CAAC,kBAAkB,EAAE,CAAC;gBACzC,OAAO,GAAG,WAAW,CAAC;YACxB,CAAC;iBAAM,CAAC;gBACN,OAAO,GAAG,UAAU,CAAC;gBACrB,SAAS,GAAG,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9C,CAAC;YAED,SAAS,CAAC,IAAI,CAAC;gBACb,OAAO;gBACP,OAAO;gBACP,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,cAAc,EAAE,SAAS;gBACzB,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS;aACnD,CAAC,CAAC;YAEH,cAAc,GAAG,SAAS,CAAC;QAC7B,CAAC;QAED,MAAM,QAAQ,GAAI,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,UAAU,CAAC,CAAC;QAClE,MAAM,OAAO,GAAK,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,WAAW,CAAC,CAAC;QACnE,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QAClF,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;QACrE,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAE5F,OAAO;YACL,SAAS;YACT,QAAQ;YACR,OAAO;YACP,SAAS;YACT,UAAU;YACV,eAAe;YACf,cAAc;YACd,OAAO,EAAE;gBACP,aAAa,EAAG,QAAQ,CAAC,MAAM;gBAC/B,YAAY,EAAI,OAAO,CAAC,MAAM;gBAC9B,cAAc,EAAE,SAAS,CAAC,MAAM;gBAChC,cAAc,EAAE,SAAS,CAAC,MAAM;aACjC;SACF,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAAoB;QACjC,OAAO,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC;IACrD,CAAC;IAED,MAAM,CAAC,eAAe,CAAC,IAAoB;QACzC,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;IACzC,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,IAAoB;QACxC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;YAAE,OAAO,CAAC,CAAC;QAC/C,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,IAAoB,EAAE,SAAS,GAAG,GAAG;QACtD,OAAO,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,SAAS,GAAG,GAAG,CAAC;IAC9D,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,IAAoB,EAAE,kBAAkB,GAAG,GAAG;QAChE,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC;QAClD,MAAM,cAAc,GAAG,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QACzD,OAAO;YACL,MAAM,EAAU,IAAI,CAAC,MAAM,CAAC,MAAM;YAClC,KAAK,EAAW,IAAI,CAAC,KAAK;YAC1B,SAAS;YACT,cAAc;YACd,WAAW,EAAK,cAAc,IAAI,kBAAkB,GAAG,GAAG;YAC1D,WAAW,EAAK,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,aAAa,CAAC,IAAoB;QACvC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,CAAC,cAAc,CAAC,IAAoB,EAAE,QAAQ,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI;QACnE,MAAM,SAAS,GAAG,YAAY,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QACnD,OAAO,SAAS,GAAG,CAAC,IAAI,SAAS,IAAI,QAAQ,CAAC;IAChD,CAAC;CACF;AAvID,oCAuIC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-protocol/sdk",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.2",
|
|
4
4
|
"description": "Programmable trust infrastructure for autonomous AI agents on Sui. Give agents your rules, not your keys.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -44,4 +44,4 @@
|
|
|
44
44
|
"ts-node": "^10.9.2",
|
|
45
45
|
"typescript": "^5.9.3"
|
|
46
46
|
}
|
|
47
|
-
}
|
|
47
|
+
}
|