@x12i/ai-dispatcher 2.4.1 → 3.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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,11 +17,20 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
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
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.ts
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ AI_DISPATCHER_GATE: () => AI_DISPATCHER_GATE,
23
34
  AiDispatcherError: () => AiDispatcherError,
24
35
  DEFAULT_METADATA_PRIORITY: () => DEFAULT_METADATA_PRIORITY,
25
36
  IMPLEMENTED_AI_PROVIDERS: () => IMPLEMENTED_AI_PROVIDERS,
@@ -41,6 +52,314 @@ var import_openrouter_runtime4 = require("@x12i/openrouter-runtime");
41
52
  // src/create-dispatcher.ts
42
53
  var import_provider_metadata = require("@x12i/provider-metadata");
43
54
 
55
+ // src/ai-input.ts
56
+ var import_node_async_hooks = require("async_hooks");
57
+ var import_core = require("@x12i/ai-input/core");
58
+
59
+ // src/types/providers.ts
60
+ var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
61
+ function isImplementedAiProvider(provider) {
62
+ return IMPLEMENTED_AI_PROVIDERS.includes(provider);
63
+ }
64
+
65
+ // src/errors.ts
66
+ var AiDispatcherError = class extends Error {
67
+ constructor(code, message, details) {
68
+ super(message);
69
+ this.code = code;
70
+ this.details = details;
71
+ this.name = "AiDispatcherError";
72
+ }
73
+ code;
74
+ details;
75
+ };
76
+ function providerNotImplemented(provider) {
77
+ const implemented = [...IMPLEMENTED_AI_PROVIDERS];
78
+ return new AiDispatcherError(
79
+ "PROVIDER_NOT_IMPLEMENTED",
80
+ `AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
81
+ {
82
+ provider,
83
+ implementedProviders: implemented,
84
+ operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
85
+ }
86
+ );
87
+ }
88
+
89
+ // src/ai-input.ts
90
+ var GPT_ENCODINGS = /* @__PURE__ */ new Set(["o200k_base", "cl100k_base", "p50k_base", "p50k_edit", "r50k_base"]);
91
+ var inputStorage = new import_node_async_hooks.AsyncLocalStorage();
92
+ function mergeAiInput(factory, request) {
93
+ if (request === false) return false;
94
+ if (request === void 0) {
95
+ if (factory === false) return false;
96
+ return factory ?? {};
97
+ }
98
+ const base = typeof factory === "object" ? factory : {};
99
+ const { approvals: requestApprovals, ...requestRest } = request;
100
+ return {
101
+ ...base,
102
+ ...requestRest,
103
+ ...requestApprovals !== void 0 ? { approvals: requestApprovals } : base.approvals !== void 0 ? { approvals: base.approvals } : {}
104
+ };
105
+ }
106
+ function visibleInputResolution(track) {
107
+ if (!track.enabled) return { enabled: false, changed: false };
108
+ const baseline = isBaselineCodec(track.codec) && track.representation === "value";
109
+ if (!track.changed && baseline && !track.surfaced) return void 0;
110
+ return {
111
+ enabled: true,
112
+ ...track.model ? { model: track.model } : {},
113
+ mode: track.mode,
114
+ codec: track.codec,
115
+ representation: track.representation,
116
+ approvalSource: track.approvalSource,
117
+ changed: track.changed
118
+ };
119
+ }
120
+ function wrapToolRegistry(tools) {
121
+ const wrapped = {};
122
+ for (const [name, executor] of Object.entries(tools)) {
123
+ if (!executor) continue;
124
+ wrapped[name] = wrapProviderExecutor(executor);
125
+ }
126
+ return wrapped;
127
+ }
128
+ function wrapProviderExecutor(executor) {
129
+ return async (args, context) => {
130
+ const result = await executor(args, context);
131
+ const track = inputStorage.getStore();
132
+ if (!track?.enabled || !track.client) return result;
133
+ return applyEncodedResult(track, result);
134
+ };
135
+ }
136
+ async function applyAiInput(params) {
137
+ const requestSetting = params.request.aiInput;
138
+ delete params.request.aiInput;
139
+ const settings = mergeAiInput(params.factory, requestSetting);
140
+ if (settings === false) return disabledTrack();
141
+ const track = await openTrack(settings, lookupModel(settings.model, params.effectiveModel));
142
+ await encodeRequestText(params.request, track);
143
+ wrapRequestTools(params.request, track);
144
+ return track;
145
+ }
146
+ function disabledTrack() {
147
+ return {
148
+ enabled: false,
149
+ mode: "compact",
150
+ codec: "text",
151
+ representation: "value",
152
+ approvalSource: "baseline",
153
+ changed: false,
154
+ surfaced: false
155
+ };
156
+ }
157
+ async function openTrack(settings, model) {
158
+ const tokenizer = settings.tokenizer ?? "none";
159
+ const mode = settings.mode ?? "compact";
160
+ let tokenCounter;
161
+ try {
162
+ tokenCounter = await loadTokenizer(tokenizer);
163
+ const framing = framingFor(settings);
164
+ const resolved = (0, import_core.resolveSettings)({
165
+ files: [],
166
+ env: {},
167
+ factory: {
168
+ mode,
169
+ codec: settings.codec ?? "auto",
170
+ readableFormat: settings.readableFormat ?? "auto",
171
+ sharing: settings.sharing ?? "auto",
172
+ tokenizer: tokenCounter?.id ?? "none",
173
+ framing,
174
+ ...model ? { model } : {}
175
+ }
176
+ });
177
+ const approvals = settings.approvals;
178
+ const client = (0, import_core.createCoreClient)({
179
+ config: resolved.config,
180
+ explanation: resolved.explanation,
181
+ ...approvals && approvals.length > 0 ? { policy: { approvals: [...approvals] } } : {},
182
+ ...tokenCounter ? { tokenCounter } : {},
183
+ warn: () => void 0
184
+ });
185
+ return {
186
+ enabled: true,
187
+ client,
188
+ ...model ? { model } : {},
189
+ mode,
190
+ codec: "text",
191
+ representation: "value",
192
+ approvalSource: "baseline",
193
+ changed: false,
194
+ surfaced: false
195
+ };
196
+ } catch (error) {
197
+ throw mapAiInputError(error);
198
+ }
199
+ }
200
+ function lookupModel(override, effectiveModel) {
201
+ const explicit = override?.trim();
202
+ const chosen = explicit ? explicit : effectiveModel.trim();
203
+ return chosen.length > 0 ? chosen : void 0;
204
+ }
205
+ function framingFor(settings) {
206
+ if (settings.framing) return settings.framing;
207
+ const tokenizer = settings.tokenizer ?? "none";
208
+ if (tokenizer !== "none" && (settings.approvals?.length ?? 0) > 0) return "self-describing";
209
+ return "raw";
210
+ }
211
+ async function loadTokenizer(tokenizer) {
212
+ if (tokenizer === "none") return void 0;
213
+ if (!tokenizer.startsWith("gpt:")) {
214
+ throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
215
+ code: "INVALID_CONFIG"
216
+ });
217
+ }
218
+ const encoding = tokenizer.slice("gpt:".length);
219
+ if (!GPT_ENCODINGS.has(encoding)) {
220
+ throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
221
+ code: "INVALID_CONFIG"
222
+ });
223
+ }
224
+ const gpt = await import("@x12i/ai-input/tokenizers/gpt");
225
+ return gpt.createGptTokenCounter(encoding);
226
+ }
227
+ async function encodeRequestText(request, track) {
228
+ if (typeof request.prompt === "string") request.prompt = await encodeString(track, request.prompt);
229
+ if (typeof request.system === "string") request.system = await encodeString(track, request.system);
230
+ if (typeof request.instructions === "string") request.instructions = await encodeString(track, request.instructions);
231
+ if (typeof request.input === "string") {
232
+ request.input = await encodeString(track, request.input);
233
+ } else if (Array.isArray(request.input)) {
234
+ for (let index = 0; index < request.input.length; index += 1) {
235
+ const item = request.input[index];
236
+ if (typeof item === "string") {
237
+ request.input[index] = await encodeString(track, item);
238
+ } else if (item) {
239
+ await encodeContentRecord(item, track);
240
+ }
241
+ }
242
+ }
243
+ if (request.messages) {
244
+ for (const message of request.messages) {
245
+ await encodeMessage(message, track);
246
+ }
247
+ }
248
+ }
249
+ async function encodeMessage(message, track) {
250
+ if (typeof message.content === "string") {
251
+ message.content = await encodeString(track, message.content);
252
+ return;
253
+ }
254
+ for (const part of message.content) {
255
+ if (part.type === "text") part.text = await encodeString(track, part.text);
256
+ }
257
+ }
258
+ async function encodeContentRecord(record2, track) {
259
+ if (typeof record2.content === "string") {
260
+ record2.content = await encodeString(track, record2.content);
261
+ return;
262
+ }
263
+ if (!Array.isArray(record2.content)) return;
264
+ for (const part of record2.content) {
265
+ if (!isRecord(part) || typeof part.text !== "string") continue;
266
+ if (part.type === "text" || part.type === "input_text") {
267
+ part.text = await encodeString(track, part.text);
268
+ }
269
+ }
270
+ }
271
+ function wrapRequestTools(request, track) {
272
+ if (!request.functionTools) return;
273
+ request.functionTools = request.functionTools.map((tool) => {
274
+ if (!tool.executor) return tool;
275
+ const executor = tool.executor;
276
+ return {
277
+ ...tool,
278
+ executor: async (args, context) => applyEncodedResult(track, await executor(args, context))
279
+ };
280
+ });
281
+ }
282
+ async function encodeString(track, value) {
283
+ const encoded = await applyEncodedResult(track, value);
284
+ return typeof encoded === "string" ? encoded : value;
285
+ }
286
+ async function applyEncodedResult(track, value) {
287
+ if (!track.enabled || !track.client) return value;
288
+ let prepared;
289
+ try {
290
+ prepared = await track.client.prepare(value);
291
+ } catch (error) {
292
+ const failure = readPackageFailure(error);
293
+ if (failure?.code === "UNSUPPORTED_VALUE") return value;
294
+ throw mapAiInputError(error);
295
+ }
296
+ const wire = unframedBaseline(prepared);
297
+ note(track, prepared, value, wire);
298
+ if (wire === canonicalWire(value)) return value;
299
+ return wire;
300
+ }
301
+ function unframedBaseline(prepared) {
302
+ const baseline = isBaselineCodec(prepared.codec) && prepared.representation === "value";
303
+ if (!baseline || prepared.framing === "raw") return prepared.text;
304
+ const newline = prepared.text.indexOf("\n");
305
+ if (!prepared.text.startsWith("@ai-input/") || newline < 0) return prepared.text;
306
+ return prepared.text.slice(newline + 1);
307
+ }
308
+ function isBaselineCodec(codec) {
309
+ return codec === "json" || codec === "text" || codec === "outline";
310
+ }
311
+ function note(track, prepared, source, wire) {
312
+ const changed = wire !== canonicalWire(source);
313
+ if (changed) track.changed = true;
314
+ const alternate = prepared.codec === "toon" || prepared.representation !== "value";
315
+ if (alternate || !track.surfaced) {
316
+ track.codec = prepared.codec;
317
+ track.representation = prepared.representation;
318
+ track.approvalSource = prepared.receipt.approvalSource;
319
+ track.mode = prepared.mode;
320
+ }
321
+ if (alternate) track.surfaced = true;
322
+ }
323
+ function canonicalWire(value) {
324
+ if (typeof value === "string") return value;
325
+ return JSON.stringify(value);
326
+ }
327
+ function mapAiInputError(error) {
328
+ if (error instanceof AiDispatcherError) return error;
329
+ const failure = readPackageFailure(error);
330
+ if (failure) {
331
+ return new AiDispatcherError("AI_INPUT_FAILED", failure.message, {
332
+ code: failure.code,
333
+ ...failure.path !== void 0 ? { path: failure.path } : {},
334
+ ...failure.details !== void 0 ? { details: failure.details } : {}
335
+ });
336
+ }
337
+ const message = error instanceof Error ? error.message : "Input preparation failed.";
338
+ return new AiDispatcherError("AI_INPUT_FAILED", message);
339
+ }
340
+ function readPackageFailure(error) {
341
+ if ((0, import_core.isAiInputError)(error)) {
342
+ return {
343
+ code: error.code,
344
+ message: error.message,
345
+ ...error.path !== void 0 ? { path: error.path } : {},
346
+ ...error.details !== void 0 ? { details: error.details } : {}
347
+ };
348
+ }
349
+ if (!(error instanceof Error) || error.name !== "AiInputError") return void 0;
350
+ const coded = error;
351
+ if (typeof coded.code !== "string") return void 0;
352
+ return {
353
+ code: coded.code,
354
+ message: error.message,
355
+ ...typeof coded.path === "string" ? { path: coded.path } : {},
356
+ ...coded.details !== void 0 ? { details: coded.details } : {}
357
+ };
358
+ }
359
+ function isRecord(value) {
360
+ return typeof value === "object" && value !== null && !Array.isArray(value);
361
+ }
362
+
44
363
  // src/cache-usage.ts
45
364
  function readCacheTokens(source) {
46
365
  const cached = [];
@@ -89,7 +408,7 @@ function record(value) {
89
408
 
90
409
  // src/json-pointer.ts
91
410
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
92
- function isRecord(value) {
411
+ function isRecord2(value) {
93
412
  return typeof value === "object" && value !== null && !Array.isArray(value);
94
413
  }
95
414
  function unescapeToken(token) {
@@ -116,7 +435,7 @@ function walk(current, tokens) {
116
435
  if (index >= current.length) return [];
117
436
  return walk(current[index], rest);
118
437
  }
119
- if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
438
+ if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
120
439
  return walk(current[head], rest);
121
440
  }
122
441
  function valuesAtPointer(root, pointer) {
@@ -153,7 +472,7 @@ function collectParents(current, tokens) {
153
472
  if (index >= current.length) return [];
154
473
  return collectParents(current[index], rest);
155
474
  }
156
- if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
475
+ if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
157
476
  return collectParents(current[head], rest);
158
477
  }
159
478
  function deletePointer(root, pointer) {
@@ -169,27 +488,27 @@ function deletePointer(root, pointer) {
169
488
  if (index < parent.length) parent.splice(index, 1);
170
489
  continue;
171
490
  }
172
- if (isRecord(parent) && Object.prototype.hasOwnProperty.call(parent, last)) {
491
+ if (isRecord2(parent) && Object.prototype.hasOwnProperty.call(parent, last)) {
173
492
  delete parent[last];
174
493
  }
175
494
  }
176
495
  }
177
496
 
178
497
  // src/reasoning-extract.ts
179
- function isRecord2(value) {
498
+ function isRecord3(value) {
180
499
  return typeof value === "object" && value !== null && !Array.isArray(value);
181
500
  }
182
501
  function textFromValue(value) {
183
502
  if (typeof value === "string") return value;
184
503
  if (Array.isArray(value)) return value.map(textFromValue).join("");
185
- if (!isRecord2(value)) return "";
504
+ if (!isRecord3(value)) return "";
186
505
  if (typeof value.text === "string") return value.text;
187
506
  if (typeof value.summary === "string") return value.summary;
188
507
  return "";
189
508
  }
190
509
  function extractStreamReasoningText(event, response) {
191
510
  if (response.streamReasoningKeys.length > 0) {
192
- if (!isRecord2(event)) return void 0;
511
+ if (!isRecord3(event)) return void 0;
193
512
  for (const key of response.streamReasoningKeys) {
194
513
  const text = textFromValue(event[key]);
195
514
  if (text) return text;
@@ -287,6 +606,8 @@ function applyResolution(response, prepared) {
287
606
  if (prepared.cacheWarnings?.length) {
288
607
  result.warnings = [...result.warnings, ...prepared.cacheWarnings];
289
608
  }
609
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
610
+ if (inputResolution) result.inputResolution = inputResolution;
290
611
  applyCacheUsage(result);
291
612
  const directive = prepared.directive;
292
613
  if (!directive) return result;
@@ -330,7 +651,7 @@ function applyUsage(response, raw, directive) {
330
651
  }
331
652
 
332
653
  // src/decorate-stream.ts
333
- function isRecord3(value) {
654
+ function isRecord4(value) {
334
655
  return typeof value === "object" && value !== null && !Array.isArray(value);
335
656
  }
336
657
  async function* decorateStream(prepared, source) {
@@ -388,11 +709,13 @@ async function* decorateStream(prepared, source) {
388
709
  };
389
710
  }
390
711
  }
391
- const data = isRecord3(event.data) ? { ...event.data } : {};
712
+ const data = isRecord4(event.data) ? { ...event.data } : {};
392
713
  if (splitter) data.text = splitter.answer;
393
714
  if (reasoningText) data.reasoningText = reasoningText;
394
715
  if (prepared.resolution) data.reasoningResolution = prepared.resolution;
395
716
  if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
717
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
718
+ if (inputResolution) data.inputResolution = inputResolution;
396
719
  yield { ...event, data };
397
720
  continue;
398
721
  }
@@ -400,18 +723,20 @@ async function* decorateStream(prepared, source) {
400
723
  }
401
724
  }
402
725
  function withResolution(event, prepared) {
403
- if (!prepared.resolution && !prepared.cacheResolution) return event;
404
- const data = isRecord3(event.data) ? { ...event.data } : {};
726
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
727
+ if (!prepared.resolution && !prepared.cacheResolution && !inputResolution) return event;
728
+ const data = isRecord4(event.data) ? { ...event.data } : {};
405
729
  if (prepared.resolution) data.reasoningResolution = prepared.resolution;
406
730
  if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
731
+ if (inputResolution) data.inputResolution = inputResolution;
407
732
  return { ...event, data };
408
733
  }
409
734
  function textOf(event) {
410
- if (!isRecord3(event.data) || typeof event.data.text !== "string") return "";
735
+ if (!isRecord4(event.data) || typeof event.data.text !== "string") return "";
411
736
  return event.data.text;
412
737
  }
413
738
  function adjustUsageEvent(event, prepared) {
414
- if (!isRecord3(event.data) || !isRecord3(event.data.usage)) return event;
739
+ if (!isRecord4(event.data) || !isRecord4(event.data.usage)) return event;
415
740
  const paths = prepared.directive?.usage?.reasoningTokenPaths;
416
741
  const usage = { ...event.data.usage };
417
742
  let changed = false;
@@ -440,36 +765,6 @@ function adjustUsageEvent(event, prepared) {
440
765
  return { ...event, data: { ...event.data, usage } };
441
766
  }
442
767
 
443
- // src/types/providers.ts
444
- var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
445
- function isImplementedAiProvider(provider) {
446
- return IMPLEMENTED_AI_PROVIDERS.includes(provider);
447
- }
448
-
449
- // src/errors.ts
450
- var AiDispatcherError = class extends Error {
451
- constructor(code, message, details) {
452
- super(message);
453
- this.code = code;
454
- this.details = details;
455
- this.name = "AiDispatcherError";
456
- }
457
- code;
458
- details;
459
- };
460
- function providerNotImplemented(provider) {
461
- const implemented = [...IMPLEMENTED_AI_PROVIDERS];
462
- return new AiDispatcherError(
463
- "PROVIDER_NOT_IMPLEMENTED",
464
- `AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
465
- {
466
- provider,
467
- implementedProviders: implemented,
468
- operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
469
- }
470
- );
471
- }
472
-
473
768
  // src/mcp.ts
474
769
  var TOOL_TOKEN = /^[A-Za-z0-9_-]+$/;
475
770
  var MAX_EXPOSED_NAME_LENGTH = 64;
@@ -491,7 +786,7 @@ function indexMcpServers(servers) {
491
786
  for (const server of servers) {
492
787
  assertServer(server, seenServers);
493
788
  for (const tool of server.tools) {
494
- if (!isRecord4(tool)) {
789
+ if (!isRecord5(tool)) {
495
790
  throw new AiDispatcherError(
496
791
  "MCP_TOOL_NAME_INVALID",
497
792
  `MCP server "${server.id}" has a tool that is not an object.`
@@ -578,18 +873,17 @@ function applyMcpCatalog(params) {
578
873
  function createMcpExecutor(server, rawName) {
579
874
  return async (args, context) => {
580
875
  const result = await server.callTool(rawName, toArgs(args), context);
581
- return flattenMcpResult(result);
876
+ return mcpModelValue(result);
582
877
  };
583
878
  }
584
- function flattenMcpResult(result) {
585
- if (typeof result === "string") return result;
586
- if (!isRecord4(result)) return stringify(result);
879
+ function mcpModelValue(result) {
880
+ if (typeof result === "string" || !isRecord5(result)) return result;
587
881
  const text = textContent(result.content);
588
882
  if (result.isError === true) {
589
883
  throw new Error(text ?? stringify(result));
590
884
  }
591
885
  if (text !== void 0) return text;
592
- return stringify(result);
886
+ return result;
593
887
  }
594
888
  function resolveAllowlist(filter, tools) {
595
889
  if (filter === void 0) return void 0;
@@ -608,7 +902,7 @@ function resolveAllowlist(filter, tools) {
608
902
  return new Set(filter);
609
903
  }
610
904
  function assertServer(server, seen) {
611
- if (!isRecord4(server) || typeof server.id !== "string" || server.id.length === 0 || !Array.isArray(server.tools) || typeof server.callTool !== "function") {
905
+ if (!isRecord5(server) || typeof server.id !== "string" || server.id.length === 0 || !Array.isArray(server.tools) || typeof server.callTool !== "function") {
612
906
  throw new AiDispatcherError(
613
907
  "MCP_SERVER_INVALID",
614
908
  "An MCP server needs an id, a tools array, and a callTool function."
@@ -653,7 +947,7 @@ function toolDescription(tool) {
653
947
  }
654
948
  function toolParameters(tool) {
655
949
  if (tool.inputSchema === void 0) return { ...DEFAULT_PARAMETERS, properties: {} };
656
- if (!isRecord4(tool.inputSchema)) {
950
+ if (!isRecord5(tool.inputSchema)) {
657
951
  throw new AiDispatcherError(
658
952
  "MCP_TOOL_NAME_INVALID",
659
953
  `MCP tool "${tool.name}" inputSchema must be an object.`
@@ -665,14 +959,14 @@ function textContent(content) {
665
959
  if (!Array.isArray(content)) return void 0;
666
960
  const parts = [];
667
961
  for (const part of content) {
668
- if (!isRecord4(part) || part.type !== "text" || typeof part.text !== "string") continue;
962
+ if (!isRecord5(part) || part.type !== "text" || typeof part.text !== "string") continue;
669
963
  parts.push(part.text);
670
964
  }
671
965
  if (!parts.length) return void 0;
672
966
  return parts.join("\n");
673
967
  }
674
968
  function toArgs(args) {
675
- if (isRecord4(args)) return args;
969
+ if (isRecord5(args)) return args;
676
970
  return {};
677
971
  }
678
972
  function listFailed(serverId, error) {
@@ -684,7 +978,7 @@ function stringify(result) {
684
978
  const serialized = JSON.stringify(result);
685
979
  return serialized === void 0 ? "null" : serialized;
686
980
  }
687
- function isRecord4(value) {
981
+ function isRecord5(value) {
688
982
  return typeof value === "object" && value !== null && !Array.isArray(value);
689
983
  }
690
984
 
@@ -1240,7 +1534,7 @@ function compileCloudflareRequest(request, options, call, streaming) {
1240
1534
  if (call?.finalizeBody) body = call.finalizeBody(body);
1241
1535
  if (endpoint !== "run") body = { ...body, stream: streaming };
1242
1536
  const gateway = mergeGateway(options.gateway, call?.cloudflare?.gateway);
1243
- const fromBody = isRecord5(body.metadata) ? body.metadata : void 0;
1537
+ const fromBody = isRecord6(body.metadata) ? body.metadata : void 0;
1244
1538
  const packed = (0, import_openrouter_runtime.packProviderRecord)(
1245
1539
  request.metadata,
1246
1540
  request.agentId,
@@ -1494,7 +1788,7 @@ function messagesFromInput(request) {
1494
1788
  messages.push({ role: "user", content: item });
1495
1789
  continue;
1496
1790
  }
1497
- if (!isRecord5(item)) continue;
1791
+ if (!isRecord6(item)) continue;
1498
1792
  const role = item.role === "assistant" || item.role === "tool" || item.role === "system" ? item.role : "user";
1499
1793
  if (role === "system") continue;
1500
1794
  const content = inputContent(item.content);
@@ -1511,12 +1805,12 @@ function inputContent(content) {
1511
1805
  if (!Array.isArray(content)) return void 0;
1512
1806
  const parts = [];
1513
1807
  for (const part of content) {
1514
- if (!isRecord5(part)) continue;
1808
+ if (!isRecord6(part)) continue;
1515
1809
  if ((part.type === "text" || part.type === "input_text") && typeof part.text === "string") {
1516
1810
  parts.push({ type: "text", text: part.text });
1517
1811
  } else if (part.type === "image_url" && typeof part.imageUrl === "string") {
1518
1812
  parts.push({ type: "image_url", imageUrl: part.imageUrl });
1519
- } else if (part.type === "image_url" && isRecord5(part.image_url) && typeof part.image_url.url === "string") {
1813
+ } else if (part.type === "image_url" && isRecord6(part.image_url) && typeof part.image_url.url === "string") {
1520
1814
  parts.push({ type: "image_url", imageUrl: part.image_url.url });
1521
1815
  } else if (part.type === "input_image" && typeof part.image_url === "string") {
1522
1816
  parts.push({ type: "image_url", imageUrl: part.image_url });
@@ -1735,7 +2029,7 @@ function continueMessages(body, response, results) {
1735
2029
  return { ...body, messages };
1736
2030
  }
1737
2031
  function continueRun(body, response, results) {
1738
- const input = isRecord5(body.input) ? body.input : {};
2032
+ const input = isRecord6(body.input) ? body.input : {};
1739
2033
  const continued = continueChat({ messages: input.messages }, response, results);
1740
2034
  return { ...body, input: { ...input, messages: continued.messages } };
1741
2035
  }
@@ -1824,7 +2118,7 @@ async function* streamCloudflare(request, options, call) {
1824
2118
  throw failure;
1825
2119
  }
1826
2120
  for await (const event of parseSse(response)) {
1827
- if (!isRecord5(event)) continue;
2121
+ if (!isRecord6(event)) continue;
1828
2122
  const catalogReasoning = call?.extractStreamReasoning?.(event);
1829
2123
  const reasoningText = catalogReasoning || reasoningDelta(event);
1830
2124
  if (reasoningText) yield { type: "stream.reasoning.delta", requestId, data: { text: reasoningText } };
@@ -1887,7 +2181,7 @@ async function sendOnce(compiled, options, timeoutMs) {
1887
2181
  if (!response.ok) throw await readFailure(response);
1888
2182
  const text = await response.text();
1889
2183
  const parsed = text ? JSON.parse(text) : {};
1890
- if (isRecord5(parsed) && parsed.success === false) {
2184
+ if (isRecord6(parsed) && parsed.success === false) {
1891
2185
  throw new AiDispatcherError(
1892
2186
  "PROVIDER_REQUEST_FAILED",
1893
2187
  messageFrom(parsed) ?? "Cloudflare request failed.",
@@ -1938,7 +2232,7 @@ function isConfigError(code) {
1938
2232
  }
1939
2233
  function asTerminalError(error) {
1940
2234
  if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
1941
- const status = isRecord5(error.details) ? error.details.status : void 0;
2235
+ const status = isRecord6(error.details) ? error.details.status : void 0;
1942
2236
  if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
1943
2237
  return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
1944
2238
  }
@@ -1948,7 +2242,7 @@ function asTerminalError(error) {
1948
2242
  function normalizeCloudflareResponse(params) {
1949
2243
  const payload = unwrap(params.response);
1950
2244
  const calls = params.functionTools ?? extractCalls(params.compiled.endpoint, params.response);
1951
- const usage = normalizeUsage(payload.usage) ?? normalizeUsage(isRecord5(params.response) ? params.response.usage : void 0);
2245
+ const usage = normalizeUsage(payload.usage) ?? normalizeUsage(isRecord6(params.response) ? params.response.usage : void 0);
1952
2246
  const status = params.status ?? (calls.length ? "requires_action" : "completed");
1953
2247
  return {
1954
2248
  id: typeof payload.id === "string" ? payload.id : params.requestId,
@@ -1981,16 +2275,16 @@ function answerText(response) {
1981
2275
  if (chat) return chat;
1982
2276
  const anthropic = anthropicAnswer(response);
1983
2277
  if (anthropic) return anthropic;
1984
- if (isRecord5(response.result)) return answerText(response.result);
2278
+ if (isRecord6(response.result)) return answerText(response.result);
1985
2279
  return "";
1986
2280
  }
1987
2281
  function responsesAnswer(response) {
1988
2282
  if (!Array.isArray(response.output)) return "";
1989
2283
  const parts = [];
1990
2284
  for (const item of response.output) {
1991
- if (!isRecord5(item) || item.type !== "message" || !Array.isArray(item.content)) continue;
2285
+ if (!isRecord6(item) || item.type !== "message" || !Array.isArray(item.content)) continue;
1992
2286
  for (const part of item.content) {
1993
- if (isRecord5(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
2287
+ if (isRecord6(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
1994
2288
  }
1995
2289
  }
1996
2290
  return parts.join("");
@@ -2001,16 +2295,16 @@ function chatAnswer(response) {
2001
2295
  const content = message.content;
2002
2296
  if (typeof content === "string") return content;
2003
2297
  if (!Array.isArray(content)) return "";
2004
- return content.map((part) => isRecord5(part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2298
+ return content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2005
2299
  }
2006
2300
  function anthropicAnswer(response) {
2007
2301
  if (!Array.isArray(response.content)) return "";
2008
- return response.content.map((part) => isRecord5(part) && part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2302
+ return response.content.map((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2009
2303
  }
2010
2304
  function chatAssistantMessage(response) {
2011
- if (!Array.isArray(response.choices) || !isRecord5(response.choices[0])) return void 0;
2305
+ if (!Array.isArray(response.choices) || !isRecord6(response.choices[0])) return void 0;
2012
2306
  const message = response.choices[0].message;
2013
- return isRecord5(message) ? message : void 0;
2307
+ return isRecord6(message) ? message : void 0;
2014
2308
  }
2015
2309
  function extractCalls(endpoint, response) {
2016
2310
  const record2 = unwrap(response);
@@ -2022,7 +2316,7 @@ function responsesCalls(response) {
2022
2316
  if (!Array.isArray(response.output)) return [];
2023
2317
  const calls = [];
2024
2318
  for (const item of response.output) {
2025
- if (!isRecord5(item) || item.type !== "function_call") continue;
2319
+ if (!isRecord6(item) || item.type !== "function_call") continue;
2026
2320
  calls.push(callUsage(
2027
2321
  typeof item.name === "string" ? item.name : "function",
2028
2322
  typeof item.call_id === "string" ? item.call_id : "function",
@@ -2036,7 +2330,7 @@ function chatCalls(response) {
2036
2330
  if (!message || !Array.isArray(message.tool_calls)) return [];
2037
2331
  const calls = [];
2038
2332
  for (const item of message.tool_calls) {
2039
- if (!isRecord5(item) || !isRecord5(item.function)) continue;
2333
+ if (!isRecord6(item) || !isRecord6(item.function)) continue;
2040
2334
  const name = typeof item.function.name === "string" ? item.function.name : "function";
2041
2335
  calls.push(callUsage(
2042
2336
  name,
@@ -2050,7 +2344,7 @@ function anthropicCalls(response) {
2050
2344
  if (!Array.isArray(response.content)) return [];
2051
2345
  const calls = [];
2052
2346
  for (const item of response.content) {
2053
- if (!isRecord5(item) || item.type !== "tool_use") continue;
2347
+ if (!isRecord6(item) || item.type !== "tool_use") continue;
2054
2348
  const name = typeof item.name === "string" ? item.name : "function";
2055
2349
  calls.push({
2056
2350
  name,
@@ -2089,7 +2383,7 @@ function emptyToolUsage(calls) {
2089
2383
  };
2090
2384
  }
2091
2385
  function normalizeUsage(usage) {
2092
- if (!isRecord5(usage)) return void 0;
2386
+ if (!isRecord6(usage)) return void 0;
2093
2387
  const inputTokens = numberValue(usage.input_tokens) ?? numberValue(usage.prompt_tokens);
2094
2388
  const outputTokens = numberValue(usage.output_tokens) ?? numberValue(usage.completion_tokens);
2095
2389
  const totalTokens = numberValue(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
@@ -2140,7 +2434,7 @@ function errorEvent(requestId, error) {
2140
2434
  }
2141
2435
  function textDelta(event) {
2142
2436
  if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
2143
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "text_delta" && typeof event.delta.text === "string" && event.delta.text) {
2437
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "text_delta" && typeof event.delta.text === "string" && event.delta.text) {
2144
2438
  return event.delta.text;
2145
2439
  }
2146
2440
  const choice = firstChoiceDelta(event);
@@ -2149,7 +2443,7 @@ function textDelta(event) {
2149
2443
  }
2150
2444
  function reasoningDelta(event) {
2151
2445
  if (event.type === "response.reasoning_summary_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
2152
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "thinking_delta" && typeof event.delta.thinking === "string" && event.delta.thinking) {
2446
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "thinking_delta" && typeof event.delta.thinking === "string" && event.delta.thinking) {
2153
2447
  return event.delta.thinking;
2154
2448
  }
2155
2449
  const choice = firstChoiceDelta(event);
@@ -2166,13 +2460,13 @@ function toolCallDelta(event) {
2166
2460
  argumentsDelta: event.delta
2167
2461
  };
2168
2462
  }
2169
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
2463
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
2170
2464
  return { index: typeof event.index === "number" ? event.index : 0, argumentsDelta: event.delta.partial_json };
2171
2465
  }
2172
2466
  const choice = firstChoiceDelta(event);
2173
- if (!choice || !Array.isArray(choice.tool_calls) || !isRecord5(choice.tool_calls[0])) return void 0;
2467
+ if (!choice || !Array.isArray(choice.tool_calls) || !isRecord6(choice.tool_calls[0])) return void 0;
2174
2468
  const tool = choice.tool_calls[0];
2175
- const fn = isRecord5(tool.function) ? tool.function : void 0;
2469
+ const fn = isRecord6(tool.function) ? tool.function : void 0;
2176
2470
  const args = fn && typeof fn.arguments === "string" ? fn.arguments : void 0;
2177
2471
  if (!args && !(fn && typeof fn.name === "string")) return void 0;
2178
2472
  return {
@@ -2182,18 +2476,18 @@ function toolCallDelta(event) {
2182
2476
  };
2183
2477
  }
2184
2478
  function streamUsage(event) {
2185
- if (event.type === "response.completed" && isRecord5(event.response)) return normalizeUsage(event.response.usage);
2186
- if (isRecord5(event.usage)) return normalizeUsage(event.usage);
2479
+ if (event.type === "response.completed" && isRecord6(event.response)) return normalizeUsage(event.response.usage);
2480
+ if (isRecord6(event.usage)) return normalizeUsage(event.usage);
2187
2481
  return void 0;
2188
2482
  }
2189
2483
  function firstChoiceDelta(event) {
2190
- if (!Array.isArray(event.choices) || !isRecord5(event.choices[0])) return void 0;
2484
+ if (!Array.isArray(event.choices) || !isRecord6(event.choices[0])) return void 0;
2191
2485
  const delta = event.choices[0].delta;
2192
- return isRecord5(delta) ? delta : void 0;
2486
+ return isRecord6(delta) ? delta : void 0;
2193
2487
  }
2194
2488
  function unwrap(response) {
2195
- if (!isRecord5(response)) return {};
2196
- if (response.success === true && isRecord5(response.result)) return response.result;
2489
+ if (!isRecord6(response)) return {};
2490
+ if (response.success === true && isRecord6(response.result)) return response.result;
2197
2491
  return response;
2198
2492
  }
2199
2493
  function modelOf(compiled) {
@@ -2201,12 +2495,12 @@ function modelOf(compiled) {
2201
2495
  return "";
2202
2496
  }
2203
2497
  function messageFrom(body) {
2204
- if (!isRecord5(body)) return void 0;
2498
+ if (!isRecord6(body)) return void 0;
2205
2499
  if (typeof body.message === "string") return body.message;
2206
- if (isRecord5(body.error) && typeof body.error.message === "string") return body.error.message;
2500
+ if (isRecord6(body.error) && typeof body.error.message === "string") return body.error.message;
2207
2501
  if (Array.isArray(body.errors)) {
2208
2502
  for (const error of body.errors) {
2209
- if (isRecord5(error) && typeof error.message === "string") return error.message;
2503
+ if (isRecord6(error) && typeof error.message === "string") return error.message;
2210
2504
  }
2211
2505
  }
2212
2506
  return void 0;
@@ -2347,7 +2641,7 @@ function readEnv(name) {
2347
2641
  if (typeof process === "undefined") return void 0;
2348
2642
  return process.env[name];
2349
2643
  }
2350
- function isRecord5(value) {
2644
+ function isRecord6(value) {
2351
2645
  return typeof value === "object" && value !== null && !Array.isArray(value);
2352
2646
  }
2353
2647
 
@@ -2357,6 +2651,7 @@ function prepareDispatchRequest(params) {
2357
2651
  applyRoleStamp(cloned);
2358
2652
  applyDispatchIdentity(cloned);
2359
2653
  absorbOverrideMetadata(cloned);
2654
+ applyDispatchGate(cloned);
2360
2655
  const provider = params.provider;
2361
2656
  const reasoningEffort = cloned.reasoningEffort;
2362
2657
  const bedrockControls = cloned.bedrockControls;
@@ -2555,7 +2850,7 @@ function settleNested(policy, kind, parentEffort) {
2555
2850
  if (kind === "fusion") next.judgeModel = directive.model;
2556
2851
  else next.model = directive.model;
2557
2852
  }
2558
- if (isRecord6(directive.body.reasoning)) next.reasoning = directive.body.reasoning;
2853
+ if (isRecord7(directive.body.reasoning)) next.reasoning = directive.body.reasoning;
2559
2854
  return next;
2560
2855
  }
2561
2856
  function applyPromptPrefix(request, prefix) {
@@ -2605,7 +2900,7 @@ function prefixInputItems(items, prefix) {
2605
2900
  }
2606
2901
  if (!Array.isArray(item.content)) continue;
2607
2902
  for (const part of item.content) {
2608
- if (!isRecord6(part) || typeof part.text !== "string") continue;
2903
+ if (!isRecord7(part) || typeof part.text !== "string") continue;
2609
2904
  if (part.type === "text" || part.type === "input_text") {
2610
2905
  part.text = prefix + part.text;
2611
2906
  return true;
@@ -2666,7 +2961,7 @@ var ROLE_STAMP_FIELDS = ["role", "roleLayer", "roleDigest", "profileChoice"];
2666
2961
  function applyRoleStamp(request) {
2667
2962
  const stamp = request.role;
2668
2963
  delete request.role;
2669
- if (!isRecord6(stamp)) return;
2964
+ if (!isRecord7(stamp)) return;
2670
2965
  const identity = {};
2671
2966
  for (const field of ROLE_STAMP_FIELDS) {
2672
2967
  const value = stamp[field];
@@ -2681,8 +2976,8 @@ function applyRoleStamp(request) {
2681
2976
  }
2682
2977
  function absorbOverrideMetadata(request) {
2683
2978
  const overrides = request.rawOpenRouterOverrides;
2684
- if (!isRecord6(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
2685
- if (isRecord6(overrides.metadata)) {
2979
+ if (!isRecord7(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
2980
+ if (isRecord7(overrides.metadata)) {
2686
2981
  const current = { ...request.metadata ?? {} };
2687
2982
  const extra = {};
2688
2983
  for (const [key, value] of Object.entries(overrides.metadata)) {
@@ -2709,6 +3004,12 @@ function applyDispatchIdentity(request) {
2709
3004
  for (const key of Object.keys(identity)) delete rest[key];
2710
3005
  request.metadata = { ...identity, ...rest };
2711
3006
  }
3007
+ var AI_DISPATCHER_GATE = "ai-dispatcher";
3008
+ function applyDispatchGate(request) {
3009
+ const rest = { ...request.metadata ?? {} };
3010
+ delete rest["xtivix.gate"];
3011
+ request.metadata = { "xtivix.gate": AI_DISPATCHER_GATE, ...rest };
3012
+ }
2712
3013
  function cloneRequest(request) {
2713
3014
  const functionTools = request.functionTools?.map((tool) => ({ ...tool }));
2714
3015
  const cloned = JSON.parse(JSON.stringify(request));
@@ -2721,7 +3022,7 @@ function hasOwnKeys(value) {
2721
3022
  function hasStreamMetadata(directive) {
2722
3023
  return directive.response.streamReasoningKeys.length > 0 || directive.response.streamReasoningPaths.length > 0;
2723
3024
  }
2724
- function isRecord6(value) {
3025
+ function isRecord7(value) {
2725
3026
  return typeof value === "object" && value !== null && !Array.isArray(value);
2726
3027
  }
2727
3028
  function readEnv2(name) {
@@ -3040,7 +3341,7 @@ async function runOpenAi(request, options, call, requestId) {
3040
3341
  let response;
3041
3342
  for (let iteration = 0; iteration <= maxIterations; iteration += 1) {
3042
3343
  response = await sendWithRetries2(active, options, timeoutFor2(request, options));
3043
- const calls = functionCalls(isRecord7(response) ? response : {});
3344
+ const calls = functionCalls(isRecord8(response) ? response : {});
3044
3345
  if (!calls.length) break;
3045
3346
  if (!calls.every((item) => resolveExecutor2(item.name, request, options))) {
3046
3347
  return normalizeOpenAiResponse({
@@ -3060,7 +3361,7 @@ async function runOpenAi(request, options, call, requestId) {
3060
3361
  results.push(await executeOpenAiTool(item, request, options));
3061
3362
  }
3062
3363
  functionUsage.push(...results);
3063
- const record2 = isRecord7(response) ? response : {};
3364
+ const record2 = isRecord8(response) ? response : {};
3064
3365
  active = {
3065
3366
  ...active,
3066
3367
  body: clean2({
@@ -3175,7 +3476,7 @@ async function* streamOpenAi(request, options, call) {
3175
3476
  throw failure;
3176
3477
  }
3177
3478
  for await (const event of parseSse2(response)) {
3178
- if (!isRecord7(event)) continue;
3479
+ if (!isRecord8(event)) continue;
3179
3480
  if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) {
3180
3481
  text += event.delta;
3181
3482
  yield { type: "stream.text.delta", requestId, data: { text: event.delta } };
@@ -3194,7 +3495,7 @@ async function* streamOpenAi(request, options, call) {
3194
3495
  argumentsDelta: event.delta
3195
3496
  }
3196
3497
  };
3197
- } else if (event.type === "response.completed" && isRecord7(event.response)) {
3498
+ } else if (event.type === "response.completed" && isRecord8(event.response)) {
3198
3499
  const usage = normalizeUsage2(event.response.usage);
3199
3500
  if (usage) yield { type: "stream.usage", requestId, data: { usage } };
3200
3501
  } else if (event.type === "error") {
@@ -3288,7 +3589,7 @@ function isConfigError2(code) {
3288
3589
  }
3289
3590
  function asTerminalError2(error) {
3290
3591
  if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
3291
- const status = isRecord7(error.details) ? error.details.status : void 0;
3592
+ const status = isRecord8(error.details) ? error.details.status : void 0;
3292
3593
  if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
3293
3594
  return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
3294
3595
  }
@@ -3296,7 +3597,7 @@ function asTerminalError2(error) {
3296
3597
  return new AiDispatcherError("PROVIDER_REQUEST_FAILED", error instanceof Error ? error.message : String(error));
3297
3598
  }
3298
3599
  function normalizeOpenAiResponse(params) {
3299
- const record2 = isRecord7(params.response) ? params.response : {};
3600
+ const record2 = isRecord8(params.response) ? params.response : {};
3300
3601
  const text = answerText2(record2);
3301
3602
  const calls = params.functionTools ?? functionCalls(record2);
3302
3603
  const usage = normalizeUsage2(record2.usage);
@@ -3323,9 +3624,9 @@ function answerText2(response) {
3323
3624
  if (Array.isArray(output)) {
3324
3625
  const parts = [];
3325
3626
  for (const item of output) {
3326
- if (!isRecord7(item) || item.type === "reasoning" || item.type !== "message" || !Array.isArray(item.content)) continue;
3627
+ if (!isRecord8(item) || item.type === "reasoning" || item.type !== "message" || !Array.isArray(item.content)) continue;
3327
3628
  for (const part of item.content) {
3328
- if (isRecord7(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
3629
+ if (isRecord8(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
3329
3630
  }
3330
3631
  }
3331
3632
  if (parts.length > 0) return parts.join("");
@@ -3336,7 +3637,7 @@ function functionCalls(response) {
3336
3637
  if (!Array.isArray(response.output)) return [];
3337
3638
  const calls = [];
3338
3639
  for (const item of response.output) {
3339
- if (!isRecord7(item) || item.type !== "function_call") continue;
3640
+ if (!isRecord8(item) || item.type !== "function_call") continue;
3340
3641
  const name = typeof item.name === "string" ? item.name : "function";
3341
3642
  const callId = typeof item.call_id === "string" ? item.call_id : name;
3342
3643
  let args = item.arguments;
@@ -3368,7 +3669,7 @@ function toolUsage(calls) {
3368
3669
  };
3369
3670
  }
3370
3671
  function normalizeUsage2(usage) {
3371
- if (!isRecord7(usage)) return void 0;
3672
+ if (!isRecord8(usage)) return void 0;
3372
3673
  const inputTokens = numberValue2(usage.input_tokens);
3373
3674
  const outputTokens = numberValue2(usage.output_tokens);
3374
3675
  const totalTokens = numberValue2(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
@@ -3420,16 +3721,16 @@ function modelOf2(compiled) {
3420
3721
  return typeof compiled.body.model === "string" ? compiled.body.model : "";
3421
3722
  }
3422
3723
  function messageFrom2(body) {
3423
- if (!isRecord7(body)) return void 0;
3724
+ if (!isRecord8(body)) return void 0;
3424
3725
  if (typeof body.message === "string") return body.message;
3425
- if (isRecord7(body.error) && typeof body.error.message === "string") return body.error.message;
3726
+ if (isRecord8(body.error) && typeof body.error.message === "string") return body.error.message;
3426
3727
  return void 0;
3427
3728
  }
3428
3729
  function numberValue2(value) {
3429
3730
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3430
3731
  }
3431
3732
  function attachProviderMetadata(body, request, packer) {
3432
- const fromBody = isRecord7(body.metadata) ? body.metadata : void 0;
3733
+ const fromBody = isRecord8(body.metadata) ? body.metadata : void 0;
3433
3734
  const packed = (0, import_openrouter_runtime2.packProviderRecord)(request.metadata, request.agentId, fromBody, packer);
3434
3735
  if (packed.wire) return { body: { ...body, metadata: packed.wire }, ...packed.warning ? { warning: packed.warning } : {} };
3435
3736
  if (body.metadata === void 0) return { body, ...packed.warning ? { warning: packed.warning } : {} };
@@ -3503,7 +3804,7 @@ function readEnv3(name) {
3503
3804
  if (typeof process === "undefined") return void 0;
3504
3805
  return process.env[name];
3505
3806
  }
3506
- function isRecord7(value) {
3807
+ function isRecord8(value) {
3507
3808
  return typeof value === "object" && value !== null && !Array.isArray(value);
3508
3809
  }
3509
3810
 
@@ -3571,6 +3872,7 @@ function createAiDispatcher(options = {}) {
3571
3872
  if (provider === "openrouter") {
3572
3873
  const openrouterOptions = {
3573
3874
  ...options.openrouter,
3875
+ ...options.openrouter?.tools ? { tools: wrapToolRegistry(options.openrouter.tools) } : {},
3574
3876
  metadataPacker: packer,
3575
3877
  ...options.logger && !options.openrouter?.logger ? { logger: options.logger } : {}
3576
3878
  };
@@ -3579,6 +3881,7 @@ function createAiDispatcher(options = {}) {
3579
3881
  if (provider === "bedrock") {
3580
3882
  const bedrockOptions = {
3581
3883
  ...options.bedrock,
3884
+ ...options.bedrock?.tools ? { tools: wrapToolRegistry(options.bedrock.tools) } : {},
3582
3885
  metadataPacker: packer,
3583
3886
  ...options.logger && !options.bedrock?.logger ? { logger: options.logger } : {}
3584
3887
  };
@@ -3587,6 +3890,7 @@ function createAiDispatcher(options = {}) {
3587
3890
  if (provider === "openai") {
3588
3891
  const openaiOptions = {
3589
3892
  ...options.openai,
3893
+ ...options.openai?.tools ? { tools: wrapToolRegistry(options.openai.tools) } : {},
3590
3894
  ...options.logger && !options.openai?.logger ? { logger: options.logger } : {}
3591
3895
  };
3592
3896
  return createOpenAiProviderAdapter(openaiOptions, packer);
@@ -3594,6 +3898,7 @@ function createAiDispatcher(options = {}) {
3594
3898
  if (provider === "cloudflare") {
3595
3899
  const cloudflareOptions = {
3596
3900
  ...options.cloudflare,
3901
+ ...options.cloudflare?.tools ? { tools: wrapToolRegistry(options.cloudflare.tools) } : {},
3597
3902
  ...options.logger && !options.cloudflare?.logger ? { logger: options.logger } : {}
3598
3903
  };
3599
3904
  return createCloudflareProviderAdapter(cloudflareOptions, packer);
@@ -3615,21 +3920,27 @@ function createAiDispatcher(options = {}) {
3615
3920
  return {
3616
3921
  provider: defaultProvider,
3617
3922
  async run(request) {
3618
- const prepared = prepare(request, true);
3923
+ const prepared = await prepare(request, true);
3619
3924
  log(options, "ai-dispatcher.request.started", prepared, "run", false);
3620
- const response = await getAdapter(prepared.provider).run(prepared.request, callOf(prepared.call));
3925
+ const response = await inputStorage.run(
3926
+ prepared.inputTrack,
3927
+ () => getAdapter(prepared.provider).run(prepared.request, callOf(prepared.call))
3928
+ );
3621
3929
  return applyResolution(response, prepared);
3622
3930
  },
3623
3931
  executeStreamingChat(request) {
3624
- const prepared = prepare(request, false);
3625
- log(options, "ai-dispatcher.request.started", prepared, "executeStreamingChat", true);
3626
- return decorateStream(
3627
- prepared,
3628
- getAdapter(prepared.provider).executeStreamingChat(prepared.request, callOf(prepared.call))
3629
- );
3932
+ return (async function* () {
3933
+ const prepared = await prepare(request, false);
3934
+ log(options, "ai-dispatcher.request.started", prepared, "executeStreamingChat", true);
3935
+ if (prepared.inputTrack) inputStorage.enterWith(prepared.inputTrack);
3936
+ yield* decorateStream(
3937
+ prepared,
3938
+ getAdapter(prepared.provider).executeStreamingChat(prepared.request, callOf(prepared.call))
3939
+ );
3940
+ })();
3630
3941
  },
3631
- compile(request) {
3632
- const prepared = prepare(request, true);
3942
+ async compile(request) {
3943
+ const prepared = await prepare(request, true);
3633
3944
  log(options, "ai-dispatcher.request.compiled", prepared, "compile", false);
3634
3945
  const compiled = getAdapter(prepared.provider).compile(prepared.request, callOf(prepared.call));
3635
3946
  return attachCompileMeta(prepared, compiled);
@@ -3643,7 +3954,7 @@ function createAiDispatcher(options = {}) {
3643
3954
  return pending;
3644
3955
  }
3645
3956
  };
3646
- function prepare(request, includeMcp) {
3957
+ async function prepare(request, includeMcp) {
3647
3958
  const provider = resolveProvider(request.provider, defaultProvider);
3648
3959
  const prepared = prepareDispatchRequest({ request, provider, options });
3649
3960
  const tools = providerTools(provider, options);
@@ -3653,6 +3964,11 @@ function createAiDispatcher(options = {}) {
3653
3964
  ...tools ? { providerTools: tools } : {},
3654
3965
  includeMcp
3655
3966
  });
3967
+ prepared.inputTrack = await applyAiInput({
3968
+ request: prepared.request,
3969
+ effectiveModel: prepared.effectiveModel,
3970
+ factory: options.aiInput
3971
+ });
3656
3972
  return prepared;
3657
3973
  }
3658
3974
  }
@@ -3670,7 +3986,8 @@ function callOf(call) {
3670
3986
  }
3671
3987
  function attachCompileMeta(prepared, compiled) {
3672
3988
  const record2 = compiled;
3673
- const headers = isRecord8(record2.headers) ? redactHeaders(record2.headers) : void 0;
3989
+ const headers = isRecord9(record2.headers) ? redactHeaders(record2.headers) : void 0;
3990
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
3674
3991
  return {
3675
3992
  ...record2,
3676
3993
  ...headers ? { headers } : {},
@@ -3679,6 +3996,7 @@ function attachCompileMeta(prepared, compiled) {
3679
3996
  effectiveModel: prepared.effectiveModel,
3680
3997
  ...prepared.resolution ? { reasoningResolution: prepared.resolution } : {},
3681
3998
  ...prepared.cacheResolution ? { cacheResolution: prepared.cacheResolution } : {},
3999
+ ...inputResolution ? { inputResolution } : {},
3682
4000
  ...prepared.cacheWarnings?.length ? { warnings: [...Array.isArray(record2.warnings) ? record2.warnings : [], ...prepared.cacheWarnings] } : {}
3683
4001
  };
3684
4002
  }
@@ -3704,7 +4022,7 @@ function log(options, event, prepared, entrypoint, streaming) {
3704
4022
  if (event.endsWith("compiled")) options.logger?.debug?.(event, payload);
3705
4023
  else options.logger?.info?.(event, payload);
3706
4024
  }
3707
- function isRecord8(value) {
4025
+ function isRecord9(value) {
3708
4026
  return typeof value === "object" && value !== null && !Array.isArray(value);
3709
4027
  }
3710
4028
 
@@ -3716,6 +4034,7 @@ function stripReasoningHistoryFields(message, paths) {
3716
4034
  }
3717
4035
  // Annotate the CommonJS export names for ESM import in node:
3718
4036
  0 && (module.exports = {
4037
+ AI_DISPATCHER_GATE,
3719
4038
  AiDispatcherError,
3720
4039
  DEFAULT_METADATA_PRIORITY,
3721
4040
  IMPLEMENTED_AI_PROVIDERS,