@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.js CHANGED
@@ -10,6 +10,318 @@ import {
10
10
  // src/create-dispatcher.ts
11
11
  import { createMetadataPacker } from "@x12i/provider-metadata";
12
12
 
13
+ // src/ai-input.ts
14
+ import { AsyncLocalStorage } from "async_hooks";
15
+ import {
16
+ createCoreClient,
17
+ isAiInputError,
18
+ resolveSettings
19
+ } from "@x12i/ai-input/core";
20
+
21
+ // src/types/providers.ts
22
+ var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
23
+ function isImplementedAiProvider(provider) {
24
+ return IMPLEMENTED_AI_PROVIDERS.includes(provider);
25
+ }
26
+
27
+ // src/errors.ts
28
+ var AiDispatcherError = class extends Error {
29
+ constructor(code, message, details) {
30
+ super(message);
31
+ this.code = code;
32
+ this.details = details;
33
+ this.name = "AiDispatcherError";
34
+ }
35
+ code;
36
+ details;
37
+ };
38
+ function providerNotImplemented(provider) {
39
+ const implemented = [...IMPLEMENTED_AI_PROVIDERS];
40
+ return new AiDispatcherError(
41
+ "PROVIDER_NOT_IMPLEMENTED",
42
+ `AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
43
+ {
44
+ provider,
45
+ implementedProviders: implemented,
46
+ operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
47
+ }
48
+ );
49
+ }
50
+
51
+ // src/ai-input.ts
52
+ var GPT_ENCODINGS = /* @__PURE__ */ new Set(["o200k_base", "cl100k_base", "p50k_base", "p50k_edit", "r50k_base"]);
53
+ var inputStorage = new AsyncLocalStorage();
54
+ function mergeAiInput(factory, request) {
55
+ if (request === false) return false;
56
+ if (request === void 0) {
57
+ if (factory === false) return false;
58
+ return factory ?? {};
59
+ }
60
+ const base = typeof factory === "object" ? factory : {};
61
+ const { approvals: requestApprovals, ...requestRest } = request;
62
+ return {
63
+ ...base,
64
+ ...requestRest,
65
+ ...requestApprovals !== void 0 ? { approvals: requestApprovals } : base.approvals !== void 0 ? { approvals: base.approvals } : {}
66
+ };
67
+ }
68
+ function visibleInputResolution(track) {
69
+ if (!track.enabled) return { enabled: false, changed: false };
70
+ const baseline = isBaselineCodec(track.codec) && track.representation === "value";
71
+ if (!track.changed && baseline && !track.surfaced) return void 0;
72
+ return {
73
+ enabled: true,
74
+ ...track.model ? { model: track.model } : {},
75
+ mode: track.mode,
76
+ codec: track.codec,
77
+ representation: track.representation,
78
+ approvalSource: track.approvalSource,
79
+ changed: track.changed
80
+ };
81
+ }
82
+ function wrapToolRegistry(tools) {
83
+ const wrapped = {};
84
+ for (const [name, executor] of Object.entries(tools)) {
85
+ if (!executor) continue;
86
+ wrapped[name] = wrapProviderExecutor(executor);
87
+ }
88
+ return wrapped;
89
+ }
90
+ function wrapProviderExecutor(executor) {
91
+ return async (args, context) => {
92
+ const result = await executor(args, context);
93
+ const track = inputStorage.getStore();
94
+ if (!track?.enabled || !track.client) return result;
95
+ return applyEncodedResult(track, result);
96
+ };
97
+ }
98
+ async function applyAiInput(params) {
99
+ const requestSetting = params.request.aiInput;
100
+ delete params.request.aiInput;
101
+ const settings = mergeAiInput(params.factory, requestSetting);
102
+ if (settings === false) return disabledTrack();
103
+ const track = await openTrack(settings, lookupModel(settings.model, params.effectiveModel));
104
+ await encodeRequestText(params.request, track);
105
+ wrapRequestTools(params.request, track);
106
+ return track;
107
+ }
108
+ function disabledTrack() {
109
+ return {
110
+ enabled: false,
111
+ mode: "compact",
112
+ codec: "text",
113
+ representation: "value",
114
+ approvalSource: "baseline",
115
+ changed: false,
116
+ surfaced: false
117
+ };
118
+ }
119
+ async function openTrack(settings, model) {
120
+ const tokenizer = settings.tokenizer ?? "none";
121
+ const mode = settings.mode ?? "compact";
122
+ let tokenCounter;
123
+ try {
124
+ tokenCounter = await loadTokenizer(tokenizer);
125
+ const framing = framingFor(settings);
126
+ const resolved = resolveSettings({
127
+ files: [],
128
+ env: {},
129
+ factory: {
130
+ mode,
131
+ codec: settings.codec ?? "auto",
132
+ readableFormat: settings.readableFormat ?? "auto",
133
+ sharing: settings.sharing ?? "auto",
134
+ tokenizer: tokenCounter?.id ?? "none",
135
+ framing,
136
+ ...model ? { model } : {}
137
+ }
138
+ });
139
+ const approvals = settings.approvals;
140
+ const client = createCoreClient({
141
+ config: resolved.config,
142
+ explanation: resolved.explanation,
143
+ ...approvals && approvals.length > 0 ? { policy: { approvals: [...approvals] } } : {},
144
+ ...tokenCounter ? { tokenCounter } : {},
145
+ warn: () => void 0
146
+ });
147
+ return {
148
+ enabled: true,
149
+ client,
150
+ ...model ? { model } : {},
151
+ mode,
152
+ codec: "text",
153
+ representation: "value",
154
+ approvalSource: "baseline",
155
+ changed: false,
156
+ surfaced: false
157
+ };
158
+ } catch (error) {
159
+ throw mapAiInputError(error);
160
+ }
161
+ }
162
+ function lookupModel(override, effectiveModel) {
163
+ const explicit = override?.trim();
164
+ const chosen = explicit ? explicit : effectiveModel.trim();
165
+ return chosen.length > 0 ? chosen : void 0;
166
+ }
167
+ function framingFor(settings) {
168
+ if (settings.framing) return settings.framing;
169
+ const tokenizer = settings.tokenizer ?? "none";
170
+ if (tokenizer !== "none" && (settings.approvals?.length ?? 0) > 0) return "self-describing";
171
+ return "raw";
172
+ }
173
+ async function loadTokenizer(tokenizer) {
174
+ if (tokenizer === "none") return void 0;
175
+ if (!tokenizer.startsWith("gpt:")) {
176
+ throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
177
+ code: "INVALID_CONFIG"
178
+ });
179
+ }
180
+ const encoding = tokenizer.slice("gpt:".length);
181
+ if (!GPT_ENCODINGS.has(encoding)) {
182
+ throw new AiDispatcherError("AI_INPUT_FAILED", `Unsupported tokenizer "${tokenizer}".`, {
183
+ code: "INVALID_CONFIG"
184
+ });
185
+ }
186
+ const gpt = await import("@x12i/ai-input/tokenizers/gpt");
187
+ return gpt.createGptTokenCounter(encoding);
188
+ }
189
+ async function encodeRequestText(request, track) {
190
+ if (typeof request.prompt === "string") request.prompt = await encodeString(track, request.prompt);
191
+ if (typeof request.system === "string") request.system = await encodeString(track, request.system);
192
+ if (typeof request.instructions === "string") request.instructions = await encodeString(track, request.instructions);
193
+ if (typeof request.input === "string") {
194
+ request.input = await encodeString(track, request.input);
195
+ } else if (Array.isArray(request.input)) {
196
+ for (let index = 0; index < request.input.length; index += 1) {
197
+ const item = request.input[index];
198
+ if (typeof item === "string") {
199
+ request.input[index] = await encodeString(track, item);
200
+ } else if (item) {
201
+ await encodeContentRecord(item, track);
202
+ }
203
+ }
204
+ }
205
+ if (request.messages) {
206
+ for (const message of request.messages) {
207
+ await encodeMessage(message, track);
208
+ }
209
+ }
210
+ }
211
+ async function encodeMessage(message, track) {
212
+ if (typeof message.content === "string") {
213
+ message.content = await encodeString(track, message.content);
214
+ return;
215
+ }
216
+ for (const part of message.content) {
217
+ if (part.type === "text") part.text = await encodeString(track, part.text);
218
+ }
219
+ }
220
+ async function encodeContentRecord(record2, track) {
221
+ if (typeof record2.content === "string") {
222
+ record2.content = await encodeString(track, record2.content);
223
+ return;
224
+ }
225
+ if (!Array.isArray(record2.content)) return;
226
+ for (const part of record2.content) {
227
+ if (!isRecord(part) || typeof part.text !== "string") continue;
228
+ if (part.type === "text" || part.type === "input_text") {
229
+ part.text = await encodeString(track, part.text);
230
+ }
231
+ }
232
+ }
233
+ function wrapRequestTools(request, track) {
234
+ if (!request.functionTools) return;
235
+ request.functionTools = request.functionTools.map((tool) => {
236
+ if (!tool.executor) return tool;
237
+ const executor = tool.executor;
238
+ return {
239
+ ...tool,
240
+ executor: async (args, context) => applyEncodedResult(track, await executor(args, context))
241
+ };
242
+ });
243
+ }
244
+ async function encodeString(track, value) {
245
+ const encoded = await applyEncodedResult(track, value);
246
+ return typeof encoded === "string" ? encoded : value;
247
+ }
248
+ async function applyEncodedResult(track, value) {
249
+ if (!track.enabled || !track.client) return value;
250
+ let prepared;
251
+ try {
252
+ prepared = await track.client.prepare(value);
253
+ } catch (error) {
254
+ const failure = readPackageFailure(error);
255
+ if (failure?.code === "UNSUPPORTED_VALUE") return value;
256
+ throw mapAiInputError(error);
257
+ }
258
+ const wire = unframedBaseline(prepared);
259
+ note(track, prepared, value, wire);
260
+ if (wire === canonicalWire(value)) return value;
261
+ return wire;
262
+ }
263
+ function unframedBaseline(prepared) {
264
+ const baseline = isBaselineCodec(prepared.codec) && prepared.representation === "value";
265
+ if (!baseline || prepared.framing === "raw") return prepared.text;
266
+ const newline = prepared.text.indexOf("\n");
267
+ if (!prepared.text.startsWith("@ai-input/") || newline < 0) return prepared.text;
268
+ return prepared.text.slice(newline + 1);
269
+ }
270
+ function isBaselineCodec(codec) {
271
+ return codec === "json" || codec === "text" || codec === "outline";
272
+ }
273
+ function note(track, prepared, source, wire) {
274
+ const changed = wire !== canonicalWire(source);
275
+ if (changed) track.changed = true;
276
+ const alternate = prepared.codec === "toon" || prepared.representation !== "value";
277
+ if (alternate || !track.surfaced) {
278
+ track.codec = prepared.codec;
279
+ track.representation = prepared.representation;
280
+ track.approvalSource = prepared.receipt.approvalSource;
281
+ track.mode = prepared.mode;
282
+ }
283
+ if (alternate) track.surfaced = true;
284
+ }
285
+ function canonicalWire(value) {
286
+ if (typeof value === "string") return value;
287
+ return JSON.stringify(value);
288
+ }
289
+ function mapAiInputError(error) {
290
+ if (error instanceof AiDispatcherError) return error;
291
+ const failure = readPackageFailure(error);
292
+ if (failure) {
293
+ return new AiDispatcherError("AI_INPUT_FAILED", failure.message, {
294
+ code: failure.code,
295
+ ...failure.path !== void 0 ? { path: failure.path } : {},
296
+ ...failure.details !== void 0 ? { details: failure.details } : {}
297
+ });
298
+ }
299
+ const message = error instanceof Error ? error.message : "Input preparation failed.";
300
+ return new AiDispatcherError("AI_INPUT_FAILED", message);
301
+ }
302
+ function readPackageFailure(error) {
303
+ if (isAiInputError(error)) {
304
+ return {
305
+ code: error.code,
306
+ message: error.message,
307
+ ...error.path !== void 0 ? { path: error.path } : {},
308
+ ...error.details !== void 0 ? { details: error.details } : {}
309
+ };
310
+ }
311
+ if (!(error instanceof Error) || error.name !== "AiInputError") return void 0;
312
+ const coded = error;
313
+ if (typeof coded.code !== "string") return void 0;
314
+ return {
315
+ code: coded.code,
316
+ message: error.message,
317
+ ...typeof coded.path === "string" ? { path: coded.path } : {},
318
+ ...coded.details !== void 0 ? { details: coded.details } : {}
319
+ };
320
+ }
321
+ function isRecord(value) {
322
+ return typeof value === "object" && value !== null && !Array.isArray(value);
323
+ }
324
+
13
325
  // src/cache-usage.ts
14
326
  function readCacheTokens(source) {
15
327
  const cached = [];
@@ -58,7 +370,7 @@ function record(value) {
58
370
 
59
371
  // src/json-pointer.ts
60
372
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
61
- function isRecord(value) {
373
+ function isRecord2(value) {
62
374
  return typeof value === "object" && value !== null && !Array.isArray(value);
63
375
  }
64
376
  function unescapeToken(token) {
@@ -85,7 +397,7 @@ function walk(current, tokens) {
85
397
  if (index >= current.length) return [];
86
398
  return walk(current[index], rest);
87
399
  }
88
- if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
400
+ if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
89
401
  return walk(current[head], rest);
90
402
  }
91
403
  function valuesAtPointer(root, pointer) {
@@ -122,7 +434,7 @@ function collectParents(current, tokens) {
122
434
  if (index >= current.length) return [];
123
435
  return collectParents(current[index], rest);
124
436
  }
125
- if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
437
+ if (!isRecord2(current) || !Object.prototype.hasOwnProperty.call(current, head)) return [];
126
438
  return collectParents(current[head], rest);
127
439
  }
128
440
  function deletePointer(root, pointer) {
@@ -138,27 +450,27 @@ function deletePointer(root, pointer) {
138
450
  if (index < parent.length) parent.splice(index, 1);
139
451
  continue;
140
452
  }
141
- if (isRecord(parent) && Object.prototype.hasOwnProperty.call(parent, last)) {
453
+ if (isRecord2(parent) && Object.prototype.hasOwnProperty.call(parent, last)) {
142
454
  delete parent[last];
143
455
  }
144
456
  }
145
457
  }
146
458
 
147
459
  // src/reasoning-extract.ts
148
- function isRecord2(value) {
460
+ function isRecord3(value) {
149
461
  return typeof value === "object" && value !== null && !Array.isArray(value);
150
462
  }
151
463
  function textFromValue(value) {
152
464
  if (typeof value === "string") return value;
153
465
  if (Array.isArray(value)) return value.map(textFromValue).join("");
154
- if (!isRecord2(value)) return "";
466
+ if (!isRecord3(value)) return "";
155
467
  if (typeof value.text === "string") return value.text;
156
468
  if (typeof value.summary === "string") return value.summary;
157
469
  return "";
158
470
  }
159
471
  function extractStreamReasoningText(event, response) {
160
472
  if (response.streamReasoningKeys.length > 0) {
161
- if (!isRecord2(event)) return void 0;
473
+ if (!isRecord3(event)) return void 0;
162
474
  for (const key of response.streamReasoningKeys) {
163
475
  const text = textFromValue(event[key]);
164
476
  if (text) return text;
@@ -256,6 +568,8 @@ function applyResolution(response, prepared) {
256
568
  if (prepared.cacheWarnings?.length) {
257
569
  result.warnings = [...result.warnings, ...prepared.cacheWarnings];
258
570
  }
571
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
572
+ if (inputResolution) result.inputResolution = inputResolution;
259
573
  applyCacheUsage(result);
260
574
  const directive = prepared.directive;
261
575
  if (!directive) return result;
@@ -299,7 +613,7 @@ function applyUsage(response, raw, directive) {
299
613
  }
300
614
 
301
615
  // src/decorate-stream.ts
302
- function isRecord3(value) {
616
+ function isRecord4(value) {
303
617
  return typeof value === "object" && value !== null && !Array.isArray(value);
304
618
  }
305
619
  async function* decorateStream(prepared, source) {
@@ -357,11 +671,13 @@ async function* decorateStream(prepared, source) {
357
671
  };
358
672
  }
359
673
  }
360
- const data = isRecord3(event.data) ? { ...event.data } : {};
674
+ const data = isRecord4(event.data) ? { ...event.data } : {};
361
675
  if (splitter) data.text = splitter.answer;
362
676
  if (reasoningText) data.reasoningText = reasoningText;
363
677
  if (prepared.resolution) data.reasoningResolution = prepared.resolution;
364
678
  if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
679
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
680
+ if (inputResolution) data.inputResolution = inputResolution;
365
681
  yield { ...event, data };
366
682
  continue;
367
683
  }
@@ -369,18 +685,20 @@ async function* decorateStream(prepared, source) {
369
685
  }
370
686
  }
371
687
  function withResolution(event, prepared) {
372
- if (!prepared.resolution && !prepared.cacheResolution) return event;
373
- const data = isRecord3(event.data) ? { ...event.data } : {};
688
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
689
+ if (!prepared.resolution && !prepared.cacheResolution && !inputResolution) return event;
690
+ const data = isRecord4(event.data) ? { ...event.data } : {};
374
691
  if (prepared.resolution) data.reasoningResolution = prepared.resolution;
375
692
  if (prepared.cacheResolution) data.cacheResolution = prepared.cacheResolution;
693
+ if (inputResolution) data.inputResolution = inputResolution;
376
694
  return { ...event, data };
377
695
  }
378
696
  function textOf(event) {
379
- if (!isRecord3(event.data) || typeof event.data.text !== "string") return "";
697
+ if (!isRecord4(event.data) || typeof event.data.text !== "string") return "";
380
698
  return event.data.text;
381
699
  }
382
700
  function adjustUsageEvent(event, prepared) {
383
- if (!isRecord3(event.data) || !isRecord3(event.data.usage)) return event;
701
+ if (!isRecord4(event.data) || !isRecord4(event.data.usage)) return event;
384
702
  const paths = prepared.directive?.usage?.reasoningTokenPaths;
385
703
  const usage = { ...event.data.usage };
386
704
  let changed = false;
@@ -409,36 +727,6 @@ function adjustUsageEvent(event, prepared) {
409
727
  return { ...event, data: { ...event.data, usage } };
410
728
  }
411
729
 
412
- // src/types/providers.ts
413
- var IMPLEMENTED_AI_PROVIDERS = ["openrouter", "bedrock", "openai", "cloudflare"];
414
- function isImplementedAiProvider(provider) {
415
- return IMPLEMENTED_AI_PROVIDERS.includes(provider);
416
- }
417
-
418
- // src/errors.ts
419
- var AiDispatcherError = class extends Error {
420
- constructor(code, message, details) {
421
- super(message);
422
- this.code = code;
423
- this.details = details;
424
- this.name = "AiDispatcherError";
425
- }
426
- code;
427
- details;
428
- };
429
- function providerNotImplemented(provider) {
430
- const implemented = [...IMPLEMENTED_AI_PROVIDERS];
431
- return new AiDispatcherError(
432
- "PROVIDER_NOT_IMPLEMENTED",
433
- `AI provider "${provider}" is not implemented yet. Supported by @x12i/ai-dispatcher: ${implemented.map((id) => `"${id}"`).join(", ")}.`,
434
- {
435
- provider,
436
- implementedProviders: implemented,
437
- operatorHint: `Pass provider: "${implemented[0]}" (or omit provider to use the default).`
438
- }
439
- );
440
- }
441
-
442
730
  // src/mcp.ts
443
731
  var TOOL_TOKEN = /^[A-Za-z0-9_-]+$/;
444
732
  var MAX_EXPOSED_NAME_LENGTH = 64;
@@ -460,7 +748,7 @@ function indexMcpServers(servers) {
460
748
  for (const server of servers) {
461
749
  assertServer(server, seenServers);
462
750
  for (const tool of server.tools) {
463
- if (!isRecord4(tool)) {
751
+ if (!isRecord5(tool)) {
464
752
  throw new AiDispatcherError(
465
753
  "MCP_TOOL_NAME_INVALID",
466
754
  `MCP server "${server.id}" has a tool that is not an object.`
@@ -547,18 +835,17 @@ function applyMcpCatalog(params) {
547
835
  function createMcpExecutor(server, rawName) {
548
836
  return async (args, context) => {
549
837
  const result = await server.callTool(rawName, toArgs(args), context);
550
- return flattenMcpResult(result);
838
+ return mcpModelValue(result);
551
839
  };
552
840
  }
553
- function flattenMcpResult(result) {
554
- if (typeof result === "string") return result;
555
- if (!isRecord4(result)) return stringify(result);
841
+ function mcpModelValue(result) {
842
+ if (typeof result === "string" || !isRecord5(result)) return result;
556
843
  const text = textContent(result.content);
557
844
  if (result.isError === true) {
558
845
  throw new Error(text ?? stringify(result));
559
846
  }
560
847
  if (text !== void 0) return text;
561
- return stringify(result);
848
+ return result;
562
849
  }
563
850
  function resolveAllowlist(filter, tools) {
564
851
  if (filter === void 0) return void 0;
@@ -577,7 +864,7 @@ function resolveAllowlist(filter, tools) {
577
864
  return new Set(filter);
578
865
  }
579
866
  function assertServer(server, seen) {
580
- if (!isRecord4(server) || typeof server.id !== "string" || server.id.length === 0 || !Array.isArray(server.tools) || typeof server.callTool !== "function") {
867
+ if (!isRecord5(server) || typeof server.id !== "string" || server.id.length === 0 || !Array.isArray(server.tools) || typeof server.callTool !== "function") {
581
868
  throw new AiDispatcherError(
582
869
  "MCP_SERVER_INVALID",
583
870
  "An MCP server needs an id, a tools array, and a callTool function."
@@ -622,7 +909,7 @@ function toolDescription(tool) {
622
909
  }
623
910
  function toolParameters(tool) {
624
911
  if (tool.inputSchema === void 0) return { ...DEFAULT_PARAMETERS, properties: {} };
625
- if (!isRecord4(tool.inputSchema)) {
912
+ if (!isRecord5(tool.inputSchema)) {
626
913
  throw new AiDispatcherError(
627
914
  "MCP_TOOL_NAME_INVALID",
628
915
  `MCP tool "${tool.name}" inputSchema must be an object.`
@@ -634,14 +921,14 @@ function textContent(content) {
634
921
  if (!Array.isArray(content)) return void 0;
635
922
  const parts = [];
636
923
  for (const part of content) {
637
- if (!isRecord4(part) || part.type !== "text" || typeof part.text !== "string") continue;
924
+ if (!isRecord5(part) || part.type !== "text" || typeof part.text !== "string") continue;
638
925
  parts.push(part.text);
639
926
  }
640
927
  if (!parts.length) return void 0;
641
928
  return parts.join("\n");
642
929
  }
643
930
  function toArgs(args) {
644
- if (isRecord4(args)) return args;
931
+ if (isRecord5(args)) return args;
645
932
  return {};
646
933
  }
647
934
  function listFailed(serverId, error) {
@@ -653,7 +940,7 @@ function stringify(result) {
653
940
  const serialized = JSON.stringify(result);
654
941
  return serialized === void 0 ? "null" : serialized;
655
942
  }
656
- function isRecord4(value) {
943
+ function isRecord5(value) {
657
944
  return typeof value === "object" && value !== null && !Array.isArray(value);
658
945
  }
659
946
 
@@ -1215,7 +1502,7 @@ function compileCloudflareRequest(request, options, call, streaming) {
1215
1502
  if (call?.finalizeBody) body = call.finalizeBody(body);
1216
1503
  if (endpoint !== "run") body = { ...body, stream: streaming };
1217
1504
  const gateway = mergeGateway(options.gateway, call?.cloudflare?.gateway);
1218
- const fromBody = isRecord5(body.metadata) ? body.metadata : void 0;
1505
+ const fromBody = isRecord6(body.metadata) ? body.metadata : void 0;
1219
1506
  const packed = packProviderRecord(
1220
1507
  request.metadata,
1221
1508
  request.agentId,
@@ -1469,7 +1756,7 @@ function messagesFromInput(request) {
1469
1756
  messages.push({ role: "user", content: item });
1470
1757
  continue;
1471
1758
  }
1472
- if (!isRecord5(item)) continue;
1759
+ if (!isRecord6(item)) continue;
1473
1760
  const role = item.role === "assistant" || item.role === "tool" || item.role === "system" ? item.role : "user";
1474
1761
  if (role === "system") continue;
1475
1762
  const content = inputContent(item.content);
@@ -1486,12 +1773,12 @@ function inputContent(content) {
1486
1773
  if (!Array.isArray(content)) return void 0;
1487
1774
  const parts = [];
1488
1775
  for (const part of content) {
1489
- if (!isRecord5(part)) continue;
1776
+ if (!isRecord6(part)) continue;
1490
1777
  if ((part.type === "text" || part.type === "input_text") && typeof part.text === "string") {
1491
1778
  parts.push({ type: "text", text: part.text });
1492
1779
  } else if (part.type === "image_url" && typeof part.imageUrl === "string") {
1493
1780
  parts.push({ type: "image_url", imageUrl: part.imageUrl });
1494
- } else if (part.type === "image_url" && isRecord5(part.image_url) && typeof part.image_url.url === "string") {
1781
+ } else if (part.type === "image_url" && isRecord6(part.image_url) && typeof part.image_url.url === "string") {
1495
1782
  parts.push({ type: "image_url", imageUrl: part.image_url.url });
1496
1783
  } else if (part.type === "input_image" && typeof part.image_url === "string") {
1497
1784
  parts.push({ type: "image_url", imageUrl: part.image_url });
@@ -1710,7 +1997,7 @@ function continueMessages(body, response, results) {
1710
1997
  return { ...body, messages };
1711
1998
  }
1712
1999
  function continueRun(body, response, results) {
1713
- const input = isRecord5(body.input) ? body.input : {};
2000
+ const input = isRecord6(body.input) ? body.input : {};
1714
2001
  const continued = continueChat({ messages: input.messages }, response, results);
1715
2002
  return { ...body, input: { ...input, messages: continued.messages } };
1716
2003
  }
@@ -1799,7 +2086,7 @@ async function* streamCloudflare(request, options, call) {
1799
2086
  throw failure;
1800
2087
  }
1801
2088
  for await (const event of parseSse(response)) {
1802
- if (!isRecord5(event)) continue;
2089
+ if (!isRecord6(event)) continue;
1803
2090
  const catalogReasoning = call?.extractStreamReasoning?.(event);
1804
2091
  const reasoningText = catalogReasoning || reasoningDelta(event);
1805
2092
  if (reasoningText) yield { type: "stream.reasoning.delta", requestId, data: { text: reasoningText } };
@@ -1862,7 +2149,7 @@ async function sendOnce(compiled, options, timeoutMs) {
1862
2149
  if (!response.ok) throw await readFailure(response);
1863
2150
  const text = await response.text();
1864
2151
  const parsed = text ? JSON.parse(text) : {};
1865
- if (isRecord5(parsed) && parsed.success === false) {
2152
+ if (isRecord6(parsed) && parsed.success === false) {
1866
2153
  throw new AiDispatcherError(
1867
2154
  "PROVIDER_REQUEST_FAILED",
1868
2155
  messageFrom(parsed) ?? "Cloudflare request failed.",
@@ -1913,7 +2200,7 @@ function isConfigError(code) {
1913
2200
  }
1914
2201
  function asTerminalError(error) {
1915
2202
  if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
1916
- const status = isRecord5(error.details) ? error.details.status : void 0;
2203
+ const status = isRecord6(error.details) ? error.details.status : void 0;
1917
2204
  if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
1918
2205
  return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
1919
2206
  }
@@ -1923,7 +2210,7 @@ function asTerminalError(error) {
1923
2210
  function normalizeCloudflareResponse(params) {
1924
2211
  const payload = unwrap(params.response);
1925
2212
  const calls = params.functionTools ?? extractCalls(params.compiled.endpoint, params.response);
1926
- const usage = normalizeUsage(payload.usage) ?? normalizeUsage(isRecord5(params.response) ? params.response.usage : void 0);
2213
+ const usage = normalizeUsage(payload.usage) ?? normalizeUsage(isRecord6(params.response) ? params.response.usage : void 0);
1927
2214
  const status = params.status ?? (calls.length ? "requires_action" : "completed");
1928
2215
  return {
1929
2216
  id: typeof payload.id === "string" ? payload.id : params.requestId,
@@ -1956,16 +2243,16 @@ function answerText(response) {
1956
2243
  if (chat) return chat;
1957
2244
  const anthropic = anthropicAnswer(response);
1958
2245
  if (anthropic) return anthropic;
1959
- if (isRecord5(response.result)) return answerText(response.result);
2246
+ if (isRecord6(response.result)) return answerText(response.result);
1960
2247
  return "";
1961
2248
  }
1962
2249
  function responsesAnswer(response) {
1963
2250
  if (!Array.isArray(response.output)) return "";
1964
2251
  const parts = [];
1965
2252
  for (const item of response.output) {
1966
- if (!isRecord5(item) || item.type !== "message" || !Array.isArray(item.content)) continue;
2253
+ if (!isRecord6(item) || item.type !== "message" || !Array.isArray(item.content)) continue;
1967
2254
  for (const part of item.content) {
1968
- if (isRecord5(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
2255
+ if (isRecord6(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
1969
2256
  }
1970
2257
  }
1971
2258
  return parts.join("");
@@ -1976,16 +2263,16 @@ function chatAnswer(response) {
1976
2263
  const content = message.content;
1977
2264
  if (typeof content === "string") return content;
1978
2265
  if (!Array.isArray(content)) return "";
1979
- return content.map((part) => isRecord5(part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2266
+ return content.map((part) => isRecord6(part) && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
1980
2267
  }
1981
2268
  function anthropicAnswer(response) {
1982
2269
  if (!Array.isArray(response.content)) return "";
1983
- return response.content.map((part) => isRecord5(part) && part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
2270
+ return response.content.map((part) => isRecord6(part) && part.type === "text" && typeof part.text === "string" ? part.text : "").filter(Boolean).join("");
1984
2271
  }
1985
2272
  function chatAssistantMessage(response) {
1986
- if (!Array.isArray(response.choices) || !isRecord5(response.choices[0])) return void 0;
2273
+ if (!Array.isArray(response.choices) || !isRecord6(response.choices[0])) return void 0;
1987
2274
  const message = response.choices[0].message;
1988
- return isRecord5(message) ? message : void 0;
2275
+ return isRecord6(message) ? message : void 0;
1989
2276
  }
1990
2277
  function extractCalls(endpoint, response) {
1991
2278
  const record2 = unwrap(response);
@@ -1997,7 +2284,7 @@ function responsesCalls(response) {
1997
2284
  if (!Array.isArray(response.output)) return [];
1998
2285
  const calls = [];
1999
2286
  for (const item of response.output) {
2000
- if (!isRecord5(item) || item.type !== "function_call") continue;
2287
+ if (!isRecord6(item) || item.type !== "function_call") continue;
2001
2288
  calls.push(callUsage(
2002
2289
  typeof item.name === "string" ? item.name : "function",
2003
2290
  typeof item.call_id === "string" ? item.call_id : "function",
@@ -2011,7 +2298,7 @@ function chatCalls(response) {
2011
2298
  if (!message || !Array.isArray(message.tool_calls)) return [];
2012
2299
  const calls = [];
2013
2300
  for (const item of message.tool_calls) {
2014
- if (!isRecord5(item) || !isRecord5(item.function)) continue;
2301
+ if (!isRecord6(item) || !isRecord6(item.function)) continue;
2015
2302
  const name = typeof item.function.name === "string" ? item.function.name : "function";
2016
2303
  calls.push(callUsage(
2017
2304
  name,
@@ -2025,7 +2312,7 @@ function anthropicCalls(response) {
2025
2312
  if (!Array.isArray(response.content)) return [];
2026
2313
  const calls = [];
2027
2314
  for (const item of response.content) {
2028
- if (!isRecord5(item) || item.type !== "tool_use") continue;
2315
+ if (!isRecord6(item) || item.type !== "tool_use") continue;
2029
2316
  const name = typeof item.name === "string" ? item.name : "function";
2030
2317
  calls.push({
2031
2318
  name,
@@ -2064,7 +2351,7 @@ function emptyToolUsage(calls) {
2064
2351
  };
2065
2352
  }
2066
2353
  function normalizeUsage(usage) {
2067
- if (!isRecord5(usage)) return void 0;
2354
+ if (!isRecord6(usage)) return void 0;
2068
2355
  const inputTokens = numberValue(usage.input_tokens) ?? numberValue(usage.prompt_tokens);
2069
2356
  const outputTokens = numberValue(usage.output_tokens) ?? numberValue(usage.completion_tokens);
2070
2357
  const totalTokens = numberValue(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
@@ -2115,7 +2402,7 @@ function errorEvent(requestId, error) {
2115
2402
  }
2116
2403
  function textDelta(event) {
2117
2404
  if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
2118
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "text_delta" && typeof event.delta.text === "string" && event.delta.text) {
2405
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "text_delta" && typeof event.delta.text === "string" && event.delta.text) {
2119
2406
  return event.delta.text;
2120
2407
  }
2121
2408
  const choice = firstChoiceDelta(event);
@@ -2124,7 +2411,7 @@ function textDelta(event) {
2124
2411
  }
2125
2412
  function reasoningDelta(event) {
2126
2413
  if (event.type === "response.reasoning_summary_text.delta" && typeof event.delta === "string" && event.delta) return event.delta;
2127
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "thinking_delta" && typeof event.delta.thinking === "string" && event.delta.thinking) {
2414
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "thinking_delta" && typeof event.delta.thinking === "string" && event.delta.thinking) {
2128
2415
  return event.delta.thinking;
2129
2416
  }
2130
2417
  const choice = firstChoiceDelta(event);
@@ -2141,13 +2428,13 @@ function toolCallDelta(event) {
2141
2428
  argumentsDelta: event.delta
2142
2429
  };
2143
2430
  }
2144
- if (event.type === "content_block_delta" && isRecord5(event.delta) && event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
2431
+ if (event.type === "content_block_delta" && isRecord6(event.delta) && event.delta.type === "input_json_delta" && typeof event.delta.partial_json === "string") {
2145
2432
  return { index: typeof event.index === "number" ? event.index : 0, argumentsDelta: event.delta.partial_json };
2146
2433
  }
2147
2434
  const choice = firstChoiceDelta(event);
2148
- if (!choice || !Array.isArray(choice.tool_calls) || !isRecord5(choice.tool_calls[0])) return void 0;
2435
+ if (!choice || !Array.isArray(choice.tool_calls) || !isRecord6(choice.tool_calls[0])) return void 0;
2149
2436
  const tool = choice.tool_calls[0];
2150
- const fn = isRecord5(tool.function) ? tool.function : void 0;
2437
+ const fn = isRecord6(tool.function) ? tool.function : void 0;
2151
2438
  const args = fn && typeof fn.arguments === "string" ? fn.arguments : void 0;
2152
2439
  if (!args && !(fn && typeof fn.name === "string")) return void 0;
2153
2440
  return {
@@ -2157,18 +2444,18 @@ function toolCallDelta(event) {
2157
2444
  };
2158
2445
  }
2159
2446
  function streamUsage(event) {
2160
- if (event.type === "response.completed" && isRecord5(event.response)) return normalizeUsage(event.response.usage);
2161
- if (isRecord5(event.usage)) return normalizeUsage(event.usage);
2447
+ if (event.type === "response.completed" && isRecord6(event.response)) return normalizeUsage(event.response.usage);
2448
+ if (isRecord6(event.usage)) return normalizeUsage(event.usage);
2162
2449
  return void 0;
2163
2450
  }
2164
2451
  function firstChoiceDelta(event) {
2165
- if (!Array.isArray(event.choices) || !isRecord5(event.choices[0])) return void 0;
2452
+ if (!Array.isArray(event.choices) || !isRecord6(event.choices[0])) return void 0;
2166
2453
  const delta = event.choices[0].delta;
2167
- return isRecord5(delta) ? delta : void 0;
2454
+ return isRecord6(delta) ? delta : void 0;
2168
2455
  }
2169
2456
  function unwrap(response) {
2170
- if (!isRecord5(response)) return {};
2171
- if (response.success === true && isRecord5(response.result)) return response.result;
2457
+ if (!isRecord6(response)) return {};
2458
+ if (response.success === true && isRecord6(response.result)) return response.result;
2172
2459
  return response;
2173
2460
  }
2174
2461
  function modelOf(compiled) {
@@ -2176,12 +2463,12 @@ function modelOf(compiled) {
2176
2463
  return "";
2177
2464
  }
2178
2465
  function messageFrom(body) {
2179
- if (!isRecord5(body)) return void 0;
2466
+ if (!isRecord6(body)) return void 0;
2180
2467
  if (typeof body.message === "string") return body.message;
2181
- if (isRecord5(body.error) && typeof body.error.message === "string") return body.error.message;
2468
+ if (isRecord6(body.error) && typeof body.error.message === "string") return body.error.message;
2182
2469
  if (Array.isArray(body.errors)) {
2183
2470
  for (const error of body.errors) {
2184
- if (isRecord5(error) && typeof error.message === "string") return error.message;
2471
+ if (isRecord6(error) && typeof error.message === "string") return error.message;
2185
2472
  }
2186
2473
  }
2187
2474
  return void 0;
@@ -2322,7 +2609,7 @@ function readEnv(name) {
2322
2609
  if (typeof process === "undefined") return void 0;
2323
2610
  return process.env[name];
2324
2611
  }
2325
- function isRecord5(value) {
2612
+ function isRecord6(value) {
2326
2613
  return typeof value === "object" && value !== null && !Array.isArray(value);
2327
2614
  }
2328
2615
 
@@ -2332,6 +2619,7 @@ function prepareDispatchRequest(params) {
2332
2619
  applyRoleStamp(cloned);
2333
2620
  applyDispatchIdentity(cloned);
2334
2621
  absorbOverrideMetadata(cloned);
2622
+ applyDispatchGate(cloned);
2335
2623
  const provider = params.provider;
2336
2624
  const reasoningEffort = cloned.reasoningEffort;
2337
2625
  const bedrockControls = cloned.bedrockControls;
@@ -2530,7 +2818,7 @@ function settleNested(policy, kind, parentEffort) {
2530
2818
  if (kind === "fusion") next.judgeModel = directive.model;
2531
2819
  else next.model = directive.model;
2532
2820
  }
2533
- if (isRecord6(directive.body.reasoning)) next.reasoning = directive.body.reasoning;
2821
+ if (isRecord7(directive.body.reasoning)) next.reasoning = directive.body.reasoning;
2534
2822
  return next;
2535
2823
  }
2536
2824
  function applyPromptPrefix(request, prefix) {
@@ -2580,7 +2868,7 @@ function prefixInputItems(items, prefix) {
2580
2868
  }
2581
2869
  if (!Array.isArray(item.content)) continue;
2582
2870
  for (const part of item.content) {
2583
- if (!isRecord6(part) || typeof part.text !== "string") continue;
2871
+ if (!isRecord7(part) || typeof part.text !== "string") continue;
2584
2872
  if (part.type === "text" || part.type === "input_text") {
2585
2873
  part.text = prefix + part.text;
2586
2874
  return true;
@@ -2641,7 +2929,7 @@ var ROLE_STAMP_FIELDS = ["role", "roleLayer", "roleDigest", "profileChoice"];
2641
2929
  function applyRoleStamp(request) {
2642
2930
  const stamp = request.role;
2643
2931
  delete request.role;
2644
- if (!isRecord6(stamp)) return;
2932
+ if (!isRecord7(stamp)) return;
2645
2933
  const identity = {};
2646
2934
  for (const field of ROLE_STAMP_FIELDS) {
2647
2935
  const value = stamp[field];
@@ -2656,8 +2944,8 @@ function applyRoleStamp(request) {
2656
2944
  }
2657
2945
  function absorbOverrideMetadata(request) {
2658
2946
  const overrides = request.rawOpenRouterOverrides;
2659
- if (!isRecord6(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
2660
- if (isRecord6(overrides.metadata)) {
2947
+ if (!isRecord7(overrides) || !Object.prototype.hasOwnProperty.call(overrides, "metadata")) return;
2948
+ if (isRecord7(overrides.metadata)) {
2661
2949
  const current = { ...request.metadata ?? {} };
2662
2950
  const extra = {};
2663
2951
  for (const [key, value] of Object.entries(overrides.metadata)) {
@@ -2684,6 +2972,12 @@ function applyDispatchIdentity(request) {
2684
2972
  for (const key of Object.keys(identity)) delete rest[key];
2685
2973
  request.metadata = { ...identity, ...rest };
2686
2974
  }
2975
+ var AI_DISPATCHER_GATE = "ai-dispatcher";
2976
+ function applyDispatchGate(request) {
2977
+ const rest = { ...request.metadata ?? {} };
2978
+ delete rest["xtivix.gate"];
2979
+ request.metadata = { "xtivix.gate": AI_DISPATCHER_GATE, ...rest };
2980
+ }
2687
2981
  function cloneRequest(request) {
2688
2982
  const functionTools = request.functionTools?.map((tool) => ({ ...tool }));
2689
2983
  const cloned = JSON.parse(JSON.stringify(request));
@@ -2696,7 +2990,7 @@ function hasOwnKeys(value) {
2696
2990
  function hasStreamMetadata(directive) {
2697
2991
  return directive.response.streamReasoningKeys.length > 0 || directive.response.streamReasoningPaths.length > 0;
2698
2992
  }
2699
- function isRecord6(value) {
2993
+ function isRecord7(value) {
2700
2994
  return typeof value === "object" && value !== null && !Array.isArray(value);
2701
2995
  }
2702
2996
  function readEnv2(name) {
@@ -3019,7 +3313,7 @@ async function runOpenAi(request, options, call, requestId) {
3019
3313
  let response;
3020
3314
  for (let iteration = 0; iteration <= maxIterations; iteration += 1) {
3021
3315
  response = await sendWithRetries2(active, options, timeoutFor2(request, options));
3022
- const calls = functionCalls(isRecord7(response) ? response : {});
3316
+ const calls = functionCalls(isRecord8(response) ? response : {});
3023
3317
  if (!calls.length) break;
3024
3318
  if (!calls.every((item) => resolveExecutor2(item.name, request, options))) {
3025
3319
  return normalizeOpenAiResponse({
@@ -3039,7 +3333,7 @@ async function runOpenAi(request, options, call, requestId) {
3039
3333
  results.push(await executeOpenAiTool(item, request, options));
3040
3334
  }
3041
3335
  functionUsage.push(...results);
3042
- const record2 = isRecord7(response) ? response : {};
3336
+ const record2 = isRecord8(response) ? response : {};
3043
3337
  active = {
3044
3338
  ...active,
3045
3339
  body: clean2({
@@ -3154,7 +3448,7 @@ async function* streamOpenAi(request, options, call) {
3154
3448
  throw failure;
3155
3449
  }
3156
3450
  for await (const event of parseSse2(response)) {
3157
- if (!isRecord7(event)) continue;
3451
+ if (!isRecord8(event)) continue;
3158
3452
  if (event.type === "response.output_text.delta" && typeof event.delta === "string" && event.delta) {
3159
3453
  text += event.delta;
3160
3454
  yield { type: "stream.text.delta", requestId, data: { text: event.delta } };
@@ -3173,7 +3467,7 @@ async function* streamOpenAi(request, options, call) {
3173
3467
  argumentsDelta: event.delta
3174
3468
  }
3175
3469
  };
3176
- } else if (event.type === "response.completed" && isRecord7(event.response)) {
3470
+ } else if (event.type === "response.completed" && isRecord8(event.response)) {
3177
3471
  const usage = normalizeUsage2(event.response.usage);
3178
3472
  if (usage) yield { type: "stream.usage", requestId, data: { usage } };
3179
3473
  } else if (event.type === "error") {
@@ -3267,7 +3561,7 @@ function isConfigError2(code) {
3267
3561
  }
3268
3562
  function asTerminalError2(error) {
3269
3563
  if (error instanceof AiDispatcherError && error.code === "PROVIDER_RETRYABLE") {
3270
- const status = isRecord7(error.details) ? error.details.status : void 0;
3564
+ const status = isRecord8(error.details) ? error.details.status : void 0;
3271
3565
  if (status === 429) return new AiDispatcherError("PROVIDER_RATE_LIMITED", error.message, error.details);
3272
3566
  return new AiDispatcherError("PROVIDER_GATEWAY_UNREACHABLE", error.message, error.details);
3273
3567
  }
@@ -3275,7 +3569,7 @@ function asTerminalError2(error) {
3275
3569
  return new AiDispatcherError("PROVIDER_REQUEST_FAILED", error instanceof Error ? error.message : String(error));
3276
3570
  }
3277
3571
  function normalizeOpenAiResponse(params) {
3278
- const record2 = isRecord7(params.response) ? params.response : {};
3572
+ const record2 = isRecord8(params.response) ? params.response : {};
3279
3573
  const text = answerText2(record2);
3280
3574
  const calls = params.functionTools ?? functionCalls(record2);
3281
3575
  const usage = normalizeUsage2(record2.usage);
@@ -3302,9 +3596,9 @@ function answerText2(response) {
3302
3596
  if (Array.isArray(output)) {
3303
3597
  const parts = [];
3304
3598
  for (const item of output) {
3305
- if (!isRecord7(item) || item.type === "reasoning" || item.type !== "message" || !Array.isArray(item.content)) continue;
3599
+ if (!isRecord8(item) || item.type === "reasoning" || item.type !== "message" || !Array.isArray(item.content)) continue;
3306
3600
  for (const part of item.content) {
3307
- if (isRecord7(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
3601
+ if (isRecord8(part) && part.type === "output_text" && typeof part.text === "string") parts.push(part.text);
3308
3602
  }
3309
3603
  }
3310
3604
  if (parts.length > 0) return parts.join("");
@@ -3315,7 +3609,7 @@ function functionCalls(response) {
3315
3609
  if (!Array.isArray(response.output)) return [];
3316
3610
  const calls = [];
3317
3611
  for (const item of response.output) {
3318
- if (!isRecord7(item) || item.type !== "function_call") continue;
3612
+ if (!isRecord8(item) || item.type !== "function_call") continue;
3319
3613
  const name = typeof item.name === "string" ? item.name : "function";
3320
3614
  const callId = typeof item.call_id === "string" ? item.call_id : name;
3321
3615
  let args = item.arguments;
@@ -3347,7 +3641,7 @@ function toolUsage(calls) {
3347
3641
  };
3348
3642
  }
3349
3643
  function normalizeUsage2(usage) {
3350
- if (!isRecord7(usage)) return void 0;
3644
+ if (!isRecord8(usage)) return void 0;
3351
3645
  const inputTokens = numberValue2(usage.input_tokens);
3352
3646
  const outputTokens = numberValue2(usage.output_tokens);
3353
3647
  const totalTokens = numberValue2(usage.total_tokens) ?? (inputTokens !== void 0 || outputTokens !== void 0 ? (inputTokens ?? 0) + (outputTokens ?? 0) : void 0);
@@ -3399,16 +3693,16 @@ function modelOf2(compiled) {
3399
3693
  return typeof compiled.body.model === "string" ? compiled.body.model : "";
3400
3694
  }
3401
3695
  function messageFrom2(body) {
3402
- if (!isRecord7(body)) return void 0;
3696
+ if (!isRecord8(body)) return void 0;
3403
3697
  if (typeof body.message === "string") return body.message;
3404
- if (isRecord7(body.error) && typeof body.error.message === "string") return body.error.message;
3698
+ if (isRecord8(body.error) && typeof body.error.message === "string") return body.error.message;
3405
3699
  return void 0;
3406
3700
  }
3407
3701
  function numberValue2(value) {
3408
3702
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3409
3703
  }
3410
3704
  function attachProviderMetadata(body, request, packer) {
3411
- const fromBody = isRecord7(body.metadata) ? body.metadata : void 0;
3705
+ const fromBody = isRecord8(body.metadata) ? body.metadata : void 0;
3412
3706
  const packed = packProviderRecord2(request.metadata, request.agentId, fromBody, packer);
3413
3707
  if (packed.wire) return { body: { ...body, metadata: packed.wire }, ...packed.warning ? { warning: packed.warning } : {} };
3414
3708
  if (body.metadata === void 0) return { body, ...packed.warning ? { warning: packed.warning } : {} };
@@ -3482,7 +3776,7 @@ function readEnv3(name) {
3482
3776
  if (typeof process === "undefined") return void 0;
3483
3777
  return process.env[name];
3484
3778
  }
3485
- function isRecord7(value) {
3779
+ function isRecord8(value) {
3486
3780
  return typeof value === "object" && value !== null && !Array.isArray(value);
3487
3781
  }
3488
3782
 
@@ -3552,6 +3846,7 @@ function createAiDispatcher(options = {}) {
3552
3846
  if (provider === "openrouter") {
3553
3847
  const openrouterOptions = {
3554
3848
  ...options.openrouter,
3849
+ ...options.openrouter?.tools ? { tools: wrapToolRegistry(options.openrouter.tools) } : {},
3555
3850
  metadataPacker: packer,
3556
3851
  ...options.logger && !options.openrouter?.logger ? { logger: options.logger } : {}
3557
3852
  };
@@ -3560,6 +3855,7 @@ function createAiDispatcher(options = {}) {
3560
3855
  if (provider === "bedrock") {
3561
3856
  const bedrockOptions = {
3562
3857
  ...options.bedrock,
3858
+ ...options.bedrock?.tools ? { tools: wrapToolRegistry(options.bedrock.tools) } : {},
3563
3859
  metadataPacker: packer,
3564
3860
  ...options.logger && !options.bedrock?.logger ? { logger: options.logger } : {}
3565
3861
  };
@@ -3568,6 +3864,7 @@ function createAiDispatcher(options = {}) {
3568
3864
  if (provider === "openai") {
3569
3865
  const openaiOptions = {
3570
3866
  ...options.openai,
3867
+ ...options.openai?.tools ? { tools: wrapToolRegistry(options.openai.tools) } : {},
3571
3868
  ...options.logger && !options.openai?.logger ? { logger: options.logger } : {}
3572
3869
  };
3573
3870
  return createOpenAiProviderAdapter(openaiOptions, packer);
@@ -3575,6 +3872,7 @@ function createAiDispatcher(options = {}) {
3575
3872
  if (provider === "cloudflare") {
3576
3873
  const cloudflareOptions = {
3577
3874
  ...options.cloudflare,
3875
+ ...options.cloudflare?.tools ? { tools: wrapToolRegistry(options.cloudflare.tools) } : {},
3578
3876
  ...options.logger && !options.cloudflare?.logger ? { logger: options.logger } : {}
3579
3877
  };
3580
3878
  return createCloudflareProviderAdapter(cloudflareOptions, packer);
@@ -3596,21 +3894,27 @@ function createAiDispatcher(options = {}) {
3596
3894
  return {
3597
3895
  provider: defaultProvider,
3598
3896
  async run(request) {
3599
- const prepared = prepare(request, true);
3897
+ const prepared = await prepare(request, true);
3600
3898
  log(options, "ai-dispatcher.request.started", prepared, "run", false);
3601
- const response = await getAdapter(prepared.provider).run(prepared.request, callOf(prepared.call));
3899
+ const response = await inputStorage.run(
3900
+ prepared.inputTrack,
3901
+ () => getAdapter(prepared.provider).run(prepared.request, callOf(prepared.call))
3902
+ );
3602
3903
  return applyResolution(response, prepared);
3603
3904
  },
3604
3905
  executeStreamingChat(request) {
3605
- const prepared = prepare(request, false);
3606
- log(options, "ai-dispatcher.request.started", prepared, "executeStreamingChat", true);
3607
- return decorateStream(
3608
- prepared,
3609
- getAdapter(prepared.provider).executeStreamingChat(prepared.request, callOf(prepared.call))
3610
- );
3906
+ return (async function* () {
3907
+ const prepared = await prepare(request, false);
3908
+ log(options, "ai-dispatcher.request.started", prepared, "executeStreamingChat", true);
3909
+ if (prepared.inputTrack) inputStorage.enterWith(prepared.inputTrack);
3910
+ yield* decorateStream(
3911
+ prepared,
3912
+ getAdapter(prepared.provider).executeStreamingChat(prepared.request, callOf(prepared.call))
3913
+ );
3914
+ })();
3611
3915
  },
3612
- compile(request) {
3613
- const prepared = prepare(request, true);
3916
+ async compile(request) {
3917
+ const prepared = await prepare(request, true);
3614
3918
  log(options, "ai-dispatcher.request.compiled", prepared, "compile", false);
3615
3919
  const compiled = getAdapter(prepared.provider).compile(prepared.request, callOf(prepared.call));
3616
3920
  return attachCompileMeta(prepared, compiled);
@@ -3624,7 +3928,7 @@ function createAiDispatcher(options = {}) {
3624
3928
  return pending;
3625
3929
  }
3626
3930
  };
3627
- function prepare(request, includeMcp) {
3931
+ async function prepare(request, includeMcp) {
3628
3932
  const provider = resolveProvider(request.provider, defaultProvider);
3629
3933
  const prepared = prepareDispatchRequest({ request, provider, options });
3630
3934
  const tools = providerTools(provider, options);
@@ -3634,6 +3938,11 @@ function createAiDispatcher(options = {}) {
3634
3938
  ...tools ? { providerTools: tools } : {},
3635
3939
  includeMcp
3636
3940
  });
3941
+ prepared.inputTrack = await applyAiInput({
3942
+ request: prepared.request,
3943
+ effectiveModel: prepared.effectiveModel,
3944
+ factory: options.aiInput
3945
+ });
3637
3946
  return prepared;
3638
3947
  }
3639
3948
  }
@@ -3651,7 +3960,8 @@ function callOf(call) {
3651
3960
  }
3652
3961
  function attachCompileMeta(prepared, compiled) {
3653
3962
  const record2 = compiled;
3654
- const headers = isRecord8(record2.headers) ? redactHeaders(record2.headers) : void 0;
3963
+ const headers = isRecord9(record2.headers) ? redactHeaders(record2.headers) : void 0;
3964
+ const inputResolution = prepared.inputTrack ? visibleInputResolution(prepared.inputTrack) : void 0;
3655
3965
  return {
3656
3966
  ...record2,
3657
3967
  ...headers ? { headers } : {},
@@ -3660,6 +3970,7 @@ function attachCompileMeta(prepared, compiled) {
3660
3970
  effectiveModel: prepared.effectiveModel,
3661
3971
  ...prepared.resolution ? { reasoningResolution: prepared.resolution } : {},
3662
3972
  ...prepared.cacheResolution ? { cacheResolution: prepared.cacheResolution } : {},
3973
+ ...inputResolution ? { inputResolution } : {},
3663
3974
  ...prepared.cacheWarnings?.length ? { warnings: [...Array.isArray(record2.warnings) ? record2.warnings : [], ...prepared.cacheWarnings] } : {}
3664
3975
  };
3665
3976
  }
@@ -3685,7 +3996,7 @@ function log(options, event, prepared, entrypoint, streaming) {
3685
3996
  if (event.endsWith("compiled")) options.logger?.debug?.(event, payload);
3686
3997
  else options.logger?.info?.(event, payload);
3687
3998
  }
3688
- function isRecord8(value) {
3999
+ function isRecord9(value) {
3689
4000
  return typeof value === "object" && value !== null && !Array.isArray(value);
3690
4001
  }
3691
4002
 
@@ -3696,6 +4007,7 @@ function stripReasoningHistoryFields(message, paths) {
3696
4007
  return clone;
3697
4008
  }
3698
4009
  export {
4010
+ AI_DISPATCHER_GATE,
3699
4011
  AiDispatcherError,
3700
4012
  DEFAULT_METADATA_PRIORITY,
3701
4013
  IMPLEMENTED_AI_PROVIDERS,