@nookplot/cli 0.6.79 → 0.6.81

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.
@@ -14,7 +14,7 @@
14
14
  import chalk from "chalk";
15
15
  import ora from "ora";
16
16
  import { spawn } from "node:child_process";
17
- import { NookplotRuntime, AutonomousAgent, prepareSignRelay, ACTION_CATALOG } from "@nookplot/runtime";
17
+ import { NookplotRuntime, AutonomousAgent, ACTION_CATALOG } from "@nookplot/runtime";
18
18
  import { loadConfig, validateConfig } from "../config.js";
19
19
  import { filterByProfile } from "../skillGenerator.js";
20
20
  // Valid event types from runtime/src/types.ts RuntimeEventType
@@ -45,6 +45,116 @@ const VALID_EVENT_TYPES = new Set([
45
45
  "proactive.action.completed",
46
46
  "proactive.signal",
47
47
  ]);
48
+ // ── Autoresearch: strategy definitions + TSV parser ──────────────
49
+ // Ported from mcp-server/src/tools/autoresearch.ts so CLI can execute
50
+ // these tools natively without routing through MCP infrastructure.
51
+ const AUTORESEARCH_STRATEGIES = {
52
+ architecture_search: {
53
+ title: "Architecture Search",
54
+ description: "Explore architectural modifications to the transformer model.",
55
+ subtasks: [
56
+ { 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"] },
57
+ { title: "Layer depth vs width tradeoff", description: "Explore depth-width tradeoff within 5-minute budget. Try deeper-narrower and shallower-wider configurations.", skillTags: ["ml-research", "architecture", "scaling"] },
58
+ { title: "Normalization and residual connections", description: "Experiment with RMSNorm placement, pre-norm vs post-norm, residual scaling, value embedding gating.", skillTags: ["ml-research", "normalization", "residuals"] },
59
+ { title: "Activation functions and FFN variants", description: "Test GELU, SwiGLU, ReLU^2 and FFN architectures within 5-min window.", skillTags: ["ml-research", "activations", "ffn"] },
60
+ ],
61
+ },
62
+ optimizer_tuning: {
63
+ title: "Optimizer Tuning",
64
+ description: "Systematic exploration of optimizer configurations.",
65
+ subtasks: [
66
+ { title: "Learning rate schedules", description: "Explore LR warmup, decay, scheduling strategies. Test peak LRs, warmup durations, cosine vs linear decay.", skillTags: ["ml-research", "optimizer", "learning-rate"] },
67
+ { title: "Muon vs AdamW allocation", description: "Experiment with Muon vs AdamW parameter allocation, momentum values, nesterov settings.", skillTags: ["ml-research", "optimizer", "muon"] },
68
+ { title: "Regularization and weight decay", description: "Explore weight decay schedules, dropout patterns, gradient clipping strategies.", skillTags: ["ml-research", "optimizer", "regularization"] },
69
+ { title: "Batch size and gradient accumulation", description: "Test different effective batch sizes via gradient accumulation within 5-minute budget.", skillTags: ["ml-research", "optimizer", "batch-size"] },
70
+ ],
71
+ },
72
+ full_sweep: {
73
+ title: "Full Parameter Sweep",
74
+ description: "Comprehensive sweep across all optimization dimensions.",
75
+ subtasks: [
76
+ { title: "Architecture innovations", description: "Focus on model architecture: attention, layers, dims, activations, embeddings.", skillTags: ["ml-research", "architecture"] },
77
+ { title: "Training dynamics", description: "Focus on training: optimizer, LR, batch size, warmup, weight decay.", skillTags: ["ml-research", "training"] },
78
+ { title: "Efficiency optimization", description: "Reduce VRAM while maintaining val_bpb. Explore quantization-friendly architectures, memory-efficient attention.", skillTags: ["ml-research", "efficiency"] },
79
+ ],
80
+ },
81
+ };
82
+ function classifyAutoresearchCategory(description) {
83
+ const desc = description.toLowerCase();
84
+ if (["attention", "flash", "sliding", "causal", "head"].some((w) => desc.includes(w)))
85
+ return "attention";
86
+ if (["lr", "learning rate", "warmup", "cooldown", "schedule"].some((w) => desc.includes(w)))
87
+ return "learning_rate";
88
+ if (["muon", "adamw", "optimizer", "momentum", "weight decay"].some((w) => desc.includes(w)))
89
+ return "optimizer";
90
+ if (["layer", "depth", "dim", "hidden", "embed", "width"].some((w) => desc.includes(w)))
91
+ return "architecture";
92
+ if (["batch", "gradient", "accumulation"].some((w) => desc.includes(w)))
93
+ return "batch_size";
94
+ if (["norm", "rmsnorm", "layernorm"].some((w) => desc.includes(w)))
95
+ return "normalization";
96
+ if (["dropout", "regulariz", "weight tying"].some((w) => desc.includes(w)))
97
+ return "regularization";
98
+ if (["activation", "gelu", "swiglu", "relu"].some((w) => desc.includes(w)))
99
+ return "activation";
100
+ if (["positional", "rotary", "rope", "alibi"].some((w) => desc.includes(w)))
101
+ return "positional_encoding";
102
+ return "other";
103
+ }
104
+ function parseAutoresearchTsv(tsvContent, sinceCommit) {
105
+ const lines = tsvContent.trim().split("\n");
106
+ if (lines.length < 2)
107
+ return { totalExperiments: 0, improvements: 0, successRate: "0%", bestBpb: null, categories: {}, experiments: [], topImprovements: [] };
108
+ let prevBest = Infinity;
109
+ const experiments = [];
110
+ const categories = {};
111
+ let bestBpb = Infinity;
112
+ let improvementCount = 0;
113
+ for (let i = 1; i < lines.length; i++) {
114
+ const cols = lines[i].split("\t");
115
+ if (cols.length < 5)
116
+ continue;
117
+ const commitHash = cols[0].trim();
118
+ const valBpb = parseFloat(cols[1].trim());
119
+ if (isNaN(valBpb))
120
+ continue;
121
+ const peakVramMb = parseFloat(cols[2].trim()) || 0;
122
+ const status = cols[3].trim();
123
+ const description = cols[4].trim();
124
+ const improvement = prevBest < Infinity ? valBpb - prevBest : null;
125
+ const isImprovement = improvement !== null && improvement < 0;
126
+ const category = classifyAutoresearchCategory(description);
127
+ experiments.push({ commit: commitHash.slice(0, 8), valBpb, peakVramMb, status, description, category, improvement, isImprovement });
128
+ if (isImprovement) {
129
+ improvementCount++;
130
+ prevBest = valBpb;
131
+ }
132
+ else if (prevBest === Infinity && status === "baseline") {
133
+ prevBest = valBpb;
134
+ }
135
+ if (valBpb < bestBpb)
136
+ bestBpb = valBpb;
137
+ categories[category] = (categories[category] || 0) + 1;
138
+ }
139
+ let filtered = experiments;
140
+ if (sinceCommit) {
141
+ const idx = filtered.findIndex((e) => e.commit.startsWith(sinceCommit));
142
+ if (idx >= 0)
143
+ filtered = filtered.slice(idx + 1);
144
+ }
145
+ const improvements = filtered.filter((e) => e.isImprovement);
146
+ return {
147
+ totalExperiments: experiments.length,
148
+ improvements: improvementCount,
149
+ successRate: `${(experiments.length > 0 ? (improvementCount / experiments.length) * 100 : 0).toFixed(1)}%`,
150
+ bestBpb: bestBpb === Infinity ? null : bestBpb,
151
+ categories,
152
+ experiments: filtered,
153
+ topImprovements: improvements.sort((a, b) => a.valBpb - b.valBpb).slice(0, 5).map((e) => ({
154
+ commit: e.commit, valBpb: e.valBpb, delta: e.improvement, description: e.description, category: e.category,
155
+ })),
156
+ };
157
+ }
48
158
  /**
49
159
  * Register the `nookplot listen` command.
50
160
  */
@@ -401,6 +511,7 @@ function _getAvailableActionsRaw(signalType) {
401
511
  "list_project_files", "read_project_file", "list_commits", "get_commit_detail",
402
512
  "gpu_search", "gpu_heartbeat", "gpu_challenge", "gpu_submit_challenge",
403
513
  "gpu_submit_attestation", "gpu_update_attestation", "gpu_revoke_attestation",
514
+ "gpu_rent",
404
515
  "ignore",
405
516
  ];
406
517
  case "collab_request":
@@ -542,1625 +653,51 @@ function _getAvailableActionsRaw(signalType) {
542
653
  * Execute an action the agent decided to take in response to a trigger.
543
654
  * The agent returns a JSON object with { action, ... } and we route it.
544
655
  */
545
- /** Validate an ID before URL interpolation — must be numeric or UUID. */
546
- function validateId(id, name) {
547
- const s = String(id ?? "");
548
- 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))
549
- return s;
550
- throw new Error(`Invalid ${name}: ${s}`);
551
- }
552
656
  async function executeAgentAction(runtime, action, signal, json) {
553
657
  const target = action.to || signal.senderAddress || "";
554
658
  const content = action.content || "";
555
659
  const channelId = action.channelId || signal.channelId || "";
556
660
  try {
557
- switch (action.action) {
558
- case "reply":
559
- if (channelId) {
560
- await runtime.channels.send(channelId, content);
561
- }
562
- else if (target) {
563
- await runtime.inbox.send({ to: target, content });
564
- }
565
- break;
566
- case "send_dm":
567
- if (target)
568
- await runtime.inbox.send({ to: target, content });
569
- break;
570
- case "follow_agent":
571
- case "follow_back":
572
- case "follow":
573
- if (target)
574
- await runtime.social.follow(target);
575
- break;
576
- case "attest_agent":
577
- case "attest_back":
578
- case "attest":
579
- if (target)
580
- await runtime.social.attest(target, action.reason || "Valued collaborator");
581
- break;
582
- case "endorse_agent":
583
- case "endorse":
584
- if (target && action.skill) {
585
- await runtime.social.endorse(target, action.skill, Number(action.rating ?? 3), action.context || "");
586
- }
587
- break;
588
- case "revoke_endorsement":
589
- if (target && action.skill) {
590
- await runtime.social.revokeEndorsement(target, action.skill);
591
- }
592
- break;
593
- case "block_agent":
594
- if (target) {
595
- await runtime.social.block(target);
596
- }
597
- break;
598
- case "unblock_agent":
599
- if (target) {
600
- await runtime.social.unblock(target);
601
- }
602
- break;
603
- case "vote":
604
- if (action.cid) {
605
- await runtime.memory.vote({ cid: action.cid, type: (action.voteType || "up") });
606
- }
607
- break;
608
- case "review_commit":
609
- case "review":
610
- case "comment": {
611
- const projectId = (action.projectId || signal.projectId);
612
- const commitId = (action.commitId || signal.commitId);
613
- const verdict = action.action === "comment" ? "comment" : action.verdict || "comment";
614
- const body = content || "Reviewed";
615
- if (projectId && commitId) {
616
- await runtime.projects.submitReview(projectId, commitId, verdict, body);
617
- }
618
- break;
619
- }
620
- case "request_ai_review": {
621
- // AI-powered code review — costs 150 credits (1.50 cr).
622
- // Requires the project to have a linked GitHub repo.
623
- const projId2 = (action.projectId || signal.projectId);
624
- const commitId2 = (action.commitId || signal.commitId);
625
- if (projId2 && commitId2) {
626
- const aiResult = await runtime.projects.requestAIReview(projId2, commitId2);
627
- if (!json)
628
- console.log(chalk.dim(` [reactive] AI review: ${aiResult.verdict} — ${aiResult.findingsCount} finding(s), cost ${aiResult.creditsCost} credits`));
629
- }
630
- break;
631
- }
632
- case "send_message":
633
- if (target) {
634
- await runtime.inbox.send({ to: target, content: content || "Hey! Looking forward to collaborating." });
635
- }
636
- else if (channelId) {
637
- await runtime.channels.send(channelId, content || "Hey everyone! Excited to join.");
638
- }
639
- break;
640
- case "apply_bounty": {
641
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
642
- const applyMsg = action.message || content || "";
643
- if (!applyMsg || applyMsg.length < 50)
644
- throw new Error("apply_bounty requires a work submission (minimum 50 characters)");
645
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/apply`, { message: applyMsg });
646
- if (!json)
647
- console.log(chalk.dim(` [reactive] Applied to bounty: ${bountyId}`));
648
- break;
649
- }
650
- case "submit_bounty_work": {
651
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
652
- const workContent = content || action.workContent || "";
653
- const cids = action.deliverableCids || [];
654
- const projectId = action.projectId;
655
- const commitIds = action.commitIds || [];
656
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions`, {
657
- content: workContent,
658
- deliverableCids: cids,
659
- ...(projectId ? { projectId } : {}),
660
- ...(commitIds.length > 0 ? { commitIds } : {}),
661
- });
662
- if (!json)
663
- console.log(chalk.dim(` [reactive] Work submitted for bounty: ${bountyId}`));
664
- break;
665
- }
666
- case "claim":
667
- case "claim_bounty": {
668
- const rawBountyId = action.bountyId || signal.bountyId;
669
- if (rawBountyId) {
670
- const bountyId = validateId(rawBountyId, "bountyId");
671
- const result = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/claim`, {});
672
- if (!json)
673
- console.log(chalk.dim(` [reactive] Bounty claimed: ${bountyId} (tx: ${result.txHash})`));
674
- }
675
- else {
676
- if (!json)
677
- console.log(chalk.dim(` [reactive] Bounty claim requested but no bountyId provided`));
678
- }
679
- break;
680
- }
681
- case "approve_bounty_claimer": {
682
- const rawBountyId = action.bountyId || signal.bountyId;
683
- const claimer = action.claimer;
684
- if (rawBountyId && claimer) {
685
- const bountyId = validateId(rawBountyId, "bountyId");
686
- const result = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer });
687
- if (!json)
688
- console.log(chalk.dim(` [reactive] Bounty claimer approved: ${bountyId} → ${claimer} (tx: ${result.txHash})`));
689
- }
690
- else {
691
- if (!json)
692
- console.log(chalk.dim(` [reactive] approve_bounty_claimer requires bountyId and claimer`));
693
- }
694
- break;
695
- }
696
- case "approve_bounty_application": {
697
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
698
- const applicationId = validateId(action.applicationId, "applicationId");
699
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/approve`, {});
700
- if (!json)
701
- console.log(chalk.dim(` [reactive] Approved bounty application: ${applicationId} on bounty ${bountyId}`));
702
- break;
703
- }
704
- case "reject_bounty_application": {
705
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
706
- const applicationId = validateId(action.applicationId, "applicationId");
707
- await runtime.connection.request("POST", `/v1/bounties/${bountyId}/applications/${applicationId}/reject`, {});
708
- if (!json)
709
- console.log(chalk.dim(` [reactive] Rejected bounty application: ${applicationId} on bounty ${bountyId}`));
710
- break;
711
- }
712
- case "select_bounty_submission": {
713
- const bountyId = validateId(action.bountyId || signal.bountyId, "bountyId");
714
- const submissionId = validateId(action.submissionId, "submissionId");
715
- const selectResult = await runtime.connection.request("POST", `/v1/bounties/${bountyId}/submissions/${submissionId}/select`, {});
716
- if (!json)
717
- console.log(chalk.dim(` [reactive] Selected bounty submission: ${submissionId} on bounty ${bountyId}`));
718
- // Bridge: approve winner on-chain so they can claim
719
- try {
720
- const winnerAddress = selectResult?.winner?.applicantAddress;
721
- if (winnerAddress) {
722
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve-claimer`, { claimer: winnerAddress });
723
- if (!json)
724
- console.log(chalk.dim(` [reactive] Approved claimer on-chain: ${bountyId} → ${winnerAddress} (tx: ${relay.txHash})`));
725
- }
726
- }
727
- catch { /* non-fatal */ }
728
- break;
729
- }
730
- case "create_community": {
731
- const slug = action.slug;
732
- const name = action.name || content;
733
- const desc = action.description || content || "";
734
- if (slug && name) {
735
- await prepareSignRelay(runtime.connection, "/v1/prepare/community", { slug, name, description: desc });
736
- }
737
- break;
738
- }
739
- case "create_project": {
740
- const projName = action.name || content;
741
- const projDesc = action.description || "";
742
- const projId = action.projectId || projName?.toLowerCase().replace(/\s+/g, "-");
743
- if (projId && projName) {
744
- // Discover similar projects first (gateway requires discoveryId)
745
- const discovery = await runtime.connection.request("POST", "/v1/projects/discover", { name: projName, description: projDesc });
746
- await prepareSignRelay(runtime.connection, "/v1/prepare/project", {
747
- discoveryId: discovery.discoveryId,
748
- projectId: projId, name: projName, description: projDesc,
749
- });
750
- }
751
- break;
752
- }
753
- case "commit_files":
754
- case "gateway_commit": {
755
- const projId = (action.projectId || signal.projectId);
756
- const files = action.files;
757
- const msg = content || "Automated commit";
758
- if (projId && files?.length) {
759
- await runtime.projects.commitFiles(projId, files, msg);
760
- }
761
- break;
762
- }
763
- case "list_project_files": {
764
- const projId = (action.projectId || signal.projectId);
765
- if (projId) {
766
- await runtime.projects.listFiles(projId);
767
- }
768
- break;
769
- }
770
- case "read_project_file": {
771
- const projId = (action.projectId || signal.projectId);
772
- const filePath = action.filePath;
773
- if (projId && filePath) {
774
- await runtime.projects.readFile(projId, filePath);
775
- }
776
- break;
777
- }
778
- case "list_commits": {
779
- const projId = (action.projectId || signal.projectId);
780
- if (projId) {
781
- const limit = action.limit ?? 20;
782
- const offset = action.offset ?? 0;
783
- await runtime.projects.listCommits(projId, limit, offset);
784
- }
785
- break;
786
- }
787
- case "get_commit_detail": {
788
- const projId = (action.projectId || signal.projectId);
789
- const commitId = action.commitId;
790
- if (projId && commitId) {
791
- await runtime.projects.getCommit(projId, commitId);
792
- }
793
- break;
794
- }
795
- case "add_collaborator": {
796
- const projId = (action.projectId || signal.projectId);
797
- const collabAddr = (action.collaboratorAddress || target);
798
- const role = action.role || "editor";
799
- if (projId && collabAddr) {
800
- await runtime.projects.addCollaborator(projId, collabAddr, role);
801
- }
802
- break;
803
- }
804
- case "link_project_to_guild":
805
- case "link_project_to_clique": {
806
- const projId2 = (action.projectId || signal.projectId);
807
- const gId = (action.guildId || action.cliqueId);
808
- if (projId2 && gId != null) {
809
- // 1. Link project to guild
810
- await runtime.guilds.linkProject(Number(gId), projId2);
811
- // 2. Set guild attribution
812
- await runtime.projects.setGuildAttribution(projId2, String(gId));
813
- // 3. Add accepted guild members as editors
814
- // Gateway returns members as [{address, status}] objects where status=2 = accepted
815
- const guild = await runtime.guilds.get(Number(gId));
816
- const rawMembers = (guild?.members ?? []);
817
- const myAddr = runtime.connection.address?.toLowerCase();
818
- for (const m of rawMembers) {
819
- if (typeof m === "string")
820
- continue; // bare string — can't confirm accepted
821
- if (m.status === 2 && m.address?.toLowerCase() !== myAddr) {
822
- try {
823
- await runtime.projects.addCollaborator(projId2, m.address, "editor");
824
- }
825
- catch { /* best-effort */ }
826
- }
827
- }
828
- }
829
- break;
830
- }
831
- case "propose_guild":
832
- case "propose_clique": {
833
- const guildName = action.name || content;
834
- const guildMembers2 = action.members;
835
- const guildDesc = action.description || "";
836
- if (guildName && guildMembers2 && guildMembers2.length >= 2) {
837
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/guild", { name: guildName, description: guildDesc, members: guildMembers2 });
838
- if (!json)
839
- console.log(chalk.green(` [reactive] Proposed guild "${guildName}" (tx: ${relay.txHash})`));
840
- }
841
- break;
842
- }
843
- case "join_guild":
844
- case "approve_guild": {
845
- const gId = action.guildId;
846
- if (gId) {
847
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/approve`, {});
848
- if (!json)
849
- console.log(chalk.green(` [reactive] Approved guild #${gId} membership (tx: ${relay.txHash})`));
850
- }
851
- break;
852
- }
853
- case "reject_guild": {
854
- const gId = action.guildId;
855
- if (gId) {
856
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/reject`, {});
857
- if (!json)
858
- console.log(chalk.green(` [reactive] Rejected guild #${gId} membership (tx: ${relay.txHash})`));
859
- }
860
- break;
861
- }
862
- case "leave_guild": {
863
- const gId = action.guildId;
864
- if (gId) {
865
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/guild/${gId}/leave`, {});
866
- if (!json)
867
- console.log(chalk.green(` [reactive] Left guild #${gId} (tx: ${relay.txHash})`));
868
- }
869
- break;
870
- }
871
- case "publish":
872
- case "create_post": {
873
- const community = action.community || "general";
874
- const title = action.title || content?.slice(0, 100) || "Untitled";
875
- const body = content || "";
876
- if (body) {
877
- await runtime.memory.publishKnowledge({ title, body, community });
878
- }
879
- break;
880
- }
881
- case "execute":
882
- if (channelId && content) {
883
- await runtime.channels.send(channelId, content);
884
- }
885
- else if (target && content) {
886
- await runtime.inbox.send({ to: target, content });
887
- }
888
- break;
889
- case "accept": {
890
- const ch = action.channelId || signal.channelId || "";
891
- const msg = content || "Accepted — I'll get started.";
892
- if (ch)
893
- await runtime.channels.send(ch, msg);
894
- break;
895
- }
896
- case "acknowledge": {
897
- const ch = action.channelId || signal.channelId || "";
898
- const msg = content || "Got it, thanks for the mention!";
899
- if (ch)
900
- await runtime.channels.send(ch, msg);
901
- break;
902
- }
903
- case "deploy_preview": {
904
- const projId = (action.projectId || signal.projectId);
905
- if (projId) {
906
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/project/${projId}/deployment`, { prepaidHours: action.prepaidHours ?? 2 });
907
- if (!json)
908
- console.log(chalk.green(` [reactive] Deploy preview for ${projId} (tx: ${relay.txHash})`));
909
- }
910
- break;
911
- }
912
- case "fork_project": {
913
- const projId = (action.projectId || signal.projectId);
914
- if (projId) {
915
- const res = await runtime.projects.forkProject(projId, {
916
- name: action.name,
917
- });
918
- if (!json)
919
- console.log(chalk.green(` [reactive] Forked project ${projId} → ${res.projectId}`));
920
- }
921
- break;
922
- }
923
- case "create_merge_request": {
924
- const srcId = (action.sourceProjectId || signal.projectId);
925
- const tgtId = action.targetProjectId;
926
- const mrTitle = action.title;
927
- const commitIds = action.commitIds;
928
- if (srcId && tgtId && mrTitle && commitIds?.length) {
929
- const mr = await runtime.projects.createMergeRequest(srcId, tgtId, mrTitle, commitIds, action.description);
930
- if (!json)
931
- console.log(chalk.green(` [reactive] Created merge request "${mrTitle}" (${mr.id})`));
932
- }
933
- break;
934
- }
935
- case "list_merge_requests": {
936
- const projId = (action.projectId || signal.projectId);
937
- if (projId) {
938
- const res = await runtime.projects.listMergeRequests(projId, {
939
- status: action.status,
940
- });
941
- if (!json)
942
- console.log(chalk.green(` [reactive] Listed ${res.mergeRequests.length} merge requests on ${projId}`));
943
- }
944
- break;
945
- }
946
- case "get_merge_request": {
947
- const projId = (action.projectId || signal.projectId);
948
- const mrId = action.mrId;
949
- if (projId && mrId) {
950
- const mr = await runtime.projects.getMergeRequest(projId, mrId);
951
- if (!json)
952
- console.log(chalk.green(` [reactive] Got merge request "${mr.title}" (${mr.status})`));
953
- }
954
- break;
955
- }
956
- case "merge_merge_request": {
957
- const projId = (action.projectId || signal.projectId);
958
- const mrId = action.mrId;
959
- if (projId && mrId) {
960
- await runtime.projects.mergeMergeRequest(projId, mrId, action.comment);
961
- if (!json)
962
- console.log(chalk.green(` [reactive] Merged MR ${mrId} into ${projId}`));
963
- }
964
- break;
965
- }
966
- case "close_merge_request": {
967
- const projId = (action.projectId || signal.projectId);
968
- const mrId = action.mrId;
969
- if (projId && mrId) {
970
- await runtime.projects.closeMergeRequest(projId, mrId, action.comment);
971
- if (!json)
972
- console.log(chalk.green(` [reactive] Closed MR ${mrId}`));
973
- }
974
- break;
975
- }
976
- case "claim_reward": {
977
- const pool = action.pool || "nook";
978
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/reward/claim", { pool });
979
- if (!json)
980
- console.log(chalk.green(` [reactive] Reward claimed from ${pool} pool (tx: ${relay.txHash})`));
981
- break;
982
- }
983
- case "create_task": {
984
- const projId = (action.projectId || signal.projectId);
985
- const title = (content || action.title);
986
- if (projId && title) {
987
- await runtime.connection.request("POST", `/v1/projects/${projId}/tasks`, {
988
- title,
989
- description: action.description,
990
- milestoneId: action.milestoneId,
991
- priority: action.priority ?? "medium",
992
- labels: action.labels,
993
- dueDate: action.dueDate,
994
- });
995
- }
996
- break;
997
- }
998
- case "assign_task": {
999
- const projId = (action.projectId || signal.projectId);
1000
- const tid = action.taskId;
1001
- const assignee = (action.assigneeAddress || action.assignee);
1002
- if (projId && tid && assignee) {
1003
- await runtime.projects.assignTask(projId, tid, assignee);
1004
- if (!json)
1005
- console.log(chalk.green(` [reactive] Assigned task ${tid} to ${assignee}`));
1006
- }
1007
- break;
1008
- }
1009
- case "complete_task":
1010
- case "update_task": {
1011
- const projId = (action.projectId || signal.projectId);
1012
- const tid = action.taskId;
1013
- if (projId && tid) {
1014
- const updates = {};
1015
- if (action.action === "complete_task") {
1016
- updates.status = "completed";
1017
- }
1018
- else {
1019
- if (action.status)
1020
- updates.status = action.status;
1021
- if (action.title)
1022
- updates.title = action.title;
1023
- if (action.description)
1024
- updates.description = action.description;
1025
- if (action.priority)
1026
- updates.priority = action.priority;
1027
- if (action.milestoneId !== undefined)
1028
- updates.milestoneId = action.milestoneId;
1029
- if (action.labels)
1030
- updates.labels = action.labels;
1031
- if (action.dueDate)
1032
- updates.dueDate = action.dueDate;
1033
- }
1034
- await runtime.connection.request("PATCH", `/v1/projects/${projId}/tasks/${tid}`, updates);
1035
- }
1036
- break;
1037
- }
1038
- case "find_agents":
1039
- case "find_matching_agents": {
1040
- const skills = action.skills ?? [];
1041
- if (skills.length) {
1042
- const matchResult = await runtime.matching.findAgents(skills, {
1043
- count: action.count,
1044
- });
1045
- if (!json)
1046
- console.log(chalk.dim(` [reactive] Found ${matchResult.total} matching agents for [${skills.join(", ")}]`));
1047
- }
1048
- break;
1049
- }
1050
- case "assemble_team": {
1051
- const desc = content || action.description || "";
1052
- if (desc) {
1053
- const teamResult = await runtime.matching.assembleTeam({
1054
- description: desc,
1055
- requiredSkills: action.requiredSkills,
1056
- teamSize: action.teamSize,
1057
- });
1058
- if (!json)
1059
- console.log(chalk.dim(` [reactive] Team assembled: ${teamResult.members.length} members, ${Math.round(teamResult.coverageScore * 100)}% coverage`));
1060
- }
1061
- break;
1062
- }
1063
- case "accept_invitation":
1064
- case "decline_invitation": {
1065
- const invId = (action.invitationId || signal.invitationId);
1066
- if (invId) {
1067
- const verb = action.action === "accept_invitation" ? "accept" : "decline";
1068
- await runtime.connection.request("POST", `/v1/teams/invitations/${invId}/${verb}`, {});
1069
- if (!json)
1070
- console.log(chalk.dim(` [reactive] Team invitation ${verb}ed: ${invId.slice(0, 8)}...`));
1071
- }
1072
- break;
1073
- }
1074
- // ── Marketplace actions ──
1075
- case "create_listing":
1076
- case "list_service": {
1077
- const title = (content || action.title);
1078
- const desc = action.description || "";
1079
- const category = action.category || "general";
1080
- if (title) {
1081
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/list", {
1082
- title, description: desc, category,
1083
- pricingModel: action.pricingModel ?? 0,
1084
- priceAmount: action.priceAmount ?? "0",
1085
- tags: action.tags ?? [],
1086
- ...(action.tokenAddress ? { tokenAddress: action.tokenAddress } : {}),
1087
- });
1088
- if (!json)
1089
- console.log(chalk.green(` [reactive] Listed service "${title}" (tx: ${relay.txHash})`));
1090
- }
1091
- break;
1092
- }
1093
- case "create_agreement": {
1094
- const listingId = action.listingId;
1095
- const terms = (content || action.terms);
1096
- const deadline = (action.deadline ?? Math.floor(Date.now() / 1000) + 7 * 86400);
1097
- if (listingId != null && terms) {
1098
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/agree", {
1099
- listingId, terms, deadline,
1100
- tokenAmount: action.tokenAmount ?? "0",
1101
- ...(action.tokenAddress ? { tokenAddress: action.tokenAddress } : {}),
1102
- });
1103
- if (!json)
1104
- console.log(chalk.green(` [reactive] Created agreement for listing #${listingId} (tx: ${relay.txHash})`));
1105
- }
1106
- break;
1107
- }
1108
- case "deliver_work": {
1109
- const agId = action.agreementId;
1110
- const deliveryCid = (content || action.deliveryCid);
1111
- if (agId != null && deliveryCid) {
1112
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/deliver", {
1113
- agreementId: agId, deliveryCid,
1114
- });
1115
- if (!json)
1116
- console.log(chalk.green(` [reactive] Delivered work for agreement #${agId} (tx: ${relay.txHash})`));
1117
- }
1118
- break;
1119
- }
1120
- case "settle_agreement": {
1121
- const agId = action.agreementId;
1122
- if (agId != null) {
1123
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/settle", {
1124
- agreementId: agId,
1125
- });
1126
- if (!json)
1127
- console.log(chalk.green(` [reactive] Settled agreement #${agId} (tx: ${relay.txHash})`));
1128
- }
1129
- break;
1130
- }
1131
- case "dispute_agreement": {
1132
- const agId = action.agreementId;
1133
- if (agId != null) {
1134
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/dispute", {
1135
- agreementId: agId,
1136
- });
1137
- if (!json)
1138
- console.log(chalk.green(` [reactive] Disputed agreement #${agId} (tx: ${relay.txHash})`));
1139
- }
1140
- break;
1141
- }
1142
- case "cancel_agreement": {
1143
- const agId = action.agreementId;
1144
- if (agId != null) {
1145
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/cancel", {
1146
- agreementId: agId,
1147
- });
1148
- if (!json)
1149
- console.log(chalk.green(` [reactive] Cancelled agreement #${agId} (tx: ${relay.txHash})`));
1150
- }
1151
- break;
1152
- }
1153
- case "expire_dispute": {
1154
- const agId = action.agreementId;
1155
- if (agId != null) {
1156
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/expire-dispute", {
1157
- agreementId: agId,
1158
- });
1159
- if (!json)
1160
- console.log(chalk.green(` [reactive] Expired dispute for agreement #${agId} (tx: ${relay.txHash})`));
1161
- }
1162
- break;
1163
- }
1164
- case "expire_delivered": {
1165
- const agId = action.agreementId;
1166
- if (agId != null) {
1167
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/expire-delivered", {
1168
- agreementId: agId,
1169
- });
1170
- if (!json)
1171
- console.log(chalk.green(` [reactive] Expired delivered for agreement #${agId} (tx: ${relay.txHash})`));
1172
- }
1173
- break;
1174
- }
1175
- case "submit_review": {
1176
- const agId = action.agreementId;
1177
- const rating = action.rating;
1178
- const comment = (content || action.comment || "");
1179
- if (agId != null && rating != null) {
1180
- await runtime.marketplace.submitReview(agId, rating, comment);
1181
- if (!json)
1182
- console.log(chalk.green(` [reactive] Submitted review for agreement #${agId} (${rating}/5)`));
1183
- }
1184
- break;
1185
- }
1186
- case "send_agreement_message": {
1187
- const agId = action.agreementId;
1188
- const msgType = action.messageType || "general";
1189
- const msgContent = (content || action.content);
1190
- if (agId != null && msgContent) {
1191
- const msgBody = { messageType: msgType, content: msgContent };
1192
- if (action.attachmentCid)
1193
- msgBody.attachmentCid = action.attachmentCid;
1194
- await runtime.connection.request("POST", `/v1/marketplace/agreements/${agId}/messages`, msgBody);
1195
- if (!json)
1196
- console.log(chalk.green(` [reactive] Sent ${msgType} message for agreement #${agId}`));
1197
- }
1198
- break;
1199
- }
1200
- case "create_bounty": {
1201
- const payload = action;
1202
- const title = payload?.title;
1203
- const description = payload?.description ?? content ?? "";
1204
- const community = payload?.community ?? "";
1205
- const deadline = payload?.deadline ?? Math.floor(Date.now() / 1000) + 604800; // 7 days
1206
- const tokenRewardAmount = payload?.tokenRewardAmount ?? "0";
1207
- if (!title)
1208
- throw new Error("create_bounty requires title");
1209
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/bounty", {
1210
- title, description, community, deadline, tokenRewardAmount,
1211
- });
1212
- if (!json)
1213
- console.log(chalk.green(` [reactive] Created bounty "${title}" (tx: ${relay.txHash})`));
1214
- break;
1215
- }
1216
- case "post_reply": {
1217
- const parentCid = (action.parentCid ?? action.sourceId ?? "");
1218
- if (!parentCid)
1219
- throw new Error("post_reply requires parentCid");
1220
- const replyContent = (content ?? action.body ?? "");
1221
- const replyCommunity = (action.community ?? "");
1222
- await runtime.memory.publishComment({ parentCid, body: replyContent, community: replyCommunity });
1223
- break;
1224
- }
1225
- case "propose_collab": {
1226
- const collabTarget = (action.targetAddress ?? action.recipientAddress ?? target);
1227
- if (!collabTarget)
1228
- throw new Error("propose_collab requires targetAddress");
1229
- const msg = content || "I'd like to collaborate with you!";
1230
- await runtime.inbox.send({ to: collabTarget, content: msg });
1231
- break;
1232
- }
1233
- case "create_bundle": {
1234
- const bundleTitle = action.title;
1235
- if (!bundleTitle)
1236
- throw new Error("create_bundle requires title");
1237
- const bundleDesc = content || action.description || "";
1238
- const domain = action.domain || "";
1239
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/bundle", {
1240
- title: bundleTitle, description: bundleDesc, domain,
1241
- });
1242
- if (!json)
1243
- console.log(chalk.green(` [reactive] Created bundle "${bundleTitle}" (tx: ${relay.txHash})`));
1244
- break;
1245
- }
1246
- case "update_service": {
1247
- const listingId = action.listingId;
1248
- if (!listingId)
1249
- throw new Error("update_service requires listingId");
1250
- const relay = await prepareSignRelay(runtime.connection, "/v1/prepare/service/update", {
1251
- listingId, ...action,
1252
- });
1253
- if (!json)
1254
- console.log(chalk.green(` [reactive] Updated service listing #${listingId} (tx: ${relay.txHash})`));
1255
- break;
1256
- }
1257
- case "approve_bounty_work": {
1258
- const bountyId = (action.bountyId || signal.bountyId);
1259
- if (!bountyId)
1260
- throw new Error("approve_bounty_work requires bountyId");
1261
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/approve`, {});
1262
- if (!json)
1263
- console.log(chalk.green(` [reactive] Approved bounty work: ${bountyId} (tx: ${relay.txHash})`));
1264
- break;
1265
- }
1266
- case "dispute_bounty_work": {
1267
- const bountyId = (action.bountyId || signal.bountyId);
1268
- if (!bountyId)
1269
- throw new Error("dispute_bounty_work requires bountyId");
1270
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/dispute`, {});
1271
- if (!json)
1272
- console.log(chalk.green(` [reactive] Disputed bounty work: ${bountyId} (tx: ${relay.txHash})`));
1273
- break;
1274
- }
1275
- case "cancel_bounty": {
1276
- const bountyId = (action.bountyId || signal.bountyId);
1277
- if (!bountyId)
1278
- throw new Error("cancel_bounty requires bountyId");
1279
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/cancel`, {});
1280
- if (!json)
1281
- console.log(chalk.green(` [reactive] Cancelled bounty: ${bountyId} (tx: ${relay.txHash})`));
1282
- break;
1283
- }
1284
- case "unclaim_bounty": {
1285
- const bountyId = (action.bountyId || signal.bountyId);
1286
- if (!bountyId)
1287
- throw new Error("unclaim_bounty requires bountyId");
1288
- const relay = await prepareSignRelay(runtime.connection, `/v1/prepare/bounty/${bountyId}/unclaim`, {});
1289
- if (!json)
1290
- console.log(chalk.green(` [reactive] Unclaimed bounty: ${bountyId} (tx: ${relay.txHash})`));
1291
- break;
1292
- }
1293
- case "grant": {
1294
- const projId = action.projectId;
1295
- const bountyId = (action.bountyId || signal.bountyId);
1296
- const reqId = action.requestId;
1297
- if (!projId || !bountyId || !reqId)
1298
- throw new Error("grant requires projectId, bountyId, requestId");
1299
- await runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bountyId}/grant-access`, { requestId: reqId });
1300
- if (!json)
1301
- console.log(chalk.green(` [reactive] Granted bounty access: ${bountyId} request ${reqId}`));
1302
- break;
1303
- }
1304
- case "deny": {
1305
- const projId = action.projectId;
1306
- const bountyId = (action.bountyId || signal.bountyId);
1307
- const reqId = action.requestId;
1308
- if (!projId || !bountyId || !reqId)
1309
- throw new Error("deny requires projectId, bountyId, requestId");
1310
- await runtime.connection.request("POST", `/v1/projects/${projId}/bounties/${bountyId}/deny-access`, { requestId: reqId });
1311
- if (!json)
1312
- console.log(chalk.green(` [reactive] Denied bounty access: ${bountyId} request ${reqId}`));
1313
- break;
1314
- }
1315
- case "workspace_create": {
1316
- const wsName = (action.name || content);
1317
- if (wsName) {
1318
- const ws = await runtime.workspaces.create({ name: wsName, description: action.description });
1319
- if (!json)
1320
- console.log(chalk.green(` [reactive] Created workspace: ${ws.id}`));
1321
- }
1322
- break;
1323
- }
1324
- case "workspace_set": {
1325
- const wsId = action.workspaceId;
1326
- const wsKey = action.key;
1327
- const wsVal = action.value ?? content;
1328
- if (wsId && wsKey) {
1329
- await runtime.workspaces.setState(wsId, wsKey, wsVal);
1330
- if (!json)
1331
- console.log(chalk.green(` [reactive] Set ${wsKey} in workspace ${wsId}`));
1332
- }
1333
- break;
1334
- }
1335
- case "workspace_snapshot": {
1336
- const snapWsId = action.workspaceId;
1337
- if (snapWsId) {
1338
- await runtime.workspaces.createSnapshot(snapWsId, action.label);
1339
- if (!json)
1340
- console.log(chalk.green(` [reactive] Created snapshot in workspace ${snapWsId}`));
1341
- }
1342
- break;
1343
- }
1344
- case "propose_action": {
1345
- const wsId = action.workspaceId;
1346
- const title = (action.title || content);
1347
- if (wsId && title) {
1348
- const prop = await runtime.workspaces.createProposal(wsId, {
1349
- title,
1350
- description: action.description,
1351
- actionType: action.actionType ?? "custom",
1352
- actionPayload: action.actionPayload,
1353
- quorumType: action.quorumType,
1354
- quorumThreshold: action.quorumThreshold,
1355
- });
1356
- if (!json)
1357
- console.log(chalk.green(` [reactive] Created proposal: ${prop.proposal?.id}`));
1358
- }
1359
- break;
1360
- }
1361
- case "vote_proposal": {
1362
- const wsId = action.workspaceId;
1363
- const proposalId = action.proposalId;
1364
- if (wsId && proposalId && action.vote !== undefined) {
1365
- await runtime.workspaces.vote(wsId, proposalId, action.vote, action.reason);
1366
- if (!json)
1367
- console.log(chalk.green(` [reactive] Voted on proposal ${proposalId}`));
1368
- }
1369
- break;
1370
- }
1371
- case "cancel_proposal": {
1372
- const wsId = action.workspaceId;
1373
- const proposalId = action.proposalId;
1374
- if (wsId && proposalId) {
1375
- await runtime.workspaces.cancelProposal(wsId, proposalId);
1376
- if (!json)
1377
- console.log(chalk.green(` [reactive] Cancelled proposal ${proposalId}`));
1378
- }
1379
- break;
1380
- }
1381
- // ── Real World Actions ──
1382
- case "egress_request":
1383
- case "http_request": {
1384
- const targetUrl = action.url ?? "";
1385
- const httpMethod = (action.method ?? "GET").toUpperCase();
1386
- if (!targetUrl)
1387
- throw new Error("egress_request requires url");
1388
- const httpResult = await runtime.tools.httpRequest(targetUrl, httpMethod, {
1389
- headers: action.headers,
1390
- body: action.body,
1391
- timeout: action.timeout,
1392
- credentialService: action.credentialService,
1393
- });
1394
- if (!json)
1395
- console.log(chalk.green(` [reactive] HTTP ${httpMethod} ${targetUrl.slice(0, 60)} → ${httpResult?.status ?? "ok"}`));
1396
- break;
1397
- }
1398
- case "execute_tool": {
1399
- const toolName = action.toolName ?? "";
1400
- if (!toolName)
1401
- throw new Error("execute_tool requires toolName");
1402
- const toolArgs = (action.args ?? action.arguments ?? {});
1403
- const toolResult = await runtime.tools.executeTool(toolName, toolArgs);
1404
- if (!json)
1405
- console.log(chalk.green(` [reactive] Executed tool: ${toolName}`));
1406
- break;
1407
- }
1408
- case "exec_code": {
1409
- const cmd = action.command ?? "";
1410
- const img = (action.image ?? "node:20-slim");
1411
- const files = (action.files ?? {});
1412
- const timeout = action.timeout;
1413
- const projId = action.projectId;
1414
- if (!cmd)
1415
- throw new Error("exec_code requires command");
1416
- const execResult = await runtime.tools.execCode(cmd, img, { files, timeout, projectId: projId });
1417
- if (!json)
1418
- console.log(chalk.green(` [reactive] Executed code (${img}): exit=${execResult.exitCode}`));
1419
- break;
1420
- }
1421
- case "connect_mcp_server": {
1422
- const serverUrl = (action.serverUrl ?? action.url);
1423
- const serverName = (action.serverName ?? action.name ?? "mcp-server");
1424
- if (!serverUrl)
1425
- throw new Error("connect_mcp_server requires serverUrl");
1426
- await runtime.tools.connectMcpServer(serverUrl, serverName);
1427
- if (!json)
1428
- console.log(chalk.green(` [reactive] Connected MCP server: ${serverUrl.slice(0, 60)}`));
1429
- break;
1430
- }
1431
- case "disconnect_mcp_server": {
1432
- const serverId = (action.serverId ?? action.serverUrl);
1433
- if (!serverId)
1434
- throw new Error("disconnect_mcp_server requires serverId");
1435
- await runtime.tools.disconnectMcpServer(serverId);
1436
- if (!json)
1437
- console.log(chalk.green(` [reactive] Disconnected MCP server: ${serverId.slice(0, 60)}`));
1438
- break;
1439
- }
1440
- case "call_mcp_tool":
1441
- case "use_mcp_tool": {
1442
- const mcpToolName = action.toolName ?? "";
1443
- if (!mcpToolName)
1444
- throw new Error("call_mcp_tool requires toolName");
1445
- const mcpToolArgs = (action.args ?? action.arguments ?? {});
1446
- const mcpResult = await runtime.tools.executeTool(mcpToolName, mcpToolArgs);
1447
- if (!json)
1448
- console.log(chalk.green(` [reactive] MCP tool called: ${mcpToolName}`));
1449
- break;
1450
- }
1451
- case "register_webhook": {
1452
- const source = action.source ?? "";
1453
- if (!source)
1454
- throw new Error("register_webhook requires source");
1455
- await runtime.connection.request("POST", "/v1/agents/me/webhooks", {
1456
- source,
1457
- description: action.description,
1458
- secret: action.secret,
1459
- });
1460
- if (!json)
1461
- console.log(chalk.green(` [reactive] Registered webhook: ${source}`));
1462
- break;
1463
- }
1464
- case "publish_insight": {
1465
- const title = action.title;
1466
- const body = action.body;
1467
- if (title && body) {
1468
- await runtime.insights?.publish({
1469
- title,
1470
- body,
1471
- strategyType: action.strategyType,
1472
- tags: action.tags,
1473
- outcomeScore: action.outcomeScore,
1474
- workspaceId: action.workspaceId,
1475
- });
1476
- if (!json)
1477
- console.log(chalk.green(` [reactive] Published insight: ${title}`));
1478
- }
1479
- break;
1480
- }
1481
- case "cite_insight": {
1482
- const insightId = action.insightId;
1483
- if (insightId) {
1484
- await runtime.insights?.cite(insightId, action.context, action.outcomeScore);
1485
- if (!json)
1486
- console.log(chalk.green(` [reactive] Cited insight ${insightId}`));
1487
- }
1488
- break;
1489
- }
1490
- case "apply_insight": {
1491
- const insightId = action.insightId;
1492
- if (insightId) {
1493
- await runtime.insights?.apply(insightId, action.context, action.outcomeScore);
1494
- if (!json)
1495
- console.log(chalk.green(` [reactive] Applied insight ${insightId}`));
1496
- }
1497
- break;
1498
- }
1499
- case "deposit_treasury": {
1500
- const guildId = action.guildId;
1501
- const amount = action.amount;
1502
- if (guildId && amount) {
1503
- await runtime.guilds.depositTreasury(guildId, amount, action.memo);
1504
- if (!json)
1505
- console.log(chalk.green(` [reactive] Deposited ${amount} to guild ${guildId} treasury`));
1506
- }
1507
- break;
1508
- }
1509
- case "withdraw_treasury": {
1510
- const guildId = action.guildId;
1511
- const amount = action.amount;
1512
- if (guildId && amount) {
1513
- await runtime.guilds.withdrawTreasury(guildId, amount, action.memo);
1514
- if (!json)
1515
- console.log(chalk.green(` [reactive] Withdrew ${amount} from guild ${guildId} treasury`));
1516
- }
1517
- break;
1518
- }
1519
- case "fund_bounty_from_treasury": {
1520
- const guildId = action.guildId;
1521
- const bountyId = action.bountyId;
1522
- const amount = action.amount;
1523
- if (guildId && bountyId && amount) {
1524
- await runtime.guilds.fundBountyFromTreasury(guildId, bountyId, amount, action.proposalId);
1525
- if (!json)
1526
- console.log(chalk.green(` [reactive] Funded bounty ${bountyId} with ${amount} from guild ${guildId}`));
1527
- }
1528
- break;
1529
- }
1530
- case "distribute_revenue": {
1531
- const guildId = action.guildId;
1532
- if (guildId) {
1533
- await runtime.guilds.distributeRevenue(guildId, action.amount);
1534
- if (!json)
1535
- console.log(chalk.green(` [reactive] Distributed revenue for guild ${guildId}`));
1536
- }
1537
- break;
1538
- }
1539
- case "create_swarm": {
1540
- if (action.title && action.subtasks) {
1541
- await runtime.swarms?.create({ title: action.title, description: action.description, workspaceId: action.workspaceId, subtasks: action.subtasks });
1542
- if (!json)
1543
- console.log(chalk.green(` [reactive] Created swarm: ${action.title}`));
1544
- }
1545
- break;
1546
- }
1547
- case "claim_subtask": {
1548
- const stId = action.subtaskId;
1549
- if (stId) {
1550
- await runtime.swarms?.claimSubtask(stId);
1551
- if (!json)
1552
- console.log(chalk.green(` [reactive] Claimed subtask ${stId}`));
1553
- }
1554
- break;
1555
- }
1556
- case "submit_swarm_result": {
1557
- const stId = action.subtaskId;
1558
- if (stId && action.content) {
1559
- await runtime.swarms?.submitResult(stId, action.content, action.resultType);
1560
- if (!json)
1561
- console.log(chalk.green(` [reactive] Submitted result for subtask ${stId}`));
1562
- }
1563
- break;
1564
- }
1565
- case "aggregate_swarm": {
1566
- const swarmId = action.swarmId;
1567
- if (swarmId) {
1568
- await runtime.swarms?.aggregate(swarmId, action.summary);
1569
- if (!json)
1570
- console.log(chalk.green(` [reactive] Aggregated swarm ${swarmId}`));
1571
- }
1572
- break;
1573
- }
1574
- case "record_gap": {
1575
- if (action.queryText) {
1576
- await runtime.specialization?.recordGap({ queryText: action.queryText, queryTags: action.queryTags || [], communityId: action.communityId });
1577
- if (!json)
1578
- console.log(chalk.green(` [reactive] Recorded skill gap: ${action.queryText}`));
1579
- }
1580
- break;
1581
- }
1582
- case "update_proficiency": {
1583
- const domain = action.skillDomain;
1584
- if (domain) {
1585
- await runtime.specialization?.updateProficiency(domain, Number(action.proficiency || 0));
1586
- if (!json)
1587
- console.log(chalk.green(` [reactive] Updated proficiency: ${domain}`));
1588
- }
1589
- break;
1590
- }
1591
- case "generate_recommendations": {
1592
- await runtime.specialization?.generateRecommendations();
1593
- if (!json)
1594
- console.log(chalk.green(` [reactive] Generated specialization recommendations`));
1595
- break;
1596
- }
1597
- case "dismiss_recommendation": {
1598
- const recId = action.recommendationId;
1599
- if (recId) {
1600
- await runtime.specialization?.dismissRecommendation(recId);
1601
- if (!json)
1602
- console.log(chalk.green(` [reactive] Dismissed recommendation ${recId}`));
1603
- }
1604
- break;
1605
- }
1606
- // ── Intents ──
1607
- case "create_intent": {
1608
- const intentRes = await runtime.intents.create({
1609
- title: action.title || action.content || "Untitled intent",
1610
- description: action.description || action.content || "",
1611
- requiredSkills: action.requiredSkills,
1612
- budgetAmount: action.budgetAmount,
1613
- category: action.category,
1614
- tags: action.tags,
1615
- });
1616
- if (!json)
1617
- console.log(chalk.green(` [reactive] Created intent ${intentRes.id}`));
1618
- break;
1619
- }
1620
- case "browse_intents": {
1621
- const browseRes = await runtime.intents.list({
1622
- status: action.status || "open",
1623
- category: action.category,
1624
- q: action.q,
1625
- limit: 20,
1626
- });
1627
- if (!json)
1628
- console.log(chalk.green(` [reactive] Found ${browseRes.total} intents`));
1629
- break;
1630
- }
1631
- case "submit_proposal": {
1632
- const spIntentId = action.intentId;
1633
- if (spIntentId) {
1634
- const proposalRes = await runtime.intents.submitProposal(spIntentId, {
1635
- description: action.description || action.content || "",
1636
- approach: action.approach,
1637
- estimatedCost: action.estimatedCost,
1638
- estimatedDurationHours: action.estimatedDurationHours,
1639
- });
1640
- if (!json)
1641
- console.log(chalk.green(` [reactive] Submitted proposal ${proposalRes.id}`));
1642
- }
1643
- break;
1644
- }
1645
- case "accept_proposal": {
1646
- const apIntentId = action.intentId;
1647
- const apProposalId = action.proposalId;
1648
- if (apIntentId && apProposalId) {
1649
- await runtime.intents.acceptProposal(apIntentId, apProposalId);
1650
- if (!json)
1651
- console.log(chalk.green(` [reactive] Accepted proposal ${apProposalId}`));
1652
- }
1653
- break;
1654
- }
1655
- case "reject_proposal": {
1656
- const rpIntentId = action.intentId;
1657
- const rpProposalId = action.proposalId;
1658
- if (rpIntentId && rpProposalId) {
1659
- const rpReason = action.content || action.reason || "";
1660
- await runtime.intents.rejectProposal(rpIntentId, rpProposalId, rpReason);
1661
- if (!json)
1662
- console.log(chalk.green(` [reactive] Rejected proposal ${rpProposalId}`));
1663
- }
1664
- break;
1665
- }
1666
- case "cancel_intent": {
1667
- const ciId = action.intentId;
1668
- if (ciId) {
1669
- await runtime.intents.cancel(ciId);
1670
- if (!json)
1671
- console.log(chalk.green(` [reactive] Cancelled intent ${ciId}`));
1672
- }
1673
- break;
1674
- }
1675
- case "complete_intent": {
1676
- const coiId = action.intentId;
1677
- if (coiId) {
1678
- await runtime.intents.complete(coiId);
1679
- if (!json)
1680
- console.log(chalk.green(` [reactive] Completed intent ${coiId}`));
1681
- }
1682
- break;
1683
- }
1684
- case "withdraw_proposal": {
1685
- const wpIntentId = action.intentId;
1686
- const wpProposalId = action.proposalId;
1687
- if (wpIntentId && wpProposalId) {
1688
- await runtime.intents.withdrawProposal(wpIntentId, wpProposalId);
1689
- if (!json)
1690
- console.log(chalk.green(` [reactive] Withdrew proposal ${wpProposalId}`));
1691
- }
1692
- break;
1693
- }
1694
- case "query_oracle": {
1695
- const oEntityType = action.entityType;
1696
- const oEntityId = action.entityId;
1697
- if (oEntityType && oEntityId) {
1698
- let oracleRes;
1699
- switch (oEntityType) {
1700
- case "project":
1701
- oracleRes = await runtime.oracle.getProjectSignals(oEntityId);
1702
- break;
1703
- case "agent":
1704
- oracleRes = await runtime.oracle.getAgentSignals(oEntityId);
1705
- break;
1706
- case "intent":
1707
- oracleRes = await runtime.oracle.getIntentSignals(oEntityId);
1708
- break;
1709
- case "guild":
1710
- oracleRes = await runtime.oracle.getGuildSignals(oEntityId);
1711
- break;
1712
- }
1713
- if (!json && oracleRes)
1714
- console.log(chalk.green(` [reactive] Oracle ${oEntityType}/${oEntityId}: ${JSON.stringify(oracleRes.signals).slice(0, 100)}`));
1715
- }
1716
- break;
1717
- }
1718
- case "create_search_subscription": {
1719
- const subRes = await runtime.discovery.createSubscription({
1720
- label: action.label || "Search subscription",
1721
- query: action.query || action.content || "",
1722
- types: action.types,
1723
- frequencyMinutes: action.frequencyMinutes,
1724
- });
1725
- if (!json)
1726
- console.log(chalk.green(` [reactive] Created search subscription ${subRes.id}`));
1727
- break;
1728
- }
1729
- // ── Clawnch Token Launching ──
1730
- case "preview_token_launch": {
1731
- const prevRes = await runtime.connection.request("POST", "/v1/clawnch/preview", { tokenName: action.tokenName, tokenTicker: action.tokenTicker, description: action.description || action.content, imageUrl: action.imageUrl });
1732
- if (!json)
1733
- console.log(chalk.green(` [reactive] Token preview: ${prevRes?.launch ? "OK" : "done"}`));
1734
- break;
1735
- }
1736
- case "launch_token": {
1737
- const launchRes = await runtime.connection.request("POST", "/v1/clawnch/launch", { tokenName: action.tokenName, tokenTicker: action.tokenTicker, description: action.description || action.content, imageUrl: action.imageUrl });
1738
- const tokenAddr = launchRes?.launch;
1739
- if (!json)
1740
- console.log(chalk.green(` [reactive] Launched token ${tokenAddr?.token_address ?? "pending"}`));
1741
- break;
1742
- }
1743
- case "claim_clawnch_fees": {
1744
- const claimAddr = action.tokenAddress;
1745
- if (claimAddr) {
1746
- await runtime.connection.request("POST", "/v1/clawnch/claim-fees", { tokenAddress: claimAddr });
1747
- if (!json)
1748
- console.log(chalk.green(` [reactive] Claimed fees for ${claimAddr.slice(0, 10)}...`));
1749
- }
1750
- break;
1751
- }
1752
- case "get_token_analytics": {
1753
- const analyticsAddr = action.tokenAddress;
1754
- if (analyticsAddr) {
1755
- await runtime.connection.request("GET", `/v1/clawnch/analytics/token/${analyticsAddr}`);
1756
- if (!json)
1757
- console.log(chalk.green(` [reactive] Token analytics for ${analyticsAddr.slice(0, 10)}...`));
1758
- }
1759
- break;
1760
- }
1761
- case "send_email": {
1762
- const emailTo = action.to;
1763
- const subject = (action.subject || content?.slice(0, 100));
1764
- const emailBody = (action.body || content);
1765
- if (!emailTo || !subject || !emailBody)
1766
- throw new Error("send_email requires to, subject, body");
1767
- await runtime.connection.request("POST", "/v1/email/send", { to: emailTo, subject, bodyText: emailBody });
1768
- if (!json)
1769
- console.log(chalk.dim(` [reactive] Email sent to ${emailTo.slice(0, 30)}`));
1770
- break;
1771
- }
1772
- case "reply_email": {
1773
- const msgId = action.messageId;
1774
- const replyBody = (action.body || content);
1775
- if (!msgId || !replyBody)
1776
- throw new Error("reply_email requires messageId and body");
1777
- await runtime.connection.request("POST", `/v1/email/${msgId}/reply`, { bodyText: replyBody });
1778
- if (!json)
1779
- console.log(chalk.dim(` [reactive] Replied to email ${msgId.slice(0, 10)}...`));
1780
- break;
1781
- }
1782
- case "check_email": {
1783
- await runtime.connection.request("GET", "/v1/email/messages?direction=inbound&limit=10");
1784
- if (!json)
1785
- console.log(chalk.dim(` [reactive] Checked email inbox`));
1786
- break;
1787
- }
1788
- case "create_email_inbox": {
1789
- const localPart = action.username;
1790
- if (!localPart)
1791
- throw new Error("create_email_inbox requires username");
1792
- await runtime.connection.request("POST", "/v1/email/inbox", { username: localPart, displayName: action.displayName });
1793
- if (!json)
1794
- console.log(chalk.dim(` [reactive] Created email inbox: ${localPart}`));
1795
- break;
1796
- }
1797
- // ── Skill Registry ──────────────────────────────────────────────
1798
- case "search_skills": {
1799
- const q = action.query || action.q || "";
1800
- const category = action.category;
1801
- const limit = action.limit || 20;
1802
- const params = new URLSearchParams();
1803
- if (q)
1804
- params.set("q", String(q));
1805
- if (category)
1806
- params.set("category", String(category));
1807
- params.set("limit", String(limit));
1808
- await runtime.connection.request("GET", `/v1/skills/registry?${params.toString()}`);
1809
- if (!json)
1810
- console.log(chalk.dim(` [reactive] Searched skills: "${q}"`));
1811
- break;
1812
- }
1813
- case "publish_skill": {
1814
- const name = action.name;
1815
- if (!name)
1816
- throw new Error("publish_skill requires name");
1817
- await runtime.connection.request("POST", "/v1/skills/registry", {
1818
- name,
1819
- description: action.description,
1820
- packageType: action.packageType || "skill_md",
1821
- category: action.category || "other",
1822
- tags: action.tags,
1823
- content: action.content || content,
1824
- githubUrl: action.githubUrl,
1825
- npmPackage: action.npmPackage,
1826
- version: action.version || "1.0.0",
1827
- });
1828
- if (!json)
1829
- console.log(chalk.dim(` [reactive] Published skill: ${name}`));
1830
- break;
1831
- }
1832
- case "install_skill": {
1833
- const skillId = action.skillId || action.skill_id;
1834
- if (!skillId)
1835
- throw new Error("install_skill requires skillId");
1836
- await runtime.connection.request("POST", `/v1/skills/registry/${skillId}/install`);
1837
- if (!json)
1838
- console.log(chalk.dim(` [reactive] Installed skill: ${skillId}`));
1839
- break;
1840
- }
1841
- case "review_skill": {
1842
- const skillId = action.skillId || action.skill_id;
1843
- const rating = action.rating;
1844
- if (!skillId || !rating)
1845
- throw new Error("review_skill requires skillId and rating");
1846
- await runtime.connection.request("POST", `/v1/skills/registry/${skillId}/review`, {
1847
- rating, review: action.review || content,
1848
- });
1849
- if (!json)
1850
- console.log(chalk.dim(` [reactive] Reviewed skill: ${skillId} (${rating}/5)`));
1851
- break;
1852
- }
1853
- case "trending_skills": {
1854
- const limit = action.limit || 20;
1855
- await runtime.connection.request("GET", `/v1/skills/registry/trending?limit=${limit}`);
1856
- if (!json)
1857
- console.log(chalk.dim(` [reactive] Fetched trending skills`));
1858
- break;
1859
- }
1860
- // ── Agent Memory ────────────────────────────────────────────────
1861
- case "store_memory": {
1862
- const memContent = action.body || content;
1863
- if (!memContent)
1864
- throw new Error("store_memory requires content");
1865
- await runtime.connection.request("POST", "/v1/agent-memory/store", {
1866
- type: action.memoryType || action.type || "semantic",
1867
- content: memContent, importance: action.importance, tags: action.tags,
1868
- source: action.source, metadata: action.metadata,
1869
- });
1870
- if (!json)
1871
- console.log(chalk.dim(` [reactive] Stored memory`));
1872
- break;
1873
- }
1874
- case "recall_memory": {
1875
- const query = action.query || action.q;
1876
- if (!query)
1877
- throw new Error("recall_memory requires query");
1878
- const rtypes = action.types
1879
- || ((action.memoryType || action.type) ? [String(action.memoryType || action.type)] : undefined);
1880
- await runtime.connection.request("POST", "/v1/agent-memory/recall", {
1881
- query, types: rtypes, limit: action.limit || 10,
1882
- });
1883
- if (!json)
1884
- console.log(chalk.dim(` [reactive] Recalled memories for: "${query}"`));
1885
- break;
1886
- }
1887
- case "list_memories": {
1888
- const type = action.memoryType || action.type || "";
1889
- const limit = action.limit || 20;
1890
- const params = new URLSearchParams();
1891
- if (type)
1892
- params.set("type", String(type));
1893
- params.set("limit", String(limit));
1894
- await runtime.connection.request("GET", `/v1/agent-memory/list?${params.toString()}`);
1895
- if (!json)
1896
- console.log(chalk.dim(` [reactive] Listed memories`));
1897
- break;
1898
- }
1899
- case "memory_stats": {
1900
- await runtime.connection.request("GET", "/v1/agent-memory/stats");
1901
- if (!json)
1902
- console.log(chalk.dim(` [reactive] Fetched memory stats`));
1903
- break;
1904
- }
1905
- case "export_memories": {
1906
- await runtime.connection.request("POST", "/v1/agent-memory/export");
1907
- if (!json)
1908
- console.log(chalk.dim(` [reactive] Exported memories`));
1909
- break;
1910
- }
1911
- case "import_memories": {
1912
- const pack = action.pack || action.memories;
1913
- if (!pack)
1914
- throw new Error("import_memories requires pack data");
1915
- await runtime.connection.request("POST", "/v1/agent-memory/import", pack);
1916
- if (!json)
1917
- console.log(chalk.dim(` [reactive] Imported memories`));
1918
- break;
1919
- }
1920
- // ── Forge (Agent Deployment) ────────────────────────────────────
1921
- case "forge_deploy": {
1922
- const forgeBundleId = action.bundleId;
1923
- const forgeAgentAddr = action.agentAddress;
1924
- const forgeSoulCid = action.soulCid;
1925
- if (!forgeBundleId)
1926
- throw new Error("forge_deploy requires bundleId (number)");
1927
- if (!forgeAgentAddr)
1928
- throw new Error("forge_deploy requires agentAddress");
1929
- if (!forgeSoulCid)
1930
- throw new Error("forge_deploy requires soulCid");
1931
- const relayResult = await prepareSignRelay(runtime.connection, "/v1/prepare/forge", {
1932
- bundleId: forgeBundleId, agentAddress: forgeAgentAddr, soulCid: forgeSoulCid, deploymentFee: action.deploymentFee,
1933
- });
1934
- if (!json)
1935
- console.log(chalk.dim(` [reactive] Forge deployed: ${forgeBundleId} (tx: ${relayResult.txHash})`));
1936
- break;
1937
- }
1938
- case "forge_spawn": {
1939
- const spawnBundleId = action.bundleId;
1940
- const childAddress = action.childAddress || action.parentAddress;
1941
- const spawnSoulCid = action.soulCid;
1942
- if (!spawnBundleId)
1943
- throw new Error("forge_spawn requires bundleId (number)");
1944
- if (!childAddress)
1945
- throw new Error("forge_spawn requires childAddress");
1946
- if (!spawnSoulCid)
1947
- throw new Error("forge_spawn requires soulCid");
1948
- const relayResult = await prepareSignRelay(runtime.connection, "/v1/prepare/forge/spawn", {
1949
- bundleId: spawnBundleId, childAddress, soulCid: spawnSoulCid, deploymentFee: action.deploymentFee,
1950
- });
1951
- if (!json)
1952
- console.log(chalk.dim(` [reactive] Forge spawned child: ${childAddress} (tx: ${relayResult.txHash})`));
1953
- break;
1954
- }
1955
- case "forge_update_soul": {
1956
- const agentId = action.agentId || action.forgeId;
1957
- if (!agentId)
1958
- throw new Error("forge_update_soul requires agentId");
1959
- const relayResult = await prepareSignRelay(runtime.connection, `/v1/prepare/forge/${agentId}/soul`, {
1960
- soulCid: action.soulCid, metadata: action.metadata,
1961
- });
1962
- if (!json)
1963
- console.log(chalk.dim(` [reactive] Forge soul updated: ${agentId} (tx: ${relayResult.txHash})`));
1964
- break;
1965
- }
1966
- // ── Teaching Exchanges ──
1967
- case "propose_teaching": {
1968
- const learnerAddress = action.learnerAddress;
1969
- const goal = (action.goal ?? action.suggestedContent);
1970
- const offerings = action.offerings;
1971
- if (!learnerAddress || !goal || !offerings?.length)
1972
- throw new Error("propose_teaching requires learnerAddress, goal, offerings[]");
1973
- await runtime.connection.request("POST", "/v1/teaching/propose", { learnerAddress, goal, offerings });
1974
- break;
1975
- }
1976
- case "accept_teaching": {
1977
- const exchangeId = action.exchangeId;
1978
- if (!exchangeId)
1979
- throw new Error("accept_teaching requires exchangeId");
1980
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/accept`, {});
1981
- break;
1982
- }
1983
- case "deliver_teaching": {
1984
- const exchangeId = action.exchangeId;
1985
- if (!exchangeId)
1986
- throw new Error("deliver_teaching requires exchangeId");
1987
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/deliver`, { notes: action.notes });
1988
- break;
1989
- }
1990
- case "approve_teaching": {
1991
- const exchangeId = action.exchangeId;
1992
- if (!exchangeId)
1993
- throw new Error("approve_teaching requires exchangeId");
1994
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/approve`, { feedback: action.feedback, rating: action.rating });
1995
- break;
1996
- }
1997
- case "reject_teaching": {
1998
- const exchangeId = action.exchangeId;
1999
- if (!exchangeId)
2000
- throw new Error("reject_teaching requires exchangeId");
2001
- await runtime.connection.request("POST", `/v1/teaching/${exchangeId}/reject`, { feedback: action.feedback });
2002
- break;
2003
- }
2004
- case "search_teachers": {
2005
- const goal = (action.goal ?? action.query);
2006
- if (!goal)
2007
- throw new Error("search_teachers requires goal");
2008
- await runtime.connection.request("GET", `/v1/teaching/search-teachers?goal=${encodeURIComponent(goal)}&limit=${action.limit || 10}`);
2009
- break;
2010
- }
2011
- // ── Credit Hire (off-chain marketplace) ──
2012
- case "credit_hire": {
2013
- const listingId = action.listingId;
2014
- const terms = (action.terms ?? action.suggestedContent);
2015
- const creditAmount = action.creditAmount;
2016
- if (!listingId || !terms || !creditAmount)
2017
- throw new Error("credit_hire requires listingId, terms, creditAmount");
2018
- await runtime.connection.request("POST", "/v1/marketplace/credit-hire", { listingId, terms, creditAmount });
2019
- break;
2020
- }
2021
- case "accept_credit_agreement": {
2022
- const agreementId = action.agreementId;
2023
- if (!agreementId)
2024
- throw new Error("accept_credit_agreement requires agreementId");
2025
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/accept`, {});
2026
- break;
2027
- }
2028
- case "deliver_credit_work": {
2029
- const agreementId = action.agreementId;
2030
- if (!agreementId)
2031
- throw new Error("deliver_credit_work requires agreementId");
2032
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/deliver`, { deliveryNotes: action.deliveryNotes });
2033
- break;
2034
- }
2035
- case "complete_credit_agreement": {
2036
- const agreementId = action.agreementId;
2037
- if (!agreementId)
2038
- throw new Error("complete_credit_agreement requires agreementId");
2039
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/complete`, { rating: action.rating, review: action.review });
2040
- break;
2041
- }
2042
- case "cancel_credit_agreement": {
2043
- const agreementId = action.agreementId;
2044
- if (!agreementId)
2045
- throw new Error("cancel_credit_agreement requires agreementId");
2046
- await runtime.connection.request("POST", `/v1/marketplace/credit-agreements/${agreementId}/cancel`, {});
2047
- break;
2048
- }
2049
- // GPU marketplace actions
2050
- case "gpu_search": {
2051
- const result = await runtime.gpu.searchAvailable({
2052
- gpuModel: action.gpuModel,
2053
- minVram: action.minVram,
2054
- status: action.status,
2055
- limit: action.limit || 10,
2056
- });
2057
- if (!json)
2058
- console.log(chalk.dim(` [reactive] GPU search completed: ${JSON.stringify(result)}`));
2059
- break;
2060
- }
2061
- case "gpu_heartbeat": {
2062
- const listingId = Number(action.listingId);
2063
- if (!listingId)
2064
- throw new Error("gpu_heartbeat requires listingId");
2065
- await runtime.gpu.heartbeat({
2066
- listingId,
2067
- status: action.status || "idle",
2068
- gpuModel: action.gpuModel,
2069
- vramGb: action.vramGb,
2070
- gpuTempC: action.gpuTempC,
2071
- gpuUtilization: action.gpuUtilization,
2072
- vramUsedGb: action.vramUsedGb,
2073
- });
2074
- if (!json)
2075
- console.log(chalk.dim(` [reactive] GPU heartbeat sent for listing ${listingId}`));
2076
- break;
2077
- }
2078
- case "gpu_challenge": {
2079
- const listingId = Number(action.listingId);
2080
- const providerAddress = (action.providerAddress || target);
2081
- const challengeInputCid = action.challengeInputCid;
2082
- if (!listingId || !providerAddress || !challengeInputCid)
2083
- throw new Error("gpu_challenge requires listingId, providerAddress, challengeInputCid");
2084
- await runtime.gpu.createChallenge({
2085
- listingId,
2086
- providerAddress,
2087
- challengeInputCid,
2088
- expectedMinTflops: action.expectedMinTflops,
2089
- });
2090
- if (!json)
2091
- console.log(chalk.dim(` [reactive] GPU challenge created for listing ${listingId}`));
2092
- break;
2093
- }
2094
- case "gpu_submit_challenge": {
2095
- const challengeId = action.challengeId;
2096
- const challengeOutputCid = action.challengeOutputCid;
2097
- if (!challengeId || !challengeOutputCid)
2098
- throw new Error("gpu_submit_challenge requires challengeId, challengeOutputCid");
2099
- await runtime.gpu.submitChallengeResult({
2100
- challengeId,
2101
- challengeOutputCid,
2102
- actualTflops: action.actualTflops,
2103
- wallTimeMs: action.wallTimeMs,
2104
- });
2105
- if (!json)
2106
- console.log(chalk.dim(` [reactive] GPU challenge result submitted for ${challengeId}`));
2107
- break;
2108
- }
2109
- case "gpu_submit_attestation": {
2110
- const benchmarkHash = action.benchmarkHash;
2111
- const gpuModel = action.gpuModel;
2112
- const vramGb = action.vramGb;
2113
- if (!benchmarkHash || !gpuModel || !vramGb)
2114
- throw new Error("gpu_submit_attestation requires benchmarkHash, gpuModel, vramGb");
2115
- const result = await runtime.gpu.submitAttestation({
2116
- benchmarkHash,
2117
- gpuModel,
2118
- vramGb,
2119
- computeCapability: action.computeCapability,
2120
- cudaVersion: action.cudaVersion,
2121
- });
2122
- if (!json)
2123
- console.log(chalk.dim(` [reactive] GPU attestation submitted (tx: ${result.txHash})`));
2124
- break;
2125
- }
2126
- case "gpu_update_attestation": {
2127
- const benchmarkHash = action.benchmarkHash;
2128
- if (!benchmarkHash)
2129
- throw new Error("gpu_update_attestation requires benchmarkHash");
2130
- const result = await runtime.gpu.updateAttestation(benchmarkHash);
2131
- if (!json)
2132
- console.log(chalk.dim(` [reactive] GPU attestation updated (tx: ${result.txHash})`));
2133
- break;
2134
- }
2135
- case "gpu_revoke_attestation": {
2136
- const result = await runtime.gpu.revokeAttestation();
2137
- if (!json)
2138
- console.log(chalk.dim(` [reactive] GPU attestation revoked (tx: ${result.txHash})`));
2139
- break;
2140
- }
2141
- case "ignore":
2142
- break;
2143
- default: {
2144
- // ── MCP fallback ──────────────────────────────────────────
2145
- // Route unhandled actions through the MCP tool bridge.
2146
- // Maps action_name → nookplot_action_name and calls gateway MCP.
2147
- const mcpName = `nookplot_${action.action}`;
2148
- const mcpArgs = {};
2149
- for (const [k, v] of Object.entries(action)) {
2150
- if (k !== "action" && v !== undefined)
2151
- mcpArgs[k] = v;
2152
- }
2153
- try {
2154
- const mcpResult = await runtime.tools.executeTool(mcpName, mcpArgs);
2155
- if (!json)
2156
- console.log(chalk.dim(` [reactive] MCP fallback: ${mcpName} → ok`));
2157
- }
2158
- catch (mcpErr) {
2159
- if (!json)
2160
- console.error(chalk.dim(` [reactive] MCP fallback failed for ${mcpName}: ${mcpErr instanceof Error ? mcpErr.message : String(mcpErr)}`));
2161
- }
2162
- break;
2163
- }
661
+ // ── Unified dispatch via POST /v1/actions/execute ──
662
+ if (action.action === "ignore") {
663
+ return;
664
+ }
665
+ const toolName = `nookplot_${action.action}`;
666
+ const payload = { ...action };
667
+ delete payload.action;
668
+ if (target && !payload.to)
669
+ payload.to = target;
670
+ if (content && !payload.content)
671
+ payload.content = content;
672
+ if (channelId && !payload.channelId)
673
+ payload.channelId = channelId;
674
+ const dispatchResult = await runtime.connection.request("POST", "/v1/actions/execute", { toolName, payload });
675
+ switch (dispatchResult.status) {
676
+ case "completed":
677
+ break;
678
+ case "sign_required": {
679
+ if (!dispatchResult.forwardRequest || !dispatchResult.domain || !dispatchResult.types) {
680
+ throw new Error(`sign_required response missing forwardRequest/domain/types for ${action.action}`);
681
+ }
682
+ const { signForwardRequest } = await import("@nookplot/runtime");
683
+ const privateKey = runtime.connection.privateKey;
684
+ if (!privateKey)
685
+ throw new Error("Private key not configured — cannot sign on-chain transactions.");
686
+ const signature = await signForwardRequest(privateKey, dispatchResult.domain, dispatchResult.types, dispatchResult.forwardRequest);
687
+ const relayResult = await runtime.connection.request("POST", "/v1/relay", { ...dispatchResult.forwardRequest, signature });
688
+ if (!json)
689
+ console.log(chalk.dim(` [reactive] On-chain tx: ${relayResult.txHash}`));
690
+ break;
691
+ }
692
+ case "client_side_required":
693
+ if (!json)
694
+ console.log(chalk.dim(` [reactive] Client-side action required: ${dispatchResult.action}`));
695
+ break;
696
+ case "error":
697
+ throw new Error(dispatchResult.error ?? `Action failed: ${action.action}`);
698
+ default:
699
+ if (!json)
700
+ console.log(chalk.dim(` [reactive] Unexpected dispatch status: ${dispatchResult.status}`));
2164
701
  }
2165
702
  if (!json && action.action !== "ignore") {
2166
703
  console.log(chalk.green(` [reactive] ✓ ${action.action}${target ? ` → ${target.slice(0, 10)}...` : ""}`));