@nookplot/runtime 0.5.69 → 0.5.71
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/dist/__tests__/autonomous.actionDispatch.test.js +247 -523
- package/dist/__tests__/autonomous.actionDispatch.test.js.map +1 -1
- package/dist/__tests__/autonomous.lifecycle.test.js +8 -5
- package/dist/__tests__/autonomous.lifecycle.test.js.map +1 -1
- package/dist/autonomous.d.ts.map +1 -1
- package/dist/autonomous.js +58 -1726
- package/dist/autonomous.js.map +1 -1
- package/dist/gpu.d.ts +35 -0
- package/dist/gpu.d.ts.map +1 -1
- package/dist/gpu.js +56 -0
- package/dist/gpu.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,563 +1,287 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Action dispatch tests for AutonomousAgent.
|
|
2
|
+
* Action dispatch tests for AutonomousAgent — unified dispatch via gateway.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* After the thin dispatch refactor, all actions go through
|
|
5
|
+
* `POST /v1/actions/execute { toolName, payload }`. These tests verify:
|
|
6
|
+
* - Correct toolName + payload are sent to the gateway
|
|
7
|
+
* - `completed` results are handled
|
|
8
|
+
* - `sign_required` results trigger local signing + relay
|
|
9
|
+
* - `client_side_required` results are handled locally
|
|
10
|
+
* - `error` results reject delegated actions
|
|
11
|
+
* - Approval gate still works for on-chain actions
|
|
12
|
+
* - actionId lifecycle (completeAction / rejectDelegatedAction)
|
|
6
13
|
*/
|
|
7
14
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
8
15
|
import { createMockRuntime } from "./helpers/mockRuntime.js";
|
|
9
16
|
import { AutonomousAgent } from "../autonomous.js";
|
|
10
|
-
// Mock
|
|
17
|
+
// Mock the signing module
|
|
11
18
|
vi.mock("../signing.js", () => ({
|
|
12
19
|
prepareSignRelay: vi.fn().mockResolvedValue({ txHash: "0xRELAY_TX" }),
|
|
13
|
-
signForwardRequest: vi.fn(),
|
|
20
|
+
signForwardRequest: vi.fn().mockResolvedValue("0xSIGNATURE"),
|
|
14
21
|
}));
|
|
15
|
-
import { prepareSignRelay } from "../signing.js";
|
|
16
|
-
const mockPrepareSignRelay = vi.mocked(prepareSignRelay);
|
|
17
22
|
let runtime;
|
|
18
23
|
let callbacks;
|
|
19
24
|
let agent;
|
|
25
|
+
let requestMock;
|
|
20
26
|
beforeEach(() => {
|
|
21
27
|
vi.clearAllMocks();
|
|
22
28
|
const mock = createMockRuntime();
|
|
23
29
|
runtime = mock.runtime;
|
|
24
30
|
callbacks = mock.callbacks;
|
|
31
|
+
// Default mock: return `completed` for POST /v1/actions/execute
|
|
32
|
+
requestMock = runtime.connection.request;
|
|
33
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
34
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
35
|
+
return { status: "completed", result: { success: true } };
|
|
36
|
+
}
|
|
37
|
+
return { success: true };
|
|
38
|
+
});
|
|
25
39
|
agent = new AutonomousAgent(runtime, { verbose: false });
|
|
26
40
|
agent.start();
|
|
27
41
|
});
|
|
28
42
|
/** Helper: dispatch an action request and wait for processing */
|
|
29
43
|
async function dispatchAction(actionType, payload, suggestedContent, actionId) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// Allow async handler to settle
|
|
33
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
44
|
+
callbacks.actionCb({ agentId: "agent_1", actionType, payload, suggestedContent, actionId });
|
|
45
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
34
46
|
}
|
|
35
|
-
// ──
|
|
36
|
-
describe("
|
|
37
|
-
it("
|
|
38
|
-
await dispatchAction("
|
|
39
|
-
expect(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
57
|
-
it("attest_agent calls social.attest", async () => {
|
|
58
|
-
await dispatchAction("attest_agent", { targetAddress: "0xTARGET" }, "Great work!");
|
|
59
|
-
expect(runtime.social.attest).toHaveBeenCalledWith("0xTARGET", "Great work!");
|
|
60
|
-
});
|
|
61
|
-
it("list_service calls marketplace.createListing", async () => {
|
|
62
|
-
await dispatchAction("list_service", { category: "dev" }, "Build APIs");
|
|
63
|
-
expect(runtime.marketplace.createListing).toHaveBeenCalledWith(expect.objectContaining({ title: "Build APIs", category: "dev" }));
|
|
64
|
-
});
|
|
65
|
-
it("create_agreement calls marketplace.createAgreement", async () => {
|
|
66
|
-
await dispatchAction("create_agreement", { listingId: 42 }, "Build me an API");
|
|
67
|
-
expect(runtime.marketplace.createAgreement).toHaveBeenCalledWith(expect.objectContaining({ listingId: 42, terms: "Build me an API" }));
|
|
68
|
-
});
|
|
69
|
-
it("deliver_work calls marketplace.deliver", async () => {
|
|
70
|
-
await dispatchAction("deliver_work", { agreementId: 5 }, "Qm_delivery_cid");
|
|
71
|
-
expect(runtime.marketplace.deliver).toHaveBeenCalledWith(5, "Qm_delivery_cid");
|
|
72
|
-
});
|
|
73
|
-
it("settle_agreement calls marketplace.settle", async () => {
|
|
74
|
-
await dispatchAction("settle_agreement", { agreementId: 5 });
|
|
75
|
-
expect(runtime.marketplace.settle).toHaveBeenCalledWith(5);
|
|
76
|
-
});
|
|
77
|
-
it("dispute_agreement calls marketplace.dispute", async () => {
|
|
78
|
-
await dispatchAction("dispute_agreement", { agreementId: 5 });
|
|
79
|
-
expect(runtime.marketplace.dispute).toHaveBeenCalledWith(5);
|
|
80
|
-
});
|
|
81
|
-
it("cancel_agreement calls marketplace.cancel", async () => {
|
|
82
|
-
await dispatchAction("cancel_agreement", { agreementId: 5 });
|
|
83
|
-
expect(runtime.marketplace.cancel).toHaveBeenCalledWith(5);
|
|
84
|
-
});
|
|
85
|
-
it("expire_dispute calls marketplace.expireDispute", async () => {
|
|
86
|
-
await dispatchAction("expire_dispute", { agreementId: 5 });
|
|
87
|
-
expect(runtime.marketplace.expireDispute).toHaveBeenCalledWith(5);
|
|
88
|
-
});
|
|
89
|
-
it("expire_delivered calls marketplace.expireDelivered", async () => {
|
|
90
|
-
await dispatchAction("expire_delivered", { agreementId: 5 });
|
|
91
|
-
expect(runtime.marketplace.expireDelivered).toHaveBeenCalledWith(5);
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
// ── On-chain via prepareSignRelay ────────────────────────────
|
|
95
|
-
describe("on-chain actions via prepareSignRelay", () => {
|
|
96
|
-
it("create_community calls prepareSignRelay with /v1/prepare/community", async () => {
|
|
97
|
-
await dispatchAction("create_community", { slug: "ai", name: "AI Agents" }, "An AI community");
|
|
98
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/community", expect.objectContaining({ slug: "ai", name: "AI Agents" }));
|
|
99
|
-
});
|
|
100
|
-
it("propose_guild calls prepareSignRelay with /v1/prepare/guild", async () => {
|
|
101
|
-
await dispatchAction("propose_guild", { name: "BuilderGuild", members: ["0xA", "0xB", "0xC"] }, "A builder guild");
|
|
102
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/guild", expect.objectContaining({ name: "BuilderGuild", members: ["0xA", "0xB", "0xC"] }));
|
|
103
|
-
});
|
|
104
|
-
it("propose_clique is alias for propose_guild", async () => {
|
|
105
|
-
await dispatchAction("propose_clique", { name: "Clique", members: ["0xA", "0xB"] }, "desc");
|
|
106
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/guild", expect.anything());
|
|
107
|
-
});
|
|
108
|
-
it("create_project does 2-step discover + prepare", async () => {
|
|
109
|
-
vi.mocked(runtime.connection.request).mockResolvedValueOnce({ discoveryId: "disc_1" });
|
|
110
|
-
await dispatchAction("create_project", { projectId: "proj_1", name: "My Project" }, "Project desc");
|
|
111
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/projects/discover", expect.anything());
|
|
112
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/project", expect.objectContaining({ discoveryId: "disc_1", projectId: "proj_1" }));
|
|
113
|
-
});
|
|
114
|
-
it("claim_bounty calls prepareSignRelay with /v1/prepare/bounty/:id/claim", async () => {
|
|
115
|
-
await dispatchAction("claim_bounty", { bountyId: "42" });
|
|
116
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/42/claim", {});
|
|
117
|
-
});
|
|
118
|
-
it("claim is alias for claim_bounty", async () => {
|
|
119
|
-
await dispatchAction("claim", { bountyId: "99" });
|
|
120
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/99/claim", {});
|
|
121
|
-
});
|
|
122
|
-
it("create_bounty calls prepareSignRelay with /v1/prepare/bounty", async () => {
|
|
123
|
-
await dispatchAction("create_bounty", { tokenRewardAmount: "100" }, "Fix the bug");
|
|
124
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty", expect.objectContaining({ title: "Fix the bug", tokenRewardAmount: "100" }));
|
|
125
|
-
});
|
|
126
|
-
it("create_bundle calls prepareSignRelay with /v1/prepare/bundle", async () => {
|
|
127
|
-
await dispatchAction("create_bundle", { name: "Research Bundle" });
|
|
128
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bundle", expect.objectContaining({ name: "Research Bundle" }));
|
|
129
|
-
});
|
|
130
|
-
it("approve_bounty_claimer calls prepareSignRelay with /v1/prepare/bounty/:id/approve-claimer", async () => {
|
|
131
|
-
await dispatchAction("approve_bounty_claimer", { bountyId: "10", claimer: "0xCLAIMER" });
|
|
132
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/10/approve-claimer", { claimer: "0xCLAIMER" });
|
|
133
|
-
});
|
|
134
|
-
it("approve_bounty_work calls prepareSignRelay with /v1/prepare/bounty/:id/approve", async () => {
|
|
135
|
-
await dispatchAction("approve_bounty_work", { bountyId: "10" });
|
|
136
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/10/approve", {});
|
|
137
|
-
});
|
|
138
|
-
it("dispute_bounty_work calls prepareSignRelay with /v1/prepare/bounty/:id/dispute", async () => {
|
|
139
|
-
await dispatchAction("dispute_bounty_work", { bountyId: "10" });
|
|
140
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/10/dispute", {});
|
|
141
|
-
});
|
|
142
|
-
it("cancel_bounty calls prepareSignRelay with /v1/prepare/bounty/:id/cancel", async () => {
|
|
143
|
-
await dispatchAction("cancel_bounty", { bountyId: "10" });
|
|
144
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/10/cancel", {});
|
|
145
|
-
});
|
|
146
|
-
it("unclaim_bounty calls prepareSignRelay with /v1/prepare/bounty/:id/unclaim", async () => {
|
|
147
|
-
await dispatchAction("unclaim_bounty", { bountyId: "10" });
|
|
148
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/bounty/10/unclaim", {});
|
|
149
|
-
});
|
|
150
|
-
it("deploy_preview calls prepareSignRelay with /v1/prepare/project/:id/deployment", async () => {
|
|
151
|
-
await dispatchAction("deploy_preview", { projectId: "proj_1" });
|
|
152
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/project/proj_1/deployment", expect.objectContaining({ prepaidHours: 2 }));
|
|
153
|
-
});
|
|
154
|
-
it("join_guild / approve_guild calls prepareSignRelay with /v1/prepare/guild/:id/approve", async () => {
|
|
155
|
-
await dispatchAction("join_guild", { guildId: 5 });
|
|
156
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/guild/5/approve", {});
|
|
157
|
-
});
|
|
158
|
-
it("reject_guild calls prepareSignRelay with /v1/prepare/guild/:id/reject", async () => {
|
|
159
|
-
await dispatchAction("reject_guild", { guildId: 5 });
|
|
160
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/guild/5/reject", {});
|
|
161
|
-
});
|
|
162
|
-
it("leave_guild calls prepareSignRelay with /v1/prepare/guild/:id/leave", async () => {
|
|
163
|
-
await dispatchAction("leave_guild", { guildId: 5 });
|
|
164
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/guild/5/leave", {});
|
|
165
|
-
});
|
|
166
|
-
it("update_service calls prepareSignRelay with /v1/prepare/service/update", async () => {
|
|
167
|
-
await dispatchAction("update_service", { listingId: "list_1", title: "Updated" });
|
|
168
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/service/update", expect.objectContaining({ listingId: "list_1" }));
|
|
169
|
-
});
|
|
170
|
-
it("create_listing calls prepareSignRelay with /v1/prepare/service/list", async () => {
|
|
171
|
-
await dispatchAction("create_listing", { title: "Service", category: "dev" });
|
|
172
|
-
expect(mockPrepareSignRelay).toHaveBeenCalledWith(runtime.connection, "/v1/prepare/service/list", expect.anything());
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
// ── Off-chain messaging ──────────────────────────────────────
|
|
176
|
-
describe("off-chain messaging actions", () => {
|
|
177
|
-
it("reply with channelId sends to channel", async () => {
|
|
178
|
-
await dispatchAction("reply", { channelId: "ch_1" }, "Hello channel!");
|
|
179
|
-
expect(runtime.channels.send).toHaveBeenCalledWith("ch_1", "Hello channel!");
|
|
180
|
-
});
|
|
181
|
-
it("reply with to sends DM", async () => {
|
|
182
|
-
await dispatchAction("reply", { to: "0xTARGET" }, "Hello DM!");
|
|
183
|
-
expect(runtime.inbox.send).toHaveBeenCalledWith(expect.objectContaining({ to: "0xTARGET", content: "Hello DM!" }));
|
|
184
|
-
});
|
|
185
|
-
it("send_dm calls inbox.send", async () => {
|
|
186
|
-
await dispatchAction("send_dm", { recipientAddress: "0xTARGET" }, "Hey!");
|
|
187
|
-
expect(runtime.inbox.send).toHaveBeenCalledWith(expect.objectContaining({ to: "0xTARGET", content: "Hey!" }));
|
|
188
|
-
});
|
|
189
|
-
it("send_message with to sends DM", async () => {
|
|
190
|
-
await dispatchAction("send_message", { to: "0xTARGET" }, "Collaboration time!");
|
|
191
|
-
expect(runtime.inbox.send).toHaveBeenCalled();
|
|
192
|
-
});
|
|
193
|
-
it("send_message with channelId sends to channel", async () => {
|
|
194
|
-
await dispatchAction("send_message", { channelId: "ch_2" }, "Hi team!");
|
|
195
|
-
expect(runtime.channels.send).toHaveBeenCalledWith("ch_2", "Hi team!");
|
|
196
|
-
});
|
|
197
|
-
it("acknowledge sends to project channel", async () => {
|
|
198
|
-
await dispatchAction("acknowledge", { projectId: "proj_1" });
|
|
199
|
-
expect(runtime.channels.sendToProject).toHaveBeenCalledWith("proj_1", expect.any(String));
|
|
200
|
-
});
|
|
201
|
-
it("accept sends to project channel", async () => {
|
|
202
|
-
await dispatchAction("accept", { projectId: "proj_1" });
|
|
203
|
-
expect(runtime.channels.sendToProject).toHaveBeenCalledWith("proj_1", expect.any(String));
|
|
204
|
-
});
|
|
205
|
-
it("execute with channelId sends to channel", async () => {
|
|
206
|
-
await dispatchAction("execute", { channelId: "ch_1" }, "Executing task...");
|
|
207
|
-
expect(runtime.channels.send).toHaveBeenCalledWith("ch_1", "Executing task...");
|
|
208
|
-
});
|
|
209
|
-
it("execute with to sends DM", async () => {
|
|
210
|
-
await dispatchAction("execute", { to: "0xTARGET" }, "Done!");
|
|
211
|
-
expect(runtime.inbox.send).toHaveBeenCalled();
|
|
212
|
-
});
|
|
213
|
-
it("propose_collab sends DM", async () => {
|
|
214
|
-
await dispatchAction("propose_collab", { targetAddress: "0xTARGET" }, "Let's work together!");
|
|
215
|
-
expect(runtime.inbox.send).toHaveBeenCalledWith(expect.objectContaining({ to: "0xTARGET", content: "Let's work together!" }));
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
// ── Off-chain social ─────────────────────────────────────────
|
|
219
|
-
describe("off-chain social actions", () => {
|
|
220
|
-
it("follow calls social.follow", async () => {
|
|
47
|
+
// ── Unified dispatch to gateway ──────────────────────────────
|
|
48
|
+
describe("unified dispatch via POST /v1/actions/execute", () => {
|
|
49
|
+
it("dispatches action with correct toolName and payload", async () => {
|
|
50
|
+
await dispatchAction("send_message", { to: "0xTARGET", content: "Hello!" });
|
|
51
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({
|
|
52
|
+
toolName: "nookplot_send_message",
|
|
53
|
+
payload: expect.objectContaining({ to: "0xTARGET", content: "Hello!" }),
|
|
54
|
+
}));
|
|
55
|
+
});
|
|
56
|
+
it("includes suggestedContent in payload when present", async () => {
|
|
57
|
+
await dispatchAction("create_post", { community: "general", title: "Test" }, "Post body here");
|
|
58
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({
|
|
59
|
+
toolName: "nookplot_create_post",
|
|
60
|
+
payload: expect.objectContaining({
|
|
61
|
+
community: "general",
|
|
62
|
+
title: "Test",
|
|
63
|
+
suggestedContent: "Post body here",
|
|
64
|
+
}),
|
|
65
|
+
}));
|
|
66
|
+
});
|
|
67
|
+
it("follows dispatches as nookplot_follow", async () => {
|
|
221
68
|
await dispatchAction("follow", { targetAddress: "0xTARGET" });
|
|
222
|
-
expect(
|
|
223
|
-
});
|
|
224
|
-
it("
|
|
225
|
-
await dispatchAction("
|
|
226
|
-
expect(
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
69
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({ toolName: "nookplot_follow" }));
|
|
70
|
+
});
|
|
71
|
+
it("vote dispatches as nookplot_vote", async () => {
|
|
72
|
+
await dispatchAction("vote", { contentCid: "Qm_cid", isUpvote: true });
|
|
73
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({
|
|
74
|
+
toolName: "nookplot_vote",
|
|
75
|
+
payload: expect.objectContaining({ contentCid: "Qm_cid", isUpvote: true }),
|
|
76
|
+
}));
|
|
77
|
+
});
|
|
78
|
+
it("reply action dispatches correctly", async () => {
|
|
79
|
+
await dispatchAction("reply", { channelId: "ch_1" }, "My reply");
|
|
80
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({
|
|
81
|
+
toolName: "nookplot_reply",
|
|
82
|
+
payload: expect.objectContaining({ channelId: "ch_1", suggestedContent: "My reply" }),
|
|
83
|
+
}));
|
|
235
84
|
});
|
|
236
85
|
});
|
|
237
|
-
// ──
|
|
238
|
-
describe("
|
|
239
|
-
it("
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
await dispatchAction("update_task", { projectId: "proj_1", taskId: "task_1", status: "in_progress", priority: "high" });
|
|
279
|
-
expect(runtime.projects.updateTask).toHaveBeenCalledWith("proj_1", "task_1", expect.objectContaining({ status: "in_progress", priority: "high" }));
|
|
280
|
-
});
|
|
281
|
-
it("link_project_to_guild performs 3-step linking", async () => {
|
|
282
|
-
vi.mocked(runtime.guilds.get).mockResolvedValueOnce({ members: [{ address: "0xMEMBER", status: 2 }] });
|
|
283
|
-
await dispatchAction("link_project_to_guild", { projectId: "proj_1", guildId: 5 });
|
|
284
|
-
expect(runtime.guilds.linkProject).toHaveBeenCalledWith(5, "proj_1");
|
|
285
|
-
expect(runtime.projects.setGuildAttribution).toHaveBeenCalledWith("proj_1", "5");
|
|
286
|
-
expect(runtime.projects.addCollaborator).toHaveBeenCalledWith("proj_1", "0xMEMBER", "editor");
|
|
86
|
+
// ── sign_required handling ──────────────────────────────
|
|
87
|
+
describe("sign_required response handling", () => {
|
|
88
|
+
it("signs and relays when gateway returns sign_required", async () => {
|
|
89
|
+
// Provide a private key for signing
|
|
90
|
+
Object.defineProperty(runtime.connection, "privateKey", { value: "0xPRIVATE_KEY", writable: true });
|
|
91
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
92
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
93
|
+
return {
|
|
94
|
+
status: "sign_required",
|
|
95
|
+
forwardRequest: { from: "0xAGENT", to: "0xCONTRACT", data: "0x..." },
|
|
96
|
+
domain: { name: "Nookplot" },
|
|
97
|
+
types: { ForwardRequest: [] },
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
if (method === "POST" && path === "/v1/relay") {
|
|
101
|
+
return { txHash: "0xRELAY_TX_HASH" };
|
|
102
|
+
}
|
|
103
|
+
return { success: true };
|
|
104
|
+
});
|
|
105
|
+
await dispatchAction("create_community", { slug: "test", name: "Test" }, undefined, "act_1");
|
|
106
|
+
// Should have called relay with the signed request
|
|
107
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/relay", expect.objectContaining({ signature: "0xSIGNATURE" }));
|
|
108
|
+
// Should complete the action with txHash
|
|
109
|
+
expect(runtime.proactive.completeAction).toHaveBeenCalledWith("act_1", "0xRELAY_TX_HASH", { txHash: "0xRELAY_TX_HASH" });
|
|
110
|
+
});
|
|
111
|
+
it("throws when sign_required but no private key", async () => {
|
|
112
|
+
Object.defineProperty(runtime.connection, "privateKey", { value: null, writable: true });
|
|
113
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
114
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
115
|
+
return {
|
|
116
|
+
status: "sign_required",
|
|
117
|
+
forwardRequest: { from: "0xAGENT" },
|
|
118
|
+
domain: { name: "Nookplot" },
|
|
119
|
+
types: {},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { success: true };
|
|
123
|
+
});
|
|
124
|
+
await dispatchAction("vote", { contentCid: "Qm_cid" }, undefined, "act_2");
|
|
125
|
+
// Should reject the delegated action
|
|
126
|
+
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("act_2", expect.stringContaining("Private key not configured"));
|
|
287
127
|
});
|
|
288
128
|
});
|
|
289
|
-
// ──
|
|
290
|
-
describe("
|
|
291
|
-
it("
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
await dispatchAction("approve_bounty_application", { bountyId: "42", applicationId: "app_1" });
|
|
302
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/bounties/42/applications/app_1/approve", {});
|
|
303
|
-
});
|
|
304
|
-
it("reject_bounty_application calls connection.request POST", async () => {
|
|
305
|
-
await dispatchAction("reject_bounty_application", { bountyId: "42", applicationId: "app_1" });
|
|
306
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/bounties/42/applications/app_1/reject", {});
|
|
307
|
-
});
|
|
308
|
-
it("select_bounty_submission calls connection.request POST", async () => {
|
|
309
|
-
await dispatchAction("select_bounty_submission", { bountyId: "42", submissionId: "sub_1" });
|
|
310
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/bounties/42/submissions/sub_1/select", {});
|
|
311
|
-
});
|
|
312
|
-
it("accept_invitation calls connection.request POST", async () => {
|
|
313
|
-
await dispatchAction("accept_invitation", { invitationId: "inv_1" });
|
|
314
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/teams/invitations/inv_1/accept", {});
|
|
315
|
-
});
|
|
316
|
-
it("decline_invitation calls connection.request POST", async () => {
|
|
317
|
-
await dispatchAction("decline_invitation", { invitationId: "inv_1" });
|
|
318
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/teams/invitations/inv_1/decline", {});
|
|
319
|
-
});
|
|
320
|
-
it("grant calls connection.request POST with grant-access", async () => {
|
|
321
|
-
await dispatchAction("grant", { projectId: "proj_1", bountyId: "42", requestId: "req_1" });
|
|
322
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/projects/proj_1/bounties/42/grant-access", { requestId: "req_1" });
|
|
323
|
-
});
|
|
324
|
-
it("deny calls connection.request POST with deny-access", async () => {
|
|
325
|
-
await dispatchAction("deny", { projectId: "proj_1", bountyId: "42", requestId: "req_1" });
|
|
326
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("POST", "/v1/projects/proj_1/bounties/42/deny-access", { requestId: "req_1" });
|
|
129
|
+
// ── completed response handling ──────────────────────────────
|
|
130
|
+
describe("completed response handling", () => {
|
|
131
|
+
it("completes delegated action with result", async () => {
|
|
132
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
133
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
134
|
+
return { status: "completed", result: { messageId: "msg_1", sent: true } };
|
|
135
|
+
}
|
|
136
|
+
return { success: true };
|
|
137
|
+
});
|
|
138
|
+
await dispatchAction("send_message", { to: "0xTARGET", content: "Hi" }, undefined, "act_3");
|
|
139
|
+
expect(runtime.proactive.completeAction).toHaveBeenCalledWith("act_3", undefined, // no txHash for off-chain
|
|
140
|
+
{ messageId: "msg_1", sent: true });
|
|
327
141
|
});
|
|
328
142
|
});
|
|
329
|
-
// ──
|
|
330
|
-
describe("
|
|
331
|
-
it("
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
await dispatchAction("
|
|
349
|
-
expect(runtime.
|
|
350
|
-
});
|
|
351
|
-
it("call_mcp_tool calls tools.executeTool", async () => {
|
|
352
|
-
await dispatchAction("call_mcp_tool", { toolName: "mcp_tool_1", args: { x: 1 } });
|
|
353
|
-
expect(runtime.tools.executeTool).toHaveBeenCalledWith("mcp_tool_1", { x: 1 });
|
|
354
|
-
});
|
|
355
|
-
it("register_webhook calls tools.registerWebhook", async () => {
|
|
356
|
-
await dispatchAction("register_webhook", { source: "github" });
|
|
357
|
-
expect(runtime.tools.registerWebhook).toHaveBeenCalledWith("github", expect.anything());
|
|
143
|
+
// ── error response handling ──────────────────────────────
|
|
144
|
+
describe("error response handling", () => {
|
|
145
|
+
it("rejects delegated action when gateway returns error", async () => {
|
|
146
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
147
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
148
|
+
return { status: "error", error: "Rate limited" };
|
|
149
|
+
}
|
|
150
|
+
return { success: true };
|
|
151
|
+
});
|
|
152
|
+
await dispatchAction("send_message", { to: "0xTARGET" }, "Hi", "act_4");
|
|
153
|
+
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("act_4", "Rate limited");
|
|
154
|
+
});
|
|
155
|
+
it("rejects delegated action when request throws", async () => {
|
|
156
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
157
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
158
|
+
throw new Error("Network failure");
|
|
159
|
+
}
|
|
160
|
+
return { success: true };
|
|
161
|
+
});
|
|
162
|
+
await dispatchAction("vote", { contentCid: "Qm_cid" }, undefined, "act_5");
|
|
163
|
+
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("act_5", "Network failure");
|
|
358
164
|
});
|
|
359
165
|
});
|
|
360
|
-
// ──
|
|
361
|
-
describe("
|
|
362
|
-
it("
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
expect(
|
|
393
|
-
});
|
|
394
|
-
it("
|
|
395
|
-
|
|
396
|
-
|
|
166
|
+
// ── approval gate ──────────────────────────────
|
|
167
|
+
describe("approval gate for on-chain actions", () => {
|
|
168
|
+
it("blocks on-chain action when approval handler rejects", async () => {
|
|
169
|
+
const agentWithApproval = new AutonomousAgent(runtime, {
|
|
170
|
+
verbose: false,
|
|
171
|
+
onApproval: async () => false,
|
|
172
|
+
});
|
|
173
|
+
agentWithApproval.start();
|
|
174
|
+
callbacks.actionCb({
|
|
175
|
+
agentId: "a1",
|
|
176
|
+
actionType: "create_community",
|
|
177
|
+
actionId: "act_6",
|
|
178
|
+
payload: { slug: "x", name: "X" },
|
|
179
|
+
});
|
|
180
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
181
|
+
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("act_6", "Rejected by approval handler");
|
|
182
|
+
// Should NOT call the gateway execute endpoint
|
|
183
|
+
expect(requestMock).not.toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.anything());
|
|
184
|
+
});
|
|
185
|
+
it("allows on-chain action when approval handler approves", async () => {
|
|
186
|
+
const agentWithApproval = new AutonomousAgent(runtime, {
|
|
187
|
+
verbose: false,
|
|
188
|
+
onApproval: async () => true,
|
|
189
|
+
});
|
|
190
|
+
agentWithApproval.start();
|
|
191
|
+
callbacks.actionCb({
|
|
192
|
+
agentId: "a1",
|
|
193
|
+
actionType: "follow",
|
|
194
|
+
payload: { targetAddress: "0xTARGET" },
|
|
195
|
+
});
|
|
196
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
197
|
+
// Should have dispatched to gateway
|
|
198
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({ toolName: "nookplot_follow" }));
|
|
199
|
+
});
|
|
200
|
+
it("skips approval for off-chain actions", async () => {
|
|
201
|
+
const approvalFn = vi.fn().mockResolvedValue(false);
|
|
202
|
+
const agentWithApproval = new AutonomousAgent(runtime, {
|
|
203
|
+
verbose: false,
|
|
204
|
+
onApproval: approvalFn,
|
|
205
|
+
});
|
|
206
|
+
agentWithApproval.start();
|
|
207
|
+
callbacks.actionCb({
|
|
208
|
+
agentId: "a1",
|
|
209
|
+
actionType: "reply",
|
|
210
|
+
payload: { channelId: "ch1" },
|
|
211
|
+
suggestedContent: "hi",
|
|
212
|
+
});
|
|
213
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
214
|
+
expect(approvalFn).not.toHaveBeenCalled();
|
|
215
|
+
// Should still dispatch to gateway
|
|
216
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({ toolName: "nookplot_reply" }));
|
|
397
217
|
});
|
|
398
218
|
});
|
|
399
|
-
// ──
|
|
400
|
-
describe("
|
|
401
|
-
it("
|
|
402
|
-
await dispatchAction("
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
219
|
+
// ── action lifecycle ──────────────────────────────
|
|
220
|
+
describe("action lifecycle (completeAction / rejectDelegatedAction)", () => {
|
|
221
|
+
it("completes action on success (no actionId = no completeAction call)", async () => {
|
|
222
|
+
await dispatchAction("send_message", { to: "0xA", content: "hi" });
|
|
223
|
+
// No actionId → no completeAction call
|
|
224
|
+
expect(runtime.proactive.completeAction).not.toHaveBeenCalled();
|
|
225
|
+
});
|
|
226
|
+
it("completes action with actionId on success", async () => {
|
|
227
|
+
await dispatchAction("send_message", { to: "0xA", content: "hi" }, undefined, "act_7");
|
|
228
|
+
expect(runtime.proactive.completeAction).toHaveBeenCalledWith("act_7", undefined, { success: true });
|
|
229
|
+
});
|
|
230
|
+
it("rejects action with actionId on failure", async () => {
|
|
231
|
+
requestMock.mockImplementation(async (method, path) => {
|
|
232
|
+
if (method === "POST" && path === "/v1/actions/execute") {
|
|
233
|
+
return { status: "error", error: "Tool not found" };
|
|
234
|
+
}
|
|
235
|
+
return { success: true };
|
|
236
|
+
});
|
|
237
|
+
await dispatchAction("nonexistent_tool", {}, undefined, "act_8");
|
|
238
|
+
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("act_8", "Tool not found");
|
|
416
239
|
});
|
|
417
240
|
});
|
|
418
|
-
// ──
|
|
419
|
-
describe("
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
241
|
+
// ── various action types dispatch correctly ──────────────────────────────
|
|
242
|
+
describe("action type → toolName mapping", () => {
|
|
243
|
+
const cases = [
|
|
244
|
+
["create_post", { community: "general", title: "T" }],
|
|
245
|
+
["post_reply", { parentCid: "Qm_p" }],
|
|
246
|
+
["vote", { contentCid: "Qm_c", isUpvote: true }],
|
|
247
|
+
["follow", { targetAddress: "0xT" }],
|
|
248
|
+
["follow_agent", { targetAddress: "0xT" }],
|
|
249
|
+
["attest", { targetAddress: "0xT" }],
|
|
250
|
+
["attest_agent", { targetAddress: "0xT" }],
|
|
251
|
+
["send_message", { to: "0xT", content: "hi" }],
|
|
252
|
+
["send_dm", { to: "0xT", content: "hi" }],
|
|
253
|
+
["send_channel_message", { channelId: "ch_1", content: "hi" }],
|
|
254
|
+
["create_community", { slug: "test", name: "Test", description: "A test" }],
|
|
255
|
+
["propose_guild", { name: "G1", description: "Guild" }],
|
|
256
|
+
["create_bounty", { title: "B1", description: "bounty", community: "gen", rewardCredits: 100 }],
|
|
257
|
+
["claim_bounty", { bountyId: "b_1" }],
|
|
258
|
+
["create_bundle", { name: "Bundle", cids: ["Qm_1"] }],
|
|
259
|
+
["create_project", { projectId: "my-proj", name: "My Project", description: "desc" }],
|
|
260
|
+
["commit_files", { projectId: "p1", message: "init", files: [] }],
|
|
261
|
+
["create_task", { projectId: "p1", title: "T1" }],
|
|
262
|
+
["publish_insight", { title: "Insight", body: "text" }],
|
|
263
|
+
["create_swarm", { title: "S1", subtasks: [] }],
|
|
264
|
+
["create_intent", { title: "I1", description: "intent" }],
|
|
265
|
+
["apply_bounty", { bountyId: "b_1", message: "applying" }],
|
|
266
|
+
["egress_request", { url: "https://example.com" }],
|
|
267
|
+
];
|
|
268
|
+
for (const [actionType, payload] of cases) {
|
|
269
|
+
it(`${actionType} → nookplot_${actionType}`, async () => {
|
|
270
|
+
await dispatchAction(actionType, payload);
|
|
271
|
+
expect(requestMock).toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.objectContaining({
|
|
272
|
+
toolName: `nookplot_${actionType}`,
|
|
273
|
+
payload: expect.objectContaining(payload),
|
|
274
|
+
}));
|
|
275
|
+
});
|
|
276
|
+
}
|
|
436
277
|
});
|
|
437
|
-
// ──
|
|
438
|
-
describe("
|
|
439
|
-
it("
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
await dispatchAction("update_proficiency", { skillDomain: "typescript", proficiency: 85 });
|
|
445
|
-
expect(runtime.specialization.updateProficiency).toHaveBeenCalledWith("typescript", 85);
|
|
446
|
-
});
|
|
447
|
-
it("generate_recommendations calls specialization.generateRecommendations", async () => {
|
|
448
|
-
await dispatchAction("generate_recommendations", {});
|
|
449
|
-
expect(runtime.specialization.generateRecommendations).toHaveBeenCalled();
|
|
450
|
-
});
|
|
451
|
-
it("dismiss_recommendation calls specialization.dismissRecommendation", async () => {
|
|
452
|
-
await dispatchAction("dismiss_recommendation", { recommendationId: "rec_1" });
|
|
453
|
-
expect(runtime.specialization.dismissRecommendation).toHaveBeenCalledWith("rec_1");
|
|
454
|
-
});
|
|
455
|
-
});
|
|
456
|
-
// ── Intent actions ───────────────────────────────────────────
|
|
457
|
-
describe("intent actions", () => {
|
|
458
|
-
it("create_intent calls intents.create", async () => {
|
|
459
|
-
await dispatchAction("create_intent", { title: "Need a logo" });
|
|
460
|
-
expect(runtime.intents.create).toHaveBeenCalledWith(expect.objectContaining({ title: "Need a logo" }));
|
|
461
|
-
});
|
|
462
|
-
it("browse_intents calls intents.list", async () => {
|
|
463
|
-
await dispatchAction("browse_intents", { status: "open" });
|
|
464
|
-
expect(runtime.intents.list).toHaveBeenCalledWith(expect.objectContaining({ status: "open", limit: 20 }));
|
|
465
|
-
});
|
|
466
|
-
it("submit_proposal calls intents.submitProposal", async () => {
|
|
467
|
-
await dispatchAction("submit_proposal", { intentId: "intent_1" }, "I can help");
|
|
468
|
-
expect(runtime.intents.submitProposal).toHaveBeenCalledWith("intent_1", expect.objectContaining({ description: "I can help" }));
|
|
469
|
-
});
|
|
470
|
-
it("accept_proposal calls intents.acceptProposal", async () => {
|
|
471
|
-
await dispatchAction("accept_proposal", { intentId: "intent_1", proposalId: "prop_1" });
|
|
472
|
-
expect(runtime.intents.acceptProposal).toHaveBeenCalledWith("intent_1", "prop_1");
|
|
473
|
-
});
|
|
474
|
-
it("cancel_intent calls intents.cancel", async () => {
|
|
475
|
-
await dispatchAction("cancel_intent", { intentId: "intent_1" });
|
|
476
|
-
expect(runtime.intents.cancel).toHaveBeenCalledWith("intent_1");
|
|
477
|
-
});
|
|
478
|
-
it("complete_intent calls intents.complete", async () => {
|
|
479
|
-
await dispatchAction("complete_intent", { intentId: "intent_1" });
|
|
480
|
-
expect(runtime.intents.complete).toHaveBeenCalledWith("intent_1");
|
|
481
|
-
});
|
|
482
|
-
it("withdraw_proposal calls intents.withdrawProposal", async () => {
|
|
483
|
-
await dispatchAction("withdraw_proposal", { intentId: "intent_1", proposalId: "prop_1" });
|
|
484
|
-
expect(runtime.intents.withdrawProposal).toHaveBeenCalledWith("intent_1", "prop_1");
|
|
485
|
-
});
|
|
486
|
-
});
|
|
487
|
-
// ── Token launch actions ─────────────────────────────────────
|
|
488
|
-
describe("token launch actions", () => {
|
|
489
|
-
it("report_clawnch_launch is not yet wired (clawnch not live)", async () => {
|
|
490
|
-
await dispatchAction("report_clawnch_launch", { tokenName: "MyCoin", tokenTicker: "MYC", tokenAddress: "0xTOKEN" });
|
|
491
|
-
expect(runtime.connection.request).not.toHaveBeenCalled();
|
|
492
|
-
});
|
|
493
|
-
it("get_token_analytics calls connection.request GET", async () => {
|
|
494
|
-
await dispatchAction("get_token_analytics", { tokenAddress: "0xTOKEN" });
|
|
495
|
-
expect(runtime.connection.request).toHaveBeenCalledWith("GET", "/v1/clawnch/analytics/token/0xTOKEN");
|
|
496
|
-
});
|
|
497
|
-
});
|
|
498
|
-
// ── Oracle actions ───────────────────────────────────────────
|
|
499
|
-
describe("oracle actions", () => {
|
|
500
|
-
it("query_oracle project calls oracle.getProjectSignals", async () => {
|
|
501
|
-
await dispatchAction("query_oracle", { entityType: "project", entityId: "proj_1" });
|
|
502
|
-
expect(runtime.oracle.getProjectSignals).toHaveBeenCalledWith("proj_1");
|
|
503
|
-
});
|
|
504
|
-
it("query_oracle agent calls oracle.getAgentSignals", async () => {
|
|
505
|
-
await dispatchAction("query_oracle", { entityType: "agent", entityId: "0xAGENT" });
|
|
506
|
-
expect(runtime.oracle.getAgentSignals).toHaveBeenCalledWith("0xAGENT");
|
|
507
|
-
});
|
|
508
|
-
it("query_oracle intent calls oracle.getIntentSignals", async () => {
|
|
509
|
-
await dispatchAction("query_oracle", { entityType: "intent", entityId: "intent_1" });
|
|
510
|
-
expect(runtime.oracle.getIntentSignals).toHaveBeenCalledWith("intent_1");
|
|
511
|
-
});
|
|
512
|
-
it("query_oracle guild calls oracle.getGuildSignals", async () => {
|
|
513
|
-
await dispatchAction("query_oracle", { entityType: "guild", entityId: "guild_1" });
|
|
514
|
-
expect(runtime.oracle.getGuildSignals).toHaveBeenCalledWith("guild_1");
|
|
515
|
-
});
|
|
516
|
-
});
|
|
517
|
-
// ── Marketplace review + messages ────────────────────────────
|
|
518
|
-
describe("marketplace review and messages", () => {
|
|
519
|
-
it("submit_review calls marketplace.submitReview", async () => {
|
|
520
|
-
await dispatchAction("submit_review", { agreementId: 5, rating: 4 }, "Good work!");
|
|
521
|
-
expect(runtime.marketplace.submitReview).toHaveBeenCalledWith(5, 4, "Good work!");
|
|
522
|
-
});
|
|
523
|
-
it("send_agreement_message calls marketplace.sendAgreementMessage", async () => {
|
|
524
|
-
await dispatchAction("send_agreement_message", { agreementId: 5, messageType: "revision_request" }, "Please fix X");
|
|
525
|
-
expect(runtime.marketplace.sendAgreementMessage).toHaveBeenCalledWith(5, "revision_request", "Please fix X", undefined);
|
|
526
|
-
});
|
|
527
|
-
});
|
|
528
|
-
// ── Matching actions ─────────────────────────────────────────
|
|
529
|
-
describe("matching actions", () => {
|
|
530
|
-
it("find_agents calls matching.findAgents", async () => {
|
|
531
|
-
await dispatchAction("find_agents", { skills: ["typescript", "react"] });
|
|
532
|
-
expect(runtime.matching.findAgents).toHaveBeenCalledWith(["typescript", "react"], expect.anything());
|
|
533
|
-
});
|
|
534
|
-
it("assemble_team calls matching.assembleTeam", async () => {
|
|
535
|
-
await dispatchAction("assemble_team", {}, "Build a web3 dashboard");
|
|
536
|
-
expect(runtime.matching.assembleTeam).toHaveBeenCalledWith(expect.objectContaining({ description: "Build a web3 dashboard" }));
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
// ── Error cases and lifecycle ────────────────────────────────
|
|
540
|
-
describe("error cases and action lifecycle", () => {
|
|
541
|
-
it("unknown action rejects delegated action", async () => {
|
|
542
|
-
await dispatchAction("totally_unknown_action", {}, undefined, "action_42");
|
|
543
|
-
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("action_42", expect.stringContaining("Unknown"));
|
|
544
|
-
});
|
|
545
|
-
it("actionId triggers proactive.completeAction on success", async () => {
|
|
546
|
-
await dispatchAction("follow", { targetAddress: "0xTARGET" }, undefined, "action_99");
|
|
547
|
-
expect(runtime.proactive.completeAction).toHaveBeenCalledWith("action_99", "0xMOCK_TX", expect.anything());
|
|
548
|
-
});
|
|
549
|
-
it("missing required field throws and rejects delegated action", async () => {
|
|
550
|
-
await dispatchAction("send_dm", {}, undefined, "action_fail");
|
|
551
|
-
expect(runtime.proactive.rejectDelegatedAction).toHaveBeenCalledWith("action_fail", expect.stringContaining("requires"));
|
|
552
|
-
});
|
|
553
|
-
it("custom onAction handler bypasses default dispatch", async () => {
|
|
554
|
-
const customHandler = vi.fn().mockResolvedValue(undefined);
|
|
555
|
-
const customAgent = new AutonomousAgent(runtime, { onAction: customHandler, verbose: false });
|
|
556
|
-
customAgent.start();
|
|
557
|
-
callbacks.actionCb({ agentId: "a1", actionType: "follow", payload: { targetAddress: "0x1" } });
|
|
558
|
-
await new Promise((r) => setTimeout(r, 10));
|
|
559
|
-
expect(customHandler).toHaveBeenCalled();
|
|
560
|
-
expect(runtime.social.follow).not.toHaveBeenCalled();
|
|
278
|
+
// ── stop prevents dispatch ──────────────────────────────
|
|
279
|
+
describe("stop prevents action dispatch", () => {
|
|
280
|
+
it("stopped agent does not dispatch actions", async () => {
|
|
281
|
+
agent.stop();
|
|
282
|
+
callbacks.actionCb({ agentId: "a1", actionType: "follow", payload: { targetAddress: "0xA" } });
|
|
283
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
284
|
+
expect(requestMock).not.toHaveBeenCalledWith("POST", "/v1/actions/execute", expect.anything());
|
|
561
285
|
});
|
|
562
286
|
});
|
|
563
287
|
//# sourceMappingURL=autonomous.actionDispatch.test.js.map
|