@hivemind-os/collective-mcp-server 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +14 -0
- package/dist/index.d.ts +493 -0
- package/dist/index.js +2129 -0
- package/dist/index.js.map +1 -0
- package/package.json +31 -0
- package/src/context.ts +58 -0
- package/src/encryption.ts +25 -0
- package/src/index.ts +41 -0
- package/src/resources/agent.ts +77 -0
- package/src/resources/capabilities.ts +24 -0
- package/src/resources/index.ts +70 -0
- package/src/resources/task.ts +58 -0
- package/src/resources/wallet.ts +32 -0
- package/src/tools/analytics.ts +122 -0
- package/src/tools/balance.ts +36 -0
- package/src/tools/deactivate.ts +33 -0
- package/src/tools/discover.ts +256 -0
- package/src/tools/dispute.ts +135 -0
- package/src/tools/execute-async.ts +39 -0
- package/src/tools/execute.ts +418 -0
- package/src/tools/index.ts +152 -0
- package/src/tools/indexer-client.ts +163 -0
- package/src/tools/marketplace-accept-bid.ts +43 -0
- package/src/tools/marketplace-bid.ts +56 -0
- package/src/tools/marketplace-browse.ts +66 -0
- package/src/tools/marketplace-post.ts +96 -0
- package/src/tools/metering.ts +214 -0
- package/src/tools/multi-execute.ts +218 -0
- package/src/tools/policy-update.ts +94 -0
- package/src/tools/register.ts +78 -0
- package/src/tools/relay-registry.ts +95 -0
- package/src/tools/stake.ts +103 -0
- package/src/tools/task-history.ts +86 -0
- package/src/tools/task-status.ts +66 -0
- package/tests/analytics.test.ts +41 -0
- package/tests/auth-errors.test.ts +85 -0
- package/tests/balance.test.ts +32 -0
- package/tests/context.test.ts +112 -0
- package/tests/discover.test.ts +207 -0
- package/tests/dispute.test.ts +140 -0
- package/tests/execute.test.ts +150 -0
- package/tests/marketplace.test.ts +117 -0
- package/tests/metering.test.ts +173 -0
- package/tests/multi-execute.test.ts +123 -0
- package/tests/relay-registry.test.ts +71 -0
- package/tests/stake.test.ts +90 -0
- package/tsconfig.json +9 -0
- package/tsup.config.ts +10 -0
- package/vitest.config.ts +8 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2129 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
|
|
4
|
+
// src/resources/index.ts
|
|
5
|
+
import {
|
|
6
|
+
ListResourcesRequestSchema,
|
|
7
|
+
ListResourceTemplatesRequestSchema,
|
|
8
|
+
ReadResourceRequestSchema
|
|
9
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
|
|
11
|
+
// src/resources/agent.ts
|
|
12
|
+
var meshAgentResourceTemplate = {
|
|
13
|
+
uriTemplate: "mesh://agent/{did}",
|
|
14
|
+
name: "Agent Profile",
|
|
15
|
+
description: "Resolve a mesh agent profile by DID",
|
|
16
|
+
mimeType: "application/json"
|
|
17
|
+
};
|
|
18
|
+
async function readAgentResource(did, context) {
|
|
19
|
+
const cached = context.agentCache.getAgentByDID(did);
|
|
20
|
+
if (cached) {
|
|
21
|
+
return cached;
|
|
22
|
+
}
|
|
23
|
+
const discovered = await findAgentByDidOnChain(did, context);
|
|
24
|
+
if (!discovered) {
|
|
25
|
+
throw new Error(`Agent ${did} was not found.`);
|
|
26
|
+
}
|
|
27
|
+
context.agentCache.upsertAgent(discovered);
|
|
28
|
+
return discovered;
|
|
29
|
+
}
|
|
30
|
+
async function findAgentByDidOnChain(did, context) {
|
|
31
|
+
const eventType = `${context.networkConfig.packageId}::registry::AgentRegistered`;
|
|
32
|
+
let cursor = null;
|
|
33
|
+
while (true) {
|
|
34
|
+
const page = await context.suiClient.queryEvents(eventType, cursor, 50);
|
|
35
|
+
for (const event of page.events) {
|
|
36
|
+
if (!isRecord(event.parsedJson)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (!stringEquals(readString(event.parsedJson.did), did)) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const cardId = readString(event.parsedJson.id, event.parsedJson.card_id, event.parsedJson.cardId);
|
|
43
|
+
if (!cardId) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
const card = await context.registryClient.getAgentCard(cardId);
|
|
47
|
+
if (card) {
|
|
48
|
+
return card;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!page.hasMore || !page.nextCursor) {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
cursor = page.nextCursor;
|
|
55
|
+
}
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
function isRecord(value) {
|
|
59
|
+
return typeof value === "object" && value !== null;
|
|
60
|
+
}
|
|
61
|
+
function readString(...values) {
|
|
62
|
+
const match = values.find((value) => typeof value === "string");
|
|
63
|
+
return typeof match === "string" ? match : "";
|
|
64
|
+
}
|
|
65
|
+
function stringEquals(left, right) {
|
|
66
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// src/resources/capabilities.ts
|
|
70
|
+
var meshCapabilitiesResource = {
|
|
71
|
+
uri: "mesh://capabilities",
|
|
72
|
+
name: "Capability Directory",
|
|
73
|
+
description: "Known mesh capabilities aggregated from the local agent cache",
|
|
74
|
+
mimeType: "application/json"
|
|
75
|
+
};
|
|
76
|
+
async function readCapabilitiesResource(context) {
|
|
77
|
+
const counts = /* @__PURE__ */ new Map();
|
|
78
|
+
for (const agent of context.agentCache.getAllActive(1e3)) {
|
|
79
|
+
for (const capability of agent.capabilities) {
|
|
80
|
+
counts.set(capability.name, (counts.get(capability.name) ?? 0) + 1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return [...counts.entries()].map(([capability, agent_count]) => ({ capability, agent_count })).sort((left, right) => left.capability.localeCompare(right.capability));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/resources/task.ts
|
|
87
|
+
import { TaskStatus } from "@hivemind-os/collective-types";
|
|
88
|
+
|
|
89
|
+
// src/encryption.ts
|
|
90
|
+
function supportsEncryptedBlobs(blobStore) {
|
|
91
|
+
return typeof blobStore.storeEncrypted === "function" && typeof blobStore.fetchDecrypted === "function";
|
|
92
|
+
}
|
|
93
|
+
async function fetchMeshBlob(blobStore, blobId) {
|
|
94
|
+
return supportsEncryptedBlobs(blobStore) ? await blobStore.fetchDecrypted(blobId) : await blobStore.fetch(blobId);
|
|
95
|
+
}
|
|
96
|
+
function hexToBytes(value) {
|
|
97
|
+
if (!value || value.length !== 64 || !/^[a-f0-9]+$/i.test(value)) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
return new Uint8Array(Buffer.from(value, "hex"));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/resources/task.ts
|
|
104
|
+
var decoder = new TextDecoder();
|
|
105
|
+
var meshTaskResourceTemplate = {
|
|
106
|
+
uriTemplate: "mesh://task/{id}",
|
|
107
|
+
name: "Task Details",
|
|
108
|
+
description: "Resolve task details and result payload by task id",
|
|
109
|
+
mimeType: "application/json"
|
|
110
|
+
};
|
|
111
|
+
async function readTaskResource(taskId, context) {
|
|
112
|
+
const task = await context.taskClient.getTask(taskId);
|
|
113
|
+
if (!task) {
|
|
114
|
+
throw new Error(`Task ${taskId} was not found.`);
|
|
115
|
+
}
|
|
116
|
+
let result;
|
|
117
|
+
if ((task.status === TaskStatus.COMPLETED || task.status === TaskStatus.RELEASED) && task.resultBlobId) {
|
|
118
|
+
const bytes = await fetchMeshBlob(context.blobStore, task.resultBlobId);
|
|
119
|
+
result = bytes ? decoder.decode(bytes) : void 0;
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
id: task.id,
|
|
123
|
+
requester: task.requester,
|
|
124
|
+
provider: task.provider,
|
|
125
|
+
capability: task.capability,
|
|
126
|
+
input_blob_id: task.inputBlobId,
|
|
127
|
+
result_blob_id: task.resultBlobId,
|
|
128
|
+
price_mist: task.price.toString(),
|
|
129
|
+
status: TaskStatus[task.status] ?? "UNKNOWN",
|
|
130
|
+
created_at: task.createdAt,
|
|
131
|
+
accepted_at: task.acceptedAt,
|
|
132
|
+
completed_at: task.completedAt,
|
|
133
|
+
expires_at: task.expiresAt,
|
|
134
|
+
agreement_hash: task.agreementHash,
|
|
135
|
+
result
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// src/resources/wallet.ts
|
|
140
|
+
import { PaymentRail } from "@hivemind-os/collective-types";
|
|
141
|
+
|
|
142
|
+
// src/tools/balance.ts
|
|
143
|
+
var meshBalanceTool = {
|
|
144
|
+
name: "collective_balance",
|
|
145
|
+
description: "Get the current wallet balance in MIST and SUI",
|
|
146
|
+
inputSchema: {
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {},
|
|
149
|
+
required: []
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
async function runMeshBalance(_params, context) {
|
|
153
|
+
const address = context.keypair.getPublicKey().toSuiAddress();
|
|
154
|
+
const balanceMist = await context.suiClient.getBalance(address);
|
|
155
|
+
return {
|
|
156
|
+
address,
|
|
157
|
+
balance_mist: balanceMist.toString(),
|
|
158
|
+
balance_sui: formatMistToSui(balanceMist)
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function formatMistToSui(balanceMist) {
|
|
162
|
+
const whole = balanceMist / 1000000000n;
|
|
163
|
+
const fraction = balanceMist % 1000000000n;
|
|
164
|
+
if (fraction === 0n) {
|
|
165
|
+
return whole.toString();
|
|
166
|
+
}
|
|
167
|
+
const fractionText = fraction.toString().padStart(9, "0").replace(/0+$/, "");
|
|
168
|
+
return `${whole.toString()}.${fractionText}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/resources/wallet.ts
|
|
172
|
+
var meshWalletResource = {
|
|
173
|
+
uri: "mesh://wallet",
|
|
174
|
+
name: "Wallet Overview",
|
|
175
|
+
description: "Current wallet balance and local spending totals",
|
|
176
|
+
mimeType: "application/json"
|
|
177
|
+
};
|
|
178
|
+
async function readWalletResource(context) {
|
|
179
|
+
const address = context.keypair.getPublicKey().toSuiAddress();
|
|
180
|
+
const balance = await context.suiClient.getBalance(address);
|
|
181
|
+
return {
|
|
182
|
+
address,
|
|
183
|
+
balance_mist: balance.toString(),
|
|
184
|
+
balance_sui: formatMistToSui(balance),
|
|
185
|
+
spent_today_mist: context.spendingPolicy.getSpent("day", PaymentRail.SUI_ESCROW).toString(),
|
|
186
|
+
spent_month_mist: context.spendingPolicy.getSpent("month", PaymentRail.SUI_ESCROW).toString(),
|
|
187
|
+
rail: PaymentRail.SUI_ESCROW
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// src/resources/index.ts
|
|
192
|
+
var staticResources = [meshCapabilitiesResource, meshWalletResource];
|
|
193
|
+
var resourceTemplates = [meshAgentResourceTemplate, meshTaskResourceTemplate];
|
|
194
|
+
function registerResourceHandlers(server, context) {
|
|
195
|
+
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
|
|
196
|
+
resources: staticResources
|
|
197
|
+
}));
|
|
198
|
+
server.setRequestHandler(ListResourceTemplatesRequestSchema, async () => ({
|
|
199
|
+
resourceTemplates
|
|
200
|
+
}));
|
|
201
|
+
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
|
|
202
|
+
const uri = request.params.uri;
|
|
203
|
+
const data = await routeResourceRead(uri, context);
|
|
204
|
+
return {
|
|
205
|
+
contents: [
|
|
206
|
+
{
|
|
207
|
+
uri,
|
|
208
|
+
mimeType: "application/json",
|
|
209
|
+
text: serialize(data)
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
};
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
async function routeResourceRead(uri, context) {
|
|
216
|
+
const parsed = new URL(uri);
|
|
217
|
+
switch (parsed.host) {
|
|
218
|
+
case "capabilities":
|
|
219
|
+
return readCapabilitiesResource(context);
|
|
220
|
+
case "wallet":
|
|
221
|
+
return readWalletResource(context);
|
|
222
|
+
case "agent":
|
|
223
|
+
return readAgentResource(decodeURIComponent(parsed.pathname.replace(/^\//, "")), context);
|
|
224
|
+
case "task":
|
|
225
|
+
return readTaskResource(decodeURIComponent(parsed.pathname.replace(/^\//, "")), context);
|
|
226
|
+
default:
|
|
227
|
+
throw new Error(`Unknown resource: ${uri}`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
function serialize(payload) {
|
|
231
|
+
return JSON.stringify(payload, bigintReplacer, 2);
|
|
232
|
+
}
|
|
233
|
+
function bigintReplacer(_key, value) {
|
|
234
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/tools/index.ts
|
|
238
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
239
|
+
import { SessionExpiredError } from "@hivemind-os/collective-core";
|
|
240
|
+
|
|
241
|
+
// src/tools/indexer-client.ts
|
|
242
|
+
async function executeIndexerQuery(context, query, variables) {
|
|
243
|
+
const endpoint = resolveIndexerUrl(context);
|
|
244
|
+
if (!endpoint) {
|
|
245
|
+
throw new Error("Indexer is not configured.");
|
|
246
|
+
}
|
|
247
|
+
const fetchImpl = context.indexer?.fetch ?? globalThis.fetch;
|
|
248
|
+
const response = await fetchImpl(endpoint, {
|
|
249
|
+
method: "POST",
|
|
250
|
+
headers: {
|
|
251
|
+
"content-type": "application/json"
|
|
252
|
+
},
|
|
253
|
+
body: JSON.stringify({ query, variables })
|
|
254
|
+
});
|
|
255
|
+
if (!response.ok) {
|
|
256
|
+
throw new Error(`Indexer query failed with status ${response.status}.`);
|
|
257
|
+
}
|
|
258
|
+
const payload = await response.json();
|
|
259
|
+
if (payload.errors && payload.errors.length > 0) {
|
|
260
|
+
throw new Error(payload.errors.map((entry) => entry.message ?? "Unknown GraphQL error").join("; "));
|
|
261
|
+
}
|
|
262
|
+
if (!payload.data) {
|
|
263
|
+
throw new Error("Indexer query returned no data.");
|
|
264
|
+
}
|
|
265
|
+
return payload.data;
|
|
266
|
+
}
|
|
267
|
+
async function queryIndexerAgents(context, params) {
|
|
268
|
+
const data = await executeIndexerQuery(
|
|
269
|
+
context,
|
|
270
|
+
`query IndexedAgents($capability: String, $limit: Int, $minReputation: Float, $category: String) {
|
|
271
|
+
agents(capability: $capability, limit: $limit, minReputation: $minReputation, category: $category) {
|
|
272
|
+
nodes {
|
|
273
|
+
id
|
|
274
|
+
owner
|
|
275
|
+
did
|
|
276
|
+
name
|
|
277
|
+
description
|
|
278
|
+
endpoint
|
|
279
|
+
active
|
|
280
|
+
version
|
|
281
|
+
registeredAt
|
|
282
|
+
updatedAt
|
|
283
|
+
totalTasksCompleted
|
|
284
|
+
totalTasksFailed
|
|
285
|
+
totalTasksDisputed
|
|
286
|
+
totalEarningsMist
|
|
287
|
+
hasStake
|
|
288
|
+
stakeMist
|
|
289
|
+
stakeType
|
|
290
|
+
capabilities {
|
|
291
|
+
name
|
|
292
|
+
description
|
|
293
|
+
version
|
|
294
|
+
executionMode
|
|
295
|
+
paymentRails
|
|
296
|
+
pricing {
|
|
297
|
+
rail
|
|
298
|
+
amount
|
|
299
|
+
currency
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}`,
|
|
305
|
+
{
|
|
306
|
+
capability: params.capability,
|
|
307
|
+
limit: params.limit,
|
|
308
|
+
minReputation: params.minReputation,
|
|
309
|
+
category: params.category
|
|
310
|
+
}
|
|
311
|
+
);
|
|
312
|
+
return data.agents.nodes.map(mapGraphqlAgent);
|
|
313
|
+
}
|
|
314
|
+
function resolveIndexerUrl(context) {
|
|
315
|
+
const value = context.indexer?.graphqlUrl ?? process.env.COLLECTIVE_INDEXER_URL;
|
|
316
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
317
|
+
}
|
|
318
|
+
function mapGraphqlAgent(agent) {
|
|
319
|
+
return {
|
|
320
|
+
id: agent.id,
|
|
321
|
+
owner: agent.owner,
|
|
322
|
+
did: agent.did,
|
|
323
|
+
name: agent.name,
|
|
324
|
+
description: agent.description,
|
|
325
|
+
endpoint: agent.endpoint ?? void 0,
|
|
326
|
+
active: agent.active,
|
|
327
|
+
version: agent.version,
|
|
328
|
+
registeredAt: Number(agent.registeredAt),
|
|
329
|
+
updatedAt: Number(agent.updatedAt),
|
|
330
|
+
totalTasksCompleted: agent.totalTasksCompleted,
|
|
331
|
+
totalTasksFailed: agent.totalTasksFailed,
|
|
332
|
+
totalTasksDisputed: agent.totalTasksDisputed,
|
|
333
|
+
totalEarningsMist: BigInt(agent.totalEarningsMist),
|
|
334
|
+
hasStake: agent.hasStake,
|
|
335
|
+
stakeMist: agent.stakeMist ? BigInt(agent.stakeMist) : void 0,
|
|
336
|
+
stakeType: agent.stakeType ?? void 0,
|
|
337
|
+
capabilities: agent.capabilities.map((capability) => ({
|
|
338
|
+
name: capability.name,
|
|
339
|
+
description: capability.description,
|
|
340
|
+
version: capability.version,
|
|
341
|
+
executionMode: capability.executionMode ?? void 0,
|
|
342
|
+
paymentRails: capability.paymentRails ?? void 0,
|
|
343
|
+
pricing: {
|
|
344
|
+
rail: capability.pricing.rail,
|
|
345
|
+
amount: BigInt(capability.pricing.amount),
|
|
346
|
+
currency: capability.pricing.currency
|
|
347
|
+
}
|
|
348
|
+
}))
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// src/tools/analytics.ts
|
|
353
|
+
var meshAnalyticsTool = {
|
|
354
|
+
name: "collective_analytics",
|
|
355
|
+
description: "Query task volume, top providers, and marketplace analytics from the Agentic Mesh indexer",
|
|
356
|
+
inputSchema: {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
view: { type: "string", enum: ["summary", "task-volume", "top-providers", "marketplace"] },
|
|
360
|
+
period: { type: "string", enum: ["HOUR", "DAY", "WEEK"] },
|
|
361
|
+
buckets: { type: "number" },
|
|
362
|
+
limit: { type: "number" },
|
|
363
|
+
sort_by: { type: "string", enum: ["COMPLETED_TASKS", "EARNINGS", "REPUTATION"] }
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
async function runMeshAnalytics(params, context) {
|
|
368
|
+
const view = params.view ?? "summary";
|
|
369
|
+
switch (view) {
|
|
370
|
+
case "task-volume": {
|
|
371
|
+
const data = await executeIndexerQuery(
|
|
372
|
+
context,
|
|
373
|
+
`query TaskVolume($period: TimePeriod!, $buckets: Int) {
|
|
374
|
+
taskVolume(period: $period, buckets: $buckets) {
|
|
375
|
+
label
|
|
376
|
+
count
|
|
377
|
+
volumeMist
|
|
378
|
+
}
|
|
379
|
+
}`,
|
|
380
|
+
{
|
|
381
|
+
period: params.period ?? "DAY",
|
|
382
|
+
buckets: params.buckets ?? 14
|
|
383
|
+
}
|
|
384
|
+
);
|
|
385
|
+
return { view, period: params.period ?? "DAY", buckets: params.buckets ?? 14, data: data.taskVolume };
|
|
386
|
+
}
|
|
387
|
+
case "top-providers": {
|
|
388
|
+
const data = await executeIndexerQuery(
|
|
389
|
+
context,
|
|
390
|
+
`query TopProviders($limit: Int, $sortBy: ProviderSortField) {
|
|
391
|
+
topProviders(limit: $limit, sortBy: $sortBy) {
|
|
392
|
+
did
|
|
393
|
+
owner
|
|
394
|
+
name
|
|
395
|
+
completedTasks
|
|
396
|
+
earningsMist
|
|
397
|
+
disputeCount
|
|
398
|
+
successRate
|
|
399
|
+
reputation
|
|
400
|
+
}
|
|
401
|
+
}`,
|
|
402
|
+
{
|
|
403
|
+
limit: params.limit ?? 10,
|
|
404
|
+
sortBy: params.sort_by ?? "COMPLETED_TASKS"
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
return { view, limit: params.limit ?? 10, sort_by: params.sort_by ?? "COMPLETED_TASKS", data: data.topProviders };
|
|
408
|
+
}
|
|
409
|
+
case "marketplace": {
|
|
410
|
+
const data = await executeIndexerQuery(
|
|
411
|
+
context,
|
|
412
|
+
`query MarketplaceAnalytics {
|
|
413
|
+
analytics {
|
|
414
|
+
marketplace {
|
|
415
|
+
averageBidCount
|
|
416
|
+
acceptanceRate
|
|
417
|
+
categoryPopularity {
|
|
418
|
+
category
|
|
419
|
+
taskCount
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}`
|
|
424
|
+
);
|
|
425
|
+
return { view, data: data.analytics.marketplace };
|
|
426
|
+
}
|
|
427
|
+
case "summary":
|
|
428
|
+
default: {
|
|
429
|
+
const data = await executeIndexerQuery(
|
|
430
|
+
context,
|
|
431
|
+
`query AnalyticsSummary {
|
|
432
|
+
analytics {
|
|
433
|
+
totalAgents
|
|
434
|
+
activeAgents
|
|
435
|
+
totalTasks
|
|
436
|
+
completedTasks
|
|
437
|
+
disputedTasks
|
|
438
|
+
totalVolumeMist
|
|
439
|
+
averageGasCosts {
|
|
440
|
+
capability
|
|
441
|
+
averageGasMist
|
|
442
|
+
taskCount
|
|
443
|
+
}
|
|
444
|
+
marketplace {
|
|
445
|
+
averageBidCount
|
|
446
|
+
acceptanceRate
|
|
447
|
+
categoryPopularity {
|
|
448
|
+
category
|
|
449
|
+
taskCount
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}`
|
|
454
|
+
);
|
|
455
|
+
return { view: "summary", data: data.analytics };
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/tools/deactivate.ts
|
|
461
|
+
var meshDeactivateTool = {
|
|
462
|
+
name: "collective_deactivate",
|
|
463
|
+
description: "Deactivate an existing agent card",
|
|
464
|
+
inputSchema: {
|
|
465
|
+
type: "object",
|
|
466
|
+
properties: {
|
|
467
|
+
agent_card_id: { type: "string", description: "Agent card object id" }
|
|
468
|
+
},
|
|
469
|
+
required: ["agent_card_id"]
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
async function runMeshDeactivate(params, context) {
|
|
473
|
+
const result = await context.registryClient.deactivateAgent({
|
|
474
|
+
cardId: params.agent_card_id,
|
|
475
|
+
keypair: context.keypair
|
|
476
|
+
});
|
|
477
|
+
context.agentCache.removeAgent(params.agent_card_id);
|
|
478
|
+
return {
|
|
479
|
+
tx_digest: result.txDigest,
|
|
480
|
+
status: "deactivated"
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// src/tools/discover.ts
|
|
485
|
+
import { ReputationScoreCalculator } from "@hivemind-os/collective-core";
|
|
486
|
+
var scoreCalculator = new ReputationScoreCalculator();
|
|
487
|
+
var meshDiscoverTool = {
|
|
488
|
+
name: "collective_discover",
|
|
489
|
+
description: "Find agents by capability on the Agentic Mesh network",
|
|
490
|
+
inputSchema: {
|
|
491
|
+
type: "object",
|
|
492
|
+
properties: {
|
|
493
|
+
capability: { type: "string", description: "Capability name to search for" },
|
|
494
|
+
limit: { type: "number", description: "Max results (default 10)" },
|
|
495
|
+
sort_by: { type: "string", enum: ["price", "reputation"], description: "Sort results by price or reputation" },
|
|
496
|
+
useIndexer: { type: "boolean", description: "Prefer the indexer GraphQL API when available" }
|
|
497
|
+
},
|
|
498
|
+
required: ["capability"]
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
async function runMeshDiscover(params, context) {
|
|
502
|
+
const capability = params.capability.trim();
|
|
503
|
+
const limit = normalizeLimit(params.limit);
|
|
504
|
+
const sortBy = params.sort_by ?? "price";
|
|
505
|
+
const { agents, source } = await discoverAgentsByCapability(capability, context, limit, sortBy, params.useIndexer);
|
|
506
|
+
return {
|
|
507
|
+
capability,
|
|
508
|
+
source,
|
|
509
|
+
agents: agents.map((agent) => summarizeAgent(agent, capability))
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
async function discoverAgentsByCapability(capability, context, limit = 10, sortBy = "price", useIndexer) {
|
|
513
|
+
const normalizedCapability = capability.trim();
|
|
514
|
+
if (!normalizedCapability) {
|
|
515
|
+
return { agents: [], source: "cache" };
|
|
516
|
+
}
|
|
517
|
+
if (shouldUseIndexer(context, useIndexer)) {
|
|
518
|
+
try {
|
|
519
|
+
const indexed = await queryIndexerAgents(context, {
|
|
520
|
+
capability: normalizedCapability,
|
|
521
|
+
limit
|
|
522
|
+
});
|
|
523
|
+
if (indexed.length > 0) {
|
|
524
|
+
return {
|
|
525
|
+
agents: sortBy === "reputation" ? indexed.slice(0, limit) : sortByPrice(indexed, normalizedCapability).slice(0, limit),
|
|
526
|
+
source: "indexer"
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
} catch (error) {
|
|
530
|
+
context.logger?.warn?.({ err: error }, "Indexer discovery failed; falling back to local cache.");
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const cached = (await queryLocalCache(context, normalizedCapability, Math.max(limit * 2, limit), sortBy)).filter((agent) => hasCapability(agent, normalizedCapability)).slice(0, limit);
|
|
534
|
+
if (cached.length > 0) {
|
|
535
|
+
return {
|
|
536
|
+
agents: sortBy === "reputation" ? cached : sortByPrice(cached, normalizedCapability).slice(0, limit),
|
|
537
|
+
source: "cache"
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
const discovered = await context.registryClient.discoverByCapability(normalizedCapability, limit, {
|
|
541
|
+
sortByReputation: sortBy === "reputation"
|
|
542
|
+
});
|
|
543
|
+
for (const agent of discovered) {
|
|
544
|
+
context.agentCache.upsertAgent(agent);
|
|
545
|
+
}
|
|
546
|
+
const ranked = sortBy === "reputation" ? discovered : sortByPrice(discovered, normalizedCapability);
|
|
547
|
+
return { agents: ranked.slice(0, limit), source: "registry" };
|
|
548
|
+
}
|
|
549
|
+
async function resolveProviderCapability(capability, context, providerDid) {
|
|
550
|
+
const normalizedCapability = capability.trim();
|
|
551
|
+
if (!normalizedCapability) {
|
|
552
|
+
throw new Error("Capability is required.");
|
|
553
|
+
}
|
|
554
|
+
if (providerDid) {
|
|
555
|
+
const cachedAgent = context.agentCache.getAgentByDID(providerDid);
|
|
556
|
+
const cachedCapability = cachedAgent ? findCapability(cachedAgent, normalizedCapability) : void 0;
|
|
557
|
+
if (cachedAgent?.active && cachedCapability) {
|
|
558
|
+
return { agent: cachedAgent, capability: cachedCapability };
|
|
559
|
+
}
|
|
560
|
+
const { agents: agents2 } = await discoverAgentsByCapability(normalizedCapability, context, 50);
|
|
561
|
+
const matched = agents2.find((entry) => entry.did === providerDid);
|
|
562
|
+
const matchedCapability = matched ? findCapability(matched, normalizedCapability) : void 0;
|
|
563
|
+
if (!matched || !matchedCapability) {
|
|
564
|
+
throw new Error(`Provider ${providerDid} was not found for capability ${normalizedCapability}.`);
|
|
565
|
+
}
|
|
566
|
+
return { agent: matched, capability: matchedCapability };
|
|
567
|
+
}
|
|
568
|
+
const { agents } = await discoverAgentsByCapability(normalizedCapability, context, 20);
|
|
569
|
+
const ranked = agents.map((agent) => ({ agent, capability: findCapability(agent, normalizedCapability) })).filter((entry) => Boolean(entry.capability)).sort((left, right) => compareBigInt(left.capability.pricing.amount, right.capability.pricing.amount));
|
|
570
|
+
if (ranked.length === 0) {
|
|
571
|
+
throw new Error(`No providers found for capability ${normalizedCapability}.`);
|
|
572
|
+
}
|
|
573
|
+
return ranked[0];
|
|
574
|
+
}
|
|
575
|
+
function summarizeAgent(agent, capability) {
|
|
576
|
+
const scopedCapabilities = capability ? agent.capabilities.filter((entry) => capabilityNameEquals(entry.name, capability)) : agent.capabilities;
|
|
577
|
+
const reputation = scoreCalculator.computeScore(agent, []);
|
|
578
|
+
return {
|
|
579
|
+
name: agent.name,
|
|
580
|
+
did: agent.did,
|
|
581
|
+
capabilities: scopedCapabilities.map((entry) => entry.name),
|
|
582
|
+
pricing: scopedCapabilities.map((entry) => ({
|
|
583
|
+
capability: entry.name,
|
|
584
|
+
price_mist: entry.pricing.amount.toString(),
|
|
585
|
+
rail: entry.pricing.rail,
|
|
586
|
+
currency: entry.pricing.currency
|
|
587
|
+
})),
|
|
588
|
+
reputation: {
|
|
589
|
+
success_rate: reputation.successRate,
|
|
590
|
+
total_tasks: reputation.totalTasks,
|
|
591
|
+
total_disputes: reputation.totalDisputes,
|
|
592
|
+
total_earnings_mist: reputation.totalEarningsMist.toString()
|
|
593
|
+
},
|
|
594
|
+
endpoint: agent.endpoint
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
async function queryLocalCache(context, capability, limit, sortBy) {
|
|
598
|
+
const cache = context.agentCache;
|
|
599
|
+
if (typeof cache.queryAgentsAdvanced === "function") {
|
|
600
|
+
return await cache.queryAgentsAdvanced({
|
|
601
|
+
capability,
|
|
602
|
+
limit,
|
|
603
|
+
sortBy: sortBy === "reputation" ? "reputation" : "stake"
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return context.agentCache.searchByCapability(capability, limit, { sortByReputation: sortBy === "reputation" });
|
|
607
|
+
}
|
|
608
|
+
function shouldUseIndexer(context, useIndexer) {
|
|
609
|
+
if (useIndexer === false) {
|
|
610
|
+
return false;
|
|
611
|
+
}
|
|
612
|
+
return Boolean(resolveIndexerUrl(context));
|
|
613
|
+
}
|
|
614
|
+
function findCapability(agent, capability) {
|
|
615
|
+
return agent.capabilities.find((entry) => capabilityNameEquals(entry.name, capability));
|
|
616
|
+
}
|
|
617
|
+
function hasCapability(agent, capability) {
|
|
618
|
+
return findCapability(agent, capability) !== void 0;
|
|
619
|
+
}
|
|
620
|
+
function capabilityNameEquals(left, right) {
|
|
621
|
+
return left.toLowerCase() === right.toLowerCase();
|
|
622
|
+
}
|
|
623
|
+
function compareBigInt(left, right) {
|
|
624
|
+
if (left === right) {
|
|
625
|
+
return 0;
|
|
626
|
+
}
|
|
627
|
+
return left < right ? -1 : 1;
|
|
628
|
+
}
|
|
629
|
+
function normalizeLimit(limit) {
|
|
630
|
+
if (typeof limit !== "number" || Number.isNaN(limit)) {
|
|
631
|
+
return 10;
|
|
632
|
+
}
|
|
633
|
+
return Math.max(1, Math.floor(limit));
|
|
634
|
+
}
|
|
635
|
+
function sortByPrice(agents, capability) {
|
|
636
|
+
return [...agents].sort((left, right) => {
|
|
637
|
+
const leftCapability = findCapability(left, capability);
|
|
638
|
+
const rightCapability = findCapability(right, capability);
|
|
639
|
+
if (!leftCapability && !rightCapability) {
|
|
640
|
+
return 0;
|
|
641
|
+
}
|
|
642
|
+
if (!leftCapability) {
|
|
643
|
+
return 1;
|
|
644
|
+
}
|
|
645
|
+
if (!rightCapability) {
|
|
646
|
+
return -1;
|
|
647
|
+
}
|
|
648
|
+
return compareBigInt(leftCapability.pricing.amount, rightCapability.pricing.amount);
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
// src/tools/dispute.ts
|
|
653
|
+
import { DisputeClient } from "@hivemind-os/collective-core";
|
|
654
|
+
var meshDisputeTool = {
|
|
655
|
+
name: "collective_dispute",
|
|
656
|
+
description: "Open, respond to, accept, or inspect on-chain task disputes",
|
|
657
|
+
inputSchema: {
|
|
658
|
+
type: "object",
|
|
659
|
+
properties: {
|
|
660
|
+
action: { type: "string", enum: ["open", "respond", "accept", "status"] },
|
|
661
|
+
task_id: { type: "string", description: "Task object id" },
|
|
662
|
+
dispute_id: { type: "string", description: "Dispute object id" },
|
|
663
|
+
evidence: { type: "string", description: "Plain text or JSON-encoded evidence to store before opening/responding" },
|
|
664
|
+
evidence_blob_id: { type: "string", description: "Existing Walrus/blobstore blob id containing dispute evidence" },
|
|
665
|
+
proposed_split_mist: { type: "string", description: "Amount, in MIST, to allocate back to the requester" },
|
|
666
|
+
arbitrator_address: { type: "string", description: "Optional arbitrator address for open actions" }
|
|
667
|
+
},
|
|
668
|
+
required: ["action"]
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
async function runMeshDispute(params, context) {
|
|
672
|
+
const client = context.disputeClient ?? new DisputeClient(context.suiClient, context.networkConfig);
|
|
673
|
+
switch (params.action) {
|
|
674
|
+
case "open": {
|
|
675
|
+
if (!params.task_id) {
|
|
676
|
+
throw new Error("task_id is required when action=open");
|
|
677
|
+
}
|
|
678
|
+
const evidenceBlobId = await resolveEvidenceBlobId(params, context);
|
|
679
|
+
const result = await client.openDispute({
|
|
680
|
+
taskId: params.task_id,
|
|
681
|
+
evidenceBlobId,
|
|
682
|
+
proposedSplitMist: parseMist(params.proposed_split_mist, "proposed_split_mist"),
|
|
683
|
+
arbitratorAddress: params.arbitrator_address,
|
|
684
|
+
signer: context.keypair
|
|
685
|
+
});
|
|
686
|
+
return {
|
|
687
|
+
action: "open",
|
|
688
|
+
dispute_id: result.disputeId,
|
|
689
|
+
task_id: params.task_id,
|
|
690
|
+
evidence_blob_id: evidenceBlobId,
|
|
691
|
+
tx_digest: result.txDigest
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
case "respond": {
|
|
695
|
+
if (!params.dispute_id) {
|
|
696
|
+
throw new Error("dispute_id is required when action=respond");
|
|
697
|
+
}
|
|
698
|
+
const evidenceBlobId = await resolveEvidenceBlobId(params, context);
|
|
699
|
+
const result = await client.respondToDispute({
|
|
700
|
+
disputeId: params.dispute_id,
|
|
701
|
+
evidenceBlobId,
|
|
702
|
+
proposedSplitMist: parseMist(params.proposed_split_mist, "proposed_split_mist"),
|
|
703
|
+
signer: context.keypair
|
|
704
|
+
});
|
|
705
|
+
return {
|
|
706
|
+
action: "respond",
|
|
707
|
+
dispute_id: params.dispute_id,
|
|
708
|
+
evidence_blob_id: evidenceBlobId,
|
|
709
|
+
tx_digest: result.txDigest
|
|
710
|
+
};
|
|
711
|
+
}
|
|
712
|
+
case "accept": {
|
|
713
|
+
if (!params.dispute_id || !params.task_id) {
|
|
714
|
+
throw new Error("dispute_id and task_id are required when action=accept");
|
|
715
|
+
}
|
|
716
|
+
const result = await client.acceptResolution({
|
|
717
|
+
disputeId: params.dispute_id,
|
|
718
|
+
taskId: params.task_id,
|
|
719
|
+
signer: context.keypair
|
|
720
|
+
});
|
|
721
|
+
return {
|
|
722
|
+
action: "accept",
|
|
723
|
+
dispute_id: params.dispute_id,
|
|
724
|
+
task_id: params.task_id,
|
|
725
|
+
requester_amount_mist: result.requesterAmount.toString(),
|
|
726
|
+
provider_amount_mist: result.providerAmount.toString(),
|
|
727
|
+
tx_digest: result.txDigest
|
|
728
|
+
};
|
|
729
|
+
}
|
|
730
|
+
case "status": {
|
|
731
|
+
if ((params.dispute_id ? 1 : 0) + (params.task_id ? 1 : 0) !== 1) {
|
|
732
|
+
throw new Error("Provide exactly one of dispute_id or task_id when action=status");
|
|
733
|
+
}
|
|
734
|
+
const dispute = params.dispute_id ? await client.getDispute(params.dispute_id) : await client.getDisputeByTask(params.task_id);
|
|
735
|
+
if (!dispute) {
|
|
736
|
+
throw new Error(params.task_id ? `No dispute found for task ${params.task_id}.` : `Dispute ${params.dispute_id ?? ""} was not found.`);
|
|
737
|
+
}
|
|
738
|
+
return {
|
|
739
|
+
action: "status",
|
|
740
|
+
dispute
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
default:
|
|
744
|
+
throw new Error(`Unknown dispute action: ${String(params.action)}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
async function resolveEvidenceBlobId(params, context) {
|
|
748
|
+
if ((params.evidence_blob_id ? 1 : 0) + (params.evidence ? 1 : 0) !== 1) {
|
|
749
|
+
throw new Error("Provide exactly one of evidence or evidence_blob_id for dispute open/respond actions");
|
|
750
|
+
}
|
|
751
|
+
if (params.evidence_blob_id) {
|
|
752
|
+
return params.evidence_blob_id;
|
|
753
|
+
}
|
|
754
|
+
if (!params.evidence) {
|
|
755
|
+
throw new Error("evidence or evidence_blob_id is required for dispute open/respond actions");
|
|
756
|
+
}
|
|
757
|
+
const stored = await context.blobStore.store(new TextEncoder().encode(params.evidence));
|
|
758
|
+
return stored.blobId;
|
|
759
|
+
}
|
|
760
|
+
function parseMist(value, field) {
|
|
761
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
762
|
+
return BigInt(value);
|
|
763
|
+
}
|
|
764
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
765
|
+
return BigInt(value.trim());
|
|
766
|
+
}
|
|
767
|
+
throw new Error(`${field} must be a non-negative integer string.`);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// src/tools/execute.ts
|
|
771
|
+
import { PaymentRail as PaymentRail2, TaskStatus as TaskStatus2 } from "@hivemind-os/collective-types";
|
|
772
|
+
import { PaymentRailSelector, RelayConsumerClient, ReputationEventPublisher } from "@hivemind-os/collective-core";
|
|
773
|
+
var encoder = new TextEncoder();
|
|
774
|
+
var decoder2 = new TextDecoder();
|
|
775
|
+
var DEFAULT_TIMEOUT_SECONDS = 120;
|
|
776
|
+
var DEFAULT_DISPUTE_WINDOW_MS = 5 * 6e4;
|
|
777
|
+
var DEFAULT_EXPIRY_HOURS = 24;
|
|
778
|
+
var meshExecuteTool = {
|
|
779
|
+
name: "collective_execute",
|
|
780
|
+
description: "Execute a mesh task. Returns a task handle (async) if the client supports MCP Tasks, otherwise blocks until completion.",
|
|
781
|
+
inputSchema: {
|
|
782
|
+
type: "object",
|
|
783
|
+
properties: {
|
|
784
|
+
capability: { type: "string", description: "Capability name to execute" },
|
|
785
|
+
provider_did: { type: "string", description: "Specific provider DID to use" },
|
|
786
|
+
input: { type: "string", description: "Task input payload" },
|
|
787
|
+
max_price_mist: { type: "number", description: "Maximum spend in MIST" },
|
|
788
|
+
timeout_seconds: { type: "number", description: "Polling timeout in seconds (default 120)" },
|
|
789
|
+
mode: { type: "string", enum: ["auto", "sync", "async"], description: "Execution preference (default auto)" }
|
|
790
|
+
},
|
|
791
|
+
required: ["capability", "input"]
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
async function runMeshExecute(params, context) {
|
|
795
|
+
const resolved = await resolveProviderCapability(params.capability, context, params.provider_did);
|
|
796
|
+
const priceMist = resolved.capability.pricing.amount;
|
|
797
|
+
const maxPrice = toOptionalBigInt(params.max_price_mist);
|
|
798
|
+
if (maxPrice !== void 0 && priceMist > maxPrice) {
|
|
799
|
+
throw new Error(`Provider price ${priceMist.toString()} exceeds max_price_mist ${maxPrice.toString()}.`);
|
|
800
|
+
}
|
|
801
|
+
const mode = params.mode ?? "auto";
|
|
802
|
+
if (mode !== "async") {
|
|
803
|
+
const relayResult = await tryRelayExecution(params, context, resolved, priceMist);
|
|
804
|
+
if (relayResult) {
|
|
805
|
+
await publishSuccessfulReputationEvent(context, {
|
|
806
|
+
providerDid: relayResult.provider_did,
|
|
807
|
+
taskId: relayResult.task_id,
|
|
808
|
+
capability: resolved.capability.name,
|
|
809
|
+
priceMist,
|
|
810
|
+
latencyMs: relayResult.latency_ms
|
|
811
|
+
});
|
|
812
|
+
return relayResult;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const prepared = await prepareAsyncExecution(params, context, resolved, priceMist);
|
|
816
|
+
const task = await waitForTaskCompletion(prepared.taskId, context, params.timeout_seconds);
|
|
817
|
+
if (!task.resultBlobId) {
|
|
818
|
+
throw new Error(`Task ${prepared.taskId} completed without a result blob.`);
|
|
819
|
+
}
|
|
820
|
+
const resultBytes = await fetchMeshBlob(context.blobStore, task.resultBlobId);
|
|
821
|
+
if (!resultBytes) {
|
|
822
|
+
throw new Error(`Result blob ${task.resultBlobId} was not found.`);
|
|
823
|
+
}
|
|
824
|
+
await context.taskClient.releasePayment({
|
|
825
|
+
taskId: prepared.taskId,
|
|
826
|
+
keypair: context.keypair
|
|
827
|
+
});
|
|
828
|
+
context.spendingPolicy.record({
|
|
829
|
+
amountMist: prepared.priceMist,
|
|
830
|
+
rail: prepared.rail,
|
|
831
|
+
taskId: prepared.taskId,
|
|
832
|
+
appId: prepared.providerDid,
|
|
833
|
+
originAppName: context.originAppName
|
|
834
|
+
});
|
|
835
|
+
await publishSuccessfulReputationEvent(context, {
|
|
836
|
+
providerDid: prepared.providerDid,
|
|
837
|
+
taskId: prepared.taskId,
|
|
838
|
+
capability: resolved.capability.name,
|
|
839
|
+
priceMist: prepared.priceMist,
|
|
840
|
+
latencyMs: task.acceptedAt && task.completedAt ? task.completedAt - task.acceptedAt : void 0
|
|
841
|
+
});
|
|
842
|
+
return {
|
|
843
|
+
task_id: prepared.taskId,
|
|
844
|
+
result: decoder2.decode(resultBytes),
|
|
845
|
+
provider_did: prepared.providerDid,
|
|
846
|
+
price_mist: prepared.priceMist.toString(),
|
|
847
|
+
status: TaskStatus2[TaskStatus2.RELEASED],
|
|
848
|
+
execution_mode: "async",
|
|
849
|
+
payment_rail: prepared.rail
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
async function prepareMeshExecution(params, context) {
|
|
853
|
+
const resolved = await resolveProviderCapability(params.capability, context, params.provider_did);
|
|
854
|
+
return prepareAsyncExecution(params, context, resolved, resolved.capability.pricing.amount);
|
|
855
|
+
}
|
|
856
|
+
async function prepareAsyncExecution(params, context, resolved, priceMist) {
|
|
857
|
+
if (resolved.capability.pricing.rail !== PaymentRail2.SUI_ESCROW) {
|
|
858
|
+
throw new Error(`Capability ${resolved.capability.name} does not support SUI escrow execution.`);
|
|
859
|
+
}
|
|
860
|
+
approveSpend(context, priceMist, resolved.capability.pricing.rail, resolved.agent.did);
|
|
861
|
+
const { blobId } = await storeTaskInput(context, resolved.agent, encoder.encode(params.input));
|
|
862
|
+
const posted = await context.taskClient.postTask({
|
|
863
|
+
capability: resolved.capability.name,
|
|
864
|
+
category: "general",
|
|
865
|
+
inputBlobId: blobId,
|
|
866
|
+
priceMist,
|
|
867
|
+
disputeWindowMs: DEFAULT_DISPUTE_WINDOW_MS,
|
|
868
|
+
expiryHours: DEFAULT_EXPIRY_HOURS,
|
|
869
|
+
keypair: context.keypair
|
|
870
|
+
});
|
|
871
|
+
return {
|
|
872
|
+
taskId: posted.taskId,
|
|
873
|
+
providerDid: resolved.agent.did,
|
|
874
|
+
priceMist,
|
|
875
|
+
rail: resolved.capability.pricing.rail
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
async function tryRelayExecution(params, context, resolved, priceMist) {
|
|
879
|
+
const relayUrl = getRelayUrl(resolved.agent);
|
|
880
|
+
if (!relayUrl || !context.relayAuthProvider) {
|
|
881
|
+
return null;
|
|
882
|
+
}
|
|
883
|
+
try {
|
|
884
|
+
const selector = context.paymentRailSelector ?? new PaymentRailSelector();
|
|
885
|
+
const selectedRail = selector.selectRail({
|
|
886
|
+
executionMode: "sync",
|
|
887
|
+
consumerHasSuiWallet: Boolean(context.relayAuthProvider),
|
|
888
|
+
consumerHasEvmWallet: Boolean(context.x402Client),
|
|
889
|
+
providerAcceptsSui: providerAcceptsSui(resolved.capability),
|
|
890
|
+
providerAcceptsX402: providerAcceptsX402(resolved.agent, resolved.capability),
|
|
891
|
+
amount: priceMist,
|
|
892
|
+
currency: resolved.capability.pricing.currency
|
|
893
|
+
});
|
|
894
|
+
const rail = toPaymentRail(selectedRail);
|
|
895
|
+
approveSpend(context, priceMist, rail, resolved.agent.did);
|
|
896
|
+
const client = new RelayConsumerClient(context.x402Client ?? null, context.relayAuthProvider, { relayUrl });
|
|
897
|
+
const response = await client.executeSync({
|
|
898
|
+
providerDid: resolved.agent.did,
|
|
899
|
+
capability: resolved.capability.name,
|
|
900
|
+
input: params.input,
|
|
901
|
+
paymentRail: rail,
|
|
902
|
+
timeoutMs: normalizeTimeoutMs(params.timeout_seconds)
|
|
903
|
+
});
|
|
904
|
+
const taskId = response.taskId ?? `relay-${resolved.agent.did}-${Date.now()}`;
|
|
905
|
+
context.spendingPolicy.record({
|
|
906
|
+
amountMist: priceMist,
|
|
907
|
+
rail,
|
|
908
|
+
taskId,
|
|
909
|
+
appId: resolved.agent.did,
|
|
910
|
+
originAppName: context.originAppName
|
|
911
|
+
});
|
|
912
|
+
return {
|
|
913
|
+
task_id: taskId,
|
|
914
|
+
result: stringifyRelayResult(response.result),
|
|
915
|
+
provider_did: response.providerDid ?? resolved.agent.did,
|
|
916
|
+
price_mist: priceMist.toString(),
|
|
917
|
+
status: "COMPLETED",
|
|
918
|
+
execution_mode: "sync",
|
|
919
|
+
payment_rail: rail,
|
|
920
|
+
payment_receipt: response.paymentReceipt,
|
|
921
|
+
latency_ms: response.latencyMs
|
|
922
|
+
};
|
|
923
|
+
} catch (error) {
|
|
924
|
+
if (!shouldFallbackToAsync(error)) {
|
|
925
|
+
throw error;
|
|
926
|
+
}
|
|
927
|
+
context.logger?.warn?.(
|
|
928
|
+
{ err: error, providerDid: resolved.agent.did, capability: resolved.capability.name },
|
|
929
|
+
"Relay execution unavailable; falling back to async Sui flow."
|
|
930
|
+
);
|
|
931
|
+
return null;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
async function waitForTaskCompletion(taskId, context, timeoutSeconds = DEFAULT_TIMEOUT_SECONDS) {
|
|
935
|
+
const timeoutMs = normalizeTimeoutMs(timeoutSeconds);
|
|
936
|
+
const startedAt = Date.now();
|
|
937
|
+
while (true) {
|
|
938
|
+
const task = await context.taskClient.getTask(taskId);
|
|
939
|
+
if (!task) {
|
|
940
|
+
throw new Error(`Task ${taskId} was not found.`);
|
|
941
|
+
}
|
|
942
|
+
if (task.status === TaskStatus2.COMPLETED || task.status === TaskStatus2.RELEASED) {
|
|
943
|
+
return task;
|
|
944
|
+
}
|
|
945
|
+
if (task.status === TaskStatus2.CANCELLED || task.status === TaskStatus2.DISPUTED) {
|
|
946
|
+
throw new Error(`Task ${taskId} ended with status ${taskStatusLabel(task.status)}.`);
|
|
947
|
+
}
|
|
948
|
+
const elapsedMs = Date.now() - startedAt;
|
|
949
|
+
if (elapsedMs >= timeoutMs) {
|
|
950
|
+
throw new Error(`Timed out waiting for task ${taskId} to complete.`);
|
|
951
|
+
}
|
|
952
|
+
const remainingMs = timeoutMs - elapsedMs;
|
|
953
|
+
await delay(Math.min(remainingMs, 1e3));
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
function approveSpend(context, amountMist, rail, appId) {
|
|
957
|
+
const decision = context.spendingPolicy.evaluate({
|
|
958
|
+
amountMist,
|
|
959
|
+
rail,
|
|
960
|
+
appId,
|
|
961
|
+
originAppName: context.originAppName
|
|
962
|
+
});
|
|
963
|
+
if (!decision.approved) {
|
|
964
|
+
throw new Error(decision.reason ?? "Spending policy rejected the request.");
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
function getRelayUrl(agent) {
|
|
968
|
+
const relayEndpoint = agent.relayEndpoints?.find((endpoint) => !endpoint.modes || endpoint.modes.includes("sync"));
|
|
969
|
+
if (relayEndpoint?.endpoint) {
|
|
970
|
+
return relayEndpoint.endpoint;
|
|
971
|
+
}
|
|
972
|
+
if (agent.endpoint && /^(https?|wss?):\/\//i.test(agent.endpoint)) {
|
|
973
|
+
return agent.endpoint;
|
|
974
|
+
}
|
|
975
|
+
return void 0;
|
|
976
|
+
}
|
|
977
|
+
function providerAcceptsSui(capability) {
|
|
978
|
+
return capability.paymentRails?.includes(PaymentRail2.SUI_TRANSFER) ?? capability.pricing.rail !== PaymentRail2.X402_BASE;
|
|
979
|
+
}
|
|
980
|
+
function providerAcceptsX402(agent, capability) {
|
|
981
|
+
return capability.paymentRails?.includes(PaymentRail2.X402_BASE) ?? Boolean(getRelayUrl(agent));
|
|
982
|
+
}
|
|
983
|
+
function toPaymentRail(selectedRail) {
|
|
984
|
+
switch (selectedRail) {
|
|
985
|
+
case "sui-escrow":
|
|
986
|
+
return PaymentRail2.SUI_ESCROW;
|
|
987
|
+
case "sui-transfer":
|
|
988
|
+
return PaymentRail2.SUI_TRANSFER;
|
|
989
|
+
case "x402-base":
|
|
990
|
+
return PaymentRail2.X402_BASE;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function shouldFallbackToAsync(error) {
|
|
994
|
+
if (!(error instanceof Error)) {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
return /relay request timed out|fetch failed|provider .* is not connected to the relay|provider not found|payment challenge required|relay execution unavailable/i.test(
|
|
998
|
+
error.message
|
|
999
|
+
);
|
|
1000
|
+
}
|
|
1001
|
+
function stringifyRelayResult(result) {
|
|
1002
|
+
return typeof result === "string" ? result : JSON.stringify(result);
|
|
1003
|
+
}
|
|
1004
|
+
function normalizeTimeoutMs(timeoutSeconds) {
|
|
1005
|
+
if (typeof timeoutSeconds !== "number" || Number.isNaN(timeoutSeconds)) {
|
|
1006
|
+
return DEFAULT_TIMEOUT_SECONDS * 1e3;
|
|
1007
|
+
}
|
|
1008
|
+
return Math.max(0, Math.floor(timeoutSeconds * 1e3));
|
|
1009
|
+
}
|
|
1010
|
+
function toOptionalBigInt(value) {
|
|
1011
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
1012
|
+
return void 0;
|
|
1013
|
+
}
|
|
1014
|
+
return BigInt(Math.max(0, Math.floor(value)));
|
|
1015
|
+
}
|
|
1016
|
+
async function storeTaskInput(context, provider, input) {
|
|
1017
|
+
const providerEncryptionKey = hexToBytes(provider.encryptionPublicKey);
|
|
1018
|
+
const encryptionEnabled = context.encryption?.enabled ?? supportsEncryptedBlobs(context.blobStore);
|
|
1019
|
+
const requireEncryption = context.encryption?.requireEncryption ?? false;
|
|
1020
|
+
if (encryptionEnabled && providerEncryptionKey) {
|
|
1021
|
+
if (!supportsEncryptedBlobs(context.blobStore)) {
|
|
1022
|
+
throw new Error("Encryption is enabled, but the configured blobstore does not support encrypted payloads.");
|
|
1023
|
+
}
|
|
1024
|
+
return await context.blobStore.storeEncrypted(input, providerEncryptionKey);
|
|
1025
|
+
}
|
|
1026
|
+
if (requireEncryption) {
|
|
1027
|
+
throw new Error(`Provider ${provider.did} does not publish an encryption key.`);
|
|
1028
|
+
}
|
|
1029
|
+
return await context.blobStore.store(input);
|
|
1030
|
+
}
|
|
1031
|
+
function taskStatusLabel(status) {
|
|
1032
|
+
return TaskStatus2[status] ?? "UNKNOWN";
|
|
1033
|
+
}
|
|
1034
|
+
async function publishSuccessfulReputationEvent(context, params) {
|
|
1035
|
+
const publisher = context.reputationPublisher ?? (hasSigningIdentity(context.relayAuthProvider) ? new ReputationEventPublisher(context.blobStore, context.relayAuthProvider) : null);
|
|
1036
|
+
if (!publisher) {
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
try {
|
|
1040
|
+
const event = await publisher.createEvent({
|
|
1041
|
+
type: "task_completion",
|
|
1042
|
+
subject: params.providerDid,
|
|
1043
|
+
taskId: params.taskId,
|
|
1044
|
+
outcome: "success",
|
|
1045
|
+
capability: params.capability,
|
|
1046
|
+
latencyMs: params.latencyMs,
|
|
1047
|
+
paymentAmount: {
|
|
1048
|
+
amount: params.priceMist.toString(),
|
|
1049
|
+
currency: "MIST"
|
|
1050
|
+
}
|
|
1051
|
+
});
|
|
1052
|
+
await context.reputationStore?.addEvent(event);
|
|
1053
|
+
await publisher.publishEvent(event);
|
|
1054
|
+
} catch (error) {
|
|
1055
|
+
context.logger?.warn?.({ err: error, taskId: params.taskId }, "Failed to publish reputation event.");
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
function hasSigningIdentity(identity) {
|
|
1059
|
+
return Boolean(identity && typeof identity.getDID === "function" && typeof identity.signPersonalMessage === "function");
|
|
1060
|
+
}
|
|
1061
|
+
function delay(ms) {
|
|
1062
|
+
return new Promise((resolvePromise) => {
|
|
1063
|
+
setTimeout(resolvePromise, ms);
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// src/tools/execute-async.ts
|
|
1068
|
+
var meshExecuteAsyncTool = {
|
|
1069
|
+
name: "collective_execute_async",
|
|
1070
|
+
description: "Submit a mesh task and return immediately",
|
|
1071
|
+
inputSchema: {
|
|
1072
|
+
type: "object",
|
|
1073
|
+
properties: {
|
|
1074
|
+
capability: { type: "string", description: "Capability name to execute" },
|
|
1075
|
+
provider_did: { type: "string", description: "Specific provider DID to use" },
|
|
1076
|
+
input: { type: "string", description: "Task input payload" },
|
|
1077
|
+
max_price_mist: { type: "number", description: "Maximum spend in MIST" }
|
|
1078
|
+
},
|
|
1079
|
+
required: ["capability", "input"]
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
async function runMeshExecuteAsync(params, context) {
|
|
1083
|
+
const prepared = await prepareMeshExecution(params, context);
|
|
1084
|
+
return {
|
|
1085
|
+
task_id: prepared.taskId,
|
|
1086
|
+
provider_did: prepared.providerDid,
|
|
1087
|
+
price_mist: prepared.priceMist.toString(),
|
|
1088
|
+
status: "OPEN"
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
// src/tools/metering.ts
|
|
1093
|
+
import { decodeMeteredResult, getMeteredResultUnits, parseMeteredResultEnvelope, ResultVerifier } from "@hivemind-os/collective-core";
|
|
1094
|
+
import { PaymentRail as PaymentRail3, PaymentScheme, TaskStatus as TaskStatus3 } from "@hivemind-os/collective-types";
|
|
1095
|
+
var encoder2 = new TextEncoder();
|
|
1096
|
+
var decoder3 = new TextDecoder();
|
|
1097
|
+
var DEFAULT_TIMEOUT_SECONDS2 = 120;
|
|
1098
|
+
var DEFAULT_DISPUTE_WINDOW_MS2 = 5 * 6e4;
|
|
1099
|
+
var DEFAULT_EXPIRY_HOURS2 = 24;
|
|
1100
|
+
var meshMeteredExecuteTool = {
|
|
1101
|
+
name: "collective_metered_execute",
|
|
1102
|
+
description: "Execute a metered mesh task with capped escrow and result verification",
|
|
1103
|
+
inputSchema: {
|
|
1104
|
+
type: "object",
|
|
1105
|
+
properties: {
|
|
1106
|
+
capability: { type: "string", description: "Capability name to execute" },
|
|
1107
|
+
provider_did: { type: "string", description: "Specific provider DID to use" },
|
|
1108
|
+
input: { type: "string", description: "Task input payload" },
|
|
1109
|
+
max_price_mist: { type: "number", description: "Maximum escrow in MIST" },
|
|
1110
|
+
unit_price_mist: { type: "number", description: "Price per metered unit in MIST" },
|
|
1111
|
+
timeout_seconds: { type: "number", description: "Polling timeout in seconds (default 120)" }
|
|
1112
|
+
},
|
|
1113
|
+
required: ["capability", "input", "max_price_mist", "unit_price_mist"]
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
var meshVerifyResultTool = {
|
|
1117
|
+
name: "collective_verify_result",
|
|
1118
|
+
description: "Verify a metered task result blob against its on-chain hash chain root",
|
|
1119
|
+
inputSchema: {
|
|
1120
|
+
type: "object",
|
|
1121
|
+
properties: {
|
|
1122
|
+
task_id: { type: "string", description: "Task object id" }
|
|
1123
|
+
},
|
|
1124
|
+
required: ["task_id"]
|
|
1125
|
+
}
|
|
1126
|
+
};
|
|
1127
|
+
async function runMeshMeteredExecute(params, context) {
|
|
1128
|
+
const resolved = await resolveProviderCapability(params.capability, context, params.provider_did);
|
|
1129
|
+
if (resolved.capability.pricing.rail !== PaymentRail3.SUI_ESCROW) {
|
|
1130
|
+
throw new Error(`Capability ${resolved.capability.name} does not support SUI escrow execution.`);
|
|
1131
|
+
}
|
|
1132
|
+
const maxPriceMist = toRequiredBigInt(params.max_price_mist, "max_price_mist");
|
|
1133
|
+
const unitPriceMist = toRequiredBigInt(params.unit_price_mist, "unit_price_mist");
|
|
1134
|
+
const spendingDecision = context.spendingPolicy.evaluate({
|
|
1135
|
+
amountMist: maxPriceMist,
|
|
1136
|
+
rail: PaymentRail3.SUI_ESCROW,
|
|
1137
|
+
appId: resolved.agent.did,
|
|
1138
|
+
originAppName: context.originAppName
|
|
1139
|
+
});
|
|
1140
|
+
if (!spendingDecision.approved) {
|
|
1141
|
+
throw new Error(spendingDecision.reason ?? "Spending policy rejected the request.");
|
|
1142
|
+
}
|
|
1143
|
+
const inputBlob = await storeTaskInput2(context, resolved.agent, encoder2.encode(params.input));
|
|
1144
|
+
const posted = await context.taskClient.postMeteredTask({
|
|
1145
|
+
capability: resolved.capability.name,
|
|
1146
|
+
category: "general",
|
|
1147
|
+
inputBlobId: inputBlob.blobId,
|
|
1148
|
+
agreementHash: `metered:${resolved.capability.name}`,
|
|
1149
|
+
maxPriceMist,
|
|
1150
|
+
unitPriceMist,
|
|
1151
|
+
disputeWindowMs: DEFAULT_DISPUTE_WINDOW_MS2,
|
|
1152
|
+
expiryHours: DEFAULT_EXPIRY_HOURS2,
|
|
1153
|
+
keypair: context.keypair
|
|
1154
|
+
});
|
|
1155
|
+
const task = await waitForTaskCompletion(posted.taskId, context, params.timeout_seconds ?? DEFAULT_TIMEOUT_SECONDS2);
|
|
1156
|
+
const verification = await verifyMeteredTaskResult(task.id, context);
|
|
1157
|
+
await context.taskClient.releaseMeteredPayment({
|
|
1158
|
+
taskId: task.id,
|
|
1159
|
+
keypair: context.keypair
|
|
1160
|
+
});
|
|
1161
|
+
context.spendingPolicy.record({
|
|
1162
|
+
amountMist: task.price,
|
|
1163
|
+
rail: PaymentRail3.SUI_ESCROW,
|
|
1164
|
+
taskId: task.id,
|
|
1165
|
+
appId: resolved.agent.did,
|
|
1166
|
+
originAppName: context.originAppName
|
|
1167
|
+
});
|
|
1168
|
+
return {
|
|
1169
|
+
task_id: task.id,
|
|
1170
|
+
provider_did: resolved.agent.did,
|
|
1171
|
+
result: verification.result,
|
|
1172
|
+
status: TaskStatus3[TaskStatus3.RELEASED],
|
|
1173
|
+
payment_rail: PaymentRail3.SUI_ESCROW,
|
|
1174
|
+
payment_scheme: PaymentScheme.UPTO,
|
|
1175
|
+
max_price_mist: (task.maxPrice ?? maxPriceMist).toString(),
|
|
1176
|
+
actual_price_mist: task.price.toString(),
|
|
1177
|
+
unit_price_mist: (task.unitPrice ?? unitPriceMist).toString(),
|
|
1178
|
+
metered_units: task.meteredUnits ?? verification.metered_units,
|
|
1179
|
+
verification_hash: verification.verification_hash,
|
|
1180
|
+
verified: verification.verified
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
async function runMeshVerifyResult(params, context) {
|
|
1184
|
+
return await verifyMeteredTaskResult(params.task_id, context);
|
|
1185
|
+
}
|
|
1186
|
+
async function verifyMeteredTaskResult(taskId, context) {
|
|
1187
|
+
const task = await context.taskClient.getTask(taskId);
|
|
1188
|
+
if (!task) {
|
|
1189
|
+
throw new Error(`Task ${taskId} was not found.`);
|
|
1190
|
+
}
|
|
1191
|
+
if (!task.resultBlobId) {
|
|
1192
|
+
throw new Error(`Task ${taskId} does not have a result blob.`);
|
|
1193
|
+
}
|
|
1194
|
+
const resultBytes = await fetchMeshBlob(context.blobStore, task.resultBlobId);
|
|
1195
|
+
if (!resultBytes) {
|
|
1196
|
+
throw new Error(`Result blob ${task.resultBlobId} was not found.`);
|
|
1197
|
+
}
|
|
1198
|
+
const envelope = parseMeteredResultEnvelope(resultBytes);
|
|
1199
|
+
if (!envelope) {
|
|
1200
|
+
throw new Error(`Result blob ${task.resultBlobId} is not a metered result envelope.`);
|
|
1201
|
+
}
|
|
1202
|
+
const verifier = new ResultVerifier();
|
|
1203
|
+
const verified = verifier.verify(task, envelope.proof, getMeteredResultUnits(envelope));
|
|
1204
|
+
return {
|
|
1205
|
+
task_id: task.id,
|
|
1206
|
+
verified,
|
|
1207
|
+
verification_hash: task.verificationHash ?? envelope.proof.root,
|
|
1208
|
+
metered_units: task.meteredUnits ?? envelope.proof.unitCount,
|
|
1209
|
+
result: decoder3.decode(decodeMeteredResult(envelope))
|
|
1210
|
+
};
|
|
1211
|
+
}
|
|
1212
|
+
function toRequiredBigInt(value, name) {
|
|
1213
|
+
if (typeof value !== "number" || Number.isNaN(value) || value < 0) {
|
|
1214
|
+
throw new Error(`Invalid ${name}.`);
|
|
1215
|
+
}
|
|
1216
|
+
return BigInt(Math.floor(value));
|
|
1217
|
+
}
|
|
1218
|
+
async function storeTaskInput2(context, provider, input) {
|
|
1219
|
+
const providerEncryptionKey = hexToBytes(provider.encryptionPublicKey) ?? void 0;
|
|
1220
|
+
const encryptionEnabled = context.encryption?.enabled ?? supportsEncryptedBlobs(context.blobStore);
|
|
1221
|
+
const requireEncryption = context.encryption?.requireEncryption ?? false;
|
|
1222
|
+
if (encryptionEnabled && providerEncryptionKey) {
|
|
1223
|
+
if (!supportsEncryptedBlobs(context.blobStore)) {
|
|
1224
|
+
throw new Error("Encryption is enabled, but the configured blobstore does not support encrypted payloads.");
|
|
1225
|
+
}
|
|
1226
|
+
return await context.blobStore.storeEncrypted(input, providerEncryptionKey);
|
|
1227
|
+
}
|
|
1228
|
+
if (requireEncryption) {
|
|
1229
|
+
throw new Error(`Provider ${provider.did} does not publish an encryption key.`);
|
|
1230
|
+
}
|
|
1231
|
+
return await context.blobStore.store(input);
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// src/tools/marketplace-accept-bid.ts
|
|
1235
|
+
import { MarketplaceClient } from "@hivemind-os/collective-core";
|
|
1236
|
+
var meshMarketplaceAcceptBidTool = {
|
|
1237
|
+
name: "collective_marketplace_accept_bid",
|
|
1238
|
+
description: "Accept a marketplace bid and optionally reject competing bids",
|
|
1239
|
+
inputSchema: {
|
|
1240
|
+
type: "object",
|
|
1241
|
+
properties: {
|
|
1242
|
+
task_id: { type: "string", description: "Marketplace task id" },
|
|
1243
|
+
bid_id: { type: "string", description: "Bid id to accept" },
|
|
1244
|
+
reject_competing: { type: "boolean", description: "Reject other active bids in the same transaction (default true)" }
|
|
1245
|
+
},
|
|
1246
|
+
required: ["task_id", "bid_id"]
|
|
1247
|
+
}
|
|
1248
|
+
};
|
|
1249
|
+
async function runMeshMarketplaceAcceptBid(params, context) {
|
|
1250
|
+
const client = context.marketplaceClient ?? new MarketplaceClient(context.suiClient, context.networkConfig);
|
|
1251
|
+
const result = await client.acceptBid({
|
|
1252
|
+
taskId: params.task_id,
|
|
1253
|
+
bidId: params.bid_id,
|
|
1254
|
+
rejectCompeting: params.reject_competing,
|
|
1255
|
+
signer: context.keypair
|
|
1256
|
+
});
|
|
1257
|
+
return {
|
|
1258
|
+
task_id: params.task_id,
|
|
1259
|
+
bid_id: params.bid_id,
|
|
1260
|
+
rejected_bid_ids: result.rejectedBidIds,
|
|
1261
|
+
tx_digest: result.txDigest
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
// src/tools/marketplace-bid.ts
|
|
1266
|
+
import { MarketplaceClient as MarketplaceClient2 } from "@hivemind-os/collective-core";
|
|
1267
|
+
var meshMarketplaceBidTool = {
|
|
1268
|
+
name: "collective_marketplace_bid",
|
|
1269
|
+
description: "Place a marketplace bid on an open task",
|
|
1270
|
+
inputSchema: {
|
|
1271
|
+
type: "object",
|
|
1272
|
+
properties: {
|
|
1273
|
+
task_id: { type: "string", description: "Open task object id" },
|
|
1274
|
+
bid_price_mist: { type: "string", description: "Bid price in MIST" },
|
|
1275
|
+
reputation_score: { type: "string", description: "Optional reputation score override" },
|
|
1276
|
+
evidence: { type: "string", description: "Optional proposal or qualifications blob content" }
|
|
1277
|
+
},
|
|
1278
|
+
required: ["task_id", "bid_price_mist"]
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
async function runMeshMarketplaceBid(params, context) {
|
|
1282
|
+
const client = context.marketplaceClient ?? new MarketplaceClient2(context.suiClient, context.networkConfig);
|
|
1283
|
+
const result = await client.placeBid({
|
|
1284
|
+
taskId: params.task_id,
|
|
1285
|
+
bidPriceMist: parseMist2(params.bid_price_mist, "bid_price_mist"),
|
|
1286
|
+
reputationScore: params.reputation_score == null ? void 0 : parseMist2(params.reputation_score, "reputation_score"),
|
|
1287
|
+
evidenceBlob: params.evidence,
|
|
1288
|
+
signer: context.keypair
|
|
1289
|
+
});
|
|
1290
|
+
return {
|
|
1291
|
+
bid_id: result.bidId,
|
|
1292
|
+
task_id: params.task_id,
|
|
1293
|
+
tx_digest: result.txDigest,
|
|
1294
|
+
reputation_score: result.reputationScore.toString()
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
function parseMist2(value, field) {
|
|
1298
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
1299
|
+
return BigInt(value);
|
|
1300
|
+
}
|
|
1301
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
1302
|
+
return BigInt(value.trim());
|
|
1303
|
+
}
|
|
1304
|
+
throw new Error(`${field} must be a non-negative integer string.`);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
// src/tools/marketplace-browse.ts
|
|
1308
|
+
import { MarketplaceClient as MarketplaceClient3 } from "@hivemind-os/collective-core";
|
|
1309
|
+
var meshMarketplaceBrowseTool = {
|
|
1310
|
+
name: "collective_marketplace_browse",
|
|
1311
|
+
description: "Browse open marketplace tasks by category and price filters",
|
|
1312
|
+
inputSchema: {
|
|
1313
|
+
type: "object",
|
|
1314
|
+
properties: {
|
|
1315
|
+
category: { type: "string", description: "Optional marketplace category filter" },
|
|
1316
|
+
min_price_mist: { type: "string", description: "Optional minimum escrow budget in MIST" },
|
|
1317
|
+
max_price_mist: { type: "string", description: "Optional maximum escrow budget in MIST" },
|
|
1318
|
+
limit: { type: "number", description: "Maximum number of open tasks to return (default 20)" }
|
|
1319
|
+
},
|
|
1320
|
+
required: []
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
async function runMeshMarketplaceBrowse(params, context) {
|
|
1324
|
+
const client = context.marketplaceClient ?? new MarketplaceClient3(context.suiClient, context.networkConfig);
|
|
1325
|
+
const tasks = await client.browseOpenTasks({
|
|
1326
|
+
category: params.category,
|
|
1327
|
+
minPriceMist: parseOptionalMist(params.min_price_mist),
|
|
1328
|
+
maxPriceMist: parseOptionalMist(params.max_price_mist),
|
|
1329
|
+
limit: normalizeLimit2(params.limit)
|
|
1330
|
+
});
|
|
1331
|
+
return {
|
|
1332
|
+
tasks,
|
|
1333
|
+
count: tasks.length
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1336
|
+
function parseOptionalMist(value) {
|
|
1337
|
+
if (value == null) {
|
|
1338
|
+
return void 0;
|
|
1339
|
+
}
|
|
1340
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
1341
|
+
return BigInt(value);
|
|
1342
|
+
}
|
|
1343
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
1344
|
+
return BigInt(value.trim());
|
|
1345
|
+
}
|
|
1346
|
+
throw new Error("Price filters must be non-negative integer strings.");
|
|
1347
|
+
}
|
|
1348
|
+
function normalizeLimit2(limit) {
|
|
1349
|
+
if (limit == null) {
|
|
1350
|
+
return 20;
|
|
1351
|
+
}
|
|
1352
|
+
if (!Number.isSafeInteger(limit) || limit <= 0) {
|
|
1353
|
+
throw new Error("limit must be a positive integer.");
|
|
1354
|
+
}
|
|
1355
|
+
return limit;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// src/tools/marketplace-post.ts
|
|
1359
|
+
import { MarketplaceClient as MarketplaceClient4 } from "@hivemind-os/collective-core";
|
|
1360
|
+
var DEFAULT_DISPUTE_WINDOW_MS3 = 6e4;
|
|
1361
|
+
var DEFAULT_EXPIRY_HOURS3 = 24;
|
|
1362
|
+
var meshMarketplacePostTool = {
|
|
1363
|
+
name: "collective_marketplace_post",
|
|
1364
|
+
description: "Post an open marketplace task with category metadata",
|
|
1365
|
+
inputSchema: {
|
|
1366
|
+
type: "object",
|
|
1367
|
+
properties: {
|
|
1368
|
+
capability: { type: "string", description: "Capability requested for the task" },
|
|
1369
|
+
category: { type: "string", description: "Marketplace category used for browsing" },
|
|
1370
|
+
price_mist: { type: "string", description: "Escrowed task budget in MIST" },
|
|
1371
|
+
input: { type: "string", description: "Inline task input to upload to blob storage" },
|
|
1372
|
+
input_blob_id: { type: "string", description: "Existing blob id containing task input" },
|
|
1373
|
+
agreement_hash: { type: "string", description: "Optional agreement hash or plaintext summary" },
|
|
1374
|
+
dispute_window_ms: { type: "number", description: "Optional dispute window override in milliseconds" },
|
|
1375
|
+
expiry_hours: { type: "number", description: "Optional task expiry in hours" }
|
|
1376
|
+
},
|
|
1377
|
+
required: ["capability", "category", "price_mist"]
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
async function runMeshMarketplacePost(params, context) {
|
|
1381
|
+
const client = context.marketplaceClient ?? new MarketplaceClient4(context.suiClient, context.networkConfig);
|
|
1382
|
+
const inputBlobId = await resolveInputBlobId(params, context);
|
|
1383
|
+
const result = await client.postOpenTask({
|
|
1384
|
+
capability: params.capability,
|
|
1385
|
+
category: params.category,
|
|
1386
|
+
inputBlobId,
|
|
1387
|
+
agreementHash: params.agreement_hash,
|
|
1388
|
+
priceMist: parseMist3(params.price_mist, "price_mist"),
|
|
1389
|
+
disputeWindowMs: normalizeNonNegativeInteger(params.dispute_window_ms, DEFAULT_DISPUTE_WINDOW_MS3, "dispute_window_ms"),
|
|
1390
|
+
expiryHours: normalizeNonNegativeInteger(params.expiry_hours, DEFAULT_EXPIRY_HOURS3, "expiry_hours"),
|
|
1391
|
+
signer: context.keypair
|
|
1392
|
+
});
|
|
1393
|
+
return {
|
|
1394
|
+
task_id: result.taskId,
|
|
1395
|
+
tx_digest: result.txDigest,
|
|
1396
|
+
capability: params.capability,
|
|
1397
|
+
category: params.category,
|
|
1398
|
+
input_blob_id: inputBlobId
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
async function resolveInputBlobId(params, context) {
|
|
1402
|
+
if ((params.input_blob_id ? 1 : 0) + (params.input ? 1 : 0) !== 1) {
|
|
1403
|
+
throw new Error("Provide exactly one of input or input_blob_id when posting a marketplace task");
|
|
1404
|
+
}
|
|
1405
|
+
if (params.input_blob_id) {
|
|
1406
|
+
return params.input_blob_id;
|
|
1407
|
+
}
|
|
1408
|
+
if (!params.input) {
|
|
1409
|
+
throw new Error("input or input_blob_id is required when posting a marketplace task");
|
|
1410
|
+
}
|
|
1411
|
+
const stored = await context.blobStore.store(new TextEncoder().encode(params.input));
|
|
1412
|
+
return stored.blobId;
|
|
1413
|
+
}
|
|
1414
|
+
function parseMist3(value, field) {
|
|
1415
|
+
if (typeof value === "number" && Number.isSafeInteger(value) && value >= 0) {
|
|
1416
|
+
return BigInt(value);
|
|
1417
|
+
}
|
|
1418
|
+
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
1419
|
+
return BigInt(value.trim());
|
|
1420
|
+
}
|
|
1421
|
+
throw new Error(`${field} must be a non-negative integer string.`);
|
|
1422
|
+
}
|
|
1423
|
+
function normalizeNonNegativeInteger(value, fallback, field) {
|
|
1424
|
+
if (value == null) {
|
|
1425
|
+
return fallback;
|
|
1426
|
+
}
|
|
1427
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
1428
|
+
throw new Error(`${field} must be a non-negative integer.`);
|
|
1429
|
+
}
|
|
1430
|
+
return value;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// src/tools/multi-execute.ts
|
|
1434
|
+
import {
|
|
1435
|
+
AggregationMode,
|
|
1436
|
+
ProviderSelectionStrategy
|
|
1437
|
+
} from "@hivemind-os/collective-types";
|
|
1438
|
+
import {
|
|
1439
|
+
CircuitBreaker,
|
|
1440
|
+
FanOutExecutor,
|
|
1441
|
+
PerformanceTracker,
|
|
1442
|
+
ProviderSelector
|
|
1443
|
+
} from "@hivemind-os/collective-core";
|
|
1444
|
+
var routingCircuitBreaker = new CircuitBreaker();
|
|
1445
|
+
var routingPerformanceTracker = new PerformanceTracker();
|
|
1446
|
+
var providerSelector = new ProviderSelector({
|
|
1447
|
+
circuitBreaker: routingCircuitBreaker,
|
|
1448
|
+
performanceTracker: routingPerformanceTracker
|
|
1449
|
+
});
|
|
1450
|
+
var meshMultiExecuteTool = {
|
|
1451
|
+
name: "collective_multi_execute",
|
|
1452
|
+
description: "Execute a mesh task across multiple providers and aggregate the results",
|
|
1453
|
+
inputSchema: {
|
|
1454
|
+
type: "object",
|
|
1455
|
+
properties: {
|
|
1456
|
+
capability: { type: "string", description: "Capability name to execute" },
|
|
1457
|
+
input: { description: "Task input payload" },
|
|
1458
|
+
fanOutCount: { type: "number", description: "How many providers to fan out to (default 3)" },
|
|
1459
|
+
strategy: {
|
|
1460
|
+
type: "string",
|
|
1461
|
+
enum: Object.values(ProviderSelectionStrategy),
|
|
1462
|
+
description: "Provider selection strategy (default weighted)"
|
|
1463
|
+
},
|
|
1464
|
+
aggregation: {
|
|
1465
|
+
type: "string",
|
|
1466
|
+
enum: Object.values(AggregationMode),
|
|
1467
|
+
description: "Aggregation mode (default first_success)"
|
|
1468
|
+
},
|
|
1469
|
+
timeout: { type: "number", description: "Per-provider timeout in milliseconds" },
|
|
1470
|
+
maxPricePerProvider: { type: "number", description: "Maximum price per provider in MIST" }
|
|
1471
|
+
},
|
|
1472
|
+
required: ["capability", "input"]
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
async function runMeshMultiExecute(params, context) {
|
|
1476
|
+
const capability = params.capability.trim();
|
|
1477
|
+
if (!capability) {
|
|
1478
|
+
throw new Error("Capability is required.");
|
|
1479
|
+
}
|
|
1480
|
+
const fanOutCount = normalizePositiveInteger(params.fanOutCount, 3);
|
|
1481
|
+
const strategy = parseSelectionStrategy(params.strategy);
|
|
1482
|
+
const aggregation = parseAggregation(params.aggregation);
|
|
1483
|
+
const request = {
|
|
1484
|
+
capability,
|
|
1485
|
+
input: params.input,
|
|
1486
|
+
fanOutCount,
|
|
1487
|
+
strategy,
|
|
1488
|
+
aggregation,
|
|
1489
|
+
timeout: normalizeOptionalInteger(params.timeout),
|
|
1490
|
+
maxPricePerProvider: toOptionalBigInt2(params.maxPricePerProvider)
|
|
1491
|
+
};
|
|
1492
|
+
const { agents: discoveredAgents } = await discoverAgentsByCapability(
|
|
1493
|
+
capability,
|
|
1494
|
+
context,
|
|
1495
|
+
Math.max(fanOutCount * 3, fanOutCount),
|
|
1496
|
+
"reputation"
|
|
1497
|
+
);
|
|
1498
|
+
const candidates = filterByMaxPrice(discoveredAgents, capability, request.maxPricePerProvider);
|
|
1499
|
+
const selected = providerSelector.selectProviders(candidates, capability, strategy, fanOutCount);
|
|
1500
|
+
if (selected.length === 0) {
|
|
1501
|
+
throw new Error(`No eligible providers found for capability ${capability}.`);
|
|
1502
|
+
}
|
|
1503
|
+
const executor = new FanOutExecutor({
|
|
1504
|
+
circuitBreaker: routingCircuitBreaker,
|
|
1505
|
+
performanceTracker: routingPerformanceTracker,
|
|
1506
|
+
executeProvider: async (provider, executionRequest, executionContext) => {
|
|
1507
|
+
if (executionContext.signal.aborted) {
|
|
1508
|
+
throw executionContext.signal.reason ?? new Error("Provider execution aborted.");
|
|
1509
|
+
}
|
|
1510
|
+
const value = await runMeshExecute({
|
|
1511
|
+
capability: executionRequest.capability,
|
|
1512
|
+
provider_did: provider.did,
|
|
1513
|
+
input: serializeInput(executionRequest.input),
|
|
1514
|
+
timeout_seconds: executionRequest.timeout ? Math.max(1, Math.ceil(executionRequest.timeout / 1e3)) : void 0
|
|
1515
|
+
}, context);
|
|
1516
|
+
return {
|
|
1517
|
+
value,
|
|
1518
|
+
aggregateValue: value.result,
|
|
1519
|
+
cost: BigInt(value.price_mist)
|
|
1520
|
+
};
|
|
1521
|
+
}
|
|
1522
|
+
});
|
|
1523
|
+
const result = await executor.execute(request, selected);
|
|
1524
|
+
return {
|
|
1525
|
+
capability,
|
|
1526
|
+
strategy,
|
|
1527
|
+
aggregation,
|
|
1528
|
+
providers: selected.map((provider) => ({
|
|
1529
|
+
did: provider.did,
|
|
1530
|
+
price_mist: provider.price.toString(),
|
|
1531
|
+
reputation: provider.reputation,
|
|
1532
|
+
estimated_latency_ms: provider.estimatedLatency,
|
|
1533
|
+
composite_score: provider.compositeScore
|
|
1534
|
+
})),
|
|
1535
|
+
results: result.results.map((entry) => ({
|
|
1536
|
+
provider: entry.provider,
|
|
1537
|
+
status: entry.status,
|
|
1538
|
+
result: entry.result,
|
|
1539
|
+
duration_ms: entry.durationMs,
|
|
1540
|
+
error: entry.error
|
|
1541
|
+
})),
|
|
1542
|
+
aggregated_result: result.aggregatedResult,
|
|
1543
|
+
total_cost_mist: result.totalCost.toString()
|
|
1544
|
+
};
|
|
1545
|
+
}
|
|
1546
|
+
function filterByMaxPrice(agents, capability, maxPricePerProvider) {
|
|
1547
|
+
if (maxPricePerProvider === void 0) {
|
|
1548
|
+
return agents;
|
|
1549
|
+
}
|
|
1550
|
+
return agents.filter((agent) => {
|
|
1551
|
+
const matched = agent.capabilities.find((entry) => entry.name.toLowerCase() === capability.toLowerCase());
|
|
1552
|
+
return matched ? matched.pricing.amount <= maxPricePerProvider : false;
|
|
1553
|
+
});
|
|
1554
|
+
}
|
|
1555
|
+
function parseSelectionStrategy(strategy) {
|
|
1556
|
+
if (!strategy) {
|
|
1557
|
+
return ProviderSelectionStrategy.WEIGHTED;
|
|
1558
|
+
}
|
|
1559
|
+
if (Object.values(ProviderSelectionStrategy).includes(strategy)) {
|
|
1560
|
+
return strategy;
|
|
1561
|
+
}
|
|
1562
|
+
throw new Error(`Unsupported provider selection strategy: ${String(strategy)}.`);
|
|
1563
|
+
}
|
|
1564
|
+
function parseAggregation(aggregation) {
|
|
1565
|
+
if (!aggregation) {
|
|
1566
|
+
return AggregationMode.FIRST_SUCCESS;
|
|
1567
|
+
}
|
|
1568
|
+
if (Object.values(AggregationMode).includes(aggregation)) {
|
|
1569
|
+
return aggregation;
|
|
1570
|
+
}
|
|
1571
|
+
throw new Error(`Unsupported aggregation mode: ${String(aggregation)}.`);
|
|
1572
|
+
}
|
|
1573
|
+
function normalizePositiveInteger(value, fallback) {
|
|
1574
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
1575
|
+
return fallback;
|
|
1576
|
+
}
|
|
1577
|
+
return Math.max(1, Math.floor(value));
|
|
1578
|
+
}
|
|
1579
|
+
function normalizeOptionalInteger(value) {
|
|
1580
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
1581
|
+
return void 0;
|
|
1582
|
+
}
|
|
1583
|
+
return Math.max(1, Math.floor(value));
|
|
1584
|
+
}
|
|
1585
|
+
function toOptionalBigInt2(value) {
|
|
1586
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
1587
|
+
return void 0;
|
|
1588
|
+
}
|
|
1589
|
+
return BigInt(Math.max(0, Math.floor(value)));
|
|
1590
|
+
}
|
|
1591
|
+
function serializeInput(input) {
|
|
1592
|
+
return typeof input === "string" ? input : JSON.stringify(input);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
// src/tools/policy-update.ts
|
|
1596
|
+
import { PaymentRail as PaymentRail4 } from "@hivemind-os/collective-types";
|
|
1597
|
+
var meshPolicyUpdateTool = {
|
|
1598
|
+
name: "collective_policy_update",
|
|
1599
|
+
description: "Update local spending policy limits",
|
|
1600
|
+
inputSchema: {
|
|
1601
|
+
type: "object",
|
|
1602
|
+
properties: {
|
|
1603
|
+
daily_limit_mist: { type: "number", description: "Daily MIST limit" },
|
|
1604
|
+
monthly_limit_mist: { type: "number", description: "Monthly MIST limit" },
|
|
1605
|
+
per_task_limit_mist: { type: "number", description: "Per-task MIST limit" }
|
|
1606
|
+
},
|
|
1607
|
+
required: []
|
|
1608
|
+
}
|
|
1609
|
+
};
|
|
1610
|
+
async function runMeshPolicyUpdate(params, context) {
|
|
1611
|
+
const currentPolicy = clonePolicy(readPolicySnapshot(context));
|
|
1612
|
+
const nextLimits = [...currentPolicy.limits];
|
|
1613
|
+
applyLimit(nextLimits, "transaction", params.per_task_limit_mist);
|
|
1614
|
+
applyLimit(nextLimits, "day", params.daily_limit_mist);
|
|
1615
|
+
applyLimit(nextLimits, "month", params.monthly_limit_mist);
|
|
1616
|
+
const updatedPolicy = {
|
|
1617
|
+
...currentPolicy,
|
|
1618
|
+
limits: nextLimits
|
|
1619
|
+
};
|
|
1620
|
+
context.spendingPolicy.updatePolicy(updatedPolicy);
|
|
1621
|
+
return {
|
|
1622
|
+
updated: true,
|
|
1623
|
+
limits: updatedPolicy.limits.map((limit) => ({
|
|
1624
|
+
interval: limit.interval,
|
|
1625
|
+
amount_mist: limit.amount.toString(),
|
|
1626
|
+
rail: limit.rail ?? PaymentRail4.SUI_ESCROW
|
|
1627
|
+
}))
|
|
1628
|
+
};
|
|
1629
|
+
}
|
|
1630
|
+
function readPolicySnapshot(context) {
|
|
1631
|
+
const engineWithPolicy = context.spendingPolicy;
|
|
1632
|
+
return engineWithPolicy.policy ?? { limits: [] };
|
|
1633
|
+
}
|
|
1634
|
+
function clonePolicy(policy) {
|
|
1635
|
+
return {
|
|
1636
|
+
...policy,
|
|
1637
|
+
limits: policy.limits.map((limit) => ({ ...limit })),
|
|
1638
|
+
allowlist: policy.allowlist ? [...policy.allowlist] : void 0,
|
|
1639
|
+
denylist: policy.denylist ? [...policy.denylist] : void 0
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
function applyLimit(limits, interval, amount) {
|
|
1643
|
+
if (typeof amount !== "number" || Number.isNaN(amount)) {
|
|
1644
|
+
return;
|
|
1645
|
+
}
|
|
1646
|
+
const nextLimit = {
|
|
1647
|
+
amount: BigInt(Math.max(0, Math.floor(amount))),
|
|
1648
|
+
interval,
|
|
1649
|
+
rail: PaymentRail4.SUI_ESCROW
|
|
1650
|
+
};
|
|
1651
|
+
const index = limits.findIndex(
|
|
1652
|
+
(entry) => entry.interval === interval && (entry.rail ?? PaymentRail4.SUI_ESCROW) === PaymentRail4.SUI_ESCROW
|
|
1653
|
+
);
|
|
1654
|
+
if (index >= 0) {
|
|
1655
|
+
limits[index] = nextLimit;
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
limits.push(nextLimit);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1661
|
+
// src/tools/register.ts
|
|
1662
|
+
import { PaymentRail as PaymentRail5 } from "@hivemind-os/collective-types";
|
|
1663
|
+
var meshRegisterTool = {
|
|
1664
|
+
name: "collective_register",
|
|
1665
|
+
description: "Register the current daemon identity as a provider",
|
|
1666
|
+
inputSchema: {
|
|
1667
|
+
type: "object",
|
|
1668
|
+
properties: {
|
|
1669
|
+
name: { type: "string", description: "Display name for the agent" },
|
|
1670
|
+
description: { type: "string", description: "Agent description" },
|
|
1671
|
+
capabilities: {
|
|
1672
|
+
type: "array",
|
|
1673
|
+
items: {
|
|
1674
|
+
type: "object",
|
|
1675
|
+
properties: {
|
|
1676
|
+
name: { type: "string" },
|
|
1677
|
+
description: { type: "string" },
|
|
1678
|
+
version: { type: "string" },
|
|
1679
|
+
price_mist: { type: "number" }
|
|
1680
|
+
},
|
|
1681
|
+
required: ["name", "description", "version", "price_mist"]
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
},
|
|
1685
|
+
required: ["name", "description", "capabilities"]
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
async function runMeshRegister(params, context) {
|
|
1689
|
+
const capabilities = params.capabilities.map((entry) => ({
|
|
1690
|
+
name: entry.name,
|
|
1691
|
+
description: entry.description,
|
|
1692
|
+
version: entry.version,
|
|
1693
|
+
pricing: {
|
|
1694
|
+
rail: PaymentRail5.SUI_ESCROW,
|
|
1695
|
+
amount: BigInt(Math.max(0, Math.floor(entry.price_mist))),
|
|
1696
|
+
currency: "MIST"
|
|
1697
|
+
}
|
|
1698
|
+
}));
|
|
1699
|
+
const result = await context.registryClient.registerAgent({
|
|
1700
|
+
name: params.name,
|
|
1701
|
+
did: context.did,
|
|
1702
|
+
description: params.description,
|
|
1703
|
+
capabilities,
|
|
1704
|
+
endpoint: `mesh://agent/${context.did}`,
|
|
1705
|
+
encryptionPublicKey: context.encryption?.enabled ? hexToBytes(context.encryption.publicKey) ?? void 0 : void 0,
|
|
1706
|
+
keypair: context.keypair
|
|
1707
|
+
});
|
|
1708
|
+
const card = await context.registryClient.getAgentCard(result.agentCardId);
|
|
1709
|
+
if (card) {
|
|
1710
|
+
context.agentCache.upsertAgent(card);
|
|
1711
|
+
}
|
|
1712
|
+
return {
|
|
1713
|
+
agent_card_id: result.agentCardId,
|
|
1714
|
+
did: context.did,
|
|
1715
|
+
tx_digest: result.txDigest
|
|
1716
|
+
};
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
// src/tools/relay-registry.ts
|
|
1720
|
+
import { RelayRegistryClient } from "@hivemind-os/collective-core";
|
|
1721
|
+
var meshRelayRegistryTool = {
|
|
1722
|
+
name: "collective_relay_registry",
|
|
1723
|
+
description: "List registered community relays or register this node as a relay operator",
|
|
1724
|
+
inputSchema: {
|
|
1725
|
+
type: "object",
|
|
1726
|
+
properties: {
|
|
1727
|
+
action: { type: "string", enum: ["list", "register"] },
|
|
1728
|
+
endpoint: { type: "string", description: "Relay websocket or HTTPS endpoint when action=register" },
|
|
1729
|
+
stake_id: { type: "string", description: "Relay stake position object id when action=register" },
|
|
1730
|
+
region: { type: "string", description: "Relay region label when action=register" },
|
|
1731
|
+
routing_fee_bps: { type: "number", description: "Routing fee in basis points when action=register" },
|
|
1732
|
+
capabilities: {
|
|
1733
|
+
type: "array",
|
|
1734
|
+
items: { type: "string" },
|
|
1735
|
+
description: 'Optional relay capabilities when action=register (defaults to ["routing"])'
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1738
|
+
required: ["action"]
|
|
1739
|
+
}
|
|
1740
|
+
};
|
|
1741
|
+
async function runMeshRelayRegistry(params, context) {
|
|
1742
|
+
const client = context.relayRegistryClient ?? new RelayRegistryClient(context.suiClient, context.networkConfig);
|
|
1743
|
+
switch (params.action) {
|
|
1744
|
+
case "list": {
|
|
1745
|
+
const relays = await client.listRelays();
|
|
1746
|
+
return {
|
|
1747
|
+
action: "list",
|
|
1748
|
+
count: relays.length,
|
|
1749
|
+
relays
|
|
1750
|
+
};
|
|
1751
|
+
}
|
|
1752
|
+
case "register": {
|
|
1753
|
+
const endpoint = requireNonEmpty(params.endpoint, "endpoint");
|
|
1754
|
+
const stakeId = requireNonEmpty(params.stake_id, "stake_id");
|
|
1755
|
+
const region = requireNonEmpty(params.region, "region");
|
|
1756
|
+
if (params.routing_fee_bps === void 0) {
|
|
1757
|
+
throw new Error("routing_fee_bps is required when action=register");
|
|
1758
|
+
}
|
|
1759
|
+
if (!Number.isInteger(params.routing_fee_bps) || params.routing_fee_bps < 0 || params.routing_fee_bps > 1e4) {
|
|
1760
|
+
throw new Error("routing_fee_bps must be an integer between 0 and 10000");
|
|
1761
|
+
}
|
|
1762
|
+
const capabilities = normalizeCapabilities(params.capabilities);
|
|
1763
|
+
const result = await client.registerRelay({
|
|
1764
|
+
endpoint,
|
|
1765
|
+
stakeId,
|
|
1766
|
+
region,
|
|
1767
|
+
routingFeeBps: params.routing_fee_bps,
|
|
1768
|
+
capabilities,
|
|
1769
|
+
signer: context.keypair
|
|
1770
|
+
});
|
|
1771
|
+
return {
|
|
1772
|
+
action: "register",
|
|
1773
|
+
relay_id: result.relayId,
|
|
1774
|
+
tx_digest: result.txDigest,
|
|
1775
|
+
endpoint,
|
|
1776
|
+
region,
|
|
1777
|
+
routing_fee_bps: params.routing_fee_bps,
|
|
1778
|
+
capabilities
|
|
1779
|
+
};
|
|
1780
|
+
}
|
|
1781
|
+
default:
|
|
1782
|
+
throw new Error(`Unknown relay registry action: ${String(params.action)}`);
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
function requireNonEmpty(value, field) {
|
|
1786
|
+
const normalized = value?.trim();
|
|
1787
|
+
if (!normalized) {
|
|
1788
|
+
throw new Error(`${field} is required when action=register`);
|
|
1789
|
+
}
|
|
1790
|
+
return normalized;
|
|
1791
|
+
}
|
|
1792
|
+
function normalizeCapabilities(capabilities) {
|
|
1793
|
+
const normalized = capabilities?.map((capability) => capability.trim()).filter(Boolean) ?? [];
|
|
1794
|
+
return normalized.length > 0 ? [...new Set(normalized)] : ["routing"];
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
// src/tools/stake.ts
|
|
1798
|
+
import { StakingClient, STAKING_COOLDOWN_MS } from "@hivemind-os/collective-core";
|
|
1799
|
+
var meshStakeTool = {
|
|
1800
|
+
name: "collective_stake",
|
|
1801
|
+
description: "Manage stake deposits, status, and withdrawals for the local Agentic Mesh identity",
|
|
1802
|
+
inputSchema: {
|
|
1803
|
+
type: "object",
|
|
1804
|
+
properties: {
|
|
1805
|
+
action: { type: "string", enum: ["deposit", "status", "withdraw"] },
|
|
1806
|
+
amount_sui: { type: "string", description: "SUI amount to deposit when action=deposit" },
|
|
1807
|
+
stake_type: { type: "string", enum: ["agent", "relay"], description: "Stake type for deposits (default agent)" }
|
|
1808
|
+
},
|
|
1809
|
+
required: ["action"]
|
|
1810
|
+
}
|
|
1811
|
+
};
|
|
1812
|
+
async function runMeshStake(params, context) {
|
|
1813
|
+
const client = context.stakingClient ?? new StakingClient(context.suiClient, context.networkConfig);
|
|
1814
|
+
const owner = context.keypair.getPublicKey().toSuiAddress();
|
|
1815
|
+
switch (params.action) {
|
|
1816
|
+
case "deposit": {
|
|
1817
|
+
if (!params.amount_sui) {
|
|
1818
|
+
throw new Error("amount_sui is required when action=deposit");
|
|
1819
|
+
}
|
|
1820
|
+
const amountMist = parseSuiToMist(params.amount_sui);
|
|
1821
|
+
const result = await client.depositStake({
|
|
1822
|
+
amountMist,
|
|
1823
|
+
stakeType: params.stake_type ?? "agent",
|
|
1824
|
+
signer: context.keypair
|
|
1825
|
+
});
|
|
1826
|
+
return {
|
|
1827
|
+
action: "deposit",
|
|
1828
|
+
stake_id: result.stakeId,
|
|
1829
|
+
tx_digest: result.txDigest,
|
|
1830
|
+
amount_mist: amountMist.toString(),
|
|
1831
|
+
amount_sui: params.amount_sui,
|
|
1832
|
+
stake_type: params.stake_type ?? "agent"
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
case "status": {
|
|
1836
|
+
const stake = await client.getStakeByOwner(owner);
|
|
1837
|
+
return {
|
|
1838
|
+
action: "status",
|
|
1839
|
+
owner,
|
|
1840
|
+
staked: Boolean(stake),
|
|
1841
|
+
stake
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
case "withdraw": {
|
|
1845
|
+
const stake = await client.getStakeByOwner(owner);
|
|
1846
|
+
if (!stake) {
|
|
1847
|
+
throw new Error("No stake position found for this wallet.");
|
|
1848
|
+
}
|
|
1849
|
+
if (stake.deactivatedAt === 0) {
|
|
1850
|
+
const result2 = await client.startDeactivation({ stakeId: stake.id, signer: context.keypair });
|
|
1851
|
+
return {
|
|
1852
|
+
action: "deactivation_started",
|
|
1853
|
+
stake_id: stake.id,
|
|
1854
|
+
cooldown_ends_at: result2.cooldownEndsAt,
|
|
1855
|
+
tx_digest: result2.txDigest
|
|
1856
|
+
};
|
|
1857
|
+
}
|
|
1858
|
+
const cooldownEndsAt = stake.deactivatedAt + STAKING_COOLDOWN_MS;
|
|
1859
|
+
if (Date.now() < cooldownEndsAt) {
|
|
1860
|
+
return {
|
|
1861
|
+
action: "cooling_down",
|
|
1862
|
+
stake_id: stake.id,
|
|
1863
|
+
cooldown_ends_at: cooldownEndsAt,
|
|
1864
|
+
cooldown_remaining_ms: Math.max(cooldownEndsAt - Date.now(), 0)
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
const result = await client.withdrawStake({ stakeId: stake.id, signer: context.keypair });
|
|
1868
|
+
return {
|
|
1869
|
+
action: "withdrawn",
|
|
1870
|
+
stake_id: stake.id,
|
|
1871
|
+
amount_returned_mist: result.amountReturned.toString(),
|
|
1872
|
+
tx_digest: result.txDigest
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
default:
|
|
1876
|
+
throw new Error(`Unknown staking action: ${String(params.action)}`);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
function parseSuiToMist(input) {
|
|
1880
|
+
const trimmed = input.trim();
|
|
1881
|
+
if (!/^\d+(?:\.\d{1,9})?$/.test(trimmed)) {
|
|
1882
|
+
throw new Error(`Invalid SUI amount: ${input}`);
|
|
1883
|
+
}
|
|
1884
|
+
const [whole, fraction = ""] = trimmed.split(".");
|
|
1885
|
+
return BigInt(whole) * 1000000000n + BigInt(fraction.padEnd(9, "0"));
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
// src/tools/task-history.ts
|
|
1889
|
+
var meshTaskHistoryTool = {
|
|
1890
|
+
name: "collective_task_history",
|
|
1891
|
+
description: "Query locally persisted task history",
|
|
1892
|
+
inputSchema: {
|
|
1893
|
+
type: "object",
|
|
1894
|
+
properties: {
|
|
1895
|
+
limit: { type: "number", description: "Max task rows to return (default 20)" },
|
|
1896
|
+
status_filter: { type: "string", description: "Optional task status filter" }
|
|
1897
|
+
},
|
|
1898
|
+
required: []
|
|
1899
|
+
}
|
|
1900
|
+
};
|
|
1901
|
+
async function runMeshTaskHistory(params, context) {
|
|
1902
|
+
const db = context.taskHistoryDb;
|
|
1903
|
+
if (!db?.prepare) {
|
|
1904
|
+
return {
|
|
1905
|
+
tasks: [],
|
|
1906
|
+
note: "Local task history is not initialized yet."
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
const limit = normalizeLimit3(params.limit);
|
|
1910
|
+
const statusFilter = params.status_filter?.trim();
|
|
1911
|
+
try {
|
|
1912
|
+
const statement = statusFilter ? db.prepare(
|
|
1913
|
+
"SELECT * FROM task_history WHERE status = ? ORDER BY created_at DESC LIMIT ?"
|
|
1914
|
+
) : db.prepare("SELECT * FROM task_history ORDER BY created_at DESC LIMIT ?");
|
|
1915
|
+
const rows = statusFilter ? statement.all(statusFilter, limit) : statement.all(limit);
|
|
1916
|
+
return {
|
|
1917
|
+
tasks: rows.map((row) => serializeBigInts(row))
|
|
1918
|
+
};
|
|
1919
|
+
} catch {
|
|
1920
|
+
return {
|
|
1921
|
+
tasks: [],
|
|
1922
|
+
note: "Local task history database is not available yet."
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
function normalizeLimit3(limit) {
|
|
1927
|
+
if (typeof limit !== "number" || Number.isNaN(limit)) {
|
|
1928
|
+
return 20;
|
|
1929
|
+
}
|
|
1930
|
+
return Math.max(1, Math.floor(limit));
|
|
1931
|
+
}
|
|
1932
|
+
function serializeBigInts(value) {
|
|
1933
|
+
if (typeof value === "bigint") {
|
|
1934
|
+
return value.toString();
|
|
1935
|
+
}
|
|
1936
|
+
if (Array.isArray(value)) {
|
|
1937
|
+
return value.map((entry) => serializeBigInts(entry));
|
|
1938
|
+
}
|
|
1939
|
+
if (value && typeof value === "object") {
|
|
1940
|
+
return Object.fromEntries(
|
|
1941
|
+
Object.entries(value).map(([key, entry]) => [key, serializeBigInts(entry)])
|
|
1942
|
+
);
|
|
1943
|
+
}
|
|
1944
|
+
return value;
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
// src/tools/task-status.ts
|
|
1948
|
+
import { decodeMeteredResult as decodeMeteredResult2, parseMeteredResultEnvelope as parseMeteredResultEnvelope2 } from "@hivemind-os/collective-core";
|
|
1949
|
+
import { TaskStatus as TaskStatus4 } from "@hivemind-os/collective-types";
|
|
1950
|
+
var decoder4 = new TextDecoder();
|
|
1951
|
+
var meshTaskStatusTool = {
|
|
1952
|
+
name: "collective_task_status",
|
|
1953
|
+
description: "Get task status and fetch the result if available",
|
|
1954
|
+
inputSchema: {
|
|
1955
|
+
type: "object",
|
|
1956
|
+
properties: {
|
|
1957
|
+
task_id: { type: "string", description: "Task object id" }
|
|
1958
|
+
},
|
|
1959
|
+
required: ["task_id"]
|
|
1960
|
+
}
|
|
1961
|
+
};
|
|
1962
|
+
async function runMeshTaskStatus(params, context) {
|
|
1963
|
+
const task = await context.taskClient.getTask(params.task_id);
|
|
1964
|
+
if (!task) {
|
|
1965
|
+
throw new Error(`Task ${params.task_id} was not found.`);
|
|
1966
|
+
}
|
|
1967
|
+
let result;
|
|
1968
|
+
if ((task.status === TaskStatus4.COMPLETED || task.status === TaskStatus4.RELEASED) && task.resultBlobId) {
|
|
1969
|
+
const resultBytes = await fetchMeshBlob(context.blobStore, task.resultBlobId);
|
|
1970
|
+
if (resultBytes) {
|
|
1971
|
+
const envelope = parseMeteredResultEnvelope2(resultBytes);
|
|
1972
|
+
result = decoder4.decode(envelope ? decodeMeteredResult2(envelope) : resultBytes);
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
return {
|
|
1976
|
+
task_id: task.id,
|
|
1977
|
+
status: TaskStatus4[task.status] ?? "UNKNOWN",
|
|
1978
|
+
provider_did: findProviderDid(context, task.provider),
|
|
1979
|
+
result,
|
|
1980
|
+
price_mist: task.price.toString(),
|
|
1981
|
+
created_at: task.createdAt
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
function findProviderDid(context, owner) {
|
|
1985
|
+
if (!owner) {
|
|
1986
|
+
return void 0;
|
|
1987
|
+
}
|
|
1988
|
+
return context.agentCache.getAllActive(1e3).find((agent) => agent.owner === owner)?.did;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// src/tools/index.ts
|
|
1992
|
+
var toolDefinitions = [
|
|
1993
|
+
meshAnalyticsTool,
|
|
1994
|
+
meshDiscoverTool,
|
|
1995
|
+
meshDisputeTool,
|
|
1996
|
+
meshExecuteTool,
|
|
1997
|
+
meshExecuteAsyncTool,
|
|
1998
|
+
meshMeteredExecuteTool,
|
|
1999
|
+
meshVerifyResultTool,
|
|
2000
|
+
meshMarketplacePostTool,
|
|
2001
|
+
meshMarketplaceBrowseTool,
|
|
2002
|
+
meshMultiExecuteTool,
|
|
2003
|
+
meshMarketplaceBidTool,
|
|
2004
|
+
meshMarketplaceAcceptBidTool,
|
|
2005
|
+
meshTaskStatusTool,
|
|
2006
|
+
meshRegisterTool,
|
|
2007
|
+
meshDeactivateTool,
|
|
2008
|
+
meshBalanceTool,
|
|
2009
|
+
meshPolicyUpdateTool,
|
|
2010
|
+
meshStakeTool,
|
|
2011
|
+
meshRelayRegistryTool,
|
|
2012
|
+
meshTaskHistoryTool
|
|
2013
|
+
];
|
|
2014
|
+
var meshToolHandlers = {
|
|
2015
|
+
[meshAnalyticsTool.name]: runMeshAnalytics,
|
|
2016
|
+
[meshDiscoverTool.name]: runMeshDiscover,
|
|
2017
|
+
[meshDisputeTool.name]: runMeshDispute,
|
|
2018
|
+
[meshExecuteTool.name]: runMeshExecute,
|
|
2019
|
+
[meshExecuteAsyncTool.name]: runMeshExecuteAsync,
|
|
2020
|
+
[meshMeteredExecuteTool.name]: runMeshMeteredExecute,
|
|
2021
|
+
[meshVerifyResultTool.name]: runMeshVerifyResult,
|
|
2022
|
+
[meshMarketplacePostTool.name]: runMeshMarketplacePost,
|
|
2023
|
+
[meshMarketplaceBrowseTool.name]: runMeshMarketplaceBrowse,
|
|
2024
|
+
[meshMultiExecuteTool.name]: runMeshMultiExecute,
|
|
2025
|
+
[meshMarketplaceBidTool.name]: runMeshMarketplaceBid,
|
|
2026
|
+
[meshMarketplaceAcceptBidTool.name]: runMeshMarketplaceAcceptBid,
|
|
2027
|
+
[meshTaskStatusTool.name]: runMeshTaskStatus,
|
|
2028
|
+
[meshRegisterTool.name]: runMeshRegister,
|
|
2029
|
+
[meshDeactivateTool.name]: runMeshDeactivate,
|
|
2030
|
+
[meshBalanceTool.name]: runMeshBalance,
|
|
2031
|
+
[meshPolicyUpdateTool.name]: runMeshPolicyUpdate,
|
|
2032
|
+
[meshStakeTool.name]: runMeshStake,
|
|
2033
|
+
[meshRelayRegistryTool.name]: runMeshRelayRegistry,
|
|
2034
|
+
[meshTaskHistoryTool.name]: runMeshTaskHistory
|
|
2035
|
+
};
|
|
2036
|
+
function registerToolHandlers(server, context) {
|
|
2037
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
2038
|
+
tools: toolDefinitions
|
|
2039
|
+
}));
|
|
2040
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
2041
|
+
const handler = meshToolHandlers[request.params.name];
|
|
2042
|
+
if (!handler) {
|
|
2043
|
+
return createErrorResult(`Unknown tool: ${request.params.name}`);
|
|
2044
|
+
}
|
|
2045
|
+
try {
|
|
2046
|
+
const response = await handler(request.params.arguments ?? {}, context);
|
|
2047
|
+
return createSuccessResult(response);
|
|
2048
|
+
} catch (error) {
|
|
2049
|
+
const message = error instanceof SessionExpiredError ? "Authentication expired. Please re-authenticate via the daemon portal." : error instanceof Error ? error.message : String(error);
|
|
2050
|
+
const sessionState = await getSessionState(context);
|
|
2051
|
+
return createErrorResult(
|
|
2052
|
+
sessionState ? `${message} (session state: ${sessionState})` : message,
|
|
2053
|
+
sessionState ? { session_state: sessionState, reauth_required: error instanceof SessionExpiredError } : void 0
|
|
2054
|
+
);
|
|
2055
|
+
}
|
|
2056
|
+
});
|
|
2057
|
+
}
|
|
2058
|
+
var meshToolDefinitions = toolDefinitions;
|
|
2059
|
+
function createSuccessResult(payload) {
|
|
2060
|
+
return {
|
|
2061
|
+
content: [
|
|
2062
|
+
{
|
|
2063
|
+
type: "text",
|
|
2064
|
+
text: serialize2(payload)
|
|
2065
|
+
}
|
|
2066
|
+
]
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
function createErrorResult(message, details) {
|
|
2070
|
+
return {
|
|
2071
|
+
isError: true,
|
|
2072
|
+
content: [
|
|
2073
|
+
{
|
|
2074
|
+
type: "text",
|
|
2075
|
+
text: serialize2({ error: message, ...details })
|
|
2076
|
+
}
|
|
2077
|
+
]
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
async function getSessionState(context) {
|
|
2081
|
+
const provider = context.authProvider;
|
|
2082
|
+
if (!provider?.getSessionState) {
|
|
2083
|
+
return null;
|
|
2084
|
+
}
|
|
2085
|
+
const state = await provider.getSessionState();
|
|
2086
|
+
return typeof state === "string" ? state : null;
|
|
2087
|
+
}
|
|
2088
|
+
function serialize2(payload) {
|
|
2089
|
+
return JSON.stringify(payload, bigintReplacer2, 2);
|
|
2090
|
+
}
|
|
2091
|
+
function bigintReplacer2(_key, value) {
|
|
2092
|
+
return typeof value === "bigint" ? value.toString() : value;
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// src/index.ts
|
|
2096
|
+
function registerMeshTools(server, context) {
|
|
2097
|
+
registerToolHandlers(server, context);
|
|
2098
|
+
registerResourceHandlers(server, context);
|
|
2099
|
+
}
|
|
2100
|
+
function createMeshMcpServer(context) {
|
|
2101
|
+
const server = new Server(
|
|
2102
|
+
{
|
|
2103
|
+
name: "@hivemind-os/collective-mcp-server",
|
|
2104
|
+
version: "0.1.0"
|
|
2105
|
+
},
|
|
2106
|
+
{
|
|
2107
|
+
capabilities: {
|
|
2108
|
+
tools: {},
|
|
2109
|
+
resources: {}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
);
|
|
2113
|
+
registerMeshTools(server, context);
|
|
2114
|
+
return server;
|
|
2115
|
+
}
|
|
2116
|
+
export {
|
|
2117
|
+
createMeshMcpServer,
|
|
2118
|
+
meshMeteredExecuteTool,
|
|
2119
|
+
meshMultiExecuteTool,
|
|
2120
|
+
meshToolDefinitions,
|
|
2121
|
+
meshToolHandlers,
|
|
2122
|
+
meshVerifyResultTool,
|
|
2123
|
+
registerMeshTools,
|
|
2124
|
+
registerResourceHandlers,
|
|
2125
|
+
runMeshMeteredExecute,
|
|
2126
|
+
runMeshMultiExecute,
|
|
2127
|
+
runMeshVerifyResult
|
|
2128
|
+
};
|
|
2129
|
+
//# sourceMappingURL=index.js.map
|