@agether/agether 2.6.0 → 2.6.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/package.json +1 -1
- package/skills/agether/SKILL.md +49 -25
- package/src/index.ts +31 -1
package/package.json
CHANGED
package/skills/agether/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agether
|
|
3
|
-
description: On-chain credit protocol for AI agents
|
|
3
|
+
description: On-chain credit protocol for AI agents. Morpho credit & lending, x402 payments, ERC-8004 identity.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Agether — On-Chain Credit for AI Agents
|
|
@@ -26,11 +26,11 @@ If you don't paste it, the user sees NOTHING. An empty colon ":" with no data af
|
|
|
26
26
|
1. **PASTE tool results into your reply.** Every tool returns JSON. Extract the key fields and write them in your message. The user cannot see tool output directly.
|
|
27
27
|
2. **ALWAYS paste tx links.** Tools return `tx` field like `https://basescan.org/tx/0x...`. Copy it verbatim into your reply.
|
|
28
28
|
3. **After on-chain actions, call `agether_balance` and paste the balances.**
|
|
29
|
-
4. **On first message, call `agether_health`.** This is your single best "context loader" — it returns balances, positions, LTV, alerts, and headroom in one call. If `
|
|
29
|
+
4. **On first message, call `agether_health`.** This is your single best "context loader" — it returns balances, positions, LTV, alerts, and headroom in one call. If `chain` is `"?"`, follow the MANDATORY BOOT SEQUENCE above — set chain FIRST, then agent ID.
|
|
30
30
|
5. **Be proactive** — if the user asks to call a paid API, do the full flow without asking.
|
|
31
31
|
6. **Never ask for private keys** — they come from OpenClaw secrets (AGETHER_PRIVATE_KEY env var).
|
|
32
32
|
7. **Max LTV is 80%** (125% collateral ratio). To borrow $X, you need $X × 1.25 in collateral value.
|
|
33
|
-
8. **When user says "register" → ALWAYS call `agether_register`.** A wallet CAN have multiple ERC-8004 identities. The tool handles everything. Never refuse to register because the wallet "already has an identity". Just call the tool.
|
|
33
|
+
8. **When user says "register" → ALWAYS call `agether_register`.** But ONLY after chain is set (see MANDATORY BOOT SEQUENCE). A wallet CAN have multiple ERC-8004 identities. The tool handles everything. Never refuse to register because the wallet "already has an identity". Just call the tool.
|
|
34
34
|
9. **Never guess about blockchain state.** If you're unsure, call the tool. Don't tell the user something is impossible — try it first.
|
|
35
35
|
10. **Your EOA wallet IS the user's wallet.** Same private key, same address. If the user says "I have WETH", call `agether_balance` — you'll see it in the `collateral` field. You already have access to those tokens.
|
|
36
36
|
11. **`morpho_deposit` and `morpho_deposit_and_borrow` transfer collateral from your EOA automatically.** You do NOT need anyone to "send you" tokens. If your EOA has WETH, just call the deposit tool directly. The SDK handles EOA→Safe account→Morpho in one flow.
|
|
@@ -43,39 +43,63 @@ If you don't paste it, the user sees NOTHING. An empty colon ":" with no data af
|
|
|
43
43
|
|
|
44
44
|
---
|
|
45
45
|
|
|
46
|
-
##
|
|
46
|
+
## 🚫🚫🚫 MANDATORY BOOT SEQUENCE — READ THIS FIRST 🚫🚫🚫
|
|
47
47
|
|
|
48
|
-
|
|
48
|
+
There is a **strict ordering requirement**. Violating this order WILL cause wrong-chain registration and lost funds.
|
|
49
|
+
|
|
50
|
+
### STEP 1 → CHAIN (must be first, ALWAYS, no exceptions)
|
|
51
|
+
|
|
52
|
+
**You MUST set the chain BEFORE you do ANYTHING else — before `agether_set_agent`, before `agether_register`, before ANY on-chain tool call.**
|
|
53
|
+
|
|
54
|
+
An `agentId` is **chain-specific**. Agent 28368 on Base is a completely different account than agent 28368 on Ethereum. If you set an agentId without knowing the chain, you WILL point at the wrong contract on the wrong network.
|
|
55
|
+
|
|
56
|
+
**NEVER call `agether_set_agent` before the chain is confirmed. NEVER.**
|
|
49
57
|
|
|
50
58
|
```
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
→ IF user says no → call `agether_register(name: "<ask name>")`
|
|
56
|
-
3. IF no agentId in memory:
|
|
57
|
-
→ call `agether_register(name: "<ask name>")`
|
|
58
|
-
4. After either path → call `agether_health` to confirm everything is working
|
|
59
|
+
IF agether_health returns chain: "?"
|
|
60
|
+
→ Ask user: "Which chain — Ethereum or Base?"
|
|
61
|
+
→ Call agether_set_chain(chain: "<ethereum or base>")
|
|
62
|
+
→ Call agether_health again to confirm chain is set (not "?")
|
|
59
63
|
```
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
`agether_set_chain` accepts: `ethereum`, `eth`, `1`, `base`, `8453`.
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
### STEP 2 → AGENT ID (ONLY after chain is confirmed, not "?")
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
**NEVER call `agether_set_agent` or `agether_register` until the chain is confirmed (chain ≠ "?").**
|
|
66
70
|
|
|
67
|
-
|
|
71
|
+
Even if you have an agentId cached in memory or config — that agentId belongs to a **specific chain**. Do NOT blindly reuse it. A config value like `"agentId": "28368"` might be from the old deployment or a different chain.
|
|
68
72
|
|
|
69
73
|
```
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
+
IF chain is confirmed AND agentId is "?"
|
|
75
|
+
1. IF you remember an agentId AND know which chain it was for:
|
|
76
|
+
→ Ask: "I remember agent <X> on <CHAIN>. Is this still correct?"
|
|
77
|
+
→ IF yes AND chain matches → agether_set_agent(agentId: "<X>")
|
|
78
|
+
→ IF no → agether_register(name: "<ask>")
|
|
79
|
+
2. IF no memory of agentId:
|
|
80
|
+
→ agether_register(name: "<ask>")
|
|
81
|
+
3. Call agether_health to confirm
|
|
74
82
|
```
|
|
75
83
|
|
|
76
|
-
|
|
84
|
+
### ⛔ FORBIDDEN (will cause bugs)
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
- ❌ `agether_set_agent` → then `agether_set_chain` — **WRONG ORDER, agent points at wrong chain**
|
|
87
|
+
- ❌ `agether_register` before chain is set — **WILL FAIL**
|
|
88
|
+
- ❌ Reusing a cached agentId without confirming which chain it belongs to — **WRONG ACCOUNT**
|
|
89
|
+
- ❌ Seeing `"agentId": "28368"` in config and assuming it's valid — it might be stale or from a different chain
|
|
90
|
+
- ❌ Calling `agether_set_agent` just because config has an agentId — **ASK THE USER FIRST**
|
|
91
|
+
|
|
92
|
+
### ✅ CORRECT SEQUENCE (always follow this)
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
1. agether_health → check chain and agentId status
|
|
96
|
+
2. agether_set_chain → if chain is "?", ask user and set it FIRST
|
|
97
|
+
3. agether_health → confirm chain is now set
|
|
98
|
+
4. agether_set_agent OR agether_register → only NOW, after chain is confirmed
|
|
99
|
+
5. agether_health → final confirmation that both chain + agentId are set
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Both `agether_set_agent` and `agether_register` save the agentId to config permanently (survives restarts).
|
|
79
103
|
|
|
80
104
|
---
|
|
81
105
|
|
|
@@ -176,11 +200,11 @@ When `autoYield` and/or `autoDraw` are enabled, `x402_pay` automatically sources
|
|
|
176
200
|
```
|
|
177
201
|
1. agether_health ← ONE call for full context
|
|
178
202
|
2. Check the "chain" field:
|
|
179
|
-
→ If chain is "?" →
|
|
203
|
+
→ If chain is "?" → STOP. Set chain first (MANDATORY BOOT SEQUENCE). Do NOT proceed until chain is confirmed.
|
|
180
204
|
3. Check the "alerts" array:
|
|
181
205
|
→ If any 🔴 alerts → WARN user immediately (liquidation risk!)
|
|
182
206
|
→ If any 🟡 alerts → mention the risk casually
|
|
183
|
-
→ If "agentId: ?" →
|
|
207
|
+
→ If "agentId: ?" → set agent ID (but only AFTER chain is confirmed — see MANDATORY BOOT SEQUENCE)
|
|
184
208
|
4. Now you have full context to handle any request
|
|
185
209
|
```
|
|
186
210
|
|
package/src/index.ts
CHANGED
|
@@ -175,6 +175,20 @@ function getConfig(api: any): PluginConfig {
|
|
|
175
175
|
let cachedAgentId: string | undefined;
|
|
176
176
|
let activeChainId: ChainId = ChainId.Ethereum;
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Hard guardrail: refuse to proceed if chain was never explicitly configured.
|
|
180
|
+
* Prevents silent default-to-Ethereum when user hasn't chosen a chain.
|
|
181
|
+
* Throws an error that the LLM sees, forcing it to call agether_set_chain first.
|
|
182
|
+
*/
|
|
183
|
+
function requireChain(cfg: PluginConfig): void {
|
|
184
|
+
if (!cfg.chainConfigured) {
|
|
185
|
+
throw new Error(
|
|
186
|
+
"Chain not configured. Ask the user which chain to use (Ethereum or Base), " +
|
|
187
|
+
"then call agether_set_chain before proceeding."
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
178
192
|
function createClient(cfg: PluginConfig): MorphoClient {
|
|
179
193
|
const agentId = cachedAgentId || cfg.agentId;
|
|
180
194
|
return new MorphoClient({
|
|
@@ -258,7 +272,8 @@ export default function register(api: any) {
|
|
|
258
272
|
api.registerTool({
|
|
259
273
|
name: "agether_register",
|
|
260
274
|
description:
|
|
261
|
-
"Register a new ERC-8004 agent identity and create a Safe smart account (via Safe7579).
|
|
275
|
+
"Register a new ERC-8004 agent identity and create a Safe smart account (via Safe7579). " +
|
|
276
|
+
"REQUIRES chain to be configured first — call agether_set_chain before this tool. Returns the new agentId.",
|
|
262
277
|
parameters: {
|
|
263
278
|
type: "object",
|
|
264
279
|
properties: {
|
|
@@ -269,6 +284,7 @@ export default function register(api: any) {
|
|
|
269
284
|
async execute(_id: string, params: { name: string }) {
|
|
270
285
|
try {
|
|
271
286
|
const cfg = getConfig(api);
|
|
287
|
+
requireChain(cfg);
|
|
272
288
|
const client = createClient(cfg);
|
|
273
289
|
const result = await client.register(params.name);
|
|
274
290
|
const persistStatus = persistAgentId(result.agentId);
|
|
@@ -417,6 +433,7 @@ export default function register(api: any) {
|
|
|
417
433
|
async execute(_id: string, params: { registryAddress: string }) {
|
|
418
434
|
try {
|
|
419
435
|
const cfg = getConfig(api);
|
|
436
|
+
requireChain(cfg);
|
|
420
437
|
const client = createClient(cfg);
|
|
421
438
|
|
|
422
439
|
// Build the calldata for ERC8004ValidationModule.setValidationRegistry — the owner (Timelock) must execute it
|
|
@@ -526,6 +543,7 @@ export default function register(api: any) {
|
|
|
526
543
|
async execute(_id: string, params: { amount: string; token: string }) {
|
|
527
544
|
try {
|
|
528
545
|
const cfg = getConfig(api);
|
|
546
|
+
requireChain(cfg);
|
|
529
547
|
const client = createClient(cfg);
|
|
530
548
|
const result = await client.supplyCollateral(params.token, params.amount);
|
|
531
549
|
return ok(JSON.stringify({
|
|
@@ -558,6 +576,7 @@ export default function register(api: any) {
|
|
|
558
576
|
async execute(_id: string, params: { collateralAmount: string; token: string; borrowAmount: string }) {
|
|
559
577
|
try {
|
|
560
578
|
const cfg = getConfig(api);
|
|
579
|
+
requireChain(cfg);
|
|
561
580
|
const client = createClient(cfg);
|
|
562
581
|
const result = await client.depositAndBorrow(
|
|
563
582
|
params.token,
|
|
@@ -597,6 +616,7 @@ export default function register(api: any) {
|
|
|
597
616
|
try {
|
|
598
617
|
if (!params.agentId && !params.agentAddress) return fail("Provide either agentId or agentAddress");
|
|
599
618
|
const cfg = getConfig(api);
|
|
619
|
+
requireChain(cfg);
|
|
600
620
|
const client = createClient(cfg);
|
|
601
621
|
|
|
602
622
|
const target = params.agentId
|
|
@@ -634,6 +654,7 @@ export default function register(api: any) {
|
|
|
634
654
|
async execute(_id: string, params: { amount: string; token?: string }) {
|
|
635
655
|
try {
|
|
636
656
|
const cfg = getConfig(api);
|
|
657
|
+
requireChain(cfg);
|
|
637
658
|
const client = createClient(cfg);
|
|
638
659
|
const result = await client.borrow(params.amount, params.token);
|
|
639
660
|
return ok(JSON.stringify({
|
|
@@ -666,6 +687,7 @@ export default function register(api: any) {
|
|
|
666
687
|
async execute(_id: string, params: { amount: string; token?: string }) {
|
|
667
688
|
try {
|
|
668
689
|
const cfg = getConfig(api);
|
|
690
|
+
requireChain(cfg);
|
|
669
691
|
const client = createClient(cfg);
|
|
670
692
|
const result = await client.repay(params.amount, params.token);
|
|
671
693
|
return ok(JSON.stringify({
|
|
@@ -699,6 +721,7 @@ export default function register(api: any) {
|
|
|
699
721
|
async execute(_id: string, params: { amount: string; token: string; toEoa?: boolean }) {
|
|
700
722
|
try {
|
|
701
723
|
const cfg = getConfig(api);
|
|
724
|
+
requireChain(cfg);
|
|
702
725
|
const client = createClient(cfg);
|
|
703
726
|
// Default: keep in AgentAccount. toEoa=true → send to EOA.
|
|
704
727
|
const receiver = params.toEoa ? await client.getSignerAddress() : await client.getAccountAddress();
|
|
@@ -739,6 +762,7 @@ export default function register(api: any) {
|
|
|
739
762
|
async execute(_id: string, params: { amount: string; market?: string }) {
|
|
740
763
|
try {
|
|
741
764
|
const cfg = getConfig(api);
|
|
765
|
+
requireChain(cfg);
|
|
742
766
|
const client = createClient(cfg);
|
|
743
767
|
const result = await client.supplyAsset(params.amount, params.market);
|
|
744
768
|
return ok(JSON.stringify({
|
|
@@ -831,6 +855,7 @@ export default function register(api: any) {
|
|
|
831
855
|
async execute(_id: string, params: { amount: string; market?: string; toEoa?: boolean }) {
|
|
832
856
|
try {
|
|
833
857
|
const cfg = getConfig(api);
|
|
858
|
+
requireChain(cfg);
|
|
834
859
|
const client = createClient(cfg);
|
|
835
860
|
// Default: keep in AgentAccount. toEoa=true → send to EOA.
|
|
836
861
|
const receiver = params.toEoa ? await client.getSignerAddress() : await client.getAccountAddress();
|
|
@@ -903,6 +928,7 @@ export default function register(api: any) {
|
|
|
903
928
|
async execute(_id: string, params: { refresh?: boolean }) {
|
|
904
929
|
try {
|
|
905
930
|
const cfg = getConfig(api);
|
|
931
|
+
requireChain(cfg);
|
|
906
932
|
const client = createClient(cfg);
|
|
907
933
|
const agentId = client.getAgentId();
|
|
908
934
|
|
|
@@ -952,6 +978,7 @@ export default function register(api: any) {
|
|
|
952
978
|
async execute(_id: string, params: { amount: string }) {
|
|
953
979
|
try {
|
|
954
980
|
const cfg = getConfig(api);
|
|
981
|
+
requireChain(cfg);
|
|
955
982
|
const client = createClient(cfg);
|
|
956
983
|
const result = await client.fundAccount(params.amount);
|
|
957
984
|
return ok(JSON.stringify({
|
|
@@ -984,6 +1011,7 @@ export default function register(api: any) {
|
|
|
984
1011
|
async execute(_id: string, params: { token: string; amount: string }) {
|
|
985
1012
|
try {
|
|
986
1013
|
const cfg = getConfig(api);
|
|
1014
|
+
requireChain(cfg);
|
|
987
1015
|
const client = createClient(cfg);
|
|
988
1016
|
const result = await client.withdrawToken(params.token, params.amount);
|
|
989
1017
|
return ok(JSON.stringify({
|
|
@@ -1015,6 +1043,7 @@ export default function register(api: any) {
|
|
|
1015
1043
|
async execute(_id: string, params: { amount: string }) {
|
|
1016
1044
|
try {
|
|
1017
1045
|
const cfg = getConfig(api);
|
|
1046
|
+
requireChain(cfg);
|
|
1018
1047
|
const client = createClient(cfg);
|
|
1019
1048
|
const result = await client.withdrawEth(params.amount);
|
|
1020
1049
|
return ok(JSON.stringify({
|
|
@@ -1049,6 +1078,7 @@ export default function register(api: any) {
|
|
|
1049
1078
|
async execute(_id: string, params: { url: string; method?: string; body?: string }) {
|
|
1050
1079
|
try {
|
|
1051
1080
|
const cfg = getConfig(api);
|
|
1081
|
+
requireChain(cfg);
|
|
1052
1082
|
const agetherCfg = api.config?.plugins?.entries?.agether?.config || {};
|
|
1053
1083
|
const client = createClient(cfg);
|
|
1054
1084
|
|