@agentfield/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/dist/index.d.ts +823 -0
- package/dist/index.js +2257 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,2257 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import crypto, { randomUUID } from 'crypto';
|
|
3
|
+
import { AsyncLocalStorage } from 'async_hooks';
|
|
4
|
+
import { generateText, streamText, embed, embedMany } from 'ai';
|
|
5
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
6
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
7
|
+
import os from 'os';
|
|
8
|
+
import axios, { isAxiosError } from 'axios';
|
|
9
|
+
import WebSocket from 'ws';
|
|
10
|
+
import { Buffer } from 'buffer';
|
|
11
|
+
|
|
12
|
+
// src/agent/Agent.ts
|
|
13
|
+
|
|
14
|
+
// src/agent/ReasonerRegistry.ts
|
|
15
|
+
var ReasonerRegistry = class {
|
|
16
|
+
reasoners = /* @__PURE__ */ new Map();
|
|
17
|
+
register(name, handler, options) {
|
|
18
|
+
this.reasoners.set(name, { name, handler, options });
|
|
19
|
+
}
|
|
20
|
+
includeRouter(router) {
|
|
21
|
+
router.reasoners.forEach((reasoner) => {
|
|
22
|
+
this.reasoners.set(reasoner.name, reasoner);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
get(name) {
|
|
26
|
+
return this.reasoners.get(name);
|
|
27
|
+
}
|
|
28
|
+
all() {
|
|
29
|
+
return Array.from(this.reasoners.values());
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// src/agent/SkillRegistry.ts
|
|
34
|
+
var SkillRegistry = class {
|
|
35
|
+
skills = /* @__PURE__ */ new Map();
|
|
36
|
+
register(name, handler, options) {
|
|
37
|
+
this.skills.set(name, { name, handler, options });
|
|
38
|
+
}
|
|
39
|
+
includeRouter(router) {
|
|
40
|
+
router.skills.forEach((skill) => {
|
|
41
|
+
this.skills.set(skill.name, skill);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
get(name) {
|
|
45
|
+
return this.skills.get(name);
|
|
46
|
+
}
|
|
47
|
+
all() {
|
|
48
|
+
return Array.from(this.skills.values());
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
var store = new AsyncLocalStorage();
|
|
52
|
+
var ExecutionContext = class {
|
|
53
|
+
input;
|
|
54
|
+
metadata;
|
|
55
|
+
req;
|
|
56
|
+
res;
|
|
57
|
+
agent;
|
|
58
|
+
constructor(params) {
|
|
59
|
+
this.input = params.input;
|
|
60
|
+
this.metadata = params.metadata;
|
|
61
|
+
this.req = params.req;
|
|
62
|
+
this.res = params.res;
|
|
63
|
+
this.agent = params.agent;
|
|
64
|
+
}
|
|
65
|
+
static run(ctx, fn) {
|
|
66
|
+
return store.run(ctx, fn);
|
|
67
|
+
}
|
|
68
|
+
static getCurrent() {
|
|
69
|
+
return store.getStore();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// src/context/ReasonerContext.ts
|
|
74
|
+
var ReasonerContext = class {
|
|
75
|
+
input;
|
|
76
|
+
executionId;
|
|
77
|
+
runId;
|
|
78
|
+
sessionId;
|
|
79
|
+
actorId;
|
|
80
|
+
workflowId;
|
|
81
|
+
parentExecutionId;
|
|
82
|
+
callerDid;
|
|
83
|
+
targetDid;
|
|
84
|
+
agentNodeDid;
|
|
85
|
+
req;
|
|
86
|
+
res;
|
|
87
|
+
agent;
|
|
88
|
+
aiClient;
|
|
89
|
+
memory;
|
|
90
|
+
workflow;
|
|
91
|
+
did;
|
|
92
|
+
constructor(params) {
|
|
93
|
+
this.input = params.input;
|
|
94
|
+
this.executionId = params.executionId;
|
|
95
|
+
this.runId = params.runId;
|
|
96
|
+
this.sessionId = params.sessionId;
|
|
97
|
+
this.actorId = params.actorId;
|
|
98
|
+
this.workflowId = params.workflowId;
|
|
99
|
+
this.parentExecutionId = params.parentExecutionId;
|
|
100
|
+
this.callerDid = params.callerDid;
|
|
101
|
+
this.targetDid = params.targetDid;
|
|
102
|
+
this.agentNodeDid = params.agentNodeDid;
|
|
103
|
+
this.req = params.req;
|
|
104
|
+
this.res = params.res;
|
|
105
|
+
this.agent = params.agent;
|
|
106
|
+
this.aiClient = params.aiClient;
|
|
107
|
+
this.memory = params.memory;
|
|
108
|
+
this.workflow = params.workflow;
|
|
109
|
+
this.did = params.did;
|
|
110
|
+
}
|
|
111
|
+
ai(prompt, options) {
|
|
112
|
+
return this.aiClient.generate(prompt, options);
|
|
113
|
+
}
|
|
114
|
+
aiStream(prompt, options) {
|
|
115
|
+
return this.aiClient.stream(prompt, options);
|
|
116
|
+
}
|
|
117
|
+
call(target, input) {
|
|
118
|
+
return this.agent.call(target, input);
|
|
119
|
+
}
|
|
120
|
+
discover(options) {
|
|
121
|
+
return this.agent.discover(options);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
function getCurrentContext() {
|
|
125
|
+
const execution = ExecutionContext.getCurrent();
|
|
126
|
+
if (!execution) return void 0;
|
|
127
|
+
const { metadata, input, agent, req, res } = execution;
|
|
128
|
+
return new ReasonerContext({
|
|
129
|
+
input,
|
|
130
|
+
executionId: metadata.executionId,
|
|
131
|
+
runId: metadata.runId,
|
|
132
|
+
sessionId: metadata.sessionId,
|
|
133
|
+
actorId: metadata.actorId,
|
|
134
|
+
workflowId: metadata.workflowId,
|
|
135
|
+
parentExecutionId: metadata.parentExecutionId,
|
|
136
|
+
callerDid: metadata.callerDid,
|
|
137
|
+
targetDid: metadata.targetDid,
|
|
138
|
+
agentNodeDid: metadata.agentNodeDid,
|
|
139
|
+
req,
|
|
140
|
+
res,
|
|
141
|
+
agent,
|
|
142
|
+
aiClient: agent.getAIClient(),
|
|
143
|
+
memory: agent.getMemoryInterface(metadata),
|
|
144
|
+
workflow: agent.getWorkflowReporter(metadata),
|
|
145
|
+
did: agent.getDidInterface(metadata, input)
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// src/context/SkillContext.ts
|
|
150
|
+
var SkillContext = class {
|
|
151
|
+
input;
|
|
152
|
+
executionId;
|
|
153
|
+
sessionId;
|
|
154
|
+
workflowId;
|
|
155
|
+
callerDid;
|
|
156
|
+
agentNodeDid;
|
|
157
|
+
req;
|
|
158
|
+
res;
|
|
159
|
+
agent;
|
|
160
|
+
memory;
|
|
161
|
+
workflow;
|
|
162
|
+
did;
|
|
163
|
+
constructor(params) {
|
|
164
|
+
this.input = params.input;
|
|
165
|
+
this.executionId = params.executionId;
|
|
166
|
+
this.sessionId = params.sessionId;
|
|
167
|
+
this.workflowId = params.workflowId;
|
|
168
|
+
this.callerDid = params.callerDid;
|
|
169
|
+
this.agentNodeDid = params.agentNodeDid;
|
|
170
|
+
this.req = params.req;
|
|
171
|
+
this.res = params.res;
|
|
172
|
+
this.agent = params.agent;
|
|
173
|
+
this.memory = params.memory;
|
|
174
|
+
this.workflow = params.workflow;
|
|
175
|
+
this.did = params.did;
|
|
176
|
+
}
|
|
177
|
+
discover(options) {
|
|
178
|
+
return this.agent.discover(options);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
function getCurrentSkillContext() {
|
|
182
|
+
const execution = ExecutionContext.getCurrent();
|
|
183
|
+
if (!execution) return void 0;
|
|
184
|
+
const { metadata, input, agent, req, res } = execution;
|
|
185
|
+
return new SkillContext({
|
|
186
|
+
input,
|
|
187
|
+
executionId: metadata.executionId,
|
|
188
|
+
sessionId: metadata.sessionId,
|
|
189
|
+
workflowId: metadata.workflowId,
|
|
190
|
+
callerDid: metadata.callerDid,
|
|
191
|
+
agentNodeDid: metadata.agentNodeDid,
|
|
192
|
+
req,
|
|
193
|
+
res,
|
|
194
|
+
agent,
|
|
195
|
+
memory: agent.getMemoryInterface(metadata),
|
|
196
|
+
workflow: agent.getWorkflowReporter(metadata),
|
|
197
|
+
did: agent.getDidInterface(metadata, input)
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
var RateLimitError = class extends Error {
|
|
201
|
+
retryAfter;
|
|
202
|
+
constructor(message, retryAfter) {
|
|
203
|
+
super(message);
|
|
204
|
+
this.name = "RateLimitError";
|
|
205
|
+
this.retryAfter = retryAfter;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
var StatelessRateLimiter = class {
|
|
209
|
+
maxRetries;
|
|
210
|
+
baseDelay;
|
|
211
|
+
maxDelay;
|
|
212
|
+
jitterFactor;
|
|
213
|
+
circuitBreakerThreshold;
|
|
214
|
+
circuitBreakerTimeout;
|
|
215
|
+
_containerSeed;
|
|
216
|
+
_consecutiveFailures = 0;
|
|
217
|
+
_circuitOpenTime;
|
|
218
|
+
constructor(options = {}) {
|
|
219
|
+
this.maxRetries = options.maxRetries ?? 20;
|
|
220
|
+
this.baseDelay = options.baseDelay ?? 1;
|
|
221
|
+
this.maxDelay = options.maxDelay ?? 300;
|
|
222
|
+
this.jitterFactor = options.jitterFactor ?? 0.25;
|
|
223
|
+
this.circuitBreakerThreshold = options.circuitBreakerThreshold ?? 10;
|
|
224
|
+
this.circuitBreakerTimeout = options.circuitBreakerTimeout ?? 300;
|
|
225
|
+
this._containerSeed = this._getContainerSeed();
|
|
226
|
+
}
|
|
227
|
+
_getContainerSeed() {
|
|
228
|
+
const identifier = `${os.hostname()}-${process.pid}`;
|
|
229
|
+
const hash = crypto.createHash("md5").update(identifier).digest("hex");
|
|
230
|
+
return parseInt(hash.slice(0, 8), 16);
|
|
231
|
+
}
|
|
232
|
+
_isRateLimitError(error) {
|
|
233
|
+
if (!error) return false;
|
|
234
|
+
const err = error;
|
|
235
|
+
const className = err?.constructor?.name;
|
|
236
|
+
if (className && className.includes("RateLimitError")) {
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
const response = err?.response;
|
|
240
|
+
const statusCandidates = [
|
|
241
|
+
err?.status,
|
|
242
|
+
err?.statusCode,
|
|
243
|
+
response?.status,
|
|
244
|
+
response?.statusCode,
|
|
245
|
+
response?.status_code
|
|
246
|
+
];
|
|
247
|
+
if (statusCandidates.some((code) => code === 429 || code === 503)) {
|
|
248
|
+
return true;
|
|
249
|
+
}
|
|
250
|
+
const message = String(err?.message ?? err ?? "").toLowerCase();
|
|
251
|
+
const rateLimitKeywords = [
|
|
252
|
+
"rate limit",
|
|
253
|
+
"rate-limit",
|
|
254
|
+
"rate_limit",
|
|
255
|
+
"too many requests",
|
|
256
|
+
"quota exceeded",
|
|
257
|
+
"temporarily rate-limited",
|
|
258
|
+
"rate limited",
|
|
259
|
+
"requests per",
|
|
260
|
+
"rpm exceeded",
|
|
261
|
+
"tpm exceeded",
|
|
262
|
+
"usage limit",
|
|
263
|
+
"throttled",
|
|
264
|
+
"throttling"
|
|
265
|
+
];
|
|
266
|
+
return rateLimitKeywords.some((keyword) => message.includes(keyword));
|
|
267
|
+
}
|
|
268
|
+
_extractRetryAfter(error) {
|
|
269
|
+
if (!error) return void 0;
|
|
270
|
+
const err = error;
|
|
271
|
+
const headers = err?.response?.headers ?? err?.response?.Headers ?? err?.response?.header;
|
|
272
|
+
if (headers && typeof headers === "object") {
|
|
273
|
+
const retryAfterKey = Object.keys(headers).find((k) => k.toLowerCase() === "retry-after");
|
|
274
|
+
if (retryAfterKey) {
|
|
275
|
+
const value = Array.isArray(headers[retryAfterKey]) ? headers[retryAfterKey][0] : headers[retryAfterKey];
|
|
276
|
+
const parsed2 = parseFloat(value);
|
|
277
|
+
if (!Number.isNaN(parsed2)) {
|
|
278
|
+
return parsed2;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
const retryAfter = err?.retryAfter ?? err?.retry_after;
|
|
283
|
+
const parsed = parseFloat(retryAfter);
|
|
284
|
+
if (!Number.isNaN(parsed)) {
|
|
285
|
+
return parsed;
|
|
286
|
+
}
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
_createJitterRng(seed) {
|
|
290
|
+
let x = seed >>> 0;
|
|
291
|
+
return () => {
|
|
292
|
+
x = (1664525 * x + 1013904223) % 4294967296;
|
|
293
|
+
return x / 4294967296;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
_calculateBackoffDelay(attempt, retryAfter) {
|
|
297
|
+
let baseDelay;
|
|
298
|
+
if (retryAfter && retryAfter <= this.maxDelay) {
|
|
299
|
+
baseDelay = retryAfter;
|
|
300
|
+
} else {
|
|
301
|
+
baseDelay = Math.min(this.baseDelay * 2 ** attempt, this.maxDelay);
|
|
302
|
+
}
|
|
303
|
+
const jitterRange = baseDelay * this.jitterFactor;
|
|
304
|
+
const rng = this._createJitterRng(this._containerSeed + attempt);
|
|
305
|
+
const jitter = (rng() * 2 - 1) * jitterRange;
|
|
306
|
+
const delay = Math.max(0.1, baseDelay + jitter);
|
|
307
|
+
return delay;
|
|
308
|
+
}
|
|
309
|
+
_checkCircuitBreaker() {
|
|
310
|
+
if (this._circuitOpenTime === void 0) {
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
if (this._now() - this._circuitOpenTime > this.circuitBreakerTimeout) {
|
|
314
|
+
this._circuitOpenTime = void 0;
|
|
315
|
+
this._consecutiveFailures = 0;
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
_updateCircuitBreaker(success) {
|
|
321
|
+
if (success) {
|
|
322
|
+
this._consecutiveFailures = 0;
|
|
323
|
+
this._circuitOpenTime = void 0;
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
this._consecutiveFailures += 1;
|
|
327
|
+
if (this._consecutiveFailures >= this.circuitBreakerThreshold && this._circuitOpenTime === void 0) {
|
|
328
|
+
this._circuitOpenTime = this._now();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async _sleep(delaySeconds) {
|
|
332
|
+
await new Promise((resolve) => setTimeout(resolve, delaySeconds * 1e3));
|
|
333
|
+
}
|
|
334
|
+
_now() {
|
|
335
|
+
return Date.now() / 1e3;
|
|
336
|
+
}
|
|
337
|
+
async executeWithRetry(fn) {
|
|
338
|
+
if (this._checkCircuitBreaker()) {
|
|
339
|
+
throw new RateLimitError(
|
|
340
|
+
`Circuit breaker is open. Too many consecutive rate limit failures. Will retry after ${this.circuitBreakerTimeout} seconds.`
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
let lastError;
|
|
344
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt += 1) {
|
|
345
|
+
try {
|
|
346
|
+
const result = await fn();
|
|
347
|
+
this._updateCircuitBreaker(true);
|
|
348
|
+
return result;
|
|
349
|
+
} catch (error) {
|
|
350
|
+
lastError = error;
|
|
351
|
+
if (!this._isRateLimitError(error)) {
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
this._updateCircuitBreaker(false);
|
|
355
|
+
if (attempt >= this.maxRetries) {
|
|
356
|
+
break;
|
|
357
|
+
}
|
|
358
|
+
const retryAfter = this._extractRetryAfter(error);
|
|
359
|
+
const delay = this._calculateBackoffDelay(attempt, retryAfter);
|
|
360
|
+
await this._sleep(delay);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
throw new RateLimitError(
|
|
364
|
+
`Rate limit retries exhausted after ${this.maxRetries} attempts. Last error: ${String(lastError)}`,
|
|
365
|
+
this._extractRetryAfter(lastError)
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
// src/ai/AIClient.ts
|
|
371
|
+
var AIClient = class {
|
|
372
|
+
config;
|
|
373
|
+
rateLimiter;
|
|
374
|
+
constructor(config = {}) {
|
|
375
|
+
this.config = {
|
|
376
|
+
enableRateLimitRetry: true,
|
|
377
|
+
rateLimitMaxRetries: 20,
|
|
378
|
+
rateLimitBaseDelay: 1,
|
|
379
|
+
rateLimitMaxDelay: 300,
|
|
380
|
+
rateLimitJitterFactor: 0.25,
|
|
381
|
+
rateLimitCircuitBreakerThreshold: 10,
|
|
382
|
+
rateLimitCircuitBreakerTimeout: 300,
|
|
383
|
+
...config
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async generate(prompt, options = {}) {
|
|
387
|
+
const model = this.buildModel(options);
|
|
388
|
+
const call = async () => generateText({
|
|
389
|
+
// type cast to avoid provider-model signature drift
|
|
390
|
+
model,
|
|
391
|
+
prompt,
|
|
392
|
+
system: options.system,
|
|
393
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
394
|
+
maxTokens: options.maxTokens ?? this.config.maxTokens,
|
|
395
|
+
schema: options.schema
|
|
396
|
+
});
|
|
397
|
+
const response = await this.withRateLimitRetry(call);
|
|
398
|
+
if (options.schema && response.object !== void 0) {
|
|
399
|
+
return response.object;
|
|
400
|
+
}
|
|
401
|
+
return response.text;
|
|
402
|
+
}
|
|
403
|
+
async stream(prompt, options = {}) {
|
|
404
|
+
const model = this.buildModel(options);
|
|
405
|
+
const streamResult = await this.withRateLimitRetry(
|
|
406
|
+
() => streamText({
|
|
407
|
+
model,
|
|
408
|
+
prompt,
|
|
409
|
+
system: options.system,
|
|
410
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
411
|
+
maxTokens: options.maxTokens ?? this.config.maxTokens
|
|
412
|
+
})
|
|
413
|
+
);
|
|
414
|
+
return streamResult.textStream;
|
|
415
|
+
}
|
|
416
|
+
async embed(value, options = {}) {
|
|
417
|
+
const model = this.buildEmbeddingModel(options);
|
|
418
|
+
const result = await this.withRateLimitRetry(
|
|
419
|
+
() => embed({
|
|
420
|
+
model,
|
|
421
|
+
value
|
|
422
|
+
})
|
|
423
|
+
);
|
|
424
|
+
return result.embedding;
|
|
425
|
+
}
|
|
426
|
+
async embedMany(values, options = {}) {
|
|
427
|
+
const model = this.buildEmbeddingModel(options);
|
|
428
|
+
const result = await this.withRateLimitRetry(
|
|
429
|
+
() => embedMany({
|
|
430
|
+
model,
|
|
431
|
+
values
|
|
432
|
+
})
|
|
433
|
+
);
|
|
434
|
+
return result.embeddings;
|
|
435
|
+
}
|
|
436
|
+
buildModel(options) {
|
|
437
|
+
const provider = options.provider ?? this.config.provider ?? "openai";
|
|
438
|
+
const modelName = options.model ?? this.config.model ?? "gpt-4o";
|
|
439
|
+
if (provider === "anthropic") {
|
|
440
|
+
const anthropic = createAnthropic({
|
|
441
|
+
apiKey: this.config.apiKey,
|
|
442
|
+
baseURL: this.config.baseUrl
|
|
443
|
+
});
|
|
444
|
+
return anthropic(modelName);
|
|
445
|
+
}
|
|
446
|
+
const openai = createOpenAI({
|
|
447
|
+
apiKey: this.config.apiKey,
|
|
448
|
+
baseURL: this.config.baseUrl
|
|
449
|
+
});
|
|
450
|
+
return openai(modelName);
|
|
451
|
+
}
|
|
452
|
+
buildEmbeddingModel(options) {
|
|
453
|
+
const provider = options.provider ?? this.config.provider ?? "openai";
|
|
454
|
+
const modelName = options.model ?? this.config.embeddingModel ?? "text-embedding-3-small";
|
|
455
|
+
if (provider === "anthropic") {
|
|
456
|
+
throw new Error("Embedding generation is not supported for Anthropic provider");
|
|
457
|
+
}
|
|
458
|
+
const openai = createOpenAI({
|
|
459
|
+
apiKey: this.config.apiKey,
|
|
460
|
+
baseURL: this.config.baseUrl
|
|
461
|
+
});
|
|
462
|
+
if (typeof openai.embedding !== "function") {
|
|
463
|
+
throw new Error("Embedding model is not available for the configured provider");
|
|
464
|
+
}
|
|
465
|
+
return openai.embedding(modelName);
|
|
466
|
+
}
|
|
467
|
+
getRateLimiter() {
|
|
468
|
+
if (!this.rateLimiter) {
|
|
469
|
+
this.rateLimiter = new StatelessRateLimiter({
|
|
470
|
+
maxRetries: this.config.rateLimitMaxRetries,
|
|
471
|
+
baseDelay: this.config.rateLimitBaseDelay,
|
|
472
|
+
maxDelay: this.config.rateLimitMaxDelay,
|
|
473
|
+
jitterFactor: this.config.rateLimitJitterFactor,
|
|
474
|
+
circuitBreakerThreshold: this.config.rateLimitCircuitBreakerThreshold,
|
|
475
|
+
circuitBreakerTimeout: this.config.rateLimitCircuitBreakerTimeout
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
return this.rateLimiter;
|
|
479
|
+
}
|
|
480
|
+
withRateLimitRetry(fn) {
|
|
481
|
+
if (this.config.enableRateLimitRetry === false) {
|
|
482
|
+
return fn();
|
|
483
|
+
}
|
|
484
|
+
return this.getRateLimiter().executeWithRetry(fn);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
var AgentFieldClient = class {
|
|
488
|
+
http;
|
|
489
|
+
config;
|
|
490
|
+
defaultHeaders;
|
|
491
|
+
constructor(config) {
|
|
492
|
+
const baseURL = (config.agentFieldUrl ?? "http://localhost:8080").replace(/\/$/, "");
|
|
493
|
+
this.http = axios.create({ baseURL });
|
|
494
|
+
this.config = config;
|
|
495
|
+
this.defaultHeaders = this.sanitizeHeaders(config.defaultHeaders ?? {});
|
|
496
|
+
}
|
|
497
|
+
async register(payload) {
|
|
498
|
+
await this.http.post("/api/v1/nodes/register", payload, { headers: this.mergeHeaders() });
|
|
499
|
+
}
|
|
500
|
+
async heartbeat(status = "ready") {
|
|
501
|
+
const nodeId = this.config.nodeId;
|
|
502
|
+
const res = await this.http.post(
|
|
503
|
+
`/api/v1/nodes/${nodeId}/heartbeat`,
|
|
504
|
+
{
|
|
505
|
+
status,
|
|
506
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
507
|
+
},
|
|
508
|
+
{ headers: this.mergeHeaders() }
|
|
509
|
+
);
|
|
510
|
+
return res.data;
|
|
511
|
+
}
|
|
512
|
+
async execute(target, input, metadata) {
|
|
513
|
+
const headers = {};
|
|
514
|
+
if (metadata?.runId) headers["X-Run-ID"] = metadata.runId;
|
|
515
|
+
if (metadata?.workflowId) headers["X-Workflow-ID"] = metadata.workflowId;
|
|
516
|
+
if (metadata?.parentExecutionId) headers["X-Parent-Execution-ID"] = metadata.parentExecutionId;
|
|
517
|
+
if (metadata?.sessionId) headers["X-Session-ID"] = metadata.sessionId;
|
|
518
|
+
if (metadata?.actorId) headers["X-Actor-ID"] = metadata.actorId;
|
|
519
|
+
if (metadata?.callerDid) headers["X-Caller-DID"] = metadata.callerDid;
|
|
520
|
+
if (metadata?.targetDid) headers["X-Target-DID"] = metadata.targetDid;
|
|
521
|
+
if (metadata?.agentNodeDid) headers["X-Agent-Node-DID"] = metadata.agentNodeDid;
|
|
522
|
+
if (metadata?.agentNodeId) headers["X-Agent-Node-ID"] = metadata.agentNodeId;
|
|
523
|
+
const res = await this.http.post(
|
|
524
|
+
`/api/v1/execute/${target}`,
|
|
525
|
+
{
|
|
526
|
+
input
|
|
527
|
+
},
|
|
528
|
+
{ headers: this.mergeHeaders(headers) }
|
|
529
|
+
);
|
|
530
|
+
return res.data?.result ?? res.data;
|
|
531
|
+
}
|
|
532
|
+
async publishWorkflowEvent(event) {
|
|
533
|
+
const payload = {
|
|
534
|
+
execution_id: event.executionId,
|
|
535
|
+
workflow_id: event.workflowId ?? event.runId,
|
|
536
|
+
run_id: event.runId,
|
|
537
|
+
reasoner_id: event.reasonerId,
|
|
538
|
+
type: event.reasonerId,
|
|
539
|
+
agent_node_id: event.agentNodeId,
|
|
540
|
+
status: event.status,
|
|
541
|
+
parent_execution_id: event.parentExecutionId,
|
|
542
|
+
parent_workflow_id: event.parentWorkflowId ?? event.workflowId ?? event.runId,
|
|
543
|
+
input_data: event.inputData ?? {},
|
|
544
|
+
result: event.result,
|
|
545
|
+
error: event.error,
|
|
546
|
+
duration_ms: event.durationMs
|
|
547
|
+
};
|
|
548
|
+
this.http.post("/api/v1/workflow/executions/events", payload, {
|
|
549
|
+
headers: this.mergeHeaders(),
|
|
550
|
+
timeout: this.config.devMode ? 1e3 : void 0
|
|
551
|
+
}).catch(() => {
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
async updateExecutionStatus(executionId, update) {
|
|
555
|
+
if (!executionId) {
|
|
556
|
+
throw new Error("executionId is required to update workflow status");
|
|
557
|
+
}
|
|
558
|
+
const payload = {
|
|
559
|
+
status: update.status ?? "running",
|
|
560
|
+
result: update.result,
|
|
561
|
+
error: update.error,
|
|
562
|
+
duration_ms: update.durationMs,
|
|
563
|
+
progress: update.progress !== void 0 ? Math.round(update.progress) : void 0
|
|
564
|
+
};
|
|
565
|
+
await this.http.post(`/api/v1/executions/${executionId}/status`, payload, { headers: this.mergeHeaders() });
|
|
566
|
+
}
|
|
567
|
+
async discoverCapabilities(options = {}) {
|
|
568
|
+
const format = (options.format ?? "json").toLowerCase();
|
|
569
|
+
const params = { format };
|
|
570
|
+
const dedupe = (values) => Array.from(new Set((values ?? []).filter(Boolean))).map((v) => v);
|
|
571
|
+
const combinedAgents = dedupe([
|
|
572
|
+
...options.agent ? [options.agent] : [],
|
|
573
|
+
...options.nodeId ? [options.nodeId] : [],
|
|
574
|
+
...options.agentIds ?? [],
|
|
575
|
+
...options.nodeIds ?? []
|
|
576
|
+
]);
|
|
577
|
+
if (combinedAgents.length === 1) {
|
|
578
|
+
params.agent = combinedAgents[0];
|
|
579
|
+
} else if (combinedAgents.length > 1) {
|
|
580
|
+
params.agent_ids = combinedAgents.join(",");
|
|
581
|
+
}
|
|
582
|
+
if (options.reasoner) params.reasoner = options.reasoner;
|
|
583
|
+
if (options.skill) params.skill = options.skill;
|
|
584
|
+
if (options.tags?.length) params.tags = dedupe(options.tags).join(",");
|
|
585
|
+
if (options.includeInputSchema !== void 0) {
|
|
586
|
+
params.include_input_schema = String(Boolean(options.includeInputSchema));
|
|
587
|
+
}
|
|
588
|
+
if (options.includeOutputSchema !== void 0) {
|
|
589
|
+
params.include_output_schema = String(Boolean(options.includeOutputSchema));
|
|
590
|
+
}
|
|
591
|
+
if (options.includeDescriptions !== void 0) {
|
|
592
|
+
params.include_descriptions = String(Boolean(options.includeDescriptions));
|
|
593
|
+
}
|
|
594
|
+
if (options.includeExamples !== void 0) {
|
|
595
|
+
params.include_examples = String(Boolean(options.includeExamples));
|
|
596
|
+
}
|
|
597
|
+
if (options.healthStatus) params.health_status = options.healthStatus.toLowerCase();
|
|
598
|
+
if (options.limit !== void 0) params.limit = String(options.limit);
|
|
599
|
+
if (options.offset !== void 0) params.offset = String(options.offset);
|
|
600
|
+
const res = await this.http.get("/api/v1/discovery/capabilities", {
|
|
601
|
+
params,
|
|
602
|
+
headers: this.mergeHeaders({
|
|
603
|
+
...options.headers ?? {},
|
|
604
|
+
Accept: format === "xml" ? "application/xml" : "application/json"
|
|
605
|
+
}),
|
|
606
|
+
responseType: format === "xml" ? "text" : "json",
|
|
607
|
+
transformResponse: (data) => data
|
|
608
|
+
// preserve raw body for xml
|
|
609
|
+
});
|
|
610
|
+
const raw = typeof res.data === "string" ? res.data : JSON.stringify(res.data);
|
|
611
|
+
if (format === "xml") {
|
|
612
|
+
return { format: "xml", raw, xml: raw };
|
|
613
|
+
}
|
|
614
|
+
const parsed = typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
615
|
+
if (format === "compact") {
|
|
616
|
+
return {
|
|
617
|
+
format: "compact",
|
|
618
|
+
raw,
|
|
619
|
+
compact: this.mapCompactDiscovery(parsed)
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
format: "json",
|
|
624
|
+
raw,
|
|
625
|
+
json: this.mapDiscoveryResponse(parsed)
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
mapDiscoveryResponse(payload) {
|
|
629
|
+
return {
|
|
630
|
+
discoveredAt: String(payload?.discovered_at ?? ""),
|
|
631
|
+
totalAgents: Number(payload?.total_agents ?? 0),
|
|
632
|
+
totalReasoners: Number(payload?.total_reasoners ?? 0),
|
|
633
|
+
totalSkills: Number(payload?.total_skills ?? 0),
|
|
634
|
+
pagination: {
|
|
635
|
+
limit: Number(payload?.pagination?.limit ?? 0),
|
|
636
|
+
offset: Number(payload?.pagination?.offset ?? 0),
|
|
637
|
+
hasMore: Boolean(payload?.pagination?.has_more)
|
|
638
|
+
},
|
|
639
|
+
capabilities: (payload?.capabilities ?? []).map((cap) => ({
|
|
640
|
+
agentId: cap?.agent_id ?? "",
|
|
641
|
+
baseUrl: cap?.base_url ?? "",
|
|
642
|
+
version: cap?.version ?? "",
|
|
643
|
+
healthStatus: cap?.health_status ?? "",
|
|
644
|
+
deploymentType: cap?.deployment_type,
|
|
645
|
+
lastHeartbeat: cap?.last_heartbeat,
|
|
646
|
+
reasoners: (cap?.reasoners ?? []).map((r) => ({
|
|
647
|
+
id: r?.id ?? "",
|
|
648
|
+
description: r?.description,
|
|
649
|
+
tags: r?.tags ?? [],
|
|
650
|
+
inputSchema: r?.input_schema,
|
|
651
|
+
outputSchema: r?.output_schema,
|
|
652
|
+
examples: r?.examples,
|
|
653
|
+
invocationTarget: r?.invocation_target ?? ""
|
|
654
|
+
})),
|
|
655
|
+
skills: (cap?.skills ?? []).map((s) => ({
|
|
656
|
+
id: s?.id ?? "",
|
|
657
|
+
description: s?.description,
|
|
658
|
+
tags: s?.tags ?? [],
|
|
659
|
+
inputSchema: s?.input_schema,
|
|
660
|
+
invocationTarget: s?.invocation_target ?? ""
|
|
661
|
+
}))
|
|
662
|
+
}))
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
mapCompactDiscovery(payload) {
|
|
666
|
+
const toCap = (cap) => ({
|
|
667
|
+
id: cap?.id ?? "",
|
|
668
|
+
agentId: cap?.agent_id ?? "",
|
|
669
|
+
target: cap?.target ?? "",
|
|
670
|
+
tags: cap?.tags ?? []
|
|
671
|
+
});
|
|
672
|
+
return {
|
|
673
|
+
discoveredAt: String(payload?.discovered_at ?? ""),
|
|
674
|
+
reasoners: (payload?.reasoners ?? []).map(toCap),
|
|
675
|
+
skills: (payload?.skills ?? []).map(toCap)
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
sanitizeHeaders(headers) {
|
|
679
|
+
const sanitized = {};
|
|
680
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
681
|
+
if (value === void 0 || value === null) return;
|
|
682
|
+
sanitized[key] = typeof value === "string" ? value : String(value);
|
|
683
|
+
});
|
|
684
|
+
return sanitized;
|
|
685
|
+
}
|
|
686
|
+
mergeHeaders(headers) {
|
|
687
|
+
return {
|
|
688
|
+
...this.defaultHeaders,
|
|
689
|
+
...this.sanitizeHeaders(headers ?? {})
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
var MemoryClient = class {
|
|
694
|
+
http;
|
|
695
|
+
defaultHeaders;
|
|
696
|
+
constructor(baseUrl, defaultHeaders) {
|
|
697
|
+
this.http = axios.create({
|
|
698
|
+
baseURL: baseUrl.replace(/\/$/, "")
|
|
699
|
+
});
|
|
700
|
+
this.defaultHeaders = this.sanitizeHeaders(defaultHeaders ?? {});
|
|
701
|
+
}
|
|
702
|
+
async set(key, data, options = {}) {
|
|
703
|
+
const payload = { key, data };
|
|
704
|
+
if (options.scope) payload.scope = options.scope;
|
|
705
|
+
await this.http.post("/api/v1/memory/set", payload, {
|
|
706
|
+
headers: this.buildHeaders(options)
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
async get(key, options = {}) {
|
|
710
|
+
try {
|
|
711
|
+
const payload = { key };
|
|
712
|
+
if (options.scope) payload.scope = options.scope;
|
|
713
|
+
const res = await this.http.post("/api/v1/memory/get", payload, {
|
|
714
|
+
headers: this.buildHeaders(options)
|
|
715
|
+
});
|
|
716
|
+
return res.data?.data;
|
|
717
|
+
} catch (err) {
|
|
718
|
+
if (isAxiosError(err) && err.response?.status === 404) {
|
|
719
|
+
return void 0;
|
|
720
|
+
}
|
|
721
|
+
throw err;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
async delete(key, options = {}) {
|
|
725
|
+
const payload = { key };
|
|
726
|
+
if (options.scope) payload.scope = options.scope;
|
|
727
|
+
await this.http.post("/api/v1/memory/delete", payload, {
|
|
728
|
+
headers: this.buildHeaders(options)
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
async listKeys(scope, options = {}) {
|
|
732
|
+
const res = await this.http.get("/api/v1/memory/list", {
|
|
733
|
+
params: { scope },
|
|
734
|
+
headers: this.buildHeaders({ ...options, scope })
|
|
735
|
+
});
|
|
736
|
+
return (res.data ?? []).map((item) => item?.key).filter(Boolean);
|
|
737
|
+
}
|
|
738
|
+
async exists(key, options = {}) {
|
|
739
|
+
const value = await this.get(key, options);
|
|
740
|
+
return value !== void 0;
|
|
741
|
+
}
|
|
742
|
+
async setVector(key, embedding, metadata, options = {}) {
|
|
743
|
+
const payload = {
|
|
744
|
+
key,
|
|
745
|
+
embedding
|
|
746
|
+
};
|
|
747
|
+
if (metadata !== void 0) payload.metadata = metadata;
|
|
748
|
+
if (options.scope) payload.scope = options.scope;
|
|
749
|
+
await this.http.post("/api/v1/memory/vector/set", payload, {
|
|
750
|
+
headers: this.buildHeaders(options)
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
async deleteVector(key, options = {}) {
|
|
754
|
+
const payload = { key };
|
|
755
|
+
if (options.scope) payload.scope = options.scope;
|
|
756
|
+
await this.http.post("/api/v1/memory/vector/delete", payload, {
|
|
757
|
+
headers: this.buildHeaders(options)
|
|
758
|
+
});
|
|
759
|
+
}
|
|
760
|
+
async searchVector(queryEmbedding, options = {}) {
|
|
761
|
+
const payload = {
|
|
762
|
+
query_embedding: queryEmbedding,
|
|
763
|
+
top_k: options.topK ?? 10
|
|
764
|
+
};
|
|
765
|
+
if (options.filters) payload.filters = options.filters;
|
|
766
|
+
if (options.scope) payload.scope = options.scope;
|
|
767
|
+
const res = await this.http.post("/api/v1/memory/vector/search", payload, {
|
|
768
|
+
headers: this.buildHeaders(options)
|
|
769
|
+
});
|
|
770
|
+
return res.data ?? [];
|
|
771
|
+
}
|
|
772
|
+
buildHeaders(options = {}) {
|
|
773
|
+
const { scope, scopeId, metadata } = options;
|
|
774
|
+
const headers = { ...this.defaultHeaders };
|
|
775
|
+
const workflowId = metadata?.workflowId ?? metadata?.runId;
|
|
776
|
+
if (workflowId) headers["X-Workflow-ID"] = workflowId;
|
|
777
|
+
if (metadata?.sessionId) headers["X-Session-ID"] = metadata.sessionId;
|
|
778
|
+
if (metadata?.actorId) headers["X-Actor-ID"] = metadata.actorId;
|
|
779
|
+
if (metadata?.runId) headers["X-Run-ID"] = metadata.runId;
|
|
780
|
+
if (metadata?.executionId) headers["X-Execution-ID"] = metadata.executionId;
|
|
781
|
+
if (metadata?.parentExecutionId) headers["X-Parent-Execution-ID"] = metadata.parentExecutionId;
|
|
782
|
+
if (metadata?.callerDid) headers["X-Caller-DID"] = metadata.callerDid;
|
|
783
|
+
if (metadata?.targetDid) headers["X-Target-DID"] = metadata.targetDid;
|
|
784
|
+
if (metadata?.agentNodeDid) headers["X-Agent-Node-DID"] = metadata.agentNodeDid;
|
|
785
|
+
if (metadata?.agentNodeId) headers["X-Agent-Node-ID"] = metadata.agentNodeId;
|
|
786
|
+
const headerName = this.scopeToHeader(scope);
|
|
787
|
+
const resolvedScopeId = this.resolveScopeId(scope, scopeId, metadata);
|
|
788
|
+
if (headerName && resolvedScopeId) {
|
|
789
|
+
headers[headerName] = resolvedScopeId;
|
|
790
|
+
}
|
|
791
|
+
return { ...headers, ...this.sanitizeHeaders(options.headers ?? {}) };
|
|
792
|
+
}
|
|
793
|
+
scopeToHeader(scope) {
|
|
794
|
+
switch (scope) {
|
|
795
|
+
case "workflow":
|
|
796
|
+
return "X-Workflow-ID";
|
|
797
|
+
case "session":
|
|
798
|
+
return "X-Session-ID";
|
|
799
|
+
case "actor":
|
|
800
|
+
return "X-Actor-ID";
|
|
801
|
+
default:
|
|
802
|
+
return void 0;
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
resolveScopeId(scope, scopeId, metadata) {
|
|
806
|
+
if (scopeId) return scopeId;
|
|
807
|
+
switch (scope) {
|
|
808
|
+
case "workflow":
|
|
809
|
+
return metadata?.workflowId ?? metadata?.runId;
|
|
810
|
+
case "session":
|
|
811
|
+
return metadata?.sessionId;
|
|
812
|
+
case "actor":
|
|
813
|
+
return metadata?.actorId;
|
|
814
|
+
case "global":
|
|
815
|
+
return "global";
|
|
816
|
+
default:
|
|
817
|
+
return void 0;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
sanitizeHeaders(headers) {
|
|
821
|
+
const sanitized = {};
|
|
822
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
823
|
+
if (value === void 0 || value === null) return;
|
|
824
|
+
sanitized[key] = typeof value === "string" ? value : String(value);
|
|
825
|
+
});
|
|
826
|
+
return sanitized;
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
var MemoryEventClient = class {
|
|
830
|
+
url;
|
|
831
|
+
ws;
|
|
832
|
+
handlers = [];
|
|
833
|
+
reconnectDelay = 1e3;
|
|
834
|
+
closed = false;
|
|
835
|
+
headers;
|
|
836
|
+
constructor(baseUrl, headers) {
|
|
837
|
+
this.url = `${baseUrl.replace(/^http/, "ws")}/api/v1/memory/events/ws`;
|
|
838
|
+
this.headers = this.buildForwardHeaders(headers ?? {});
|
|
839
|
+
}
|
|
840
|
+
start() {
|
|
841
|
+
if (this.ws) return;
|
|
842
|
+
this.connect();
|
|
843
|
+
}
|
|
844
|
+
onEvent(handler) {
|
|
845
|
+
this.handlers.push(handler);
|
|
846
|
+
}
|
|
847
|
+
stop() {
|
|
848
|
+
this.closed = true;
|
|
849
|
+
this.ws?.close();
|
|
850
|
+
}
|
|
851
|
+
connect() {
|
|
852
|
+
this.ws = new WebSocket(this.url, { headers: this.headers });
|
|
853
|
+
this.ws.on("open", () => {
|
|
854
|
+
this.reconnectDelay = 1e3;
|
|
855
|
+
});
|
|
856
|
+
this.ws.on("message", async (raw) => {
|
|
857
|
+
try {
|
|
858
|
+
const parsed = JSON.parse(raw.toString());
|
|
859
|
+
for (const handler of this.handlers) {
|
|
860
|
+
await handler(parsed);
|
|
861
|
+
}
|
|
862
|
+
} catch (err) {
|
|
863
|
+
console.error("Failed to handle memory event", err);
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
this.ws.on("close", () => this.scheduleReconnect());
|
|
867
|
+
this.ws.on("error", () => this.scheduleReconnect());
|
|
868
|
+
}
|
|
869
|
+
scheduleReconnect() {
|
|
870
|
+
if (this.closed) return;
|
|
871
|
+
setTimeout(() => {
|
|
872
|
+
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 3e4);
|
|
873
|
+
this.connect();
|
|
874
|
+
}, this.reconnectDelay);
|
|
875
|
+
}
|
|
876
|
+
buildForwardHeaders(headers) {
|
|
877
|
+
const allowed = /* @__PURE__ */ new Set(["authorization", "cookie"]);
|
|
878
|
+
const sanitized = {};
|
|
879
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
880
|
+
if (value === void 0 || value === null) return;
|
|
881
|
+
const lower = key.toLowerCase();
|
|
882
|
+
if (lower.startsWith("x-") || allowed.has(lower)) {
|
|
883
|
+
sanitized[key] = typeof value === "string" ? value : String(value);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
return sanitized;
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
|
|
890
|
+
// src/memory/MemoryInterface.ts
|
|
891
|
+
var MemoryInterface = class _MemoryInterface {
|
|
892
|
+
client;
|
|
893
|
+
eventClient;
|
|
894
|
+
aiClient;
|
|
895
|
+
defaultScope;
|
|
896
|
+
defaultScopeId;
|
|
897
|
+
metadata;
|
|
898
|
+
constructor(params) {
|
|
899
|
+
this.client = params.client;
|
|
900
|
+
this.eventClient = params.eventClient;
|
|
901
|
+
this.aiClient = params.aiClient;
|
|
902
|
+
this.defaultScope = params.defaultScope ?? "workflow";
|
|
903
|
+
this.defaultScopeId = params.defaultScopeId;
|
|
904
|
+
this.metadata = params.metadata;
|
|
905
|
+
}
|
|
906
|
+
async set(key, data, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
907
|
+
await this.client.set(key, data, {
|
|
908
|
+
scope,
|
|
909
|
+
scopeId,
|
|
910
|
+
metadata: this.metadata
|
|
911
|
+
});
|
|
912
|
+
}
|
|
913
|
+
get(key, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
914
|
+
if (scope === this.defaultScope && scopeId === this.defaultScopeId) {
|
|
915
|
+
return this.getWithFallback(key);
|
|
916
|
+
}
|
|
917
|
+
return this.client.get(key, {
|
|
918
|
+
scope,
|
|
919
|
+
scopeId,
|
|
920
|
+
metadata: this.metadata
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
async getWithFallback(key) {
|
|
924
|
+
for (const candidate of this.getScopeOrder()) {
|
|
925
|
+
const value = await this.client.get(key, {
|
|
926
|
+
scope: candidate.scope,
|
|
927
|
+
scopeId: candidate.scopeId,
|
|
928
|
+
metadata: this.metadata
|
|
929
|
+
});
|
|
930
|
+
if (value !== void 0) return value;
|
|
931
|
+
}
|
|
932
|
+
return void 0;
|
|
933
|
+
}
|
|
934
|
+
async setVector(key, embedding, metadata, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
935
|
+
await this.client.setVector(key, embedding, metadata, {
|
|
936
|
+
scope,
|
|
937
|
+
scopeId,
|
|
938
|
+
metadata: this.metadata
|
|
939
|
+
});
|
|
940
|
+
}
|
|
941
|
+
async deleteVector(key, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
942
|
+
await this.client.deleteVector(key, {
|
|
943
|
+
scope,
|
|
944
|
+
scopeId,
|
|
945
|
+
metadata: this.metadata
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
searchVector(queryEmbedding, options = {}) {
|
|
949
|
+
return this.client.searchVector(queryEmbedding, {
|
|
950
|
+
...options,
|
|
951
|
+
metadata: this.metadata
|
|
952
|
+
});
|
|
953
|
+
}
|
|
954
|
+
delete(key, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
955
|
+
return this.client.delete(key, {
|
|
956
|
+
scope,
|
|
957
|
+
scopeId,
|
|
958
|
+
metadata: this.metadata
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
exists(key, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
962
|
+
return this.client.exists(key, {
|
|
963
|
+
scope,
|
|
964
|
+
scopeId,
|
|
965
|
+
metadata: this.metadata
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
listKeys(scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
969
|
+
return this.client.listKeys(scope, {
|
|
970
|
+
scope,
|
|
971
|
+
scopeId,
|
|
972
|
+
metadata: this.metadata
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
async embedText(text, options) {
|
|
976
|
+
if (!this.aiClient) {
|
|
977
|
+
throw new Error("AI client not configured for embeddings");
|
|
978
|
+
}
|
|
979
|
+
return this.aiClient.embed(text, options);
|
|
980
|
+
}
|
|
981
|
+
async embedTexts(texts, options) {
|
|
982
|
+
if (!this.aiClient) {
|
|
983
|
+
throw new Error("AI client not configured for embeddings");
|
|
984
|
+
}
|
|
985
|
+
return this.aiClient.embedMany(texts, options);
|
|
986
|
+
}
|
|
987
|
+
async embedAndSet(key, text, metadata, scope = this.defaultScope, scopeId = this.defaultScopeId, embeddingOptions) {
|
|
988
|
+
const embedding = await this.embedText(text, embeddingOptions);
|
|
989
|
+
await this.setVector(key, embedding, metadata, scope, scopeId);
|
|
990
|
+
return embedding;
|
|
991
|
+
}
|
|
992
|
+
async deleteVectors(keys, scope = this.defaultScope, scopeId = this.defaultScopeId) {
|
|
993
|
+
for (const key of keys) {
|
|
994
|
+
await this.deleteVector(key, scope, scopeId);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
workflow(scopeId) {
|
|
998
|
+
return this.cloneWithScope("workflow", scopeId);
|
|
999
|
+
}
|
|
1000
|
+
session(scopeId) {
|
|
1001
|
+
return this.cloneWithScope("session", scopeId);
|
|
1002
|
+
}
|
|
1003
|
+
actor(scopeId) {
|
|
1004
|
+
return this.cloneWithScope("actor", scopeId);
|
|
1005
|
+
}
|
|
1006
|
+
get globalScope() {
|
|
1007
|
+
return this.cloneWithScope("global", "global");
|
|
1008
|
+
}
|
|
1009
|
+
onEvent(handler) {
|
|
1010
|
+
this.eventClient?.onEvent(handler);
|
|
1011
|
+
}
|
|
1012
|
+
cloneWithScope(scope, scopeId) {
|
|
1013
|
+
return new _MemoryInterface({
|
|
1014
|
+
client: this.client,
|
|
1015
|
+
eventClient: this.eventClient,
|
|
1016
|
+
aiClient: this.aiClient,
|
|
1017
|
+
defaultScope: scope,
|
|
1018
|
+
defaultScopeId: scopeId ?? this.resolveScopeId(scope, this.metadata),
|
|
1019
|
+
metadata: this.metadata
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
getScopeOrder() {
|
|
1023
|
+
const metadata = this.metadata ?? {};
|
|
1024
|
+
const order = [];
|
|
1025
|
+
const pushUnique = (scope, scopeId) => {
|
|
1026
|
+
const key = `${scope}:${scopeId ?? ""}`;
|
|
1027
|
+
if (!order.some((c) => `${c.scope}:${c.scopeId ?? ""}` === key)) {
|
|
1028
|
+
order.push({ scope, scopeId });
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
pushUnique(this.defaultScope, this.defaultScopeId ?? this.resolveScopeId(this.defaultScope, metadata));
|
|
1032
|
+
const defaultSequence = ["workflow", "session", "actor", "global"];
|
|
1033
|
+
defaultSequence.forEach((scope) => {
|
|
1034
|
+
pushUnique(scope, this.resolveScopeId(scope, metadata));
|
|
1035
|
+
});
|
|
1036
|
+
return order;
|
|
1037
|
+
}
|
|
1038
|
+
resolveScopeId(scope, metadata) {
|
|
1039
|
+
switch (scope) {
|
|
1040
|
+
case "workflow":
|
|
1041
|
+
return metadata?.workflowId ?? metadata?.runId;
|
|
1042
|
+
case "session":
|
|
1043
|
+
return metadata?.sessionId;
|
|
1044
|
+
case "actor":
|
|
1045
|
+
return metadata?.actorId;
|
|
1046
|
+
case "global":
|
|
1047
|
+
return "global";
|
|
1048
|
+
default:
|
|
1049
|
+
return void 0;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
var DidClient = class {
|
|
1054
|
+
http;
|
|
1055
|
+
defaultHeaders;
|
|
1056
|
+
constructor(baseUrl, defaultHeaders) {
|
|
1057
|
+
this.http = axios.create({ baseURL: baseUrl.replace(/\/$/, "") });
|
|
1058
|
+
this.defaultHeaders = this.sanitizeHeaders(defaultHeaders ?? {});
|
|
1059
|
+
}
|
|
1060
|
+
async generateCredential(params) {
|
|
1061
|
+
const ctx = params.executionContext;
|
|
1062
|
+
const timestamp = ctx.timestamp instanceof Date ? ctx.timestamp.toISOString() : ctx.timestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
1063
|
+
const payload = {
|
|
1064
|
+
execution_context: {
|
|
1065
|
+
execution_id: ctx.executionId,
|
|
1066
|
+
workflow_id: ctx.workflowId,
|
|
1067
|
+
session_id: ctx.sessionId,
|
|
1068
|
+
caller_did: ctx.callerDid,
|
|
1069
|
+
target_did: ctx.targetDid,
|
|
1070
|
+
agent_node_did: ctx.agentNodeDid,
|
|
1071
|
+
timestamp
|
|
1072
|
+
},
|
|
1073
|
+
input_data: this.serializeDataForJson(params.inputData),
|
|
1074
|
+
output_data: this.serializeDataForJson(params.outputData),
|
|
1075
|
+
status: params.status ?? "succeeded",
|
|
1076
|
+
error_message: params.errorMessage,
|
|
1077
|
+
duration_ms: params.durationMs ?? 0
|
|
1078
|
+
};
|
|
1079
|
+
const res = await this.http.post("/api/v1/execution/vc", payload, {
|
|
1080
|
+
headers: this.mergeHeaders(params.headers)
|
|
1081
|
+
});
|
|
1082
|
+
return this.mapExecutionCredential(res.data);
|
|
1083
|
+
}
|
|
1084
|
+
async exportAuditTrail(filters = {}) {
|
|
1085
|
+
const res = await this.http.get("/api/v1/did/export/vcs", {
|
|
1086
|
+
params: this.cleanFilters(filters),
|
|
1087
|
+
headers: this.mergeHeaders()
|
|
1088
|
+
});
|
|
1089
|
+
const data = res.data ?? {};
|
|
1090
|
+
return {
|
|
1091
|
+
agentDids: data.agent_dids ?? [],
|
|
1092
|
+
executionVcs: (data.execution_vcs ?? []).map((vc) => ({
|
|
1093
|
+
vcId: vc.vc_id,
|
|
1094
|
+
executionId: vc.execution_id,
|
|
1095
|
+
workflowId: vc.workflow_id,
|
|
1096
|
+
sessionId: vc.session_id,
|
|
1097
|
+
issuerDid: vc.issuer_did,
|
|
1098
|
+
targetDid: vc.target_did,
|
|
1099
|
+
callerDid: vc.caller_did,
|
|
1100
|
+
status: vc.status,
|
|
1101
|
+
createdAt: vc.created_at
|
|
1102
|
+
})),
|
|
1103
|
+
workflowVcs: (data.workflow_vcs ?? []).map((vc) => ({
|
|
1104
|
+
workflowId: vc.workflow_id,
|
|
1105
|
+
sessionId: vc.session_id,
|
|
1106
|
+
componentVcs: vc.component_vcs ?? [],
|
|
1107
|
+
workflowVcId: vc.workflow_vc_id ?? vc.workflowVcId ?? vc.workflow_id,
|
|
1108
|
+
status: vc.status,
|
|
1109
|
+
startTime: vc.start_time,
|
|
1110
|
+
endTime: vc.end_time,
|
|
1111
|
+
totalSteps: vc.total_steps ?? 0,
|
|
1112
|
+
completedSteps: vc.completed_steps ?? 0
|
|
1113
|
+
})),
|
|
1114
|
+
totalCount: data.total_count ?? 0,
|
|
1115
|
+
filtersApplied: data.filters_applied
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
serializeDataForJson(data) {
|
|
1119
|
+
if (data === void 0 || data === null) return "";
|
|
1120
|
+
let value;
|
|
1121
|
+
if (typeof data === "string") {
|
|
1122
|
+
value = data;
|
|
1123
|
+
} else if (data instanceof Uint8Array) {
|
|
1124
|
+
value = Buffer.from(data).toString("utf-8");
|
|
1125
|
+
} else if (typeof data === "object") {
|
|
1126
|
+
try {
|
|
1127
|
+
value = JSON.stringify(data, Object.keys(data).sort());
|
|
1128
|
+
} catch {
|
|
1129
|
+
value = String(data);
|
|
1130
|
+
}
|
|
1131
|
+
} else {
|
|
1132
|
+
value = String(data);
|
|
1133
|
+
}
|
|
1134
|
+
return Buffer.from(value, "utf-8").toString("base64");
|
|
1135
|
+
}
|
|
1136
|
+
mapExecutionCredential(data) {
|
|
1137
|
+
return {
|
|
1138
|
+
vcId: data?.vc_id ?? "",
|
|
1139
|
+
executionId: data?.execution_id ?? "",
|
|
1140
|
+
workflowId: data?.workflow_id ?? "",
|
|
1141
|
+
sessionId: data?.session_id,
|
|
1142
|
+
issuerDid: data?.issuer_did,
|
|
1143
|
+
targetDid: data?.target_did,
|
|
1144
|
+
callerDid: data?.caller_did,
|
|
1145
|
+
vcDocument: data?.vc_document,
|
|
1146
|
+
signature: data?.signature,
|
|
1147
|
+
inputHash: data?.input_hash,
|
|
1148
|
+
outputHash: data?.output_hash,
|
|
1149
|
+
status: data?.status ?? "",
|
|
1150
|
+
createdAt: data?.created_at ?? ""
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
cleanFilters(filters) {
|
|
1154
|
+
const cleaned = {};
|
|
1155
|
+
if (filters.workflowId) cleaned.workflow_id = filters.workflowId;
|
|
1156
|
+
if (filters.sessionId) cleaned.session_id = filters.sessionId;
|
|
1157
|
+
if (filters.issuerDid) cleaned.issuer_did = filters.issuerDid;
|
|
1158
|
+
if (filters.status) cleaned.status = filters.status;
|
|
1159
|
+
if (filters.limit !== void 0) cleaned.limit = filters.limit;
|
|
1160
|
+
return cleaned;
|
|
1161
|
+
}
|
|
1162
|
+
mergeHeaders(headers) {
|
|
1163
|
+
return {
|
|
1164
|
+
...this.defaultHeaders,
|
|
1165
|
+
...this.sanitizeHeaders(headers ?? {})
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
sanitizeHeaders(headers) {
|
|
1169
|
+
const sanitized = {};
|
|
1170
|
+
Object.entries(headers).forEach(([key, value]) => {
|
|
1171
|
+
if (value === void 0 || value === null) return;
|
|
1172
|
+
sanitized[key] = typeof value === "string" ? value : String(value);
|
|
1173
|
+
});
|
|
1174
|
+
return sanitized;
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// src/did/DidInterface.ts
|
|
1179
|
+
var DidInterface = class {
|
|
1180
|
+
client;
|
|
1181
|
+
metadata;
|
|
1182
|
+
enabled;
|
|
1183
|
+
defaultInput;
|
|
1184
|
+
constructor(params) {
|
|
1185
|
+
this.client = params.client;
|
|
1186
|
+
this.metadata = params.metadata;
|
|
1187
|
+
this.enabled = params.enabled;
|
|
1188
|
+
this.defaultInput = params.defaultInput;
|
|
1189
|
+
}
|
|
1190
|
+
async generateCredential(options = {}) {
|
|
1191
|
+
if (!this.enabled) {
|
|
1192
|
+
throw new Error("DID/VC features are disabled. Enable didEnabled in AgentConfig to use ctx.did.");
|
|
1193
|
+
}
|
|
1194
|
+
const executionContext = {
|
|
1195
|
+
executionId: options.executionId ?? this.metadata.executionId,
|
|
1196
|
+
workflowId: options.workflowId ?? this.metadata.workflowId ?? this.metadata.runId,
|
|
1197
|
+
sessionId: options.sessionId ?? this.metadata.sessionId,
|
|
1198
|
+
callerDid: options.callerDid ?? this.metadata.callerDid,
|
|
1199
|
+
targetDid: options.targetDid ?? this.metadata.targetDid,
|
|
1200
|
+
agentNodeDid: options.agentNodeDid ?? this.metadata.agentNodeDid,
|
|
1201
|
+
timestamp: options.timestamp
|
|
1202
|
+
};
|
|
1203
|
+
return this.client.generateCredential({
|
|
1204
|
+
executionContext,
|
|
1205
|
+
inputData: options.inputData ?? this.defaultInput,
|
|
1206
|
+
outputData: options.outputData,
|
|
1207
|
+
status: options.status,
|
|
1208
|
+
errorMessage: options.errorMessage,
|
|
1209
|
+
durationMs: options.durationMs,
|
|
1210
|
+
headers: options.headers
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
exportAuditTrail(filters) {
|
|
1214
|
+
if (!this.enabled) {
|
|
1215
|
+
throw new Error("DID/VC features are disabled. Enable didEnabled in AgentConfig to use ctx.did.");
|
|
1216
|
+
}
|
|
1217
|
+
return this.client.exportAuditTrail(filters);
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1221
|
+
// src/utils/pattern.ts
|
|
1222
|
+
function matchesPattern(pattern, value) {
|
|
1223
|
+
const escaped = pattern.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&").replace(/\*/g, ".*");
|
|
1224
|
+
const regex = new RegExp(`^${escaped}$`);
|
|
1225
|
+
return regex.test(value);
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// src/workflow/WorkflowReporter.ts
|
|
1229
|
+
var WorkflowReporter = class {
|
|
1230
|
+
client;
|
|
1231
|
+
metadata;
|
|
1232
|
+
constructor(client, metadata) {
|
|
1233
|
+
if (!metadata.executionId) {
|
|
1234
|
+
throw new Error("WorkflowReporter requires an executionId");
|
|
1235
|
+
}
|
|
1236
|
+
this.client = client;
|
|
1237
|
+
this.metadata = metadata;
|
|
1238
|
+
}
|
|
1239
|
+
async progress(progress, options) {
|
|
1240
|
+
const normalized = Math.min(100, Math.max(0, Math.round(progress)));
|
|
1241
|
+
return this.client.updateExecutionStatus(this.metadata.executionId, {
|
|
1242
|
+
status: options?.status ?? "running",
|
|
1243
|
+
progress: normalized,
|
|
1244
|
+
result: options?.result,
|
|
1245
|
+
error: options?.error,
|
|
1246
|
+
durationMs: options?.durationMs
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
};
|
|
1250
|
+
var MCPClient = class {
|
|
1251
|
+
alias;
|
|
1252
|
+
baseUrl;
|
|
1253
|
+
transport;
|
|
1254
|
+
http;
|
|
1255
|
+
devMode;
|
|
1256
|
+
lastHealthy = false;
|
|
1257
|
+
constructor(config, devMode) {
|
|
1258
|
+
if (!config.alias) {
|
|
1259
|
+
throw new Error("MCP server alias is required");
|
|
1260
|
+
}
|
|
1261
|
+
if (!config.url && !config.port) {
|
|
1262
|
+
throw new Error(`MCP server "${config.alias}" requires a url or port`);
|
|
1263
|
+
}
|
|
1264
|
+
this.alias = config.alias;
|
|
1265
|
+
this.transport = config.transport ?? "http";
|
|
1266
|
+
this.baseUrl = (config.url ?? `http://localhost:${config.port}`).replace(/\/$/, "");
|
|
1267
|
+
this.http = axios.create({
|
|
1268
|
+
baseURL: this.baseUrl,
|
|
1269
|
+
headers: config.headers
|
|
1270
|
+
});
|
|
1271
|
+
this.devMode = Boolean(devMode);
|
|
1272
|
+
}
|
|
1273
|
+
async healthCheck() {
|
|
1274
|
+
try {
|
|
1275
|
+
await this.http.get("/health");
|
|
1276
|
+
this.lastHealthy = true;
|
|
1277
|
+
return true;
|
|
1278
|
+
} catch (err) {
|
|
1279
|
+
this.lastHealthy = false;
|
|
1280
|
+
if (this.devMode) {
|
|
1281
|
+
console.warn(`MCP health check failed for ${this.alias}:`, err instanceof Error ? err.message : err);
|
|
1282
|
+
}
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
async listTools() {
|
|
1287
|
+
try {
|
|
1288
|
+
if (this.transport === "bridge") {
|
|
1289
|
+
const res2 = await this.http.post("/mcp/tools/list");
|
|
1290
|
+
const tools2 = res2.data?.tools ?? [];
|
|
1291
|
+
return this.normalizeTools(tools2);
|
|
1292
|
+
}
|
|
1293
|
+
const res = await this.http.post("/mcp/v1", {
|
|
1294
|
+
jsonrpc: "2.0",
|
|
1295
|
+
id: Date.now(),
|
|
1296
|
+
method: "tools/list",
|
|
1297
|
+
params: {}
|
|
1298
|
+
});
|
|
1299
|
+
const tools = res.data?.result?.tools ?? [];
|
|
1300
|
+
return this.normalizeTools(tools);
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
if (this.devMode) {
|
|
1303
|
+
console.warn(`MCP listTools failed for ${this.alias}:`, err instanceof Error ? err.message : err);
|
|
1304
|
+
}
|
|
1305
|
+
return [];
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
async callTool(toolName, arguments_ = {}) {
|
|
1309
|
+
if (!toolName) {
|
|
1310
|
+
throw new Error("toolName is required");
|
|
1311
|
+
}
|
|
1312
|
+
try {
|
|
1313
|
+
if (this.transport === "bridge") {
|
|
1314
|
+
const res2 = await this.http.post("/mcp/tools/call", {
|
|
1315
|
+
tool_name: toolName,
|
|
1316
|
+
arguments: arguments_
|
|
1317
|
+
});
|
|
1318
|
+
return res2.data?.result ?? res2.data;
|
|
1319
|
+
}
|
|
1320
|
+
const res = await this.http.post("/mcp/v1", {
|
|
1321
|
+
jsonrpc: "2.0",
|
|
1322
|
+
id: Date.now(),
|
|
1323
|
+
method: "tools/call",
|
|
1324
|
+
params: { name: toolName, arguments: arguments_ }
|
|
1325
|
+
});
|
|
1326
|
+
if (res.data?.error) {
|
|
1327
|
+
throw new Error(String(res.data.error?.message ?? res.data.error));
|
|
1328
|
+
}
|
|
1329
|
+
if (res.data?.result !== void 0) {
|
|
1330
|
+
return res.data.result;
|
|
1331
|
+
}
|
|
1332
|
+
return res.data;
|
|
1333
|
+
} catch (err) {
|
|
1334
|
+
if (this.devMode) {
|
|
1335
|
+
console.warn(`MCP callTool failed for ${this.alias}.${toolName}:`, err instanceof Error ? err.message : err);
|
|
1336
|
+
}
|
|
1337
|
+
throw err;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
get lastHealthStatus() {
|
|
1341
|
+
return this.lastHealthy;
|
|
1342
|
+
}
|
|
1343
|
+
normalizeTools(tools) {
|
|
1344
|
+
return (tools ?? []).map((tool) => ({
|
|
1345
|
+
name: tool?.name ?? "unknown",
|
|
1346
|
+
description: tool?.description,
|
|
1347
|
+
inputSchema: tool?.inputSchema ?? tool?.input_schema,
|
|
1348
|
+
input_schema: tool?.input_schema
|
|
1349
|
+
}));
|
|
1350
|
+
}
|
|
1351
|
+
};
|
|
1352
|
+
|
|
1353
|
+
// src/mcp/MCPClientRegistry.ts
|
|
1354
|
+
var MCPClientRegistry = class {
|
|
1355
|
+
clients = /* @__PURE__ */ new Map();
|
|
1356
|
+
devMode;
|
|
1357
|
+
constructor(devMode) {
|
|
1358
|
+
this.devMode = Boolean(devMode);
|
|
1359
|
+
}
|
|
1360
|
+
register(config) {
|
|
1361
|
+
const client = new MCPClient(config, this.devMode);
|
|
1362
|
+
this.clients.set(config.alias, client);
|
|
1363
|
+
return client;
|
|
1364
|
+
}
|
|
1365
|
+
get(alias) {
|
|
1366
|
+
return this.clients.get(alias);
|
|
1367
|
+
}
|
|
1368
|
+
list() {
|
|
1369
|
+
return Array.from(this.clients.values());
|
|
1370
|
+
}
|
|
1371
|
+
async healthSummary() {
|
|
1372
|
+
if (!this.clients.size) {
|
|
1373
|
+
return {
|
|
1374
|
+
status: "disabled",
|
|
1375
|
+
totalServers: 0,
|
|
1376
|
+
healthyServers: 0,
|
|
1377
|
+
servers: []
|
|
1378
|
+
};
|
|
1379
|
+
}
|
|
1380
|
+
const results = await Promise.all(
|
|
1381
|
+
Array.from(this.clients.values()).map(async (client) => {
|
|
1382
|
+
const healthy = await client.healthCheck();
|
|
1383
|
+
return {
|
|
1384
|
+
alias: client.alias,
|
|
1385
|
+
baseUrl: client.baseUrl,
|
|
1386
|
+
transport: client.transport,
|
|
1387
|
+
healthy
|
|
1388
|
+
};
|
|
1389
|
+
})
|
|
1390
|
+
);
|
|
1391
|
+
const healthyCount = results.filter((r) => r.healthy).length;
|
|
1392
|
+
const status = healthyCount === 0 ? "degraded" : healthyCount === results.length ? "ok" : "degraded";
|
|
1393
|
+
return {
|
|
1394
|
+
status,
|
|
1395
|
+
totalServers: results.length,
|
|
1396
|
+
healthyServers: healthyCount,
|
|
1397
|
+
servers: results
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
// src/mcp/MCPToolRegistrar.ts
|
|
1403
|
+
var MCPToolRegistrar = class {
|
|
1404
|
+
constructor(agent, registry, options = {}) {
|
|
1405
|
+
this.agent = agent;
|
|
1406
|
+
this.registry = registry;
|
|
1407
|
+
this.options = options;
|
|
1408
|
+
this.devMode = Boolean(options.devMode);
|
|
1409
|
+
}
|
|
1410
|
+
registered = /* @__PURE__ */ new Set();
|
|
1411
|
+
devMode;
|
|
1412
|
+
registerServers(servers) {
|
|
1413
|
+
servers.forEach((server) => this.registry.register(server));
|
|
1414
|
+
}
|
|
1415
|
+
async registerAll() {
|
|
1416
|
+
const registrations = [];
|
|
1417
|
+
const clients = this.registry.list();
|
|
1418
|
+
for (const client of clients) {
|
|
1419
|
+
const healthy = await client.healthCheck();
|
|
1420
|
+
if (!healthy) {
|
|
1421
|
+
if (this.devMode) {
|
|
1422
|
+
console.warn(`Skipping MCP server ${client.alias} (health check failed)`);
|
|
1423
|
+
}
|
|
1424
|
+
continue;
|
|
1425
|
+
}
|
|
1426
|
+
const tools = await client.listTools();
|
|
1427
|
+
for (const tool of tools) {
|
|
1428
|
+
if (!tool?.name) continue;
|
|
1429
|
+
const skillName = this.buildSkillName(client.alias, tool.name);
|
|
1430
|
+
if (this.registered.has(skillName) || this.agent.skills.get(skillName)) {
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
this.agent.skill(
|
|
1434
|
+
skillName,
|
|
1435
|
+
async (ctx) => {
|
|
1436
|
+
const args = ctx.input && typeof ctx.input === "object" ? ctx.input : {};
|
|
1437
|
+
const result = await client.callTool(tool.name, args);
|
|
1438
|
+
return {
|
|
1439
|
+
status: "success",
|
|
1440
|
+
result,
|
|
1441
|
+
server: client.alias,
|
|
1442
|
+
tool: tool.name
|
|
1443
|
+
};
|
|
1444
|
+
},
|
|
1445
|
+
{
|
|
1446
|
+
description: tool.description ?? `MCP tool ${tool.name} from ${client.alias}`,
|
|
1447
|
+
inputSchema: tool.inputSchema ?? tool.input_schema ?? {},
|
|
1448
|
+
tags: this.buildTags(client.alias)
|
|
1449
|
+
}
|
|
1450
|
+
);
|
|
1451
|
+
this.registered.add(skillName);
|
|
1452
|
+
registrations.push({ server: client.alias, skillName, tool });
|
|
1453
|
+
if (this.devMode) {
|
|
1454
|
+
console.info(`Registered MCP skill ${skillName}`);
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
return { registered: registrations };
|
|
1459
|
+
}
|
|
1460
|
+
buildTags(alias) {
|
|
1461
|
+
return Array.from(/* @__PURE__ */ new Set(["mcp", alias, ...this.options.tags ?? []]));
|
|
1462
|
+
}
|
|
1463
|
+
buildSkillName(serverAlias, toolName) {
|
|
1464
|
+
const base = [this.options.namespace, serverAlias, toolName].filter(Boolean).join("_");
|
|
1465
|
+
return this.sanitize(base);
|
|
1466
|
+
}
|
|
1467
|
+
sanitize(value) {
|
|
1468
|
+
const collapsed = value.replace(/[^a-zA-Z0-9_]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
1469
|
+
if (/^[0-9]/.test(collapsed)) {
|
|
1470
|
+
return `mcp_${collapsed}`;
|
|
1471
|
+
}
|
|
1472
|
+
return collapsed || "mcp_tool";
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
|
|
1476
|
+
// src/agent/Agent.ts
|
|
1477
|
+
var TargetNotFoundError = class extends Error {
|
|
1478
|
+
};
|
|
1479
|
+
var Agent = class {
|
|
1480
|
+
config;
|
|
1481
|
+
app;
|
|
1482
|
+
reasoners = new ReasonerRegistry();
|
|
1483
|
+
skills = new SkillRegistry();
|
|
1484
|
+
server;
|
|
1485
|
+
heartbeatTimer;
|
|
1486
|
+
aiClient;
|
|
1487
|
+
agentFieldClient;
|
|
1488
|
+
memoryClient;
|
|
1489
|
+
memoryEventClient;
|
|
1490
|
+
didClient;
|
|
1491
|
+
memoryWatchers = [];
|
|
1492
|
+
mcpClientRegistry;
|
|
1493
|
+
mcpToolRegistrar;
|
|
1494
|
+
constructor(config) {
|
|
1495
|
+
const mcp = config.mcp ? {
|
|
1496
|
+
autoRegisterTools: config.mcp.autoRegisterTools ?? true,
|
|
1497
|
+
...config.mcp
|
|
1498
|
+
} : void 0;
|
|
1499
|
+
this.config = {
|
|
1500
|
+
port: 8001,
|
|
1501
|
+
agentFieldUrl: "http://localhost:8080",
|
|
1502
|
+
host: "0.0.0.0",
|
|
1503
|
+
...config,
|
|
1504
|
+
didEnabled: config.didEnabled ?? true,
|
|
1505
|
+
deploymentType: config.deploymentType ?? "long_running",
|
|
1506
|
+
mcp
|
|
1507
|
+
};
|
|
1508
|
+
this.app = express();
|
|
1509
|
+
this.app.use(express.json());
|
|
1510
|
+
this.aiClient = new AIClient(this.config.aiConfig);
|
|
1511
|
+
this.agentFieldClient = new AgentFieldClient(this.config);
|
|
1512
|
+
this.memoryClient = new MemoryClient(this.config.agentFieldUrl, this.config.defaultHeaders);
|
|
1513
|
+
this.memoryEventClient = new MemoryEventClient(this.config.agentFieldUrl, this.config.defaultHeaders);
|
|
1514
|
+
this.didClient = new DidClient(this.config.agentFieldUrl, this.config.defaultHeaders);
|
|
1515
|
+
this.memoryEventClient.onEvent((event) => this.dispatchMemoryEvent(event));
|
|
1516
|
+
if (this.config.mcp?.servers?.length) {
|
|
1517
|
+
this.mcpClientRegistry = new MCPClientRegistry(this.config.devMode);
|
|
1518
|
+
this.mcpToolRegistrar = new MCPToolRegistrar(this, this.mcpClientRegistry, {
|
|
1519
|
+
namespace: this.config.mcp.namespace,
|
|
1520
|
+
tags: this.config.mcp.tags,
|
|
1521
|
+
devMode: this.config.devMode
|
|
1522
|
+
});
|
|
1523
|
+
this.mcpToolRegistrar.registerServers(this.config.mcp.servers);
|
|
1524
|
+
}
|
|
1525
|
+
this.registerDefaultRoutes();
|
|
1526
|
+
}
|
|
1527
|
+
reasoner(name, handler, options) {
|
|
1528
|
+
this.reasoners.register(name, handler, options);
|
|
1529
|
+
return this;
|
|
1530
|
+
}
|
|
1531
|
+
skill(name, handler, options) {
|
|
1532
|
+
this.skills.register(name, handler, options);
|
|
1533
|
+
return this;
|
|
1534
|
+
}
|
|
1535
|
+
includeRouter(router) {
|
|
1536
|
+
this.reasoners.includeRouter(router);
|
|
1537
|
+
this.skills.includeRouter(router);
|
|
1538
|
+
}
|
|
1539
|
+
handler() {
|
|
1540
|
+
return async (event, res) => {
|
|
1541
|
+
if (res && typeof res === "object" && typeof res.setHeader === "function") {
|
|
1542
|
+
return this.handleHttpRequest(event, res);
|
|
1543
|
+
}
|
|
1544
|
+
return this.handleServerlessEvent(event);
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
watchMemory(pattern, handler, options) {
|
|
1548
|
+
const patterns = Array.isArray(pattern) ? pattern : [pattern];
|
|
1549
|
+
patterns.forEach(
|
|
1550
|
+
(p) => this.memoryWatchers.push({ pattern: p, handler, scope: options?.scope, scopeId: options?.scopeId })
|
|
1551
|
+
);
|
|
1552
|
+
this.memoryEventClient.start();
|
|
1553
|
+
}
|
|
1554
|
+
discover(options) {
|
|
1555
|
+
return this.agentFieldClient.discoverCapabilities(options);
|
|
1556
|
+
}
|
|
1557
|
+
async registerMcpTools() {
|
|
1558
|
+
if (!this.mcpToolRegistrar) return { registered: [] };
|
|
1559
|
+
return this.mcpToolRegistrar.registerAll();
|
|
1560
|
+
}
|
|
1561
|
+
getAIClient() {
|
|
1562
|
+
return this.aiClient;
|
|
1563
|
+
}
|
|
1564
|
+
getMemoryInterface(metadata) {
|
|
1565
|
+
const defaultScope = this.config.memoryConfig?.defaultScope ?? "workflow";
|
|
1566
|
+
const defaultScopeId = defaultScope === "session" ? metadata?.sessionId : defaultScope === "actor" ? metadata?.actorId : metadata?.workflowId ?? metadata?.runId ?? metadata?.sessionId ?? metadata?.actorId;
|
|
1567
|
+
return new MemoryInterface({
|
|
1568
|
+
client: this.memoryClient,
|
|
1569
|
+
eventClient: this.memoryEventClient,
|
|
1570
|
+
aiClient: this.aiClient,
|
|
1571
|
+
defaultScope,
|
|
1572
|
+
defaultScopeId,
|
|
1573
|
+
metadata: {
|
|
1574
|
+
workflowId: metadata?.workflowId ?? metadata?.runId,
|
|
1575
|
+
sessionId: metadata?.sessionId,
|
|
1576
|
+
actorId: metadata?.actorId,
|
|
1577
|
+
runId: metadata?.runId,
|
|
1578
|
+
executionId: metadata?.executionId,
|
|
1579
|
+
parentExecutionId: metadata?.parentExecutionId,
|
|
1580
|
+
callerDid: metadata?.callerDid,
|
|
1581
|
+
targetDid: metadata?.targetDid,
|
|
1582
|
+
agentNodeDid: metadata?.agentNodeDid,
|
|
1583
|
+
agentNodeId: this.config.nodeId
|
|
1584
|
+
}
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
getWorkflowReporter(metadata) {
|
|
1588
|
+
return new WorkflowReporter(this.agentFieldClient, {
|
|
1589
|
+
executionId: metadata.executionId,
|
|
1590
|
+
runId: metadata.runId,
|
|
1591
|
+
workflowId: metadata.workflowId,
|
|
1592
|
+
agentNodeId: this.config.nodeId
|
|
1593
|
+
});
|
|
1594
|
+
}
|
|
1595
|
+
getDidInterface(metadata, defaultInput) {
|
|
1596
|
+
return new DidInterface({
|
|
1597
|
+
client: this.didClient,
|
|
1598
|
+
metadata: {
|
|
1599
|
+
...metadata,
|
|
1600
|
+
agentNodeDid: metadata.agentNodeDid ?? this.config.defaultHeaders?.["X-Agent-Node-DID"]?.toString()
|
|
1601
|
+
},
|
|
1602
|
+
enabled: Boolean(this.config.didEnabled),
|
|
1603
|
+
defaultInput
|
|
1604
|
+
});
|
|
1605
|
+
}
|
|
1606
|
+
async serve() {
|
|
1607
|
+
if (this.config.mcp?.autoRegisterTools !== false) {
|
|
1608
|
+
try {
|
|
1609
|
+
await this.registerMcpTools();
|
|
1610
|
+
} catch (err) {
|
|
1611
|
+
if (this.config.devMode) {
|
|
1612
|
+
console.warn("MCP tool registration failed", err);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
await this.registerWithControlPlane();
|
|
1617
|
+
const port = this.config.port ?? 8001;
|
|
1618
|
+
const host = this.config.host ?? "0.0.0.0";
|
|
1619
|
+
await this.agentFieldClient.heartbeat("starting");
|
|
1620
|
+
await new Promise((resolve, reject) => {
|
|
1621
|
+
this.server = this.app.listen(port, host, () => resolve()).on("error", reject);
|
|
1622
|
+
});
|
|
1623
|
+
this.memoryEventClient.start();
|
|
1624
|
+
this.startHeartbeat();
|
|
1625
|
+
}
|
|
1626
|
+
async shutdown() {
|
|
1627
|
+
if (this.heartbeatTimer) {
|
|
1628
|
+
clearInterval(this.heartbeatTimer);
|
|
1629
|
+
}
|
|
1630
|
+
await new Promise((resolve, reject) => {
|
|
1631
|
+
this.server?.close((err) => {
|
|
1632
|
+
if (err) reject(err);
|
|
1633
|
+
else resolve();
|
|
1634
|
+
});
|
|
1635
|
+
});
|
|
1636
|
+
this.memoryEventClient.stop();
|
|
1637
|
+
}
|
|
1638
|
+
async call(target, input) {
|
|
1639
|
+
const { agentId, name } = this.parseTarget(target);
|
|
1640
|
+
if (!agentId || agentId === this.config.nodeId) {
|
|
1641
|
+
const local = this.reasoners.get(name);
|
|
1642
|
+
if (!local) throw new Error(`Reasoner not found: ${name}`);
|
|
1643
|
+
const parentMetadata = ExecutionContext.getCurrent()?.metadata;
|
|
1644
|
+
const runId = parentMetadata?.runId ?? parentMetadata?.executionId ?? randomUUID();
|
|
1645
|
+
const metadata2 = {
|
|
1646
|
+
...parentMetadata,
|
|
1647
|
+
executionId: randomUUID(),
|
|
1648
|
+
parentExecutionId: parentMetadata?.executionId,
|
|
1649
|
+
runId,
|
|
1650
|
+
workflowId: parentMetadata?.workflowId ?? runId
|
|
1651
|
+
};
|
|
1652
|
+
const dummyReq = {};
|
|
1653
|
+
const dummyRes = {};
|
|
1654
|
+
const execCtx = new ExecutionContext({
|
|
1655
|
+
input,
|
|
1656
|
+
metadata: {
|
|
1657
|
+
...metadata2,
|
|
1658
|
+
executionId: metadata2.executionId ?? randomUUID()
|
|
1659
|
+
},
|
|
1660
|
+
req: dummyReq,
|
|
1661
|
+
res: dummyRes,
|
|
1662
|
+
agent: this
|
|
1663
|
+
});
|
|
1664
|
+
const startTime = Date.now();
|
|
1665
|
+
const emitEvent = async (status, payload) => {
|
|
1666
|
+
await this.agentFieldClient.publishWorkflowEvent({
|
|
1667
|
+
executionId: execCtx.metadata.executionId,
|
|
1668
|
+
runId: execCtx.metadata.runId ?? execCtx.metadata.executionId,
|
|
1669
|
+
workflowId: execCtx.metadata.workflowId,
|
|
1670
|
+
reasonerId: name,
|
|
1671
|
+
agentNodeId: this.config.nodeId,
|
|
1672
|
+
status,
|
|
1673
|
+
parentExecutionId: execCtx.metadata.parentExecutionId,
|
|
1674
|
+
parentWorkflowId: execCtx.metadata.workflowId,
|
|
1675
|
+
inputData: status === "running" ? input : void 0,
|
|
1676
|
+
result: status === "succeeded" ? payload : void 0,
|
|
1677
|
+
error: status === "failed" ? payload?.message ?? String(payload) : void 0,
|
|
1678
|
+
durationMs: status === "running" ? void 0 : Date.now() - startTime
|
|
1679
|
+
});
|
|
1680
|
+
};
|
|
1681
|
+
await emitEvent("running", null);
|
|
1682
|
+
return ExecutionContext.run(execCtx, async () => {
|
|
1683
|
+
try {
|
|
1684
|
+
const result = await local.handler(
|
|
1685
|
+
new ReasonerContext({
|
|
1686
|
+
input,
|
|
1687
|
+
executionId: execCtx.metadata.executionId,
|
|
1688
|
+
runId: execCtx.metadata.runId,
|
|
1689
|
+
sessionId: execCtx.metadata.sessionId,
|
|
1690
|
+
actorId: execCtx.metadata.actorId,
|
|
1691
|
+
workflowId: execCtx.metadata.workflowId,
|
|
1692
|
+
parentExecutionId: execCtx.metadata.parentExecutionId,
|
|
1693
|
+
callerDid: execCtx.metadata.callerDid,
|
|
1694
|
+
targetDid: execCtx.metadata.targetDid,
|
|
1695
|
+
agentNodeDid: execCtx.metadata.agentNodeDid,
|
|
1696
|
+
req: dummyReq,
|
|
1697
|
+
res: dummyRes,
|
|
1698
|
+
agent: this,
|
|
1699
|
+
aiClient: this.aiClient,
|
|
1700
|
+
memory: this.getMemoryInterface(execCtx.metadata),
|
|
1701
|
+
workflow: this.getWorkflowReporter(execCtx.metadata),
|
|
1702
|
+
did: this.getDidInterface(execCtx.metadata, input)
|
|
1703
|
+
})
|
|
1704
|
+
);
|
|
1705
|
+
await emitEvent("succeeded", result);
|
|
1706
|
+
return result;
|
|
1707
|
+
} catch (err) {
|
|
1708
|
+
await emitEvent("failed", err);
|
|
1709
|
+
throw err;
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
const metadata = ExecutionContext.getCurrent()?.metadata;
|
|
1714
|
+
return this.agentFieldClient.execute(target, input, {
|
|
1715
|
+
runId: metadata?.runId ?? metadata?.executionId,
|
|
1716
|
+
workflowId: metadata?.workflowId ?? metadata?.runId,
|
|
1717
|
+
parentExecutionId: metadata?.executionId,
|
|
1718
|
+
sessionId: metadata?.sessionId,
|
|
1719
|
+
actorId: metadata?.actorId,
|
|
1720
|
+
callerDid: metadata?.callerDid,
|
|
1721
|
+
targetDid: metadata?.targetDid,
|
|
1722
|
+
agentNodeDid: metadata?.agentNodeDid,
|
|
1723
|
+
agentNodeId: this.config.nodeId
|
|
1724
|
+
});
|
|
1725
|
+
}
|
|
1726
|
+
registerDefaultRoutes() {
|
|
1727
|
+
this.app.get("/health", (_req, res) => {
|
|
1728
|
+
res.json(this.health());
|
|
1729
|
+
});
|
|
1730
|
+
this.app.get("/discover", (_req, res) => {
|
|
1731
|
+
res.json(this.discoveryPayload(this.config.deploymentType ?? "long_running"));
|
|
1732
|
+
});
|
|
1733
|
+
this.app.get("/health/mcp", async (_req, res) => {
|
|
1734
|
+
if (!this.mcpClientRegistry) {
|
|
1735
|
+
res.json({ status: "disabled", totalServers: 0, healthyServers: 0, servers: [] });
|
|
1736
|
+
return;
|
|
1737
|
+
}
|
|
1738
|
+
try {
|
|
1739
|
+
const summary = await this.mcpClientRegistry.healthSummary();
|
|
1740
|
+
res.json(summary);
|
|
1741
|
+
} catch (err) {
|
|
1742
|
+
res.status(500).json({ status: "error", error: err?.message ?? "MCP health check failed" });
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
this.app.get("/mcp/status", (_req, res) => {
|
|
1746
|
+
res.json(this.mcpStatus());
|
|
1747
|
+
});
|
|
1748
|
+
this.app.get("/status", (_req, res) => {
|
|
1749
|
+
res.json({
|
|
1750
|
+
...this.health(),
|
|
1751
|
+
reasoners: this.reasoners.all().map((r) => r.name),
|
|
1752
|
+
skills: this.skills.all().map((s) => s.name)
|
|
1753
|
+
});
|
|
1754
|
+
});
|
|
1755
|
+
this.app.get("/reasoners", (_req, res) => {
|
|
1756
|
+
res.json(this.reasoners.all().map((r) => r.name));
|
|
1757
|
+
});
|
|
1758
|
+
this.app.get("/skills", (_req, res) => {
|
|
1759
|
+
res.json(this.skills.all().map((s) => s.name));
|
|
1760
|
+
});
|
|
1761
|
+
this.app.post("/api/v1/reasoners/*", (req, res) => this.executeReasoner(req, res, req.params[0]));
|
|
1762
|
+
this.app.post("/reasoners/:name", (req, res) => this.executeReasoner(req, res, req.params.name));
|
|
1763
|
+
this.app.post("/api/v1/skills/*", (req, res) => this.executeSkill(req, res, req.params[0]));
|
|
1764
|
+
this.app.post("/skills/:name", (req, res) => this.executeSkill(req, res, req.params.name));
|
|
1765
|
+
this.app.post("/execute", (req, res) => this.executeServerlessHttp(req, res));
|
|
1766
|
+
this.app.post("/execute/:name", (req, res) => this.executeServerlessHttp(req, res, req.params.name));
|
|
1767
|
+
}
|
|
1768
|
+
async executeReasoner(req, res, name) {
|
|
1769
|
+
try {
|
|
1770
|
+
await this.executeInvocation({
|
|
1771
|
+
targetName: name,
|
|
1772
|
+
targetType: "reasoner",
|
|
1773
|
+
input: req.body,
|
|
1774
|
+
metadata: this.buildMetadata(req),
|
|
1775
|
+
req,
|
|
1776
|
+
res,
|
|
1777
|
+
respond: true
|
|
1778
|
+
});
|
|
1779
|
+
} catch (err) {
|
|
1780
|
+
if (err instanceof TargetNotFoundError) {
|
|
1781
|
+
res.status(404).json({ error: err.message });
|
|
1782
|
+
} else {
|
|
1783
|
+
res.status(500).json({ error: err?.message ?? "Execution failed" });
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
async executeSkill(req, res, name) {
|
|
1788
|
+
try {
|
|
1789
|
+
await this.executeInvocation({
|
|
1790
|
+
targetName: name,
|
|
1791
|
+
targetType: "skill",
|
|
1792
|
+
input: req.body,
|
|
1793
|
+
metadata: this.buildMetadata(req),
|
|
1794
|
+
req,
|
|
1795
|
+
res,
|
|
1796
|
+
respond: true
|
|
1797
|
+
});
|
|
1798
|
+
} catch (err) {
|
|
1799
|
+
if (err instanceof TargetNotFoundError) {
|
|
1800
|
+
res.status(404).json({ error: err.message });
|
|
1801
|
+
} else {
|
|
1802
|
+
res.status(500).json({ error: err?.message ?? "Execution failed" });
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
buildMetadata(req) {
|
|
1807
|
+
return this.buildMetadataFromHeaders(req.headers);
|
|
1808
|
+
}
|
|
1809
|
+
async executeServerlessHttp(req, res, explicitName) {
|
|
1810
|
+
const invocation = this.extractInvocationDetails({
|
|
1811
|
+
path: req.path,
|
|
1812
|
+
explicitTarget: explicitName,
|
|
1813
|
+
query: req.query,
|
|
1814
|
+
body: req.body
|
|
1815
|
+
});
|
|
1816
|
+
if (!invocation.name) {
|
|
1817
|
+
res.status(400).json({ error: "Missing 'target' or 'reasoner' in request" });
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
try {
|
|
1821
|
+
const result = await this.executeInvocation({
|
|
1822
|
+
targetName: invocation.name,
|
|
1823
|
+
targetType: invocation.targetType,
|
|
1824
|
+
input: invocation.input,
|
|
1825
|
+
metadata: this.buildMetadata(req),
|
|
1826
|
+
req,
|
|
1827
|
+
res,
|
|
1828
|
+
respond: true
|
|
1829
|
+
});
|
|
1830
|
+
if (result !== void 0 && !res.headersSent) {
|
|
1831
|
+
res.json(result);
|
|
1832
|
+
}
|
|
1833
|
+
} catch (err) {
|
|
1834
|
+
if (err instanceof TargetNotFoundError) {
|
|
1835
|
+
res.status(404).json({ error: err.message });
|
|
1836
|
+
} else {
|
|
1837
|
+
res.status(500).json({ error: err?.message ?? "Execution failed" });
|
|
1838
|
+
}
|
|
1839
|
+
}
|
|
1840
|
+
}
|
|
1841
|
+
buildMetadataFromHeaders(headers, overrides) {
|
|
1842
|
+
const normalized = {};
|
|
1843
|
+
Object.entries(headers ?? {}).forEach(([key, value]) => {
|
|
1844
|
+
normalized[key.toLowerCase()] = Array.isArray(value) ? value[0] : value;
|
|
1845
|
+
});
|
|
1846
|
+
const executionId = overrides?.executionId ?? normalized["x-execution-id"] ?? randomUUID();
|
|
1847
|
+
const runId = overrides?.runId ?? normalized["x-run-id"] ?? executionId;
|
|
1848
|
+
const workflowId = overrides?.workflowId ?? normalized["x-workflow-id"] ?? runId;
|
|
1849
|
+
return {
|
|
1850
|
+
executionId,
|
|
1851
|
+
runId,
|
|
1852
|
+
workflowId,
|
|
1853
|
+
sessionId: overrides?.sessionId ?? normalized["x-session-id"],
|
|
1854
|
+
actorId: overrides?.actorId ?? normalized["x-actor-id"],
|
|
1855
|
+
parentExecutionId: overrides?.parentExecutionId ?? normalized["x-parent-execution-id"],
|
|
1856
|
+
callerDid: overrides?.callerDid ?? normalized["x-caller-did"],
|
|
1857
|
+
targetDid: overrides?.targetDid ?? normalized["x-target-did"],
|
|
1858
|
+
agentNodeDid: overrides?.agentNodeDid ?? normalized["x-agent-node-did"] ?? normalized["x-agent-did"]
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
handleHttpRequest(req, res) {
|
|
1862
|
+
const handler = this.app;
|
|
1863
|
+
return handler(req, res);
|
|
1864
|
+
}
|
|
1865
|
+
async handleServerlessEvent(event) {
|
|
1866
|
+
const path = event?.path ?? event?.rawPath ?? "";
|
|
1867
|
+
const action = event?.action ?? "";
|
|
1868
|
+
if (path === "/discover" || action === "discover") {
|
|
1869
|
+
return {
|
|
1870
|
+
statusCode: 200,
|
|
1871
|
+
headers: { "content-type": "application/json" },
|
|
1872
|
+
body: this.discoveryPayload(this.config.deploymentType ?? "serverless")
|
|
1873
|
+
};
|
|
1874
|
+
}
|
|
1875
|
+
const body = this.normalizeEventBody(event);
|
|
1876
|
+
const invocation = this.extractInvocationDetails({
|
|
1877
|
+
path,
|
|
1878
|
+
query: event?.queryStringParameters,
|
|
1879
|
+
body,
|
|
1880
|
+
reasoner: event?.reasoner,
|
|
1881
|
+
target: event?.target,
|
|
1882
|
+
skill: event?.skill,
|
|
1883
|
+
type: event?.type
|
|
1884
|
+
});
|
|
1885
|
+
if (!invocation.name) {
|
|
1886
|
+
return {
|
|
1887
|
+
statusCode: 400,
|
|
1888
|
+
headers: { "content-type": "application/json" },
|
|
1889
|
+
body: { error: "Missing 'target' or 'reasoner' in request" }
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
const metadata = this.buildMetadataFromHeaders(event?.headers ?? {}, this.mergeExecutionContext(event));
|
|
1893
|
+
try {
|
|
1894
|
+
const result = await this.executeInvocation({
|
|
1895
|
+
targetName: invocation.name,
|
|
1896
|
+
targetType: invocation.targetType,
|
|
1897
|
+
input: invocation.input,
|
|
1898
|
+
metadata
|
|
1899
|
+
});
|
|
1900
|
+
return { statusCode: 200, headers: { "content-type": "application/json" }, body: result };
|
|
1901
|
+
} catch (err) {
|
|
1902
|
+
if (err instanceof TargetNotFoundError) {
|
|
1903
|
+
return {
|
|
1904
|
+
statusCode: 404,
|
|
1905
|
+
headers: { "content-type": "application/json" },
|
|
1906
|
+
body: { error: err.message }
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1909
|
+
return {
|
|
1910
|
+
statusCode: 500,
|
|
1911
|
+
headers: { "content-type": "application/json" },
|
|
1912
|
+
body: { error: err?.message ?? "Execution failed" }
|
|
1913
|
+
};
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
normalizeEventBody(event) {
|
|
1917
|
+
const parsed = this.parseBody(event?.body);
|
|
1918
|
+
if (parsed && typeof parsed === "object" && event?.input !== void 0 && parsed.input === void 0) {
|
|
1919
|
+
return { ...parsed, input: event.input };
|
|
1920
|
+
}
|
|
1921
|
+
if ((parsed === void 0 || parsed === null) && event?.input !== void 0) {
|
|
1922
|
+
return { input: event.input };
|
|
1923
|
+
}
|
|
1924
|
+
return parsed;
|
|
1925
|
+
}
|
|
1926
|
+
mergeExecutionContext(event) {
|
|
1927
|
+
const ctx = event?.executionContext ?? event?.execution_context;
|
|
1928
|
+
if (!ctx) return {};
|
|
1929
|
+
return {
|
|
1930
|
+
executionId: ctx.executionId ?? ctx.execution_id ?? ctx.executionId,
|
|
1931
|
+
runId: ctx.runId ?? ctx.run_id,
|
|
1932
|
+
workflowId: ctx.workflowId ?? ctx.workflow_id,
|
|
1933
|
+
parentExecutionId: ctx.parentExecutionId ?? ctx.parent_execution_id,
|
|
1934
|
+
sessionId: ctx.sessionId ?? ctx.session_id,
|
|
1935
|
+
actorId: ctx.actorId ?? ctx.actor_id,
|
|
1936
|
+
callerDid: ctx.callerDid ?? ctx.caller_did,
|
|
1937
|
+
targetDid: ctx.targetDid ?? ctx.target_did,
|
|
1938
|
+
agentNodeDid: ctx.agentNodeDid ?? ctx.agent_node_did
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1941
|
+
extractInvocationDetails(params) {
|
|
1942
|
+
const pathTarget = this.parsePathTarget(params.path);
|
|
1943
|
+
const name = this.firstDefined(
|
|
1944
|
+
params.explicitTarget,
|
|
1945
|
+
pathTarget.name,
|
|
1946
|
+
params.query?.target,
|
|
1947
|
+
params.query?.reasoner,
|
|
1948
|
+
params.query?.skill,
|
|
1949
|
+
params.target,
|
|
1950
|
+
params.reasoner,
|
|
1951
|
+
params.skill,
|
|
1952
|
+
params.body?.target,
|
|
1953
|
+
params.body?.reasoner,
|
|
1954
|
+
params.body?.skill
|
|
1955
|
+
) ?? pathTarget.name;
|
|
1956
|
+
const typeValue = this.firstDefined(
|
|
1957
|
+
pathTarget.targetType,
|
|
1958
|
+
params.type,
|
|
1959
|
+
params.query?.type,
|
|
1960
|
+
params.query?.targetType,
|
|
1961
|
+
params.body?.type,
|
|
1962
|
+
params.body?.targetType
|
|
1963
|
+
) ?? void 0;
|
|
1964
|
+
const input = this.normalizeInputPayload(params.body);
|
|
1965
|
+
return { name: name ?? void 0, targetType: typeValue, input };
|
|
1966
|
+
}
|
|
1967
|
+
parsePathTarget(path) {
|
|
1968
|
+
if (!path) return {};
|
|
1969
|
+
const normalized = path.split("?")[0];
|
|
1970
|
+
const reasonerMatch = normalized.match(/\/reasoners\/([^/]+)/);
|
|
1971
|
+
if (reasonerMatch?.[1]) {
|
|
1972
|
+
return { name: reasonerMatch[1], targetType: "reasoner" };
|
|
1973
|
+
}
|
|
1974
|
+
const skillMatch = normalized.match(/\/skills\/([^/]+)/);
|
|
1975
|
+
if (skillMatch?.[1]) {
|
|
1976
|
+
return { name: skillMatch[1], targetType: "skill" };
|
|
1977
|
+
}
|
|
1978
|
+
const executeMatch = normalized.match(/\/execute\/([^/]+)/);
|
|
1979
|
+
if (executeMatch?.[1]) {
|
|
1980
|
+
return { name: executeMatch[1] };
|
|
1981
|
+
}
|
|
1982
|
+
return {};
|
|
1983
|
+
}
|
|
1984
|
+
parseBody(body) {
|
|
1985
|
+
if (body === void 0 || body === null) return body;
|
|
1986
|
+
if (typeof body === "string") {
|
|
1987
|
+
try {
|
|
1988
|
+
return JSON.parse(body);
|
|
1989
|
+
} catch {
|
|
1990
|
+
return body;
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return body;
|
|
1994
|
+
}
|
|
1995
|
+
normalizeInputPayload(body) {
|
|
1996
|
+
if (body === void 0 || body === null) return {};
|
|
1997
|
+
const parsed = this.parseBody(body);
|
|
1998
|
+
if (parsed && typeof parsed === "object") {
|
|
1999
|
+
const { target, reasoner, skill, type, targetType, ...rest } = parsed;
|
|
2000
|
+
if (parsed.input !== void 0) {
|
|
2001
|
+
return parsed.input;
|
|
2002
|
+
}
|
|
2003
|
+
if (parsed.data !== void 0) {
|
|
2004
|
+
return parsed.data;
|
|
2005
|
+
}
|
|
2006
|
+
if (Object.keys(rest).length === 0) {
|
|
2007
|
+
return {};
|
|
2008
|
+
}
|
|
2009
|
+
return rest;
|
|
2010
|
+
}
|
|
2011
|
+
return parsed;
|
|
2012
|
+
}
|
|
2013
|
+
firstDefined(...values) {
|
|
2014
|
+
for (const value of values) {
|
|
2015
|
+
if (value !== void 0 && value !== null) {
|
|
2016
|
+
return value;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
return void 0;
|
|
2020
|
+
}
|
|
2021
|
+
reasonerDefinitions() {
|
|
2022
|
+
return this.reasoners.all().map((r) => ({
|
|
2023
|
+
id: r.name,
|
|
2024
|
+
input_schema: r.options?.inputSchema ?? {},
|
|
2025
|
+
output_schema: r.options?.outputSchema ?? {},
|
|
2026
|
+
memory_config: r.options?.memoryConfig ?? {
|
|
2027
|
+
auto_inject: [],
|
|
2028
|
+
memory_retention: "",
|
|
2029
|
+
cache_results: false
|
|
2030
|
+
},
|
|
2031
|
+
tags: r.options?.tags ?? []
|
|
2032
|
+
}));
|
|
2033
|
+
}
|
|
2034
|
+
skillDefinitions() {
|
|
2035
|
+
return this.skills.all().map((s) => ({
|
|
2036
|
+
id: s.name,
|
|
2037
|
+
input_schema: s.options?.inputSchema ?? {},
|
|
2038
|
+
tags: s.options?.tags ?? []
|
|
2039
|
+
}));
|
|
2040
|
+
}
|
|
2041
|
+
discoveryPayload(deploymentType) {
|
|
2042
|
+
return {
|
|
2043
|
+
node_id: this.config.nodeId,
|
|
2044
|
+
version: this.config.version,
|
|
2045
|
+
deployment_type: deploymentType,
|
|
2046
|
+
reasoners: this.reasonerDefinitions(),
|
|
2047
|
+
skills: this.skillDefinitions()
|
|
2048
|
+
};
|
|
2049
|
+
}
|
|
2050
|
+
async executeInvocation(params) {
|
|
2051
|
+
const targetType = params.targetType;
|
|
2052
|
+
if (targetType === "skill") {
|
|
2053
|
+
const skill = this.skills.get(params.targetName);
|
|
2054
|
+
if (!skill) {
|
|
2055
|
+
throw new TargetNotFoundError(`Skill not found: ${params.targetName}`);
|
|
2056
|
+
}
|
|
2057
|
+
return this.runSkill(skill, params);
|
|
2058
|
+
}
|
|
2059
|
+
const reasoner = this.reasoners.get(params.targetName);
|
|
2060
|
+
if (reasoner) {
|
|
2061
|
+
return this.runReasoner(reasoner, params);
|
|
2062
|
+
}
|
|
2063
|
+
const fallbackSkill = this.skills.get(params.targetName);
|
|
2064
|
+
if (fallbackSkill) {
|
|
2065
|
+
return this.runSkill(fallbackSkill, params);
|
|
2066
|
+
}
|
|
2067
|
+
throw new TargetNotFoundError(`Reasoner not found: ${params.targetName}`);
|
|
2068
|
+
}
|
|
2069
|
+
async runReasoner(reasoner, params) {
|
|
2070
|
+
const req = params.req ?? {};
|
|
2071
|
+
const res = params.res ?? {};
|
|
2072
|
+
const execCtx = new ExecutionContext({
|
|
2073
|
+
input: params.input,
|
|
2074
|
+
metadata: params.metadata,
|
|
2075
|
+
req,
|
|
2076
|
+
res,
|
|
2077
|
+
agent: this
|
|
2078
|
+
});
|
|
2079
|
+
return ExecutionContext.run(execCtx, async () => {
|
|
2080
|
+
try {
|
|
2081
|
+
const ctx = new ReasonerContext({
|
|
2082
|
+
input: params.input,
|
|
2083
|
+
executionId: params.metadata.executionId,
|
|
2084
|
+
runId: params.metadata.runId,
|
|
2085
|
+
sessionId: params.metadata.sessionId,
|
|
2086
|
+
actorId: params.metadata.actorId,
|
|
2087
|
+
workflowId: params.metadata.workflowId,
|
|
2088
|
+
parentExecutionId: params.metadata.parentExecutionId,
|
|
2089
|
+
callerDid: params.metadata.callerDid,
|
|
2090
|
+
targetDid: params.metadata.targetDid,
|
|
2091
|
+
agentNodeDid: params.metadata.agentNodeDid,
|
|
2092
|
+
req,
|
|
2093
|
+
res,
|
|
2094
|
+
agent: this,
|
|
2095
|
+
aiClient: this.aiClient,
|
|
2096
|
+
memory: this.getMemoryInterface(params.metadata),
|
|
2097
|
+
workflow: this.getWorkflowReporter(params.metadata),
|
|
2098
|
+
did: this.getDidInterface(params.metadata, params.input)
|
|
2099
|
+
});
|
|
2100
|
+
const result = await reasoner.handler(ctx);
|
|
2101
|
+
if (params.respond && params.res) {
|
|
2102
|
+
params.res.json(result);
|
|
2103
|
+
return;
|
|
2104
|
+
}
|
|
2105
|
+
return result;
|
|
2106
|
+
} catch (err) {
|
|
2107
|
+
if (params.respond && params.res) {
|
|
2108
|
+
params.res.status(500).json({ error: err?.message ?? "Execution failed" });
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
throw err;
|
|
2112
|
+
}
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
async runSkill(skill, params) {
|
|
2116
|
+
const req = params.req ?? {};
|
|
2117
|
+
const res = params.res ?? {};
|
|
2118
|
+
const execCtx = new ExecutionContext({
|
|
2119
|
+
input: params.input,
|
|
2120
|
+
metadata: params.metadata,
|
|
2121
|
+
req,
|
|
2122
|
+
res,
|
|
2123
|
+
agent: this
|
|
2124
|
+
});
|
|
2125
|
+
return ExecutionContext.run(execCtx, async () => {
|
|
2126
|
+
try {
|
|
2127
|
+
const ctx = new SkillContext({
|
|
2128
|
+
input: params.input,
|
|
2129
|
+
executionId: params.metadata.executionId,
|
|
2130
|
+
sessionId: params.metadata.sessionId,
|
|
2131
|
+
workflowId: params.metadata.workflowId,
|
|
2132
|
+
req,
|
|
2133
|
+
res,
|
|
2134
|
+
agent: this,
|
|
2135
|
+
memory: this.getMemoryInterface(params.metadata),
|
|
2136
|
+
workflow: this.getWorkflowReporter(params.metadata),
|
|
2137
|
+
did: this.getDidInterface(params.metadata, params.input)
|
|
2138
|
+
});
|
|
2139
|
+
const result = await skill.handler(ctx);
|
|
2140
|
+
if (params.respond && params.res) {
|
|
2141
|
+
params.res.json(result);
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
return result;
|
|
2145
|
+
} catch (err) {
|
|
2146
|
+
if (params.respond && params.res) {
|
|
2147
|
+
params.res.status(500).json({ error: err?.message ?? "Execution failed" });
|
|
2148
|
+
return;
|
|
2149
|
+
}
|
|
2150
|
+
throw err;
|
|
2151
|
+
}
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
2154
|
+
async registerWithControlPlane() {
|
|
2155
|
+
try {
|
|
2156
|
+
const reasoners = this.reasonerDefinitions();
|
|
2157
|
+
const skills = this.skillDefinitions();
|
|
2158
|
+
const port = this.config.port ?? 8001;
|
|
2159
|
+
const hostForUrl = this.config.publicUrl ? void 0 : this.config.host && this.config.host !== "0.0.0.0" ? this.config.host : "127.0.0.1";
|
|
2160
|
+
const publicUrl = this.config.publicUrl ?? `http://${hostForUrl ?? "127.0.0.1"}:${port}`;
|
|
2161
|
+
await this.agentFieldClient.register({
|
|
2162
|
+
id: this.config.nodeId,
|
|
2163
|
+
version: this.config.version,
|
|
2164
|
+
base_url: publicUrl,
|
|
2165
|
+
public_url: publicUrl,
|
|
2166
|
+
deployment_type: this.config.deploymentType ?? "long_running",
|
|
2167
|
+
reasoners,
|
|
2168
|
+
skills
|
|
2169
|
+
});
|
|
2170
|
+
} catch (err) {
|
|
2171
|
+
if (!this.config.devMode) {
|
|
2172
|
+
throw err;
|
|
2173
|
+
}
|
|
2174
|
+
console.warn("Control plane registration failed (devMode=true), continuing locally", err);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
startHeartbeat() {
|
|
2178
|
+
const interval = this.config.heartbeatIntervalMs ?? 3e4;
|
|
2179
|
+
if (interval <= 0) return;
|
|
2180
|
+
const tick = async () => {
|
|
2181
|
+
try {
|
|
2182
|
+
await this.agentFieldClient.heartbeat("ready");
|
|
2183
|
+
} catch (err) {
|
|
2184
|
+
if (!this.config.devMode) {
|
|
2185
|
+
console.warn("Heartbeat failed", err);
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
this.heartbeatTimer = setInterval(tick, interval);
|
|
2190
|
+
tick();
|
|
2191
|
+
}
|
|
2192
|
+
health() {
|
|
2193
|
+
return {
|
|
2194
|
+
status: "running",
|
|
2195
|
+
nodeId: this.config.nodeId,
|
|
2196
|
+
version: this.config.version
|
|
2197
|
+
};
|
|
2198
|
+
}
|
|
2199
|
+
mcpStatus() {
|
|
2200
|
+
const servers = this.mcpClientRegistry ? this.mcpClientRegistry.list().map((client) => ({
|
|
2201
|
+
alias: client.alias,
|
|
2202
|
+
baseUrl: client.baseUrl,
|
|
2203
|
+
transport: client.transport
|
|
2204
|
+
})) : [];
|
|
2205
|
+
const skills = this.skills.all().filter((skill) => skill.options?.tags?.includes("mcp")).map((skill) => skill.name);
|
|
2206
|
+
return {
|
|
2207
|
+
status: servers.length ? "configured" : "disabled",
|
|
2208
|
+
servers,
|
|
2209
|
+
skills
|
|
2210
|
+
};
|
|
2211
|
+
}
|
|
2212
|
+
dispatchMemoryEvent(event) {
|
|
2213
|
+
this.memoryWatchers.forEach(({ pattern, handler, scope, scopeId }) => {
|
|
2214
|
+
const scopeMatch = (!scope || scope === event.scope) && (!scopeId || scopeId === event.scopeId);
|
|
2215
|
+
if (scopeMatch && matchesPattern(pattern, event.key)) {
|
|
2216
|
+
handler(event);
|
|
2217
|
+
}
|
|
2218
|
+
});
|
|
2219
|
+
}
|
|
2220
|
+
parseTarget(target) {
|
|
2221
|
+
if (!target.includes(".")) {
|
|
2222
|
+
return { name: target };
|
|
2223
|
+
}
|
|
2224
|
+
const [agentId, remainder] = target.split(".", 2);
|
|
2225
|
+
const name = remainder.replace(":", "/");
|
|
2226
|
+
return { agentId, name };
|
|
2227
|
+
}
|
|
2228
|
+
};
|
|
2229
|
+
|
|
2230
|
+
// src/router/AgentRouter.ts
|
|
2231
|
+
var AgentRouter = class {
|
|
2232
|
+
prefix;
|
|
2233
|
+
tags;
|
|
2234
|
+
reasoners = [];
|
|
2235
|
+
skills = [];
|
|
2236
|
+
constructor(options = {}) {
|
|
2237
|
+
this.prefix = options.prefix;
|
|
2238
|
+
this.tags = options.tags;
|
|
2239
|
+
}
|
|
2240
|
+
reasoner(name, handler, options) {
|
|
2241
|
+
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
2242
|
+
this.reasoners.push({ name: fullName, handler, options });
|
|
2243
|
+
return this;
|
|
2244
|
+
}
|
|
2245
|
+
skill(name, handler, options) {
|
|
2246
|
+
const fullName = this.prefix ? `${sanitize(this.prefix)}_${name}` : name;
|
|
2247
|
+
this.skills.push({ name: fullName, handler, options });
|
|
2248
|
+
return this;
|
|
2249
|
+
}
|
|
2250
|
+
};
|
|
2251
|
+
function sanitize(value) {
|
|
2252
|
+
return value.replace(/[^0-9a-zA-Z]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
export { AIClient, Agent, AgentRouter, DidClient, DidInterface, ExecutionContext, MCPClient, MCPClientRegistry, MCPToolRegistrar, MemoryClient, MemoryEventClient, MemoryInterface, RateLimitError, ReasonerContext, SkillContext, StatelessRateLimiter, WorkflowReporter, getCurrentContext, getCurrentSkillContext };
|
|
2256
|
+
//# sourceMappingURL=index.js.map
|
|
2257
|
+
//# sourceMappingURL=index.js.map
|