@hol-org/hashnet-mcp 1.0.20 → 1.0.21

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/dist/index.js DELETED
@@ -1,2767 +0,0 @@
1
- // src/transports/index.ts
2
- import http from "http";
3
- import { randomUUID as randomUUID4 } from "crypto";
4
- import { PassThrough } from "stream";
5
-
6
- // src/mcp.ts
7
- import { randomUUID as randomUUID3 } from "crypto";
8
- import { RegistryBrokerError as RegistryBrokerError3 } from "@hashgraphonline/standards-sdk";
9
- import { FastMCP } from "fastmcp";
10
- import { z as z3 } from "zod";
11
-
12
- // src/broker.ts
13
- import { RegistryBrokerClient, RegistryBrokerError } from "@hashgraphonline/standards-sdk";
14
- import Bottleneck from "bottleneck";
15
- import IORedis from "ioredis";
16
- import { fetch as undiciFetch } from "undici";
17
-
18
- // src/config.ts
19
- import { config as loadEnv } from "dotenv";
20
- import { z } from "zod";
21
- if (process.env.NODE_ENV !== "test") {
22
- loadEnv({ quiet: true });
23
- }
24
- var LOG_LEVELS = ["fatal", "error", "warn", "info", "debug", "trace"];
25
- var envSchema = z.object({
26
- REGISTRY_BROKER_API_URL: z.string().url().default("https://hol.org/registry/api/v1"),
27
- REGISTRY_BROKER_API_KEY: z.string().min(1).optional(),
28
- HEDERA_ACCOUNT_ID: z.string().min(1).optional(),
29
- HEDERA_PRIVATE_KEY: z.string().min(1).optional(),
30
- PORT: z.coerce.number().int().positive().default(3333),
31
- BROKER_MAX_CONCURRENT: z.coerce.number().int().positive().optional(),
32
- BROKER_MIN_TIME_MS: z.coerce.number().int().nonnegative().optional(),
33
- BROKER_RESERVOIR: z.coerce.number().int().positive().optional(),
34
- BROKER_RESERVOIR_REFRESH_INTERVAL_MS: z.coerce.number().int().positive().optional(),
35
- BROKER_RESERVOIR_REFRESH_AMOUNT: z.coerce.number().int().positive().optional(),
36
- BROKER_RATE_LIMIT_REDIS_URL: z.string().url().optional(),
37
- LOG_LEVEL: z.enum(LOG_LEVELS).default("info"),
38
- WORKFLOW_DRY_RUN: z.enum(["0", "1"]).optional().transform((value) => value === "1"),
39
- BROKER_AUTO_TOP_UP: z.enum(["0", "1"]).optional().transform((value) => value === "1"),
40
- HTTP_STREAM_PORT: z.coerce.number().int().positive().optional(),
41
- MEMORY_ENABLED: z.enum(["0", "1"]).optional().transform((value) => value === "1"),
42
- MEMORY_STORE: z.enum(["file", "sqlite", "rocksdb", "memory", "redis"]).optional(),
43
- MEMORY_STORAGE_PATH: z.string().default("tmp/memory.json"),
44
- MEMORY_MAX_ENTRIES_PER_SCOPE: z.coerce.number().int().positive().optional(),
45
- MEMORY_DEFAULT_TTL_SECONDS: z.coerce.number().int().positive().optional(),
46
- MEMORY_SUMMARY_TRIGGER: z.coerce.number().int().positive().optional(),
47
- MEMORY_MAX_RETURN_ENTRIES: z.coerce.number().int().positive().optional(),
48
- MEMORY_CAPTURE_TOOLS: z.enum(["0", "1"]).optional().transform((value) => value === "1")
49
- }).superRefine((val, ctx) => {
50
- const hasAccount = Boolean(val.HEDERA_ACCOUNT_ID);
51
- const hasKey = Boolean(val.HEDERA_PRIVATE_KEY);
52
- if (hasAccount !== hasKey) {
53
- ctx.addIssue({
54
- code: z.ZodIssueCode.custom,
55
- message: "HEDERA_ACCOUNT_ID and HEDERA_PRIVATE_KEY must both be set to enable registrationAutoTopUp."
56
- });
57
- }
58
- });
59
- var normalized = (value) => {
60
- if (value === void 0) return void 0;
61
- const trimmed = value.trim();
62
- return trimmed.length === 0 ? void 0 : trimmed;
63
- };
64
- var parsed = envSchema.safeParse({
65
- REGISTRY_BROKER_API_URL: normalized(process.env.REGISTRY_BROKER_API_URL),
66
- REGISTRY_BROKER_API_KEY: normalized(process.env.REGISTRY_BROKER_API_KEY),
67
- HEDERA_ACCOUNT_ID: normalized(process.env.HEDERA_ACCOUNT_ID),
68
- HEDERA_PRIVATE_KEY: normalized(process.env.HEDERA_PRIVATE_KEY),
69
- PORT: normalized(process.env.PORT),
70
- BROKER_MAX_CONCURRENT: normalized(process.env.BROKER_MAX_CONCURRENT),
71
- BROKER_MIN_TIME_MS: normalized(process.env.BROKER_MIN_TIME_MS),
72
- BROKER_RESERVOIR: normalized(process.env.BROKER_RESERVOIR),
73
- BROKER_RESERVOIR_REFRESH_INTERVAL_MS: normalized(process.env.BROKER_RESERVOIR_REFRESH_INTERVAL_MS),
74
- BROKER_RESERVOIR_REFRESH_AMOUNT: normalized(process.env.BROKER_RESERVOIR_REFRESH_AMOUNT),
75
- BROKER_RATE_LIMIT_REDIS_URL: normalized(process.env.BROKER_RATE_LIMIT_REDIS_URL),
76
- LOG_LEVEL: normalized(process.env.LOG_LEVEL),
77
- WORKFLOW_DRY_RUN: normalized(process.env.WORKFLOW_DRY_RUN),
78
- BROKER_AUTO_TOP_UP: normalized(process.env.BROKER_AUTO_TOP_UP),
79
- HTTP_STREAM_PORT: normalized(process.env.HTTP_STREAM_PORT),
80
- MEMORY_ENABLED: normalized(process.env.MEMORY_ENABLED),
81
- MEMORY_STORE: normalized(process.env.MEMORY_STORE),
82
- MEMORY_STORAGE_PATH: normalized(process.env.MEMORY_STORAGE_PATH),
83
- MEMORY_MAX_ENTRIES_PER_SCOPE: normalized(process.env.MEMORY_MAX_ENTRIES_PER_SCOPE),
84
- MEMORY_DEFAULT_TTL_SECONDS: normalized(process.env.MEMORY_DEFAULT_TTL_SECONDS),
85
- MEMORY_SUMMARY_TRIGGER: normalized(process.env.MEMORY_SUMMARY_TRIGGER),
86
- MEMORY_MAX_RETURN_ENTRIES: normalized(process.env.MEMORY_MAX_RETURN_ENTRIES),
87
- MEMORY_CAPTURE_TOOLS: normalized(process.env.MEMORY_CAPTURE_TOOLS)
88
- });
89
- if (!parsed.success) {
90
- throw new Error(`Invalid environment configuration:
91
- ${parsed.error.toString()}`);
92
- }
93
- var config = {
94
- registryBrokerUrl: parsed.data.REGISTRY_BROKER_API_URL,
95
- registryBrokerApiKey: parsed.data.REGISTRY_BROKER_API_KEY,
96
- hederaAccountId: parsed.data.HEDERA_ACCOUNT_ID,
97
- hederaPrivateKey: parsed.data.HEDERA_PRIVATE_KEY,
98
- port: parsed.data.PORT,
99
- autoTopUpEnabled: Boolean(parsed.data.BROKER_AUTO_TOP_UP) && Boolean(parsed.data.HEDERA_ACCOUNT_ID && parsed.data.HEDERA_PRIVATE_KEY),
100
- rateLimit: (() => {
101
- const {
102
- BROKER_MAX_CONCURRENT,
103
- BROKER_MIN_TIME_MS,
104
- BROKER_RESERVOIR,
105
- BROKER_RESERVOIR_REFRESH_AMOUNT,
106
- BROKER_RESERVOIR_REFRESH_INTERVAL_MS,
107
- BROKER_RATE_LIMIT_REDIS_URL
108
- } = parsed.data;
109
- const hasLimiter = BROKER_MAX_CONCURRENT || BROKER_MIN_TIME_MS || BROKER_RESERVOIR || BROKER_RATE_LIMIT_REDIS_URL;
110
- if (!hasLimiter) return void 0;
111
- return {
112
- maxConcurrent: BROKER_MAX_CONCURRENT,
113
- minTimeMs: BROKER_MIN_TIME_MS,
114
- reservoir: BROKER_RESERVOIR,
115
- reservoirRefreshAmount: BROKER_RESERVOIR_REFRESH_AMOUNT,
116
- reservoirRefreshIntervalMs: BROKER_RESERVOIR_REFRESH_INTERVAL_MS,
117
- redis: BROKER_RATE_LIMIT_REDIS_URL ? {
118
- url: BROKER_RATE_LIMIT_REDIS_URL
119
- } : void 0
120
- };
121
- })(),
122
- workflowDryRun: parsed.data.WORKFLOW_DRY_RUN ?? false,
123
- httpStreamPort: parsed.data.HTTP_STREAM_PORT,
124
- logLevel: parsed.data.LOG_LEVEL,
125
- memory: {
126
- enabled: parsed.data.MEMORY_ENABLED ?? false,
127
- store: parsed.data.MEMORY_STORE ?? "file",
128
- path: parsed.data.MEMORY_STORAGE_PATH ?? "tmp/memory.json",
129
- maxEntriesPerScope: parsed.data.MEMORY_MAX_ENTRIES_PER_SCOPE ?? 200,
130
- defaultTtlSeconds: parsed.data.MEMORY_DEFAULT_TTL_SECONDS ?? 24 * 60 * 60,
131
- summaryTrigger: parsed.data.MEMORY_SUMMARY_TRIGGER ?? 100,
132
- maxReturnEntries: parsed.data.MEMORY_MAX_RETURN_ENTRIES ?? 50,
133
- captureTools: parsed.data.MEMORY_CAPTURE_TOOLS ?? true
134
- }
135
- };
136
-
137
- // src/broker.ts
138
- var autoTopUpConfig = config.autoTopUpEnabled ? {
139
- accountId: config.hederaAccountId,
140
- privateKey: config.hederaPrivateKey,
141
- memo: "mcp-autotopup"
142
- } : void 0;
143
- var broker = new RegistryBrokerClient({
144
- baseUrl: normalizeRegistryUrl(config.registryBrokerUrl),
145
- apiKey: config.registryBrokerApiKey,
146
- fetchImplementation: undiciFetch,
147
- registrationAutoTopUp: autoTopUpConfig,
148
- historyAutoTopUp: autoTopUpConfig
149
- });
150
- var brokerLimiter = createLimiter();
151
- function createLimiter() {
152
- if (!config.rateLimit) return void 0;
153
- const redisClient = config.rateLimit.redis?.url ? new IORedis(config.rateLimit.redis.url) : void 0;
154
- const limiterOptions = {
155
- ...config.rateLimit.maxConcurrent !== void 0 ? { maxConcurrent: config.rateLimit.maxConcurrent } : {},
156
- ...config.rateLimit.minTimeMs !== void 0 ? { minTime: config.rateLimit.minTimeMs } : {},
157
- ...config.rateLimit.reservoir !== void 0 ? { reservoir: config.rateLimit.reservoir } : {},
158
- ...config.rateLimit.reservoirRefreshAmount !== void 0 ? { reservoirRefreshAmount: config.rateLimit.reservoirRefreshAmount } : {},
159
- ...config.rateLimit.reservoirRefreshIntervalMs !== void 0 ? { reservoirRefreshInterval: config.rateLimit.reservoirRefreshIntervalMs } : {},
160
- ...redisClient ? { datastore: "ioredis", connection: redisClient } : {}
161
- };
162
- if (!limiterOptions.maxConcurrent && !limiterOptions.minTime && !limiterOptions.reservoir && !limiterOptions.datastore) {
163
- return void 0;
164
- }
165
- return new Bottleneck(limiterOptions);
166
- }
167
- async function withBroker(task, label) {
168
- const run = async () => {
169
- if (!config.registryBrokerApiKey) {
170
- throw new Error("REGISTRY_BROKER_API_KEY is required to call the registry broker. Set it in your environment or .env file.");
171
- }
172
- try {
173
- return await task(broker);
174
- } catch (error) {
175
- throw formatBrokerError(error, label);
176
- }
177
- };
178
- if (brokerLimiter) {
179
- return brokerLimiter.schedule(run);
180
- }
181
- return run();
182
- }
183
- async function getCreditBalance(accountId) {
184
- if (!config.registryBrokerApiKey) {
185
- throw new Error("REGISTRY_BROKER_API_KEY is required to fetch credit balances.");
186
- }
187
- const base = config.registryBrokerUrl.endsWith("/") ? config.registryBrokerUrl : `${config.registryBrokerUrl}/`;
188
- const url = new URL("credits/balance", base);
189
- if (accountId) {
190
- url.searchParams.set("accountId", accountId);
191
- }
192
- const headers = {
193
- accept: "application/json",
194
- "x-api-key": config.registryBrokerApiKey
195
- };
196
- const request = async () => {
197
- const response = await undiciFetch(url, { method: "GET", headers });
198
- if (!response.ok) {
199
- const hint = await safeReadBody(response);
200
- throw new Error(`Failed to fetch credit balance (${response.status}): ${hint ?? response.statusText}`);
201
- }
202
- return await response.json();
203
- };
204
- if (brokerLimiter) {
205
- return brokerLimiter.schedule(request);
206
- }
207
- return request();
208
- }
209
- async function safeReadBody(response) {
210
- try {
211
- const text = await response.text();
212
- return text || void 0;
213
- } catch {
214
- return void 0;
215
- }
216
- }
217
- function formatBrokerError(error, label) {
218
- if (error instanceof RegistryBrokerError) {
219
- const body = typeof error.body === "object" ? JSON.stringify(error.body) : error.body ? String(error.body) : "no response body";
220
- const statusText = error.statusText ? ` ${error.statusText}` : "";
221
- const prefix = label ? `${label} failed` : "Registry broker request failed";
222
- return new Error(`${prefix} (${error.status}${statusText}): ${body}`);
223
- }
224
- return error instanceof Error ? error : new Error(String(error));
225
- }
226
- function normalizeRegistryUrl(rawUrl) {
227
- try {
228
- const parsed2 = new URL(rawUrl);
229
- if (parsed2.hostname === "registry.hashgraphonline.com") {
230
- parsed2.hostname = "hol.org";
231
- parsed2.pathname = "/registry/api/v1";
232
- parsed2.search = "";
233
- parsed2.hash = "";
234
- return stripTrailingSlash(parsed2.toString());
235
- }
236
- const cleanPath = parsed2.pathname.replace(/\/+$/, "");
237
- parsed2.pathname = cleanPath.endsWith("/api/v1") ? cleanPath : `${cleanPath || ""}/api/v1`;
238
- return stripTrailingSlash(parsed2.toString());
239
- } catch {
240
- return rawUrl;
241
- }
242
- }
243
- function stripTrailingSlash(value) {
244
- return value.endsWith("/") ? value.slice(0, -1) : value;
245
- }
246
-
247
- // src/logger.ts
248
- import pino from "pino";
249
- var logger = pino(
250
- {
251
- level: config.logLevel,
252
- base: void 0
253
- },
254
- pino.destination(2)
255
- );
256
-
257
- // src/schemas/agent.ts
258
- import { z as z2 } from "zod";
259
- var socialLinkSchema = z2.object({
260
- platform: z2.string(),
261
- handle: z2.string()
262
- });
263
- var aiAgentSchema = z2.object({
264
- type: z2.union([z2.literal(0), z2.literal(1)]),
265
- capabilities: z2.array(z2.number().int().nonnegative()),
266
- model: z2.string(),
267
- creator: z2.string().optional()
268
- });
269
- var mcpServerSchema = z2.object({
270
- version: z2.string(),
271
- connectionInfo: z2.object({
272
- url: z2.string().url(),
273
- transport: z2.enum(["stdio", "sse"])
274
- }),
275
- services: z2.array(z2.number().int().nonnegative()),
276
- description: z2.string(),
277
- capabilities: z2.array(z2.string()).optional(),
278
- resources: z2.array(
279
- z2.object({
280
- name: z2.string(),
281
- description: z2.string()
282
- })
283
- ).optional(),
284
- tools: z2.array(
285
- z2.object({
286
- name: z2.string(),
287
- description: z2.string()
288
- })
289
- ).optional(),
290
- maintainer: z2.string().optional(),
291
- repository: z2.string().optional(),
292
- docs: z2.string().optional()
293
- });
294
- var baseProfileSchema = z2.object({
295
- version: z2.string(),
296
- type: z2.number().int(),
297
- display_name: z2.string(),
298
- alias: z2.string().optional(),
299
- bio: z2.string().optional(),
300
- socials: z2.array(socialLinkSchema).optional(),
301
- profileImage: z2.string().optional(),
302
- uaid: z2.string().optional(),
303
- properties: z2.record(z2.string(), z2.any()).optional(),
304
- inboundTopicId: z2.string().optional(),
305
- outboundTopicId: z2.string().optional(),
306
- base_account: z2.string().optional()
307
- });
308
- var agentProfileSchema = baseProfileSchema.extend({
309
- aiAgent: aiAgentSchema.optional(),
310
- mcpServer: mcpServerSchema.optional()
311
- });
312
- var metadataSchema = z2.object({
313
- trustScore: z2.number().min(0).max(100).optional(),
314
- verified: z2.boolean().optional(),
315
- avgLatency: z2.number().nonnegative().optional(),
316
- uptime: z2.number().min(0).max(100).optional(),
317
- provider: z2.string().optional(),
318
- category: z2.string().optional(),
319
- adapter: z2.string().optional(),
320
- openConvAICompatible: z2.boolean().optional(),
321
- customFields: z2.record(z2.string(), z2.union([z2.string(), z2.number(), z2.boolean()])).optional()
322
- });
323
- var agentRegistrationSchema = z2.object({
324
- profile: agentProfileSchema,
325
- endpoint: z2.string().url().optional(),
326
- protocol: z2.string().optional(),
327
- communicationProtocol: z2.string().optional(),
328
- registry: z2.string().optional(),
329
- additionalRegistries: z2.array(z2.string()).optional(),
330
- metadata: metadataSchema.optional()
331
- });
332
-
333
- // src/workflows/env.ts
334
- function ensureRequiredEnv(requiredEnv, options) {
335
- if (!requiredEnv?.length) return;
336
- const missing = getMissingEnvVars(requiredEnv);
337
- if (missing.length > 0) {
338
- options?.logger?.error?.({ missingEnv: missing }, "workflow.env.missing");
339
- const label = options?.context ?? "workflow";
340
- throw new Error(`Missing required environment variables for ${label}: ${missing.join(", ")}`);
341
- }
342
- }
343
- function getMissingEnvVars(requiredEnv) {
344
- return requiredEnv.filter((variable) => {
345
- const value = process.env[variable];
346
- return value === void 0 || value.length === 0;
347
- });
348
- }
349
-
350
- // src/workflows/pipeline.ts
351
- function createPipeline(definition) {
352
- async function run(input, options) {
353
- const pipelineLogger = logger.child({ pipeline: definition.name });
354
- const dryRun = options?.dryRun ?? config.workflowDryRun;
355
- ensureRequiredEnv(definition.requiredEnv, { logger: pipelineLogger, context: definition.name });
356
- const context = await definition.createContext(input);
357
- const stepsResults = [];
358
- for (let index = 0; index < definition.steps.length; index += 1) {
359
- const step = definition.steps[index];
360
- const stepLogger = pipelineLogger.child({ step: step.name, index });
361
- const stepArgs = {
362
- input,
363
- context,
364
- dryRun,
365
- logger: stepLogger
366
- };
367
- const shouldSkip = await shouldSkipStep(step, stepArgs, dryRun);
368
- const startedAt = Date.now();
369
- if (shouldSkip) {
370
- stepLogger.info({ dryRun }, "pipeline.step.skipped");
371
- stepsResults.push({ name: step.name, durationMs: 0, skipped: true });
372
- await options?.hooks?.onStepStart?.({ pipeline: definition.name, step: step.name, index, context });
373
- await options?.hooks?.onStepSuccess?.({ pipeline: definition.name, step: step.name, index, context, output: void 0 });
374
- continue;
375
- }
376
- pipelineLogger.info({ step: step.name }, "pipeline.step.start");
377
- await options?.hooks?.onStepStart?.({ pipeline: definition.name, step: step.name, index, context });
378
- try {
379
- const output = await step.run(stepArgs);
380
- const durationMs = Date.now() - startedAt;
381
- stepLogger.info({ durationMs }, "pipeline.step.success");
382
- stepsResults.push({ name: step.name, durationMs, skipped: false, output });
383
- await options?.hooks?.onStepSuccess?.({ pipeline: definition.name, step: step.name, index, context, output });
384
- } catch (error) {
385
- const durationMs = Date.now() - startedAt;
386
- const message = error instanceof Error ? error.message : String(error);
387
- stepLogger.error({ durationMs, error: message }, "pipeline.step.error");
388
- stepsResults.push({ name: step.name, durationMs, skipped: false, error: message });
389
- await options?.hooks?.onStepError?.({ pipeline: definition.name, step: step.name, index, context, error });
390
- throw error;
391
- }
392
- }
393
- return {
394
- pipeline: definition.name,
395
- context,
396
- steps: stepsResults,
397
- dryRun
398
- };
399
- }
400
- return { definition, run };
401
- }
402
- async function shouldSkipStep(step, args, dryRun) {
403
- if (dryRun && step.allowDuringDryRun !== true) {
404
- return true;
405
- }
406
- if (step.skip) {
407
- return Boolean(await step.skip(args));
408
- }
409
- return false;
410
- }
411
-
412
- // src/workflows/registry.ts
413
- var pipelines = /* @__PURE__ */ new Map();
414
- function registerPipeline(definition) {
415
- const pipeline = createPipeline(definition);
416
- pipelines.set(definition.name, pipeline);
417
- return pipeline;
418
- }
419
-
420
- // src/workflows/discovery.ts
421
- var discoveryDefinition = {
422
- name: "workflow.discovery",
423
- description: "Run hol.search and hol.vectorSearch to explore agents.",
424
- version: "1.0.0",
425
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
426
- createContext: () => ({ results: {} }),
427
- steps: [
428
- {
429
- name: "hol.search",
430
- description: "Keyword search",
431
- allowDuringDryRun: true,
432
- run: async ({ input, context }) => {
433
- const payload = { q: input.query, limit: input.limit ?? 5 };
434
- const response = await withBroker((client) => client.search(payload));
435
- context.results.search = response;
436
- return response;
437
- }
438
- },
439
- {
440
- name: "hol.vectorSearch",
441
- description: "Vector similarity search",
442
- allowDuringDryRun: true,
443
- run: async ({ input, context }) => {
444
- if (!input.query) {
445
- return void 0;
446
- }
447
- const response = await withBroker((client) => client.vectorSearch({ query: input.query, limit: input.limit ?? 5 }));
448
- context.results.vector = response;
449
- return response;
450
- }
451
- }
452
- ]
453
- };
454
- var discoveryPipeline = registerPipeline(discoveryDefinition);
455
-
456
- // src/workflows/utils/credits.ts
457
- import { RegistryBrokerError as RegistryBrokerError2 } from "@hashgraphonline/standards-sdk";
458
-
459
- // src/workflows/errors.ts
460
- var InsufficientCreditsError = class extends Error {
461
- code = "INSUFFICIENT_CREDITS";
462
- summary;
463
- constructor(quote, hint) {
464
- const shortfall = Math.max(0, quote.shortfallCredits ?? 0);
465
- const message = hint ?? `Insufficient registry credits (shortfall: ${shortfall})`;
466
- super(message);
467
- this.name = "InsufficientCreditsError";
468
- this.summary = {
469
- requiredCredits: quote.requiredCredits ?? 0,
470
- availableCredits: quote.availableCredits ?? 0,
471
- shortfallCredits: shortfall,
472
- estimatedHbar: quote.estimatedHbar,
473
- creditsPerHbar: quote.creditsPerHbar,
474
- registry: quote.registry,
475
- protocol: quote.protocol,
476
- accountId: quote.accountId
477
- };
478
- }
479
- };
480
-
481
- // src/workflows/utils/credits.ts
482
- async function runCreditAwareRegistration({ payload, onShortfall }) {
483
- while (true) {
484
- try {
485
- const response = await withBroker((client) => client.registerAgent(payload));
486
- return response;
487
- } catch (error) {
488
- const converted = await translateCreditError(error, payload);
489
- if (converted) {
490
- const action = await onShortfall?.(converted) ?? "abort";
491
- if (action === "retry") {
492
- continue;
493
- }
494
- throw converted;
495
- }
496
- throw error;
497
- }
498
- }
499
- }
500
- async function translateCreditError(error, payload) {
501
- if (!(error instanceof RegistryBrokerError2) || error.status !== 402) {
502
- return null;
503
- }
504
- const quote = await withBroker((client) => client.getRegistrationQuote(payload));
505
- return new InsufficientCreditsError(quote);
506
- }
507
- async function waitForRegistrationCompletion(attemptId) {
508
- return withBroker(
509
- (client) => client.waitForRegistrationCompletion(attemptId, {
510
- intervalMs: 2e3,
511
- timeoutMs: 5 * 6e4
512
- })
513
- );
514
- }
515
-
516
- // src/workflows/registration.ts
517
- var registrationDefinition = {
518
- name: "workflow.registerMcp",
519
- description: "Quote, register, and wait for completion.",
520
- version: "1.0.0",
521
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
522
- createContext: ({ payload }) => ({ payload }),
523
- steps: [
524
- {
525
- name: "hol.getRegistrationQuote",
526
- allowDuringDryRun: true,
527
- run: async ({ context }) => {
528
- const quote = await withBroker((client) => client.getRegistrationQuote(context.payload));
529
- context.quote = quote;
530
- return quote;
531
- }
532
- },
533
- {
534
- name: "hol.registerAgent",
535
- run: async ({ context }) => {
536
- const response = await runCreditAwareRegistration({
537
- payload: context.payload,
538
- onShortfall: (err) => {
539
- context.quote = err.summary;
540
- return "abort";
541
- }
542
- });
543
- if ("attemptId" in response && typeof response.attemptId === "string") {
544
- context.attemptId = response.attemptId;
545
- }
546
- context.result = response;
547
- return response;
548
- }
549
- },
550
- {
551
- name: "hol.waitForRegistrationCompletion",
552
- run: async ({ context }) => {
553
- if (!context.attemptId) {
554
- throw new Error("Registration attemptId missing.");
555
- }
556
- const result = await withBroker(
557
- (client) => client.waitForRegistrationCompletion(context.attemptId, {
558
- intervalMs: 2e3,
559
- timeoutMs: 5 * 6e4
560
- })
561
- );
562
- if (result && typeof result === "object" && "result" in result) {
563
- const progress = result;
564
- const maybeUaid = progress.result?.uaid;
565
- if (typeof maybeUaid === "string") {
566
- context.uaid = maybeUaid;
567
- }
568
- }
569
- return result;
570
- }
571
- }
572
- ]
573
- };
574
- var registrationPipeline = registerPipeline(registrationDefinition);
575
-
576
- // src/memory/factory.ts
577
- import { createRequire } from "module";
578
-
579
- // src/memory/in-memory-store.ts
580
- import { randomUUID } from "crypto";
581
-
582
- // src/memory/types.ts
583
- function scopeKey(scope) {
584
- return [scope.uaid ?? "", scope.sessionId ?? "", scope.namespace ?? "", scope.userId ?? ""].join("::");
585
- }
586
-
587
- // src/memory/in-memory-store.ts
588
- var InMemoryMemoryStore = class {
589
- entries = /* @__PURE__ */ new Map();
590
- summaries = /* @__PURE__ */ new Map();
591
- async append(entry) {
592
- const now = Date.now();
593
- const key = scopeKey(entry.scope);
594
- const record = {
595
- ...entry,
596
- id: randomUUID(),
597
- createdAt: now
598
- };
599
- const existing = this.entries.get(key) ?? [];
600
- existing.push(record);
601
- this.entries.set(key, existing);
602
- return record;
603
- }
604
- async listRecent(scope, limit) {
605
- const key = scopeKey(scope);
606
- const items = this.entries.get(key) ?? [];
607
- return this.filterLive(items).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
608
- }
609
- async trim(scope, maxEntries) {
610
- const key = scopeKey(scope);
611
- const items = this.filterLive(this.entries.get(key) ?? []).sort((a, b) => b.createdAt - a.createdAt);
612
- const toKeep = items.slice(0, maxEntries);
613
- const trimmed = items.length - toKeep.length;
614
- this.entries.set(key, toKeep);
615
- return trimmed;
616
- }
617
- async clear(scope) {
618
- const key = scopeKey(scope);
619
- const count = (this.entries.get(key) ?? []).length;
620
- this.entries.delete(key);
621
- this.summaries.delete(key);
622
- return count;
623
- }
624
- async search(scope, query, limit) {
625
- const key = scopeKey(scope);
626
- const items = this.filterLive(this.entries.get(key) ?? []);
627
- const normalized2 = query.toLowerCase();
628
- return items.filter((item) => item.content.toLowerCase().includes(normalized2)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
629
- }
630
- async getSummary(scope) {
631
- const key = scopeKey(scope);
632
- return this.summaries.get(key) ?? null;
633
- }
634
- async upsertSummary(summary) {
635
- const key = scopeKey(summary.scope);
636
- this.summaries.set(key, summary);
637
- }
638
- async purgeExpired(nowMs) {
639
- let removed = 0;
640
- for (const [key, values] of this.entries.entries()) {
641
- const live = values.filter((entry) => !entry.expiresAt || entry.expiresAt > nowMs);
642
- removed += values.length - live.length;
643
- this.entries.set(key, live);
644
- }
645
- return removed;
646
- }
647
- filterLive(entries) {
648
- const now = Date.now();
649
- return entries.filter((entry) => !entry.expiresAt || entry.expiresAt > now);
650
- }
651
- };
652
-
653
- // src/memory/file-store.ts
654
- import fs from "fs";
655
- import path from "path";
656
- import { randomUUID as randomUUID2 } from "crypto";
657
- var FileMemoryStore = class {
658
- filePath;
659
- data;
660
- constructor(filePath) {
661
- this.filePath = filePath;
662
- this.data = { entries: [], summaries: [] };
663
- this.ensureLoaded();
664
- }
665
- async append(entry) {
666
- const record = {
667
- ...entry,
668
- id: randomUUID2(),
669
- createdAt: Date.now()
670
- };
671
- this.data.entries.push(record);
672
- await this.save();
673
- return record;
674
- }
675
- async listRecent(scope, limit) {
676
- const key = scopeKey(scope);
677
- const now = Date.now();
678
- return this.data.entries.filter((entry) => scopeKey(entry.scope) === key).filter((entry) => !entry.expiresAt || entry.expiresAt > now).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
679
- }
680
- async trim(scope, maxEntries) {
681
- const key = scopeKey(scope);
682
- const scoped = this.data.entries.filter((entry) => scopeKey(entry.scope) === key).sort((a, b) => b.createdAt - a.createdAt);
683
- const keep = scoped.slice(0, maxEntries).map((entry) => entry.id);
684
- const before = this.data.entries.length;
685
- this.data.entries = this.data.entries.filter((entry) => scopeKey(entry.scope) !== key || keep.includes(entry.id));
686
- const removed = before - this.data.entries.length;
687
- if (removed > 0) {
688
- await this.save();
689
- }
690
- return removed;
691
- }
692
- async clear(scope) {
693
- const key = scopeKey(scope);
694
- const beforeEntries = this.data.entries.length;
695
- const beforeSummaries = this.data.summaries.length;
696
- this.data.entries = this.data.entries.filter((entry) => scopeKey(entry.scope) !== key);
697
- this.data.summaries = this.data.summaries.filter((summary) => scopeKey(summary.scope) !== key);
698
- const removed = beforeEntries - this.data.entries.length + (beforeSummaries - this.data.summaries.length);
699
- if (removed > 0) {
700
- await this.save();
701
- }
702
- return removed;
703
- }
704
- async search(scope, query, limit) {
705
- const key = scopeKey(scope);
706
- const normalized2 = query.toLowerCase();
707
- const now = Date.now();
708
- return this.data.entries.filter((entry) => scopeKey(entry.scope) === key).filter((entry) => !entry.expiresAt || entry.expiresAt > now).filter((entry) => entry.content.toLowerCase().includes(normalized2)).sort((a, b) => b.createdAt - a.createdAt).slice(0, limit);
709
- }
710
- async getSummary(scope) {
711
- const key = scopeKey(scope);
712
- return this.data.summaries.find((summary) => scopeKey(summary.scope) === key) ?? null;
713
- }
714
- async upsertSummary(summary) {
715
- const key = scopeKey(summary.scope);
716
- const existingIndex = this.data.summaries.findIndex((entry) => scopeKey(entry.scope) === key);
717
- if (existingIndex >= 0) {
718
- this.data.summaries[existingIndex] = summary;
719
- } else {
720
- this.data.summaries.push(summary);
721
- }
722
- await this.save();
723
- }
724
- async purgeExpired(nowMs) {
725
- const before = this.data.entries.length;
726
- this.data.entries = this.data.entries.filter((entry) => !entry.expiresAt || entry.expiresAt > nowMs);
727
- const removed = before - this.data.entries.length;
728
- if (removed > 0) {
729
- await this.save();
730
- }
731
- return removed;
732
- }
733
- ensureLoaded() {
734
- const dir = path.dirname(this.filePath);
735
- if (!fs.existsSync(dir)) {
736
- fs.mkdirSync(dir, { recursive: true });
737
- }
738
- if (!fs.existsSync(this.filePath)) {
739
- this.saveSync();
740
- return;
741
- }
742
- try {
743
- const content = fs.readFileSync(this.filePath, "utf8");
744
- const parsed2 = JSON.parse(content);
745
- this.data = {
746
- entries: parsed2.entries ?? [],
747
- summaries: parsed2.summaries ?? []
748
- };
749
- } catch {
750
- this.data = { entries: [], summaries: [] };
751
- this.saveSync();
752
- }
753
- }
754
- async save() {
755
- await fs.promises.writeFile(this.filePath, JSON.stringify(this.data, null, 2), "utf8");
756
- }
757
- saveSync() {
758
- fs.writeFileSync(this.filePath, JSON.stringify(this.data, null, 2), "utf8");
759
- }
760
- };
761
-
762
- // src/memory/factory.ts
763
- function createMemoryStore(config2) {
764
- switch (config2.store) {
765
- case "file":
766
- return new FileMemoryStore(config2.path ?? "tmp/memory.json");
767
- case "sqlite":
768
- return loadSqliteStore(config2.path ?? "tmp/memory.db");
769
- case "memory":
770
- return new InMemoryMemoryStore();
771
- case "rocksdb":
772
- throw new Error("RocksDB backend not yet implemented. Use MEMORY_STORE=sqlite or memory for now.");
773
- case "redis":
774
- throw new Error("Redis backend not yet implemented. Use MEMORY_STORE=sqlite or memory for now.");
775
- default:
776
- throw new Error(`Unsupported memory store "${config2.store}".`);
777
- }
778
- }
779
- function loadSqliteStore(filePath) {
780
- const require2 = createRequire(import.meta.url);
781
- const module = require2("./sqlite-store");
782
- return new module.SqliteMemoryStore(filePath);
783
- }
784
-
785
- // src/memory/service.ts
786
- var DEFAULT_MAX_CHARS = 4e3;
787
- var REDACT_KEYS = ["privateKey", "apiKey", "token", "authorization", "auth", "evmPrivateKey", "password"];
788
- var MemoryService = class {
789
- store;
790
- config;
791
- logger;
792
- constructor(store, memoryConfig, logger2) {
793
- this.store = store;
794
- this.config = memoryConfig;
795
- this.logger = logger2.child({ module: "memory" });
796
- }
797
- isEnabled() {
798
- return this.config.enabled;
799
- }
800
- async recordEntry(params) {
801
- if (!this.config.enabled) return null;
802
- const content = truncate(params.content, DEFAULT_MAX_CHARS);
803
- const ttlSeconds = params.ttlSeconds ?? this.config.defaultTtlSeconds;
804
- const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1e3 : void 0;
805
- const entry = await this.store.append({
806
- scope: params.scope,
807
- role: params.role,
808
- content,
809
- toolName: params.toolName,
810
- metadata: params.metadata ? redact(params.metadata) : void 0,
811
- expiresAt
812
- });
813
- await this.store.trim(params.scope, this.config.maxEntriesPerScope);
814
- await this.store.purgeExpired(Date.now());
815
- if (this.config.summaryTrigger && this.config.summaryTrigger > 0) {
816
- void this.maybeSummarize(params.scope).catch((error) => {
817
- this.logger.warn({ error }, "memory.summarize.failed");
818
- });
819
- }
820
- return entry;
821
- }
822
- async recordToolEvent(toolName, scope, payload) {
823
- if (!this.config.enabled || !this.config.captureTools) return;
824
- const excerpt = truncate(serialize(payload), DEFAULT_MAX_CHARS);
825
- await this.recordEntry({
826
- scope,
827
- role: "tool",
828
- content: `[${toolName}] ${excerpt}`,
829
- toolName
830
- });
831
- }
832
- async note(scope, content) {
833
- return this.recordEntry({ scope, role: "note", content });
834
- }
835
- async getContext(params) {
836
- const limit = clamp(params.limit ?? this.config.maxReturnEntries, 1, this.config.maxReturnEntries);
837
- const entries = await this.store.listRecent(params.scope, limit);
838
- const summary = params.includeSummary ? await this.store.getSummary(params.scope) : void 0;
839
- return { entries, summary };
840
- }
841
- async search(params) {
842
- const limit = clamp(params.limit ?? this.config.maxReturnEntries, 1, this.config.maxReturnEntries);
843
- return this.store.search(params.scope, params.query, limit);
844
- }
845
- async clear(scope) {
846
- return this.store.clear(scope);
847
- }
848
- async summarize(scope) {
849
- if (!this.config.enabled) return null;
850
- const entries = await this.store.listRecent(scope, this.config.summaryTrigger ?? this.config.maxReturnEntries);
851
- if (!entries.length) return null;
852
- const summary = buildHeuristicSummary(entries);
853
- const record = { scope, content: summary, updatedAt: Date.now() };
854
- await this.store.upsertSummary(record);
855
- return record;
856
- }
857
- async maybeSummarize(scope) {
858
- if (!this.config.summaryTrigger) return;
859
- const entries = await this.store.listRecent(scope, this.config.summaryTrigger + 5);
860
- if (entries.length < this.config.summaryTrigger) return;
861
- await this.summarize(scope);
862
- }
863
- };
864
- function createMemoryService() {
865
- if (!config.memory.enabled) return null;
866
- try {
867
- const store = createMemoryStore(config.memory);
868
- return new MemoryService(store, config.memory, logger);
869
- } catch (error) {
870
- logger.error({ error }, "memory.init.failed");
871
- return null;
872
- }
873
- }
874
- function truncate(value, max) {
875
- if (value.length <= max) return value;
876
- return `${value.slice(0, max)}\u2026`;
877
- }
878
- function clamp(value, min, max) {
879
- return Math.min(Math.max(value, min), max);
880
- }
881
- function serialize(payload) {
882
- if (typeof payload === "string") return payload;
883
- if (payload === void 0 || payload === null) return "";
884
- try {
885
- return JSON.stringify(redact(payload), null, 2);
886
- } catch (error) {
887
- return `unserializable payload: ${error instanceof Error ? error.message : String(error)}`;
888
- }
889
- }
890
- function redact(payload) {
891
- if (Array.isArray(payload)) {
892
- return payload.map((item) => redact(item));
893
- }
894
- if (payload && typeof payload === "object") {
895
- const clone = {};
896
- for (const [key, value] of Object.entries(payload)) {
897
- if (REDACT_KEYS.includes(key.toLowerCase())) {
898
- clone[key] = "[redacted]";
899
- } else {
900
- clone[key] = redact(value);
901
- }
902
- }
903
- return clone;
904
- }
905
- return payload;
906
- }
907
- function buildHeuristicSummary(entries) {
908
- const recent = entries.slice(0, 10).map((entry) => `${entry.role}${entry.toolName ? `(${entry.toolName})` : ""}: ${truncate(entry.content, 512)}`);
909
- return ["Summary (heuristic, no LLM):", ...recent].join("\n");
910
- }
911
-
912
- // src/memory/index.ts
913
- var memoryService = createMemoryService();
914
-
915
- // src/workflows/utils/memory.ts
916
- async function loadMemoryContext(options) {
917
- if (options.optOut) return null;
918
- if (!memoryService || !memoryService.isEnabled()) return null;
919
- return memoryService.getContext({
920
- scope: options.scope,
921
- limit: options.limit,
922
- includeSummary: options.includeSummary ?? true
923
- });
924
- }
925
- async function recordMemory(options) {
926
- if (options.optOut) return;
927
- if (!memoryService || !memoryService.isEnabled()) return;
928
- await memoryService.recordEntry({
929
- scope: options.scope,
930
- role: options.role,
931
- content: options.content,
932
- toolName: options.toolName
933
- });
934
- }
935
-
936
- // src/workflows/chat.ts
937
- var chatDefinition = {
938
- name: "workflow.chatSmoke",
939
- description: "Create a chat session, send a message, read history, compact, and close.",
940
- version: "1.0.0",
941
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
942
- createContext: ({ uaid, auth, disableMemory }) => ({ uaid, auth, transcript: void 0, memoryOptOut: disableMemory }),
943
- steps: [
944
- {
945
- name: "workflow.chatSmoke.memory.load",
946
- skip: ({ input }) => Boolean(input.disableMemory),
947
- run: async ({ context }) => {
948
- const scope = { uaid: context.uaid };
949
- const loaded = await loadMemoryContext({ scope, optOut: context.memoryOptOut });
950
- context.memoryContext = loaded ?? void 0;
951
- return loaded ?? { skipped: true };
952
- }
953
- },
954
- {
955
- name: "hol.chat.createSession",
956
- run: async ({ context }) => {
957
- const response = await withBroker(
958
- (client) => client.chat.createSession({ uaid: context.uaid, historyTtlSeconds: 60, auth: context.auth })
959
- );
960
- if (response?.sessionId) {
961
- context.sessionId = response.sessionId;
962
- }
963
- return response;
964
- }
965
- },
966
- {
967
- name: "hol.chat.sendMessage",
968
- run: async ({ input, context }) => {
969
- if (!context.sessionId) throw new Error("Missing chat session");
970
- const message = input.message ?? "Hello from workflow.chatSmoke";
971
- await recordMemory({
972
- scope: { uaid: context.uaid, sessionId: context.sessionId },
973
- role: "user",
974
- content: message,
975
- toolName: "workflow.chatSmoke",
976
- optOut: context.memoryOptOut
977
- });
978
- const response = await withBroker(
979
- (client) => client.chat.sendMessage({
980
- sessionId: context.sessionId,
981
- message,
982
- uaid: context.uaid,
983
- auth: input.auth ?? context.auth
984
- })
985
- );
986
- await recordMemory({
987
- scope: { uaid: context.uaid, sessionId: context.sessionId },
988
- role: "assistant",
989
- content: JSON.stringify(response),
990
- toolName: "workflow.chatSmoke",
991
- optOut: context.memoryOptOut
992
- });
993
- return response;
994
- }
995
- },
996
- {
997
- name: "hol.chat.history",
998
- run: async ({ context }) => {
999
- if (!context.sessionId) throw new Error("Missing chat session");
1000
- const history = await withBroker((client) => client.chat.getHistory(context.sessionId));
1001
- context.transcript = history;
1002
- await recordMemory({
1003
- scope: { uaid: context.uaid, sessionId: context.sessionId },
1004
- role: "event",
1005
- content: JSON.stringify({ history }),
1006
- toolName: "workflow.chatSmoke",
1007
- optOut: context.memoryOptOut
1008
- });
1009
- return history;
1010
- }
1011
- },
1012
- {
1013
- name: "hol.chat.compact",
1014
- allowDuringDryRun: true,
1015
- run: async ({ context }) => {
1016
- if (!context.sessionId) throw new Error("Missing chat session");
1017
- try {
1018
- return await withBroker((client) => client.chat.compactHistory({ sessionId: context.sessionId, preserveEntries: 2 }));
1019
- } catch (error) {
1020
- const message = error instanceof Error ? error.message.toLowerCase() : "";
1021
- if (message.includes("authenticated account required") || message.includes("insufficient credits")) {
1022
- return { skipped: true, reason: "history compaction requires authenticated account" };
1023
- }
1024
- throw error;
1025
- }
1026
- }
1027
- },
1028
- {
1029
- name: "hol.chat.end",
1030
- allowDuringDryRun: true,
1031
- run: async ({ context }) => {
1032
- if (!context.sessionId) throw new Error("Missing chat session");
1033
- return withBroker((client) => client.chat.endSession(context.sessionId));
1034
- }
1035
- }
1036
- ]
1037
- };
1038
- var chatPipeline = registerPipeline(chatDefinition);
1039
-
1040
- // src/workflows/ops.ts
1041
- var opsDefinition = {
1042
- name: "workflow.opsCheck",
1043
- description: "Run stats, metrics, dashboard, listProtocols, detectProtocol.",
1044
- version: "1.0.0",
1045
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1046
- createContext: () => ({}),
1047
- steps: [
1048
- {
1049
- name: "hol.stats",
1050
- allowDuringDryRun: true,
1051
- run: async ({ context }) => {
1052
- const response = await withBroker((client) => client.stats());
1053
- context.stats = response;
1054
- return response;
1055
- }
1056
- },
1057
- {
1058
- name: "hol.metricsSummary",
1059
- allowDuringDryRun: true,
1060
- run: async ({ context }) => {
1061
- const response = await withBroker((client) => client.metricsSummary());
1062
- context.metrics = response;
1063
- return response;
1064
- }
1065
- },
1066
- {
1067
- name: "hol.dashboardStats",
1068
- allowDuringDryRun: true,
1069
- run: async ({ context }) => {
1070
- const response = await withBroker((client) => client.dashboardStats());
1071
- context.dashboard = response;
1072
- return response;
1073
- }
1074
- },
1075
- {
1076
- name: "hol.listProtocols",
1077
- allowDuringDryRun: true,
1078
- run: async () => withBroker((client) => client.listProtocols())
1079
- },
1080
- {
1081
- name: "hol.detectProtocol",
1082
- allowDuringDryRun: true,
1083
- run: async () => withBroker(
1084
- (client) => client.detectProtocol({
1085
- headers: { "content-type": "application/json" },
1086
- body: "{}"
1087
- })
1088
- )
1089
- }
1090
- ]
1091
- };
1092
- var opsPipeline = registerPipeline(opsDefinition);
1093
-
1094
- // src/workflows/combined.ts
1095
- var fullDefinition = {
1096
- name: "workflow.fullRegistration",
1097
- description: "Discovery \u2192 Registration \u2192 Chat \u2192 Ops health check",
1098
- version: "1.0.0",
1099
- requiredEnv: ["REGISTRY_BROKER_API_KEY", "HEDERA_ACCOUNT_ID", "HEDERA_PRIVATE_KEY"],
1100
- createContext: (input) => ({ memoryOptOut: input.disableMemory }),
1101
- steps: [
1102
- {
1103
- name: "workflow.discovery",
1104
- allowDuringDryRun: true,
1105
- run: async ({ input, context }) => {
1106
- const result = await discoveryPipeline.run({ query: input.discoveryQuery, limit: 5 });
1107
- context.discovery = result;
1108
- await recordMemory({
1109
- scope: { namespace: "workflow.fullRegistration" },
1110
- role: "event",
1111
- content: JSON.stringify({ discovery: result }),
1112
- toolName: "workflow.fullRegistration",
1113
- optOut: context.memoryOptOut
1114
- });
1115
- return result;
1116
- }
1117
- },
1118
- {
1119
- name: "workflow.registerMcp",
1120
- run: async ({ input, context, dryRun }) => {
1121
- const payload = { payload: input.registrationPayload };
1122
- const result = await registrationPipeline.run(payload, { dryRun });
1123
- context.registration = result;
1124
- context.uaid = result.context.uaid;
1125
- if (context.uaid) {
1126
- await recordMemory({
1127
- scope: { uaid: context.uaid },
1128
- role: "event",
1129
- content: JSON.stringify({ registration: result }),
1130
- toolName: "workflow.fullRegistration",
1131
- optOut: context.memoryOptOut
1132
- });
1133
- }
1134
- return result;
1135
- }
1136
- },
1137
- {
1138
- name: "workflow.fullRegistration.memory.load",
1139
- skip: ({ input, context }) => Boolean(input.disableMemory || !context.uaid),
1140
- run: async ({ context }) => {
1141
- if (!context.uaid) return { skipped: true };
1142
- const loaded = await loadMemoryContext({ scope: { uaid: context.uaid }, optOut: context.memoryOptOut });
1143
- context.memoryContext = loaded ?? void 0;
1144
- return loaded ?? { skipped: true };
1145
- }
1146
- },
1147
- {
1148
- name: "workflow.chatSmoke",
1149
- run: async ({ input, context, dryRun }) => {
1150
- if (!context.uaid) throw new Error("UAID missing from registration context");
1151
- const result = await chatPipeline.run({ uaid: context.uaid, message: input.chatMessage, disableMemory: input.disableMemory }, { dryRun });
1152
- context.chat = result;
1153
- return result;
1154
- }
1155
- },
1156
- {
1157
- name: "workflow.opsCheck",
1158
- allowDuringDryRun: true,
1159
- run: async ({ context, dryRun }) => {
1160
- const result = await opsPipeline.run({}, { dryRun });
1161
- context.ops = result;
1162
- return result;
1163
- }
1164
- }
1165
- ]
1166
- };
1167
- var fullWorkflowPipeline = registerPipeline(fullDefinition);
1168
-
1169
- // src/workflows/scaffold.ts
1170
- function scaffoldWorkflow(definition) {
1171
- if (!definition.version) {
1172
- definition.version = "1.0.0";
1173
- }
1174
- if (!definition.requiredEnv) {
1175
- definition.requiredEnv = ["REGISTRY_BROKER_API_KEY"];
1176
- }
1177
- return registerPipeline(definition);
1178
- }
1179
-
1180
- // src/workflows/openrouter-chat.ts
1181
- var openRouterChatDefinition = {
1182
- name: "workflow.openrouterChat",
1183
- description: "Discover an OpenRouter model and run a chat message against it.",
1184
- version: "1.0.0",
1185
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1186
- createContext: (input) => ({ memoryOptOut: input.disableMemory }),
1187
- steps: [
1188
- {
1189
- name: "workflow.openrouterChat.memory.load",
1190
- skip: ({ input }) => Boolean(input.disableMemory),
1191
- run: async ({ input, context }) => {
1192
- const scope = { namespace: "openrouter", userId: input.modelId };
1193
- const loaded = await loadMemoryContext({ scope, optOut: context.memoryOptOut });
1194
- return loaded ?? { skipped: true };
1195
- }
1196
- },
1197
- {
1198
- name: "hol.search",
1199
- run: async ({ input, context }) => {
1200
- const result = await withBroker(
1201
- (client) => client.search({ q: input.modelId, registries: input.registry ? [input.registry] : ["openrouter"], limit: 1 })
1202
- );
1203
- if (!result.hits?.length) {
1204
- throw new Error(`Model ${input.modelId} not found in registry ${input.registry ?? "openrouter"}`);
1205
- }
1206
- context.uaid = result.hits[0].uaid;
1207
- return result.hits[0];
1208
- }
1209
- },
1210
- {
1211
- name: "hol.chat.createSession",
1212
- run: async ({ input, context }) => {
1213
- if (!context.uaid) throw new Error("UAID missing from discovery step");
1214
- const auth = input.authToken ? { type: "bearer", token: input.authToken } : void 0;
1215
- const response = await withBroker(
1216
- (client) => client.chat.createSession({
1217
- uaid: context.uaid,
1218
- historyTtlSeconds: input.historyTtlSeconds ?? 900,
1219
- auth
1220
- })
1221
- );
1222
- context.sessionId = response.sessionId;
1223
- return response;
1224
- }
1225
- },
1226
- {
1227
- name: "hol.chat.sendMessage",
1228
- run: async ({ input, context }) => {
1229
- if (!context.sessionId) throw new Error("Missing chat session");
1230
- const auth = input.authToken ? { type: "bearer", token: input.authToken } : void 0;
1231
- await recordMemory({
1232
- scope: { uaid: context.uaid, sessionId: context.sessionId },
1233
- role: "user",
1234
- content: input.message,
1235
- toolName: "workflow.openrouterChat",
1236
- optOut: context.memoryOptOut
1237
- });
1238
- const response = await withBroker(
1239
- (client) => client.chat.sendMessage({ sessionId: context.sessionId, auth, message: input.message, uaid: context.uaid })
1240
- );
1241
- await recordMemory({
1242
- scope: { uaid: context.uaid, sessionId: context.sessionId },
1243
- role: "assistant",
1244
- content: JSON.stringify(response),
1245
- toolName: "workflow.openrouterChat",
1246
- optOut: context.memoryOptOut
1247
- });
1248
- return response;
1249
- }
1250
- },
1251
- {
1252
- name: "hol.chat.history",
1253
- allowDuringDryRun: true,
1254
- run: async ({ context }) => {
1255
- if (!context.sessionId) throw new Error("Missing chat session");
1256
- const history = await withBroker((client) => client.chat.getHistory(context.sessionId));
1257
- context.transcript = history;
1258
- await recordMemory({
1259
- scope: { uaid: context.uaid, sessionId: context.sessionId },
1260
- role: "event",
1261
- content: JSON.stringify({ history }),
1262
- toolName: "workflow.openrouterChat",
1263
- optOut: context.memoryOptOut
1264
- });
1265
- return history;
1266
- }
1267
- },
1268
- {
1269
- name: "hol.chat.end",
1270
- allowDuringDryRun: true,
1271
- run: async ({ context }) => {
1272
- if (!context.sessionId) throw new Error("Missing chat session");
1273
- return withBroker((client) => client.chat.endSession(context.sessionId));
1274
- }
1275
- }
1276
- ]
1277
- };
1278
- var openRouterChatWorkflow = scaffoldWorkflow(openRouterChatDefinition);
1279
-
1280
- // src/workflows/registry-showcase.ts
1281
- var registryShowcaseDefinition = {
1282
- name: "workflow.registryBrokerShowcase",
1283
- description: "Discovery + analytics + chat showcase workflow inspired by registry-broker-demo.ts.",
1284
- version: "1.0.0",
1285
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1286
- createContext: (input) => ({ memoryOptOut: input.disableMemory }),
1287
- steps: [
1288
- {
1289
- name: "workflow.discovery",
1290
- allowDuringDryRun: true,
1291
- run: async ({ input, context, dryRun }) => {
1292
- const result = await discoveryPipeline.run({ query: input.query }, { dryRun });
1293
- context.discovery = result;
1294
- context.uaid = input.uaid ?? result.steps[0]?.output?.hits?.[0]?.uaid;
1295
- if (context.uaid) {
1296
- await recordMemory({
1297
- scope: { uaid: context.uaid },
1298
- role: "event",
1299
- content: JSON.stringify({ discovery: result }),
1300
- toolName: "workflow.registryBrokerShowcase",
1301
- optOut: context.memoryOptOut
1302
- });
1303
- }
1304
- return result;
1305
- }
1306
- },
1307
- {
1308
- name: "workflow.registryBrokerShowcase.memory.load",
1309
- skip: ({ input }) => Boolean(input.disableMemory),
1310
- run: async ({ context }) => {
1311
- if (!context.uaid) return { skipped: true };
1312
- const loaded = await loadMemoryContext({ scope: { uaid: context.uaid }, optOut: context.memoryOptOut });
1313
- context.memoryContext = loaded ?? void 0;
1314
- return loaded ?? { skipped: true };
1315
- }
1316
- },
1317
- {
1318
- name: "hol.listProtocols",
1319
- allowDuringDryRun: true,
1320
- run: async ({ context }) => {
1321
- const response = await withBroker((client) => client.listProtocols());
1322
- context.listProtocols = response;
1323
- return response;
1324
- }
1325
- },
1326
- {
1327
- name: "hol.detectProtocol",
1328
- allowDuringDryRun: true,
1329
- run: async ({ context }) => {
1330
- const response = await withBroker((client) => client.detectProtocol({ headers: { "content-type": "application/json" }, body: "{}" }));
1331
- context.detectProtocol = response;
1332
- await recordMemory({
1333
- scope: { uaid: context.uaid },
1334
- role: "event",
1335
- content: JSON.stringify({ detectProtocol: response }),
1336
- toolName: "workflow.registryBrokerShowcase",
1337
- optOut: context.memoryOptOut
1338
- });
1339
- return response;
1340
- }
1341
- },
1342
- {
1343
- name: "hol.stats",
1344
- allowDuringDryRun: true,
1345
- run: async ({ context }) => {
1346
- const response = await withBroker((client) => client.stats());
1347
- context.stats = response;
1348
- return response;
1349
- }
1350
- },
1351
- {
1352
- name: "hol.metricsSummary",
1353
- allowDuringDryRun: true,
1354
- run: async ({ context }) => {
1355
- const response = await withBroker((client) => client.metricsSummary());
1356
- context.metrics = response;
1357
- return response;
1358
- }
1359
- },
1360
- {
1361
- name: "hol.dashboardStats",
1362
- allowDuringDryRun: true,
1363
- run: async ({ context }) => {
1364
- const response = await withBroker((client) => client.dashboardStats());
1365
- context.dashboard = response;
1366
- return response;
1367
- }
1368
- },
1369
- {
1370
- name: "hol.websocketStats",
1371
- allowDuringDryRun: true,
1372
- run: async ({ context }) => {
1373
- const response = await withBroker((client) => client.websocketStats());
1374
- context.websocket = response;
1375
- return response;
1376
- }
1377
- },
1378
- {
1379
- name: "workflow.registryBrokerShowcase.chat",
1380
- skip: ({ input, context }) => !(input.message && context.uaid),
1381
- run: async ({ input, context, dryRun }) => {
1382
- if (!context.uaid) throw new Error("No UAID discovered for chat");
1383
- const result = await chatPipeline.run({ uaid: context.uaid, message: input.message, disableMemory: input.disableMemory }, { dryRun });
1384
- context.chat = result;
1385
- return result;
1386
- }
1387
- },
1388
- {
1389
- name: "hol.getRegistrationQuote",
1390
- skip: ({ input }) => !input.performCreditCheck,
1391
- run: async ({ context }) => {
1392
- const discoveryRecord = context.discovery && typeof context.discovery === "object" ? context.discovery : null;
1393
- const payload = discoveryRecord?.context?.registrationPayload ?? context.discovery;
1394
- const response = await withBroker((client) => client.getRegistrationQuote(payload));
1395
- context.creditQuote = response;
1396
- return response;
1397
- }
1398
- }
1399
- ]
1400
- };
1401
- var registryBrokerShowcaseWorkflow = scaffoldWorkflow(registryShowcaseDefinition);
1402
-
1403
- // src/workflows/agentverse-bridge.ts
1404
- var agentverseBridgeDefinition = {
1405
- name: "workflow.agentverseBridge",
1406
- description: "Relay messages between a local UAID session and an Agentverse UAID.",
1407
- version: "1.0.0",
1408
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1409
- createContext: () => ({ transcripts: [] }),
1410
- steps: [
1411
- {
1412
- name: "workflow.agentverseBridge.createSessions",
1413
- run: async ({ input, context }) => {
1414
- const local = await withBroker(
1415
- (client) => client.chat.createSession({ uaid: input.uaid, auth: input.localAuth, historyTtlSeconds: 300 })
1416
- );
1417
- const agentverse = await withBroker(
1418
- (client) => client.chat.createSession({ uaid: input.agentverseUaid, auth: input.agentverseAuth, historyTtlSeconds: 300 })
1419
- );
1420
- context.localSession = local.sessionId;
1421
- context.agentverseSession = agentverse.sessionId;
1422
- context.localUaid = input.uaid;
1423
- context.agentverseUaid = input.agentverseUaid;
1424
- return { local, agentverse };
1425
- }
1426
- },
1427
- {
1428
- name: "workflow.agentverseBridge.relay",
1429
- run: async ({ input, context }) => {
1430
- if (!context.localSession || !context.agentverseSession) {
1431
- throw new Error("Sessions missing");
1432
- }
1433
- const iterations = input.iterations ?? 1;
1434
- for (let i = 0; i < iterations; i += 1) {
1435
- const localResponse = await withBroker(
1436
- (client) => client.chat.sendMessage({
1437
- sessionId: context.localSession,
1438
- auth: input.localAuth,
1439
- message: input.localMessage,
1440
- uaid: context.localUaid
1441
- })
1442
- );
1443
- context.transcripts.push({ target: "local", response: localResponse });
1444
- const agentverseResponse = await withBroker(
1445
- (client) => client.chat.sendMessage({
1446
- sessionId: context.agentverseSession,
1447
- auth: input.agentverseAuth,
1448
- message: input.agentverseMessage,
1449
- uaid: context.agentverseUaid
1450
- })
1451
- );
1452
- context.transcripts.push({ target: "agentverse", response: agentverseResponse });
1453
- }
1454
- return context.transcripts;
1455
- }
1456
- },
1457
- {
1458
- name: "workflow.agentverseBridge.history",
1459
- allowDuringDryRun: true,
1460
- run: async ({ context }) => {
1461
- const localHistory = await withBroker((client) => client.chat.getHistory(context.localSession));
1462
- const agentverseHistory = await withBroker((client) => client.chat.getHistory(context.agentverseSession));
1463
- return { localHistory, agentverseHistory };
1464
- }
1465
- },
1466
- {
1467
- name: "workflow.agentverseBridge.cleanup",
1468
- allowDuringDryRun: true,
1469
- run: async ({ context }) => {
1470
- await Promise.all([
1471
- withBroker((client) => client.chat.endSession(context.localSession)),
1472
- withBroker((client) => client.chat.endSession(context.agentverseSession))
1473
- ]);
1474
- return { ended: true };
1475
- }
1476
- }
1477
- ]
1478
- };
1479
- var agentverseBridgeWorkflow = scaffoldWorkflow(agentverseBridgeDefinition);
1480
-
1481
- // src/workflows/erc8004-discovery.ts
1482
- var erc8004DiscoveryDefinition = {
1483
- name: "workflow.erc8004Discovery",
1484
- description: "Filter search/vector/namespace lookups for ERC-8004 registries.",
1485
- version: "1.0.0",
1486
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1487
- createContext: () => ({}),
1488
- steps: [
1489
- {
1490
- name: "hol.search",
1491
- allowDuringDryRun: true,
1492
- run: async ({ input, context }) => {
1493
- const response = await withBroker(
1494
- (client) => client.search({ q: input.query, registries: ["erc-8004"], limit: input.limit ?? 10 })
1495
- );
1496
- context.hits = response.hits;
1497
- return response;
1498
- }
1499
- },
1500
- {
1501
- name: "hol.registrySearchByNamespace",
1502
- allowDuringDryRun: true,
1503
- run: async ({ input }) => withBroker((client) => client.registrySearchByNamespace("erc-8004", input.query))
1504
- }
1505
- ]
1506
- };
1507
- var erc8004DiscoveryWorkflow = scaffoldWorkflow(erc8004DiscoveryDefinition);
1508
-
1509
- // src/workflows/register-advanced.ts
1510
- var registerAgentAdvancedDefinition = {
1511
- name: "workflow.registerAgentAdvanced",
1512
- description: "Extended registration workflow with additional registries and optional updates.",
1513
- version: "1.0.0",
1514
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1515
- createContext: ({ payload }) => ({ payload, resolvedRegistries: [], missingRegistries: [] }),
1516
- steps: [
1517
- {
1518
- name: "hol.getAdditionalRegistries",
1519
- allowDuringDryRun: true,
1520
- skip: ({ input }) => !input.additionalRegistrySelections?.length && !input.updateAdditionalRegistries?.length,
1521
- run: async ({ context }) => {
1522
- const catalog = await withBroker((client) => client.getAdditionalRegistries());
1523
- context.catalog = catalog;
1524
- return catalog;
1525
- }
1526
- },
1527
- {
1528
- name: "workflow.resolveAdditionalRegistries",
1529
- allowDuringDryRun: true,
1530
- run: async ({ input, context }) => {
1531
- const catalog = context.catalog;
1532
- if (!catalog) return null;
1533
- const selections = input.additionalRegistrySelections ?? [];
1534
- const { resolved, missing } = resolveAdditionalSelections(selections, catalog);
1535
- context.resolvedRegistries = resolved;
1536
- context.missingRegistries = missing;
1537
- if (resolved.length > 0) {
1538
- context.payload = {
1539
- ...context.payload,
1540
- additionalRegistries: resolved
1541
- };
1542
- }
1543
- return { resolved, missing };
1544
- }
1545
- },
1546
- {
1547
- name: "hol.getRegistrationQuote",
1548
- allowDuringDryRun: true,
1549
- run: async ({ context }) => withBroker((client) => client.getRegistrationQuote(context.payload))
1550
- },
1551
- {
1552
- name: "workflow.registerAgentAdvanced.register",
1553
- run: async ({ input, context }) => {
1554
- const response = await runCreditAwareRegistration({
1555
- payload: context.payload,
1556
- onShortfall: async (err) => {
1557
- context.lastQuote = err.summary;
1558
- if (!input.creditTopUp) {
1559
- return "abort";
1560
- }
1561
- await purchaseCreditsWithHbar(input.creditTopUp, err.summary);
1562
- return "retry";
1563
- }
1564
- });
1565
- context.registrationResult = response;
1566
- if ("attemptId" in response && typeof response.attemptId === "string") {
1567
- context.attemptId = response.attemptId;
1568
- }
1569
- if ("uaid" in response && typeof response.uaid === "string") {
1570
- context.uaid = response.uaid;
1571
- }
1572
- return response;
1573
- }
1574
- },
1575
- {
1576
- name: "hol.waitForRegistrationCompletion",
1577
- run: async ({ context }) => {
1578
- if (!context.attemptId) throw new Error("Registration attemptId missing.");
1579
- const result = await waitForRegistrationCompletion(context.attemptId);
1580
- context.progress = result;
1581
- if (result && typeof result === "object" && "result" in result) {
1582
- const progress = result;
1583
- const maybeUaid = progress.result?.uaid;
1584
- if (typeof maybeUaid === "string") {
1585
- context.uaid = maybeUaid;
1586
- }
1587
- }
1588
- return result;
1589
- }
1590
- },
1591
- {
1592
- name: "workflow.registerAgentAdvanced.update",
1593
- skip: ({ input }) => input.skipUpdate || !input.updateAdditionalRegistries?.length,
1594
- run: async ({ input, context }) => {
1595
- if (!context.uaid) throw new Error("UAID missing for update.");
1596
- const resolved = input.updateAdditionalRegistries ?? [];
1597
- const updatePayload = {
1598
- ...context.payload,
1599
- additionalRegistries: resolved
1600
- };
1601
- const response = await withBroker((client) => client.updateAgent(context.uaid, updatePayload));
1602
- context.updateResult = response;
1603
- return response;
1604
- }
1605
- }
1606
- ]
1607
- };
1608
- var registerAgentAdvancedPipeline = scaffoldWorkflow(registerAgentAdvancedDefinition);
1609
- function resolveAdditionalSelections(selections, catalog) {
1610
- if (!catalog.registries || catalog.registries.length === 0) {
1611
- return { resolved: [], missing: selections };
1612
- }
1613
- const resolved = /* @__PURE__ */ new Set();
1614
- const missing = [];
1615
- for (const selection of selections) {
1616
- const matches = collectMatches(selection, catalog);
1617
- if (matches.length === 0) {
1618
- missing.push(selection);
1619
- } else {
1620
- matches.forEach((key) => resolved.add(key));
1621
- }
1622
- }
1623
- return { resolved: Array.from(resolved), missing };
1624
- }
1625
- function collectMatches(selection, catalog) {
1626
- const target = selection.trim().toLowerCase();
1627
- if (!target) return [];
1628
- const matches = [];
1629
- for (const registry of catalog.registries ?? []) {
1630
- const registryId = registry.id?.toLowerCase();
1631
- const networks = registry.networks ?? [];
1632
- if (!registryId || networks.length === 0) continue;
1633
- if (registryId === target) {
1634
- networks.forEach((network) => {
1635
- if (network?.key) {
1636
- matches.push(network.key);
1637
- }
1638
- });
1639
- continue;
1640
- }
1641
- for (const network of networks) {
1642
- if (!network?.key) continue;
1643
- const keyLower = network.key.toLowerCase();
1644
- const networkIdLower = network.networkId?.toLowerCase();
1645
- const labelLower = network.label?.toLowerCase();
1646
- const nameLower = network.name?.toLowerCase();
1647
- if (target === keyLower || networkIdLower && target === networkIdLower || labelLower && target === labelLower || nameLower && target === nameLower || target === `${registryId}:${networkIdLower}`) {
1648
- matches.push(network.key);
1649
- }
1650
- }
1651
- }
1652
- return matches;
1653
- }
1654
- async function purchaseCreditsWithHbar(config2, summary) {
1655
- const hbarAmount = config2.hbarAmount ?? Math.max(0.25, (summary.shortfallCredits ?? 1) / 100);
1656
- await withBroker(
1657
- (client) => client.purchaseCreditsWithHbar({
1658
- accountId: config2.accountId,
1659
- privateKey: config2.privateKey,
1660
- hbarAmount,
1661
- memo: config2.memo ?? "workflow.registerAgentAdvanced:topup",
1662
- metadata: {
1663
- shortfall: summary.shortfallCredits,
1664
- requiredCredits: summary.requiredCredits
1665
- }
1666
- })
1667
- );
1668
- }
1669
-
1670
- // src/workflows/register-erc8004.ts
1671
- var registerAgentErc8004Definition = {
1672
- name: "workflow.registerAgentErc8004",
1673
- description: "ERC-8004-specific registration workflow with optional ledger verification.",
1674
- version: "1.0.0",
1675
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1676
- createContext: () => ({ resolvedNetworks: [], missingNetworks: [] }),
1677
- steps: [
1678
- {
1679
- name: "hol.getAdditionalRegistries",
1680
- allowDuringDryRun: true,
1681
- run: async ({ context }) => {
1682
- const catalog = await withBroker((client) => client.getAdditionalRegistries());
1683
- context.catalog = catalog;
1684
- return catalog;
1685
- }
1686
- },
1687
- {
1688
- name: "workflow.erc8004.resolveNetworks",
1689
- allowDuringDryRun: true,
1690
- run: async ({ input, context }) => {
1691
- const response = context.catalog;
1692
- if (!response) {
1693
- context.resolvedNetworks = [];
1694
- context.missingNetworks = [];
1695
- return { resolved: [], missing: [] };
1696
- }
1697
- const selections = input.erc8004Networks?.length ? input.erc8004Networks : defaultErc8004Selections(response);
1698
- const { resolved, missing } = resolveErc8004Selections(selections, response);
1699
- context.resolvedNetworks = resolved;
1700
- context.missingNetworks = missing;
1701
- return { resolved, missing };
1702
- }
1703
- },
1704
- {
1705
- name: "hol.ledger.authenticate",
1706
- skip: ({ input }) => !input.ledgerVerification,
1707
- run: async ({ input }) => withBroker((client) => client.verifyLedgerChallenge(input.ledgerVerification))
1708
- },
1709
- {
1710
- name: "workflow.registerAgentAdvanced",
1711
- run: async ({ input, context, dryRun }) => {
1712
- const advancedInput = {
1713
- payload: withAdditionalRegistries(input.payload, context.resolvedNetworks),
1714
- additionalRegistrySelections: context.resolvedNetworks,
1715
- updateAdditionalRegistries: input.updateAdditionalRegistries,
1716
- skipUpdate: input.skipUpdate,
1717
- creditTopUp: input.creditTopUp
1718
- };
1719
- const result = await registerAgentAdvancedPipeline.run(advancedInput, { dryRun });
1720
- context.advancedResult = result;
1721
- return result;
1722
- }
1723
- }
1724
- ]
1725
- };
1726
- var registerAgentErc8004Pipeline = scaffoldWorkflow(registerAgentErc8004Definition);
1727
- function resolveErc8004Selections(selections, catalog) {
1728
- if (!catalog.registries || catalog.registries.length === 0) {
1729
- return { resolved: [], missing: selections };
1730
- }
1731
- const resolved = /* @__PURE__ */ new Set();
1732
- const missing = [];
1733
- selections.forEach((entry) => {
1734
- const normalized2 = entry.trim().toLowerCase();
1735
- if (!normalized2) return;
1736
- let matched = false;
1737
- for (const descriptor of catalog.registries ?? []) {
1738
- const descriptorId = descriptor.id?.toLowerCase();
1739
- const networks = descriptor.networks ?? [];
1740
- if (!descriptorId || !descriptorId.startsWith("erc-8004")) continue;
1741
- for (const network of networks) {
1742
- if (!network) continue;
1743
- const candidates = [network.key, network.networkId, network.label, network.name].map((value) => value?.toLowerCase().trim()).filter(Boolean);
1744
- if (candidates.includes(normalized2) || `${descriptorId}:${network.networkId?.toLowerCase()}` === normalized2) {
1745
- if (network.key) {
1746
- resolved.add(network.key);
1747
- }
1748
- matched = true;
1749
- }
1750
- }
1751
- }
1752
- if (!matched) {
1753
- missing.push(entry);
1754
- }
1755
- });
1756
- return { resolved: Array.from(resolved), missing };
1757
- }
1758
- function defaultErc8004Selections(catalog) {
1759
- const defaults = catalog.registries?.find((entry) => entry.id?.toLowerCase() === "erc-8004");
1760
- if (!defaults || !defaults.networks) return [];
1761
- return defaults.networks.map((network) => network?.key).filter((key) => Boolean(key));
1762
- }
1763
- function withAdditionalRegistries(payload, networks) {
1764
- if (networks.length === 0) return payload;
1765
- return { ...payload, additionalRegistries: networks };
1766
- }
1767
-
1768
- // src/workflows/x402-topup.ts
1769
- var x402TopUpDefinition = {
1770
- name: "workflow.x402TopUp",
1771
- description: "Buy registry credits via X402 using an EVM wallet.",
1772
- version: "1.0.0",
1773
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1774
- createContext: () => ({}),
1775
- steps: [
1776
- {
1777
- name: "hol.ledger.authenticate",
1778
- skip: ({ input }) => !input.ledgerVerification,
1779
- run: async ({ input, context }) => {
1780
- const verification = await withBroker((client) => client.verifyLedgerChallenge(input.ledgerVerification));
1781
- context.ledgerVerification = verification;
1782
- return verification;
1783
- }
1784
- },
1785
- {
1786
- name: "hol.x402.minimums",
1787
- allowDuringDryRun: true,
1788
- run: async ({ context }) => {
1789
- const minimums = await withBroker((client) => client.getX402Minimums());
1790
- context.minimums = minimums;
1791
- return minimums;
1792
- }
1793
- },
1794
- {
1795
- name: "hol.x402.buyCredits",
1796
- run: async ({ input, context }) => {
1797
- const payload = {
1798
- accountId: input.accountId,
1799
- credits: input.credits,
1800
- usdAmount: input.usdAmount,
1801
- description: input.description,
1802
- metadata: input.metadata,
1803
- evmPrivateKey: input.evmPrivateKey,
1804
- network: input.network,
1805
- rpcUrl: input.rpcUrl
1806
- };
1807
- const purchase = await withBroker((client) => client.buyCreditsWithX402(payload));
1808
- context.purchase = purchase;
1809
- return purchase;
1810
- }
1811
- }
1812
- ]
1813
- };
1814
- var x402TopUpWorkflow = scaffoldWorkflow(x402TopUpDefinition);
1815
-
1816
- // src/workflows/erc8004-x402.ts
1817
- var erc8004X402Definition = {
1818
- name: "workflow.erc8004X402",
1819
- description: "Register on ERC-8004 networks with X402 credit purchases and chat smoke.",
1820
- version: "1.0.0",
1821
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1822
- createContext: () => ({}),
1823
- steps: [
1824
- {
1825
- name: "workflow.x402TopUp",
1826
- skip: ({ input }) => !input.creditPurchase,
1827
- run: async ({ input, dryRun }) => x402TopUpWorkflow.run(
1828
- {
1829
- accountId: input.creditPurchase.accountId,
1830
- credits: input.creditPurchase.credits,
1831
- evmPrivateKey: input.creditPurchase.evmPrivateKey,
1832
- network: input.creditPurchase.network,
1833
- ledgerVerification: input.creditPurchase.ledgerVerification
1834
- },
1835
- { dryRun }
1836
- )
1837
- },
1838
- {
1839
- name: "workflow.registerAgentErc8004",
1840
- run: async ({ input, dryRun }) => registerAgentErc8004Pipeline.run(
1841
- {
1842
- payload: input.payload,
1843
- erc8004Networks: input.erc8004Networks,
1844
- creditTopUp: void 0
1845
- },
1846
- { dryRun }
1847
- )
1848
- },
1849
- {
1850
- name: "workflow.erc8004X402.chat",
1851
- skip: ({ input }) => !input.chatMessage,
1852
- run: async ({ input, context, dryRun }) => {
1853
- const uaid = context?.context?.registrationResult?.context?.uaid;
1854
- if (!uaid) throw new Error("UAID missing after registration");
1855
- return chatPipeline.run({ uaid, message: input.chatMessage }, { dryRun });
1856
- }
1857
- }
1858
- ]
1859
- };
1860
- var erc8004X402Workflow = scaffoldWorkflow(erc8004X402Definition);
1861
-
1862
- // src/workflows/x402-registration.ts
1863
- var x402RegistrationDefinition = {
1864
- name: "workflow.x402Registration",
1865
- description: "Full agent registration funded via X402 credits with chat validation.",
1866
- version: "1.0.0",
1867
- requiredEnv: ["REGISTRY_BROKER_API_KEY"],
1868
- createContext: () => ({}),
1869
- steps: [
1870
- {
1871
- name: "workflow.x402TopUp",
1872
- run: async ({ input, dryRun }) => x402TopUpWorkflow.run(
1873
- {
1874
- accountId: input.x402.accountId,
1875
- credits: input.x402.credits,
1876
- evmPrivateKey: input.x402.evmPrivateKey,
1877
- ledgerVerification: input.x402.ledgerVerification
1878
- },
1879
- { dryRun }
1880
- )
1881
- },
1882
- {
1883
- name: "workflow.registerAgentAdvanced",
1884
- run: async ({ input, dryRun }) => registerAgentAdvancedPipeline.run({ payload: input.payload }, { dryRun })
1885
- },
1886
- {
1887
- name: "workflow.x402Registration.chat",
1888
- skip: ({ input }) => !input.chatMessage,
1889
- run: async ({ input, context, dryRun }) => {
1890
- const uaid = context.context?.registrationResult?.context?.uaid;
1891
- if (!uaid) throw new Error("UAID missing after registration");
1892
- return chatPipeline.run({ uaid, message: input.chatMessage }, { dryRun });
1893
- }
1894
- }
1895
- ]
1896
- };
1897
- var x402RegistrationWorkflow = scaffoldWorkflow(x402RegistrationDefinition);
1898
-
1899
- // src/mcp.ts
1900
- var connectionInstructions = [
1901
- "You expose the Hashgraph Online Registry Broker via hol.* primitives and workflow.* pipelines. Prefer workflow.* when possible\u2014they bundle common steps and return a pipeline summary plus full results.",
1902
- "Discovery: use workflow.discovery (or hol.search / hol.vectorSearch) to find UAIDs/agents/MCP servers; pass q/query and optional filters like capabilities, metadata, or type=ai-agents|mcp-servers.",
1903
- "Registration: workflow.registerMcp (quote \u2192 register \u2192 wait) is the default; workflow.fullRegistration adds discovery/chat/ops. hol.registerAgent + hol.waitForRegistrationCompletion are the lower-level primitives.",
1904
- "Chat: call hol.resolveUaid if the UAID is unverified, then hol.chat.createSession (uaid + optional auth) followed by hol.chat.sendMessage (sessionId or uaid). Use hol.chat.history/compact/end to manage the session.",
1905
- "Operations: workflow.opsCheck or hol.stats/hol.metricsSummary/hol.dashboardStats show registry health; hol.listProtocols + hol.detectProtocol help route third-party requests.",
1906
- "Credits: check hol.credits.balance before purchases. Use hol.purchaseCredits.hbar or hol.x402.buyCredits only with explicit user approval (X402 requires an EVM key); hol.x402.minimums provides thresholds.",
1907
- "Always include UAIDs/sessionIds exactly as given and echo any auth headers/tokens the user supplies. If required fields are missing (UAID, payload, accountId), ask for them before calling tools.",
1908
- "Additional help resources: help://rb/search documents hol.search filters; help://hol/usage lists common recipes."
1909
- ].join("\n");
1910
- var agentAuthSchema = z3.object({
1911
- type: z3.enum(["bearer", "basic", "header", "apiKey"]).optional(),
1912
- token: z3.string().optional(),
1913
- username: z3.string().optional(),
1914
- password: z3.string().optional(),
1915
- headerName: z3.string().optional(),
1916
- headerValue: z3.string().optional(),
1917
- headers: z3.record(z3.string(), z3.string()).optional()
1918
- }).partial();
1919
- var chatSessionSchema = z3.object({
1920
- uaid: z3.string().min(1),
1921
- historyTtlSeconds: z3.number().int().positive().optional(),
1922
- auth: agentAuthSchema.optional(),
1923
- senderUaid: z3.string().min(1).optional(),
1924
- encryptionRequested: z3.boolean().optional()
1925
- });
1926
- var chatMessageSchema = z3.object({
1927
- sessionId: z3.string().min(1).optional(),
1928
- uaid: z3.string().min(1).optional(),
1929
- message: z3.string().min(1),
1930
- streaming: z3.boolean().optional(),
1931
- auth: agentAuthSchema.optional()
1932
- }).superRefine((value, ctx) => {
1933
- if (!value.sessionId && !value.uaid) {
1934
- ctx.addIssue({ code: z3.ZodIssueCode.custom, message: "Provide sessionId for existing chats or uaid to start a new one." });
1935
- }
1936
- });
1937
- var chatCompactSchema = z3.object({
1938
- sessionId: z3.string().min(1),
1939
- preserveEntries: z3.number().int().min(0).default(4),
1940
- auth: agentAuthSchema.optional()
1941
- });
1942
- var searchInput = z3.object({
1943
- q: z3.string().optional(),
1944
- limit: z3.number().int().min(1).max(50).default(10),
1945
- capabilities: z3.array(z3.string()).optional(),
1946
- registries: z3.array(z3.string()).optional(),
1947
- minTrust: z3.number().int().min(0).max(100).optional(),
1948
- verified: z3.boolean().optional(),
1949
- online: z3.boolean().optional(),
1950
- sortBy: z3.enum(["trust", "latency", "most-recent"]).optional(),
1951
- sortOrder: z3.enum(["asc", "desc"]).optional(),
1952
- metadata: z3.record(z3.string(), z3.array(z3.string())).optional(),
1953
- type: z3.enum(["ai-agents", "mcp-servers"]).optional()
1954
- });
1955
- var vectorSearchInput = z3.object({
1956
- query: z3.string().min(1),
1957
- limit: z3.number().int().min(1).max(20).default(5)
1958
- });
1959
- var uaidInput = z3.object({ uaid: z3.string().min(1) });
1960
- var registrationPayload = z3.object({
1961
- payload: agentRegistrationSchema
1962
- });
1963
- var workflowDiscoveryInput = z3.object({
1964
- query: z3.string().optional(),
1965
- limit: z3.number().int().min(1).max(50).optional()
1966
- });
1967
- var workflowRegistrationInput = z3.object({
1968
- payload: agentRegistrationSchema
1969
- });
1970
- var workflowChatInput = z3.object({
1971
- uaid: z3.string().min(1),
1972
- message: z3.string().optional(),
1973
- disableMemory: z3.boolean().optional()
1974
- });
1975
- var workflowOpsInput = z3.object({});
1976
- var workflowFullInput = z3.object({
1977
- registrationPayload: agentRegistrationSchema,
1978
- discoveryQuery: z3.string().optional(),
1979
- chatMessage: z3.string().optional(),
1980
- disableMemory: z3.boolean().optional()
1981
- });
1982
- var openRouterChatToolSchema = z3.object({
1983
- modelId: z3.string().min(1),
1984
- registry: z3.string().optional(),
1985
- message: z3.string(),
1986
- authToken: z3.string().optional(),
1987
- disableMemory: z3.boolean().optional()
1988
- });
1989
- var registryShowcaseToolSchema = z3.object({
1990
- query: z3.string().optional(),
1991
- uaid: z3.string().optional(),
1992
- message: z3.string().optional(),
1993
- performCreditCheck: z3.boolean().optional(),
1994
- disableMemory: z3.boolean().optional()
1995
- });
1996
- var erc8004DiscoveryToolSchema = z3.object({ query: z3.string().optional(), limit: z3.number().int().positive().optional() });
1997
- var bridgePayloadSchema = z3.object({
1998
- uaid: z3.string().min(1),
1999
- agentverseUaid: z3.string().min(1),
2000
- localMessage: z3.string().min(1),
2001
- agentverseMessage: z3.string().min(1),
2002
- iterations: z3.number().int().positive().optional()
2003
- });
2004
- var erc8004X402ToolSchema = z3.object({
2005
- payload: agentRegistrationSchema,
2006
- erc8004Networks: z3.array(z3.string()).optional(),
2007
- chatMessage: z3.string().optional()
2008
- });
2009
- var x402RegistrationToolSchema = z3.object({
2010
- payload: agentRegistrationSchema,
2011
- x402: z3.object({
2012
- accountId: z3.string().min(1),
2013
- credits: z3.number().positive(),
2014
- evmPrivateKey: z3.string().min(1),
2015
- ledgerVerification: z3.object({
2016
- challengeId: z3.string().min(1),
2017
- accountId: z3.string().min(1),
2018
- network: z3.enum(["mainnet", "testnet"]),
2019
- signature: z3.string().min(1),
2020
- signatureKind: z3.enum(["raw", "map"]).optional(),
2021
- publicKey: z3.string().optional(),
2022
- expiresInMinutes: z3.number().int().positive().optional()
2023
- }).optional()
2024
- }),
2025
- chatMessage: z3.string().optional()
2026
- });
2027
- var memoryScopeSchema = z3.object({
2028
- uaid: z3.string().min(1).optional(),
2029
- sessionId: z3.string().min(1).optional(),
2030
- namespace: z3.string().min(1).optional(),
2031
- userId: z3.string().min(1).optional()
2032
- }).refine((value) => Boolean(value.uaid || value.sessionId || value.namespace || value.userId), {
2033
- message: "Provide at least one scope identifier (uaid, sessionId, namespace, or userId)."
2034
- });
2035
- var memoryContextSchema = z3.object({
2036
- scope: memoryScopeSchema,
2037
- limit: z3.number().int().positive().max(200).optional(),
2038
- includeSummary: z3.boolean().optional()
2039
- });
2040
- var memoryNoteSchema = z3.object({
2041
- scope: memoryScopeSchema,
2042
- content: z3.string().min(1)
2043
- });
2044
- var memoryClearSchema = z3.object({
2045
- scope: memoryScopeSchema
2046
- });
2047
- var memorySearchSchema = z3.object({
2048
- scope: memoryScopeSchema,
2049
- query: z3.string().min(1),
2050
- limit: z3.number().int().positive().max(200).optional()
2051
- });
2052
- var waitForRegistrationInput = z3.object({
2053
- attemptId: z3.string(),
2054
- intervalMs: z3.number().int().positive().default(2e3),
2055
- timeoutMs: z3.number().int().positive().default(5 * 60 * 1e3)
2056
- });
2057
- var detectProtocolInput = z3.object({
2058
- headers: z3.record(z3.string(), z3.string()),
2059
- body: z3.string().optional()
2060
- });
2061
- var sessionIdInput = z3.object({ sessionId: z3.string().min(1) });
2062
- var emptyObject = z3.object({});
2063
- var updateAgentInput = z3.object({
2064
- uaid: z3.string().min(1),
2065
- payload: agentRegistrationSchema
2066
- });
2067
- var registryNamespaceInput = z3.object({
2068
- registry: z3.string().min(1),
2069
- query: z3.string().optional()
2070
- });
2071
- var creditBalanceInput = z3.object({
2072
- hederaAccountId: z3.string().min(1).optional(),
2073
- x402AccountId: z3.string().min(1).optional()
2074
- });
2075
- var ledgerNetworkEnum = z3.enum(["mainnet", "testnet"]);
2076
- var ledgerChallengeInput = z3.object({
2077
- accountId: z3.string().min(1),
2078
- network: ledgerNetworkEnum
2079
- });
2080
- var ledgerVerifyInput = z3.object({
2081
- challengeId: z3.string().min(1),
2082
- accountId: z3.string().min(1),
2083
- network: ledgerNetworkEnum,
2084
- signature: z3.string().min(1),
2085
- signatureKind: z3.enum(["raw", "map"]).optional(),
2086
- publicKey: z3.string().optional(),
2087
- expiresInMinutes: z3.number().int().positive().optional()
2088
- });
2089
- var purchaseHbarInput = z3.object({
2090
- accountId: z3.string().min(1),
2091
- privateKey: z3.string().min(1),
2092
- hbarAmount: z3.number().positive(),
2093
- memo: z3.string().optional(),
2094
- metadata: z3.record(z3.string(), z3.any()).optional()
2095
- });
2096
- var buyX402Input = z3.object({
2097
- accountId: z3.string().min(1),
2098
- credits: z3.number().positive(),
2099
- usdAmount: z3.number().positive().optional(),
2100
- description: z3.string().optional(),
2101
- metadata: z3.record(z3.string(), z3.any()).optional(),
2102
- evmPrivateKey: z3.string().min(1),
2103
- network: z3.enum(["base", "base-sepolia"]).optional(),
2104
- rpcUrl: z3.string().url().optional()
2105
- });
2106
- var mcp = new FastMCP({
2107
- name: "hashgraph-standards",
2108
- version: "1.0.0",
2109
- instructions: connectionInstructions,
2110
- // Route FastMCP logging to our pino logger (stderr) to keep stdio transport clean.
2111
- logger: {
2112
- debug: (...args) => logger.debug(args),
2113
- info: (...args) => logger.info(args),
2114
- warn: (...args) => logger.warn(args),
2115
- error: (...args) => logger.error(args),
2116
- log: (...args) => logger.info(args)
2117
- }
2118
- });
2119
- var rawToolDefinitions = [
2120
- {
2121
- name: "hol.search",
2122
- description: "Keyword search for agents or MCP servers with filtering controls.",
2123
- schema: searchInput,
2124
- handler: (input) => withBroker((client) => client.search(input), "hol.search")
2125
- },
2126
- {
2127
- name: "hol.vectorSearch",
2128
- description: "Vector similarity search across registered agents.",
2129
- schema: vectorSearchInput,
2130
- handler: (input) => withBroker((client) => client.vectorSearch(input), "hol.vectorSearch")
2131
- },
2132
- {
2133
- name: "hol.resolveUaid",
2134
- description: "Resolve, validate, and check the status of a UAID in one call.",
2135
- schema: uaidInput,
2136
- handler: ({ uaid }) => withBroker(async (client) => {
2137
- const [resolved, validation, status] = await Promise.all([
2138
- client.resolveUaid(uaid),
2139
- client.validateUaid(uaid),
2140
- client.getUaidConnectionStatus(uaid)
2141
- ]);
2142
- return { resolved, validation, status };
2143
- }, "hol.resolveUaid")
2144
- },
2145
- {
2146
- name: "hol.closeUaidConnection",
2147
- description: "Force-close any open UAID connection.",
2148
- schema: uaidInput,
2149
- handler: ({ uaid }) => withBroker((client) => client.closeUaidConnection(uaid), "hol.closeUaidConnection")
2150
- },
2151
- {
2152
- name: "hol.getRegistrationQuote",
2153
- description: "Estimate fees for a given agent registration payload.",
2154
- schema: registrationPayload,
2155
- handler: ({ payload }) => withBroker((client) => client.getRegistrationQuote(payload), "hol.getRegistrationQuote")
2156
- },
2157
- {
2158
- name: "hol.registerAgent",
2159
- description: "Submit an HCS-11-compatible agent registration.",
2160
- schema: registrationPayload,
2161
- handler: ({ payload }) => withBroker((client) => client.registerAgent(payload), "hol.registerAgent")
2162
- },
2163
- {
2164
- name: "hol.waitForRegistrationCompletion",
2165
- description: "Poll the registry broker until a registration attempt resolves.",
2166
- schema: waitForRegistrationInput,
2167
- handler: ({ attemptId, intervalMs, timeoutMs }) => withBroker(
2168
- (client) => client.waitForRegistrationCompletion(attemptId, {
2169
- intervalMs,
2170
- timeoutMs
2171
- }),
2172
- "hol.waitForRegistrationCompletion"
2173
- )
2174
- },
2175
- {
2176
- name: "hol.updateAgent",
2177
- description: "Update an existing agent registration payload.",
2178
- schema: updateAgentInput,
2179
- handler: ({ uaid, payload }) => withBroker((client) => client.updateAgent(uaid, payload), "hol.updateAgent")
2180
- },
2181
- {
2182
- name: "hol.additionalRegistries",
2183
- description: "Retrieve the catalog of additional registries and networks.",
2184
- schema: emptyObject,
2185
- handler: () => withBroker((client) => client.getAdditionalRegistries(), "hol.additionalRegistries")
2186
- },
2187
- {
2188
- name: "hol.registrySearchByNamespace",
2189
- description: "Search within a specific registry namespace.",
2190
- schema: registryNamespaceInput,
2191
- handler: ({ registry, query }) => withBroker((client) => client.registrySearchByNamespace(registry, query), "hol.registrySearchByNamespace")
2192
- },
2193
- {
2194
- name: "hol.chat.createSession",
2195
- description: "Open a chat session linked to a UAID.",
2196
- schema: chatSessionSchema,
2197
- handler: (input) => withBroker((client) => client.chat.createSession(input), "hol.chat.createSession")
2198
- },
2199
- {
2200
- name: "hol.chat.sendMessage",
2201
- description: "Send a message to an active chat session.",
2202
- schema: chatMessageSchema,
2203
- handler: (input) => withBroker(async (client) => {
2204
- const { sessionId, uaid, message, auth, streaming } = input;
2205
- const scopeForMemory = { sessionId: sessionId ?? void 0, uaid: uaid ?? void 0 };
2206
- if (sessionId) {
2207
- const payload2 = { sessionId, message, auth, streaming };
2208
- await recordMemory2(scopeForMemory, "user", message, "hol.chat.sendMessage");
2209
- const response2 = await client.chat.sendMessage(payload2);
2210
- await recordMemory2(scopeForMemory, "assistant", stringifyForMemory(response2), "hol.chat.sendMessage");
2211
- return response2;
2212
- }
2213
- if (!uaid) {
2214
- throw new Error("sessionId missing; provide uaid so a session can be created before sending.");
2215
- }
2216
- const session = await client.chat.createSession({ uaid, auth });
2217
- const derivedSessionId = session.sessionId;
2218
- if (!derivedSessionId) {
2219
- throw new Error("Unable to determine sessionId from broker response when auto-creating chat session.");
2220
- }
2221
- const payload = {
2222
- sessionId: derivedSessionId,
2223
- message,
2224
- auth,
2225
- streaming
2226
- };
2227
- scopeForMemory.sessionId = derivedSessionId;
2228
- await recordMemory2(scopeForMemory, "user", message, "hol.chat.sendMessage");
2229
- const response = await client.chat.sendMessage(payload);
2230
- await recordMemory2(scopeForMemory, "assistant", stringifyForMemory(response), "hol.chat.sendMessage");
2231
- return response;
2232
- }, "hol.chat.sendMessage")
2233
- },
2234
- {
2235
- name: "hol.chat.history",
2236
- description: "Retrieve the message history for a chat session.",
2237
- schema: sessionIdInput,
2238
- handler: ({ sessionId }) => withBroker((client) => client.chat.getHistory(sessionId), "hol.chat.history")
2239
- },
2240
- {
2241
- name: "hol.chat.compact",
2242
- description: "Compact chat history while preserving the latest entries.",
2243
- schema: chatCompactSchema,
2244
- handler: (input) => withBroker((client) => client.chat.compactHistory(input), "hol.chat.compact")
2245
- },
2246
- {
2247
- name: "hol.chat.end",
2248
- description: "End a chat session and release broker resources.",
2249
- schema: sessionIdInput,
2250
- handler: ({ sessionId }) => withBroker((client) => client.chat.endSession(sessionId), "hol.chat.end")
2251
- },
2252
- {
2253
- name: "hol.stats",
2254
- description: "High-level registry statistics and usage metrics.",
2255
- schema: emptyObject,
2256
- handler: () => withBroker((client) => client.stats(), "hol.stats")
2257
- },
2258
- {
2259
- name: "hol.metricsSummary",
2260
- description: "Aggregated broker metrics suitable for dashboards.",
2261
- schema: emptyObject,
2262
- handler: () => withBroker((client) => client.metricsSummary(), "hol.metricsSummary")
2263
- },
2264
- {
2265
- name: "hol.dashboardStats",
2266
- description: "Detailed dashboard statistics from the broker.",
2267
- schema: emptyObject,
2268
- handler: () => withBroker((client) => client.dashboardStats(), "hol.dashboardStats")
2269
- },
2270
- {
2271
- name: "hol.websocketStats",
2272
- description: "Retrieve websocket connection counts and throughput.",
2273
- schema: emptyObject,
2274
- handler: () => withBroker((client) => client.websocketStats(), "hol.websocketStats")
2275
- },
2276
- {
2277
- name: "hol.ledger.challenge",
2278
- description: "Create a ledger challenge message for account verification.",
2279
- schema: ledgerChallengeInput,
2280
- handler: (input) => withBroker((client) => client.createLedgerChallenge(input), "hol.ledger.challenge")
2281
- },
2282
- {
2283
- name: "hol.ledger.authenticate",
2284
- description: "Verify a signed ledger challenge (sets ledger API key).",
2285
- schema: ledgerVerifyInput,
2286
- handler: (input) => withBroker((client) => client.verifyLedgerChallenge(input), "hol.ledger.authenticate")
2287
- },
2288
- {
2289
- name: "hol.purchaseCredits.hbar",
2290
- description: "Purchase registry credits using HBAR funds.",
2291
- schema: purchaseHbarInput,
2292
- handler: (input) => withBroker((client) => client.purchaseCreditsWithHbar(input), "hol.purchaseCredits.hbar")
2293
- },
2294
- {
2295
- name: "hol.credits.balance",
2296
- description: "Fetch credit balances for the current API key and optional Hedera/X402 accounts.",
2297
- schema: creditBalanceInput,
2298
- handler: async (input) => {
2299
- const hederaAccountId = input.hederaAccountId;
2300
- const [apiKeyBalance, hederaBalance, x402Balance] = await Promise.all([
2301
- getCreditBalance(),
2302
- hederaAccountId ? safeBalanceLookup("hedera", hederaAccountId) : Promise.resolve(null),
2303
- input.x402AccountId ? safeBalanceLookup("x402", input.x402AccountId) : Promise.resolve(null)
2304
- ]);
2305
- return {
2306
- apiKey: apiKeyBalance,
2307
- hedera: hederaBalance,
2308
- x402: x402Balance
2309
- };
2310
- }
2311
- },
2312
- {
2313
- name: "hol.x402.minimums",
2314
- description: "Fetch the minimum credit purchase requirements for X402.",
2315
- schema: emptyObject,
2316
- handler: () => withBroker((client) => client.getX402Minimums(), "hol.x402.minimums")
2317
- },
2318
- {
2319
- name: "hol.x402.buyCredits",
2320
- description: "Buy registry credits via X402 using an EVM private key.",
2321
- schema: buyX402Input,
2322
- handler: (input) => withBroker((client) => client.buyCreditsWithX402(input), "hol.x402.buyCredits")
2323
- },
2324
- {
2325
- name: "hol.memory.context",
2326
- description: "Fetch recent memory entries and optional summary for a scope.",
2327
- schema: memoryContextSchema,
2328
- handler: async (input) => {
2329
- const service = ensureMemoryEnabled();
2330
- return service.getContext({
2331
- scope: input.scope,
2332
- limit: input.limit,
2333
- includeSummary: input.includeSummary ?? true
2334
- });
2335
- }
2336
- },
2337
- {
2338
- name: "hol.memory.note",
2339
- description: "Save a free-form note into memory for the given scope.",
2340
- schema: memoryNoteSchema,
2341
- handler: async (input) => {
2342
- const service = ensureMemoryEnabled();
2343
- return service.note(input.scope, input.content);
2344
- }
2345
- },
2346
- {
2347
- name: "hol.memory.clear",
2348
- description: "Clear memory entries and summaries for the given scope.",
2349
- schema: memoryClearSchema,
2350
- handler: async (input) => {
2351
- const service = ensureMemoryEnabled();
2352
- const removed = await service.clear(input.scope);
2353
- return { removed };
2354
- }
2355
- },
2356
- {
2357
- name: "hol.memory.search",
2358
- description: "Keyword search across stored memory for a scope.",
2359
- schema: memorySearchSchema,
2360
- handler: async (input) => {
2361
- const service = ensureMemoryEnabled();
2362
- return service.search({ scope: input.scope, query: input.query, limit: input.limit });
2363
- }
2364
- },
2365
- {
2366
- name: "workflow.discovery",
2367
- description: "Pipeline: hol.search + hol.vectorSearch",
2368
- schema: workflowDiscoveryInput,
2369
- handler: async (input) => formatPipelineResult(await discoveryPipeline.run(input))
2370
- },
2371
- {
2372
- name: "workflow.registerMcp",
2373
- description: "Pipeline: get quote, register agent, wait for completion",
2374
- schema: workflowRegistrationInput,
2375
- handler: async ({ payload }) => formatPipelineResult(await registrationPipeline.run({ payload }))
2376
- },
2377
- {
2378
- name: "workflow.chatSmoke",
2379
- description: "Pipeline: chat session smoke test for UAID",
2380
- schema: workflowChatInput,
2381
- handler: async (input) => formatPipelineResult(await chatPipeline.run(input))
2382
- },
2383
- {
2384
- name: "workflow.opsCheck",
2385
- description: "Pipeline: stats, metrics, dashboard, protocols",
2386
- schema: workflowOpsInput,
2387
- handler: async () => formatPipelineResult(await opsPipeline.run({}))
2388
- },
2389
- {
2390
- name: "workflow.openrouterChat",
2391
- description: "Pipeline: discover OpenRouter model and run a chat message",
2392
- schema: openRouterChatToolSchema,
2393
- handler: async (input) => formatPipelineResult(await openRouterChatWorkflow.run(input))
2394
- },
2395
- {
2396
- name: "workflow.registryBrokerShowcase",
2397
- description: "Pipeline: discovery, analytics, UAID validation, chat",
2398
- schema: registryShowcaseToolSchema,
2399
- handler: async (input) => formatPipelineResult(await registryBrokerShowcaseWorkflow.run(input))
2400
- },
2401
- {
2402
- name: "workflow.agentverseBridge",
2403
- description: "Pipeline: relay chat between local UAID and Agentverse UAID",
2404
- schema: bridgePayloadSchema,
2405
- handler: async (input) => formatPipelineResult(await agentverseBridgeWorkflow.run(input))
2406
- },
2407
- {
2408
- name: "workflow.erc8004Discovery",
2409
- description: "Pipeline: search ERC-8004 registries",
2410
- schema: erc8004DiscoveryToolSchema,
2411
- handler: async (input) => formatPipelineResult(await erc8004DiscoveryWorkflow.run(input))
2412
- },
2413
- {
2414
- name: "workflow.erc8004X402",
2415
- description: "Pipeline: ERC-8004 registration funded via X402 credits",
2416
- schema: erc8004X402ToolSchema,
2417
- handler: async (input) => formatPipelineResult(await erc8004X402Workflow.run(input))
2418
- },
2419
- {
2420
- name: "workflow.x402Registration",
2421
- description: "Pipeline: register agent using X402 payments + chat smoke test",
2422
- schema: x402RegistrationToolSchema,
2423
- handler: async (input) => formatPipelineResult(await x402RegistrationWorkflow.run(input))
2424
- },
2425
- {
2426
- name: "workflow.fullRegistration",
2427
- description: "Pipeline: discovery \u2192 register \u2192 chat \u2192 ops",
2428
- schema: workflowFullInput,
2429
- handler: async (input) => formatPipelineResult(await fullWorkflowPipeline.run(input))
2430
- }
2431
- ];
2432
- var toolDefinitions = rawToolDefinitions;
2433
- function ensureMemoryEnabled() {
2434
- if (!memoryService || !memoryService.isEnabled()) {
2435
- throw new Error("Memory is disabled or unavailable. Set MEMORY_ENABLED=1 and ensure the configured backend is installed.");
2436
- }
2437
- return memoryService;
2438
- }
2439
- function formatPipelineResult(result) {
2440
- const contextRecord = result.context && typeof result.context === "object" ? result.context : {};
2441
- const contextUaid = typeof contextRecord.uaid === "string" ? contextRecord.uaid : void 0;
2442
- const summaryLines = [
2443
- `Workflow: ${result.pipeline}`,
2444
- result.dryRun ? "(dry-run)" : void 0,
2445
- contextUaid ? `UAID: ${contextUaid}` : void 0,
2446
- `Steps executed: ${result.steps.length}`
2447
- ].filter(Boolean);
2448
- return {
2449
- content: [
2450
- { type: "text", text: summaryLines.join("\n") },
2451
- buildObjectContent("pipeline.result", result)
2452
- ]
2453
- };
2454
- }
2455
- function buildLoggedTool(definition) {
2456
- return {
2457
- name: definition.name,
2458
- description: definition.description,
2459
- parameters: definition.schema,
2460
- execute: async (args, context) => {
2461
- const requestId = context?.requestId ?? randomUUID3();
2462
- const started = Date.now();
2463
- try {
2464
- const parsedInput = definition.schema.parse(args);
2465
- logger.debug({ requestId, tool: definition.name }, "tool.invoke");
2466
- const result = await definition.handler(parsedInput);
2467
- if (memoryService && memoryService.isEnabled()) {
2468
- const scopeForMemory = deriveScopeFromArgs(parsedInput);
2469
- if (hasScope(scopeForMemory)) {
2470
- void memoryService.recordToolEvent(definition.name, scopeForMemory, { input: parsedInput, result }).catch((error) => logger.warn({ requestId, tool: definition.name, error }, "memory.capture.failed"));
2471
- }
2472
- }
2473
- logger.info(
2474
- {
2475
- requestId,
2476
- tool: definition.name,
2477
- durationMs: Date.now() - started
2478
- },
2479
- "tool.success"
2480
- );
2481
- return normalizeResult(result);
2482
- } catch (error) {
2483
- logger.error(
2484
- {
2485
- requestId,
2486
- tool: definition.name,
2487
- durationMs: Date.now() - started,
2488
- error: error instanceof Error ? error.message : String(error)
2489
- },
2490
- "tool.failure"
2491
- );
2492
- throw error;
2493
- }
2494
- }
2495
- };
2496
- }
2497
- for (const definition of toolDefinitions) {
2498
- mcp.addTool(buildLoggedTool(definition));
2499
- }
2500
- var registeredTools = toolDefinitions;
2501
- function isContentValue(value) {
2502
- return Boolean(
2503
- value && typeof value === "object" && "type" in value && typeof value.type === "string"
2504
- );
2505
- }
2506
- function normalizeResult(value) {
2507
- if (isResultShape(value)) {
2508
- const record = value;
2509
- return {
2510
- content: normalizeContent(record.content),
2511
- isError: typeof record.isError === "boolean" ? record.isError : void 0
2512
- };
2513
- }
2514
- if (isPlainObject(value)) {
2515
- return {
2516
- content: [buildObjectContent("tool.result", value)]
2517
- };
2518
- }
2519
- return { content: normalizeContent(value) };
2520
- }
2521
- function normalizeContent(result) {
2522
- if (Array.isArray(result) && result.every(isContentValue)) {
2523
- return result;
2524
- }
2525
- if (isContentValue(result)) {
2526
- return [result];
2527
- }
2528
- if (result === void 0 || result === null) {
2529
- return [{ type: "text", text: "ok" }];
2530
- }
2531
- if (typeof result === "string" || typeof result === "number" || typeof result === "boolean") {
2532
- return [{ type: "text", text: String(result) }];
2533
- }
2534
- if (isPlainObject(result)) {
2535
- return [buildObjectContent("tool.result", result)];
2536
- }
2537
- return [{ type: "text", text: JSON.stringify(result) }];
2538
- }
2539
- function buildObjectContent(name, value) {
2540
- return {
2541
- type: "text",
2542
- text: `${name}:
2543
- ${JSON.stringify(value, null, 2)}`
2544
- };
2545
- }
2546
- function isResultShape(value) {
2547
- return Boolean(value && typeof value === "object" && ("content" in value || "structuredContent" in value));
2548
- }
2549
- function isPlainObject(value) {
2550
- return Boolean(value && typeof value === "object" && !Array.isArray(value));
2551
- }
2552
- async function safeBalanceLookup(label, accountId) {
2553
- try {
2554
- return await getCreditBalance(accountId);
2555
- } catch (error) {
2556
- const message = error instanceof Error ? error.message : String(error);
2557
- return { error: `${label} balance unavailable: ${message}`, accountId };
2558
- }
2559
- }
2560
- function deriveScopeFromArgs(args) {
2561
- if (!args || typeof args !== "object") return {};
2562
- const record = args;
2563
- const scope = {};
2564
- if (typeof record.sessionId === "string") scope.sessionId = record.sessionId;
2565
- if (typeof record.uaid === "string") scope.uaid = record.uaid;
2566
- if (typeof record.namespace === "string") scope.namespace = record.namespace;
2567
- if (typeof record.userId === "string") scope.userId = record.userId;
2568
- return scope;
2569
- }
2570
- function hasScope(scope) {
2571
- return Boolean(scope.sessionId || scope.uaid || scope.namespace || scope.userId);
2572
- }
2573
- function stringifyForMemory(value) {
2574
- if (typeof value === "string") return value;
2575
- if (value === void 0 || value === null) return "";
2576
- try {
2577
- return JSON.stringify(value);
2578
- } catch {
2579
- return String(value);
2580
- }
2581
- }
2582
- async function recordMemory2(scope, role, content, toolName) {
2583
- if (!memoryService || !memoryService.isEnabled()) return;
2584
- try {
2585
- await memoryService.recordEntry({ scope, role, content, toolName });
2586
- } catch (error) {
2587
- logger.warn({ scope, toolName, error }, "memory.record.failed");
2588
- }
2589
- }
2590
- mcp.addResource({
2591
- name: "hol.search.help",
2592
- uri: "help://rb/search",
2593
- mimeType: "text/markdown",
2594
- load: async () => [
2595
- {
2596
- text: [
2597
- "# hol.search",
2598
- "",
2599
- "Discover registered agents or MCP servers.",
2600
- "",
2601
- "- `q`: keyword search",
2602
- "- `capabilities`: filter by declared skills",
2603
- '- `metadata`: pass `{ "region": ["na"] }` style filters',
2604
- "- `type`: limit results to `ai-agents` or `mcp-servers`"
2605
- ].join("\n")
2606
- }
2607
- ]
2608
- });
2609
- mcp.addResource({
2610
- name: "hol.tools.guide",
2611
- uri: "help://hol/usage",
2612
- mimeType: "text/markdown",
2613
- load: async () => [
2614
- {
2615
- text: [
2616
- "# Hashnet MCP quick usage",
2617
- "",
2618
- "Prefer workflow.* pipelines when available\u2014they run multiple broker calls and return both a text summary and structured results:",
2619
- "- Discovery: workflow.discovery { query?, limit? } (or hol.search / hol.vectorSearch).",
2620
- "- Registration: workflow.registerMcp { payload } (quote \u2192 register \u2192 wait) or workflow.fullRegistration to add discovery/chat/ops.",
2621
- "- Chat: hol.chat.createSession { uaid, auth?, historyTtlSeconds? } \u2192 hol.chat.sendMessage { sessionId OR uaid, message, auth?, streaming? } \u2192 hol.chat.history/compact/end.",
2622
- "- UAID validation/resets: hol.resolveUaid { uaid }, hol.closeUaidConnection { uaid }.",
2623
- "- Ops/metrics: workflow.opsCheck or hol.stats / hol.metricsSummary / hol.dashboardStats.",
2624
- "- Credits: hol.credits.balance first, then hol.purchaseCredits.hbar or hol.x402.buyCredits (X402 requires evmPrivateKey; call hol.x402.minimums to inspect limits).",
2625
- "- Protocols: hol.listProtocols and hol.detectProtocol when inspecting inbound requests.",
2626
- "",
2627
- "Ask the user for any missing UAID, registration payload fields, accountId, or auth tokens before calling tools. Keep sessionId/uaid strings verbatim."
2628
- ].join("\n")
2629
- }
2630
- ]
2631
- });
2632
- mcp.addResource({
2633
- name: "hol.memory.guide",
2634
- uri: "help://hol/memory",
2635
- mimeType: "text/markdown",
2636
- load: async () => [
2637
- {
2638
- text: [
2639
- "# Memory tools",
2640
- "",
2641
- "Memory is optional and disabled by default. Enable with `MEMORY_ENABLED=1` (defaults to SQLite at `MEMORY_STORAGE_PATH`).",
2642
- "",
2643
- "- `hol.memory.context { scope, limit?, includeSummary? }`: recent entries plus optional summary for a UAID/session/namespace.",
2644
- "- `hol.memory.note { scope, content }`: store a note in the scope.",
2645
- "- `hol.memory.search { scope, query, limit? }`: keyword search within scoped memory.",
2646
- "- `hol.memory.clear { scope }`: drop entries + summary for the scope.",
2647
- "",
2648
- "Scopes: supply at least one of `uaid`, `sessionId`, `namespace`, or `userId`. The service bounds responses to avoid overwhelming clients."
2649
- ].join("\n")
2650
- }
2651
- ]
2652
- });
2653
-
2654
- // src/transports/index.ts
2655
- var LOOPBACK = "127.0.0.1";
2656
- async function runStdio() {
2657
- logger.info("Starting stdio transport");
2658
- await mcp.start({ transportType: "stdio" });
2659
- logger.info("Stdio transport started");
2660
- }
2661
- async function runSSE() {
2662
- const upstreamPort = config.httpStreamPort ?? config.port + 1;
2663
- await startHttpStreamBackend(upstreamPort);
2664
- const gateway = createGatewayServer(upstreamPort);
2665
- await new Promise((resolve, reject) => {
2666
- gateway.once("error", reject);
2667
- gateway.listen(config.port, () => {
2668
- logger.info({ port: config.port, upstreamPort }, "Gateway listening");
2669
- resolve();
2670
- });
2671
- });
2672
- }
2673
- async function startHttpStreamBackend(port) {
2674
- await mcp.start({
2675
- transportType: "httpStream",
2676
- httpStream: {
2677
- port,
2678
- host: LOOPBACK,
2679
- endpoint: "/mcp/stream",
2680
- enableJsonResponse: false
2681
- }
2682
- });
2683
- logger.info({ port }, "HTTP stream backend ready");
2684
- }
2685
- function createGatewayServer(upstreamPort) {
2686
- return http.createServer(createGatewayHandler(upstreamPort));
2687
- }
2688
- function createGatewayHandler(upstreamPort) {
2689
- return (req, res) => {
2690
- if (!req.url) {
2691
- res.writeHead(400).end("Missing URL");
2692
- return;
2693
- }
2694
- if (req.url.startsWith("/healthz")) {
2695
- const body = JSON.stringify({
2696
- status: "ok",
2697
- uptime: process.uptime(),
2698
- tools: registeredTools.length,
2699
- requestId: randomUUID4()
2700
- });
2701
- res.writeHead(200, { "content-type": "application/json" });
2702
- res.end(body);
2703
- return;
2704
- }
2705
- proxyRequest(req, res, upstreamPort);
2706
- };
2707
- }
2708
- function proxyRequest(req, res, upstreamPort) {
2709
- logger.debug({ method: req.method, url: req.url }, "gateway.forward");
2710
- const proxyReq = http.request(
2711
- {
2712
- hostname: LOOPBACK,
2713
- port: upstreamPort,
2714
- method: req.method,
2715
- path: req.url,
2716
- headers: {
2717
- ...req.headers,
2718
- host: `${LOOPBACK}:${upstreamPort}`
2719
- }
2720
- },
2721
- (proxyRes) => {
2722
- res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
2723
- proxyRes.pipe(res);
2724
- }
2725
- );
2726
- proxyReq.on("error", (error) => {
2727
- logger.error({ error }, "proxy.error");
2728
- if (!res.headersSent) {
2729
- res.writeHead(502);
2730
- }
2731
- res.end("Upstream error");
2732
- });
2733
- if (req.method && ["POST", "PUT", "PATCH"].includes(req.method)) {
2734
- const tee = new PassThrough();
2735
- let preview = "";
2736
- tee.on("data", (chunk) => {
2737
- if (preview.length < 512) {
2738
- preview += chunk.toString();
2739
- }
2740
- });
2741
- tee.on("end", () => {
2742
- if (preview.length) {
2743
- logger.debug({ preview: preview.slice(0, 512) }, "proxy.preview");
2744
- }
2745
- });
2746
- req.pipe(tee).pipe(proxyReq);
2747
- } else {
2748
- req.pipe(proxyReq);
2749
- }
2750
- }
2751
-
2752
- // src/index.ts
2753
- var transport = (process.env.MCP_TRANSPORT ?? "sse").toLowerCase();
2754
- if (transport === "stdio") {
2755
- logger.info({ transport }, "starting MCP server");
2756
- runStdio().catch((err) => {
2757
- logger.error({ err }, "Failed to start stdio transport");
2758
- process.exitCode = 1;
2759
- });
2760
- } else {
2761
- logger.info({ transport }, "starting MCP server");
2762
- runSSE().catch((err) => {
2763
- logger.error({ err }, "Failed to start SSE transport");
2764
- process.exitCode = 1;
2765
- });
2766
- }
2767
- //# sourceMappingURL=index.js.map