@hivemind-os/collective-daemon 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.
@@ -0,0 +1,803 @@
1
+ import {
2
+ RelayClient
3
+ } from "./chunk-BJW47ZD5.js";
4
+
5
+ // src/provider/adapters/echo.ts
6
+ import { createHash } from "crypto";
7
+ var decoder = new TextDecoder("utf-8", { fatal: true });
8
+ var encoder = new TextEncoder();
9
+ var EchoAdapter = class {
10
+ name = "echo";
11
+ async execute(params) {
12
+ const result = {
13
+ echo: decodeInput(params.inputData),
14
+ taskId: params.taskId,
15
+ capability: params.capability,
16
+ timestamp: Date.now(),
17
+ inputHash: createHash("sha256").update(params.inputData).digest("hex")
18
+ };
19
+ return {
20
+ resultData: encoder.encode(JSON.stringify(result))
21
+ };
22
+ }
23
+ };
24
+ function decodeInput(input) {
25
+ try {
26
+ return decoder.decode(input);
27
+ } catch {
28
+ return Buffer.from(input).toString("hex");
29
+ }
30
+ }
31
+
32
+ // src/provider/adapters/local-fn.ts
33
+ var LocalFunctionAdapter = class {
34
+ constructor(fn) {
35
+ this.fn = fn;
36
+ }
37
+ fn;
38
+ name = "local-function";
39
+ async execute(params) {
40
+ return {
41
+ resultData: await this.fn(params.inputData, params.metadata)
42
+ };
43
+ }
44
+ };
45
+
46
+ // src/provider/adapters/mcp-sampling.ts
47
+ var DEFAULT_MAX_TOKENS = 4096;
48
+ var decoder2 = new TextDecoder("utf-8", { fatal: true });
49
+ var encoder2 = new TextEncoder();
50
+ var McpSamplingAdapter = class {
51
+ constructor(config, sample) {
52
+ this.sample = sample;
53
+ if (!config.appName || config.appName.trim().length === 0) {
54
+ throw new Error("MCP sampling adapter requires a non-empty appName");
55
+ }
56
+ if (!config.systemPrompt || config.systemPrompt.trim().length === 0) {
57
+ throw new Error("MCP sampling adapter requires a non-empty systemPrompt");
58
+ }
59
+ this.appName = config.appName.trim();
60
+ this.systemPrompt = config.systemPrompt;
61
+ this.maxTokens = config.maxTokens ?? DEFAULT_MAX_TOKENS;
62
+ this.modelHint = config.modelHint;
63
+ }
64
+ sample;
65
+ name = "mcp-sampling";
66
+ appName;
67
+ systemPrompt;
68
+ maxTokens;
69
+ modelHint;
70
+ async execute(params) {
71
+ let inputText;
72
+ try {
73
+ inputText = decoder2.decode(params.inputData);
74
+ } catch {
75
+ throw new Error("MCP sampling adapter requires valid UTF-8 input");
76
+ }
77
+ const samplingParams = {
78
+ messages: [
79
+ {
80
+ role: "user",
81
+ content: { type: "text", text: inputText }
82
+ }
83
+ ],
84
+ systemPrompt: this.systemPrompt,
85
+ maxTokens: this.maxTokens,
86
+ ...this.modelHint ? { modelPreferences: { hints: [{ name: this.modelHint }] } } : {}
87
+ };
88
+ const result = await this.sample(this.appName, samplingParams);
89
+ const text = extractTextContent(result);
90
+ if (text === void 0) {
91
+ throw new Error("MCP sampling response contained no text content");
92
+ }
93
+ return {
94
+ resultData: encoder2.encode(text),
95
+ metadata: {
96
+ model: result.model,
97
+ ...result.stopReason ? { stopReason: result.stopReason } : {}
98
+ }
99
+ };
100
+ }
101
+ };
102
+ function extractTextContent(result) {
103
+ const content = result.content;
104
+ if (Array.isArray(content)) {
105
+ for (const block of content) {
106
+ if (block.type === "text") {
107
+ return block.text;
108
+ }
109
+ }
110
+ return void 0;
111
+ }
112
+ if (content.type === "text") {
113
+ return content.text;
114
+ }
115
+ return void 0;
116
+ }
117
+
118
+ // src/provider/adapters/subprocess.ts
119
+ import { spawn } from "child_process";
120
+ var DEFAULT_TIMEOUT_MS = 6e4;
121
+ var DEFAULT_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
122
+ var SubprocessAdapter = class {
123
+ name = "subprocess";
124
+ command;
125
+ args;
126
+ cwd;
127
+ env;
128
+ timeoutMs;
129
+ maxOutputBytes;
130
+ constructor(config) {
131
+ if (!config.command || config.command.trim().length === 0) {
132
+ throw new Error("Subprocess adapter requires a non-empty command");
133
+ }
134
+ this.command = config.command;
135
+ this.args = config.args ?? [];
136
+ this.cwd = config.cwd;
137
+ this.env = { ...config.env };
138
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
139
+ this.maxOutputBytes = config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
140
+ }
141
+ async execute(params) {
142
+ return new Promise((resolve, reject) => {
143
+ const child = spawn(this.command, this.args, {
144
+ cwd: this.cwd,
145
+ shell: false,
146
+ stdio: ["pipe", "pipe", "pipe"],
147
+ env: {
148
+ ...process.env,
149
+ ...this.env,
150
+ COLLECTIVE_TASK_ID: params.taskId,
151
+ COLLECTIVE_CAPABILITY: params.capability
152
+ }
153
+ });
154
+ const stdout = [];
155
+ const stderr = [];
156
+ let stdoutSize = 0;
157
+ let stderrSize = 0;
158
+ let settled = false;
159
+ const settle = (fn) => {
160
+ if (!settled) {
161
+ settled = true;
162
+ clearTimeout(timer);
163
+ fn();
164
+ }
165
+ };
166
+ const timer = setTimeout(() => {
167
+ settle(() => {
168
+ child.kill("SIGKILL");
169
+ reject(new Error(`Subprocess timed out after ${this.timeoutMs}ms`));
170
+ });
171
+ }, this.timeoutMs);
172
+ child.stdout.on("data", (chunk) => {
173
+ stdoutSize += chunk.length;
174
+ if (stdoutSize > this.maxOutputBytes) {
175
+ settle(() => {
176
+ child.kill("SIGKILL");
177
+ reject(new Error(`Subprocess stdout exceeded ${this.maxOutputBytes} byte limit`));
178
+ });
179
+ return;
180
+ }
181
+ stdout.push(chunk);
182
+ });
183
+ child.stderr.on("data", (chunk) => {
184
+ stderrSize += chunk.length;
185
+ if (stderrSize <= 64 * 1024) {
186
+ stderr.push(chunk);
187
+ }
188
+ });
189
+ child.on("error", (error) => {
190
+ settle(() => reject(new Error(`Subprocess failed to start: ${error.message}`)));
191
+ });
192
+ child.on("close", (code) => {
193
+ settle(() => {
194
+ if (code !== 0) {
195
+ const stderrText = Buffer.concat(stderr).toString("utf-8").trim();
196
+ const truncated = stderrText.length > 1024 ? stderrText.slice(0, 1024) + "..." : stderrText;
197
+ reject(new Error(`Subprocess exited with code ${code ?? "null"}${truncated ? `: ${truncated}` : ""}`));
198
+ return;
199
+ }
200
+ resolve({
201
+ resultData: new Uint8Array(Buffer.concat(stdout))
202
+ });
203
+ });
204
+ });
205
+ child.stdin.on("error", (error) => {
206
+ settle(() => reject(new Error(`Subprocess stdin error: ${error.message}`)));
207
+ });
208
+ child.stdin.end(Buffer.from(params.inputData));
209
+ });
210
+ }
211
+ };
212
+
213
+ // src/provider/adapters/webhook.ts
214
+ var DEFAULT_TIMEOUT_MS2 = 3e4;
215
+ var DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
216
+ var BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
217
+ var WebhookAdapter = class {
218
+ name = "webhook";
219
+ url;
220
+ method;
221
+ headers;
222
+ timeoutMs;
223
+ maxResponseBytes;
224
+ constructor(config) {
225
+ this.url = parseUrl(config.url);
226
+ this.method = (config.method ?? "POST").toUpperCase();
227
+ if (!BODY_METHODS.has(this.method)) {
228
+ throw new Error(`Webhook method must be one of POST, PUT, PATCH (got ${this.method})`);
229
+ }
230
+ this.headers = { ...config.headers };
231
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS2;
232
+ this.maxResponseBytes = config.maxResponseBytes ?? DEFAULT_MAX_RESPONSE_BYTES;
233
+ }
234
+ async execute(params) {
235
+ const controller = new AbortController();
236
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
237
+ try {
238
+ const response = await fetch(this.url, {
239
+ method: this.method,
240
+ headers: {
241
+ "Content-Type": "application/octet-stream",
242
+ "X-Mesh-Task-Id": params.taskId,
243
+ "X-Mesh-Capability": params.capability,
244
+ ...this.headers
245
+ },
246
+ body: params.inputData,
247
+ signal: controller.signal,
248
+ redirect: "manual"
249
+ });
250
+ if (response.status >= 300 && response.status < 400) {
251
+ throw new Error(`Webhook returned redirect (HTTP ${response.status}) which is not followed for security`);
252
+ }
253
+ if (!response.ok) {
254
+ const body = await response.text().catch(() => "");
255
+ const truncated = body.length > 256 ? body.slice(0, 256) + "..." : body;
256
+ throw new Error(`Webhook returned HTTP ${response.status}: ${truncated}`);
257
+ }
258
+ const resultData = await readLimitedResponse(response, this.maxResponseBytes);
259
+ return { resultData };
260
+ } catch (error) {
261
+ if (error instanceof DOMException && error.name === "AbortError") {
262
+ throw new Error(`Webhook timed out after ${this.timeoutMs}ms`);
263
+ }
264
+ throw error;
265
+ } finally {
266
+ clearTimeout(timer);
267
+ }
268
+ }
269
+ };
270
+ async function readLimitedResponse(response, maxBytes) {
271
+ const contentLength = response.headers.get("content-length");
272
+ if (contentLength && Number(contentLength) > maxBytes) {
273
+ throw new Error(`Webhook response too large: ${contentLength} bytes (max ${maxBytes})`);
274
+ }
275
+ const chunks = [];
276
+ let total = 0;
277
+ if (!response.body) {
278
+ return new Uint8Array(0);
279
+ }
280
+ for await (const chunk of response.body) {
281
+ total += chunk.byteLength;
282
+ if (total > maxBytes) {
283
+ throw new Error(`Webhook response exceeded ${maxBytes} byte limit`);
284
+ }
285
+ chunks.push(chunk);
286
+ }
287
+ const result = new Uint8Array(total);
288
+ let offset = 0;
289
+ for (const chunk of chunks) {
290
+ result.set(chunk, offset);
291
+ offset += chunk.byteLength;
292
+ }
293
+ return result;
294
+ }
295
+ function parseUrl(input) {
296
+ let url;
297
+ try {
298
+ url = new URL(input);
299
+ } catch {
300
+ throw new Error(`Invalid webhook URL: ${input}`);
301
+ }
302
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
303
+ throw new Error(`Webhook URL must use http or https: ${input}`);
304
+ }
305
+ return url;
306
+ }
307
+
308
+ // src/provider/capabilities.ts
309
+ function loadProviderConfig(config) {
310
+ if (!config.provider) {
311
+ return null;
312
+ }
313
+ return {
314
+ enabled: config.provider.enabled,
315
+ capabilities: config.provider.capabilities.map((capability) => ({
316
+ name: capability.name,
317
+ description: capability.description,
318
+ version: capability.version,
319
+ priceMist: capability.priceMist,
320
+ currency: capability.currency,
321
+ adapter: capability.adapter,
322
+ adapterConfig: capability.adapterConfig
323
+ })),
324
+ maxConcurrency: Math.max(1, config.provider.maxConcurrency ?? 1),
325
+ autoRegister: config.provider.autoRegister ?? false
326
+ };
327
+ }
328
+
329
+ // src/provider/runtime.ts
330
+ import { randomUUID } from "crypto";
331
+ import pino from "pino";
332
+ import {
333
+ EventSubscription,
334
+ SqliteCursorStore,
335
+ UsageMeter,
336
+ createMeteredResultEnvelope,
337
+ parseEncryptedPayload,
338
+ parseRawEvent,
339
+ serializeMeteredResultEnvelope,
340
+ splitIntoMeteringUnits
341
+ } from "@hivemind-os/collective-core";
342
+ import { PaymentRail, PaymentScheme } from "@hivemind-os/collective-types";
343
+
344
+ // src/provider/task-queue.ts
345
+ var TaskQueue = class {
346
+ constructor(maxConcurrency = 1) {
347
+ this.maxConcurrency = maxConcurrency;
348
+ }
349
+ maxConcurrency;
350
+ running = /* @__PURE__ */ new Map();
351
+ async enqueue(taskId, work) {
352
+ if (this.isFull || this.running.has(taskId)) {
353
+ return false;
354
+ }
355
+ const runPromise = Promise.resolve().then(work).finally(() => {
356
+ this.running.delete(taskId);
357
+ });
358
+ this.running.set(taskId, runPromise);
359
+ return true;
360
+ }
361
+ get activeCount() {
362
+ return this.running.size;
363
+ }
364
+ get isFull() {
365
+ return this.activeCount >= this.maxConcurrency;
366
+ }
367
+ async drain() {
368
+ while (this.running.size > 0) {
369
+ await Promise.allSettled(this.running.values());
370
+ }
371
+ }
372
+ };
373
+
374
+ // src/provider/runtime.ts
375
+ var logger = pino({ name: "@hivemind-os/collective-daemon:provider" });
376
+ var encoder3 = new TextEncoder();
377
+ var decoder3 = new TextDecoder();
378
+ var ProviderRuntime = class {
379
+ constructor(params) {
380
+ this.params = params;
381
+ this.taskQueue = new TaskQueue(params.providerConfig.maxConcurrency);
382
+ }
383
+ params;
384
+ subscription;
385
+ cursorStore;
386
+ taskQueue;
387
+ adapters = /* @__PURE__ */ new Map();
388
+ capabilityConfigs = /* @__PURE__ */ new Map();
389
+ relayClients = [];
390
+ registeredCapabilities = [];
391
+ chainOperations = Promise.resolve();
392
+ ownAgentCardId;
393
+ async start() {
394
+ if (this.subscription) {
395
+ return;
396
+ }
397
+ this.initializeAdapters();
398
+ await this.connectRelays();
399
+ if (this.params.providerConfig.autoRegister) {
400
+ await this.registerAgentCard();
401
+ }
402
+ await this.resolveOwnAgentCardId();
403
+ const eventType = `${this.params.state.network.packageId}::task::TaskPosted`;
404
+ this.cursorStore = new SqliteCursorStore(this.params.cursorDbPath);
405
+ this.subscription = new EventSubscription({
406
+ suiClient: this.params.state.suiClient,
407
+ eventType,
408
+ cursorStore: this.cursorStore,
409
+ pollIntervalMs: 2e3,
410
+ onEvent: async (event) => {
411
+ await this.handleRawEvent(event);
412
+ },
413
+ onError: (error) => {
414
+ logger.error({ err: error }, "Provider subscription polling failed");
415
+ }
416
+ });
417
+ this.subscription.start();
418
+ }
419
+ async stop() {
420
+ this.subscription?.stop();
421
+ this.subscription = void 0;
422
+ await Promise.all(this.relayClients.splice(0).map((client) => client.disconnect()));
423
+ await this.taskQueue.drain();
424
+ this.cursorStore?.close();
425
+ this.cursorStore = void 0;
426
+ }
427
+ async connectRelays() {
428
+ const relayConfig = this.params.relayConfig;
429
+ if (!relayConfig?.enabled || !relayConfig.providerMode || !relayConfig.autoConnect) {
430
+ return;
431
+ }
432
+ const factory = this.params.relayClientFactory ?? ((config, identity) => new RelayClient(config, identity));
433
+ for (const endpoint of relayConfig.endpoints) {
434
+ const client = factory(
435
+ {
436
+ relayUrl: endpoint.url,
437
+ relayDid: endpoint.relayDid,
438
+ reconnectIntervalMs: relayConfig.reconnectIntervalMs,
439
+ heartbeatIntervalMs: relayConfig.heartbeatIntervalMs
440
+ },
441
+ this.params.state.relayAuthProvider
442
+ );
443
+ client.onTaskRequest(async (request) => {
444
+ await client.sendProgress(request.taskId ?? "relay-task", 0.1, "Task accepted by provider runtime");
445
+ const resultData = await this.executeLocalTask(
446
+ request.taskId ?? randomUUID(),
447
+ request.capability,
448
+ encodeRelayInput(request.input)
449
+ );
450
+ await client.sendProgress(request.taskId ?? "relay-task", 1, "Task completed");
451
+ return {
452
+ taskId: request.taskId ?? randomUUID(),
453
+ providerDid: this.params.state.did,
454
+ sequence: 0,
455
+ result: parseExecutionResult(resultData)
456
+ };
457
+ });
458
+ await client.connect(this.registeredCapabilities);
459
+ this.relayClients.push(client);
460
+ }
461
+ }
462
+ async handleRawEvent(event) {
463
+ const parsed = parseRawEvent(event, this.params.state.network.packageId);
464
+ if (parsed?.type !== "task.posted") {
465
+ return;
466
+ }
467
+ await this.handleTaskPosted(parsed);
468
+ }
469
+ async handleTaskPosted(event) {
470
+ const capabilityKey = normalizeCapability(event.task.capability);
471
+ if (!this.registeredCapabilities.some((registered) => normalizeCapability(registered) === capabilityKey)) {
472
+ return;
473
+ }
474
+ if (this.taskQueue.isFull) {
475
+ logger.warn({ taskId: event.task.id, capability: event.task.capability }, "Provider queue is full");
476
+ return;
477
+ }
478
+ this.params.broadcastNotification?.("notifications/mesh/inbound_task", {
479
+ taskId: event.task.id,
480
+ capability: event.task.capability,
481
+ requester: event.task.requester,
482
+ priceMist: event.task.price.toString()
483
+ });
484
+ const queued = await this.taskQueue.enqueue(event.task.id, async () => {
485
+ try {
486
+ await this.processTask(event.task);
487
+ } catch (error) {
488
+ logger.error({ err: error, taskId: event.task.id }, "Task processing failed");
489
+ }
490
+ });
491
+ if (!queued) {
492
+ logger.warn({ taskId: event.task.id }, "Task was not queued");
493
+ }
494
+ }
495
+ async processTask(task) {
496
+ const encryption = this.params.state.encryption ?? { enabled: false, requireEncryption: false };
497
+ await this.runChainOperation(async () => {
498
+ await this.params.state.taskClient.acceptTask({
499
+ taskId: task.id,
500
+ keypair: this.params.state.keypair
501
+ });
502
+ });
503
+ const rawInput = await this.params.state.blobStore.fetch(task.inputBlobId);
504
+ if (!rawInput) {
505
+ throw new Error(`Input blob ${task.inputBlobId} was not found.`);
506
+ }
507
+ const encryptedInput = parseEncryptedPayload(rawInput);
508
+ if (encryption.requireEncryption && !encryptedInput) {
509
+ throw new Error(`Task ${task.id} is missing an encrypted input payload.`);
510
+ }
511
+ const inputData = encryptedInput ? await fetchDecryptedPayload(this.params.state.blobStore, task.inputBlobId) : rawInput;
512
+ const result = await this.executeLocalTask(task.id, task.capability, inputData);
513
+ const requesterCard = await this.params.state.registryClient.getAgentCardByOwner(task.requester);
514
+ const requesterEncryptionKey = decodeHexKey(requesterCard?.encryptionPublicKey);
515
+ if (encryption.requireEncryption && !requesterEncryptionKey) {
516
+ throw new Error(`Task ${task.id} requester does not publish an encryption key.`);
517
+ }
518
+ const completionPayload = prepareCompletionPayload(task, result);
519
+ const storedResult = encryption.enabled && requesterEncryptionKey ? await storeEncryptedPayload(this.params.state.blobStore, completionPayload.resultData, requesterEncryptionKey) : await this.params.state.blobStore.store(completionPayload.resultData);
520
+ await this.runChainOperation(async () => {
521
+ if (completionPayload.metered) {
522
+ await this.params.state.taskClient.completeMeteredTask({
523
+ taskId: task.id,
524
+ resultBlobId: storedResult.blobId,
525
+ meteredUnits: completionPayload.metered.actualUnits,
526
+ verificationHash: completionPayload.metered.verificationHash,
527
+ keypair: this.params.state.keypair,
528
+ providerCardId: this.ownAgentCardId
529
+ });
530
+ return;
531
+ }
532
+ await this.params.state.taskClient.completeTask({
533
+ taskId: task.id,
534
+ resultBlobId: storedResult.blobId,
535
+ keypair: this.params.state.keypair,
536
+ providerCardId: this.ownAgentCardId
537
+ });
538
+ });
539
+ }
540
+ async executeLocalTask(taskId, capability, inputData) {
541
+ const adapter = this.adapters.get(normalizeCapability(capability));
542
+ if (!adapter) {
543
+ throw new Error(`No adapter configured for capability ${capability}.`);
544
+ }
545
+ const result = await adapter.execute({
546
+ taskId,
547
+ capability,
548
+ inputData
549
+ });
550
+ return result.resultData;
551
+ }
552
+ async runChainOperation(operation) {
553
+ const pending = this.chainOperations.then(operation, operation);
554
+ this.chainOperations = pending.then(
555
+ () => void 0,
556
+ () => void 0
557
+ );
558
+ return pending;
559
+ }
560
+ initializeAdapters() {
561
+ this.adapters.clear();
562
+ this.capabilityConfigs.clear();
563
+ this.registeredCapabilities = [];
564
+ const deps = {
565
+ mcpSamplingFn: this.params.mcpSamplingFn
566
+ };
567
+ for (const capability of this.params.providerConfig.capabilities) {
568
+ try {
569
+ const key = normalizeCapability(capability.name);
570
+ const adapter = createAdapter(capability, deps);
571
+ this.adapters.set(key, adapter);
572
+ this.capabilityConfigs.set(key, capability);
573
+ this.registeredCapabilities.push(capability.name);
574
+ } catch (error) {
575
+ logger.warn({ err: error, capability: capability.name }, "Skipping invalid provider capability");
576
+ }
577
+ }
578
+ }
579
+ async registerAgentCard() {
580
+ const encryption = this.params.state.encryption ?? { enabled: false, requireEncryption: false };
581
+ const capabilities = this.params.providerConfig.capabilities.filter((capability) => this.capabilityConfigs.has(normalizeCapability(capability.name))).map((capability) => toCapability(capability, this.hasRelaySupport));
582
+ if (capabilities.length === 0) {
583
+ logger.warn("Skipping auto-registration because no provider capabilities are available");
584
+ return;
585
+ }
586
+ try {
587
+ const registration = await this.params.state.registryClient.registerAgent({
588
+ name: "Agentic Mesh Provider",
589
+ description: `Auto-registered provider for ${this.params.state.did}`,
590
+ did: this.params.state.did,
591
+ capabilities,
592
+ endpoint: this.registrationEndpoint,
593
+ encryptionPublicKey: encryption.enabled ? this.params.state.encryptionKeyPair?.publicKey : void 0,
594
+ keypair: this.params.state.keypair
595
+ });
596
+ this.ownAgentCardId = registration.agentCardId;
597
+ const card = await this.params.state.registryClient.getAgentCard(registration.agentCardId);
598
+ if (card) {
599
+ this.params.state.agentCache.upsertAgent({
600
+ ...card,
601
+ endpoint: this.registrationEndpoint,
602
+ relayEndpoints: this.relayMetadata,
603
+ capabilities
604
+ });
605
+ }
606
+ logger.info({ agentCardId: registration.agentCardId }, "Provider agent card registered");
607
+ } catch (error) {
608
+ logger.error({ err: error }, "Provider auto-registration failed");
609
+ }
610
+ }
611
+ async resolveOwnAgentCardId() {
612
+ const cached = this.params.state.agentCache.getAgentByDID?.(this.params.state.did);
613
+ if (cached?.id) {
614
+ this.ownAgentCardId = cached.id;
615
+ return;
616
+ }
617
+ const discovered = await this.params.state.registryClient.findAgentByDid?.(this.params.state.did);
618
+ if (discovered?.id) {
619
+ this.ownAgentCardId = discovered.id;
620
+ this.params.state.agentCache.upsertAgent(discovered);
621
+ }
622
+ }
623
+ get hasRelaySupport() {
624
+ return this.relayClients.length > 0 && this.relayMetadata.length > 0;
625
+ }
626
+ get relayMetadata() {
627
+ const relayConfig = this.params.relayConfig;
628
+ if (!relayConfig?.enabled || !relayConfig.providerMode) {
629
+ return [];
630
+ }
631
+ return relayConfig.endpoints.map(
632
+ (endpoint) => ({
633
+ relayDid: endpoint.relayDid,
634
+ endpoint: endpoint.url,
635
+ modes: ["sync", "streaming", "fallback"]
636
+ })
637
+ );
638
+ }
639
+ get registrationEndpoint() {
640
+ const relay = this.relayMetadata[0];
641
+ if (!relay) {
642
+ return `mesh://agent/${this.params.state.did}`;
643
+ }
644
+ return relay.endpoint.replace(/^wss:/i, "https:").replace(/^ws:/i, "http:").replace(/\/v1\/ws$/i, "");
645
+ }
646
+ };
647
+ function createAdapter(capability, deps) {
648
+ const config = capability.adapterConfig ?? {};
649
+ switch (capability.adapter) {
650
+ case "echo":
651
+ return new EchoAdapter();
652
+ case "local-function": {
653
+ const fnCandidate = config.fn ?? config.function;
654
+ if (typeof fnCandidate !== "function") {
655
+ throw new Error(`Capability ${capability.name} is missing a local function adapter.`);
656
+ }
657
+ return new LocalFunctionAdapter(fnCandidate);
658
+ }
659
+ case "webhook":
660
+ return new WebhookAdapter({
661
+ url: requireString(config.url, "webhook url"),
662
+ method: optionalString(config.method),
663
+ headers: optionalStringRecord(config.headers),
664
+ timeoutMs: optionalNumber(config.timeoutMs),
665
+ maxResponseBytes: optionalNumber(config.maxResponseBytes)
666
+ });
667
+ case "subprocess":
668
+ return new SubprocessAdapter({
669
+ command: requireString(config.command, "subprocess command"),
670
+ args: optionalStringArray(config.args),
671
+ cwd: optionalString(config.cwd),
672
+ env: optionalStringRecord(config.env),
673
+ timeoutMs: optionalNumber(config.timeoutMs),
674
+ maxOutputBytes: optionalNumber(config.maxOutputBytes)
675
+ });
676
+ case "mcp-sampling": {
677
+ if (!deps.mcpSamplingFn) {
678
+ throw new Error("MCP sampling adapter requires an active IPC server with sampling support.");
679
+ }
680
+ return new McpSamplingAdapter(
681
+ {
682
+ appName: requireString(config.appName, "mcp-sampling appName"),
683
+ systemPrompt: requireString(config.systemPrompt, "mcp-sampling systemPrompt"),
684
+ maxTokens: optionalNumber(config.maxTokens),
685
+ modelHint: optionalString(config.modelHint)
686
+ },
687
+ deps.mcpSamplingFn
688
+ );
689
+ }
690
+ default:
691
+ throw new Error(`Unsupported execution adapter: ${capability.adapter}`);
692
+ }
693
+ }
694
+ function requireString(value, label) {
695
+ if (typeof value !== "string" || value.trim().length === 0) {
696
+ throw new Error(`Missing required config field: ${label}`);
697
+ }
698
+ return value;
699
+ }
700
+ function optionalString(value) {
701
+ return typeof value === "string" ? value : void 0;
702
+ }
703
+ function optionalNumber(value) {
704
+ return typeof value === "number" ? value : void 0;
705
+ }
706
+ function optionalStringArray(value) {
707
+ if (!Array.isArray(value)) return void 0;
708
+ return value.filter((item) => typeof item === "string");
709
+ }
710
+ function optionalStringRecord(value) {
711
+ if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
712
+ const result = {};
713
+ for (const [k, v] of Object.entries(value)) {
714
+ if (typeof v === "string") result[k] = v;
715
+ }
716
+ return result;
717
+ }
718
+ function toCapability(capability, hasRelaySupport) {
719
+ return {
720
+ name: capability.name,
721
+ description: capability.description,
722
+ version: capability.version,
723
+ pricing: {
724
+ rail: PaymentRail.SUI_ESCROW,
725
+ amount: BigInt(capability.priceMist),
726
+ currency: capability.currency ?? "MIST"
727
+ },
728
+ executionMode: hasRelaySupport ? "sync" : "async",
729
+ paymentRails: hasRelaySupport ? [PaymentRail.SUI_TRANSFER, PaymentRail.X402_BASE, PaymentRail.SUI_ESCROW] : [PaymentRail.SUI_ESCROW]
730
+ };
731
+ }
732
+ function normalizeCapability(capability) {
733
+ return capability.trim().toLowerCase();
734
+ }
735
+ function prepareCompletionPayload(task, resultData) {
736
+ if (task.paymentScheme !== PaymentScheme.UPTO && task.paymentScheme !== PaymentScheme.STREAM) {
737
+ return { resultData };
738
+ }
739
+ const meter = new UsageMeter({
740
+ taskId: task.id,
741
+ maxPrice: task.maxPrice ?? task.price,
742
+ unitPrice: task.unitPrice ?? 0n
743
+ });
744
+ for (const unit of splitIntoMeteringUnits(resultData)) {
745
+ meter.recordUnit(unit);
746
+ }
747
+ const envelope = createMeteredResultEnvelope(resultData, meter.getProof());
748
+ return {
749
+ resultData: serializeMeteredResultEnvelope(envelope),
750
+ metered: {
751
+ actualUnits: meter.getActualUnits(),
752
+ verificationHash: meter.getVerificationHash()
753
+ }
754
+ };
755
+ }
756
+ function encodeRelayInput(input) {
757
+ return encoder3.encode(typeof input === "string" ? input : JSON.stringify(input));
758
+ }
759
+ function isEncryptedBlobStore(blobStore) {
760
+ return typeof blobStore.storeEncrypted === "function" && typeof blobStore.fetchDecrypted === "function";
761
+ }
762
+ async function fetchDecryptedPayload(blobStore, blobId) {
763
+ if (!isEncryptedBlobStore(blobStore)) {
764
+ throw new Error("Encrypted payloads require an encrypted blobstore.");
765
+ }
766
+ const data = await blobStore.fetchDecrypted(blobId);
767
+ if (!data) {
768
+ throw new Error(`Input blob ${blobId} was not found.`);
769
+ }
770
+ return data;
771
+ }
772
+ async function storeEncryptedPayload(blobStore, data, recipientPublicKey) {
773
+ if (!isEncryptedBlobStore(blobStore)) {
774
+ throw new Error("Encrypted payloads require an encrypted blobstore.");
775
+ }
776
+ return await blobStore.storeEncrypted(data, recipientPublicKey);
777
+ }
778
+ function decodeHexKey(value) {
779
+ if (!value || value.length !== 64 || !/^[a-f0-9]+$/i.test(value)) {
780
+ return null;
781
+ }
782
+ return new Uint8Array(Buffer.from(value, "hex"));
783
+ }
784
+ function parseExecutionResult(resultData) {
785
+ const text = decoder3.decode(resultData);
786
+ try {
787
+ return JSON.parse(text);
788
+ } catch {
789
+ return text;
790
+ }
791
+ }
792
+
793
+ export {
794
+ EchoAdapter,
795
+ LocalFunctionAdapter,
796
+ McpSamplingAdapter,
797
+ SubprocessAdapter,
798
+ WebhookAdapter,
799
+ loadProviderConfig,
800
+ TaskQueue,
801
+ ProviderRuntime
802
+ };
803
+ //# sourceMappingURL=chunk-57QD6553.js.map