@nookplot/cli 0.6.80 → 0.6.82

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.
@@ -11,116 +11,7 @@ import { existsSync, appendFileSync, statSync, mkdirSync } from "node:fs";
11
11
  import { join } from "node:path";
12
12
  import { homedir } from "node:os";
13
13
  import { spawn } from "node:child_process";
14
- import { NookplotRuntime, AutonomousAgent, prepareSignRelay, formatActionsForPrompt } from "@nookplot/runtime";
15
- import { filterByProfile } from "../skillGenerator.js";
16
- // ── Autoresearch: strategy definitions + TSV parser ──────────
17
- // Ported from mcp-server/src/tools/autoresearch.ts so CLI can execute
18
- // these tools natively without routing through MCP infrastructure.
19
- const AUTORESEARCH_STRATEGIES = {
20
- architecture_search: {
21
- title: "Architecture Search",
22
- description: "Explore architectural modifications to the transformer model.",
23
- subtasks: [
24
- { title: "Attention pattern exploration", description: "Experiment with attention mechanisms: sliding window, flash attention, multi-head vs multi-query, grouped-query attention.", skillTags: ["ml-research", "attention", "transformers"] },
25
- { title: "Layer depth vs width tradeoff", description: "Explore depth-width tradeoff within 5-minute budget.", skillTags: ["ml-research", "architecture", "scaling"] },
26
- { title: "Normalization and residual connections", description: "Experiment with RMSNorm placement, pre-norm vs post-norm, residual scaling.", skillTags: ["ml-research", "normalization", "residuals"] },
27
- { title: "Activation functions and FFN variants", description: "Test GELU, SwiGLU, ReLU^2 and FFN architectures.", skillTags: ["ml-research", "activations", "ffn"] },
28
- ],
29
- },
30
- optimizer_tuning: {
31
- title: "Optimizer Tuning",
32
- description: "Systematic exploration of optimizer configurations.",
33
- subtasks: [
34
- { title: "Learning rate schedules", description: "Explore LR warmup, decay, scheduling strategies.", skillTags: ["ml-research", "optimizer", "learning-rate"] },
35
- { title: "Muon vs AdamW allocation", description: "Experiment with Muon vs AdamW parameter allocation.", skillTags: ["ml-research", "optimizer", "muon"] },
36
- { title: "Regularization and weight decay", description: "Explore weight decay schedules, dropout patterns, gradient clipping.", skillTags: ["ml-research", "optimizer", "regularization"] },
37
- { title: "Batch size and gradient accumulation", description: "Test different effective batch sizes via gradient accumulation.", skillTags: ["ml-research", "optimizer", "batch-size"] },
38
- ],
39
- },
40
- full_sweep: {
41
- title: "Full Parameter Sweep",
42
- description: "Comprehensive sweep across all optimization dimensions.",
43
- subtasks: [
44
- { title: "Architecture innovations", description: "Focus on model architecture: attention, layers, dims, activations.", skillTags: ["ml-research", "architecture"] },
45
- { title: "Training dynamics", description: "Focus on training: optimizer, LR, batch size, warmup, weight decay.", skillTags: ["ml-research", "training"] },
46
- { title: "Efficiency optimization", description: "Reduce VRAM while maintaining val_bpb.", skillTags: ["ml-research", "efficiency"] },
47
- ],
48
- },
49
- };
50
- function classifyAutoresearchCategory(description) {
51
- const desc = description.toLowerCase();
52
- if (["attention", "flash", "sliding", "causal", "head"].some((w) => desc.includes(w)))
53
- return "attention";
54
- if (["lr", "learning rate", "warmup", "cooldown", "schedule"].some((w) => desc.includes(w)))
55
- return "learning_rate";
56
- if (["muon", "adamw", "optimizer", "momentum", "weight decay"].some((w) => desc.includes(w)))
57
- return "optimizer";
58
- if (["layer", "depth", "dim", "hidden", "embed", "width"].some((w) => desc.includes(w)))
59
- return "architecture";
60
- if (["batch", "gradient", "accumulation"].some((w) => desc.includes(w)))
61
- return "batch_size";
62
- if (["norm", "rmsnorm", "layernorm"].some((w) => desc.includes(w)))
63
- return "normalization";
64
- if (["dropout", "regulariz", "weight tying"].some((w) => desc.includes(w)))
65
- return "regularization";
66
- if (["activation", "gelu", "swiglu", "relu"].some((w) => desc.includes(w)))
67
- return "activation";
68
- if (["positional", "rotary", "rope", "alibi"].some((w) => desc.includes(w)))
69
- return "positional_encoding";
70
- return "other";
71
- }
72
- function parseAutoresearchTsv(tsvContent, sinceCommit) {
73
- const lines = tsvContent.trim().split("\n");
74
- if (lines.length < 2)
75
- return { totalExperiments: 0, improvements: 0, successRate: "0%", bestBpb: null, categories: {}, experiments: [], topImprovements: [] };
76
- let prevBest = Infinity;
77
- const experiments = [];
78
- const categories = {};
79
- let bestBpb = Infinity;
80
- let improvementCount = 0;
81
- for (let i = 1; i < lines.length; i++) {
82
- const cols = lines[i].split("\t");
83
- if (cols.length < 5)
84
- continue;
85
- const commitHash = cols[0].trim();
86
- const valBpb = parseFloat(cols[1].trim());
87
- if (isNaN(valBpb))
88
- continue;
89
- const peakVramMb = parseFloat(cols[2].trim()) || 0;
90
- const status = cols[3].trim();
91
- const description = cols[4].trim();
92
- const improvement = prevBest < Infinity ? valBpb - prevBest : null;
93
- const isImprovement = improvement !== null && improvement < 0;
94
- const category = classifyAutoresearchCategory(description);
95
- experiments.push({ commit: commitHash.slice(0, 8), valBpb, peakVramMb, status, description, category, improvement, isImprovement });
96
- if (isImprovement) {
97
- improvementCount++;
98
- prevBest = valBpb;
99
- }
100
- else if (prevBest === Infinity && status === "baseline") {
101
- prevBest = valBpb;
102
- }
103
- if (valBpb < bestBpb)
104
- bestBpb = valBpb;
105
- categories[category] = (categories[category] || 0) + 1;
106
- }
107
- let filtered = experiments;
108
- if (sinceCommit) {
109
- const idx = filtered.findIndex((e) => e.commit.startsWith(sinceCommit));
110
- if (idx >= 0)
111
- filtered = filtered.slice(idx + 1);
112
- }
113
- const improvements = filtered.filter((e) => e.isImprovement);
114
- return {
115
- totalExperiments: experiments.length, improvements: improvementCount,
116
- successRate: `${(experiments.length > 0 ? (improvementCount / experiments.length) * 100 : 0).toFixed(1)}%`,
117
- bestBpb: bestBpb === Infinity ? null : bestBpb, categories,
118
- experiments: filtered,
119
- topImprovements: improvements.sort((a, b) => a.valBpb - b.valBpb).slice(0, 5).map((e) => ({
120
- commit: e.commit, valBpb: e.valBpb, delta: e.improvement, description: e.description, category: e.category,
121
- })),
122
- };
123
- }
14
+ import { NookplotRuntime, AutonomousAgent, formatActionsForPrompt, getAvailableActionsFromMap, getCategoryListing, getToolsInCategory } from "@nookplot/runtime";
124
15
  // ── Constants ─────────────────────────────────────────────────
125
16
  const NOOKPLOT_DIR = join(homedir(), ".nookplot");
126
17
  const EVENTS_FILE = join(NOOKPLOT_DIR, "events.jsonl");
@@ -482,2583 +373,71 @@ async function callAgentCli(cliBinary, trigger, log) {
482
373
  });
483
374
  }
484
375
  // ── Available actions ─────────────────────────────────────────
376
+ /** Session state: dynamically loaded tool categories via browse_tools. */
377
+ const loadedCategories = new Set();
485
378
  export function getAvailableActions(signalType) {
486
- return filterByProfile(_getAvailableActionsRaw(signalType));
487
- }
488
- /** Raw action lists per signal type (before profile filtering). */
489
- function _getAvailableActionsRaw(signalType) {
490
- switch (signalType) {
491
- case "dm_received":
492
- return [
493
- "reply", "send_dm", "follow_back", "attest_back", "propose_collab",
494
- "vote", "publish", "create_post", "create_bounty", "create_project",
495
- "create_community", "create_listing", "commit_files", "create_task",
496
- "link_project_to_guild", "propose_guild", "deploy_preview",
497
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool", "register_webhook",
498
- "workspace_create", "publish_insight",
499
- "create_intent", "browse_intents",
500
- "launch_token", "preview_token_launch",
501
- "search_skills", "install_skill", "store_memory", "recall_memory",
502
- "propose_teaching", "search_teachers",
503
- "credit_hire",
504
- "ignore",
505
- ];
506
- case "channel_message":
507
- case "channel_mention":
508
- case "project_discussion":
509
- return [
510
- "reply", "publish", "vote", "follow", "attest", "propose_collab",
511
- "create_post", "create_bounty", "create_project", "commit_files",
512
- "create_task", "link_project_to_guild", "propose_guild",
513
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool",
514
- "workspace_create", "publish_insight",
515
- "create_intent", "browse_intents",
516
- "search_skills", "install_skill", "store_memory",
517
- "ignore",
518
- ];
519
- case "new_follower":
520
- return ["follow_back", "send_dm", "ignore"];
521
- case "attestation_received":
522
- return ["attest_back", "endorse_agent", "send_dm", "ignore"];
523
- case "endorsement_received":
524
- return ["endorse_agent", "attest_back", "send_dm", "ignore"];
525
- case "files_committed":
526
- case "pending_review":
527
- return ["review", "comment", "request_ai_review", "list_project_files", "read_project_file", "list_commits", "get_commit_detail", "list_merge_requests", "get_merge_request", "ignore"];
528
- case "merge_request_created":
529
- return ["get_merge_request", "merge_merge_request", "close_merge_request", "reply", "ignore"];
530
- case "project_forked":
531
- return ["acknowledge", "send_dm", "ignore"];
532
- case "review_submitted":
533
- return ["reply", "ignore"];
534
- case "collaborator_added":
535
- return ["send_message", "reply", "ignore"];
536
- case "new_post_in_community":
537
- case "post_reply":
538
- case "reply_to_own_post":
539
- return ["reply", "post_reply", "vote", "publish", "ignore"];
540
- case "bounty":
541
- return ["claim", "apply_bounty", "create_bounty", "reply", "ignore"];
542
- case "community_gap":
543
- return ["create_community", "ignore"];
544
- case "potential_friend":
545
- return ["follow", "send_dm", "attest", "endorse_agent", "ignore"];
546
- case "attestation_opportunity":
547
- return ["attest", "endorse_agent", "send_dm", "ignore"];
548
- case "directive":
549
- return [
550
- "execute", "reply", "publish", "create_project", "commit_files",
551
- "create_task", "assign_task", "complete_task", "update_task",
552
- "link_project_to_guild", "propose_guild", "approve_guild", "reject_guild", "leave_guild",
553
- "create_bounty", "create_bundle", "propose_collab", "assemble_team",
554
- "find_agents", "deploy_preview", "add_collaborator",
555
- "fork_project", "create_merge_request", "list_merge_requests", "get_merge_request", "merge_merge_request", "close_merge_request",
556
- "create_listing", "create_agreement", "cancel_agreement",
557
- "workspace_create", "workspace_set", "workspace_snapshot",
558
- "propose_action", "vote_proposal", "cancel_proposal",
559
- "egress_request", "execute_tool", "exec_code", "call_mcp_tool", "connect_mcp_server", "disconnect_mcp_server", "register_webhook",
560
- "publish_insight", "cite_insight", "apply_insight",
561
- "deposit_treasury", "withdraw_treasury", "fund_bounty_from_treasury", "distribute_revenue",
562
- "create_swarm", "claim_subtask", "submit_swarm_result", "aggregate_swarm",
563
- "record_gap", "update_proficiency", "generate_recommendations",
564
- "create_intent", "browse_intents", "submit_proposal", "accept_proposal", "reject_proposal",
565
- "cancel_intent", "complete_intent", "withdraw_proposal", "query_oracle",
566
- "launch_token", "preview_token_launch", "claim_clawnch_fees", "get_token_analytics",
567
- "create_search_subscription",
568
- "send_email", "reply_email", "check_email", "create_email_inbox",
569
- "search_skills", "publish_skill", "install_skill", "review_skill", "update_skill", "trending_skills",
570
- "store_memory", "recall_memory", "list_memories", "memory_stats", "export_memories", "import_memories",
571
- "forge_deploy", "forge_spawn", "forge_update_soul",
572
- "propose_teaching", "accept_teaching", "deliver_teaching", "approve_teaching", "reject_teaching", "search_teachers",
573
- "credit_hire", "accept_credit_agreement", "deliver_credit_work", "complete_credit_agreement", "cancel_credit_agreement",
574
- "claim_reward",
575
- "endorse_agent", "revoke_endorsement",
576
- "block_agent", "unblock_agent",
577
- "list_project_files", "read_project_file", "list_commits", "get_commit_detail",
578
- "gpu_search", "gpu_heartbeat", "gpu_challenge", "gpu_submit_challenge",
579
- "gpu_submit_attestation", "gpu_update_attestation", "gpu_revoke_attestation",
580
- "gpu_rent",
581
- "ignore",
582
- ];
583
- case "collab_request":
584
- return ["add_collaborator", "propose_collab", "reply", "ignore"];
585
- case "service":
586
- return ["reply", "update_service", "create_listing", "create_agreement", "ignore"];
587
- case "time_to_post":
588
- return ["create_post", "create_bounty", "create_bundle", "publish_insight", "create_listing", "publish_skill", "ignore"];
589
- case "time_to_create_project":
590
- return ["create_project", "assemble_team", "ignore"];
591
- case "task_assigned":
592
- return ["accept", "update_task", "complete_task", "assign_task", "assemble_team", "reply", "ignore"];
593
- case "task_completed":
594
- return ["reply", "review", "create_task", "ignore"];
595
- case "milestone_reached":
596
- return ["reply", "ignore"];
597
- case "review_comment_added":
598
- return ["reply", "ignore"];
599
- case "agent_mentioned":
600
- return ["reply", "acknowledge", "ignore"];
601
- case "project_status_update":
602
- return ["reply", "ignore"];
603
- case "file_shared":
604
- return ["reply", "ignore"];
605
- case "bounty_posted_to_project":
606
- return ["reply", "claim", "ignore"];
607
- case "bounty_access_requested":
608
- return ["grant", "deny", "ignore"];
609
- case "bounty_access_granted":
610
- return ["reply", "claim", "ignore"];
611
- case "project_bounty_claimed":
612
- return ["reply", "ignore"];
613
- case "project_bounty_completed":
614
- return ["reply", "ignore"];
615
- case "team_assembly_suggested":
616
- return ["assemble_team", "ignore"];
617
- case "team_invitation":
618
- return ["accept_invitation", "decline_invitation", "ignore"];
619
- case "team_invitation_accepted":
620
- case "team_invitation_declined":
621
- return ["reply", "ignore"];
622
- case "xmtp_message":
623
- return ["reply", "ignore"];
624
- // Marketplace signals
625
- case "agreement_created":
626
- return ["deliver_work", "cancel_agreement", "send_agreement_message", "ignore"];
627
- case "work_delivered":
628
- return ["settle_agreement", "dispute_agreement", "send_agreement_message", "expire_delivered", "ignore"];
629
- case "agreement_settled":
630
- return ["submit_review", "ignore"];
631
- case "agreement_disputed":
632
- return ["send_agreement_message", "expire_dispute", "ignore"];
633
- case "agreement_cancelled":
634
- return ["ignore"];
635
- case "revision_requested":
636
- return ["deliver_work", "send_agreement_message", "ignore"];
637
- case "review_received":
638
- return ["ignore"];
639
- // Bounty application/submission signals
640
- case "bounty_application_submitted":
641
- return ["approve_bounty_claimer", "reject_bounty_application", "ignore"];
642
- case "bounty_application_approved":
643
- return ["claim_bounty", "ignore"];
644
- case "bounty_application_rejected":
645
- return ["ignore"];
646
- case "bounty_work_submitted":
647
- return ["select_bounty_submission", "ignore"];
648
- case "bounty_submission_selected":
649
- return ["claim_bounty", "ignore"];
650
- case "bounty_submission_not_selected":
651
- return ["ignore"];
652
- // On-chain bounty lifecycle signals
653
- case "bounty_claimed":
654
- return ["approve_bounty_work", "approve_bounty_claimer", "dispute_bounty_work", "unclaim_bounty", "ignore"];
655
- case "bounty_work_approved":
656
- return ["ignore"];
657
- case "bounty_disputed":
658
- return ["cancel_bounty", "ignore"];
659
- case "bounty_cancelled":
660
- return ["ignore"];
661
- case "bounty_claimer_approved":
662
- return ["claim_bounty", "ignore"];
663
- case "guild_opportunity":
664
- return ["join_guild", "approve_guild", "reject_guild", "leave_guild", "propose_guild", "link_project_to_guild", "reply", "ignore"];
665
- case "intent_matched":
666
- return ["submit_proposal", "browse_intents", "reply", "ignore"];
667
- case "proposal_received":
668
- return ["accept_proposal", "reject_proposal", "reply", "ignore"];
669
- case "intent_accepted":
670
- return ["complete_intent", "reply", "ignore"];
671
- case "email_received":
672
- return ["reply_email", "send_email", "send_dm", "ignore"];
673
- case "specialization_path":
674
- return ["reply", "record_gap", "update_proficiency", "search_skills", "install_skill", "store_memory", "ignore"];
675
- // Teaching signals
676
- case "teaching_proposed":
677
- return ["accept_teaching", "reject_teaching", "reply", "ignore"];
678
- case "teaching_accepted":
679
- return ["deliver_teaching", "reply", "ignore"];
680
- case "teaching_delivered":
681
- return ["approve_teaching", "reject_teaching", "reply", "ignore"];
682
- case "teaching_opportunity":
683
- return ["propose_teaching", "search_teachers", "reply", "ignore"];
684
- // Credit agreement signals
685
- case "credit_agreement_created":
686
- return ["accept_credit_agreement", "cancel_credit_agreement", "reply", "ignore"];
687
- case "credit_work_delivered":
688
- return ["complete_credit_agreement", "cancel_credit_agreement", "reply", "ignore"];
689
- case "credit_agreement_accepted":
690
- return ["deliver_credit_work", "cancel_credit_agreement", "reply", "ignore"];
691
- // Informational signals
692
- case "new_project":
693
- case "interesting_project":
694
- return ["propose_collab", "reply", "ignore"];
695
- case "bounty_access_denied":
696
- return ["ignore"];
697
- case "task_created":
698
- case "task_deleted":
699
- case "status_updated":
700
- return ["reply", "ignore"];
701
- case "welcome_guide":
702
- return ["reply", "create_post", "ignore"];
703
- case "onboarding_suggestion":
704
- return ["reply", "ignore"];
705
- case "new_bundle_in_domain":
706
- return ["cite_insight", "reply", "ignore"];
707
- case "bundle_cited":
708
- return ["ignore"];
709
- case "webhook_received":
710
- case "webhook.received":
711
- return ["reply", "egress_request", "execute_tool", "ignore"];
712
- case "bounty_opportunity":
713
- return ["apply_bounty", "send_dm", "reply", "ignore"];
714
- default:
715
- return ["reply", "ignore"];
716
- }
379
+ return getAvailableActionsFromMap(signalType, loadedCategories);
717
380
  }
718
381
  // ── Execute agent action ──────────────────────────────────────
719
- /** Validate an ID before URL interpolation — must be numeric or UUID. */
720
- function validateId(id, name) {
721
- const s = String(id ?? "");
722
- if (/^\d+$/.test(s) || /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s))
723
- return s;
724
- throw new Error(`Invalid ${name}: ${s}`);
725
- }
726
382
  export async function executeAgentAction(runtime, action, signal, log) {
727
383
  const target = action.to || signal.senderAddress || "";
728
384
  const content = action.content || "";
729
385
  const channelId = action.channelId || signal.channelId || "";
730
386
  try {
731
- switch (action.action) {
732
- case "reply":
733
- if (channelId) {
734
- await runtime.channels.send(channelId, content);
735
- }
736
- else if (target) {
737
- await runtime.inbox.send({ to: target, content });
738
- }
739
- break;
740
- case "send_dm":
741
- if (target)
742
- await runtime.inbox.send({ to: target, content });
743
- break;
744
- case "follow_back":
745
- case "follow":
746
- case "follow_agent":
747
- if (target)
748
- await runtime.social.follow(target);
749
- break;
750
- case "attest_back":
751
- case "attest":
752
- case "attest_agent":
753
- if (target)
754
- await runtime.social.attest(target, action.reason || "Valued collaborator");
755
- break;
756
- case "endorse_agent":
757
- case "endorse":
758
- if (target) {
759
- await runtime.social.endorse(target, action.skill || "collaboration", action.rating || 3, action.context || undefined);
760
- }
761
- break;
762
- case "revoke_endorsement":
763
- if (target) {
764
- await runtime.social.revokeEndorsement(target, action.skill || "collaboration");
765
- }
766
- break;
767
- case "block_agent":
768
- if (target) {
769
- await runtime.social.block(target);
770
- }
771
- break;
772
- case "unblock_agent":
773
- if (target) {
774
- await runtime.social.unblock(target);
775
- }
776
- break;
777
- case "vote":
778
- if (action.cid) {
779
- await runtime.memory.vote({ cid: action.cid, type: (action.voteType || "up") });
780
- }
781
- break;
782
- case "review":
783
- case "review_commit":
784
- case "comment": {
785
- const projectId = (action.projectId || signal.projectId);
786
- const commitId = (action.commitId || signal.commitId);
787
- const verdict = action.action === "comment" ? "comment" : action.verdict || "comment";
788
- const body = content || "Reviewed";
789
- if (projectId && commitId) {
790
- await runtime.projects.submitReview(projectId, commitId, verdict, body);
791
- }
792
- break;
793
- }
794
- case "request_ai_review": {
795
- const projId2 = (action.projectId || signal.projectId);
796
- const commitId2 = (action.commitId || signal.commitId);
797
- if (projId2 && commitId2) {
798
- const aiResult = await runtime.projects.requestAIReview(projId2, commitId2);
799
- log(`AI review: ${aiResult.verdict} — ${aiResult.findingsCount} finding(s), cost ${aiResult.creditsCost} credits`);
800
- }
801
- break;
802
- }
803
- case "send_message":
804
- if (target) {
805
- await runtime.inbox.send({ to: target, content: content || "Hey! Looking forward to collaborating." });
806
- }
807
- else if (channelId) {
808
- await runtime.channels.send(channelId, content || "Hey everyone! Excited to join.");
809
- }
810
- break;
811
- case "grant": {
812
- const projId = (action.projectId || signal.projectId);
813
- const bId = (action.bountyId || signal.bountyId);
814
- const reqAddr = (signal.senderAddress || target);
815
- if (projId && bId) {
816
- await runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bId}/grant-access`, { requesterAddress: reqAddr });
817
- log(`[reactive] Granted bounty access for ${reqAddr?.slice(0, 10)}... on ${projId}`);
818
- }
819
- break;
820
- }
821
- case "deny": {
822
- const projId = (action.projectId || signal.projectId);
823
- const bId = (action.bountyId || signal.bountyId);
824
- const reqAddr = (signal.senderAddress || target);
825
- if (projId && bId) {
826
- await runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bId}/deny-access`, { requesterAddress: reqAddr });
827
- log(`[reactive] Denied bounty access for ${reqAddr?.slice(0, 10)}... on ${projId}`);
828
- }
829
- break;
830
- }
831
- case "claim":
832
- case "claim_bounty": {
833
- const rawBountyId = action.bountyId || signal.bountyId;
834
- if (rawBountyId) {
835
- const bountyId = validateId(rawBountyId, "bountyId");
836
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/claim`, {});
837
- log(`[reactive] Bounty claimed: ${bountyId} (tx: ${relay.txHash})`);
838
- }
839
- else {
840
- log(`[reactive] Bounty claim requested but no bountyId provided`);
841
- }
842
- break;
843
- }
844
- case "approve_bounty_claimer": {
845
- const rawBountyId = action.bountyId || signal.bountyId;
846
- const claimer = action.claimer;
847
- if (rawBountyId && claimer) {
848
- const bountyId = validateId(rawBountyId, "bountyId");
849
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer });
850
- log(`[reactive] Bounty claimer approved: ${bountyId} → ${claimer} (tx: ${relay.txHash})`);
851
- }
852
- else {
853
- log(`[reactive] approve_bounty_claimer requires bountyId and claimer`);
854
- }
855
- break;
856
- }
857
- case "apply_bounty": {
858
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
859
- const applyMsg = content || action.message || "";
860
- if (!applyMsg || applyMsg.length < 50)
861
- throw new Error("apply_bounty requires a work submission (minimum 50 characters)");
862
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/apply`, { message: applyMsg });
863
- log(`[reactive] Applied to bounty: ${bountyId}`);
864
- break;
865
- }
866
- case "submit_bounty_work": {
867
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
868
- if (content) {
869
- const cids = (action.deliverableCids ?? []);
870
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions`, { content, deliverableCids: cids });
871
- log(`[reactive] Submitted work for bounty: ${bountyId}`);
872
- }
873
- break;
874
- }
875
- case "approve_bounty_application": {
876
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
877
- const applicationId = validateId(action.applicationId, "applicationId");
878
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/approve`, {});
879
- log(`[reactive] Approved bounty application: ${applicationId} on bounty ${bountyId}`);
880
- break;
881
- }
882
- case "reject_bounty_application": {
883
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
884
- const applicationId = validateId(action.applicationId, "applicationId");
885
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/reject`, {});
886
- log(`[reactive] Rejected bounty application: ${applicationId} on bounty ${bountyId}`);
887
- break;
888
- }
889
- case "select_bounty_submission": {
890
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
891
- const submissionId = validateId(action.submissionId, "submissionId");
892
- const selectResult = await runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions/${submissionId}/select`, {});
893
- log(`[reactive] Selected bounty submission: ${submissionId} on bounty ${bountyId}`);
894
- // Bridge: approve winner on-chain so they can claim
895
- try {
896
- const winnerAddress = selectResult?.winner?.applicantAddress;
897
- if (winnerAddress) {
898
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer: winnerAddress });
899
- log(`[reactive] Approved claimer on-chain: ${bountyId} → ${winnerAddress} (tx: ${relay.txHash})`);
900
- }
901
- }
902
- catch { /* non-fatal */ }
903
- break;
904
- }
905
- case "create_community": {
906
- const slug = action.slug;
907
- const name = action.name || content;
908
- const desc = action.description || content || "";
909
- if (slug && name) {
910
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/community", { slug, name, description: desc });
911
- log(`[reactive] Community created: ${slug} (tx: ${relay.txHash})`);
912
- }
913
- break;
914
- }
915
- case "create_project": {
916
- const projName = action.name || content;
917
- const projDesc = action.description || "";
918
- const projId = action.projectId || projName?.toLowerCase().replace(/\s+/g, "-");
919
- if (projId && projName) {
920
- const discovery = await runtime.connection.request("POST", "/v1/projects/discover", { name: projName, description: projDesc });
921
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/project", {
922
- discoveryId: discovery.discoveryId,
923
- projectId: projId, name: projName, description: projDesc,
924
- });
925
- log(`[reactive] Project created: ${projId} (tx: ${relay.txHash})`);
926
- }
927
- break;
928
- }
929
- case "commit_files":
930
- case "gateway_commit": {
931
- const projId = (action.projectId || signal.projectId);
932
- const files = action.files;
933
- const msg = content || "Automated commit";
934
- if (projId && files?.length) {
935
- await runtime.projects.commitFiles(projId, files, msg);
936
- }
937
- break;
938
- }
939
- case "list_project_files": {
940
- const projId = (action.projectId || signal.projectId);
941
- if (projId) {
942
- const files = await runtime.projects.listFiles(projId);
943
- log(`[reactive] Listed ${files.length} files in project ${projId}`);
944
- }
945
- break;
946
- }
947
- case "read_project_file": {
948
- const projId = (action.projectId || signal.projectId);
949
- const filePath = action.filePath;
950
- if (projId && filePath) {
951
- const fileContent = await runtime.projects.readFile(projId, filePath);
952
- log(`[reactive] Read file ${filePath} from project ${projId}`);
953
- }
954
- break;
955
- }
956
- case "list_commits": {
957
- const projId = (action.projectId || signal.projectId);
958
- if (projId) {
959
- const limit = action.limit ?? 20;
960
- const offset = action.offset ?? 0;
961
- const commits = await runtime.projects.listCommits(projId, limit, offset);
962
- log(`[reactive] Listed ${commits.length} commits in project ${projId}`);
963
- }
964
- break;
965
- }
966
- case "get_commit_detail": {
967
- const projId = (action.projectId || signal.projectId);
968
- const commitId = action.commitId;
969
- if (projId && commitId) {
970
- const commit = await runtime.projects.getCommit(projId, commitId);
971
- log(`[reactive] Got commit detail ${commitId} from project ${projId}`);
972
- }
973
- break;
974
- }
975
- case "add_collaborator": {
976
- const projId = (action.projectId || signal.projectId);
977
- const collabAddr = (action.collaboratorAddress || target);
978
- const role = action.role || "editor";
979
- if (projId && collabAddr) {
980
- await runtime.projects.addCollaborator(projId, collabAddr, role);
981
- }
982
- break;
983
- }
984
- case "link_project_to_guild":
985
- case "link_project_to_clique": {
986
- const projId2 = (action.projectId || signal.projectId);
987
- const gId = (action.guildId || action.cliqueId);
988
- if (projId2 && gId != null) {
989
- await runtime.guilds.linkProject(Number(gId), projId2);
990
- await runtime.projects.setGuildAttribution(projId2, String(gId));
991
- // Gateway returns members as [{address, status}] objects where status=2 = accepted
992
- const guild = await runtime.guilds.get(Number(gId));
993
- const rawMembers = (guild?.members ?? []);
994
- const myAddr = runtime.connection.address?.toLowerCase();
995
- for (const m of rawMembers) {
996
- if (typeof m === "string")
997
- continue; // bare string — can't confirm accepted
998
- if (m.status === 2 && m.address?.toLowerCase() !== myAddr) {
999
- try {
1000
- await runtime.projects.addCollaborator(projId2, m.address, "editor");
1001
- }
1002
- catch { /* best-effort */ }
1003
- }
1004
- }
1005
- log(`[reactive] Linked project ${projId2} to guild ${gId}`);
1006
- }
1007
- break;
1008
- }
1009
- case "propose_guild":
1010
- case "propose_clique": {
1011
- const guildName = action.name || content;
1012
- const guildMembers2 = action.members;
1013
- const guildDesc = action.description || "";
1014
- if (guildName && guildMembers2 && guildMembers2.length >= 2) {
1015
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/guild", { name: guildName, description: guildDesc, members: guildMembers2 });
1016
- log(`[reactive] Proposed guild "${guildName}" (tx: ${relay.txHash})`);
1017
- }
1018
- break;
1019
- }
1020
- case "join_guild":
1021
- case "approve_guild": {
1022
- const gId = action.guildId;
1023
- if (gId) {
1024
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/approve`, {});
1025
- log(`[reactive] Approved guild #${gId} membership (tx: ${relay.txHash})`);
1026
- }
1027
- break;
1028
- }
1029
- case "reject_guild": {
1030
- const gId = action.guildId;
1031
- if (gId) {
1032
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/reject`, {});
1033
- log(`[reactive] Rejected guild #${gId} membership (tx: ${relay.txHash})`);
1034
- }
1035
- break;
1036
- }
1037
- case "leave_guild": {
1038
- const gId = action.guildId;
1039
- if (gId) {
1040
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/leave`, {});
1041
- log(`[reactive] Left guild #${gId} (tx: ${relay.txHash})`);
1042
- }
1043
- break;
1044
- }
1045
- case "publish":
1046
- case "create_post": {
1047
- const community = action.community || "general";
1048
- const title = action.title || content?.slice(0, 100) || "Untitled";
1049
- const body = content || "";
1050
- if (body) {
1051
- await runtime.memory.publishKnowledge({ title, body, community });
1052
- }
1053
- break;
1054
- }
1055
- case "execute":
1056
- if (channelId && content) {
1057
- await runtime.channels.send(channelId, content);
1058
- }
1059
- else if (target && content) {
1060
- await runtime.inbox.send({ to: target, content });
1061
- }
1062
- break;
1063
- case "accept": {
1064
- const projId = (action.projectId || signal.projectId);
1065
- const channelSlug = projId ? `project-${projId}` : "";
1066
- if (channelSlug) {
1067
- await runtime.channels.send(channelSlug, content || "Accepted the task — I'll get started.");
1068
- }
1069
- break;
1070
- }
1071
- case "acknowledge": {
1072
- const projId = (action.projectId || signal.projectId);
1073
- const channelSlug = projId ? `project-${projId}` : "";
1074
- if (channelSlug) {
1075
- await runtime.channels.send(channelSlug, content || "Got it, thanks for the mention!");
1076
- }
1077
- break;
1078
- }
1079
- case "deploy_preview": {
1080
- const projId = (action.projectId || signal.projectId);
1081
- if (projId) {
1082
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/project/${projId}/deployment`, { prepaidHours: action.prepaidHours ?? 2 });
1083
- log(`[reactive] Deploy preview for ${projId} (tx: ${relay.txHash})`);
1084
- }
1085
- break;
1086
- }
1087
- case "fork_project": {
1088
- const projId = (action.projectId || signal.projectId);
1089
- if (projId) {
1090
- const res = await runtime.projects.forkProject(projId, {
1091
- name: action.name,
1092
- });
1093
- log(`[reactive] Forked project ${projId} → ${res.projectId}`);
1094
- }
1095
- break;
1096
- }
1097
- case "create_merge_request": {
1098
- const srcId = (action.sourceProjectId || signal.projectId);
1099
- const tgtId = action.targetProjectId;
1100
- const mrTitle = action.title;
1101
- const commitIds = action.commitIds;
1102
- if (srcId && tgtId && mrTitle && commitIds?.length) {
1103
- const mr = await runtime.projects.createMergeRequest(srcId, tgtId, mrTitle, commitIds, action.description);
1104
- log(`[reactive] Created merge request "${mrTitle}" (${mr.id})`);
1105
- }
1106
- break;
1107
- }
1108
- case "list_merge_requests": {
1109
- const projId = (action.projectId || signal.projectId);
1110
- if (projId) {
1111
- const res = await runtime.projects.listMergeRequests(projId, {
1112
- status: action.status,
1113
- });
1114
- log(`[reactive] Listed ${res.mergeRequests.length} merge requests on ${projId}`);
1115
- }
1116
- break;
1117
- }
1118
- case "get_merge_request": {
1119
- const projId = (action.projectId || signal.projectId);
1120
- const mrId = action.mrId;
1121
- if (projId && mrId) {
1122
- const mr = await runtime.projects.getMergeRequest(projId, mrId);
1123
- log(`[reactive] Got merge request "${mr.title}" (${mr.status})`);
1124
- }
1125
- break;
1126
- }
1127
- case "merge_merge_request": {
1128
- const projId = (action.projectId || signal.projectId);
1129
- const mrId = action.mrId;
1130
- if (projId && mrId) {
1131
- await runtime.projects.mergeMergeRequest(projId, mrId, action.comment);
1132
- log(`[reactive] Merged MR ${mrId} into ${projId}`);
1133
- }
1134
- break;
1135
- }
1136
- case "close_merge_request": {
1137
- const projId = (action.projectId || signal.projectId);
1138
- const mrId = action.mrId;
1139
- if (projId && mrId) {
1140
- await runtime.projects.closeMergeRequest(projId, mrId, action.comment);
1141
- log(`[reactive] Closed MR ${mrId}`);
1142
- }
1143
- break;
1144
- }
1145
- case "claim_reward": {
1146
- const pool = action.pool || "nook";
1147
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/reward/claim", { pool });
1148
- log(`[reactive] Reward claimed from ${pool} pool (tx: ${relay.txHash})`);
1149
- break;
1150
- }
1151
- case "create_task": {
1152
- const projId = (action.projectId || signal.projectId);
1153
- const title = (content || action.title);
1154
- if (projId && title) {
1155
- await runtime.connection.request("POST", `/v1/projects/${projId}/tasks`, {
1156
- title,
1157
- description: action.description,
1158
- milestoneId: action.milestoneId,
1159
- priority: action.priority ?? "medium",
1160
- labels: action.labels,
1161
- dueDate: action.dueDate,
1162
- });
1163
- log(`[reactive] Task created in ${projId}: ${title}`);
1164
- }
1165
- break;
1166
- }
1167
- case "assign_task": {
1168
- const projId = (action.projectId || signal.projectId);
1169
- const tid = (action.taskId);
1170
- const assignee = (action.assigneeAddress || action.assignee);
1171
- if (projId && tid && assignee) {
1172
- await runtime.projects.assignTask(projId, tid, assignee);
1173
- log(`[reactive] Assigned task ${tid} in ${projId} to ${assignee}`);
1174
- }
1175
- break;
1176
- }
1177
- case "complete_task":
1178
- case "update_task": {
1179
- const projId = (action.projectId || signal.projectId);
1180
- const tid = action.taskId;
1181
- if (projId && tid) {
1182
- const updates = {};
1183
- if (action.action === "complete_task") {
1184
- updates.status = "completed";
1185
- }
1186
- else {
1187
- if (action.status)
1188
- updates.status = action.status;
1189
- if (action.title)
1190
- updates.title = action.title;
1191
- if (action.description)
1192
- updates.description = action.description;
1193
- if (action.priority)
1194
- updates.priority = action.priority;
1195
- if (action.milestoneId !== undefined)
1196
- updates.milestoneId = action.milestoneId;
1197
- if (action.labels)
1198
- updates.labels = action.labels;
1199
- if (action.dueDate)
1200
- updates.dueDate = action.dueDate;
1201
- }
1202
- await runtime.connection.request("PATCH", `/v1/projects/${projId}/tasks/${tid}`, updates);
1203
- log(`[reactive] Task ${action.action === "complete_task" ? "completed" : "updated"}: ${tid}`);
1204
- }
1205
- break;
1206
- }
1207
- case "find_agents":
1208
- case "find_matching_agents": {
1209
- const skills = action.skills ?? [];
1210
- if (skills.length) {
1211
- const matchResult = await runtime.matching.findAgents(skills, {
1212
- count: action.count,
1213
- });
1214
- log(`[reactive] Found ${matchResult.total} matching agents for [${skills.join(", ")}]`);
1215
- }
1216
- break;
1217
- }
1218
- case "assemble_team": {
1219
- const desc = content || action.description || "";
1220
- if (desc) {
1221
- const teamResult = await runtime.matching.assembleTeam({
1222
- description: desc,
1223
- requiredSkills: action.requiredSkills,
1224
- teamSize: action.teamSize,
1225
- });
1226
- log(`[reactive] Team assembled: ${teamResult.members.length} members, ${Math.round(teamResult.coverageScore * 100)}% coverage`);
1227
- }
1228
- break;
1229
- }
1230
- case "accept_invitation":
1231
- case "decline_invitation": {
1232
- const invId = (action.invitationId || signal.invitationId);
1233
- if (invId) {
1234
- const verb = action.action === "accept_invitation" ? "accept" : "decline";
1235
- await runtime.connection.request("POST", `/v1/teams/invitations/${invId}/${verb}`, {});
1236
- log(`[reactive] Team invitation ${verb}ed: ${invId.slice(0, 8)}...`);
1237
- }
1238
- break;
1239
- }
1240
- // ── Marketplace actions ──
1241
- case "create_listing":
1242
- case "list_service": {
1243
- const title = (content || action.title);
1244
- const desc = action.description || "";
1245
- const category = action.category || "general";
1246
- if (title) {
1247
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/list", {
1248
- title, description: desc, category,
1249
- pricingModel: action.pricingModel ?? 0,
1250
- priceAmount: action.priceAmount ?? "0",
1251
- tags: action.tags ?? [],
1252
- ...(action.tokenAddress ? { tokenAddress: action.tokenAddress } : {}),
1253
- });
1254
- log(`[reactive] Listed service "${title}" (tx: ${relay.txHash})`);
1255
- }
1256
- break;
1257
- }
1258
- case "create_agreement": {
1259
- const listingId = action.listingId;
1260
- const terms = (content || action.terms);
1261
- const deadline = (action.deadline ?? Math.floor(Date.now() / 1000) + 7 * 86400);
1262
- if (listingId != null && terms) {
1263
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/agree", {
1264
- listingId, terms, deadline,
1265
- tokenAmount: action.tokenAmount ?? "0",
1266
- ...(action.tokenAddress ? { tokenAddress: action.tokenAddress } : {}),
1267
- });
1268
- log(`[reactive] Created agreement for listing #${listingId} (tx: ${relay.txHash})`);
1269
- }
1270
- break;
1271
- }
1272
- case "deliver_work": {
1273
- const agId = action.agreementId;
1274
- const deliveryCid = (content || action.deliveryCid);
1275
- if (agId != null && deliveryCid) {
1276
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/deliver", {
1277
- agreementId: agId, deliveryCid,
1278
- });
1279
- log(`[reactive] Delivered work for agreement #${agId} (tx: ${relay.txHash})`);
1280
- }
1281
- break;
1282
- }
1283
- case "settle_agreement": {
1284
- const agId = action.agreementId;
1285
- if (agId != null) {
1286
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/settle", {
1287
- agreementId: agId,
1288
- });
1289
- log(`[reactive] Settled agreement #${agId} (tx: ${relay.txHash})`);
1290
- }
1291
- break;
1292
- }
1293
- case "dispute_agreement": {
1294
- const agId = action.agreementId;
1295
- if (agId != null) {
1296
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/dispute", {
1297
- agreementId: agId,
1298
- });
1299
- log(`[reactive] Disputed agreement #${agId} (tx: ${relay.txHash})`);
1300
- }
1301
- break;
1302
- }
1303
- case "cancel_agreement": {
1304
- const agId = action.agreementId;
1305
- if (agId != null) {
1306
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/cancel", {
1307
- agreementId: agId,
1308
- });
1309
- log(`[reactive] Cancelled agreement #${agId} (tx: ${relay.txHash})`);
1310
- }
1311
- break;
1312
- }
1313
- case "submit_review": {
1314
- const agId = action.agreementId;
1315
- const rating = action.rating;
1316
- const reviewComment = (content || action.comment || "");
1317
- if (agId != null && rating != null) {
1318
- await runtime.marketplace.submitReview(agId, rating, reviewComment);
1319
- log(`[reactive] Submitted review for agreement #${agId} (${rating}/5)`);
1320
- }
1321
- break;
1322
- }
1323
- case "send_agreement_message": {
1324
- const agId = action.agreementId;
1325
- const msgType = action.messageType || "general";
1326
- const msgContent = (content || action.content);
1327
- if (agId != null && msgContent) {
1328
- const msgBody = { messageType: msgType, content: msgContent };
1329
- if (action.attachmentCid)
1330
- msgBody.attachmentCid = action.attachmentCid;
1331
- await runtime.connection.request("POST", `/v1/marketplace/agreements/${agId}/messages`, msgBody);
1332
- log(`[reactive] Sent ${msgType} message for agreement #${agId}`);
1333
- }
1334
- break;
1335
- }
1336
- case "create_bounty": {
1337
- const payload = action;
1338
- const title = payload?.title;
1339
- const description = payload?.description ?? content ?? "";
1340
- const community = payload?.community ?? "";
1341
- const deadline = payload?.deadline ?? Math.floor(Date.now() / 1000) + 604800; // 7 days
1342
- const tokenRewardAmount = payload?.tokenRewardAmount ?? "0";
1343
- if (!title)
1344
- throw new Error("create_bounty requires title");
1345
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/bounty", {
1346
- title, description, community, deadline, tokenRewardAmount,
1347
- });
1348
- log(`[reactive] Created bounty "${title}" (tx: ${relay.txHash})`);
1349
- break;
1350
- }
1351
- case "post_reply": {
1352
- const parentCid = (action.parentCid ?? action.sourceId ?? "");
1353
- if (!parentCid)
1354
- throw new Error("post_reply requires parentCid");
1355
- const replyContent = (content ?? action.body ?? "");
1356
- const replyCommunity = (action.community ?? "");
1357
- await runtime.memory.publishComment({ parentCid, body: replyContent, community: replyCommunity });
1358
- break;
1359
- }
1360
- case "propose_collab": {
1361
- const collabTarget = (action.targetAddress ?? action.recipientAddress ?? target);
1362
- if (!collabTarget)
1363
- throw new Error("propose_collab requires targetAddress");
1364
- const msg = content || "I'd like to collaborate with you!";
1365
- await runtime.inbox.send({ to: collabTarget, content: msg });
1366
- break;
1367
- }
1368
- case "create_bundle": {
1369
- const bundleTitle = action.title;
1370
- if (!bundleTitle)
1371
- throw new Error("create_bundle requires title");
1372
- const bundleDesc = content || action.description || "";
1373
- const domain = action.domain || "";
1374
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/bundle", {
1375
- title: bundleTitle, description: bundleDesc, domain,
1376
- });
1377
- log(`[reactive] Created bundle "${bundleTitle}" (tx: ${relay.txHash})`);
1378
- break;
1379
- }
1380
- case "update_service": {
1381
- const listingId = action.listingId;
1382
- if (!listingId)
1383
- throw new Error("update_service requires listingId");
1384
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/update", {
1385
- listingId, ...action,
1386
- });
1387
- log(`[reactive] Updated service listing #${listingId} (tx: ${relay.txHash})`);
1388
- break;
1389
- }
1390
- case "approve_bounty_work": {
1391
- const bountyId = (action.bountyId || signal.bountyId);
1392
- if (!bountyId)
1393
- throw new Error("approve_bounty_work requires bountyId");
1394
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve`, {});
1395
- log(`[reactive] Approved bounty work: ${bountyId} (tx: ${relay.txHash})`);
1396
- break;
1397
- }
1398
- case "dispute_bounty_work": {
1399
- const bountyId = (action.bountyId || signal.bountyId);
1400
- if (!bountyId)
1401
- throw new Error("dispute_bounty_work requires bountyId");
1402
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/dispute`, {});
1403
- log(`[reactive] Disputed bounty work: ${bountyId} (tx: ${relay.txHash})`);
1404
- break;
1405
- }
1406
- case "cancel_bounty": {
1407
- const bountyId = (action.bountyId || signal.bountyId);
1408
- if (!bountyId)
1409
- throw new Error("cancel_bounty requires bountyId");
1410
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/cancel`, {});
1411
- log(`[reactive] Cancelled bounty: ${bountyId} (tx: ${relay.txHash})`);
1412
- break;
1413
- }
1414
- case "unclaim_bounty": {
1415
- const bountyId = (action.bountyId || signal.bountyId);
1416
- if (!bountyId)
1417
- throw new Error("unclaim_bounty requires bountyId");
1418
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/unclaim`, {});
1419
- log(`[reactive] Unclaimed bounty: ${bountyId} (tx: ${relay.txHash})`);
1420
- break;
1421
- }
1422
- case "expire_dispute": {
1423
- const agId = action.agreementId;
1424
- if (agId != null) {
1425
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/expire-dispute", {
1426
- agreementId: agId,
1427
- });
1428
- log(`[reactive] Expired dispute for agreement #${agId} (tx: ${relay.txHash})`);
1429
- }
1430
- break;
1431
- }
1432
- case "expire_delivered": {
1433
- const agId = action.agreementId;
1434
- if (agId != null) {
1435
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/expire-delivered", {
1436
- agreementId: agId,
1437
- });
1438
- log(`[reactive] Expired delivered for agreement #${agId} (tx: ${relay.txHash})`);
1439
- }
1440
- break;
1441
- }
1442
- case "workspace_create": {
1443
- const wsName = (action.name || content);
1444
- if (wsName) {
1445
- const ws = await runtime.workspaces.create({ name: wsName, description: action.description });
1446
- log(`[reactive] Created workspace: ${ws.id}`);
1447
- }
1448
- break;
1449
- }
1450
- case "workspace_set": {
1451
- const wsId = action.workspaceId;
1452
- const wsKey = action.key;
1453
- const wsVal = action.value ?? content;
1454
- if (wsId && wsKey) {
1455
- await runtime.workspaces.setState(wsId, wsKey, wsVal);
1456
- log(`[reactive] Set ${wsKey} in workspace ${wsId}`);
1457
- }
1458
- break;
1459
- }
1460
- case "workspace_snapshot": {
1461
- const wsId = action.workspaceId;
1462
- if (wsId) {
1463
- await runtime.workspaces.createSnapshot(wsId, action.label);
1464
- log(`[reactive] Created snapshot in workspace ${wsId}`);
1465
- }
1466
- break;
1467
- }
1468
- case "propose_action": {
1469
- const wsId = action.workspaceId;
1470
- const title = (action.title || content);
1471
- if (wsId && title) {
1472
- const prop = await runtime.workspaces.createProposal(wsId, {
1473
- title,
1474
- description: action.description,
1475
- actionType: action.actionType ?? "custom",
1476
- actionPayload: action.actionPayload,
1477
- quorumType: action.quorumType,
1478
- quorumThreshold: action.quorumThreshold,
1479
- });
1480
- log(`[reactive] Created proposal: ${prop.proposal?.id}`);
1481
- }
1482
- break;
1483
- }
1484
- case "vote_proposal": {
1485
- const wsId = action.workspaceId;
1486
- const proposalId = action.proposalId;
1487
- if (wsId && proposalId && action.vote !== undefined) {
1488
- await runtime.workspaces.vote(wsId, proposalId, action.vote, action.reason);
1489
- log(`[reactive] Voted on proposal ${proposalId}`);
1490
- }
1491
- break;
1492
- }
1493
- case "cancel_proposal": {
1494
- const wsId = action.workspaceId;
1495
- const proposalId = action.proposalId;
1496
- if (wsId && proposalId) {
1497
- await runtime.workspaces.cancelProposal(wsId, proposalId);
1498
- log(`[reactive] Cancelled proposal ${proposalId}`);
1499
- }
1500
- break;
1501
- }
1502
- // ── Real World Actions ──
1503
- case "egress_request":
1504
- case "http_request": {
1505
- const targetUrl = action.url ?? "";
1506
- const httpMethod = (action.method ?? "GET").toUpperCase();
1507
- if (!targetUrl)
1508
- throw new Error("egress_request requires url");
1509
- const httpResult = await runtime.tools.httpRequest(targetUrl, httpMethod, {
1510
- headers: action.headers,
1511
- body: action.body,
1512
- timeout: action.timeout,
1513
- credentialService: action.credentialService,
1514
- });
1515
- log(`[reactive] HTTP ${httpMethod} ${targetUrl.slice(0, 60)} → ${httpResult?.status ?? "ok"}`);
1516
- break;
1517
- }
1518
- case "execute_tool": {
1519
- const toolName = action.toolName ?? "";
1520
- if (!toolName)
1521
- throw new Error("execute_tool requires toolName");
1522
- const toolArgs = (action.args ?? action.arguments ?? {});
1523
- const toolResult = await runtime.tools.executeTool(toolName, toolArgs);
1524
- log(`[reactive] Executed tool: ${toolName}`);
1525
- break;
1526
- }
1527
- case "exec_code": {
1528
- const cmd = action.command ?? "";
1529
- const img = (action.image ?? "node:20-slim");
1530
- const files = (action.files ?? {});
1531
- const timeout = action.timeout;
1532
- const projId = action.projectId;
1533
- if (!cmd)
1534
- throw new Error("exec_code requires command");
1535
- const execResult = await runtime.tools.execCode(cmd, img, { files, timeout, projectId: projId });
1536
- log(`[reactive] Executed code (${img}): exit=${execResult.exitCode}`);
1537
- break;
1538
- }
1539
- case "connect_mcp_server": {
1540
- const serverUrl = (action.serverUrl ?? action.url);
1541
- const serverName = (action.serverName ?? action.name ?? "mcp-server");
1542
- if (!serverUrl)
1543
- throw new Error("connect_mcp_server requires serverUrl");
1544
- await runtime.tools.connectMcpServer(serverUrl, serverName);
1545
- log(`[reactive] Connected MCP server: ${serverUrl.slice(0, 60)}`);
1546
- break;
1547
- }
1548
- case "disconnect_mcp_server": {
1549
- const serverId = (action.serverId ?? action.serverUrl);
1550
- if (!serverId)
1551
- throw new Error("disconnect_mcp_server requires serverId");
1552
- await runtime.tools.disconnectMcpServer(serverId);
1553
- log(`[reactive] Disconnected MCP server: ${serverId.slice(0, 60)}`);
1554
- break;
1555
- }
1556
- case "call_mcp_tool":
1557
- case "use_mcp_tool": {
1558
- const mcpToolName = action.toolName ?? "";
1559
- if (!mcpToolName)
1560
- throw new Error("call_mcp_tool requires toolName");
1561
- const mcpToolArgs = (action.args ?? action.arguments ?? {});
1562
- const mcpResult = await runtime.tools.executeTool(mcpToolName, mcpToolArgs);
1563
- log(`[reactive] MCP tool called: ${mcpToolName}`);
1564
- break;
1565
- }
1566
- case "register_webhook": {
1567
- const source = action.source ?? "";
1568
- if (!source)
1569
- throw new Error("register_webhook requires source");
1570
- await runtime.connection.request("POST", "/v1/agents/me/webhooks", {
1571
- source,
1572
- description: action.description,
1573
- secret: action.secret,
1574
- });
1575
- log(`[reactive] Registered webhook: ${source}`);
1576
- break;
1577
- }
1578
- case "publish_insight": {
1579
- const title = action.title;
1580
- const body = action.body;
1581
- if (title && body) {
1582
- await runtime.insights?.publish({
1583
- title,
1584
- body,
1585
- strategyType: action.strategyType,
1586
- tags: action.tags,
1587
- outcomeScore: action.outcomeScore,
1588
- workspaceId: action.workspaceId,
1589
- });
1590
- log(`[reactive] Published insight: ${title}`);
1591
- }
1592
- break;
1593
- }
1594
- case "cite_insight": {
1595
- const insightId = action.insightId;
1596
- if (insightId) {
1597
- await runtime.insights?.cite(insightId, action.context, action.outcomeScore);
1598
- log(`[reactive] Cited insight ${insightId}`);
1599
- }
1600
- break;
1601
- }
1602
- case "apply_insight": {
1603
- const insightId = action.insightId;
1604
- if (insightId) {
1605
- await runtime.insights?.apply(insightId, action.context, action.outcomeScore);
1606
- log(`[reactive] Applied insight ${insightId}`);
1607
- }
1608
- break;
1609
- }
1610
- case "deposit_treasury": {
1611
- const guildId = action.guildId;
1612
- const amount = action.amount;
1613
- if (guildId && amount) {
1614
- await runtime.guilds.depositTreasury(guildId, amount, action.memo);
1615
- log(`[reactive] Deposited ${amount} to guild ${guildId} treasury`);
1616
- }
1617
- break;
1618
- }
1619
- case "withdraw_treasury": {
1620
- const guildId = action.guildId;
1621
- const amount = action.amount;
1622
- if (guildId && amount) {
1623
- await runtime.guilds.withdrawTreasury(guildId, amount, action.memo);
1624
- log(`[reactive] Withdrew ${amount} from guild ${guildId} treasury`);
1625
- }
1626
- break;
1627
- }
1628
- case "fund_bounty_from_treasury": {
1629
- const guildId = action.guildId;
1630
- const bountyId = action.bountyId;
1631
- const amount = action.amount;
1632
- if (guildId && bountyId && amount) {
1633
- await runtime.guilds.fundBountyFromTreasury(guildId, bountyId, amount, action.proposalId);
1634
- log(`[reactive] Funded bounty ${bountyId} with ${amount} from guild ${guildId} treasury`);
1635
- }
1636
- break;
1637
- }
1638
- case "distribute_revenue": {
1639
- const guildId = action.guildId;
1640
- if (guildId) {
1641
- await runtime.guilds.distributeRevenue(guildId, action.amount);
1642
- log(`[reactive] Distributed revenue for guild ${guildId}`);
1643
- }
1644
- break;
1645
- }
1646
- case "create_swarm": {
1647
- if (action.title && action.subtasks) {
1648
- await runtime.swarms?.create({ title: action.title, description: action.description, workspaceId: action.workspaceId, subtasks: action.subtasks });
1649
- log(`[reactive] Created swarm: ${action.title}`);
1650
- }
1651
- break;
1652
- }
1653
- case "claim_subtask": {
1654
- const stId = action.subtaskId;
1655
- if (stId) {
1656
- await runtime.swarms?.claimSubtask(stId);
1657
- log(`[reactive] Claimed subtask ${stId}`);
1658
- }
1659
- break;
1660
- }
1661
- case "submit_swarm_result": {
1662
- const stId = action.subtaskId;
1663
- if (stId && action.content) {
1664
- await runtime.swarms?.submitResult(stId, action.content, action.resultType);
1665
- log(`[reactive] Submitted result for subtask ${stId}`);
1666
- }
1667
- break;
1668
- }
1669
- case "aggregate_swarm": {
1670
- const swarmId = action.swarmId;
1671
- if (swarmId) {
1672
- await runtime.swarms?.aggregate(swarmId, action.summary);
1673
- log(`[reactive] Aggregated swarm ${swarmId}`);
1674
- }
1675
- break;
1676
- }
1677
- case "record_gap": {
1678
- if (action.queryText) {
1679
- await runtime.specialization?.recordGap({ queryText: action.queryText, queryTags: action.queryTags || [], communityId: action.communityId });
1680
- log(`[reactive] Recorded skill gap: ${action.queryText}`);
1681
- }
1682
- break;
1683
- }
1684
- case "update_proficiency": {
1685
- const domain = action.skillDomain;
1686
- if (domain) {
1687
- await runtime.specialization?.updateProficiency(domain, Number(action.proficiency || 0));
1688
- log(`[reactive] Updated proficiency: ${domain}`);
1689
- }
1690
- break;
1691
- }
1692
- case "generate_recommendations": {
1693
- await runtime.specialization?.generateRecommendations();
1694
- log(`[reactive] Generated specialization recommendations`);
1695
- break;
1696
- }
1697
- case "dismiss_recommendation": {
1698
- const recId = action.recommendationId;
1699
- if (recId) {
1700
- await runtime.specialization?.dismissRecommendation(recId);
1701
- log(`[reactive] Dismissed recommendation ${recId}`);
1702
- }
1703
- break;
1704
- }
1705
- // ── Intents ──
1706
- case "create_intent": {
1707
- const intentRes = await runtime.intents.create({
1708
- title: action.title || action.content || "Untitled intent",
1709
- description: action.description || action.content || "",
1710
- requiredSkills: action.requiredSkills,
1711
- budgetAmount: action.budgetAmount,
1712
- category: action.category,
1713
- tags: action.tags,
1714
- });
1715
- log(`[reactive] Created intent ${intentRes.id}`);
1716
- break;
1717
- }
1718
- case "browse_intents": {
1719
- const browseRes = await runtime.intents.list({
1720
- status: action.status || "open",
1721
- category: action.category,
1722
- q: action.q,
1723
- limit: 20,
1724
- });
1725
- log(`[reactive] Found ${browseRes.total} intents`);
1726
- break;
1727
- }
1728
- case "submit_proposal": {
1729
- const spIntentId = action.intentId;
1730
- if (spIntentId) {
1731
- const proposalRes = await runtime.intents.submitProposal(spIntentId, {
1732
- description: action.description || action.content || "",
1733
- approach: action.approach,
1734
- estimatedCost: action.estimatedCost,
1735
- estimatedDurationHours: action.estimatedDurationHours,
1736
- });
1737
- log(`[reactive] Submitted proposal ${proposalRes.id}`);
1738
- }
1739
- break;
1740
- }
1741
- case "accept_proposal": {
1742
- const apIntentId = action.intentId;
1743
- const apProposalId = action.proposalId;
1744
- if (apIntentId && apProposalId) {
1745
- await runtime.intents.acceptProposal(apIntentId, apProposalId);
1746
- log(`[reactive] Accepted proposal ${apProposalId}`);
1747
- }
1748
- break;
1749
- }
1750
- case "reject_proposal": {
1751
- const rpIntentId = action.intentId;
1752
- const rpProposalId = action.proposalId;
1753
- if (rpIntentId && rpProposalId) {
1754
- const rpReason = action.content || action.reason || "";
1755
- await runtime.intents.rejectProposal(rpIntentId, rpProposalId, rpReason);
1756
- log(`[reactive] Rejected proposal ${rpProposalId}`);
1757
- }
1758
- break;
1759
- }
1760
- case "cancel_intent": {
1761
- const ciId = action.intentId;
1762
- if (ciId) {
1763
- await runtime.intents.cancel(ciId);
1764
- log(`[reactive] Cancelled intent ${ciId}`);
1765
- }
1766
- break;
1767
- }
1768
- case "complete_intent": {
1769
- const coiId = action.intentId;
1770
- if (coiId) {
1771
- await runtime.intents.complete(coiId);
1772
- log(`[reactive] Completed intent ${coiId}`);
1773
- }
1774
- break;
1775
- }
1776
- case "withdraw_proposal": {
1777
- const wpIntentId = action.intentId;
1778
- const wpProposalId = action.proposalId;
1779
- if (wpIntentId && wpProposalId) {
1780
- await runtime.intents.withdrawProposal(wpIntentId, wpProposalId);
1781
- log(`[reactive] Withdrew proposal ${wpProposalId}`);
1782
- }
1783
- break;
1784
- }
1785
- case "query_oracle": {
1786
- const oEntityType = action.entityType;
1787
- const oEntityId = action.entityId;
1788
- if (oEntityType && oEntityId) {
1789
- let oracleRes;
1790
- switch (oEntityType) {
1791
- case "project":
1792
- oracleRes = await runtime.oracle.getProjectSignals(oEntityId);
1793
- break;
1794
- case "agent":
1795
- oracleRes = await runtime.oracle.getAgentSignals(oEntityId);
1796
- break;
1797
- case "intent":
1798
- oracleRes = await runtime.oracle.getIntentSignals(oEntityId);
1799
- break;
1800
- case "guild":
1801
- oracleRes = await runtime.oracle.getGuildSignals(oEntityId);
1802
- break;
1803
- }
1804
- if (oracleRes)
1805
- log(`[reactive] Oracle ${oEntityType}/${oEntityId}: ${JSON.stringify(oracleRes.signals).slice(0, 100)}`);
1806
- }
1807
- break;
1808
- }
1809
- case "create_search_subscription": {
1810
- const subRes = await runtime.discovery.createSubscription({
1811
- label: action.label || "Search subscription",
1812
- query: action.query || action.content || "",
1813
- types: action.types,
1814
- frequencyMinutes: action.frequencyMinutes,
1815
- });
1816
- log(`[reactive] Created search subscription ${subRes.id}`);
1817
- break;
1818
- }
1819
- // ── Clawnch Token Launching ──
1820
- case "preview_token_launch": {
1821
- const prevRes = await runtime.connection.request("POST", "/v1/clawnch/preview", { tokenName: action.tokenName, tokenTicker: action.tokenTicker, description: action.description || action.content, imageUrl: action.imageUrl });
1822
- log(`[reactive] Token preview: ${prevRes?.launch ? "OK" : "done"}`);
1823
- break;
1824
- }
1825
- case "launch_token": {
1826
- const launchRes = await runtime.connection.request("POST", "/v1/clawnch/launch", { tokenName: action.tokenName, tokenTicker: action.tokenTicker, description: action.description || action.content, imageUrl: action.imageUrl });
1827
- const tokenAddr = launchRes?.launch;
1828
- log(`[reactive] Launched token ${tokenAddr?.token_address ?? "pending"}`);
1829
- break;
1830
- }
1831
- case "claim_clawnch_fees": {
1832
- const claimAddr = action.tokenAddress;
1833
- if (claimAddr) {
1834
- const claimRes = await runtime.connection.request("POST", "/v1/clawnch/claim-fees", { tokenAddress: claimAddr });
1835
- log(`[reactive] Claimed fees for ${claimAddr.slice(0, 10)}...`);
1836
- }
1837
- break;
1838
- }
1839
- case "get_token_analytics": {
1840
- const analyticsAddr = action.tokenAddress;
1841
- if (analyticsAddr) {
1842
- const analyticsRes = await runtime.connection.request("GET", `/v1/clawnch/analytics/token/${analyticsAddr}`);
1843
- log(`[reactive] Token analytics for ${analyticsAddr.slice(0, 10)}...`);
1844
- }
1845
- break;
1846
- }
1847
- case "send_email": {
1848
- const emailTo = action.to;
1849
- const subject = (action.subject || content?.slice(0, 100));
1850
- const emailBody = (action.body || content);
1851
- if (!emailTo || !subject || !emailBody)
1852
- throw new Error("send_email requires to, subject, body");
1853
- await runtime.connection.request("POST", "/v1/email/send", { to: emailTo, subject, bodyText: emailBody });
1854
- log(`[reactive] Email sent to ${emailTo.slice(0, 30)}`);
1855
- break;
1856
- }
1857
- case "reply_email": {
1858
- const msgId = action.messageId;
1859
- const replyBody = (action.body || content);
1860
- if (!msgId || !replyBody)
1861
- throw new Error("reply_email requires messageId and body");
1862
- await runtime.connection.request("POST", `/v1/email/${msgId}/reply`, { bodyText: replyBody });
1863
- log(`[reactive] Replied to email ${msgId.slice(0, 10)}...`);
1864
- break;
1865
- }
1866
- case "check_email": {
1867
- await runtime.connection.request("GET", "/v1/email/messages?direction=inbound&limit=10");
1868
- log(`[reactive] Checked email inbox`);
1869
- break;
1870
- }
1871
- case "create_email_inbox": {
1872
- const localPart = action.username;
1873
- if (!localPart)
1874
- throw new Error("create_email_inbox requires username");
1875
- await runtime.connection.request("POST", "/v1/email/inbox", { username: localPart, displayName: action.displayName });
1876
- log(`[reactive] Created email inbox: ${localPart}`);
1877
- break;
1878
- }
1879
- // ── Skill Registry ──────────────────────────────────────────────
1880
- case "search_skills": {
1881
- const q = action.query || action.q || "";
1882
- const category = action.category;
1883
- const limit = action.limit || 20;
1884
- const params = new URLSearchParams();
1885
- if (q)
1886
- params.set("q", String(q));
1887
- if (category)
1888
- params.set("category", String(category));
1889
- params.set("limit", String(limit));
1890
- await runtime.connection.request("GET", `/v1/skills/registry?${params.toString()}`);
1891
- log(`[reactive] Searched skills: "${q}"`);
1892
- break;
1893
- }
1894
- case "publish_skill": {
1895
- const name = action.name;
1896
- if (!name)
1897
- throw new Error("publish_skill requires name");
1898
- await runtime.connection.request("POST", "/v1/skills/registry", {
1899
- name,
1900
- description: action.description,
1901
- packageType: action.packageType || "skill_md",
1902
- category: action.category || "other",
1903
- tags: action.tags,
1904
- content: action.content || content,
1905
- githubUrl: action.githubUrl,
1906
- npmPackage: action.npmPackage,
1907
- version: action.version || "1.0.0",
1908
- });
1909
- log(`[reactive] Published skill: ${name}`);
1910
- break;
1911
- }
1912
- case "install_skill": {
1913
- const skillId = action.skillId || action.skill_id;
1914
- if (!skillId)
1915
- throw new Error("install_skill requires skillId");
1916
- await runtime.connection.request("POST", `/v1/skills/registry/${skillId}/install`);
1917
- log(`[reactive] Installed skill: ${skillId}`);
1918
- break;
1919
- }
1920
- case "review_skill": {
1921
- const skillId = action.skillId || action.skill_id;
1922
- const rating = action.rating;
1923
- if (!skillId || !rating)
1924
- throw new Error("review_skill requires skillId and rating");
1925
- await runtime.connection.request("POST", `/v1/skills/registry/${skillId}/review`, {
1926
- rating, review: action.review || content,
1927
- });
1928
- log(`[reactive] Reviewed skill: ${skillId} (${rating}/5)`);
1929
- break;
1930
- }
1931
- case "trending_skills": {
1932
- const limit = action.limit || 20;
1933
- await runtime.connection.request("GET", `/v1/skills/registry/trending?limit=${limit}`);
1934
- log(`[reactive] Fetched trending skills`);
1935
- break;
1936
- }
1937
- // ── Agent Memory ────────────────────────────────────────────────
1938
- case "store_memory": {
1939
- const memContent = action.body || content;
1940
- if (!memContent)
1941
- throw new Error("store_memory requires content");
1942
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
1943
- type: action.memoryType || action.type || "semantic",
1944
- content: memContent, importance: action.importance, tags: action.tags,
1945
- source: action.source, metadata: action.metadata,
1946
- });
1947
- log(`[reactive] Stored memory`);
1948
- break;
1949
- }
1950
- case "recall_memory": {
1951
- const query = action.query || action.q;
1952
- if (!query)
1953
- throw new Error("recall_memory requires query");
1954
- const rtypes = action.types
1955
- || ((action.memoryType || action.type) ? [String(action.memoryType || action.type)] : undefined);
1956
- await runtime.connection.request("POST", "/v1/agent-memory/recall", {
1957
- query, types: rtypes, limit: action.limit || 10,
1958
- });
1959
- log(`[reactive] Recalled memories for: "${query}"`);
1960
- break;
1961
- }
1962
- case "list_memories": {
1963
- const type = action.memoryType || action.type || "";
1964
- const limit = action.limit || 20;
1965
- const params = new URLSearchParams();
1966
- if (type)
1967
- params.set("type", String(type));
1968
- params.set("limit", String(limit));
1969
- await runtime.connection.request("GET", `/v1/agent-memory/list?${params.toString()}`);
1970
- log(`[reactive] Listed memories`);
1971
- break;
1972
- }
1973
- case "memory_stats": {
1974
- await runtime.connection.request("GET", "/v1/agent-memory/stats");
1975
- log(`[reactive] Fetched memory stats`);
1976
- break;
1977
- }
1978
- case "export_memories": {
1979
- await runtime.connection.request("POST", "/v1/agent-memory/export");
1980
- log(`[reactive] Exported memories`);
1981
- break;
1982
- }
1983
- case "import_memories": {
1984
- const pack = action.pack || action.memories;
1985
- if (!pack)
1986
- throw new Error("import_memories requires pack data");
1987
- await runtime.connection.request("POST", "/v1/agent-memory/import", pack);
1988
- log(`[reactive] Imported memories`);
1989
- break;
1990
- }
1991
- // ── Forge (Agent Deployment) ────────────────────────────────────
1992
- case "forge_deploy": {
1993
- const forgeBundleId = action.bundleId;
1994
- const forgeAgentAddr = action.agentAddress;
1995
- const forgeSoulCid = action.soulCid;
1996
- if (!forgeBundleId)
1997
- throw new Error("forge_deploy requires bundleId (number)");
1998
- if (!forgeAgentAddr)
1999
- throw new Error("forge_deploy requires agentAddress");
2000
- if (!forgeSoulCid)
2001
- throw new Error("forge_deploy requires soulCid");
2002
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/forge", {
2003
- bundleId: forgeBundleId, agentAddress: forgeAgentAddr, soulCid: forgeSoulCid, deploymentFee: action.deploymentFee,
2004
- });
2005
- log(`[reactive] Forge deployed: ${forgeBundleId} (tx: ${relay.txHash})`);
2006
- break;
2007
- }
2008
- case "forge_spawn": {
2009
- const spawnBundleId = action.bundleId;
2010
- const childAddress = action.childAddress || action.parentAddress;
2011
- const spawnSoulCid = action.soulCid;
2012
- if (!spawnBundleId)
2013
- throw new Error("forge_spawn requires bundleId (number)");
2014
- if (!childAddress)
2015
- throw new Error("forge_spawn requires childAddress");
2016
- if (!spawnSoulCid)
2017
- throw new Error("forge_spawn requires soulCid");
2018
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/forge/spawn", {
2019
- bundleId: spawnBundleId, childAddress, soulCid: spawnSoulCid, deploymentFee: action.deploymentFee,
2020
- });
2021
- log(`[reactive] Forge spawned child: ${childAddress} (tx: ${relay.txHash})`);
2022
- break;
2023
- }
2024
- case "forge_update_soul": {
2025
- const agentId = action.agentId || action.forgeId;
2026
- if (!agentId)
2027
- throw new Error("forge_update_soul requires agentId");
2028
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/forge/${agentId}/soul`, {
2029
- soulCid: action.soulCid, metadata: action.metadata,
2030
- });
2031
- log(`[reactive] Forge soul updated: ${agentId} (tx: ${relay.txHash})`);
2032
- break;
2033
- }
2034
- // ── Teaching Exchanges ──
2035
- case "propose_teaching": {
2036
- const learnerAddress = action.learnerAddress;
2037
- const goal = (action.goal ?? action.suggestedContent);
2038
- const offerings = action.offerings;
2039
- if (!learnerAddress || !goal || !offerings?.length)
2040
- throw new Error("propose_teaching requires learnerAddress, goal, offerings[]");
2041
- await runtime.connection.request("POST", "/v1/teaching/propose", { learnerAddress, goal, offerings });
2042
- break;
2043
- }
2044
- case "accept_teaching": {
2045
- const exchangeId = action.exchangeId;
2046
- if (!exchangeId)
2047
- throw new Error("accept_teaching requires exchangeId");
2048
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/accept`, {});
2049
- break;
2050
- }
2051
- case "deliver_teaching": {
2052
- const exchangeId = action.exchangeId;
2053
- if (!exchangeId)
2054
- throw new Error("deliver_teaching requires exchangeId");
2055
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/deliver`, { notes: action.notes });
2056
- break;
2057
- }
2058
- case "approve_teaching": {
2059
- const exchangeId = action.exchangeId;
2060
- if (!exchangeId)
2061
- throw new Error("approve_teaching requires exchangeId");
2062
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/approve`, { feedback: action.feedback, rating: action.rating });
2063
- break;
2064
- }
2065
- case "reject_teaching": {
2066
- const exchangeId = action.exchangeId;
2067
- if (!exchangeId)
2068
- throw new Error("reject_teaching requires exchangeId");
2069
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/reject`, { feedback: action.feedback });
2070
- break;
2071
- }
2072
- case "search_teachers": {
2073
- const goal = (action.goal ?? action.query);
2074
- if (!goal)
2075
- throw new Error("search_teachers requires goal");
2076
- await runtime.connection.request("GET", `/v1/teaching/search-teachers?goal=${encodeURIComponent(goal)}&limit=${action.limit || 10}`);
2077
- break;
2078
- }
2079
- // ── Credit Hire (off-chain marketplace) ──
2080
- case "credit_hire": {
2081
- const listingId = action.listingId;
2082
- const terms = (action.terms ?? action.suggestedContent);
2083
- const creditAmount = action.creditAmount;
2084
- if (!listingId || !terms || !creditAmount)
2085
- throw new Error("credit_hire requires listingId, terms, creditAmount");
2086
- await runtime.connection.request("POST", "/v1/marketplace/credit-hire", { listingId, terms, creditAmount });
2087
- break;
2088
- }
2089
- case "accept_credit_agreement": {
2090
- const agreementId = action.agreementId;
2091
- if (!agreementId)
2092
- throw new Error("accept_credit_agreement requires agreementId");
2093
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/accept`, {});
2094
- break;
2095
- }
2096
- case "deliver_credit_work": {
2097
- const agreementId = action.agreementId;
2098
- if (!agreementId)
2099
- throw new Error("deliver_credit_work requires agreementId");
2100
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/deliver`, { deliveryNotes: action.deliveryNotes });
2101
- break;
2102
- }
2103
- case "complete_credit_agreement": {
2104
- const agreementId = action.agreementId;
2105
- if (!agreementId)
2106
- throw new Error("complete_credit_agreement requires agreementId");
2107
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/complete`, { rating: action.rating, review: action.review });
2108
- break;
2109
- }
2110
- case "cancel_credit_agreement": {
2111
- const agreementId = action.agreementId;
2112
- if (!agreementId)
2113
- throw new Error("cancel_credit_agreement requires agreementId");
2114
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/cancel`, {});
2115
- break;
2116
- }
2117
- // GPU marketplace actions
2118
- case "gpu_search": {
2119
- const result = await runtime.gpu.searchAvailable({
2120
- gpuModel: action.gpuModel,
2121
- minVram: action.minVram,
2122
- status: action.status,
2123
- limit: action.limit || 10,
2124
- });
2125
- log(`[reactive] GPU search completed: ${JSON.stringify(result)}`);
2126
- break;
2127
- }
2128
- case "gpu_heartbeat": {
2129
- const listingId = Number(action.listingId);
2130
- if (!listingId)
2131
- throw new Error("gpu_heartbeat requires listingId");
2132
- await runtime.gpu.heartbeat({
2133
- listingId,
2134
- status: action.status || "idle",
2135
- gpuModel: action.gpuModel,
2136
- vramGb: action.vramGb,
2137
- gpuTempC: action.gpuTempC,
2138
- gpuUtilization: action.gpuUtilization,
2139
- vramUsedGb: action.vramUsedGb,
2140
- });
2141
- log(`[reactive] GPU heartbeat sent for listing ${listingId}`);
2142
- break;
2143
- }
2144
- case "gpu_challenge": {
2145
- const listingId = Number(action.listingId);
2146
- const providerAddress = (action.providerAddress || target);
2147
- const challengeInputCid = action.challengeInputCid;
2148
- if (!listingId || !providerAddress || !challengeInputCid)
2149
- throw new Error("gpu_challenge requires listingId, providerAddress, challengeInputCid");
2150
- await runtime.gpu.createChallenge({
2151
- listingId,
2152
- providerAddress,
2153
- challengeInputCid,
2154
- expectedMinTflops: action.expectedMinTflops,
2155
- });
2156
- log(`[reactive] GPU challenge created for listing ${listingId}`);
2157
- break;
2158
- }
2159
- case "gpu_submit_challenge": {
2160
- const challengeId = action.challengeId;
2161
- const challengeOutputCid = action.challengeOutputCid;
2162
- if (!challengeId || !challengeOutputCid)
2163
- throw new Error("gpu_submit_challenge requires challengeId, challengeOutputCid");
2164
- await runtime.gpu.submitChallengeResult({
2165
- challengeId,
2166
- challengeOutputCid,
2167
- actualTflops: action.actualTflops,
2168
- wallTimeMs: action.wallTimeMs,
2169
- });
2170
- log(`[reactive] GPU challenge result submitted for ${challengeId}`);
2171
- break;
2172
- }
2173
- case "gpu_submit_attestation": {
2174
- const benchmarkHash = action.benchmarkHash;
2175
- const gpuModel = action.gpuModel;
2176
- const vramGb = action.vramGb;
2177
- if (!benchmarkHash || !gpuModel || !vramGb)
2178
- throw new Error("gpu_submit_attestation requires benchmarkHash, gpuModel, vramGb");
2179
- const result = await runtime.gpu.submitAttestation({
2180
- benchmarkHash,
2181
- gpuModel,
2182
- vramGb,
2183
- computeCapability: action.computeCapability,
2184
- cudaVersion: action.cudaVersion,
2185
- });
2186
- log(`[reactive] GPU attestation submitted (tx: ${result.txHash})`);
2187
- break;
2188
- }
2189
- case "gpu_update_attestation": {
2190
- const benchmarkHash = action.benchmarkHash;
2191
- if (!benchmarkHash)
2192
- throw new Error("gpu_update_attestation requires benchmarkHash");
2193
- const result = await runtime.gpu.updateAttestation(benchmarkHash);
2194
- log(`[reactive] GPU attestation updated (tx: ${result.txHash})`);
2195
- break;
2196
- }
2197
- case "gpu_revoke_attestation": {
2198
- const result = await runtime.gpu.revokeAttestation();
2199
- log(`[reactive] GPU attestation revoked (tx: ${result.txHash})`);
2200
- break;
2201
- }
2202
- case "gpu_rent": {
2203
- const listingId = Number(action.listingId);
2204
- if (!listingId)
2205
- throw new Error("gpu_rent requires listingId");
2206
- const result = await runtime.gpu.rentGpu({
2207
- listingId,
2208
- terms: action.terms,
2209
- durationHours: action.durationHours,
2210
- tokenAmount: action.tokenAmount,
2211
- tokenAddress: action.tokenAddress,
2212
- });
2213
- log(`[reactive] GPU rented: listing ${listingId} (tx: ${result.txHash})`);
2214
- break;
2215
- }
2216
- // ── Autoresearch tools ─────────────────────────────────────
2217
- case "autoresearch_strategies": {
2218
- const strategy = action.strategy;
2219
- if (strategy) {
2220
- const s = AUTORESEARCH_STRATEGIES[strategy];
2221
- if (!s)
2222
- throw new Error(`Unknown strategy '${strategy}'. Available: ${Object.keys(AUTORESEARCH_STRATEGIES).join(", ")}`);
2223
- log(`[reactive] Autoresearch strategy: ${s.title}`);
2224
- }
2225
- else {
2226
- log(`[reactive] Listed ${Object.keys(AUTORESEARCH_STRATEGIES).length} autoresearch strategies`);
2227
- }
2228
- break;
2229
- }
2230
- case "autoresearch_parse": {
2231
- const tsv = action.tsvContent;
2232
- if (!tsv)
2233
- throw new Error("autoresearch_parse requires tsvContent");
2234
- const parsed = parseAutoresearchTsv(tsv, action.sinceCommit);
2235
- log(`[reactive] Parsed autoresearch: ${parsed.totalExperiments} experiments`);
2236
- break;
2237
- }
2238
- case "autoresearch_launch_swarm": {
2239
- const strategyName = action.strategy || "full_sweep";
2240
- const config = AUTORESEARCH_STRATEGIES[strategyName];
2241
- if (!config)
2242
- throw new Error(`Unknown strategy '${strategyName}'`);
2243
- const swarmTitle = action.customTitle || `Autoresearch: ${config.title}`;
2244
- await runtime.connection.request("POST", "/v1/swarms", {
2245
- title: swarmTitle, description: config.description,
2246
- workspaceId: action.workspaceId, subtasks: config.subtasks,
2247
- });
2248
- log(`[reactive] Launched autoresearch swarm: ${swarmTitle}`);
2249
- break;
2250
- }
2251
- case "autoresearch_report": {
2252
- const experiments = action.experiments;
2253
- if (!experiments?.length)
2254
- throw new Error("autoresearch_report requires experiments");
2255
- let memoriesStored = 0;
2256
- let postsCreated = 0;
2257
- for (const exp of experiments) {
2258
- const memContent = `Experiment [${exp.commit}]: ${exp.description}. val_bpb=${exp.valBpb}` +
2259
- (exp.improvement != null ? ` (${exp.improvement > 0 ? "+" : ""}${exp.improvement.toFixed(6)})` : "") +
2260
- ` — ${exp.isImprovement ? "improved" : "no improvement"}. ${exp.category}. ${exp.peakVramMb}MB.`;
2261
- try {
2262
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
2263
- type: "episodic", content: memContent,
2264
- importance: exp.isImprovement ? 0.8 : 0.3,
2265
- tags: ["autoresearch", exp.category, exp.isImprovement ? "improvement" : "no-improvement"],
2266
- source: "autoresearch",
2267
- });
2268
- memoriesStored++;
2269
- }
2270
- catch { }
2271
- if (exp.isImprovement) {
2272
- try {
2273
- await prepareSignRelay(runtime.connection, "/v1/prepare/post", {
2274
- title: `Autoresearch: ${exp.description}`,
2275
- body: `## Finding: ${exp.description}\n\n**val_bpb:** ${exp.valBpb}\n**Category:** ${exp.category}\n**VRAM:** ${exp.peakVramMb} MB`,
2276
- tags: ["autoresearch", exp.category, "ml-research"],
2277
- community: action.communityId || "general",
2278
- });
2279
- postsCreated++;
2280
- }
2281
- catch { }
2282
- }
2283
- }
2284
- log(`[reactive] Autoresearch report: ${memoriesStored} memories, ${postsCreated} posts`);
2285
- break;
2286
- }
2287
- case "autoresearch_submit": {
2288
- const subtaskId = action.subtaskId;
2289
- if (!subtaskId)
2290
- throw new Error("autoresearch_submit requires subtaskId");
2291
- const exps = action.experiments || [];
2292
- const improvementExps = exps.filter((e) => e.isImprovement);
2293
- const best = improvementExps.length ? improvementExps.reduce((a, b) => (a.valBpb < b.valBpb ? a : b)) : null;
2294
- await runtime.connection.request("POST", `/v1/swarms/subtasks/${encodeURIComponent(subtaskId)}/submit`, {
2295
- content: {
2296
- total_experiments: action.totalExperiments || exps.length,
2297
- improvements_found: action.improvements || improvementExps.length,
2298
- best_bpb: action.bestBpb || (best ? best.valBpb : null),
2299
- categories_explored: action.categories || {},
2300
- ...(best && { best_experiment: { commit: best.commit, val_bpb: best.valBpb, description: best.description } }),
2301
- },
2302
- resultType: "output",
2303
- });
2304
- log(`[reactive] Submitted autoresearch results to subtask ${subtaskId}`);
2305
- break;
2306
- }
2307
- case "autoresearch_bundle": {
2308
- const bundleTitle = action.title;
2309
- let bundleExps = action.experiments || [];
2310
- if (!bundleTitle || !bundleExps.length)
2311
- throw new Error("autoresearch_bundle requires title and experiments");
2312
- if (action.improvementsOnly !== false)
2313
- bundleExps = bundleExps.filter((e) => e.isImprovement);
2314
- if (!bundleExps.length) {
2315
- log("[reactive] No experiments to bundle");
2316
- break;
2317
- }
2318
- const bestBpb = Math.min(...bundleExps.map((e) => e.valBpb));
2319
- const payload = {
2320
- type: "autoresearch_bundle", version: "1.0", title: bundleTitle,
2321
- description: `Collection of ${bundleExps.length} ML experiments. Best val_bpb: ${bestBpb.toFixed(6)}.`,
2322
- tags: ["autoresearch", "ml-training", "llm", ...(action.tags || [])],
2323
- experiments: bundleExps.map((e) => ({ commit: e.commit, val_bpb: e.valBpb, description: e.description, category: e.category })),
2324
- };
2325
- const resource = await runtime.connection.request("POST", "/v1/knowledge/resources", {
2326
- source_type: "custom", title: bundleTitle, description: payload.description,
2327
- content: JSON.stringify(payload), tags: payload.tags,
2328
- });
2329
- const resourceId = resource?.resourceId || resource?.resource_id || "unknown";
2330
- try {
2331
- await prepareSignRelay(runtime.connection, "/v1/prepare/bundle", {
2332
- name: bundleTitle, description: payload.description, cids: [String(resourceId)],
2333
- tags: payload.tags, domain: "machine-learning",
2334
- });
2335
- log(`[reactive] Published autoresearch bundle: ${bundleTitle}`);
2336
- }
2337
- catch (e) {
2338
- log(`[reactive] Bundle content uploaded but on-chain failed: ${e.message}`);
2339
- }
2340
- break;
2341
- }
2342
- case "autoresearch_session_summary": {
2343
- const totalExp = action.totalExperiments;
2344
- const bestBpbVal = action.bestBpb;
2345
- if (totalExp == null || bestBpbVal == null)
2346
- throw new Error("autoresearch_session_summary requires totalExperiments and bestBpb");
2347
- const successRate = totalExp > 0 ? ((action.improvements || 0) / totalExp * 100).toFixed(1) : "0";
2348
- const summaryContent = `Autoresearch session: ${totalExp} experiments, ${action.improvements || 0} improvements (${successRate}%). Best val_bpb: ${bestBpbVal.toFixed(6)}.`;
2349
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
2350
- type: "semantic", content: summaryContent, importance: 0.9,
2351
- tags: ["autoresearch", "session-summary", "ml-research"], source: "autoresearch",
2352
- });
2353
- log(`[reactive] Stored autoresearch session summary`);
2354
- break;
2355
- }
2356
- // ── Read-only / discovery tools ────────────────────────────
2357
- case "read_feed": {
2358
- const qs = new URLSearchParams();
2359
- if (action.community)
2360
- qs.set("community", action.community);
2361
- if (action.sort)
2362
- qs.set("sort", action.sort);
2363
- if (action.limit)
2364
- qs.set("limit", String(action.limit));
2365
- await runtime.connection.request("GET", `/v1/index/feed?${qs}`);
2366
- log("[reactive] Read feed");
2367
- break;
2368
- }
2369
- case "check_balance": {
2370
- await runtime.connection.request("GET", "/v1/credits/balance");
2371
- log("[reactive] Checked balance");
2372
- break;
2373
- }
2374
- case "check_reputation": {
2375
- const addr = (action.address || target);
2376
- if (!addr)
2377
- throw new Error("check_reputation requires address");
2378
- await runtime.connection.request("GET", `/v1/contributions/${encodeURIComponent(addr)}`);
2379
- log("[reactive] Checked reputation");
2380
- break;
2381
- }
2382
- case "check_token_balance": {
2383
- await runtime.connection.request("GET", "/v1/token/balance");
2384
- log("[reactive] Checked token balance");
2385
- break;
2386
- }
2387
- case "check_token_allowance": {
2388
- const tokenAddr = action.tokenAddress;
2389
- const spenderAddr = action.spenderAddress;
2390
- if (!tokenAddr || !spenderAddr)
2391
- throw new Error("check_token_allowance requires tokenAddress and spenderAddress");
2392
- await runtime.connection.request("GET", `/v1/token/allowance?tokenAddress=${encodeURIComponent(tokenAddr)}&spenderAddress=${encodeURIComponent(spenderAddr)}`);
2393
- log("[reactive] Checked token allowance");
2394
- break;
2395
- }
2396
- case "discover": {
2397
- const dq = new URLSearchParams();
2398
- if (action.query)
2399
- dq.set("q", action.query);
2400
- if (action.types)
2401
- dq.set("types", action.types);
2402
- if (action.limit)
2403
- dq.set("limit", String(action.limit));
2404
- await runtime.connection.request("GET", `/v1/search?${dq}`);
2405
- log("[reactive] Discovered content");
2406
- break;
2407
- }
2408
- case "get_bounty": {
2409
- const bId = (action.id || action.bountyId);
2410
- if (!bId)
2411
- throw new Error("get_bounty requires id");
2412
- await runtime.connection.request("GET", `/v1/bounties/${encodeURIComponent(bId)}`);
2413
- log(`[reactive] Got bounty ${bId}`);
2414
- break;
2415
- }
2416
- case "get_comments": {
2417
- const cid = action.cid;
2418
- if (!cid)
2419
- throw new Error("get_comments requires cid");
2420
- await runtime.connection.request("GET", `/v1/index/content/${encodeURIComponent(cid)}/comments`);
2421
- log("[reactive] Got comments");
2422
- break;
2423
- }
2424
- case "get_content": {
2425
- const contentCid = action.cid;
2426
- if (!contentCid)
2427
- throw new Error("get_content requires cid");
2428
- await runtime.connection.request("GET", `/v1/index/content/${encodeURIComponent(contentCid)}`);
2429
- log("[reactive] Got content");
2430
- break;
2431
- }
2432
- case "get_credentials": {
2433
- log(`[reactive] Credentials: ${runtime.connection.address}`);
2434
- break;
2435
- }
2436
- case "get_email_inbox": {
2437
- await runtime.connection.request("GET", "/v1/email/inbox");
2438
- log("[reactive] Got email inbox");
2439
- break;
2440
- }
2441
- case "get_pending_signals": {
2442
- await runtime.connection.request("GET", "/v1/proactive/approvals");
2443
- log("[reactive] Got pending signals");
2444
- break;
2445
- }
2446
- case "get_project_commit": {
2447
- const projId = action.projectId;
2448
- const commitId = action.commitId;
2449
- if (!projId || !commitId)
2450
- throw new Error("get_project_commit requires projectId and commitId");
2451
- await runtime.connection.request("GET", `/v1/projects/${encodeURIComponent(projId)}/commits/${encodeURIComponent(commitId)}`);
2452
- log(`[reactive] Got commit ${commitId.slice(0, 8)}`);
2453
- break;
2454
- }
2455
- case "get_second_opinion": {
2456
- const question = action.question;
2457
- if (!question)
2458
- throw new Error("get_second_opinion requires question");
2459
- await runtime.connection.request("POST", "/v1/insights", { title: `Question: ${question.slice(0, 80)}`, body: question, strategyType: "question", tags: ["second-opinion"] });
2460
- log("[reactive] Posted question");
2461
- break;
2462
- }
2463
- case "get_swarm": {
2464
- const swarmId = action.swarmId;
2465
- if (!swarmId)
2466
- throw new Error("get_swarm requires swarmId");
2467
- await runtime.connection.request("GET", `/v1/swarms/${encodeURIComponent(swarmId)}`);
2468
- log(`[reactive] Got swarm ${swarmId}`);
2469
- break;
2470
- }
2471
- case "get_workspace": {
2472
- const wsId = action.workspaceId;
2473
- if (!wsId)
2474
- throw new Error("get_workspace requires workspaceId");
2475
- await runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(wsId)}`);
2476
- log(`[reactive] Got workspace ${wsId}`);
2477
- break;
2478
- }
2479
- case "leaderboard": {
2480
- const lqs = new URLSearchParams();
2481
- if (action.limit)
2482
- lqs.set("limit", String(action.limit));
2483
- await runtime.connection.request("GET", `/v1/contributions/leaderboard?${lqs}`);
2484
- log("[reactive] Got leaderboard");
2485
- break;
2486
- }
2487
- case "lookup_agent": {
2488
- const lookupAddr = (action.address || target);
2489
- if (!lookupAddr)
2490
- throw new Error("lookup_agent requires address");
2491
- await runtime.connection.request("GET", `/v1/agents/${encodeURIComponent(lookupAddr)}/profile`);
2492
- log(`[reactive] Looked up agent`);
2493
- break;
2494
- }
2495
- case "my_profile": {
2496
- await runtime.connection.request("GET", "/v1/agents/me");
2497
- log("[reactive] Got my profile");
2498
- break;
2499
- }
2500
- case "search_knowledge": {
2501
- const skqs = new URLSearchParams();
2502
- if (action.query)
2503
- skqs.set("q", action.query);
2504
- if (action.types)
2505
- skqs.set("types", action.types);
2506
- if (action.limit)
2507
- skqs.set("limit", String(action.limit));
2508
- await runtime.connection.request("GET", `/v1/search?${skqs}`);
2509
- log("[reactive] Searched knowledge");
2510
- break;
2511
- }
2512
- case "weekly_reward_info": {
2513
- await runtime.connection.request("GET", "/v1/rewards/weekly/current");
2514
- log("[reactive] Got weekly reward info");
2515
- break;
2516
- }
2517
- case "check_my_rewards": {
2518
- await runtime.connection.request("GET", "/v1/rewards/weekly/me");
2519
- log("[reactive] Checked my rewards");
2520
- break;
2521
- }
2522
- // ── List/browse tools ──────────────────────────────────────
2523
- case "list_bounties": {
2524
- const lbqs = new URLSearchParams();
2525
- if (action.community)
2526
- lbqs.set("community", action.community);
2527
- if (action.status != null)
2528
- lbqs.set("status", String(action.status));
2529
- if (action.limit)
2530
- lbqs.set("limit", String(action.limit));
2531
- await runtime.connection.request("GET", `/v1/index/bounties?${lbqs}`);
2532
- log("[reactive] Listed bounties");
2533
- break;
2534
- }
2535
- case "list_channels": {
2536
- await runtime.connection.request("GET", "/v1/channels");
2537
- log("[reactive] Listed channels");
2538
- break;
2539
- }
2540
- case "list_communities": {
2541
- await runtime.connection.request("GET", "/v1/index/communities");
2542
- log("[reactive] Listed communities");
2543
- break;
2544
- }
2545
- case "list_credit_agreements": {
2546
- const lcaqs = new URLSearchParams();
2547
- if (action.role)
2548
- lcaqs.set("role", action.role);
2549
- if (action.status)
2550
- lcaqs.set("status", action.status);
2551
- await runtime.connection.request("GET", `/v1/marketplace/credit-agreements?${lcaqs}`);
2552
- log("[reactive] Listed credit agreements");
2553
- break;
2554
- }
2555
- case "list_guilds": {
2556
- await runtime.connection.request("GET", "/v1/index/guilds");
2557
- log("[reactive] Listed guilds");
2558
- break;
2559
- }
2560
- case "list_intents": {
2561
- const liqs = new URLSearchParams();
2562
- if (action.query)
2563
- liqs.set("query", action.query);
2564
- if (action.status)
2565
- liqs.set("status", action.status);
2566
- await runtime.connection.request("GET", `/v1/intents?${liqs}`);
2567
- log("[reactive] Listed intents");
2568
- break;
2569
- }
2570
- case "list_muted": {
2571
- await runtime.connection.request("GET", "/v1/agents/muted");
2572
- log("[reactive] Listed muted agents");
2573
- break;
2574
- }
2575
- case "list_project_commits": {
2576
- const pId = action.projectId;
2577
- if (!pId)
2578
- throw new Error("list_project_commits requires projectId");
2579
- await runtime.connection.request("GET", `/v1/projects/${encodeURIComponent(pId)}/commits`);
2580
- log(`[reactive] Listed commits for ${pId}`);
2581
- break;
2582
- }
2583
- case "list_projects": {
2584
- const lpqs = new URLSearchParams();
2585
- if (action.query)
2586
- lpqs.set("q", action.query);
2587
- await runtime.connection.request("GET", `/v1/search/projects?${lpqs}`);
2588
- log("[reactive] Listed projects");
2589
- break;
2590
- }
2591
- case "list_proposals": {
2592
- const wId = action.workspaceId;
2593
- if (!wId)
2594
- throw new Error("list_proposals requires workspaceId");
2595
- await runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(wId)}/proposals`);
2596
- log("[reactive] Listed proposals");
2597
- break;
2598
- }
2599
- case "list_services": {
2600
- await runtime.connection.request("GET", "/v1/index/services");
2601
- log("[reactive] Listed services");
2602
- break;
2603
- }
2604
- case "list_swarms": {
2605
- await runtime.connection.request("GET", "/v1/swarms");
2606
- log("[reactive] Listed swarms");
2607
- break;
2608
- }
2609
- case "list_teaching_exchanges": {
2610
- const lteqs = new URLSearchParams();
2611
- if (action.role)
2612
- lteqs.set("role", action.role);
2613
- if (action.status)
2614
- lteqs.set("status", action.status);
2615
- await runtime.connection.request("GET", `/v1/teaching/exchanges?${lteqs}`);
2616
- log("[reactive] Listed teaching exchanges");
2617
- break;
2618
- }
2619
- case "list_token_launches": {
2620
- await runtime.connection.request("GET", "/v1/clawnch/launches");
2621
- log("[reactive] Listed token launches");
2622
- break;
2623
- }
2624
- case "list_workspaces": {
2625
- await runtime.connection.request("GET", "/v1/workspaces");
2626
- log("[reactive] Listed workspaces");
2627
- break;
2628
- }
2629
- case "my_agreements": {
2630
- await runtime.connection.request("GET", "/v1/marketplace/agreements");
2631
- log("[reactive] Got my agreements");
2632
- break;
2633
- }
2634
- case "my_bounties": {
2635
- await runtime.connection.request("GET", "/v1/index/bounties?mine=true");
2636
- log("[reactive] Got my bounties");
2637
- break;
2638
- }
2639
- case "my_skills": {
2640
- await runtime.connection.request("GET", `/v1/skills/registry?publisherId=${encodeURIComponent(runtime.connection.address || "")}`);
2641
- log("[reactive] Got my skills");
2642
- break;
2643
- }
2644
- case "my_tasks": {
2645
- await runtime.connection.request("GET", "/v1/swarms/subtasks?mine=true");
2646
- log("[reactive] Got my tasks");
2647
- break;
2648
- }
2649
- case "available_subtasks": {
2650
- const asqs = new URLSearchParams();
2651
- if (action.skills)
2652
- asqs.set("skills", action.skills);
2653
- if (action.swarmId)
2654
- asqs.set("swarmId", action.swarmId);
2655
- await runtime.connection.request("GET", `/v1/swarms/subtasks?${asqs}`);
2656
- log("[reactive] Listed available subtasks");
2657
- break;
2658
- }
2659
- // ── Agent-first tools ──────────────────────────────────────
2660
- case "delegate_task": {
2661
- const dtTitle = action.title;
2662
- const dtDesc = action.description;
2663
- if (!dtTitle || !dtDesc)
2664
- throw new Error("delegate_task requires title and description");
2665
- await runtime.connection.request("POST", "/v1/swarms", {
2666
- title: dtTitle, description: dtDesc,
2667
- subtasks: [{ title: dtTitle, description: dtDesc, skillTags: action.skills || [] }],
2668
- });
2669
- log(`[reactive] Delegated task: ${dtTitle}`);
2670
- break;
2671
- }
2672
- case "save_learning": {
2673
- const slTitle = action.title;
2674
- const slBody = action.body;
2675
- if (!slTitle || !slBody)
2676
- throw new Error("save_learning requires title and body");
2677
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
2678
- type: "semantic", content: `${slTitle}: ${slBody}`, importance: 0.7,
2679
- tags: ["learning", ...(action.tags || [])], source: "self",
2680
- });
2681
- log(`[reactive] Saved learning: ${slTitle}`);
2682
- break;
2683
- }
2684
- case "recall": {
2685
- const recallQuery = action.query;
2686
- if (!recallQuery)
2687
- throw new Error("recall requires query");
2688
- await runtime.connection.request("POST", "/v1/agent-memory/recall", { query: recallQuery, limit: action.limit || 10 });
2689
- log("[reactive] Recalled memories");
2690
- break;
2691
- }
2692
- case "save_checkpoint": {
2693
- const cpTask = action.task;
2694
- if (!cpTask)
2695
- throw new Error("save_checkpoint requires task");
2696
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
2697
- type: "episodic", content: `CHECKPOINT: ${cpTask} | Progress: ${action.progress || 0}%`,
2698
- importance: 0.85, tags: ["checkpoint", "work-state"], source: "self",
2699
- });
2700
- log(`[reactive] Saved checkpoint: ${cpTask}`);
2701
- break;
2702
- }
2703
- case "resume_checkpoint": {
2704
- await runtime.connection.request("POST", "/v1/agent-memory/recall", { query: "CHECKPOINT work state progress", type: "episodic", limit: 1 });
2705
- log("[reactive] Resumed checkpoint");
2706
- break;
2707
- }
2708
- case "request_review": {
2709
- const rvTitle = action.title;
2710
- const rvContent = action.content;
2711
- if (!rvTitle || !rvContent)
2712
- throw new Error("request_review requires title and content");
2713
- await runtime.connection.request("POST", "/v1/insights", { title: rvTitle, body: rvContent, strategyType: action.reviewType || "code", tags: ["review-request"] });
2714
- log(`[reactive] Requested review: ${rvTitle}`);
2715
- break;
2716
- }
2717
- case "ask_network": {
2718
- const askQ = action.question;
2719
- if (!askQ)
2720
- throw new Error("ask_network requires question");
2721
- await runtime.connection.request("POST", "/v1/insights", { title: `Question: ${askQ.slice(0, 80)}`, body: askQ, strategyType: "question", tags: ["network-question"] });
2722
- log("[reactive] Asked network");
2723
- break;
2724
- }
2725
- case "check_delegation": {
2726
- const delBountyId = action.bountyId;
2727
- if (!delBountyId)
2728
- throw new Error("check_delegation requires bountyId");
2729
- await runtime.connection.request("GET", `/v1/swarms/${encodeURIComponent(delBountyId)}`);
2730
- log(`[reactive] Checked delegation ${delBountyId}`);
2731
- break;
2732
- }
2733
- // ── On-chain tools ─────────────────────────────────────────
2734
- case "comment_on_content": {
2735
- const parentCid = action.parentCid;
2736
- const commentBody = action.body;
2737
- if (!parentCid || !commentBody)
2738
- throw new Error("comment_on_content requires parentCid and body");
2739
- await prepareSignRelay(runtime.connection, "/v1/prepare/comment", { parentCid, body: commentBody, community: action.community });
2740
- log(`[reactive] Commented on ${parentCid.slice(0, 10)}...`);
2741
- break;
2742
- }
2743
- case "unfollow_agent": {
2744
- const unfAddr = (action.targetAddress || target);
2745
- if (!unfAddr)
2746
- throw new Error("unfollow_agent requires targetAddress");
2747
- await prepareSignRelay(runtime.connection, "/v1/prepare/unfollow", { address: unfAddr });
2748
- log(`[reactive] Unfollowed ${unfAddr.slice(0, 10)}...`);
2749
- break;
2750
- }
2751
- case "approve_bounty_applicant": {
2752
- const abId = (action.bountyId || action.id);
2753
- const applicantAddr = action.applicantAddress;
2754
- if (!abId || !applicantAddr)
2755
- throw new Error("approve_bounty_applicant requires bountyId and applicantAddress");
2756
- await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${encodeURIComponent(abId)}/approve-claimer`, { applicantAddress: applicantAddr });
2757
- log(`[reactive] Approved bounty applicant`);
2758
- break;
2759
- }
2760
- case "create_service_listing": {
2761
- const slsTitle = action.title;
2762
- const slsDesc = action.description;
2763
- const slsCat = action.category;
2764
- if (!slsTitle || !slsDesc || !slsCat)
2765
- throw new Error("create_service_listing requires title, description, category");
2766
- await prepareSignRelay(runtime.connection, "/v1/prepare/service/list", { title: slsTitle, description: slsDesc, category: slsCat, tags: action.tags });
2767
- log(`[reactive] Created service listing: ${slsTitle}`);
2768
- break;
2769
- }
2770
- case "hire_agent": {
2771
- const listingId2 = action.listingId;
2772
- const requirements = action.requirements;
2773
- if (!listingId2 || !requirements)
2774
- throw new Error("hire_agent requires listingId and requirements");
2775
- await prepareSignRelay(runtime.connection, "/v1/prepare/service/agree", { listingId: listingId2, requirements, budget: action.budget });
2776
- log(`[reactive] Hired agent via listing ${listingId2}`);
2777
- break;
2778
- }
2779
- case "accept_service": {
2780
- const agreeId = action.agreementId;
2781
- if (!agreeId)
2782
- throw new Error("accept_service requires agreementId");
2783
- await runtime.connection.request("POST", `/v1/marketplace/agreements/${encodeURIComponent(agreeId)}/accept`, {});
2784
- log(`[reactive] Accepted service agreement`);
2785
- break;
2786
- }
2787
- case "dispute_service": {
2788
- const dAgreeId = action.agreementId;
2789
- if (!dAgreeId)
2790
- throw new Error("dispute_service requires agreementId");
2791
- await prepareSignRelay(runtime.connection, "/v1/prepare/service/dispute", { agreementId: dAgreeId, reason: action.reason });
2792
- log(`[reactive] Disputed service ${dAgreeId}`);
2793
- break;
2794
- }
2795
- case "cancel_service": {
2796
- const cAgreeId = action.agreementId;
2797
- if (!cAgreeId)
2798
- throw new Error("cancel_service requires agreementId");
2799
- await prepareSignRelay(runtime.connection, "/v1/prepare/service/cancel", { agreementId: cAgreeId });
2800
- log(`[reactive] Cancelled service ${cAgreeId}`);
2801
- break;
2802
- }
2803
- case "approve_token": {
2804
- const tokenAddr2 = action.tokenAddress;
2805
- const spenderAddr2 = action.spenderAddress;
2806
- if (!tokenAddr2 || !spenderAddr2)
2807
- throw new Error("approve_token requires tokenAddress and spenderAddress");
2808
- await runtime.connection.request("POST", "/v1/token/approve", { tokenAddress: tokenAddr2, spenderAddress: spenderAddr2, amount: action.amount || "max" });
2809
- log("[reactive] Approved token spending");
2810
- break;
2811
- }
2812
- // ── Proactive/signal tools ─────────────────────────────────
2813
- case "poll_signals": {
2814
- await runtime.connection.request("GET", "/v1/proactive/pending-signals");
2815
- log("[reactive] Polled signals");
2816
- break;
2817
- }
2818
- case "ack_signal": {
2819
- const sigId = action.signalId;
2820
- if (!sigId)
2821
- throw new Error("ack_signal requires signalId");
2822
- await runtime.connection.request("POST", `/v1/proactive/signals/${encodeURIComponent(sigId)}/ack`, {});
2823
- log(`[reactive] Acknowledged signal ${sigId}`);
2824
- break;
2825
- }
2826
- case "approve_action": {
2827
- const actId = action.actionId;
2828
- if (!actId)
2829
- throw new Error("approve_action requires actionId");
2830
- await runtime.connection.request("POST", `/v1/proactive/approvals/${encodeURIComponent(actId)}/approve`, {});
2831
- log(`[reactive] Approved action ${actId}`);
2832
- break;
2833
- }
2834
- case "reject_action": {
2835
- const rejActId = action.actionId;
2836
- if (!rejActId)
2837
- throw new Error("reject_action requires actionId");
2838
- await runtime.connection.request("POST", `/v1/proactive/approvals/${encodeURIComponent(rejActId)}/reject`, { reason: action.reason });
2839
- log(`[reactive] Rejected action ${rejActId}`);
2840
- break;
2841
- }
2842
- case "configure_proactive": {
2843
- await runtime.connection.request("PUT", "/v1/proactive/settings", {
2844
- enabled: action.enabled, scanIntervalMinutes: action.scanIntervalMinutes,
2845
- maxActionsPerDay: action.maxActionsPerDay, callbackFormat: action.callbackFormat,
2846
- });
2847
- log("[reactive] Configured proactive settings");
2848
- break;
2849
- }
2850
- // ── Content/write tools ────────────────────────────────────
2851
- case "send_channel_message": {
2852
- const chId = action.channelId;
2853
- const chContent = action.content;
2854
- if (!chId || !chContent)
2855
- throw new Error("send_channel_message requires channelId and content");
2856
- try {
2857
- await runtime.connection.request("POST", `/v1/channels/${encodeURIComponent(chId)}/join`, {});
2858
- }
2859
- catch { }
2860
- await runtime.connection.request("POST", `/v1/channels/${encodeURIComponent(chId)}/messages`, { content: chContent });
2861
- log("[reactive] Sent channel message");
2862
- break;
2863
- }
2864
- case "read_channel_messages": {
2865
- const rcmChId = action.channelId;
2866
- if (!rcmChId)
2867
- throw new Error("read_channel_messages requires channelId");
2868
- const rcmqs = new URLSearchParams();
2869
- if (action.limit)
2870
- rcmqs.set("limit", String(action.limit));
2871
- if (action.before)
2872
- rcmqs.set("before", action.before);
2873
- await runtime.connection.request("GET", `/v1/channels/${encodeURIComponent(rcmChId)}/messages?${rcmqs}`);
2874
- log("[reactive] Read channel messages");
2875
- break;
2876
- }
2877
- case "mute_agent": {
2878
- const muteAddr = (action.address || target);
2879
- if (!muteAddr)
2880
- throw new Error("mute_agent requires address");
2881
- await runtime.connection.request("POST", `/v1/agents/${encodeURIComponent(muteAddr)}/mute`, {});
2882
- log(`[reactive] Muted ${muteAddr.slice(0, 10)}...`);
2883
- break;
2884
- }
2885
- case "unmute_agent": {
2886
- const unmuteAddr = (action.address || target);
2887
- if (!unmuteAddr)
2888
- throw new Error("unmute_agent requires address");
2889
- await runtime.connection.request("DELETE", `/v1/agents/${encodeURIComponent(unmuteAddr)}/mute`);
2890
- log(`[reactive] Unmuted ${unmuteAddr.slice(0, 10)}...`);
2891
- break;
2892
- }
2893
- case "report_spam":
2894
- case "report_content": {
2895
- const reportCid = (action.contentCid || action.cid);
2896
- const reportReason = action.reason;
2897
- if (!reportCid || !reportReason)
2898
- throw new Error("report_content requires cid and reason");
2899
- await runtime.connection.request("POST", `/v1/content/${encodeURIComponent(reportCid)}/report`, { reason: reportReason, details: action.details });
2900
- log("[reactive] Reported content");
2901
- break;
2902
- }
2903
- case "import_project_url": {
2904
- const impProjId = action.projectId;
2905
- const impUrl = action.url;
2906
- if (!impProjId || !impUrl)
2907
- throw new Error("import_project_url requires projectId and url");
2908
- await runtime.connection.request("POST", `/v1/projects/${encodeURIComponent(impProjId)}/import-url`, { url: impUrl, branch: action.branch, subdir: action.subdir });
2909
- log(`[reactive] Imported from ${impUrl}`);
2910
- break;
2911
- }
2912
- case "match_submission_spec": {
2913
- const msBountyId = action.bountyId;
2914
- const msSubId = action.subId;
2915
- if (!msBountyId || !msSubId)
2916
- throw new Error("match_submission_spec requires bountyId and subId");
2917
- await runtime.connection.request("POST", `/v1/bounties/${encodeURIComponent(msBountyId)}/submissions/${encodeURIComponent(msSubId)}/match-spec`, {});
2918
- log("[reactive] Matched submission spec");
2919
- break;
2920
- }
2921
- case "verify_submission": {
2922
- const vsBountyId = action.bountyId;
2923
- const vsSubId = action.subId;
2924
- if (!vsBountyId || !vsSubId)
2925
- throw new Error("verify_submission requires bountyId and subId");
2926
- await runtime.connection.request("POST", `/v1/bounties/${encodeURIComponent(vsBountyId)}/submissions/${encodeURIComponent(vsSubId)}/verify`, { testCommand: action.testCommand });
2927
- log("[reactive] Verified submission");
2928
- break;
2929
- }
2930
- case "review_submission": {
2931
- const rsBountyId = action.bountyId;
2932
- const rsSubId = action.subId;
2933
- if (!rsBountyId || !rsSubId)
2934
- throw new Error("review_submission requires bountyId and subId");
2935
- await runtime.connection.request("POST", `/v1/bounties/${encodeURIComponent(rsBountyId)}/submissions/${encodeURIComponent(rsSubId)}/review`, {});
2936
- log("[reactive] Reviewed submission");
2937
- break;
2938
- }
2939
- case "review_merge_request": {
2940
- const rmrProjId = action.projectId;
2941
- const rmrMrId = action.mrId;
2942
- if (!rmrProjId || !rmrMrId)
2943
- throw new Error("review_merge_request requires projectId and mrId");
2944
- const mr = await runtime.connection.request("GET", `/v1/projects/${encodeURIComponent(rmrProjId)}/merge-requests/${encodeURIComponent(rmrMrId)}`);
2945
- const commits = (mr?.commits || []);
2946
- for (const c of commits) {
2947
- try {
2948
- await runtime.connection.request("POST", `/v1/projects/${encodeURIComponent(rmrProjId)}/commits/${encodeURIComponent(c.id || c.commitId)}/ai-review`, {});
2949
- }
2950
- catch { }
2951
- }
2952
- log(`[reactive] Reviewed MR ${rmrMrId}`);
2953
- break;
2954
- }
2955
- case "update_profile": {
2956
- await runtime.connection.request("PATCH", "/v1/agents/me", { displayName: action.displayName, description: action.description, capabilities: action.capabilities });
2957
- log("[reactive] Updated profile");
2958
- break;
2959
- }
2960
- case "register": {
2961
- await runtime.connection.request("POST", "/v1/agents/register", { name: action.name, description: action.description });
2962
- log("[reactive] Registered agent");
2963
- break;
2964
- }
2965
- // ── Workspace tools ────────────────────────────────────────
2966
- case "create_workspace": {
2967
- const cwName = action.name;
2968
- if (!cwName)
2969
- throw new Error("create_workspace requires name");
2970
- await runtime.connection.request("POST", "/v1/workspaces", { name: cwName, description: action.description });
2971
- log(`[reactive] Created workspace: ${cwName}`);
2972
- break;
2973
- }
2974
- case "workspace_add_member": {
2975
- const wamWsId = action.workspaceId;
2976
- const wamAgentId = action.agentId;
2977
- if (!wamWsId || !wamAgentId)
2978
- throw new Error("workspace_add_member requires workspaceId and agentId");
2979
- await runtime.connection.request("POST", `/v1/workspaces/${encodeURIComponent(wamWsId)}/members`, { agentId: wamAgentId, role: action.role || "editor" });
2980
- log("[reactive] Added member to workspace");
2981
- break;
2982
- }
2983
- case "workspace_set_entry": {
2984
- const wseWsId = action.workspaceId;
2985
- const wseKey = action.key;
2986
- if (!wseWsId || !wseKey)
2987
- throw new Error("workspace_set_entry requires workspaceId and key");
2988
- await runtime.connection.request("PUT", `/v1/workspaces/${encodeURIComponent(wseWsId)}/state`, { key: wseKey, value: action.value });
2989
- log(`[reactive] Set workspace entry: ${wseKey}`);
2990
- break;
2991
- }
2992
- case "workspace_get_entries": {
2993
- const wgeWsId = action.workspaceId;
2994
- if (!wgeWsId)
2995
- throw new Error("workspace_get_entries requires workspaceId");
2996
- await runtime.connection.request("GET", `/v1/workspaces/${encodeURIComponent(wgeWsId)}/state`);
2997
- log("[reactive] Got workspace entries");
2998
- break;
2999
- }
3000
- case "create_proposal": {
3001
- const cpWsId = action.workspaceId;
3002
- const cpTitle = action.title;
3003
- if (!cpWsId || !cpTitle)
3004
- throw new Error("create_proposal requires workspaceId and title");
3005
- await runtime.connection.request("POST", `/v1/workspaces/${encodeURIComponent(cpWsId)}/proposals`, { title: cpTitle, description: action.description, actionType: action.actionType, actionPayload: action.actionPayload });
3006
- log(`[reactive] Created proposal: ${cpTitle}`);
3007
- break;
3008
- }
3009
- // ── Skill/teaching/token tools ─────────────────────────────
3010
- case "rate_skill":
3011
- case "review_skill": {
3012
- const rsSkillId = action.skillId;
3013
- const rsRating = action.rating;
3014
- if (!rsSkillId || !rsRating)
3015
- throw new Error("rate_skill requires skillId and rating");
3016
- await runtime.connection.request("POST", `/v1/skills/registry/${encodeURIComponent(rsSkillId)}/review`, { rating: rsRating, review: action.review });
3017
- log(`[reactive] Rated skill ${rsSkillId}`);
3018
- break;
3019
- }
3020
- case "teaching_stats": {
3021
- await runtime.connection.request("GET", "/v1/teaching/stats");
3022
- log("[reactive] Got teaching stats");
3023
- break;
3024
- }
3025
- case "search_teachers": {
3026
- const stGoal = action.goal;
3027
- if (!stGoal)
3028
- throw new Error("search_teachers requires goal");
3029
- await runtime.connection.request("GET", `/v1/teaching/search-teachers?goal=${encodeURIComponent(stGoal)}`);
3030
- log("[reactive] Searched teachers");
3031
- break;
3032
- }
3033
- case "report_token_launch": {
3034
- const tokenName = action.tokenName;
3035
- const tokenTicker = action.tokenTicker;
3036
- const tokenAddress2 = action.tokenAddress;
3037
- if (!tokenName || !tokenTicker || !tokenAddress2)
3038
- throw new Error("report_token_launch requires tokenName, tokenTicker, tokenAddress");
3039
- await runtime.connection.request("POST", "/v1/clawnch/report-launch", { tokenName, tokenTicker, tokenAddress: tokenAddress2, description: action.description });
3040
- log(`[reactive] Reported token launch: ${tokenName}`);
3041
- break;
3042
- }
3043
- case "subscribe":
3044
- case "create_search_subscription": {
3045
- const subLabel = action.label;
3046
- const subQuery = action.query;
3047
- if (!subLabel || !subQuery)
3048
- throw new Error("subscribe requires label and query");
3049
- await runtime.connection.request("POST", "/v1/search/subscriptions", { label: subLabel, query: subQuery, types: action.types, frequencyMinutes: action.frequencyMinutes });
3050
- log(`[reactive] Created subscription: ${subLabel}`);
3051
- break;
3052
- }
3053
- case "ignore":
3054
- break;
3055
- default: {
3056
- // ── Unhandled action ─────────────────────────────────────
3057
- // All known tools should have explicit cases above.
3058
- // Log unhandled for debugging — DO NOT route through MCP.
3059
- log(`[reactive] Unhandled action: ${action.action}`);
3060
- break;
3061
- }
387
+ // ── Unified dispatch via POST /v1/actions/execute ──
388
+ // The gateway's ToolDispatcher routes the call to the correct internal
389
+ // endpoint, replacing the 2000+ line switch statement that was here before.
390
+ if (action.action === "ignore") {
391
+ return;
392
+ }
393
+ // Intercept browse_tools (client-side, no gateway call needed)
394
+ if (action.action === "browse_tools") {
395
+ const category = action.category;
396
+ if (!category) {
397
+ const listing = getCategoryListing();
398
+ log(`[browse_tools] Categories: ${listing.map((c) => `${c.name} (${c.count})`).join(", ")}`);
399
+ return;
400
+ }
401
+ loadedCategories.add(category);
402
+ const tools = getToolsInCategory(category);
403
+ log(`[browse_tools] Loaded ${tools.length} tools from "${category}"`);
404
+ return;
405
+ }
406
+ const toolName = `nookplot_${action.action}`;
407
+ const payload = { ...action };
408
+ delete payload.action; // toolName carries the action type
409
+ // Merge signal context (senderAddress, channelId) into payload
410
+ if (target && !payload.to)
411
+ payload.to = target;
412
+ if (content && !payload.content)
413
+ payload.content = content;
414
+ if (channelId && !payload.channelId)
415
+ payload.channelId = channelId;
416
+ const dispatchResult = await runtime.connection.request("POST", "/v1/actions/execute", { toolName, payload });
417
+ switch (dispatchResult.status) {
418
+ case "completed":
419
+ break;
420
+ case "sign_required": {
421
+ // On-chain action: sign locally + relay
422
+ if (!dispatchResult.forwardRequest || !dispatchResult.domain || !dispatchResult.types) {
423
+ throw new Error(`sign_required response missing forwardRequest/domain/types for ${action.action}`);
424
+ }
425
+ const { signForwardRequest } = await import("@nookplot/runtime");
426
+ const privateKey = runtime.connection.privateKey;
427
+ if (!privateKey)
428
+ throw new Error("Private key not configured — cannot sign on-chain transactions.");
429
+ const signature = await signForwardRequest(privateKey, dispatchResult.domain, dispatchResult.types, dispatchResult.forwardRequest);
430
+ const relayResult = await runtime.connection.request("POST", "/v1/relay", { ...dispatchResult.forwardRequest, signature });
431
+ log(`[reactive] On-chain tx: ${relayResult.txHash}`);
432
+ break;
433
+ }
434
+ case "client_side_required":
435
+ log(`[reactive] Client-side action required: ${dispatchResult.action}`);
436
+ break;
437
+ case "error":
438
+ throw new Error(dispatchResult.error ?? `Action failed: ${action.action}`);
439
+ default:
440
+ log(`[reactive] Unexpected dispatch status: ${dispatchResult.status}`);
3062
441
  }
3063
442
  if (action.action !== "ignore") {
3064
443
  log(`[reactive] ${action.action}${target ? ` -> ${target.slice(0, 10)}...` : ""}`);