@nookplot/runtime 0.5.70 → 0.5.72

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.
@@ -41,7 +41,8 @@
41
41
  */
42
42
  import { prepareSignRelay } from "./signing.js";
43
43
  import { wrapUntrusted, sanitizeForPrompt, UNTRUSTED_CONTENT_INSTRUCTION } from "./contentSafety.js";
44
- import { filterByProfile } from "./actionCatalog.js";
44
+ import { getAvailableActionsFromMap } from "./signalActionMap.js";
45
+ import { getCategoryListing, getToolsInCategory } from "./actionCatalog.js";
45
46
  // ----------------------------------------------------------------
46
47
  // AutonomousAgent
47
48
  // ----------------------------------------------------------------
@@ -82,237 +83,8 @@ const ON_CHAIN_ACTIONS = new Set([
82
83
  * // → "- reply: Send a text reply in the current context. Params: content (string)\n..."
83
84
  * ```
84
85
  */
85
- export function getAvailableActions(signalType) {
86
- return filterByProfile(_getAvailableActionsRaw(signalType));
87
- }
88
- /** Raw action lists per signal type (before profile filtering). */
89
- function _getAvailableActionsRaw(signalType) {
90
- switch (signalType) {
91
- case "dm_received":
92
- return [
93
- "reply", "send_dm", "follow_back", "attest_back", "propose_collab",
94
- "vote", "publish", "create_post", "create_bounty", "create_project",
95
- "create_community", "create_listing", "commit_files", "create_task",
96
- "link_project_to_guild", "propose_guild", "deploy_preview",
97
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool", "register_webhook",
98
- "workspace_create", "publish_insight",
99
- "create_intent", "browse_intents",
100
- "launch_token", "preview_token_launch",
101
- "search_skills", "install_skill", "store_memory", "recall_memory",
102
- "propose_teaching", "search_teachers",
103
- "credit_hire",
104
- "ignore",
105
- ];
106
- case "channel_message":
107
- case "channel_mention":
108
- case "project_discussion":
109
- return [
110
- "reply", "publish", "vote", "follow", "attest", "propose_collab",
111
- "create_post", "create_bounty", "create_project", "commit_files",
112
- "create_task", "link_project_to_guild", "propose_guild",
113
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool",
114
- "workspace_create", "publish_insight",
115
- "create_intent", "browse_intents",
116
- "search_skills", "install_skill", "store_memory",
117
- "ignore",
118
- ];
119
- case "new_follower":
120
- return ["follow_back", "send_dm", "ignore"];
121
- case "attestation_received":
122
- return ["attest_back", "endorse_agent", "send_dm", "ignore"];
123
- case "endorsement_received":
124
- return ["endorse_agent", "attest_back", "send_dm", "ignore"];
125
- case "files_committed":
126
- case "pending_review":
127
- return ["review", "comment", "request_ai_review", "list_project_files", "read_project_file", "list_commits", "get_commit_detail", "list_merge_requests", "get_merge_request", "ignore"];
128
- case "merge_request_created":
129
- return ["get_merge_request", "merge_merge_request", "close_merge_request", "reply", "ignore"];
130
- case "project_forked":
131
- return ["acknowledge", "send_dm", "ignore"];
132
- case "review_submitted":
133
- return ["reply", "ignore"];
134
- case "collaborator_added":
135
- return ["send_message", "reply", "ignore"];
136
- case "new_post_in_community":
137
- case "post_reply":
138
- case "reply_to_own_post":
139
- return ["reply", "post_reply", "vote", "publish", "ignore"];
140
- case "bounty":
141
- return ["claim", "apply_bounty", "create_bounty", "reply", "ignore"];
142
- case "community_gap":
143
- return ["create_community", "ignore"];
144
- case "potential_friend":
145
- return ["follow", "send_dm", "attest", "endorse_agent", "ignore"];
146
- case "attestation_opportunity":
147
- return ["attest", "endorse_agent", "send_dm", "ignore"];
148
- case "directive":
149
- return [
150
- "execute", "reply", "publish", "create_project", "commit_files",
151
- "create_task", "assign_task", "complete_task", "update_task",
152
- "link_project_to_guild", "propose_guild", "approve_guild", "reject_guild", "leave_guild",
153
- "create_bounty", "create_bundle", "propose_collab", "assemble_team",
154
- "find_agents", "deploy_preview", "add_collaborator",
155
- "fork_project", "create_merge_request", "list_merge_requests", "get_merge_request", "merge_merge_request", "close_merge_request",
156
- "create_listing", "create_agreement", "cancel_agreement",
157
- "workspace_create", "workspace_set", "workspace_snapshot",
158
- "propose_action", "vote_proposal", "cancel_proposal",
159
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool", "connect_mcp_server", "disconnect_mcp_server", "register_webhook",
160
- "publish_insight", "cite_insight", "apply_insight",
161
- "deposit_treasury", "withdraw_treasury", "fund_bounty_from_treasury", "distribute_revenue",
162
- "create_swarm", "claim_subtask", "submit_swarm_result", "aggregate_swarm",
163
- "record_gap", "update_proficiency", "generate_recommendations",
164
- "create_intent", "browse_intents", "submit_proposal", "accept_proposal", "reject_proposal",
165
- "cancel_intent", "complete_intent", "withdraw_proposal", "query_oracle",
166
- "launch_token", "preview_token_launch", "claim_clawnch_fees", "get_token_analytics",
167
- "create_search_subscription",
168
- "send_email", "reply_email", "check_email", "create_email_inbox",
169
- "search_skills", "publish_skill", "install_skill", "review_skill", "update_skill", "trending_skills",
170
- "store_memory", "recall_memory", "list_memories", "memory_stats", "export_memories", "import_memories",
171
- "forge_deploy", "forge_spawn", "forge_update_soul",
172
- "propose_teaching", "accept_teaching", "deliver_teaching", "approve_teaching", "reject_teaching", "search_teachers",
173
- "credit_hire", "accept_credit_agreement", "deliver_credit_work", "complete_credit_agreement", "cancel_credit_agreement",
174
- "claim_reward",
175
- "endorse_agent", "revoke_endorsement",
176
- "block_agent", "unblock_agent",
177
- "list_project_files", "read_project_file", "list_commits", "get_commit_detail",
178
- "gpu_search", "gpu_heartbeat", "gpu_challenge", "gpu_submit_challenge",
179
- "gpu_submit_attestation", "gpu_update_attestation", "gpu_revoke_attestation",
180
- "gpu_rent",
181
- "ignore",
182
- ];
183
- case "collab_request":
184
- return ["add_collaborator", "propose_collab", "reply", "ignore"];
185
- case "service":
186
- return ["reply", "update_service", "create_listing", "create_agreement", "ignore"];
187
- case "time_to_post":
188
- return ["create_post", "create_bounty", "create_bundle", "publish_insight", "create_listing", "publish_skill", "ignore"];
189
- case "time_to_create_project":
190
- return ["create_project", "assemble_team", "ignore"];
191
- case "task_assigned":
192
- return ["accept", "update_task", "complete_task", "assign_task", "assemble_team", "reply", "ignore"];
193
- case "task_completed":
194
- return ["reply", "review", "create_task", "ignore"];
195
- case "milestone_reached":
196
- return ["reply", "ignore"];
197
- case "review_comment_added":
198
- return ["reply", "ignore"];
199
- case "agent_mentioned":
200
- return ["reply", "acknowledge", "ignore"];
201
- case "project_status_update":
202
- return ["reply", "ignore"];
203
- case "file_shared":
204
- return ["reply", "ignore"];
205
- case "bounty_posted_to_project":
206
- return ["reply", "claim", "ignore"];
207
- case "bounty_access_requested":
208
- return ["grant", "deny", "ignore"];
209
- case "bounty_access_granted":
210
- return ["reply", "claim", "ignore"];
211
- case "project_bounty_claimed":
212
- return ["reply", "ignore"];
213
- case "project_bounty_completed":
214
- return ["reply", "ignore"];
215
- case "team_assembly_suggested":
216
- return ["assemble_team", "ignore"];
217
- case "team_invitation":
218
- return ["accept_invitation", "decline_invitation", "ignore"];
219
- case "team_invitation_accepted":
220
- case "team_invitation_declined":
221
- return ["reply", "ignore"];
222
- case "xmtp_message":
223
- return ["reply", "ignore"];
224
- // Marketplace signals
225
- case "agreement_created":
226
- return ["deliver_work", "cancel_agreement", "send_agreement_message", "ignore"];
227
- case "work_delivered":
228
- return ["settle_agreement", "dispute_agreement", "send_agreement_message", "expire_delivered", "ignore"];
229
- case "agreement_settled":
230
- return ["submit_review", "ignore"];
231
- case "agreement_disputed":
232
- return ["send_agreement_message", "expire_dispute", "ignore"];
233
- case "agreement_cancelled":
234
- return ["ignore"];
235
- case "revision_requested":
236
- return ["deliver_work", "send_agreement_message", "ignore"];
237
- case "review_received":
238
- return ["ignore"];
239
- // Bounty application/submission signals
240
- case "bounty_application_submitted":
241
- return ["approve_bounty_claimer", "reject_bounty_application", "ignore"];
242
- case "bounty_application_approved":
243
- return ["claim_bounty", "ignore"];
244
- case "bounty_application_rejected":
245
- return ["ignore"];
246
- case "bounty_work_submitted":
247
- return ["select_bounty_submission", "ignore"];
248
- case "bounty_submission_selected":
249
- return ["claim_bounty", "ignore"];
250
- case "bounty_submission_not_selected":
251
- return ["ignore"];
252
- // On-chain bounty lifecycle signals
253
- case "bounty_claimed":
254
- return ["approve_bounty_work", "approve_bounty_claimer", "dispute_bounty_work", "unclaim_bounty", "ignore"];
255
- case "bounty_work_approved":
256
- return ["ignore"];
257
- case "bounty_disputed":
258
- return ["cancel_bounty", "ignore"];
259
- case "bounty_cancelled":
260
- return ["ignore"];
261
- case "bounty_claimer_approved":
262
- return ["claim_bounty", "ignore"];
263
- case "guild_opportunity":
264
- return ["join_guild", "approve_guild", "reject_guild", "leave_guild", "propose_guild", "link_project_to_guild", "reply", "ignore"];
265
- // Intent signals
266
- case "intent_matched":
267
- return ["submit_proposal", "browse_intents", "reply", "ignore"];
268
- case "proposal_received":
269
- return ["accept_proposal", "reject_proposal", "reply", "ignore"];
270
- case "intent_accepted":
271
- return ["complete_intent", "reply", "ignore"];
272
- // Signals handled by runtime dispatchers but informational in getAvailableActions
273
- case "new_project":
274
- case "interesting_project":
275
- return ["propose_collab", "reply", "ignore"];
276
- case "bounty_access_denied":
277
- return ["ignore"];
278
- case "task_created":
279
- case "task_deleted":
280
- case "status_updated":
281
- return ["reply", "ignore"];
282
- case "welcome_guide":
283
- return ["reply", "create_post", "ignore"];
284
- case "onboarding_suggestion":
285
- return ["reply", "ignore"];
286
- case "specialization_path":
287
- return ["reply", "record_gap", "update_proficiency", "search_skills", "install_skill", "store_memory", "ignore"];
288
- case "new_bundle_in_domain":
289
- return ["cite_insight", "reply", "ignore"];
290
- case "bundle_cited":
291
- return ["ignore"];
292
- case "webhook_received":
293
- case "webhook.received":
294
- return ["reply", "egress_request", "execute_tool", "ignore"];
295
- case "email_received":
296
- return ["reply_email", "send_email", "send_dm", "ignore"];
297
- case "teaching_proposed":
298
- return ["accept_teaching", "reject_teaching", "reply", "ignore"];
299
- case "teaching_accepted":
300
- return ["deliver_teaching", "reply", "ignore"];
301
- case "teaching_delivered":
302
- return ["approve_teaching", "reject_teaching", "reply", "ignore"];
303
- case "teaching_opportunity":
304
- return ["propose_teaching", "search_teachers", "reply", "ignore"];
305
- case "credit_agreement_created":
306
- return ["accept_credit_agreement", "cancel_credit_agreement", "reply", "ignore"];
307
- case "credit_work_delivered":
308
- return ["complete_credit_agreement", "cancel_credit_agreement", "reply", "ignore"];
309
- case "credit_agreement_accepted":
310
- return ["deliver_credit_work", "cancel_credit_agreement", "reply", "ignore"];
311
- case "bounty_opportunity":
312
- return ["apply_bounty", "send_dm", "reply", "ignore"];
313
- default:
314
- return ["reply", "ignore"];
315
- }
86
+ export function getAvailableActions(signalType, loadedCategories) {
87
+ return getAvailableActionsFromMap(signalType, loadedCategories ?? new Set());
316
88
  }
317
89
  export class AutonomousAgent {
318
90
  runtime;
@@ -326,6 +98,8 @@ export class AutonomousAgent {
326
98
  channelCooldowns = new Map();
327
99
  /** Dedup: tracks signal keys already processed. Entries expire after 1h. */
328
100
  processedSignals = new Map();
101
+ /** Dynamic tool browsing: categories loaded via browse_tools. */
102
+ loadedCategories = new Set();
329
103
  constructor(runtime, options = {}) {
330
104
  this.runtime = runtime;
331
105
  this.verbose = options.verbose ?? true;
@@ -2596,2354 +2370,92 @@ export class AutonomousAgent {
2596
2370
  try {
2597
2371
  let txHash;
2598
2372
  let result;
2599
- switch (actionType) {
2600
- case "post_reply": {
2601
- const parentCid = (payload?.parentCid ?? payload?.sourceId);
2602
- const community = payload?.community;
2603
- if (!parentCid || !suggestedContent)
2604
- throw new Error("post_reply requires parentCid and suggestedContent");
2605
- const pub = await this.runtime.memory.publishComment({ parentCid, body: suggestedContent, community: community ?? "general" });
2606
- txHash = pub.txHash;
2607
- result = { cid: pub.cid, txHash };
2608
- break;
2609
- }
2610
- case "create_post":
2611
- case "publish": {
2612
- const community = (payload?.community ?? "general");
2613
- const title = (payload?.title ?? suggestedContent?.slice(0, 100));
2614
- const body = suggestedContent ?? payload?.body ?? "";
2615
- const pub = await this.runtime.memory.publishKnowledge({ title, body, community });
2616
- txHash = pub.txHash;
2617
- result = { cid: pub.cid, txHash };
2618
- break;
2619
- }
2620
- case "vote": {
2621
- const cid = payload?.cid;
2622
- if (!cid)
2623
- throw new Error("vote requires cid");
2624
- const v = await this.runtime.memory.vote({ cid, type: (payload?.voteType ?? "up") });
2625
- txHash = v.txHash;
2626
- result = { txHash };
2627
- break;
2628
- }
2629
- case "follow_agent": {
2630
- const addr = (payload?.targetAddress ?? payload?.address);
2631
- if (!addr)
2632
- throw new Error("follow_agent requires targetAddress");
2633
- const f = await this.runtime.social.follow(addr);
2634
- txHash = f.txHash;
2635
- result = { txHash };
2636
- break;
2637
- }
2638
- case "attest_agent": {
2639
- const addr = (payload?.targetAddress ?? payload?.address);
2640
- const reason = (suggestedContent ?? payload?.reason ?? "Valued collaborator");
2641
- if (!addr)
2642
- throw new Error("attest_agent requires targetAddress");
2643
- const a = await this.runtime.social.attest(addr, reason);
2644
- txHash = a.txHash;
2645
- result = { txHash };
2646
- break;
2647
- }
2648
- case "endorse_agent": {
2649
- const endorseeAddr = (payload?.address ?? payload?.targetAddress);
2650
- const endorseSkill = payload?.skill;
2651
- const endorseRating = Number(payload?.rating ?? 3);
2652
- const endorseContext = (suggestedContent ?? payload?.context ?? "");
2653
- if (!endorseeAddr)
2654
- throw new Error("endorse_agent requires address");
2655
- if (!endorseSkill)
2656
- throw new Error("endorse_agent requires skill");
2657
- const e = await this.runtime.social.endorse(endorseeAddr, endorseSkill, endorseRating, endorseContext);
2658
- txHash = e.txHash;
2659
- result = { txHash };
2660
- break;
2661
- }
2662
- case "revoke_endorsement": {
2663
- const revokeAddr = (payload?.address ?? payload?.targetAddress);
2664
- const revokeSkill = payload?.skill;
2665
- if (!revokeAddr)
2666
- throw new Error("revoke_endorsement requires address");
2667
- if (!revokeSkill)
2668
- throw new Error("revoke_endorsement requires skill");
2669
- const r = await this.runtime.social.revokeEndorsement(revokeAddr, revokeSkill);
2670
- txHash = r.txHash;
2671
- result = { txHash };
2672
- break;
2673
- }
2674
- case "block_agent": {
2675
- const blockAddr = (payload?.address ?? payload?.targetAddress);
2676
- if (!blockAddr)
2677
- throw new Error("block_agent requires address");
2678
- const b = await this.runtime.social.block(blockAddr);
2679
- txHash = b.txHash;
2680
- result = { txHash };
2681
- break;
2682
- }
2683
- case "unblock_agent": {
2684
- const unblockAddr = (payload?.address ?? payload?.targetAddress);
2685
- if (!unblockAddr)
2686
- throw new Error("unblock_agent requires address");
2687
- const u = await this.runtime.social.unblock(unblockAddr);
2688
- txHash = u.txHash;
2689
- result = { txHash };
2690
- break;
2691
- }
2692
- case "create_community": {
2693
- const slug = payload?.slug;
2694
- const name = payload?.name;
2695
- const desc = (suggestedContent ?? payload?.description ?? "");
2696
- if (!slug || !name)
2697
- throw new Error("create_community requires slug and name");
2698
- const communityRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/community", { slug, name, description: desc });
2699
- txHash = communityRelay.txHash;
2700
- result = { txHash, slug };
2701
- break;
2702
- }
2703
- case "propose_guild":
2704
- case "propose_clique": {
2705
- const name = payload?.name;
2706
- const members = payload?.members;
2707
- const desc = (suggestedContent ?? payload?.description ?? "");
2708
- if (!name || !members || members.length < 2)
2709
- throw new Error("propose_guild requires name and at least 2 members");
2710
- const guildRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/guild", { name, description: desc, members });
2711
- txHash = guildRelay.txHash;
2712
- result = { txHash, name };
2713
- break;
2714
- }
2715
- case "create_project": {
2716
- const projId = payload?.projectId;
2717
- const projName = payload?.name;
2718
- const projDesc = (suggestedContent ?? payload?.description ?? "");
2719
- if (!projId || !projName)
2720
- throw new Error("create_project requires projectId and name");
2721
- // Step 1: discover similar projects (gateway requires discoveryId)
2722
- const discoverResp = await this.runtime.connection.request("POST", "/v1/projects/discover", { name: projName, description: projDesc });
2723
- const discoveryId = discoverResp?.discoveryId;
2724
- if (!discoveryId)
2725
- throw new Error("create_project: failed to get discoveryId from /v1/projects/discover");
2726
- // Step 2: prepare + sign + relay
2727
- const projRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/project", { discoveryId, projectId: projId, name: projName, description: projDesc });
2728
- txHash = projRelay.txHash;
2729
- result = { txHash, projectId: projId, name: projName };
2730
- // Step 3: if guildId provided, link project to guild automatically
2731
- const guildIdForProject = (payload?.guildId ?? payload?.cliqueId);
2732
- if (guildIdForProject != null) {
2733
- try {
2734
- await this.runtime.guilds.linkProject(guildIdForProject, projId);
2735
- await this.runtime.projects.setGuildAttribution(projId, String(guildIdForProject));
2736
- const guild = await this.runtime.guilds.get(guildIdForProject);
2737
- const gMembers = (guild?.members ?? []);
2738
- const myAddr = this.runtime.connection.address;
2739
- for (const m of gMembers) {
2740
- if (m.status === 2 && m.address?.toLowerCase() !== myAddr?.toLowerCase()) {
2741
- try {
2742
- await this.runtime.projects.addCollaborator(projId, m.address, "editor");
2743
- }
2744
- catch { /* best-effort */ }
2745
- }
2746
- }
2747
- result.guildLinked = true;
2748
- result.guildId = guildIdForProject;
2749
- }
2750
- catch (guildErr) {
2751
- if (this.verbose)
2752
- console.warn("[autonomous] Guild linking failed (best-effort):", guildErr);
2753
- }
2754
- }
2755
- break;
2756
- }
2757
- case "review_commit": {
2758
- const projId = payload?.projectId;
2759
- const commitIdAction = payload?.commitId;
2760
- if (!projId || !commitIdAction)
2761
- throw new Error("review_commit requires projectId and commitId");
2762
- // If verdict+body provided directly (from gateway review_commit handler), submit directly
2763
- const directVerdict = payload?.verdict;
2764
- const directBody = (suggestedContent ?? payload?.body ?? "");
2765
- if (directVerdict) {
2766
- const r = await this.runtime.projects.submitReview(projId, commitIdAction, directVerdict, directBody);
2767
- result = { reviewId: r.id, verdict: directVerdict };
2768
- }
2769
- else {
2770
- // Otherwise get commit detail and let LLM review (same as handleFilesCommitted)
2771
- const detail = await this.runtime.projects.getCommit(projId, commitIdAction);
2772
- const changes = detail ? detail.changes : undefined;
2773
- const diffText = changes?.map((c) => `${c.path}: ${c.diff?.slice(0, 300) ?? ""}`).join("\n").slice(0, 2000) ?? "";
2774
- if (this.generateResponse) {
2775
- const prompt = `Review this code commit:\nMessage: ${detail?.message ?? ""}\n\nChanges:\n${diffText}\n\nVERDICT: APPROVE | REQUEST_CHANGES | COMMENT\nBODY: feedback`;
2776
- const resp = await this.generateResponse(prompt);
2777
- const text = resp?.trim() ?? "";
2778
- const vm = text.match(/VERDICT:\s*(APPROVE|REQUEST_CHANGES|COMMENT)/i);
2779
- const v = vm?.[1]?.toLowerCase() ?? "comment";
2780
- const bm = text.match(/BODY:\s*(.+)/is);
2781
- const b = (bm?.[1]?.trim() ?? text).slice(0, 500);
2782
- const r = await this.runtime.projects.submitReview(projId, commitIdAction, v, b);
2783
- result = { reviewId: r.id, verdict: v };
2784
- }
2785
- }
2786
- break;
2787
- }
2788
- case "request_ai_review": {
2789
- // AI-powered code review — costs 150 credits (1.50 cr)
2790
- const projId2 = payload?.projectId;
2791
- const commitId2 = payload?.commitId;
2792
- if (!projId2 || !commitId2)
2793
- throw new Error("request_ai_review requires projectId and commitId");
2794
- const aiResult = await this.runtime.projects.requestAIReview(projId2, commitId2);
2795
- result = { reviewId: aiResult.reviewId, verdict: aiResult.verdict, findingsCount: aiResult.findingsCount, creditsCost: aiResult.creditsCost };
2796
- break;
2797
- }
2798
- case "gateway_commit":
2799
- case "commit_files": {
2800
- const projId = payload?.projectId;
2801
- const files = payload?.files;
2802
- const msg = (suggestedContent ?? payload?.message ?? "Automated commit");
2803
- if (!projId || !files || files.length === 0)
2804
- throw new Error("gateway_commit requires projectId and files");
2805
- const commitResult = await this.runtime.projects.commitFiles(projId, files, msg);
2806
- result = commitResult;
2807
- break;
2808
- }
2809
- case "list_project_files": {
2810
- const projId = payload?.projectId;
2811
- if (!projId)
2812
- throw new Error("list_project_files requires projectId");
2813
- const files = await this.runtime.projects.listFiles(projId);
2814
- result = { files };
2815
- break;
2816
- }
2817
- case "read_project_file": {
2818
- const projId = payload?.projectId;
2819
- const fp = payload?.filePath;
2820
- if (!projId || !fp)
2821
- throw new Error("read_project_file requires projectId and filePath");
2822
- const fileContent = await this.runtime.projects.readFile(projId, fp);
2823
- result = fileContent;
2824
- break;
2825
- }
2826
- case "list_commits": {
2827
- const projId = payload?.projectId;
2828
- if (!projId)
2829
- throw new Error("list_commits requires projectId");
2830
- const limit = payload?.limit ?? 20;
2831
- const offset = payload?.offset ?? 0;
2832
- const commits = await this.runtime.projects.listCommits(projId, limit, offset);
2833
- result = { commits };
2834
- break;
2835
- }
2836
- case "get_commit_detail": {
2837
- const projId = payload?.projectId;
2838
- const commitId = payload?.commitId;
2839
- if (!projId || !commitId)
2840
- throw new Error("get_commit_detail requires projectId and commitId");
2841
- const commit = await this.runtime.projects.getCommit(projId, commitId);
2842
- result = commit;
2843
- break;
2844
- }
2845
- case "apply_bounty": {
2846
- // Apply to a bounty by submitting work — message must contain the deliverable
2847
- const bountyId = payload?.bountyId;
2848
- if (!bountyId)
2849
- throw new Error("apply_bounty requires bountyId");
2850
- const applyMsg = (suggestedContent ?? payload?.message ?? "");
2851
- if (!applyMsg || applyMsg.length < 50)
2852
- throw new Error("apply_bounty requires a work submission (minimum 50 characters)");
2853
- const applyResult = await this.runtime.connection.request("POST", `/v1/bounties/${bountyId}/apply`, { message: applyMsg });
2854
- result = applyResult;
2855
- break;
2856
- }
2857
- case "submit_bounty_work": {
2858
- // Submit work for a bounty (gateway-level, requires approved application)
2859
- const bountyId = payload?.bountyId;
2860
- if (!bountyId)
2861
- throw new Error("submit_bounty_work requires bountyId");
2862
- const workContent = (suggestedContent ?? payload?.content ?? "");
2863
- if (!workContent)
2864
- throw new Error("submit_bounty_work requires content");
2865
- const cids = (payload?.deliverableCids ?? []);
2866
- const projectId = payload?.projectId;
2867
- const commitIds = (payload?.commitIds ?? []);
2868
- const subResult = await this.runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions`, {
2869
- content: workContent,
2870
- deliverableCids: cids,
2871
- ...(projectId ? { projectId } : {}),
2872
- ...(commitIds.length > 0 ? { commitIds } : {}),
2873
- });
2874
- result = subResult;
2875
- break;
2876
- }
2877
- case "claim":
2878
- case "claim_bounty": {
2879
- // Bounty claiming — supervised action, requires explicit approval
2880
- // Only works if agent is the selected winner (application flow gate)
2881
- const bountyId = payload?.bountyId;
2882
- if (!bountyId)
2883
- throw new Error("claim_bounty requires bountyId");
2884
- // Use prepare+sign+relay flow (the old POST /v1/bounties/:id/claim is deprecated/410)
2885
- const claimRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/claim`, {});
2886
- txHash = claimRelay.txHash;
2887
- result = claimRelay;
2888
- break;
2889
- }
2890
- case "approve_bounty_claimer": {
2891
- // Approve who can claim a bounty on-chain (V5) — only bounty creator can call
2892
- const bountyId = payload?.bountyId;
2893
- const claimer = payload?.claimer;
2894
- if (!bountyId)
2895
- throw new Error("approve_bounty_claimer requires bountyId");
2896
- if (!claimer)
2897
- throw new Error("approve_bounty_claimer requires claimer");
2898
- const approveRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer });
2899
- txHash = approveRelay.txHash;
2900
- result = approveRelay;
2901
- break;
2902
- }
2903
- case "approve_token": {
2904
- // Direct ERC-20 approval — check balance + allowance via gateway, approve via gateway endpoint
2905
- const tokenAddr = payload?.tokenAddress;
2906
- const spenderAddr = payload?.spenderAddress;
2907
- const amount = payload?.amount;
2908
- if (!tokenAddr)
2909
- throw new Error("approve_token requires tokenAddress");
2910
- if (!spenderAddr)
2911
- throw new Error("approve_token requires spenderAddress");
2912
- if (!amount)
2913
- throw new Error("approve_token requires amount");
2914
- // Token approval is handled via gateway token endpoints
2915
- result = await this.runtime.connection.request("POST", "/v1/token/approve", { tokenAddress: tokenAddr, spenderAddress: spenderAddr, amount });
2916
- break;
2917
- }
2918
- case "check_token_balance": {
2919
- result = await this.runtime.connection.request("GET", "/v1/token/balance");
2920
- break;
2921
- }
2922
- case "approve_bounty_application": {
2923
- const bountyId = payload?.bountyId;
2924
- const applicationId = payload?.applicationId;
2925
- if (!bountyId || !applicationId)
2926
- throw new Error("approve_bounty_application requires bountyId and applicationId");
2927
- result = await this.runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/approve`, {});
2928
- break;
2929
- }
2930
- case "reject_bounty_application": {
2931
- const bountyId = payload?.bountyId;
2932
- const applicationId = payload?.applicationId;
2933
- if (!bountyId || !applicationId)
2934
- throw new Error("reject_bounty_application requires bountyId and applicationId");
2935
- result = await this.runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/reject`, {});
2936
- break;
2937
- }
2938
- case "select_bounty_submission": {
2939
- const bountyId = payload?.bountyId;
2940
- const submissionId = payload?.submissionId;
2941
- if (!bountyId || !submissionId)
2942
- throw new Error("select_bounty_submission requires bountyId and submissionId");
2943
- result = await this.runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions/${submissionId}/select`, {});
2944
- // Bridge: approve winner on-chain so they can claim
2945
- try {
2946
- const winner = result?.winner?.applicantAddress;
2947
- if (winner) {
2948
- const approveRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer: winner });
2949
- txHash = approveRelay.txHash;
2950
- }
2951
- }
2952
- catch { /* non-fatal — on-chain approval can be retried */ }
2953
- break;
2954
- }
2955
- case "create_bounty": {
2956
- // Create on-chain bounty — supervised action, requires approval.
2957
- // Supports optional projectId/taskId for auto-linking via relay post-hook.
2958
- const title = (suggestedContent ?? payload?.title);
2959
- const desc = (payload?.description ?? "");
2960
- const community = (payload?.community ?? "general");
2961
- const deadlineDays = (payload?.deadlineDays ?? 7);
2962
- const reward = payload?.tokenRewardAmount;
2963
- if (!title)
2964
- throw new Error("create_bounty requires title");
2965
- if (!reward)
2966
- throw new Error("create_bounty requires tokenRewardAmount");
2967
- const deadlineTs = Math.floor(Date.now() / 1000) + deadlineDays * 86400;
2968
- const bountyBody = {
2969
- title, description: desc, community,
2970
- deadline: deadlineTs, tokenRewardAmount: reward,
2971
- tokenAddress: payload?.tokenAddress ?? "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
2972
- };
2973
- if (payload?.projectId)
2974
- bountyBody.projectId = payload.projectId;
2975
- if (payload?.taskId)
2976
- bountyBody.taskId = payload.taskId;
2977
- const bountyRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/bounty", bountyBody);
2978
- txHash = bountyRelay.txHash;
2979
- result = bountyRelay;
2980
- break;
2981
- }
2982
- case "create_bundle": {
2983
- const name = (suggestedContent ?? payload?.title ?? payload?.name);
2984
- if (!name)
2985
- throw new Error("create_bundle requires name or title");
2986
- const bundleBody = { name };
2987
- const cids = payload?.cids;
2988
- if (cids && Array.isArray(cids) && cids.length > 0)
2989
- bundleBody.cids = cids;
2990
- if (payload?.description)
2991
- bundleBody.description = payload.description;
2992
- if (payload?.bundleType)
2993
- bundleBody.bundleType = payload.bundleType;
2994
- if (payload?.tags)
2995
- bundleBody.tags = payload.tags;
2996
- if (payload?.domain)
2997
- bundleBody.domain = payload.domain;
2998
- if (payload?.summary)
2999
- bundleBody.summary = payload.summary;
3000
- if (payload?.contributors)
3001
- bundleBody.contributors = payload.contributors;
3002
- const bundleRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/bundle", bundleBody);
3003
- txHash = bundleRelay.txHash;
3004
- result = bundleRelay;
3005
- break;
3006
- }
3007
- case "add_collaborator": {
3008
- const projId = payload?.projectId;
3009
- const collabAddr = (payload?.collaboratorAddress ?? payload?.address);
3010
- const role = (payload?.role ?? "editor");
3011
- if (!projId || !collabAddr)
3012
- throw new Error("add_collaborator requires projectId and collaboratorAddress");
3013
- const addResult = await this.runtime.projects.addCollaborator(projId, collabAddr, role);
3014
- result = addResult;
3015
- break;
3016
- }
3017
- case "link_project_to_guild":
3018
- case "link_project_to_clique": {
3019
- // Link a project to a guild, set attribution, and add all accepted guild members as editors
3020
- const projId2 = payload?.projectId;
3021
- const gId = (payload?.guildId ?? payload?.cliqueId);
3022
- if (!projId2 || gId == null)
3023
- throw new Error("link_project_to_guild requires projectId and guildId");
3024
- // 1. Link project to guild
3025
- await this.runtime.guilds.linkProject(gId, projId2);
3026
- // 2. Set guild attribution
3027
- await this.runtime.projects.setGuildAttribution(projId2, String(gId));
3028
- // 3. Get guild members and add accepted ones as editors
3029
- // Gateway returns members as [{address, status}] objects where status=2 means accepted
3030
- const guild = await this.runtime.guilds.get(gId);
3031
- const rawMembers = (guild?.members ?? []);
3032
- const myAddress = this.runtime.connection.address;
3033
- const accepted = rawMembers.filter((m) => {
3034
- // Handle both enriched {address, status} objects and legacy bare string[] format
3035
- if (typeof m === "string")
3036
- return false; // no status info — can't confirm accepted
3037
- return m.status === 2 && m.address?.toLowerCase() !== myAddress?.toLowerCase();
3038
- });
3039
- const addResults = await Promise.allSettled(accepted.map((m) => this.runtime.projects.addCollaborator(projId2, m.address, "editor")));
3040
- const collabsAdded = addResults.filter((r) => r.status === "fulfilled").length;
3041
- result = { linked: true, projectId: projId2, guildId: gId, collaboratorsAdded: collabsAdded };
3042
- break;
3043
- }
3044
- case "propose_collab": {
3045
- // Send a collaboration request — essentially a DM with intent
3046
- const addr = (payload?.targetAddress ?? payload?.address);
3047
- const message = suggestedContent ?? payload?.message ?? "I'd love to collaborate on your project!";
3048
- if (!addr)
3049
- throw new Error("propose_collab requires targetAddress");
3050
- await this.runtime.inbox.send({ to: addr, content: message });
3051
- result = { sent: true, to: addr };
3052
- break;
3053
- }
3054
- case "deploy_preview": {
3055
- const projectId = payload?.projectId;
3056
- if (!projectId)
3057
- throw new Error("deploy_preview requires projectId");
3058
- const relay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/project/${projectId}/deployment`, { prepaidHours: payload?.prepaidHours ?? 2 });
3059
- txHash = relay.txHash;
3060
- result = { txHash, projectId };
3061
- break;
3062
- }
3063
- case "fork_project": {
3064
- const projId = payload?.projectId;
3065
- if (!projId)
3066
- throw new Error("fork_project requires projectId");
3067
- const forkRes = await this.runtime.projects.forkProject(projId, {
3068
- name: payload?.name,
3069
- });
3070
- result = forkRes;
3071
- break;
3072
- }
3073
- case "create_merge_request": {
3074
- const srcId = payload?.sourceProjectId;
3075
- const tgtId = payload?.targetProjectId;
3076
- const mrTitle = payload?.title;
3077
- const commitIds = payload?.commitIds;
3078
- if (!srcId || !tgtId || !mrTitle || !commitIds?.length) {
3079
- throw new Error("create_merge_request requires sourceProjectId, targetProjectId, title, commitIds");
3080
- }
3081
- const mr = await this.runtime.projects.createMergeRequest(srcId, tgtId, mrTitle, commitIds, payload?.description);
3082
- result = mr;
3083
- break;
3084
- }
3085
- case "list_merge_requests": {
3086
- const projId = payload?.projectId;
3087
- if (!projId)
3088
- throw new Error("list_merge_requests requires projectId");
3089
- const mrList = await this.runtime.projects.listMergeRequests(projId, {
3090
- status: payload?.status,
3091
- });
3092
- result = mrList;
3093
- break;
3094
- }
3095
- case "get_merge_request": {
3096
- const projId = payload?.projectId;
3097
- const mrId = payload?.mrId;
3098
- if (!projId || !mrId)
3099
- throw new Error("get_merge_request requires projectId and mrId");
3100
- const mrDetail = await this.runtime.projects.getMergeRequest(projId, mrId);
3101
- result = mrDetail;
3102
- break;
3103
- }
3104
- case "merge_merge_request": {
3105
- const projId = payload?.projectId;
3106
- const mrId = payload?.mrId;
3107
- if (!projId || !mrId)
3108
- throw new Error("merge_merge_request requires projectId and mrId");
3109
- result = await this.runtime.projects.mergeMergeRequest(projId, mrId, payload?.comment);
3110
- break;
3111
- }
3112
- case "close_merge_request": {
3113
- const projId = payload?.projectId;
3114
- const mrId = payload?.mrId;
3115
- if (!projId || !mrId)
3116
- throw new Error("close_merge_request requires projectId and mrId");
3117
- const closedMr = await this.runtime.projects.closeMergeRequest(projId, mrId, payload?.comment);
3118
- result = closedMr;
3119
- break;
3120
- }
3121
- case "claim_reward": {
3122
- const pool = (payload?.pool ?? "nook");
3123
- const rewardRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/reward/claim", { pool });
3124
- txHash = rewardRelay.txHash;
3125
- result = { txHash, pool };
3126
- break;
3127
- }
3128
- case "create_task": {
3129
- const projId = payload?.projectId;
3130
- const title = (suggestedContent ?? payload?.title);
3131
- if (!projId || !title)
3132
- throw new Error("create_task requires projectId and title");
3133
- const task = await this.runtime.projects.createTask(projId, {
3134
- title,
3135
- description: payload?.description,
3136
- milestoneId: payload?.milestoneId,
3137
- priority: (payload?.priority ?? "medium"),
3138
- labels: payload?.labels,
3139
- dueDate: payload?.dueDate,
3140
- });
3141
- result = task;
3142
- break;
3143
- }
3144
- case "assign_task": {
3145
- const projId = payload?.projectId;
3146
- const tid = payload?.taskId;
3147
- const assigneeAddr = (payload?.assigneeAddress ?? payload?.assignee);
3148
- if (!projId || !tid || !assigneeAddr)
3149
- throw new Error("assign_task requires projectId, taskId, and assigneeAddress");
3150
- const assigned = await this.runtime.projects.assignTask(projId, tid, assigneeAddr);
3151
- result = assigned;
3152
- break;
3153
- }
3154
- case "complete_task":
3155
- case "update_task": {
3156
- const projId = payload?.projectId;
3157
- const tid = payload?.taskId;
3158
- if (!projId || !tid)
3159
- throw new Error(`${actionType} requires projectId and taskId`);
3160
- const updates = {};
3161
- if (actionType === "complete_task") {
3162
- updates.status = "completed";
3163
- }
3164
- else {
3165
- if (payload?.status)
3166
- updates.status = payload.status;
3167
- if (payload?.title)
3168
- updates.title = payload.title;
3169
- if (payload?.description)
3170
- updates.description = payload.description;
3171
- if (payload?.priority)
3172
- updates.priority = payload.priority;
3173
- if (payload?.milestoneId !== undefined)
3174
- updates.milestoneId = payload.milestoneId;
3175
- if (payload?.labels)
3176
- updates.labels = payload.labels;
3177
- if (payload?.dueDate)
3178
- updates.dueDate = payload.dueDate;
3179
- }
3180
- const updated = await this.runtime.projects.updateTask(projId, tid, updates);
3181
- result = updated;
3182
- break;
3183
- }
3184
- case "find_matching_agents":
3185
- case "find_agents": {
3186
- const skills = payload?.skills;
3187
- if (!skills?.length)
3188
- throw new Error("find_matching_agents requires skills array");
3189
- const matchResult = await this.runtime.matching.findAgents(skills, {
3190
- count: payload?.count,
3191
- availableOnly: payload?.availableOnly,
3192
- });
3193
- result = matchResult;
3194
- break;
3195
- }
3196
- case "assemble_team": {
3197
- const description = (suggestedContent ?? payload?.description);
3198
- if (!description)
3199
- throw new Error("assemble_team requires description");
3200
- const teamResult = await this.runtime.matching.assembleTeam({
3201
- description,
3202
- requiredSkills: payload?.requiredSkills,
3203
- teamSize: payload?.teamSize,
3204
- filters: payload?.filters,
3205
- });
3206
- result = teamResult;
3207
- break;
3208
- }
3209
- case "list_service": {
3210
- const title = (suggestedContent ?? payload?.title);
3211
- const desc = (payload?.description ?? "");
3212
- const category = (payload?.category ?? "general");
3213
- if (!title)
3214
- throw new Error("list_service requires title");
3215
- const listRelay = await this.runtime.marketplace.createListing({
3216
- title,
3217
- description: desc,
3218
- category,
3219
- pricingModel: payload?.pricingModel,
3220
- priceAmount: payload?.priceAmount,
3221
- tags: payload?.tags,
3222
- tokenAddress: payload?.tokenAddress,
3223
- });
3224
- txHash = listRelay.txHash;
3225
- result = { txHash, title, category };
3226
- break;
3227
- }
3228
- case "create_agreement": {
3229
- const listingId = payload?.listingId;
3230
- const terms = (suggestedContent ?? payload?.terms);
3231
- const deadline = (payload?.deadline ?? Math.floor(Date.now() / 1000) + 7 * 86400);
3232
- if (listingId == null || !terms)
3233
- throw new Error("create_agreement requires listingId and terms");
3234
- const agreeRelay = await this.runtime.marketplace.createAgreement({
3235
- listingId,
3236
- terms,
3237
- deadline,
3238
- tokenAmount: payload?.tokenAmount,
3239
- tokenAddress: payload?.tokenAddress,
3240
- });
3241
- txHash = agreeRelay.txHash;
3242
- result = { txHash, listingId };
3243
- break;
3244
- }
3245
- case "deliver_work": {
3246
- const agId = payload?.agreementId;
3247
- const deliveryCid = (suggestedContent ?? payload?.deliveryCid);
3248
- if (agId == null || !deliveryCid)
3249
- throw new Error("deliver_work requires agreementId and deliveryCid");
3250
- const deliverRelay = await this.runtime.marketplace.deliver(agId, deliveryCid);
3251
- txHash = deliverRelay.txHash;
3252
- result = { txHash, agreementId: agId };
3253
- break;
3254
- }
3255
- case "settle_agreement": {
3256
- const agId = payload?.agreementId;
3257
- if (agId == null)
3258
- throw new Error("settle_agreement requires agreementId");
3259
- const settleRelay = await this.runtime.marketplace.settle(agId);
3260
- txHash = settleRelay.txHash;
3261
- result = { txHash, agreementId: agId };
3262
- break;
3263
- }
3264
- case "dispute_agreement": {
3265
- const agId = payload?.agreementId;
3266
- if (agId == null)
3267
- throw new Error("dispute_agreement requires agreementId");
3268
- const disputeRelay = await this.runtime.marketplace.dispute(agId);
3269
- txHash = disputeRelay.txHash;
3270
- result = { txHash, agreementId: agId };
3271
- break;
3272
- }
3273
- case "cancel_agreement": {
3274
- const agId = payload?.agreementId;
3275
- if (agId == null)
3276
- throw new Error("cancel_agreement requires agreementId");
3277
- const cancelRelay = await this.runtime.marketplace.cancel(agId);
3278
- txHash = cancelRelay.txHash;
3279
- result = { txHash, agreementId: agId };
3280
- break;
3281
- }
3282
- case "expire_dispute": {
3283
- const agId = payload?.agreementId;
3284
- if (agId == null)
3285
- throw new Error("expire_dispute requires agreementId");
3286
- const expDisputeRelay = await this.runtime.marketplace.expireDispute(agId);
3287
- txHash = expDisputeRelay.txHash;
3288
- result = { txHash, agreementId: agId };
3289
- break;
3290
- }
3291
- case "expire_delivered": {
3292
- const agId = payload?.agreementId;
3293
- if (agId == null)
3294
- throw new Error("expire_delivered requires agreementId");
3295
- const expDeliveredRelay = await this.runtime.marketplace.expireDelivered(agId);
3296
- txHash = expDeliveredRelay.txHash;
3297
- result = { txHash, agreementId: agId };
3298
- break;
3299
- }
3300
- case "submit_review": {
3301
- const agId = payload?.agreementId;
3302
- const rating = payload?.rating;
3303
- const comment = (suggestedContent ?? payload?.comment ?? "");
3304
- if (agId == null || rating == null)
3305
- throw new Error("submit_review requires agreementId and rating");
3306
- const reviewResult = await this.runtime.marketplace.submitReview(agId, rating, comment);
3307
- result = reviewResult;
3308
- break;
3309
- }
3310
- case "send_agreement_message": {
3311
- const agId = payload?.agreementId;
3312
- const msgType = (payload?.messageType ?? "general");
3313
- const content = (suggestedContent ?? payload?.content);
3314
- if (agId == null || !content)
3315
- throw new Error("send_agreement_message requires agreementId and content");
3316
- const msgResult = await this.runtime.marketplace.sendAgreementMessage(agId, msgType, content, payload?.attachmentCid);
3317
- result = msgResult;
3318
- break;
3319
- }
3320
- // (create_bundle handled above — consolidated single handler)
3321
- case "update_service": {
3322
- const listingId = payload?.listingId;
3323
- if (!listingId)
3324
- throw new Error("update_service requires listingId");
3325
- const updateResult = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/update", { listingId, ...payload });
3326
- result = updateResult;
3327
- break;
3328
- }
3329
- case "approve_bounty_work": {
3330
- const bountyId = payload?.bountyId;
3331
- if (!bountyId)
3332
- throw new Error("approve_bounty_work requires bountyId");
3333
- const r = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/approve`, {});
3334
- result = r;
3335
- break;
3336
- }
3337
- case "dispute_bounty_work": {
3338
- const bountyId = payload?.bountyId;
3339
- if (!bountyId)
3340
- throw new Error("dispute_bounty_work requires bountyId");
3341
- const r = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/dispute`, {});
3342
- result = r;
3343
- break;
3344
- }
3345
- case "cancel_bounty": {
3346
- const bountyId = payload?.bountyId;
3347
- if (!bountyId)
3348
- throw new Error("cancel_bounty requires bountyId");
3349
- const r = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/cancel`, {});
3350
- result = r;
3351
- break;
3352
- }
3353
- case "unclaim_bounty": {
3354
- const bountyId = payload?.bountyId;
3355
- if (!bountyId)
3356
- throw new Error("unclaim_bounty requires bountyId");
3357
- const r = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${bountyId}/unclaim`, {});
3358
- result = r;
3359
- break;
3360
- }
3361
- case "accept_invitation": {
3362
- const invId = payload?.invitationId;
3363
- if (!invId)
3364
- throw new Error("accept_invitation requires invitationId");
3365
- const r = await this.runtime.connection.request("POST", `/v1/teams/invitations/${invId}/accept`, {});
3366
- result = r;
3367
- break;
3368
- }
3369
- case "decline_invitation": {
3370
- const invId = payload?.invitationId;
3371
- if (!invId)
3372
- throw new Error("decline_invitation requires invitationId");
3373
- const r = await this.runtime.connection.request("POST", `/v1/teams/invitations/${invId}/decline`, {});
3374
- result = r;
3375
- break;
3376
- }
3377
- case "grant": {
3378
- const projId = payload?.projectId;
3379
- const bountyId = payload?.bountyId;
3380
- const reqId = payload?.requestId;
3381
- if (!projId || !bountyId || !reqId)
3382
- throw new Error("grant requires projectId, bountyId, requestId");
3383
- result = await this.runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bountyId}/grant-access`, { requestId: reqId });
3384
- break;
3385
- }
3386
- case "deny": {
3387
- const projId = payload?.projectId;
3388
- const bountyId = payload?.bountyId;
3389
- const reqId = payload?.requestId;
3390
- if (!projId || !bountyId || !reqId)
3391
- throw new Error("deny requires projectId, bountyId, requestId");
3392
- result = await this.runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bountyId}/deny-access`, { requestId: reqId });
3393
- break;
3394
- }
3395
- case "workspace_create": {
3396
- const wsName = (payload?.name ?? suggestedContent);
3397
- if (!wsName)
3398
- throw new Error("workspace_create requires name");
3399
- const ws = await this.runtime.workspaces.create({
3400
- name: wsName,
3401
- description: payload?.description,
3402
- sourceType: payload?.sourceType,
3403
- sourceId: payload?.sourceId,
3404
- });
3405
- result = ws;
3406
- break;
3407
- }
3408
- case "workspace_set": {
3409
- const wsId = payload?.workspaceId;
3410
- const wsKey = payload?.key;
3411
- const wsVal = payload?.value ?? suggestedContent;
3412
- if (!wsId || !wsKey)
3413
- throw new Error("workspace_set requires workspaceId and key");
3414
- const wsResult = await this.runtime.workspaces.setState(wsId, wsKey, wsVal);
3415
- result = wsResult;
3416
- break;
3417
- }
3418
- case "workspace_snapshot": {
3419
- const wsId = payload?.workspaceId;
3420
- if (!wsId)
3421
- throw new Error("workspace_snapshot requires workspaceId");
3422
- const snap = await this.runtime.workspaces.createSnapshot(wsId, payload?.label);
3423
- result = snap;
3424
- break;
3425
- }
3426
- case "propose_action": {
3427
- const wsId = payload?.workspaceId;
3428
- const title = (payload?.title ?? suggestedContent);
3429
- if (!wsId || !title)
3430
- throw new Error("propose_action requires workspaceId and title");
3431
- const prop = await this.runtime.workspaces.createProposal(wsId, {
3432
- title,
3433
- description: payload?.description,
3434
- actionType: payload?.actionType ?? "custom",
3435
- actionPayload: payload?.actionPayload,
3436
- quorumType: payload?.quorumType,
3437
- quorumThreshold: payload?.quorumThreshold,
3438
- });
3439
- result = prop;
3440
- break;
3441
- }
3442
- case "vote_proposal": {
3443
- const wsId = payload?.workspaceId;
3444
- const proposalId = payload?.proposalId;
3445
- const vote = payload?.vote;
3446
- if (!wsId || !proposalId || vote === undefined)
3447
- throw new Error("vote_proposal requires workspaceId, proposalId, vote");
3448
- const voteResult = await this.runtime.workspaces.vote(wsId, proposalId, vote, payload?.reason);
3449
- result = voteResult;
3450
- break;
3451
- }
3452
- case "cancel_proposal": {
3453
- const wsId = payload?.workspaceId;
3454
- const proposalId = payload?.proposalId;
3455
- if (!wsId || !proposalId)
3456
- throw new Error("cancel_proposal requires workspaceId and proposalId");
3457
- const cancelResult = await this.runtime.workspaces.cancelProposal(wsId, proposalId);
3458
- result = cancelResult;
3459
- break;
3460
- }
3461
- case "send_dm": {
3462
- const target = payload?.recipientAddress;
3463
- const content = (suggestedContent ?? payload?.content);
3464
- if (!target || !content)
3465
- throw new Error("send_dm requires recipientAddress and content");
3466
- await this.runtime.inbox.send({ to: target, content });
3467
- result = { sent: true };
3468
- break;
3469
- }
3470
- case "create_listing": {
3471
- // Alias for list_service — same prepare endpoint
3472
- const listing = payload ?? {};
3473
- const listRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/list", listing);
3474
- txHash = listRelay.txHash;
3475
- result = listRelay;
3476
- break;
3477
- }
3478
- case "join_guild":
3479
- case "approve_guild": {
3480
- const guildId = payload?.guildId;
3481
- if (!guildId)
3482
- throw new Error("approve_guild requires guildId");
3483
- const approveRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/guild/${guildId}/approve`, {});
3484
- txHash = approveRelay.txHash;
3485
- result = approveRelay;
3486
- break;
3487
- }
3488
- case "reject_guild": {
3489
- const guildId = payload?.guildId;
3490
- if (!guildId)
3491
- throw new Error("reject_guild requires guildId");
3492
- const rejectRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/guild/${guildId}/reject`, {});
3493
- txHash = rejectRelay.txHash;
3494
- result = rejectRelay;
3495
- break;
3496
- }
3497
- case "leave_guild": {
3498
- const guildId = payload?.guildId;
3499
- if (!guildId)
3500
- throw new Error("leave_guild requires guildId");
3501
- const leaveRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/guild/${guildId}/leave`, {});
3502
- txHash = leaveRelay.txHash;
3503
- result = leaveRelay;
3504
- break;
3505
- }
3506
- // ── Real World Actions ─────────────────────────────────
3507
- case "egress_request":
3508
- case "http_request": {
3509
- // Outbound HTTP request via the egress proxy (0.15 credits)
3510
- const targetUrl = payload?.url ?? "";
3511
- const httpMethod = (payload?.method ?? "GET").toUpperCase();
3512
- if (!targetUrl)
3513
- throw new Error("egress_request requires url");
3514
- const httpResult = await this.runtime.tools.httpRequest(targetUrl, httpMethod, {
3515
- headers: payload?.headers,
3516
- body: payload?.body,
3517
- timeout: payload?.timeout,
3518
- credentialService: payload?.credentialService,
3519
- });
3520
- result = httpResult;
3521
- break;
3522
- }
3523
- case "execute_tool": {
3524
- // Execute a registered tool through the action registry
3525
- const toolName = (payload?.toolName ?? payload?.name);
3526
- const toolArgs = (payload?.args ?? payload?.input ?? {});
3527
- if (!toolName)
3528
- throw new Error("execute_tool requires toolName");
3529
- const toolResult = await this.runtime.tools.executeTool(toolName, toolArgs);
3530
- result = toolResult;
3531
- break;
3532
- }
3533
- case "exec_code": {
3534
- const cmd = (payload?.command ?? suggestedContent);
3535
- const img = (payload?.image ?? "node:20-slim");
3536
- const files = payload?.files;
3537
- const timeout = payload?.timeout;
3538
- const projId = payload?.projectId;
3539
- if (!cmd)
3540
- throw new Error("exec_code requires command");
3541
- const execResult = await this.runtime.tools.execCode(cmd, img, { files, timeout, projectId: projId });
3542
- result = execResult;
3543
- break;
3544
- }
3545
- case "connect_mcp_server": {
3546
- // Connect to an external MCP server
3547
- const serverUrl = payload?.serverUrl;
3548
- const serverName = (payload?.serverName ?? payload?.name);
3549
- if (!serverUrl || !serverName)
3550
- throw new Error("connect_mcp_server requires serverUrl and serverName");
3551
- const mcpResult = await this.runtime.tools.connectMcpServer(serverUrl, serverName);
3552
- result = mcpResult;
3553
- break;
3554
- }
3555
- case "disconnect_mcp_server": {
3556
- const serverId = payload?.serverId;
3557
- if (!serverId)
3558
- throw new Error("disconnect_mcp_server requires serverId");
3559
- await this.runtime.tools.disconnectMcpServer(serverId);
3560
- result = { disconnected: true, serverId };
3561
- break;
3562
- }
3563
- case "call_mcp_tool":
3564
- case "use_mcp_tool": {
3565
- // Call a tool from a connected MCP server — goes through action registry
3566
- const mcpToolName = (payload?.toolName ?? payload?.name);
3567
- const mcpToolArgs = (payload?.args ?? payload?.input ?? {});
3568
- if (!mcpToolName)
3569
- throw new Error("call_mcp_tool requires toolName");
3570
- const mcpToolResult = await this.runtime.tools.executeTool(mcpToolName, mcpToolArgs);
3571
- result = mcpToolResult;
3572
- break;
3573
- }
3574
- case "register_webhook": {
3575
- // Register a webhook source for inbound events
3576
- const source = (payload?.source ?? payload?.name);
3577
- if (!source)
3578
- throw new Error("register_webhook requires source");
3579
- const regResult = await this.runtime.tools.registerWebhook(source, {
3580
- secret: payload?.secret,
3581
- signatureHeader: payload?.signatureHeader,
3582
- eventMapping: payload?.eventMapping,
3583
- });
3584
- result = regResult;
3585
- break;
3586
- }
3587
- // ── Insight actions (strategy propagation) ─────────────────────
3588
- case "publish_insight": {
3589
- const title = payload?.title;
3590
- const body = payload?.body;
3591
- if (!title || !body)
3592
- throw new Error("publish_insight requires title and body");
3593
- const insightResult = await this.runtime.insights.publish({
3594
- title,
3595
- body,
3596
- strategyType: payload?.strategyType,
3597
- tags: payload?.tags,
3598
- context: payload?.context,
3599
- outcomeScore: payload?.outcomeScore,
3600
- workspaceId: payload?.workspaceId,
3601
- });
3602
- result = insightResult;
3603
- break;
3604
- }
3605
- case "cite_insight": {
3606
- const insightId = payload?.insightId;
3607
- if (!insightId)
3608
- throw new Error("cite_insight requires insightId");
3609
- const citeResult = await this.runtime.insights.cite(insightId, payload?.context, payload?.outcomeScore);
3610
- result = citeResult;
3611
- break;
3612
- }
3613
- case "apply_insight": {
3614
- const insightId = payload?.insightId;
3615
- if (!insightId)
3616
- throw new Error("apply_insight requires insightId");
3617
- const applyResult = await this.runtime.insights.apply(insightId, payload?.context, payload?.outcomeScore);
3618
- result = applyResult;
3619
- break;
3620
- }
3621
- // ── Treasury actions ──
3622
- case "deposit_treasury": {
3623
- const guildId = payload?.guildId;
3624
- const amount = payload?.amount;
3625
- if (!guildId || !amount)
3626
- throw new Error("deposit_treasury requires guildId and amount");
3627
- const depositResult = await this.runtime.guilds.depositTreasury(guildId, amount, payload?.memo);
3628
- result = depositResult;
3629
- break;
3630
- }
3631
- case "withdraw_treasury": {
3632
- const guildId = payload?.guildId;
3633
- const amount = payload?.amount;
3634
- if (!guildId || !amount)
3635
- throw new Error("withdraw_treasury requires guildId and amount");
3636
- const withdrawResult = await this.runtime.guilds.withdrawTreasury(guildId, amount, payload?.memo);
3637
- result = withdrawResult;
3638
- break;
3639
- }
3640
- case "fund_bounty_from_treasury": {
3641
- const guildId = payload?.guildId;
3642
- const bountyId = payload?.bountyId;
3643
- const amount = payload?.amount;
3644
- if (!guildId || !bountyId || !amount)
3645
- throw new Error("fund_bounty_from_treasury requires guildId, bountyId, amount");
3646
- const fundResult = await this.runtime.guilds.fundBountyFromTreasury(guildId, bountyId, amount, payload?.proposalId);
3647
- result = fundResult;
3648
- break;
3649
- }
3650
- case "distribute_revenue": {
3651
- const guildId = payload?.guildId;
3652
- if (!guildId)
3653
- throw new Error("distribute_revenue requires guildId");
3654
- const distResult = await this.runtime.guilds.distributeRevenue(guildId, payload?.amount);
3655
- result = distResult;
3656
- break;
3657
- }
3658
- // ── Swarm actions ──
3659
- case "create_swarm": {
3660
- const swarmResult = await this.runtime.swarms.create({
3661
- title: payload?.title || "",
3662
- description: payload?.description,
3663
- workspaceId: payload?.workspaceId,
3664
- subtasks: payload?.subtasks || [],
3665
- });
3666
- result = swarmResult;
3667
- break;
3668
- }
3669
- case "claim_subtask": {
3670
- const subtaskId = payload?.subtaskId;
3671
- if (!subtaskId)
3672
- throw new Error("claim_subtask requires subtaskId");
3673
- const claimResult = await this.runtime.swarms.claimSubtask(subtaskId);
3674
- result = claimResult;
3675
- break;
3676
- }
3677
- case "submit_swarm_result": {
3678
- const subtaskId = payload?.subtaskId;
3679
- if (!subtaskId)
3680
- throw new Error("submit_swarm_result requires subtaskId");
3681
- const submitResult = await this.runtime.swarms.submitResult(subtaskId, payload?.content, payload?.resultType);
3682
- result = submitResult;
3683
- break;
3684
- }
3685
- case "aggregate_swarm": {
3686
- const swarmId = payload?.swarmId;
3687
- if (!swarmId)
3688
- throw new Error("aggregate_swarm requires swarmId");
3689
- const aggResult = await this.runtime.swarms.aggregate(swarmId, payload?.summary);
3690
- result = aggResult;
3691
- break;
3692
- }
3693
- case "record_gap": {
3694
- await this.runtime.specialization.recordGap({
3695
- queryText: payload?.queryText || "",
3696
- queryTags: payload?.queryTags || [],
3697
- communityId: payload?.communityId,
3698
- });
3699
- result = { recorded: true };
3700
- break;
3701
- }
3702
- case "update_proficiency": {
3703
- const domain = payload?.skillDomain;
3704
- if (!domain)
3705
- throw new Error("update_proficiency requires skillDomain");
3706
- const profResult = await this.runtime.specialization.updateProficiency(domain, Number(payload?.proficiency || 0));
3707
- result = profResult;
3708
- break;
3709
- }
3710
- case "generate_recommendations": {
3711
- const recs = await this.runtime.specialization.generateRecommendations();
3712
- result = { recommendations: recs };
3713
- break;
3714
- }
3715
- case "dismiss_recommendation": {
3716
- const recId = payload?.recommendationId;
3717
- if (!recId)
3718
- throw new Error("dismiss_recommendation requires recommendationId");
3719
- await this.runtime.specialization.dismissRecommendation(recId);
3720
- result = { dismissed: true };
3721
- break;
3722
- }
3723
- // ── Intents ──
3724
- case "create_intent": {
3725
- const intentResult = await this.runtime.intents.create({
3726
- title: payload?.title || suggestedContent || "Untitled intent",
3727
- description: payload?.description || suggestedContent || "",
3728
- requiredSkills: payload?.requiredSkills,
3729
- budgetAmount: payload?.budgetAmount,
3730
- category: payload?.category,
3731
- tags: payload?.tags,
3732
- });
3733
- result = intentResult;
3734
- break;
3735
- }
3736
- case "browse_intents": {
3737
- const browseResult = await this.runtime.intents.list({
3738
- status: payload?.status || "open",
3739
- category: payload?.category,
3740
- q: payload?.q,
3741
- limit: 20,
3742
- });
3743
- result = browseResult;
3744
- break;
3745
- }
3746
- case "submit_proposal": {
3747
- const pIntentId = payload?.intentId;
3748
- if (!pIntentId)
3749
- throw new Error("submit_proposal requires intentId");
3750
- const proposalResult = await this.runtime.intents.submitProposal(pIntentId, {
3751
- description: payload?.description || suggestedContent || "",
3752
- approach: payload?.approach,
3753
- estimatedCost: payload?.estimatedCost,
3754
- estimatedDurationHours: payload?.estimatedDurationHours,
3755
- });
3756
- result = proposalResult;
3757
- break;
3758
- }
3759
- case "accept_proposal": {
3760
- const aIntentId = payload?.intentId;
3761
- const aProposalId = payload?.proposalId;
3762
- if (!aIntentId || !aProposalId)
3763
- throw new Error("accept_proposal requires intentId and proposalId");
3764
- const acceptResult = await this.runtime.intents.acceptProposal(aIntentId, aProposalId);
3765
- result = acceptResult;
3766
- break;
3767
- }
3768
- case "reject_proposal": {
3769
- const rpIntentId = payload?.intentId;
3770
- const rpProposalId = payload?.proposalId;
3771
- if (!rpIntentId || !rpProposalId)
3772
- throw new Error("reject_proposal requires intentId and proposalId");
3773
- const rpReason = suggestedContent || payload?.reason || "";
3774
- const rejectResult = await this.runtime.intents.rejectProposal(rpIntentId, rpProposalId, rpReason);
3775
- result = rejectResult;
3776
- break;
3777
- }
3778
- case "cancel_intent": {
3779
- const cIntentId = payload?.intentId;
3780
- if (!cIntentId)
3781
- throw new Error("cancel_intent requires intentId");
3782
- result = await this.runtime.intents.cancel(cIntentId);
3783
- break;
3784
- }
3785
- case "complete_intent": {
3786
- const compIntentId = payload?.intentId;
3787
- if (!compIntentId)
3788
- throw new Error("complete_intent requires intentId");
3789
- result = await this.runtime.intents.complete(compIntentId);
3790
- break;
3791
- }
3792
- case "withdraw_proposal": {
3793
- const wIntentId = payload?.intentId;
3794
- const wProposalId = payload?.proposalId;
3795
- if (!wIntentId || !wProposalId)
3796
- throw new Error("withdraw_proposal requires intentId and proposalId");
3797
- result = await this.runtime.intents.withdrawProposal(wIntentId, wProposalId);
3798
- break;
3799
- }
3800
- // ── Clawnch Token Launching ──
3801
- case "preview_token_launch": {
3802
- const prevResult = await this.runtime.connection.request("POST", "/v1/clawnch/preview", {
3803
- tokenName: payload?.tokenName,
3804
- tokenTicker: payload?.tokenTicker,
3805
- description: (suggestedContent ?? payload?.description),
3806
- imageUrl: payload?.imageUrl,
3807
- });
3808
- result = prevResult;
3809
- break;
3810
- }
3811
- case "launch_token": {
3812
- const launchResult = await this.runtime.connection.request("POST", "/v1/clawnch/launch", {
3813
- tokenName: payload?.tokenName,
3814
- tokenTicker: payload?.tokenTicker,
3815
- description: (suggestedContent ?? payload?.description),
3816
- imageUrl: payload?.imageUrl,
3817
- });
3818
- result = launchResult;
3819
- break;
3820
- }
3821
- case "claim_clawnch_fees": {
3822
- const tokenAddr = payload?.tokenAddress;
3823
- if (!tokenAddr)
3824
- throw new Error("claim_clawnch_fees requires tokenAddress");
3825
- const claimResult = await this.runtime.connection.request("POST", "/v1/clawnch/claim-fees", { tokenAddress: tokenAddr });
3826
- result = claimResult;
3827
- break;
3828
- }
3829
- case "get_token_analytics": {
3830
- const tAddr = payload?.tokenAddress;
3831
- if (!tAddr)
3832
- throw new Error("get_token_analytics requires tokenAddress");
3833
- result = await this.runtime.connection.request("GET", `/v1/clawnch/analytics/token/${tAddr}`);
3834
- break;
3835
- }
3836
- // ── Oracle ──
3837
- case "query_oracle": {
3838
- const entityType = payload?.entityType;
3839
- const entityId = payload?.entityId;
3840
- if (!entityType || !entityId)
3841
- throw new Error("query_oracle requires entityType and entityId");
3842
- switch (entityType) {
3843
- case "project":
3844
- result = await this.runtime.oracle.getProjectSignals(entityId);
3845
- break;
3846
- case "agent":
3847
- result = await this.runtime.oracle.getAgentSignals(entityId);
3848
- break;
3849
- case "intent":
3850
- result = await this.runtime.oracle.getIntentSignals(entityId);
3851
- break;
3852
- case "guild":
3853
- result = await this.runtime.oracle.getGuildSignals(entityId);
3854
- break;
3855
- default:
3856
- throw new Error(`Unknown oracle entity type: ${entityType}`);
3857
- }
3858
- break;
3859
- }
3860
- case "create_search_subscription": {
3861
- const subResult = await this.runtime.discovery.createSubscription({
3862
- label: payload?.label || "Search subscription",
3863
- query: payload?.query || "",
3864
- types: payload?.types,
3865
- frequencyMinutes: payload?.frequencyMinutes,
3866
- });
3867
- result = subResult;
3868
- break;
3869
- }
3870
- // ── Alias actions — map to existing handlers ──
3871
- case "reply": {
3872
- const replyChannelId = (payload?.channelId);
3873
- const replyTo = (payload?.to ?? payload?.targetAddress ?? payload?.senderAddress);
3874
- if (replyChannelId && suggestedContent) {
3875
- await this.runtime.channels.send(replyChannelId, suggestedContent);
3876
- }
3877
- else if (replyTo && suggestedContent) {
3878
- await this.runtime.inbox.send({ to: replyTo, content: suggestedContent });
3879
- }
3880
- result = { sent: true };
3881
- break;
3882
- }
3883
- case "follow":
3884
- case "follow_back": {
3885
- const addr = (payload?.targetAddress ?? payload?.address ?? payload?.senderAddress);
3886
- if (!addr)
3887
- throw new Error("follow requires targetAddress");
3888
- const f = await this.runtime.social.follow(addr);
3889
- txHash = f.txHash;
3890
- result = { txHash };
3891
- break;
3892
- }
3893
- case "attest":
3894
- case "attest_back": {
3895
- const addr = (payload?.targetAddress ?? payload?.address ?? payload?.senderAddress);
3896
- const reason = (suggestedContent ?? payload?.reason ?? "Valued collaborator");
3897
- if (!addr)
3898
- throw new Error("attest requires targetAddress");
3899
- const a = await this.runtime.social.attest(addr, reason);
3900
- txHash = a.txHash;
3901
- result = { txHash };
3902
- break;
3903
- }
3904
- case "send_message": {
3905
- const msgTo = (payload?.to ?? payload?.targetAddress ?? payload?.senderAddress);
3906
- const msgChannelId = payload?.channelId;
3907
- if (msgTo) {
3908
- await this.runtime.inbox.send({ to: msgTo, content: suggestedContent ?? "Hey! Looking forward to collaborating." });
3909
- }
3910
- else if (msgChannelId) {
3911
- await this.runtime.channels.send(msgChannelId, suggestedContent ?? "Hey everyone!");
3912
- }
3913
- result = { sent: true };
3914
- break;
3915
- }
3916
- case "acknowledge": {
3917
- const ackProjectId = payload?.projectId;
3918
- if (ackProjectId) {
3919
- await this.runtime.channels.sendToProject(ackProjectId, suggestedContent ?? "Got it, thanks for the mention!");
2373
+ // ── Intercept browse_tools (client-side, no gateway call needed) ──
2374
+ if (actionType === "browse_tools") {
2375
+ const category = payload?.category;
2376
+ if (!category) {
2377
+ const listing = getCategoryListing();
2378
+ if (this.verbose) {
2379
+ console.log(`[autonomous] browse_tools ${listing.length} categories: ${listing.map((c) => `${c.name} (${c.count})`).join(", ")}`);
3920
2380
  }
3921
- result = { acknowledged: true };
3922
- break;
2381
+ return;
3923
2382
  }
3924
- case "accept": {
3925
- const acceptProjectId = payload?.projectId;
3926
- if (acceptProjectId) {
3927
- await this.runtime.channels.sendToProject(acceptProjectId, suggestedContent ?? "Accepted the task I'll get started.");
3928
- }
3929
- result = { accepted: true };
3930
- break;
2383
+ this.loadedCategories.add(category);
2384
+ const tools = getToolsInCategory(category);
2385
+ if (this.verbose) {
2386
+ console.log(`[autonomous] browse_tools loaded ${tools.length} tools from "${category}"`);
3931
2387
  }
3932
- case "execute": {
3933
- const execChannelId = payload?.channelId;
3934
- const execTo = (payload?.to ?? payload?.targetAddress ?? payload?.senderAddress);
3935
- if (execChannelId && suggestedContent) {
3936
- await this.runtime.channels.send(execChannelId, suggestedContent);
3937
- }
3938
- else if (execTo && suggestedContent) {
3939
- await this.runtime.inbox.send({ to: execTo, content: suggestedContent });
2388
+ return;
2389
+ }
2390
+ // ── Unified dispatch via POST /v1/actions/execute ──
2391
+ // The gateway's ToolDispatcher routes the call to the correct internal
2392
+ // endpoint, replacing the 2000+ line switch statement that was here before.
2393
+ const toolName = `nookplot_${actionType}`;
2394
+ const dispatchPayload = {
2395
+ ...(payload ?? {}),
2396
+ ...(suggestedContent ? { suggestedContent } : {}),
2397
+ };
2398
+ const dispatchResult = await this.runtime.connection.request("POST", "/v1/actions/execute", { toolName, payload: dispatchPayload });
2399
+ switch (dispatchResult.status) {
2400
+ case "completed": {
2401
+ result = (dispatchResult.result ?? {});
2402
+ break;
2403
+ }
2404
+ case "sign_required": {
2405
+ // On-chain action: gateway returned unsigned ForwardRequest — sign locally + relay
2406
+ if (!dispatchResult.forwardRequest || !dispatchResult.domain || !dispatchResult.types) {
2407
+ throw new Error(`sign_required response missing forwardRequest/domain/types for ${actionType}`);
3940
2408
  }
3941
- result = { executed: true };
3942
- break;
3943
- }
3944
- case "review":
3945
- case "comment": {
3946
- const revProjectId = payload?.projectId;
3947
- const revCommitId = payload?.commitId;
3948
- const verdict = actionType === "comment" ? "comment" : (payload?.verdict ?? "comment");
3949
- const body = suggestedContent ?? "Reviewed";
3950
- if (revProjectId && revCommitId) {
3951
- await this.runtime.projects.submitReview(revProjectId, revCommitId, verdict, body);
2409
+ const privateKey = this.runtime.connection.privateKey;
2410
+ if (!privateKey) {
2411
+ throw new Error("Private key not configured — cannot sign on-chain transactions.");
3952
2412
  }
3953
- result = { reviewed: true };
3954
- break;
3955
- }
3956
- case "send_email": {
3957
- const to = payload?.to;
3958
- const subject = (payload?.subject ?? suggestedContent?.slice(0, 100));
3959
- const bodyText = (payload?.body ?? suggestedContent);
3960
- if (!to || !subject || !bodyText)
3961
- throw new Error("send_email requires to, subject, and body");
3962
- result = await this.runtime.connection.request("POST", "/v1/email/send", { to, subject, bodyText });
3963
- break;
3964
- }
3965
- case "reply_email": {
3966
- const messageId = payload?.messageId;
3967
- const bodyText = (payload?.body ?? suggestedContent);
3968
- if (!messageId || !bodyText)
3969
- throw new Error("reply_email requires messageId and body");
3970
- result = await this.runtime.connection.request("POST", `/v1/email/${messageId}/reply`, { bodyText });
3971
- break;
3972
- }
3973
- case "check_email": {
3974
- result = await this.runtime.connection.request("GET", "/v1/email/messages?direction=inbound&limit=10");
3975
- break;
3976
- }
3977
- case "create_email_inbox": {
3978
- const username = payload?.username;
3979
- if (!username)
3980
- throw new Error("create_email_inbox requires username");
3981
- result = await this.runtime.connection.request("POST", "/v1/email/inbox", { username, displayName: payload?.displayName });
3982
- break;
3983
- }
3984
- // ── Skill Registry ──────────────────────────────────────────────
3985
- case "search_skills": {
3986
- const q = payload?.query || payload?.q || "";
3987
- const category = payload?.category;
3988
- const tags = payload?.tags;
3989
- const limit = payload?.limit || 20;
3990
- const qs = new URLSearchParams();
3991
- if (q)
3992
- qs.set("q", q);
3993
- if (category)
3994
- qs.set("category", category);
3995
- if (tags)
3996
- qs.set("tags", tags);
3997
- qs.set("limit", String(limit));
3998
- result = await this.runtime.connection.request("GET", `/v1/skills/registry?${qs.toString()}`);
3999
- break;
4000
- }
4001
- case "publish_skill": {
4002
- const name = payload?.name;
4003
- if (!name)
4004
- throw new Error("publish_skill requires name");
4005
- result = await this.runtime.connection.request("POST", "/v1/skills/registry", {
4006
- name,
4007
- description: payload?.description,
4008
- packageType: payload?.packageType || "skill_md",
4009
- category: payload?.category || "other",
4010
- tags: payload?.tags,
4011
- content: payload?.content,
4012
- githubUrl: payload?.githubUrl,
4013
- npmPackage: payload?.npmPackage,
4014
- version: payload?.version || "1.0.0",
4015
- metadata: payload?.metadata,
4016
- });
4017
- break;
4018
- }
4019
- case "install_skill": {
4020
- const skillId = payload?.skillId;
4021
- if (!skillId)
4022
- throw new Error("install_skill requires skillId");
4023
- result = await this.runtime.connection.request("POST", `/v1/skills/registry/${skillId}/install`);
4024
- break;
4025
- }
4026
- case "review_skill": {
4027
- const skillId = payload?.skillId;
4028
- const rating = payload?.rating;
4029
- if (!skillId || !rating)
4030
- throw new Error("review_skill requires skillId and rating");
4031
- result = await this.runtime.connection.request("POST", `/v1/skills/registry/${skillId}/review`, {
4032
- rating,
4033
- review: payload?.review || payload?.content,
4034
- });
4035
- break;
4036
- }
4037
- case "update_skill": {
4038
- const skillId = payload?.skillId;
4039
- if (!skillId)
4040
- throw new Error("update_skill requires skillId");
4041
- result = await this.runtime.connection.request("PATCH", `/v1/skills/registry/${skillId}`, {
4042
- name: payload?.name,
4043
- description: payload?.description,
4044
- version: payload?.version,
4045
- tags: payload?.tags,
4046
- content: payload?.content,
4047
- metadata: payload?.metadata,
4048
- });
4049
- break;
4050
- }
4051
- case "trending_skills": {
4052
- const limit = payload?.limit || 20;
4053
- result = await this.runtime.connection.request("GET", `/v1/skills/registry/trending?limit=${limit}`);
4054
- break;
4055
- }
4056
- // ── Agent Memory ────────────────────────────────────────────────
4057
- case "store_memory": {
4058
- const content = payload?.content || payload?.body;
4059
- if (!content)
4060
- throw new Error("store_memory requires content");
4061
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/store", {
4062
- type: payload?.memoryType || payload?.type || "semantic",
4063
- content,
4064
- importance: payload?.importance,
4065
- tags: payload?.tags,
4066
- source: payload?.source,
4067
- metadata: payload?.metadata,
4068
- });
4069
- break;
4070
- }
4071
- case "recall_memory": {
4072
- const query = payload?.query || payload?.q;
4073
- if (!query)
4074
- throw new Error("recall_memory requires query");
4075
- const recallTypes = payload?.types
4076
- || (payload?.memoryType || payload?.type ? [String(payload?.memoryType || payload?.type)] : undefined);
4077
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/recall", {
4078
- query,
4079
- types: recallTypes,
4080
- limit: payload?.limit || 10,
4081
- });
4082
- break;
4083
- }
4084
- case "list_memories": {
4085
- const memType = payload?.memoryType || payload?.type || "";
4086
- const limit = payload?.limit || 20;
4087
- const qs = new URLSearchParams();
4088
- if (memType)
4089
- qs.set("type", String(memType));
4090
- qs.set("limit", String(limit));
4091
- result = await this.runtime.connection.request("GET", `/v1/agent-memory/list?${qs.toString()}`);
4092
- break;
4093
- }
4094
- case "memory_stats": {
4095
- result = await this.runtime.connection.request("GET", "/v1/agent-memory/stats");
4096
- break;
4097
- }
4098
- case "export_memories": {
4099
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/export");
4100
- break;
4101
- }
4102
- case "import_memories": {
4103
- const pack = payload?.pack || payload?.memories;
4104
- if (!pack)
4105
- throw new Error("import_memories requires pack data");
4106
- // Gateway expects MemoryPack at top level (memories + packHash), not wrapped in { pack }
4107
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/import", pack);
4108
- break;
4109
- }
4110
- // ── Forge (Agent Deployment) ────────────────────────────────────
4111
- case "forge_deploy": {
4112
- const bundleId = payload?.bundleId;
4113
- const agentAddress = payload?.agentAddress;
4114
- const soulCid = payload?.soulCid;
4115
- if (bundleId == null)
4116
- throw new Error("forge_deploy requires bundleId (number)");
4117
- if (!agentAddress)
4118
- throw new Error("forge_deploy requires agentAddress");
4119
- if (!soulCid)
4120
- throw new Error("forge_deploy requires soulCid");
4121
- const relay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/forge", { bundleId, agentAddress, soulCid, deploymentFee: payload?.deploymentFee });
4122
- txHash = relay.txHash;
4123
- break;
4124
- }
4125
- case "forge_spawn": {
4126
- const spawnBundleId = payload?.bundleId;
4127
- const childAddress = payload?.childAddress || payload?.parentAddress;
4128
- const spawnSoulCid = payload?.soulCid;
4129
- if (spawnBundleId == null)
4130
- throw new Error("forge_spawn requires bundleId (number)");
4131
- if (!childAddress)
4132
- throw new Error("forge_spawn requires childAddress");
4133
- if (!spawnSoulCid)
4134
- throw new Error("forge_spawn requires soulCid");
4135
- const relay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/forge/spawn", { bundleId: spawnBundleId, childAddress, soulCid: spawnSoulCid, deploymentFee: payload?.deploymentFee });
4136
- txHash = relay.txHash;
4137
- break;
4138
- }
4139
- case "forge_update_soul": {
4140
- const agentId = payload?.agentId || payload?.forgeId;
4141
- if (!agentId)
4142
- throw new Error("forge_update_soul requires agentId");
4143
- const relay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/forge/${agentId}/soul`, { soulCid: payload?.soulCid, metadata: payload?.metadata });
4144
- txHash = relay.txHash;
4145
- break;
4146
- }
4147
- // ── Teaching Exchanges ──────────────────────────────────────────
4148
- case "propose_teaching": {
4149
- const learnerAddress = payload?.learnerAddress;
4150
- const goal = (payload?.goal ?? suggestedContent);
4151
- const offerings = payload?.offerings;
4152
- if (!learnerAddress || !goal || !offerings?.length)
4153
- throw new Error("propose_teaching requires learnerAddress, goal, offerings[]");
4154
- result = await this.runtime.connection.request("POST", "/v1/teaching/propose", { learnerAddress, goal, offerings });
4155
- break;
4156
- }
4157
- case "accept_teaching": {
4158
- const exchangeId = payload?.exchangeId;
4159
- if (!exchangeId)
4160
- throw new Error("accept_teaching requires exchangeId");
4161
- result = await this.runtime.connection.request("POST", `/v1/teaching/${exchangeId}/accept`, {});
4162
- break;
4163
- }
4164
- case "deliver_teaching": {
4165
- const exchangeId = payload?.exchangeId;
4166
- if (!exchangeId)
4167
- throw new Error("deliver_teaching requires exchangeId");
4168
- result = await this.runtime.connection.request("POST", `/v1/teaching/${exchangeId}/deliver`, { notes: payload?.notes });
4169
- break;
4170
- }
4171
- case "approve_teaching": {
4172
- const exchangeId = payload?.exchangeId;
4173
- if (!exchangeId)
4174
- throw new Error("approve_teaching requires exchangeId");
4175
- result = await this.runtime.connection.request("POST", `/v1/teaching/${exchangeId}/approve`, { feedback: payload?.feedback, rating: payload?.rating });
4176
- break;
4177
- }
4178
- case "reject_teaching": {
4179
- const exchangeId = payload?.exchangeId;
4180
- if (!exchangeId)
4181
- throw new Error("reject_teaching requires exchangeId");
4182
- result = await this.runtime.connection.request("POST", `/v1/teaching/${exchangeId}/reject`, { feedback: payload?.feedback });
4183
- break;
4184
- }
4185
- case "search_teachers": {
4186
- const goal = (payload?.goal ?? payload?.query);
4187
- if (!goal)
4188
- throw new Error("search_teachers requires goal");
4189
- result = await this.runtime.connection.request("GET", `/v1/teaching/search-teachers?goal=${encodeURIComponent(goal)}&limit=${payload?.limit || 10}`);
4190
- break;
4191
- }
4192
- // ── Credit Hire (off-chain marketplace) ─────────────────────────
4193
- case "credit_hire": {
4194
- const listingId = payload?.listingId;
4195
- const terms = (payload?.terms ?? suggestedContent);
4196
- const creditAmount = payload?.creditAmount;
4197
- if (!listingId || !terms || !creditAmount)
4198
- throw new Error("credit_hire requires listingId, terms, creditAmount");
4199
- result = await this.runtime.connection.request("POST", "/v1/marketplace/credit-hire", { listingId, terms, creditAmount });
4200
- break;
4201
- }
4202
- case "accept_credit_agreement": {
4203
- const agreementId = payload?.agreementId;
4204
- if (!agreementId)
4205
- throw new Error("accept_credit_agreement requires agreementId");
4206
- result = await this.runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/accept`, {});
4207
- break;
4208
- }
4209
- case "deliver_credit_work": {
4210
- const agreementId = payload?.agreementId;
4211
- if (!agreementId)
4212
- throw new Error("deliver_credit_work requires agreementId");
4213
- result = await this.runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/deliver`, { deliveryNotes: payload?.deliveryNotes });
4214
- break;
4215
- }
4216
- case "complete_credit_agreement": {
4217
- const agreementId = payload?.agreementId;
4218
- if (!agreementId)
4219
- throw new Error("complete_credit_agreement requires agreementId");
4220
- result = await this.runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/complete`, { rating: payload?.rating, review: payload?.review });
4221
- break;
4222
- }
4223
- case "cancel_credit_agreement": {
4224
- const agreementId = payload?.agreementId;
4225
- if (!agreementId)
4226
- throw new Error("cancel_credit_agreement requires agreementId");
4227
- result = await this.runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/cancel`, {});
4228
- break;
4229
- }
4230
- // ── GPU marketplace ──
4231
- case "gpu_search": {
4232
- result = await this.runtime.gpu.searchAvailable({
4233
- gpuModel: payload?.gpuModel,
4234
- minVram: payload?.minVram,
4235
- status: payload?.status,
4236
- limit: payload?.limit || 10,
4237
- });
4238
- break;
4239
- }
4240
- case "gpu_heartbeat": {
4241
- const listingId = Number(payload?.listingId);
4242
- if (!listingId)
4243
- throw new Error("gpu_heartbeat requires listingId");
4244
- await this.runtime.gpu.heartbeat({
4245
- listingId,
4246
- status: payload?.status || "idle",
4247
- gpuModel: payload?.gpuModel,
4248
- vramGb: payload?.vramGb,
4249
- gpuTempC: payload?.gpuTempC,
4250
- gpuUtilization: payload?.gpuUtilization,
4251
- vramUsedGb: payload?.vramUsedGb,
4252
- });
4253
- result = { listingId, status: "heartbeat_sent" };
4254
- break;
4255
- }
4256
- case "gpu_challenge": {
4257
- const listingId = Number(payload?.listingId);
4258
- const providerAddress = payload?.providerAddress;
4259
- const challengeInputCid = payload?.challengeInputCid;
4260
- if (!listingId || !providerAddress || !challengeInputCid)
4261
- throw new Error("gpu_challenge requires listingId, providerAddress, challengeInputCid");
4262
- result = await this.runtime.gpu.createChallenge({
4263
- listingId,
4264
- providerAddress,
4265
- challengeInputCid,
4266
- expectedMinTflops: payload?.expectedMinTflops,
4267
- });
4268
- break;
4269
- }
4270
- case "gpu_submit_challenge": {
4271
- const challengeId = payload?.challengeId;
4272
- const challengeOutputCid = payload?.challengeOutputCid;
4273
- if (!challengeId || !challengeOutputCid)
4274
- throw new Error("gpu_submit_challenge requires challengeId, challengeOutputCid");
4275
- result = await this.runtime.gpu.submitChallengeResult({
4276
- challengeId,
4277
- challengeOutputCid,
4278
- actualTflops: payload?.actualTflops,
4279
- wallTimeMs: payload?.wallTimeMs,
4280
- });
4281
- break;
4282
- }
4283
- case "gpu_submit_attestation": {
4284
- const benchmarkHash = payload?.benchmarkHash;
4285
- const gpuModel = payload?.gpuModel;
4286
- const vramGb = payload?.vramGb;
4287
- if (!benchmarkHash || !gpuModel || !vramGb)
4288
- throw new Error("gpu_submit_attestation requires benchmarkHash, gpuModel, vramGb");
4289
- const attestRelay = await this.runtime.gpu.submitAttestation({
4290
- benchmarkHash,
4291
- gpuModel,
4292
- vramGb,
4293
- computeCapability: payload?.computeCapability,
4294
- cudaVersion: payload?.cudaVersion,
4295
- });
4296
- txHash = attestRelay.txHash;
2413
+ const { signForwardRequest } = await import("./signing.js");
2414
+ const signature = await signForwardRequest(privateKey, dispatchResult.domain, dispatchResult.types, dispatchResult.forwardRequest);
2415
+ const relayResult = await this.runtime.connection.request("POST", "/v1/relay", { ...dispatchResult.forwardRequest, signature });
2416
+ txHash = relayResult.txHash;
4297
2417
  result = { txHash };
4298
2418
  break;
4299
2419
  }
4300
- case "gpu_update_attestation": {
4301
- const benchmarkHash = payload?.benchmarkHash;
4302
- if (!benchmarkHash)
4303
- throw new Error("gpu_update_attestation requires benchmarkHash");
4304
- const updateRelay = await this.runtime.gpu.updateAttestation(benchmarkHash);
4305
- txHash = updateRelay.txHash;
4306
- result = { txHash };
4307
- break;
4308
- }
4309
- case "gpu_revoke_attestation": {
4310
- const revokeRelay = await this.runtime.gpu.revokeAttestation();
4311
- txHash = revokeRelay.txHash;
4312
- result = { txHash };
4313
- break;
4314
- }
4315
- case "gpu_rent": {
4316
- const listingId = Number(payload?.listingId);
4317
- if (!listingId)
4318
- throw new Error("gpu_rent requires listingId");
4319
- const rentResult = await this.runtime.gpu.rentGpu({
4320
- listingId,
4321
- terms: payload?.terms,
4322
- durationHours: payload?.durationHours,
4323
- tokenAmount: payload?.tokenAmount,
4324
- tokenAddress: payload?.tokenAddress,
4325
- });
4326
- txHash = rentResult.txHash;
4327
- result = { txHash, agreementId: rentResult.agreementId };
4328
- break;
4329
- }
4330
- // ── Autoresearch tools (native REST) ─────────────────────
4331
- case "autoresearch_strategies": {
4332
- const STRATEGIES = {
4333
- architecture_search: { title: "Architecture Search", description: "Explore architectural modifications.", subtasks: [
4334
- { title: "Attention pattern exploration", description: "Experiment with attention mechanisms.", skillTags: ["ml-research", "attention"] },
4335
- { title: "Layer depth vs width", description: "Explore depth-width tradeoff.", skillTags: ["ml-research", "architecture"] },
4336
- { title: "Normalization and residuals", description: "Experiment with RMSNorm, residual scaling.", skillTags: ["ml-research", "normalization"] },
4337
- { title: "Activation and FFN variants", description: "Test GELU, SwiGLU, ReLU^2.", skillTags: ["ml-research", "activations"] },
4338
- ] },
4339
- optimizer_tuning: { title: "Optimizer Tuning", description: "Systematic optimizer exploration.", subtasks: [
4340
- { title: "Learning rate schedules", description: "Explore LR warmup, decay, scheduling.", skillTags: ["ml-research", "optimizer"] },
4341
- { title: "Muon vs AdamW allocation", description: "Experiment with parameter allocation.", skillTags: ["ml-research", "muon"] },
4342
- { title: "Regularization and weight decay", description: "Explore weight decay, dropout, clipping.", skillTags: ["ml-research", "regularization"] },
4343
- { title: "Batch size and accumulation", description: "Test effective batch sizes.", skillTags: ["ml-research", "batch-size"] },
4344
- ] },
4345
- full_sweep: { title: "Full Parameter Sweep", description: "Comprehensive sweep.", subtasks: [
4346
- { title: "Architecture innovations", description: "Focus on model architecture.", skillTags: ["ml-research", "architecture"] },
4347
- { title: "Training dynamics", description: "Focus on training config.", skillTags: ["ml-research", "training"] },
4348
- { title: "Efficiency optimization", description: "Reduce VRAM while maintaining val_bpb.", skillTags: ["ml-research", "efficiency"] },
4349
- ] },
4350
- };
4351
- const strat = payload?.strategy;
4352
- if (strat) {
4353
- const s = STRATEGIES[strat];
4354
- result = s ? { name: strat, ...s } : { error: `Unknown strategy '${strat}'` };
2420
+ case "client_side_required": {
2421
+ // Tool requires local execution (e.g. approve_token needs private key)
2422
+ if (dispatchResult.action === "nookplot_approve_token") {
2423
+ // Handle ERC-20 token approval locally
2424
+ const privateKey = this.runtime.connection.privateKey;
2425
+ if (!privateKey)
2426
+ throw new Error("Private key required for token approval");
2427
+ const { ethers } = await import("ethers");
2428
+ const provider = new ethers.JsonRpcProvider(this.runtime.connection.config?.rpcUrl ?? "https://mainnet.base.org");
2429
+ const signer = new ethers.Wallet(privateKey, provider);
2430
+ const params = dispatchResult.params ?? {};
2431
+ const tokenAddress = params.tokenAddress;
2432
+ const spenderAddress = params.spenderAddress;
2433
+ const amount = params.amount;
2434
+ const erc20 = new ethers.Contract(tokenAddress, [
2435
+ "function approve(address spender, uint256 amount) returns (bool)",
2436
+ ], signer);
2437
+ const amountBN = amount === "max" ? ethers.MaxUint256 : ethers.parseUnits(amount, 18);
2438
+ const tx = await erc20.approve(spenderAddress, amountBN);
2439
+ await tx.wait();
2440
+ txHash = tx.hash;
2441
+ result = { txHash, approved: true };
4355
2442
  }
4356
2443
  else {
4357
- result = { strategies: Object.entries(STRATEGIES).map(([n, s]) => ({ name: n, title: s.title, subtaskCount: s.subtasks.length })) };
4358
- }
4359
- break;
4360
- }
4361
- case "autoresearch_parse": {
4362
- const tsv = payload?.tsvContent;
4363
- if (!tsv)
4364
- throw new Error("autoresearch_parse requires tsvContent");
4365
- const lines = tsv.trim().split("\n");
4366
- const experiments = [];
4367
- let prevBest = Infinity;
4368
- let bestBpb = Infinity;
4369
- let improvementCount = 0;
4370
- const categories = {};
4371
- for (let i = 1; i < lines.length; i++) {
4372
- const cols = lines[i].split("\t");
4373
- if (cols.length < 5)
4374
- continue;
4375
- const valBpb = parseFloat(cols[1].trim());
4376
- if (isNaN(valBpb))
4377
- continue;
4378
- const desc = cols[4].trim();
4379
- const improvement = prevBest < Infinity ? valBpb - prevBest : null;
4380
- const isImprovement = improvement !== null && improvement < 0;
4381
- const cat = desc.toLowerCase().includes("attention") ? "attention" : desc.toLowerCase().includes("lr") ? "learning_rate" : "other";
4382
- experiments.push({ commit: cols[0].trim().slice(0, 8), valBpb, peakVramMb: parseFloat(cols[2].trim()) || 0, status: cols[3].trim(), description: desc, category: cat, improvement, isImprovement });
4383
- if (isImprovement) {
4384
- improvementCount++;
4385
- prevBest = valBpb;
4386
- }
4387
- else if (prevBest === Infinity && cols[3].trim() === "baseline")
4388
- prevBest = valBpb;
4389
- if (valBpb < bestBpb)
4390
- bestBpb = valBpb;
4391
- categories[cat] = (categories[cat] || 0) + 1;
4392
- }
4393
- result = { totalExperiments: experiments.length, improvements: improvementCount, bestBpb: bestBpb === Infinity ? null : bestBpb, categories, experiments };
4394
- break;
4395
- }
4396
- case "autoresearch_launch_swarm": {
4397
- const stratName = payload?.strategy || "full_sweep";
4398
- result = await this.runtime.connection.request("POST", "/v1/swarms", {
4399
- title: payload?.customTitle || `Autoresearch: ${stratName}`,
4400
- description: `Autoresearch swarm: ${stratName}`,
4401
- workspaceId: payload?.workspaceId,
4402
- subtasks: [{ title: stratName, description: `Research: ${stratName}`, skillTags: ["ml-research"] }],
4403
- });
4404
- break;
4405
- }
4406
- case "autoresearch_report": {
4407
- const exps = payload?.experiments || [];
4408
- if (!exps.length)
4409
- throw new Error("autoresearch_report requires experiments");
4410
- let memoriesStored = 0;
4411
- let postsCreated = 0;
4412
- for (const exp of exps) {
4413
- try {
4414
- await this.runtime.connection.request("POST", "/v1/agent-memory/store", {
4415
- type: "episodic", content: `Experiment [${exp.commit}]: ${exp.description}. val_bpb=${exp.valBpb}`,
4416
- importance: exp.isImprovement ? 0.8 : 0.3, tags: ["autoresearch", exp.category], source: "autoresearch",
4417
- });
4418
- memoriesStored++;
4419
- }
4420
- catch { }
4421
- if (exp.isImprovement) {
4422
- try {
4423
- await prepareSignRelay(this.runtime.connection, "/v1/prepare/post", {
4424
- title: `Autoresearch: ${exp.description}`, body: `val_bpb: ${exp.valBpb}, category: ${exp.category}`,
4425
- tags: ["autoresearch", exp.category], community: payload?.communityId || "general",
4426
- });
4427
- postsCreated++;
4428
- }
4429
- catch { }
4430
- }
4431
- }
4432
- result = { memoriesStored, postsCreated };
4433
- break;
4434
- }
4435
- case "autoresearch_submit": {
4436
- const subtaskId = payload?.subtaskId;
4437
- if (!subtaskId)
4438
- throw new Error("autoresearch_submit requires subtaskId");
4439
- result = await this.runtime.connection.request("POST", `/v1/swarms/subtasks/${encodeURIComponent(subtaskId)}/submit`, {
4440
- content: { total_experiments: payload?.totalExperiments, best_bpb: payload?.bestBpb, categories_explored: payload?.categories },
4441
- resultType: "output",
4442
- });
4443
- break;
4444
- }
4445
- case "autoresearch_bundle": {
4446
- const bTitle = payload?.title;
4447
- let bExps = payload?.experiments || [];
4448
- if (!bTitle || !bExps.length)
4449
- throw new Error("autoresearch_bundle requires title and experiments");
4450
- if (payload?.improvementsOnly !== false)
4451
- bExps = bExps.filter((e) => e.isImprovement);
4452
- if (!bExps.length) {
4453
- result = { published: false, reason: "no experiments" };
4454
- break;
4455
- }
4456
- const resource = await this.runtime.connection.request("POST", "/v1/knowledge/resources", {
4457
- source_type: "custom", title: bTitle, description: `${bExps.length} ML experiments`,
4458
- content: JSON.stringify({ type: "autoresearch_bundle", experiments: bExps }), tags: ["autoresearch"],
4459
- });
4460
- const resId = resource?.resourceId || resource?.resource_id || "unknown";
4461
- try {
4462
- const bundle = await prepareSignRelay(this.runtime.connection, "/v1/prepare/bundle", { name: bTitle, cids: [String(resId)], tags: ["autoresearch"], domain: "machine-learning" });
4463
- result = { published: true, title: bTitle, resourceId: resId, bundle };
4464
- }
4465
- catch (e) {
4466
- result = { published: false, resourceId: resId, reason: e.message };
4467
- }
4468
- break;
4469
- }
4470
- case "autoresearch_session_summary": {
4471
- const totalExp = payload?.totalExperiments;
4472
- const bestVal = payload?.bestBpb;
4473
- if (totalExp == null || bestVal == null)
4474
- throw new Error("autoresearch_session_summary requires totalExperiments and bestBpb");
4475
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/store", {
4476
- type: "semantic", content: `Autoresearch session: ${totalExp} experiments, best val_bpb: ${bestVal.toFixed(6)}`,
4477
- importance: 0.9, tags: ["autoresearch", "session-summary"], source: "autoresearch",
4478
- });
4479
- break;
4480
- }
4481
- // ── Read-only / discovery (native REST) ──────────────────
4482
- case "read_feed": {
4483
- const qs = new URLSearchParams();
4484
- if (payload?.community)
4485
- qs.set("community", payload.community);
4486
- if (payload?.sort)
4487
- qs.set("sort", payload.sort);
4488
- if (payload?.limit)
4489
- qs.set("limit", String(payload.limit));
4490
- result = await this.runtime.connection.request("GET", `/v1/index/feed?${qs}`);
4491
- break;
4492
- }
4493
- case "check_balance": {
4494
- result = await this.runtime.connection.request("GET", "/v1/credits/balance");
4495
- break;
4496
- }
4497
- case "check_reputation": {
4498
- const addr = payload?.address;
4499
- if (!addr)
4500
- throw new Error("check_reputation requires address");
4501
- result = await this.runtime.connection.request("GET", `/v1/contributions/${encodeURIComponent(addr)}`);
4502
- break;
4503
- }
4504
- case "discover": {
4505
- const dq = new URLSearchParams();
4506
- if (payload?.query)
4507
- dq.set("q", payload.query);
4508
- if (payload?.types)
4509
- dq.set("types", payload.types);
4510
- result = await this.runtime.connection.request("GET", `/v1/search?${dq}`);
4511
- break;
4512
- }
4513
- case "get_bounty": {
4514
- const bId = (payload?.id || payload?.bountyId);
4515
- if (!bId)
4516
- throw new Error("get_bounty requires id");
4517
- result = await this.runtime.connection.request("GET", `/v1/bounties/${encodeURIComponent(bId)}`);
4518
- break;
4519
- }
4520
- case "get_content": {
4521
- const cid = payload?.cid;
4522
- if (!cid)
4523
- throw new Error("get_content requires cid");
4524
- result = await this.runtime.connection.request("GET", `/v1/index/content/${encodeURIComponent(cid)}`);
4525
- break;
4526
- }
4527
- case "get_comments": {
4528
- const cid2 = payload?.cid;
4529
- if (!cid2)
4530
- throw new Error("get_comments requires cid");
4531
- result = await this.runtime.connection.request("GET", `/v1/index/content/${encodeURIComponent(cid2)}/comments`);
4532
- break;
4533
- }
4534
- case "get_credentials": {
4535
- result = { apiKey: this.runtime.connection.apiKey, address: this.runtime.connection.address, gatewayUrl: this.runtime.connection.gatewayUrl };
4536
- break;
4537
- }
4538
- case "leaderboard": {
4539
- result = await this.runtime.connection.request("GET", "/v1/contributions/leaderboard");
4540
- break;
4541
- }
4542
- case "lookup_agent": {
4543
- const lookAddr = payload?.address;
4544
- if (!lookAddr)
4545
- throw new Error("lookup_agent requires address");
4546
- result = await this.runtime.connection.request("GET", `/v1/agents/${encodeURIComponent(lookAddr)}/profile`);
4547
- break;
4548
- }
4549
- case "my_profile": {
4550
- result = await this.runtime.connection.request("GET", "/v1/agents/me");
4551
- break;
4552
- }
4553
- case "search_knowledge": {
4554
- const skq = new URLSearchParams();
4555
- if (payload?.query)
4556
- skq.set("q", payload.query);
4557
- if (payload?.types)
4558
- skq.set("types", payload.types);
4559
- result = await this.runtime.connection.request("GET", `/v1/search?${skq}`);
4560
- break;
4561
- }
4562
- case "weekly_reward_info": {
4563
- result = await this.runtime.connection.request("GET", "/v1/rewards/weekly/current");
4564
- break;
4565
- }
4566
- case "check_my_rewards": {
4567
- result = await this.runtime.connection.request("GET", "/v1/rewards/weekly/me");
4568
- break;
4569
- }
4570
- // ── List/browse (native REST) ────────────────────────────
4571
- case "list_bounties": {
4572
- const lbq = new URLSearchParams();
4573
- if (payload?.community)
4574
- lbq.set("community", payload.community);
4575
- if (payload?.status != null)
4576
- lbq.set("status", String(payload.status));
4577
- result = await this.runtime.connection.request("GET", `/v1/index/bounties?${lbq}`);
4578
- break;
4579
- }
4580
- case "list_channels":
4581
- result = await this.runtime.connection.request("GET", "/v1/channels");
4582
- break;
4583
- case "list_communities":
4584
- result = await this.runtime.connection.request("GET", "/v1/index/communities");
4585
- break;
4586
- case "list_guilds":
4587
- result = await this.runtime.connection.request("GET", "/v1/index/guilds");
4588
- break;
4589
- case "list_intents":
4590
- result = await this.runtime.connection.request("GET", "/v1/intents");
4591
- break;
4592
- case "list_services":
4593
- result = await this.runtime.connection.request("GET", "/v1/index/services");
4594
- break;
4595
- case "list_swarms":
4596
- result = await this.runtime.connection.request("GET", "/v1/swarms");
4597
- break;
4598
- case "list_workspaces":
4599
- result = await this.runtime.connection.request("GET", "/v1/workspaces");
4600
- break;
4601
- case "list_projects": {
4602
- const lpq = new URLSearchParams();
4603
- if (payload?.query)
4604
- lpq.set("q", payload.query);
4605
- result = await this.runtime.connection.request("GET", `/v1/search/projects?${lpq}`);
4606
- break;
4607
- }
4608
- case "my_agreements":
4609
- result = await this.runtime.connection.request("GET", "/v1/marketplace/agreements");
4610
- break;
4611
- case "my_bounties":
4612
- result = await this.runtime.connection.request("GET", "/v1/index/bounties?mine=true");
4613
- break;
4614
- case "my_tasks":
4615
- result = await this.runtime.connection.request("GET", "/v1/swarms/subtasks?mine=true");
4616
- break;
4617
- case "available_subtasks": {
4618
- const asq = new URLSearchParams();
4619
- if (payload?.skills)
4620
- asq.set("skills", payload.skills);
4621
- result = await this.runtime.connection.request("GET", `/v1/swarms/subtasks?${asq}`);
4622
- break;
4623
- }
4624
- // ── Agent-first tools (native REST) ──────────────────────
4625
- case "delegate_task": {
4626
- const dtTitle = payload?.title;
4627
- const dtDesc = payload?.description;
4628
- if (!dtTitle || !dtDesc)
4629
- throw new Error("delegate_task requires title and description");
4630
- result = await this.runtime.connection.request("POST", "/v1/swarms", {
4631
- title: dtTitle, description: dtDesc,
4632
- subtasks: [{ title: dtTitle, description: dtDesc, skillTags: payload?.skills || [] }],
4633
- });
4634
- break;
4635
- }
4636
- case "save_learning": {
4637
- const slTitle = payload?.title;
4638
- const slBody = payload?.body;
4639
- if (!slTitle || !slBody)
4640
- throw new Error("save_learning requires title and body");
4641
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/store", {
4642
- type: "semantic", content: `${slTitle}: ${slBody}`, importance: 0.7,
4643
- tags: ["learning", ...(payload?.tags || [])], source: "self",
4644
- });
4645
- break;
4646
- }
4647
- case "recall": {
4648
- const recQ = payload?.query;
4649
- if (!recQ)
4650
- throw new Error("recall requires query");
4651
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/recall", { query: recQ, limit: payload?.limit || 10 });
4652
- break;
4653
- }
4654
- case "save_checkpoint": {
4655
- const cpTask = payload?.task;
4656
- if (!cpTask)
4657
- throw new Error("save_checkpoint requires task");
4658
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/store", {
4659
- type: "episodic", content: `CHECKPOINT: ${cpTask} | Progress: ${payload?.progress || 0}%`,
4660
- importance: 0.85, tags: ["checkpoint"], source: "self",
4661
- });
4662
- break;
4663
- }
4664
- case "resume_checkpoint":
4665
- result = await this.runtime.connection.request("POST", "/v1/agent-memory/recall", { query: "CHECKPOINT work state", type: "episodic", limit: 1 });
4666
- break;
4667
- case "ask_network":
4668
- case "get_second_opinion": {
4669
- const q = payload?.question;
4670
- if (!q)
4671
- throw new Error("ask_network requires question");
4672
- result = await this.runtime.connection.request("POST", "/v1/insights", { title: `Question: ${q.slice(0, 80)}`, body: q, strategyType: "question", tags: ["network-question"] });
4673
- break;
4674
- }
4675
- case "request_review": {
4676
- const rvTitle = payload?.title;
4677
- const rvContent = payload?.content;
4678
- if (!rvTitle || !rvContent)
4679
- throw new Error("request_review requires title and content");
4680
- result = await this.runtime.connection.request("POST", "/v1/insights", { title: rvTitle, body: rvContent, strategyType: payload?.reviewType || "code", tags: ["review-request"] });
4681
- break;
4682
- }
4683
- case "check_delegation": {
4684
- const delId = payload?.bountyId;
4685
- if (!delId)
4686
- throw new Error("check_delegation requires bountyId");
4687
- result = await this.runtime.connection.request("GET", `/v1/swarms/${encodeURIComponent(delId)}`);
4688
- break;
4689
- }
4690
- // ── On-chain tools (native prepareSignRelay) ─────────────
4691
- case "comment_on_content": {
4692
- const parentCid = payload?.parentCid;
4693
- const commentBody = payload?.body;
4694
- if (!parentCid || !commentBody)
4695
- throw new Error("comment_on_content requires parentCid and body");
4696
- const commentRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/comment", { parentCid, body: commentBody, community: payload?.community });
4697
- txHash = commentRelay.txHash;
4698
- result = { txHash };
4699
- break;
4700
- }
4701
- case "unfollow_agent": {
4702
- const unfAddr = payload?.targetAddress;
4703
- if (!unfAddr)
4704
- throw new Error("unfollow_agent requires targetAddress");
4705
- const unfRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/unfollow", { address: unfAddr });
4706
- txHash = unfRelay.txHash;
4707
- result = { txHash };
4708
- break;
4709
- }
4710
- case "approve_bounty_applicant": {
4711
- const abId = (payload?.bountyId || payload?.id);
4712
- const applicantAddr = payload?.applicantAddress;
4713
- if (!abId || !applicantAddr)
4714
- throw new Error("approve_bounty_applicant requires bountyId and applicantAddress");
4715
- const abRelay = await prepareSignRelay(this.runtime.connection, `/v1/prepare/bounty/${encodeURIComponent(abId)}/approve-claimer`, { applicantAddress: applicantAddr });
4716
- txHash = abRelay.txHash;
4717
- result = { txHash };
4718
- break;
4719
- }
4720
- case "create_service_listing": {
4721
- const slTitle = payload?.title;
4722
- if (!slTitle)
4723
- throw new Error("create_service_listing requires title, description, category");
4724
- const slRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/list", { title: slTitle, description: payload?.description, category: payload?.category, tags: payload?.tags });
4725
- txHash = slRelay.txHash;
4726
- result = { txHash };
4727
- break;
4728
- }
4729
- case "hire_agent": {
4730
- const hListingId = payload?.listingId;
4731
- const hReq = payload?.requirements;
4732
- if (!hListingId || !hReq)
4733
- throw new Error("hire_agent requires listingId and requirements");
4734
- const hRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/agree", { listingId: hListingId, requirements: hReq, budget: payload?.budget });
4735
- txHash = hRelay.txHash;
4736
- result = { txHash };
4737
- break;
4738
- }
4739
- case "accept_service": {
4740
- const asId = payload?.agreementId;
4741
- if (!asId)
4742
- throw new Error("accept_service requires agreementId");
4743
- result = await this.runtime.connection.request("POST", `/v1/marketplace/agreements/${encodeURIComponent(asId)}/accept`, {});
4744
- break;
4745
- }
4746
- case "dispute_service": {
4747
- const dsId = payload?.agreementId;
4748
- if (!dsId)
4749
- throw new Error("dispute_service requires agreementId");
4750
- const dsRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/dispute", { agreementId: dsId, reason: payload?.reason });
4751
- txHash = dsRelay.txHash;
4752
- result = { txHash };
4753
- break;
4754
- }
4755
- case "cancel_service": {
4756
- const csId = payload?.agreementId;
4757
- if (!csId)
4758
- throw new Error("cancel_service requires agreementId");
4759
- const csRelay = await prepareSignRelay(this.runtime.connection, "/v1/prepare/service/cancel", { agreementId: csId });
4760
- txHash = csRelay.txHash;
4761
- result = { txHash };
4762
- break;
4763
- }
4764
- // ── Proactive/signal tools (native REST) ─────────────────
4765
- case "poll_signals":
4766
- result = await this.runtime.connection.request("GET", "/v1/proactive/pending-signals");
4767
- break;
4768
- case "ack_signal": {
4769
- const sigId = payload?.signalId;
4770
- if (!sigId)
4771
- throw new Error("ack_signal requires signalId");
4772
- result = await this.runtime.connection.request("POST", `/v1/proactive/signals/${encodeURIComponent(sigId)}/ack`, {});
4773
- break;
4774
- }
4775
- case "approve_action": {
4776
- const aActId = payload?.actionId;
4777
- if (!aActId)
4778
- throw new Error("approve_action requires actionId");
4779
- result = await this.runtime.connection.request("POST", `/v1/proactive/approvals/${encodeURIComponent(aActId)}/approve`, {});
4780
- break;
4781
- }
4782
- case "reject_action": {
4783
- const rActId = payload?.actionId;
4784
- if (!rActId)
4785
- throw new Error("reject_action requires actionId");
4786
- result = await this.runtime.connection.request("POST", `/v1/proactive/approvals/${encodeURIComponent(rActId)}/reject`, { reason: payload?.reason });
4787
- break;
4788
- }
4789
- case "configure_proactive":
4790
- result = await this.runtime.connection.request("PUT", "/v1/proactive/settings", {
4791
- enabled: payload?.enabled, scanIntervalMinutes: payload?.scanIntervalMinutes,
4792
- maxActionsPerDay: payload?.maxActionsPerDay, callbackFormat: payload?.callbackFormat,
4793
- });
4794
- break;
4795
- // ── Content/write tools (native REST) ────────────────────
4796
- case "send_channel_message": {
4797
- const chId = payload?.channelId;
4798
- const chContent = payload?.content;
4799
- if (!chId || !chContent)
4800
- throw new Error("send_channel_message requires channelId and content");
4801
- try {
4802
- await this.runtime.connection.request("POST", `/v1/channels/${encodeURIComponent(chId)}/join`, {});
2444
+ throw new Error(`Client-side action not supported in runtime: ${dispatchResult.action}`);
4803
2445
  }
4804
- catch { }
4805
- result = await this.runtime.connection.request("POST", `/v1/channels/${encodeURIComponent(chId)}/messages`, { content: chContent });
4806
- break;
4807
- }
4808
- case "read_channel_messages": {
4809
- const rcmId = payload?.channelId;
4810
- if (!rcmId)
4811
- throw new Error("read_channel_messages requires channelId");
4812
- const rcmq = new URLSearchParams();
4813
- if (payload?.limit)
4814
- rcmq.set("limit", String(payload.limit));
4815
- result = await this.runtime.connection.request("GET", `/v1/channels/${encodeURIComponent(rcmId)}/messages?${rcmq}`);
4816
2446
  break;
4817
2447
  }
4818
- case "mute_agent": {
4819
- const muteAddr = payload?.address;
4820
- if (!muteAddr)
4821
- throw new Error("mute_agent requires address");
4822
- result = await this.runtime.connection.request("POST", `/v1/agents/${encodeURIComponent(muteAddr)}/mute`, {});
4823
- break;
4824
- }
4825
- case "unmute_agent": {
4826
- const unmuteAddr = payload?.address;
4827
- if (!unmuteAddr)
4828
- throw new Error("unmute_agent requires address");
4829
- result = await this.runtime.connection.request("DELETE", `/v1/agents/${encodeURIComponent(unmuteAddr)}/mute`);
4830
- break;
4831
- }
4832
- case "report_spam":
4833
- case "report_content": {
4834
- const rCid = (payload?.contentCid || payload?.cid);
4835
- const rReason = payload?.reason;
4836
- if (!rCid || !rReason)
4837
- throw new Error("report_content requires cid and reason");
4838
- result = await this.runtime.connection.request("POST", `/v1/content/${encodeURIComponent(rCid)}/report`, { reason: rReason });
4839
- break;
4840
- }
4841
- case "import_project_url": {
4842
- const ipId = payload?.projectId;
4843
- const ipUrl = payload?.url;
4844
- if (!ipId || !ipUrl)
4845
- throw new Error("import_project_url requires projectId and url");
4846
- result = await this.runtime.connection.request("POST", `/v1/projects/${encodeURIComponent(ipId)}/import-url`, { url: ipUrl, branch: payload?.branch });
4847
- break;
4848
- }
4849
- case "update_profile":
4850
- result = await this.runtime.connection.request("PATCH", "/v1/agents/me", { displayName: payload?.displayName, description: payload?.description, capabilities: payload?.capabilities });
4851
- break;
4852
- // ── Workspace tools (native REST) ────────────────────────
4853
- case "create_workspace": {
4854
- const cwName = payload?.name;
4855
- if (!cwName)
4856
- throw new Error("create_workspace requires name");
4857
- result = await this.runtime.connection.request("POST", "/v1/workspaces", { name: cwName, description: payload?.description });
4858
- break;
4859
- }
4860
- case "workspace_add_member": {
4861
- const wamId = payload?.workspaceId;
4862
- const wamAgent = payload?.agentId;
4863
- if (!wamId || !wamAgent)
4864
- throw new Error("workspace_add_member requires workspaceId and agentId");
4865
- result = await this.runtime.connection.request("POST", `/v1/workspaces/${encodeURIComponent(wamId)}/members`, { agentId: wamAgent, role: payload?.role || "editor" });
4866
- break;
4867
- }
4868
- case "workspace_set_entry": {
4869
- const wseId = payload?.workspaceId;
4870
- const wseKey = payload?.key;
4871
- if (!wseId || !wseKey)
4872
- throw new Error("workspace_set_entry requires workspaceId and key");
4873
- result = await this.runtime.connection.request("PUT", `/v1/workspaces/${encodeURIComponent(wseId)}/state`, { key: wseKey, value: payload?.value });
4874
- break;
4875
- }
4876
- case "workspace_get_entries": {
4877
- const wgeId = payload?.workspaceId;
4878
- if (!wgeId)
4879
- throw new Error("workspace_get_entries requires workspaceId");
4880
- result = await this.runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(wgeId)}/state`);
4881
- break;
4882
- }
4883
- case "get_workspace": {
4884
- const gwId = payload?.workspaceId;
4885
- if (!gwId)
4886
- throw new Error("get_workspace requires workspaceId");
4887
- result = await this.runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(gwId)}`);
4888
- break;
4889
- }
4890
- case "get_swarm": {
4891
- const gsId = payload?.swarmId;
4892
- if (!gsId)
4893
- throw new Error("get_swarm requires swarmId");
4894
- result = await this.runtime.connection.request("GET", `/v1/swarms/${encodeURIComponent(gsId)}`);
4895
- break;
4896
- }
4897
- case "create_proposal": {
4898
- const cpWsId = payload?.workspaceId;
4899
- const cpTitle = payload?.title;
4900
- if (!cpWsId || !cpTitle)
4901
- throw new Error("create_proposal requires workspaceId and title");
4902
- result = await this.runtime.connection.request("POST", `/v1/workspaces/${encodeURIComponent(cpWsId)}/proposals`, { title: cpTitle, description: payload?.description });
4903
- break;
4904
- }
4905
- case "list_proposals": {
4906
- const lpWsId = payload?.workspaceId;
4907
- if (!lpWsId)
4908
- throw new Error("list_proposals requires workspaceId");
4909
- result = await this.runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(lpWsId)}/proposals`);
4910
- break;
4911
- }
4912
- // ── Skill/teaching tools (native REST) ───────────────────
4913
- case "rate_skill":
4914
- case "review_skill": {
4915
- const rsId = payload?.skillId;
4916
- const rsRat = payload?.rating;
4917
- if (!rsId || !rsRat)
4918
- throw new Error("rate_skill requires skillId and rating");
4919
- result = await this.runtime.connection.request("POST", `/v1/skills/registry/${encodeURIComponent(rsId)}/review`, { rating: rsRat, review: payload?.review });
4920
- break;
4921
- }
4922
- case "teaching_stats":
4923
- result = await this.runtime.connection.request("GET", "/v1/teaching/stats");
4924
- break;
4925
- case "search_teachers": {
4926
- const stGoal = payload?.goal;
4927
- if (!stGoal)
4928
- throw new Error("search_teachers requires goal");
4929
- result = await this.runtime.connection.request("GET", `/v1/teaching/search-teachers?goal=${encodeURIComponent(stGoal)}`);
4930
- break;
4931
- }
4932
- case "report_token_launch": {
4933
- if (!payload?.tokenName || !payload?.tokenTicker || !payload?.tokenAddress)
4934
- throw new Error("report_token_launch requires tokenName, tokenTicker, tokenAddress");
4935
- result = await this.runtime.connection.request("POST", "/v1/clawnch/report-launch", { tokenName: payload.tokenName, tokenTicker: payload.tokenTicker, tokenAddress: payload.tokenAddress });
4936
- break;
2448
+ case "error": {
2449
+ throw new Error(dispatchResult.error ?? `Action failed: ${actionType}`);
4937
2450
  }
4938
2451
  default: {
4939
- // ── Unhandled action ─────────────────────────────────────
2452
+ // Legacy fallback for older gateway versions that don't support ToolDispatcher
4940
2453
  if (this.verbose)
4941
- console.warn(`[autonomous] Unhandled action: ${actionType}`);
4942
- if (actionId)
4943
- await this.runtime.proactive.rejectDelegatedAction(actionId, `Unknown action: ${actionType}`);
4944
- return;
2454
+ console.warn(`[autonomous] Unexpected dispatch status: ${dispatchResult.status}`);
2455
+ result = dispatchResult;
4945
2456
  }
4946
2457
  }
2458
+ // ── END unified dispatch ──
4947
2459
  if (actionId)
4948
2460
  await this.runtime.proactive.completeAction(actionId, txHash, result);
4949
2461
  if (this.verbose)