@decentrys/agent 0.1.0 → 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.
- package/README.md +121 -28
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# @decentrys/agent
|
|
2
2
|
|
|
3
|
-
AgentGuard:
|
|
3
|
+
**AgentGuard: stops an autonomous agent doing something its operator never authorised.**
|
|
4
|
+
|
|
5
|
+
## Why this exists separately from @decentrys/protect
|
|
6
|
+
|
|
7
|
+
Protect informs a human, who then decides. An agent has no judgement and will do
|
|
8
|
+
exactly what it is told, at machine speed, repeatedly. A person seeing
|
|
9
|
+
"unlimited approval requested" may reconsider; an agent will not.
|
|
10
|
+
|
|
11
|
+
So where Protect warns and never blocks, AgentGuard exists to be able to
|
|
12
|
+
**stop** an action — under a policy its operator set in advance.
|
|
4
13
|
|
|
5
14
|
## Install
|
|
6
15
|
|
|
@@ -8,55 +17,139 @@ AgentGuard: policy enforcement in front of an autonomous on-chain agent.
|
|
|
8
17
|
npm install @decentrys/agent @decentrys/protect
|
|
9
18
|
```
|
|
10
19
|
|
|
11
|
-
##
|
|
20
|
+
## Getting an API key
|
|
12
21
|
|
|
13
|
-
|
|
14
|
-
do exactly what it is told, at machine speed, repeatedly. A person seeing
|
|
15
|
-
"unlimited approval requested" may reconsider; an agent will not.
|
|
22
|
+
Sign in at [decentrys.com/developers](https://decentrys.com/developers) and create a key.
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
There are two kinds, and picking the wrong one is the mistake that matters:
|
|
25
|
+
|
|
26
|
+
| Prefix | Where it belongs | Why |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `dk_pub_live_…` | **Publishable.** Ships inside a wallet, extension or mobile app. | Bounded to the origins you register and to read-only Protect endpoints. Anyone can extract it from your bundle; that's expected, and it's why it can't do anything dangerous. |
|
|
29
|
+
| `dk_live_…` | **Secret.** Server-side only. | Full scope access. If this ends up in a client bundle it is a leaked credential the moment it ships. |
|
|
30
|
+
|
|
31
|
+
**Secret key only.**
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
19
34
|
|
|
20
35
|
```ts
|
|
36
|
+
import { AgentGuard, sealPolicy } from '@decentrys/agent';
|
|
37
|
+
|
|
38
|
+
// The operator sets this, ahead of time. The agent never sees a mutable copy.
|
|
21
39
|
const policy = sealPolicy({
|
|
22
40
|
maxValuePerActionUsd: 5_000,
|
|
23
|
-
cumulative: [{ windowMs: 86_400_000, maxUsd: 25_000 }],
|
|
24
|
-
|
|
41
|
+
cumulative: [{ windowMs: 86_400_000, maxUsd: 25_000 }], // $25k/day
|
|
42
|
+
rate: [{ windowMs: 60_000, maxActions: 10 }], // 10 actions/min
|
|
43
|
+
allowedActions: ['swap', 'transfer'], // may swap, may NOT approve
|
|
44
|
+
allowedChains: ['ethereum', 'base'],
|
|
45
|
+
blockedCounterparties: ['0xbad...'],
|
|
25
46
|
allowUnlimitedApproval: false,
|
|
47
|
+
maxRiskLevel: 'CAUTION',
|
|
48
|
+
requireHumanApprovalAboveUsd: 10_000,
|
|
49
|
+
failMode: 'escalate',
|
|
26
50
|
});
|
|
27
51
|
|
|
28
52
|
const guard = new AgentGuard({ apiKey: 'dk_live_...', policy });
|
|
29
|
-
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## In your agent's execution loop
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
const decision = await guard.assessAgentTransaction({
|
|
59
|
+
chain: 'ethereum',
|
|
60
|
+
from: agentWallet,
|
|
61
|
+
to: routerAddress,
|
|
62
|
+
actionType: 'swap',
|
|
63
|
+
valueUsd: 2_500,
|
|
64
|
+
idempotencyKey: taskId,
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
switch (decision.verdict) {
|
|
68
|
+
case 'allow':
|
|
69
|
+
try { await signAndSend(); await guard.confirm(decision); }
|
|
70
|
+
catch { await guard.release(decision); } // hand budget back if it didn't happen
|
|
71
|
+
break;
|
|
30
72
|
|
|
31
|
-
|
|
32
|
-
decision
|
|
73
|
+
case 'require_human_approval':
|
|
74
|
+
await notifyOperator(guard.explain(decision));
|
|
75
|
+
break;
|
|
76
|
+
|
|
77
|
+
case 'deny':
|
|
78
|
+
await log(guard.explain(decision));
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
33
81
|
```
|
|
34
82
|
|
|
83
|
+
Three verdicts, not two. Protect's `warn` has no recipient here — there's no
|
|
84
|
+
human watching — so it's refused at `sealPolicy` rather than silently
|
|
85
|
+
reinterpreted.
|
|
86
|
+
|
|
35
87
|
## The agent cannot widen its own limits
|
|
36
88
|
|
|
37
|
-
A sealed policy is branded with a runtime symbol
|
|
38
|
-
|
|
39
|
-
`narrowPolicy` only ever
|
|
89
|
+
A sealed policy is branded with a runtime symbol, deep-frozen, and your arrays
|
|
90
|
+
are cloned away — an operator holding a reference can't grow a live allowlist.
|
|
91
|
+
**There is no widening function in this package.** `narrowPolicy` only ever
|
|
92
|
+
tightens: min of caps, intersection of allowlists, union of blocklists.
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const restricted = guard.withPolicy(narrowPolicy(policy, { maxValuePerActionUsd: 500 }));
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Every allow is explainable afterwards
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
decision.evaluations // EVERY rule considered, passes included
|
|
102
|
+
guard.explain(decision)
|
|
103
|
+
guard.auditLog()
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Evaluation never short-circuits, so a record can't say "exceeded its cap" while
|
|
107
|
+
omitting "and the counterparty was a confirmed drainer". `not_configured` is
|
|
108
|
+
distinguished from `not_applicable`, so you can see which axes your policy left
|
|
109
|
+
**unbounded** — the difference between "the rate ceiling was checked" and "there
|
|
110
|
+
is no rate ceiling".
|
|
111
|
+
|
|
112
|
+
Decisions carry a policy fingerprint and action digest, and `evaluatePolicy` is
|
|
113
|
+
pure and clock-injected, so a recorded decision replays to the same answer.
|
|
40
114
|
|
|
41
|
-
##
|
|
115
|
+
## failMode defaults to `escalate`
|
|
42
116
|
|
|
43
117
|
`open` signs unscreened during exactly the window an attacker would choose.
|
|
44
|
-
`closed` halts the agent entirely
|
|
45
|
-
|
|
46
|
-
attention instead of the treasury.
|
|
47
|
-
|
|
118
|
+
`closed` halts the agent entirely — for a liquidation agent that's its own loss,
|
|
119
|
+
and it pressures operators into choosing `open`. `escalate` spends a human's
|
|
120
|
+
attention instead of the treasury.
|
|
121
|
+
|
|
122
|
+
An outage removes one input but **never suspends your limits**: a local `deny`
|
|
123
|
+
still denies under `failMode: 'open'`.
|
|
124
|
+
|
|
125
|
+
## Two behaviours worth knowing
|
|
126
|
+
|
|
127
|
+
**An unpriced action is denied** when any value rule exists. Pass `valueUsd: 0`
|
|
128
|
+
explicitly if an action genuinely moves nothing. For an autonomous signer, "we
|
|
129
|
+
couldn't price it" must not resolve to "so we signed it".
|
|
48
130
|
|
|
49
|
-
|
|
131
|
+
**An empty allowlist denies everything**, rather than reading as unconfigured.
|
|
50
132
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
133
|
+
## Known limit
|
|
134
|
+
|
|
135
|
+
The bundled `SpendLedger` is **in-process and in-memory**. Two replicas each
|
|
136
|
+
enforcing $10,000/day enforce $20,000, and a restart resets the window. Swap in
|
|
137
|
+
shared storage via the two-method `AgentUsage` interface before running a fleet —
|
|
138
|
+
`guard.reservesBudget` reports whether you got a real one.
|
|
139
|
+
|
|
140
|
+
## The rest of the SDK
|
|
141
|
+
|
|
142
|
+
| Package | For |
|
|
143
|
+
|---|---|
|
|
144
|
+
| [`@decentrys/protect`](https://www.npmjs.com/package/@decentrys/protect) | Pre-sign risk assessment for wallets and dapps |
|
|
145
|
+
| [`@decentrys/ui-sdk`](https://www.npmjs.com/package/@decentrys/ui-sdk) | React components that render Protect results |
|
|
146
|
+
| [`@decentrys/sentinel-sdk`](https://www.npmjs.com/package/@decentrys/sentinel-sdk) | Monitoring deployed contracts and treasuries |
|
|
147
|
+
| [`@decentrys/risk-sdk`](https://www.npmjs.com/package/@decentrys/risk-sdk) | Screening for exchanges and custodians |
|
|
148
|
+
| [`@decentrys/dri-sdk`](https://www.npmjs.com/package/@decentrys/dri-sdk) | Fund tracing and recovery intelligence |
|
|
149
|
+
| [`@decentrys/agent`](https://www.npmjs.com/package/@decentrys/agent) | Policy enforcement for autonomous agents |
|
|
54
150
|
|
|
55
151
|
## Licence
|
|
56
152
|
|
|
57
153
|
MIT © Decentrys Labs
|
|
58
154
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- [decentrys.com](https://decentrys.com) · [SDK overview](https://decentrys.com/sdk) · [Developer API](https://decentrys.com/developers)
|
|
62
|
-
- Source: [github.com/teamdecentrys-byte/Decentrys](https://github.com/teamdecentrys-byte/Decentrys)
|
|
155
|
+
[decentrys.com](https://decentrys.com) · [SDK overview](https://decentrys.com/sdk) · [Developer API](https://decentrys.com/developers) · [Source](https://github.com/teamdecentrys-byte/Decentrys)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decentrys/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "AgentGuard: policy enforcement in front of an autonomous on-chain agent. An agent cannot widen the limits its operator set.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Decentrys Labs",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"README.md"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@decentrys/protect": "0.1.
|
|
52
|
+
"@decentrys/protect": "0.1.1"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"typescript": "^5.7.2",
|