@microfox/ai-router 1.0.1

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 ADDED
@@ -0,0 +1,960 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ AgentDefinitionMissingError: () => AgentDefinitionMissingError,
34
+ AgentNotFoundError: () => AgentNotFoundError,
35
+ AiKitError: () => AiKitError,
36
+ AiRouter: () => AiRouter,
37
+ MaxCallDepthExceededError: () => MaxCallDepthExceededError,
38
+ StreamWriter: () => StreamWriter,
39
+ ToolNotFoundError: () => ToolNotFoundError,
40
+ ToolValidationError: () => ToolValidationError,
41
+ findFirstElement: () => findFirstElement,
42
+ findLastElement: () => findLastElement,
43
+ findLastMessageWith: () => findLastMessageWith,
44
+ getTextParts: () => getTextParts,
45
+ getTextPartsContent: () => getTextPartsContent
46
+ });
47
+ module.exports = __toCommonJS(index_exports);
48
+
49
+ // src/router.ts
50
+ var import_ai = require("ai");
51
+
52
+ // ../../node_modules/nanoid/index.js
53
+ var import_crypto = __toESM(require("crypto"), 1);
54
+ var POOL_SIZE_MULTIPLIER = 128;
55
+ var pool;
56
+ var poolOffset;
57
+ var fillPool = (bytes) => {
58
+ if (!pool || pool.length < bytes) {
59
+ pool = Buffer.allocUnsafe(bytes * POOL_SIZE_MULTIPLIER);
60
+ import_crypto.default.randomFillSync(pool);
61
+ poolOffset = 0;
62
+ } else if (poolOffset + bytes > pool.length) {
63
+ import_crypto.default.randomFillSync(pool);
64
+ poolOffset = 0;
65
+ }
66
+ poolOffset += bytes;
67
+ };
68
+ var random = (bytes) => {
69
+ fillPool(bytes |= 0);
70
+ return pool.subarray(poolOffset - bytes, poolOffset);
71
+ };
72
+ var customRandom = (alphabet, defaultSize, getRandom) => {
73
+ let mask = (2 << 31 - Math.clz32(alphabet.length - 1 | 1)) - 1;
74
+ let step = Math.ceil(1.6 * mask * defaultSize / alphabet.length);
75
+ return (size = defaultSize) => {
76
+ let id = "";
77
+ while (true) {
78
+ let bytes = getRandom(step);
79
+ let i = step;
80
+ while (i--) {
81
+ id += alphabet[bytes[i] & mask] || "";
82
+ if (id.length === size) return id;
83
+ }
84
+ }
85
+ };
86
+ };
87
+ var customAlphabet = (alphabet, size = 21) => customRandom(alphabet, size, random);
88
+
89
+ // src/helper.ts
90
+ var findLastElement = (array) => {
91
+ return array[array.length - 1];
92
+ };
93
+ var findFirstElement = (array) => {
94
+ return array[0];
95
+ };
96
+ var StreamWriter = class {
97
+ constructor(writer) {
98
+ this.generateId = () => {
99
+ return customAlphabet("1234567890abcdefghijklmnopqrstuvwxyz", 10)();
100
+ };
101
+ this.writeMessageMetadata = (metadata) => {
102
+ return this.writer.write({
103
+ type: "message-metadata",
104
+ messageMetadata: metadata
105
+ });
106
+ };
107
+ this.writeCustomTool = (tool2) => {
108
+ const toolCallId = tool2.toolName?.toString() + "-" + this.generateId();
109
+ if ("input" in tool2 && tool2.input) {
110
+ this.writer.write({
111
+ type: "tool-input-available",
112
+ input: tool2.input,
113
+ toolCallId: toolCallId || tool2.toolCallId || "",
114
+ toolName: tool2.toolName
115
+ });
116
+ }
117
+ if (tool2.inputTextDelta && tool2.inputTextDelta.length > 0 || "output" in tool2 && tool2.output) {
118
+ this.writer.write({
119
+ type: "tool-input-start",
120
+ toolCallId: toolCallId || tool2.toolCallId || "",
121
+ toolName: tool2.toolName
122
+ });
123
+ if (tool2.inputTextDelta) {
124
+ for (const delta of tool2.inputTextDelta) {
125
+ this.writer.write({
126
+ type: "tool-input-delta",
127
+ toolCallId: toolCallId || tool2.toolCallId || "",
128
+ inputTextDelta: delta
129
+ });
130
+ }
131
+ }
132
+ }
133
+ if ("output" in tool2 && tool2.output) {
134
+ this.writer.write({
135
+ type: "tool-output-available",
136
+ toolCallId: toolCallId || tool2.toolCallId || "",
137
+ output: tool2.output
138
+ });
139
+ }
140
+ };
141
+ this.writeObjectAsTool = (tool2) => {
142
+ if (!tool2.result.object) {
143
+ throw new Error("No object found in the GenerateObjectResult");
144
+ }
145
+ const toolCallId = tool2.toolName.toString() + "-" + this.generateId();
146
+ this.writer.write({
147
+ type: "tool-input-start",
148
+ toolCallId,
149
+ toolName: tool2.toolName
150
+ });
151
+ this.writer.write({
152
+ type: "tool-input-available",
153
+ toolCallId,
154
+ input: {
155
+ usage: tool2.result.usage,
156
+ warnings: tool2.result.warnings,
157
+ finishReason: tool2.result.finishReason
158
+ },
159
+ toolName: tool2.toolName
160
+ });
161
+ this.writer.write({
162
+ type: "tool-output-available",
163
+ toolCallId,
164
+ output: tool2.result.object
165
+ });
166
+ };
167
+ this.writer = writer;
168
+ }
169
+ };
170
+ var getTextParts = (message) => {
171
+ if (!message) return [];
172
+ return message.parts.filter((part) => part.type === "text").map((part) => part.text);
173
+ };
174
+ var getTextPartsContent = (message) => {
175
+ if (!message) return "";
176
+ return message.parts.filter((part) => part.type === "text").map((part) => part.text).join("").trim();
177
+ };
178
+ var findLastMessageWith = (message, filters) => {
179
+ if (!message) return null;
180
+ return message.filter((m) => {
181
+ if (filters.role && m.role !== filters.role) return false;
182
+ if (filters.metadata) {
183
+ for (const key in filters.metadata) {
184
+ }
185
+ }
186
+ return true;
187
+ }).pop();
188
+ };
189
+
190
+ // src/router.ts
191
+ var import_path = __toESM(require("path"));
192
+ var AiKitError = class extends Error {
193
+ constructor(message) {
194
+ super(message);
195
+ this.name = "AiKitError";
196
+ }
197
+ };
198
+ var AgentNotFoundError = class extends AiKitError {
199
+ constructor(path2) {
200
+ super(`[AiAgentKit] Agent not found for path: ${path2}`);
201
+ this.name = "AgentNotFoundError";
202
+ }
203
+ };
204
+ var ToolNotFoundError = class extends AiKitError {
205
+ constructor(path2) {
206
+ super(`[AiAgentKit] Tool not found at path: ${path2}`);
207
+ this.name = "ToolNotFoundError";
208
+ }
209
+ };
210
+ var ToolValidationError = class extends AiKitError {
211
+ constructor(path2, validationError) {
212
+ const message = `[AiAgentKit] Tool call validation failed for path: ${path2}: ${validationError.message}`;
213
+ super(message);
214
+ this.name = "ToolValidationError";
215
+ }
216
+ };
217
+ var MaxCallDepthExceededError = class extends AiKitError {
218
+ constructor(maxDepth) {
219
+ super(`[AiAgentKit] Agent call depth limit (${maxDepth}) exceeded.`);
220
+ this.name = "MaxCallDepthExceededError";
221
+ }
222
+ };
223
+ var AgentDefinitionMissingError = class extends AiKitError {
224
+ constructor(path2) {
225
+ super(
226
+ `[AiAgentKit] agentAsTool: No definition found for "${path2}". Please define it using '.actAsTool()' or pass a definition as the second argument.`
227
+ );
228
+ this.name = "AgentDefinitionMissingError";
229
+ }
230
+ };
231
+ function parsePathPattern(pattern) {
232
+ const paramNames = [];
233
+ const parts = pattern.split(/(\/:[^\/]+)/);
234
+ const regexPattern = parts.map((part) => {
235
+ if (part.startsWith("/:")) {
236
+ paramNames.push(part.substring(2));
237
+ return "/([^/]+)";
238
+ }
239
+ return part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
240
+ }).join("");
241
+ const regex = new RegExp(`^${regexPattern}$`);
242
+ return { regex, paramNames };
243
+ }
244
+ function extractPathParams(pattern, path2) {
245
+ const { regex, paramNames } = parsePathPattern(pattern);
246
+ const match = path2.match(regex);
247
+ if (!match) {
248
+ return null;
249
+ }
250
+ const params = {};
251
+ paramNames.forEach((paramName, index) => {
252
+ const value = match[index + 1];
253
+ if (value !== void 0) {
254
+ params[paramName] = value;
255
+ }
256
+ });
257
+ return params;
258
+ }
259
+ function hasDynamicParams(pattern) {
260
+ return /\/:[^\/]+/.test(pattern);
261
+ }
262
+ var AiRouter = class _AiRouter {
263
+ /**
264
+ * Constructs a new AiAgentKit router.
265
+ * @param stack An optional initial stack of layers, used for composing routers.
266
+ * @param options Optional configuration for the router.
267
+ */
268
+ constructor(stack, options) {
269
+ this.stack = [];
270
+ this.actAsToolDefinitions = /* @__PURE__ */ new Map();
271
+ this.logger = console;
272
+ /** Configuration options for the router instance. */
273
+ this.options = {
274
+ maxCallDepth: 10
275
+ };
276
+ this.pendingExecutions = 0;
277
+ this.logger = options?.logger ?? console;
278
+ this.logger.log("AiAgentKit v3 initialized.");
279
+ if (stack) {
280
+ this.stack = stack;
281
+ }
282
+ if (options?.maxCallDepth) {
283
+ this.options.maxCallDepth = options.maxCallDepth;
284
+ }
285
+ }
286
+ /**
287
+ * Registers a middleware-style agent that runs for a specific path prefix, regex pattern, or wildcard.
288
+ * Agents can modify the context and must call `next()` to pass control to the next handler in the chain.
289
+ * This method is primarily for middleware. For terminal agents, see `.agent()` on an instance.
290
+ *
291
+ * @param path The path prefix, regex pattern, or "*" for wildcard matching.
292
+ * @param agents The agent middleware function(s).
293
+ */
294
+ agent(path2, ...agents) {
295
+ let prefix = "/";
296
+ if (typeof path2 === "string" || path2 instanceof RegExp) {
297
+ prefix = path2;
298
+ } else {
299
+ agents.unshift(path2);
300
+ }
301
+ for (const handler of agents) {
302
+ if (typeof handler !== "function") {
303
+ if (handler instanceof _AiRouter && typeof prefix === "string") {
304
+ const stackToMount = handler.getStackWithPrefix(prefix);
305
+ this.stack.push(...stackToMount);
306
+ this.logger.log(
307
+ `Router mounted: path=${prefix}, layers=${stackToMount.length}`
308
+ );
309
+ }
310
+ continue;
311
+ }
312
+ this.stack.push({
313
+ path: prefix,
314
+ handler,
315
+ isTool: false,
316
+ isAgent: true
317
+ // Mark as an agent
318
+ });
319
+ this.logger.log(`Agent registered: path=${prefix}`);
320
+ }
321
+ return this;
322
+ }
323
+ /**
324
+ * Mounts a middleware function or another AiAgentKit router at a specific path.
325
+ * This is the primary method for composing routers and applying cross-cutting middleware.
326
+ *
327
+ * @param path The path prefix to mount the handler on.
328
+ * @param handler The middleware function or AiAgentKit router instance to mount.
329
+ */
330
+ use(mountPathArg, handler) {
331
+ if (mountPathArg instanceof RegExp && handler instanceof _AiRouter) {
332
+ throw new AiKitError(
333
+ "[AiAgentKit] Mounting a router on a RegExp path is not supported."
334
+ );
335
+ }
336
+ if (handler instanceof _AiRouter) {
337
+ const router = handler;
338
+ const mountPath = mountPathArg.toString().replace(/\/$/, "");
339
+ router.stack.forEach((layer) => {
340
+ const layerPath = layer.path.toString();
341
+ const relativeLayerPath = layerPath.startsWith("/") ? layerPath.substring(1) : layerPath;
342
+ const newPath = import_path.default.posix.join(mountPath, relativeLayerPath);
343
+ this.stack.push({ ...layer, path: newPath });
344
+ });
345
+ router.actAsToolDefinitions.forEach((value, key) => {
346
+ const keyPath = key.toString();
347
+ const relativeKeyPath = keyPath.startsWith("/") ? keyPath.substring(1) : keyPath;
348
+ const newKey = import_path.default.posix.join(mountPath, relativeKeyPath);
349
+ this.actAsToolDefinitions.set(newKey, value);
350
+ });
351
+ } else {
352
+ this.stack.push({
353
+ path: mountPathArg,
354
+ handler,
355
+ isTool: false,
356
+ isAgent: false
357
+ // Middleware is not a terminal agent
358
+ });
359
+ }
360
+ return this;
361
+ }
362
+ /**
363
+ * Pre-defines the schema and description for an agent when it is used as a tool by an LLM.
364
+ * This allows `next.agentAsTool()` to create a valid `Tool` object without needing the definition at call time.
365
+ * @param path The path of the agent being defined.
366
+ * @param options The tool definition, including a Zod schema and description.
367
+ */
368
+ actAsTool(path2, options) {
369
+ this.actAsToolDefinitions.set(path2, options);
370
+ return this;
371
+ }
372
+ // Implementation
373
+ tool(path2, optionsOrFactory, handler) {
374
+ this.logger.log(`[AiAgentKit][tool] Registering tool at path:`, path2);
375
+ if (this.stack.some((l) => l.isTool && l.path === path2)) {
376
+ this.logger.error(
377
+ `[AiAgentKit][tool] Tool already registered for path: ${path2}`
378
+ );
379
+ throw new AiKitError(`A tool is already registered for path: ${path2}`);
380
+ }
381
+ if (typeof optionsOrFactory === "function" && !handler) {
382
+ const factory = optionsOrFactory;
383
+ const isDynamicPath = typeof path2 === "string" && hasDynamicParams(path2);
384
+ const toolMiddleware = async (ctx, _next) => {
385
+ this.logger.log(
386
+ `[Tool Middleware] Executing factory for path "${path2}". Messages in context: ${ctx.request.messages?.length ?? "undefined"}`
387
+ );
388
+ const toolObject = factory(ctx);
389
+ if (!toolObject.execute) {
390
+ throw new AiKitError(
391
+ `[AiAgentKit] Tool from factory at path ${path2} does not have an execute method.`
392
+ );
393
+ }
394
+ const schema = toolObject.inputSchema;
395
+ if (!schema) {
396
+ this.logger.warn(
397
+ `[AiAgentKit][tool] Factory-based tool at path ${path2} has no inputSchema. Executing without params.`
398
+ );
399
+ return toolObject.execute({}, {});
400
+ }
401
+ const parsedParams = schema.safeParse(ctx.request.params);
402
+ if (!parsedParams.success) {
403
+ this.logger.error(
404
+ `[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
405
+ parsedParams.error.message
406
+ );
407
+ throw new ToolValidationError(path2.toString(), parsedParams.error);
408
+ }
409
+ return toolObject.execute(parsedParams.data, {
410
+ messages: (0, import_ai.convertToModelMessages)(ctx.request.messages ?? []),
411
+ toolCallId: "tool-" + (path2.toString()?.split("/").pop() ?? "direct-call") + "-" + (0, import_ai.generateId)()
412
+ });
413
+ };
414
+ this.stack.push({
415
+ path: path2,
416
+ handler: toolMiddleware,
417
+ isTool: true,
418
+ toolOptions: {
419
+ type: "factory",
420
+ factory
421
+ },
422
+ isAgent: false,
423
+ hasDynamicParams: isDynamicPath
424
+ });
425
+ this.logger.log(
426
+ `Tool registered: path=${path2}, type=factory${isDynamicPath ? " (dynamic)" : ""}`
427
+ );
428
+ return this;
429
+ }
430
+ if (typeof optionsOrFactory === "object" && handler) {
431
+ const options = optionsOrFactory;
432
+ const isDynamicPath = typeof path2 === "string" && hasDynamicParams(path2);
433
+ const dynamicParamInfo = isDynamicPath ? parsePathPattern(path2) : null;
434
+ const toolMiddleware = async (ctx, _next) => {
435
+ if (isDynamicPath && typeof path2 === "string") {
436
+ const pathParams = extractPathParams(path2, ctx.request.path || "");
437
+ this.logger.log(
438
+ `[AiAgentKit][tool] Extracted dynamic path params:`,
439
+ pathParams
440
+ );
441
+ if (pathParams) {
442
+ ctx.request.params = {
443
+ ...ctx.request.params,
444
+ ...pathParams
445
+ };
446
+ }
447
+ }
448
+ const parsedParams = options.schema.safeParse(ctx.request.params);
449
+ if (!parsedParams.success) {
450
+ this.logger.error(
451
+ `[AiAgentKit][tool] Tool call validation failed for path: ${path2}:`,
452
+ parsedParams.error.message
453
+ );
454
+ throw new ToolValidationError(path2.toString(), parsedParams.error);
455
+ }
456
+ const staticHandler = handler;
457
+ return staticHandler(ctx, parsedParams.data);
458
+ };
459
+ this.stack.push({
460
+ path: path2,
461
+ handler: toolMiddleware,
462
+ isTool: true,
463
+ toolOptions: {
464
+ type: "static",
465
+ schema: options.schema,
466
+ description: options.description,
467
+ handler
468
+ },
469
+ isAgent: false,
470
+ hasDynamicParams: isDynamicPath,
471
+ paramNames: dynamicParamInfo?.paramNames
472
+ });
473
+ this.logger.log(
474
+ `Tool registered: path=${path2}, type=static${isDynamicPath ? " (dynamic)" : ""}`
475
+ );
476
+ return this;
477
+ }
478
+ this.logger.error(
479
+ `[AiAgentKit][tool] Invalid arguments for tool registration at path: ${path2}`
480
+ );
481
+ throw new AiKitError(
482
+ `Invalid arguments for tool registration at path: ${path2}`
483
+ );
484
+ }
485
+ /**
486
+ * Returns the internal stack of layers. Primarily used for manual router composition.
487
+ * @deprecated Prefer using `.use()` for router composition.
488
+ */
489
+ getStack() {
490
+ return this.stack;
491
+ }
492
+ /**
493
+ * Returns the internal stack with a path prefix applied to each layer.
494
+ * @param prefix The prefix to add to each path.
495
+ * @deprecated Prefer using `.use()` for router composition.
496
+ */
497
+ getStackWithPrefix(prefix) {
498
+ return this.stack.map((layer) => {
499
+ let newPath;
500
+ if (layer.path instanceof RegExp) {
501
+ newPath = new RegExp(prefix.replace(/\\/g, "\\\\") + layer.path.source);
502
+ } else {
503
+ const layerPathStr = layer.path.toString();
504
+ const relativeLayerPath = layerPathStr.startsWith("/") ? layerPathStr.substring(1) : layerPathStr;
505
+ newPath = import_path.default.posix.join(prefix, relativeLayerPath);
506
+ }
507
+ return {
508
+ ...layer,
509
+ path: newPath
510
+ };
511
+ });
512
+ }
513
+ /**
514
+ * Resolves a path based on the parent path and the requested path.
515
+ * - If path starts with `@/`, it's an absolute path from the root.
516
+ * - Otherwise, it's a relative path.
517
+ * @internal
518
+ */
519
+ _resolvePath(parentPath, newPath) {
520
+ if (newPath.startsWith("@/")) {
521
+ return import_path.default.posix.normalize(newPath.substring(1));
522
+ }
523
+ const joinedPath = import_path.default.posix.join(parentPath, newPath);
524
+ return joinedPath;
525
+ }
526
+ /**
527
+ * Creates a new context for an internal agent or tool call.
528
+ * It inherits from the parent context but gets a new logger and call depth.
529
+ * @internal
530
+ */
531
+ _createSubContext(parentCtx, options) {
532
+ const parentDepth = parentCtx.executionContext.callDepth ?? 0;
533
+ const newCallDepth = parentDepth + (options.type === "agent" ? 1 : 0);
534
+ const subContext = {
535
+ ...parentCtx,
536
+ // Deep clone state and execution context to prevent race conditions.
537
+ state: JSON.parse(JSON.stringify(parentCtx.state)),
538
+ executionContext: {
539
+ ...parentCtx.executionContext,
540
+ currentPath: options.path,
541
+ callDepth: newCallDepth
542
+ },
543
+ request: {
544
+ ...parentCtx.request,
545
+ messages: options.messages || parentCtx.request.messages || [],
546
+ params: options.params,
547
+ path: options.path
548
+ // The path to execute
549
+ },
550
+ logger: this._createLogger(
551
+ parentCtx.requestId,
552
+ options.path,
553
+ newCallDepth
554
+ ),
555
+ next: void 0
556
+ // Will be replaced right after
557
+ };
558
+ subContext.executionContext.currentPath = options.path;
559
+ subContext.next = new NextHandler(
560
+ subContext,
561
+ this,
562
+ parentCtx._onExecutionStart,
563
+ parentCtx._onExecutionEnd,
564
+ parentCtx.next
565
+ );
566
+ return subContext;
567
+ }
568
+ /**
569
+ * Creates a new logger instance with a structured prefix.
570
+ * @internal
571
+ */
572
+ _createLogger(requestId, path2, callDepth = 0) {
573
+ const indent = " ".repeat(callDepth);
574
+ const prefix = `${indent}[${path2.toString()}]`;
575
+ const fullPrefix = `[${requestId}]${prefix}`;
576
+ return {
577
+ log: (...args) => this.logger.log(fullPrefix, ...args),
578
+ warn: (...args) => this.logger.warn(fullPrefix, ...args),
579
+ error: (...args) => this.logger.error(fullPrefix, ...args)
580
+ };
581
+ }
582
+ /**
583
+ * Calculates a specificity score for a layer to enable Express-style routing.
584
+ * Higher score means more specific.
585
+ * - Middleware is less specific than an agent/tool.
586
+ * - Deeper paths are more specific.
587
+ * - Static segments are more specific than dynamic segments.
588
+ * @internal
589
+ */
590
+ _getSpecificityScore(layer) {
591
+ const path2 = layer.path.toString();
592
+ let score = 0;
593
+ score += path2.split("/").length * 100;
594
+ score -= (path2.match(/:/g) || []).length * 10;
595
+ if (layer.path instanceof RegExp) {
596
+ score -= 50;
597
+ }
598
+ if (layer.isAgent || layer.isTool) {
599
+ score += 1;
600
+ }
601
+ return score;
602
+ }
603
+ /**
604
+ * The core execution engine. It finds all matching layers for a given path
605
+ * and runs them in a middleware-style chain.
606
+ * @internal
607
+ */
608
+ async _execute(path2, ctx, isInternalCall = false) {
609
+ try {
610
+ const normalizedPath = path2.length > 1 && path2.endsWith("/") ? path2.slice(0, -1) : path2;
611
+ ctx.logger.log(`Executing path. isInternalCall=${isInternalCall}`);
612
+ const layersToRun = this.stack.filter((layer) => {
613
+ let shouldRun = false;
614
+ if (layer.path instanceof RegExp) {
615
+ if (isInternalCall) {
616
+ const exactRegex = new RegExp(`^${layer.path.source}$`);
617
+ shouldRun = exactRegex.test(normalizedPath);
618
+ } else {
619
+ shouldRun = layer.path.test(normalizedPath);
620
+ }
621
+ } else if (typeof layer.path === "string") {
622
+ const layerPath = layer.path;
623
+ if (layerPath === "*") {
624
+ return !isInternalCall;
625
+ }
626
+ const normalizedLayerPath = layerPath.length > 1 && layerPath.endsWith("/") ? layerPath.slice(0, -1) : layerPath;
627
+ const isExactMatch = normalizedPath === normalizedLayerPath;
628
+ if (isInternalCall) {
629
+ if (layer.isTool && layer.hasDynamicParams) {
630
+ shouldRun = extractPathParams(layerPath, normalizedPath) !== null;
631
+ } else {
632
+ shouldRun = isExactMatch;
633
+ }
634
+ } else {
635
+ if (layer.isTool) {
636
+ if (layer.hasDynamicParams) {
637
+ shouldRun = extractPathParams(layerPath, normalizedPath) !== null;
638
+ } else {
639
+ shouldRun = isExactMatch;
640
+ }
641
+ } else if (layer.isAgent) {
642
+ shouldRun = isExactMatch;
643
+ } else {
644
+ shouldRun = normalizedPath.startsWith(normalizedLayerPath);
645
+ }
646
+ }
647
+ }
648
+ if (shouldRun) {
649
+ ctx.logger.log(
650
+ `[AiAgentKit][_execute] Layer MATCH: path=${normalizedPath}, layer.path=${layer.path}, isTool=${layer.isTool}, isAgent=${layer.isAgent}, isInternal=${isInternalCall}`
651
+ );
652
+ }
653
+ return shouldRun;
654
+ });
655
+ layersToRun.sort(
656
+ (a, b) => this._getSpecificityScore(a) - this._getSpecificityScore(b)
657
+ );
658
+ const layerDescriptions = layersToRun.map(
659
+ (l) => `${l.path.toString()} (${l.isTool ? "tool" : l.isAgent ? "agent" : "middleware"})`
660
+ );
661
+ ctx.logger.log(
662
+ `Found ${layersToRun.length} layers to run: [${layerDescriptions.join(
663
+ ", "
664
+ )}]`
665
+ );
666
+ const hasTool = layersToRun.some((l) => l.isTool);
667
+ const hasAgent = layersToRun.some((l) => l.isAgent);
668
+ if (!layersToRun.length) {
669
+ const errorMsg = `No agent or tool found for path: ${normalizedPath}`;
670
+ ctx.logger.error(errorMsg);
671
+ throw new AgentNotFoundError(normalizedPath);
672
+ }
673
+ const dispatch = async (index) => {
674
+ const layer = layersToRun[index];
675
+ if (!layer) {
676
+ return;
677
+ }
678
+ const next = () => dispatch(index + 1);
679
+ const layerPath = typeof layer.path === "string" ? layer.path : layer.path.toString();
680
+ const layerType = layer.isTool ? "tool" : layer.isAgent ? "agent" : "middleware";
681
+ ctx.logger.log(`-> Running ${layerType}: ${layerPath}`);
682
+ try {
683
+ if (ctx._onExecutionStart) {
684
+ ctx._onExecutionStart();
685
+ }
686
+ await layer.handler(ctx, next);
687
+ ctx.logger.log(`<- Finished ${layerType}: ${layerPath}`);
688
+ } catch (err) {
689
+ ctx.logger.error(
690
+ `Error in ${layerType} layer for path: ${layerPath}`,
691
+ err
692
+ );
693
+ throw err;
694
+ } finally {
695
+ if (ctx._onExecutionEnd) {
696
+ ctx._onExecutionEnd();
697
+ }
698
+ }
699
+ };
700
+ await dispatch(0);
701
+ return;
702
+ } finally {
703
+ }
704
+ }
705
+ /**
706
+ * The main public entry point for the router. It handles an incoming request,
707
+ * sets up the response stream, creates the root context, and starts the execution chain.
708
+ *
709
+ * @param path The path of the agent or tool to execute.
710
+ * @param initialContext The initial context for the request, typically containing messages.
711
+ * @returns A standard `Response` object containing the rich UI stream.
712
+ */
713
+ handle(path2, initialContext) {
714
+ this.logger.log(`Handling request for path: ${path2}`);
715
+ const self = this;
716
+ let executionCompletionResolver = null;
717
+ const executionCompletionPromise = new Promise((resolve) => {
718
+ executionCompletionResolver = resolve;
719
+ });
720
+ return (0, import_ai.createUIMessageStreamResponse)({
721
+ stream: (0, import_ai.createUIMessageStream)({
722
+ originalMessages: initialContext.request.messages,
723
+ execute: async ({ writer }) => {
724
+ const streamWriter = new StreamWriter(writer);
725
+ const requestId = (0, import_ai.generateId)();
726
+ const ctx = {
727
+ ...initialContext,
728
+ request: {
729
+ ...initialContext.request,
730
+ path: path2
731
+ // Set the initial path for the root context
732
+ },
733
+ state: {},
734
+ executionContext: { currentPath: path2, callDepth: 0 },
735
+ requestId,
736
+ logger: self._createLogger(requestId, path2, 0),
737
+ response: {
738
+ ...writer,
739
+ writeMessageMetadata: streamWriter.writeMessageMetadata,
740
+ writeCustomTool: streamWriter.writeCustomTool,
741
+ writeObjectAsTool: streamWriter.writeObjectAsTool,
742
+ generateId: import_ai.generateId
743
+ },
744
+ next: void 0,
745
+ // Will be replaced right after
746
+ _onExecutionStart: () => {
747
+ self.pendingExecutions++;
748
+ self.logger.log(
749
+ `[AiAgentKit][lifecycle] Execution started. Pending: ${self.pendingExecutions}`
750
+ );
751
+ },
752
+ _onExecutionEnd: () => {
753
+ self.pendingExecutions--;
754
+ self.logger.log(
755
+ `[AiAgentKit][lifecycle] Execution ended. Pending: ${self.pendingExecutions}`
756
+ );
757
+ if (self.pendingExecutions === 0 && executionCompletionResolver) {
758
+ self.logger.log(
759
+ `[AiAgentKit][lifecycle] All executions finished. Resolving promise.`
760
+ );
761
+ executionCompletionResolver();
762
+ }
763
+ }
764
+ };
765
+ ctx.next = new NextHandler(
766
+ ctx,
767
+ self,
768
+ ctx._onExecutionStart,
769
+ ctx._onExecutionEnd
770
+ );
771
+ ctx._onExecutionStart();
772
+ self.logger.log(
773
+ `[AiAgentKit][lifecycle] Main execution chain started.`
774
+ );
775
+ try {
776
+ await self._execute(path2, ctx);
777
+ } catch (err) {
778
+ ctx.logger.error("Unhandled error in main execution chain", err);
779
+ } finally {
780
+ ctx._onExecutionEnd();
781
+ self.logger.log(
782
+ `[AiAgentKit][lifecycle] Main execution chain finished.`
783
+ );
784
+ }
785
+ await executionCompletionPromise;
786
+ self.logger.log(
787
+ `[AiAgentKit][lifecycle] All executions truly finished. Stream can be safely closed.`
788
+ );
789
+ }
790
+ })
791
+ });
792
+ }
793
+ };
794
+ var NextHandler = class {
795
+ constructor(ctx, router, onExecutionStart, onExecutionEnd, parentNext) {
796
+ this.ctx = ctx;
797
+ this.router = router;
798
+ this.onExecutionStart = onExecutionStart;
799
+ this.onExecutionEnd = onExecutionEnd;
800
+ this.maxCallDepth = this.router.options.maxCallDepth;
801
+ }
802
+ async callAgent(agentPath, params, options) {
803
+ this.onExecutionStart();
804
+ try {
805
+ const currentDepth = this.ctx.executionContext.callDepth ?? 0;
806
+ if (currentDepth >= this.maxCallDepth) {
807
+ const err = new MaxCallDepthExceededError(this.maxCallDepth);
808
+ this.ctx.logger.error(`[callAgent] Aborting. ${err.message}`);
809
+ throw err;
810
+ }
811
+ const parentPath = this.ctx.executionContext.currentPath || "/";
812
+ const resolvedPath = this.router._resolvePath(
813
+ parentPath,
814
+ agentPath
815
+ );
816
+ this.ctx.logger.log(`Calling agent: resolvedPath='${resolvedPath}'`);
817
+ const subContext = this.router._createSubContext(this.ctx, {
818
+ type: "agent",
819
+ path: resolvedPath,
820
+ params,
821
+ messages: this.ctx.request.messages
822
+ });
823
+ const data = await this.router._execute(
824
+ resolvedPath,
825
+ subContext,
826
+ true
827
+ );
828
+ return { ok: true, data };
829
+ } catch (error) {
830
+ this.ctx.logger.error(`[callAgent] Error:`, error);
831
+ return { ok: false, error };
832
+ } finally {
833
+ this.onExecutionEnd();
834
+ }
835
+ }
836
+ async callTool(toolPath, params, options) {
837
+ this.onExecutionStart();
838
+ try {
839
+ const parentPath = this.ctx.executionContext.currentPath || "/";
840
+ const resolvedPath = this.router._resolvePath(
841
+ parentPath,
842
+ toolPath
843
+ );
844
+ this.ctx.logger.log(`Calling tool: resolvedPath='${resolvedPath}'`);
845
+ const subContext = this.router._createSubContext(this.ctx, {
846
+ type: "tool",
847
+ path: resolvedPath,
848
+ params,
849
+ messages: this.ctx.request.messages
850
+ });
851
+ const data = await this.router._execute(
852
+ resolvedPath,
853
+ subContext,
854
+ true
855
+ );
856
+ return { ok: true, data };
857
+ } catch (error) {
858
+ this.ctx.logger.error(`[callTool] Error:`, error);
859
+ return { ok: false, error };
860
+ } finally {
861
+ this.onExecutionEnd();
862
+ }
863
+ }
864
+ attachTool(toolPath, _tool) {
865
+ const parentPath = this.ctx.executionContext.currentPath || "/";
866
+ const resolvedPath = this.router._resolvePath(
867
+ parentPath,
868
+ toolPath
869
+ );
870
+ this.ctx.logger.log(`Attaching tool: resolvedPath='${resolvedPath}'`);
871
+ const layer = this.router.stack.find((l) => {
872
+ if (!l.isTool) return false;
873
+ if (l.path === resolvedPath) return true;
874
+ if (l.hasDynamicParams && typeof l.path === "string") {
875
+ return extractPathParams(l.path, resolvedPath) !== null;
876
+ }
877
+ return false;
878
+ });
879
+ if (!layer || !layer.toolOptions) {
880
+ this.ctx.logger.error(
881
+ `[attachTool] Tool not found at resolved path: ${resolvedPath}`
882
+ );
883
+ throw new ToolNotFoundError(resolvedPath);
884
+ }
885
+ if (layer.toolOptions.type === "factory") {
886
+ return layer.toolOptions.factory(this.ctx);
887
+ }
888
+ const { description, schema } = layer.toolOptions;
889
+ return {
890
+ description,
891
+ inputSchema: schema,
892
+ ..._tool ?? {},
893
+ execute: async (params, options) => {
894
+ if (_tool?.execute) {
895
+ return await _tool.execute?.(params, options);
896
+ }
897
+ const result = await this.callTool(toolPath, params, options);
898
+ if (!result.ok) {
899
+ throw result.error;
900
+ }
901
+ return result.data;
902
+ }
903
+ };
904
+ }
905
+ agentAsTool(agentPath, toolDefinition) {
906
+ const parentPath = this.ctx.executionContext.currentPath || "/";
907
+ const resolvedPath = this.router._resolvePath(
908
+ parentPath,
909
+ agentPath
910
+ );
911
+ this.ctx.logger.log(
912
+ `Wrapping agent as tool: resolvedPath='${resolvedPath}'`
913
+ );
914
+ let preDefined;
915
+ for (const [key, value] of this.router.actAsToolDefinitions) {
916
+ if (typeof key === "string" && extractPathParams(key, resolvedPath) !== null) {
917
+ preDefined = value;
918
+ break;
919
+ }
920
+ if (key instanceof RegExp && key.test(resolvedPath)) {
921
+ preDefined = value;
922
+ break;
923
+ }
924
+ }
925
+ const definition = toolDefinition || preDefined;
926
+ if (!definition) {
927
+ this.ctx.logger.error(
928
+ `[agentAsTool] No definition found for agent at resolved path: ${resolvedPath}`
929
+ );
930
+ throw new AgentDefinitionMissingError(resolvedPath);
931
+ }
932
+ return {
933
+ ...definition,
934
+ execute: async (params, options) => {
935
+ const result = await this.callAgent(agentPath, params, options);
936
+ if (!result.ok) {
937
+ throw result.error;
938
+ }
939
+ return result.data;
940
+ }
941
+ };
942
+ }
943
+ };
944
+ // Annotate the CommonJS export names for ESM import in node:
945
+ 0 && (module.exports = {
946
+ AgentDefinitionMissingError,
947
+ AgentNotFoundError,
948
+ AiKitError,
949
+ AiRouter,
950
+ MaxCallDepthExceededError,
951
+ StreamWriter,
952
+ ToolNotFoundError,
953
+ ToolValidationError,
954
+ findFirstElement,
955
+ findLastElement,
956
+ findLastMessageWith,
957
+ getTextParts,
958
+ getTextPartsContent
959
+ });
960
+ //# sourceMappingURL=index.js.map