@axlsdk/axl 0.20.1 → 0.21.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -171,6 +171,221 @@ var ValidationError = class extends AxlError {
171
171
 
172
172
  // src/tool.ts
173
173
  var DEFAULT_MAX_STRING_LENGTH = 1e4;
174
+ var MAX_MODEL_ARGUMENT_ISSUES = 8;
175
+ var MAX_MODEL_ARGUMENT_MESSAGE_LENGTH = 2e3;
176
+ var MAX_MODEL_ARGUMENT_LINE_LENGTH = 220;
177
+ var MAX_MODEL_ARGUMENT_PATH_LENGTH = 160;
178
+ var MAX_UNION_DEPTH = 3;
179
+ var MAX_UNION_BRANCHES = 4;
180
+ var MAX_LITERAL_ALTERNATIVES = 10;
181
+ var SAFE_SCHEMA_TYPES = /* @__PURE__ */ new Set([
182
+ "string",
183
+ "number",
184
+ "boolean",
185
+ "null",
186
+ "array",
187
+ "object",
188
+ "integer"
189
+ ]);
190
+ var SAFE_STRING_FORMATS = /* @__PURE__ */ new Set([
191
+ "email",
192
+ "url",
193
+ "emoji",
194
+ "uuid",
195
+ "guid",
196
+ "nanoid",
197
+ "cuid",
198
+ "cuid2",
199
+ "ulid",
200
+ "xid",
201
+ "ksuid",
202
+ "datetime",
203
+ "date",
204
+ "time",
205
+ "duration",
206
+ "ipv4",
207
+ "ipv6",
208
+ "cidrv4",
209
+ "cidrv6",
210
+ "base64",
211
+ "base64url",
212
+ "json_string",
213
+ "e164",
214
+ "lowercase",
215
+ "uppercase",
216
+ "jwt"
217
+ ]);
218
+ function truncateModelFeedback(value, maximum) {
219
+ return value.length <= maximum ? value : `${value.slice(0, Math.max(0, maximum - 1))}\u2026`;
220
+ }
221
+ var DISPLAY_CONTROL_CHARACTERS = /[\u061C\u200B-\u200F\u202A-\u202E\u2066-\u2069\u2028\u2029]/g;
222
+ function isJsonSchema(value) {
223
+ return value !== null && typeof value === "object" && !Array.isArray(value);
224
+ }
225
+ function escapeDisplayControls(value) {
226
+ return value.replace(
227
+ DISPLAY_CONTROL_CHARACTERS,
228
+ (character) => `\\u${character.codePointAt(0).toString(16).padStart(4, "0")}`
229
+ );
230
+ }
231
+ function quoteModelText(value) {
232
+ return escapeDisplayControls(JSON.stringify(value));
233
+ }
234
+ function schemaNumber(value) {
235
+ return typeof value === "number" && Number.isFinite(value) ? String(value) : void 0;
236
+ }
237
+ function schemaLiteral(value) {
238
+ if (typeof value === "string") {
239
+ const rendered = quoteModelText(value);
240
+ return rendered.length <= 120 ? rendered : '"<literal omitted>"';
241
+ }
242
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : void 0;
243
+ if (typeof value === "boolean") return String(value);
244
+ if (value === null) return "null";
245
+ return void 0;
246
+ }
247
+ function schemaType(schema) {
248
+ const type = schema.type;
249
+ return typeof type === "string" && SAFE_SCHEMA_TYPES.has(type) ? type : void 0;
250
+ }
251
+ function compositionBranches(schema) {
252
+ const branches = [];
253
+ for (const key of ["anyOf", "oneOf", "allOf"]) {
254
+ const value = schema[key];
255
+ if (!Array.isArray(value)) continue;
256
+ for (const branch of value.slice(0, MAX_UNION_BRANCHES)) {
257
+ if (isJsonSchema(branch)) branches.push(branch);
258
+ }
259
+ }
260
+ return branches;
261
+ }
262
+ function unionBranches(schema) {
263
+ const branches = [];
264
+ for (const key of ["anyOf", "oneOf"]) {
265
+ const value = schema[key];
266
+ if (!Array.isArray(value)) continue;
267
+ for (const branch of value.slice(0, MAX_UNION_BRANCHES)) {
268
+ if (isJsonSchema(branch)) branches.push(branch);
269
+ }
270
+ }
271
+ return branches;
272
+ }
273
+ function resolvePathSegment(schema, segment, depth = 0) {
274
+ if (depth >= MAX_UNION_DEPTH) return void 0;
275
+ if (typeof segment === "string") {
276
+ const properties = schema.properties;
277
+ if (isJsonSchema(properties) && Object.hasOwn(properties, segment)) {
278
+ const child = properties[segment];
279
+ if (isJsonSchema(child)) return { node: child, renderedSegment: segment };
280
+ }
281
+ if (isJsonSchema(schema.additionalProperties)) {
282
+ return { node: schema.additionalProperties, renderedSegment: "<key>" };
283
+ }
284
+ } else if (typeof segment === "number" && Number.isSafeInteger(segment) && segment >= 0) {
285
+ const prefixItems = schema.prefixItems;
286
+ if (Array.isArray(prefixItems) && isJsonSchema(prefixItems[segment])) {
287
+ return { node: prefixItems[segment], renderedSegment: String(segment) };
288
+ }
289
+ if (isJsonSchema(schema.items)) {
290
+ return { node: schema.items, renderedSegment: "<index>" };
291
+ }
292
+ }
293
+ for (const branch of compositionBranches(schema)) {
294
+ const resolved = resolvePathSegment(branch, segment, depth + 1);
295
+ if (resolved !== void 0) return resolved;
296
+ }
297
+ return void 0;
298
+ }
299
+ function resolveSchemaPath(providerVisibleSchema, issuePath) {
300
+ if (!isJsonSchema(providerVisibleSchema)) return void 0;
301
+ let node = providerVisibleSchema;
302
+ const segments = [];
303
+ for (const segment of issuePath) {
304
+ const resolved = resolvePathSegment(node, segment);
305
+ if (resolved === void 0) {
306
+ segments.push("<path omitted>");
307
+ return { node, segments };
308
+ }
309
+ node = resolved.node;
310
+ segments.push(resolved.renderedSegment);
311
+ }
312
+ return { node, segments };
313
+ }
314
+ function jsonPointerPath(segments) {
315
+ if (segments.length === 0) return "<root>";
316
+ const pointer = `/${segments.map((segment) => segment.replaceAll("~", "~0").replaceAll("/", "~1")).join("/")}`;
317
+ const rendered = quoteModelText(pointer);
318
+ return rendered.length <= MAX_MODEL_ARGUMENT_PATH_LENGTH ? rendered : '"<path omitted>"';
319
+ }
320
+ function schemaAlternatives(schema, depth = 0) {
321
+ if (depth >= MAX_UNION_DEPTH) return [];
322
+ const constant = "const" in schema ? schemaLiteral(schema.const) : void 0;
323
+ if (constant !== void 0) return [constant];
324
+ if (Array.isArray(schema.enum)) {
325
+ const values = schema.enum.slice(0, MAX_LITERAL_ALTERNATIVES).map(schemaLiteral);
326
+ if (values.every((value) => value !== void 0)) return values;
327
+ }
328
+ const type = schemaType(schema);
329
+ if (type !== void 0) return [type];
330
+ const alternatives = unionBranches(schema).flatMap(
331
+ (branch) => schemaAlternatives(branch, depth + 1)
332
+ );
333
+ return alternatives.slice(0, MAX_LITERAL_ALTERNATIVES);
334
+ }
335
+ function schemaEnumExpectation(schema) {
336
+ if ("const" in schema) {
337
+ const constant = schemaLiteral(schema.const);
338
+ return constant === void 0 ? void 0 : `expected ${constant}`;
339
+ }
340
+ if (!Array.isArray(schema.enum)) return void 0;
341
+ const values = schema.enum.slice(0, MAX_LITERAL_ALTERNATIVES).map(schemaLiteral);
342
+ if (values.some((value) => value === void 0)) return void 0;
343
+ const rendered = values;
344
+ if (rendered.length === 0) return void 0;
345
+ const omission = schema.enum.length > rendered.length ? ", \u2026" : "";
346
+ return `expected ${rendered.length === 1 ? rendered[0] : `one of ${rendered.join(", ")}`}${omission}`;
347
+ }
348
+ function schemaTypeExpectation(schema) {
349
+ const type = schemaType(schema);
350
+ if (type !== void 0) return `expected ${type}`;
351
+ const alternatives = schemaAlternatives(schema);
352
+ return alternatives.length === 0 ? void 0 : `expected one of: ${alternatives.join(", ")}`;
353
+ }
354
+ function schemaSizeExpectation(schema, direction) {
355
+ const type = schemaType(schema);
356
+ const keyword = direction === "small" ? type === "string" ? "minLength" : type === "array" ? "minItems" : "minimum" : type === "string" ? "maxLength" : type === "array" ? "maxItems" : "maximum";
357
+ const value = schemaNumber(schema[keyword]);
358
+ if (value === void 0) return void 0;
359
+ const label = type === "string" ? "string length" : type === "array" ? "array length" : type;
360
+ if (label === void 0) return void 0;
361
+ return `expected ${label} ${direction === "small" ? "at least" : "at most"} ${value}`;
362
+ }
363
+ function schemaExpectation(schema, issueCode) {
364
+ switch (issueCode) {
365
+ case "invalid_type":
366
+ return schemaTypeExpectation(schema) ?? "failed structural validation";
367
+ case "invalid_value":
368
+ return schemaEnumExpectation(schema) ?? schemaTypeExpectation(schema) ?? "failed structural validation";
369
+ case "too_small":
370
+ return schemaSizeExpectation(schema, "small") ?? schemaTypeExpectation(schema) ?? "failed structural validation";
371
+ case "too_big":
372
+ return schemaSizeExpectation(schema, "big") ?? schemaTypeExpectation(schema) ?? "failed structural validation";
373
+ case "not_multiple_of": {
374
+ const multipleOf = schemaNumber(schema.multipleOf);
375
+ return multipleOf === void 0 ? schemaTypeExpectation(schema) ?? "failed structural validation" : `expected a multiple of ${multipleOf}`;
376
+ }
377
+ case "invalid_format": {
378
+ const format = schema.format;
379
+ return typeof format === "string" && SAFE_STRING_FORMATS.has(format) ? `expected valid ${format} format` : schemaTypeExpectation(schema) ?? "failed structural validation";
380
+ }
381
+ case "invalid_union":
382
+ return schemaTypeExpectation(schema) ?? "failed structural validation";
383
+ default: {
384
+ const typeExpectation = schemaTypeExpectation(schema);
385
+ return typeExpectation === void 0 ? "failed structural validation" : `failed structural validation; ${typeExpectation}`;
386
+ }
387
+ }
388
+ }
174
389
  var ToolStringLengthError = class extends Error {
175
390
  constructor(path, actual, maximum) {
176
391
  const renderedPath = path.reduce(
@@ -186,6 +401,42 @@ var ToolStringLengthError = class extends Error {
186
401
  this.name = "ToolStringLengthError";
187
402
  }
188
403
  };
404
+ function toolArgumentModelMessage(error, providerVisibleSchema) {
405
+ try {
406
+ let lines;
407
+ let omitted = false;
408
+ if (error instanceof ZodError) {
409
+ if (error.issues.length === 0) return void 0;
410
+ lines = [];
411
+ for (const issue of error.issues.slice(0, MAX_MODEL_ARGUMENT_ISSUES)) {
412
+ const resolved = resolveSchemaPath(providerVisibleSchema, issue.path);
413
+ if (resolved === void 0) return void 0;
414
+ const path = jsonPointerPath(resolved.segments);
415
+ const expectation = schemaExpectation(resolved.node, issue.code);
416
+ lines.push(
417
+ truncateModelFeedback(`- ${path}: ${expectation}`, MAX_MODEL_ARGUMENT_LINE_LENGTH)
418
+ );
419
+ }
420
+ omitted = error.issues.length > lines.length;
421
+ } else if (error instanceof ToolStringLengthError) {
422
+ const resolved = resolveSchemaPath(providerVisibleSchema, error.path);
423
+ const maximum = schemaNumber(error.maximum);
424
+ if (resolved === void 0 || maximum === void 0) return void 0;
425
+ const path = jsonPointerPath(resolved.segments);
426
+ lines = [`- ${path}: maximum string length is ${maximum}`];
427
+ } else {
428
+ return void 0;
429
+ }
430
+ const header = "Error: Invalid tool arguments:";
431
+ const footer = "Correct the arguments and try again.";
432
+ const omission = omitted ? "Additional validation issues were omitted." : void 0;
433
+ const parts = [header, ...lines, ...omission ? [omission] : [], footer];
434
+ const message = parts.join("\n");
435
+ return message.length <= MAX_MODEL_ARGUMENT_MESSAGE_LENGTH ? message : void 0;
436
+ } catch {
437
+ return void 0;
438
+ }
439
+ }
189
440
  function validateStringLengths(value, maxLen, path = []) {
190
441
  if (typeof value === "string") {
191
442
  if (value.length > maxLen) {
@@ -496,7 +747,7 @@ async function fetchWithRetry(input, init, opts) {
496
747
  for (let attempt = 0; ; attempt++) {
497
748
  let res;
498
749
  try {
499
- res = await fetch(input, init);
750
+ res = await fetch(input, { ...init, redirect: "manual" });
500
751
  } catch (err) {
501
752
  if (isAbortError2(err, init?.signal ?? void 0)) throw err;
502
753
  if (attempt >= maxRetries) {
@@ -702,6 +953,38 @@ function isBuiltinTablePricingEligible(profile, context) {
702
953
  return typeof effectiveTier === "string" && policy.supportedServiceTiers.includes(effectiveTier);
703
954
  }
704
955
 
956
+ // src/http-transport.ts
957
+ function assertSafeProviderBaseUrl(baseUrl, surface, dangerouslyAllowInsecureHttp = false) {
958
+ assertSafeHttpEndpoint(baseUrl, surface, dangerouslyAllowInsecureHttp, "prompts and credentials");
959
+ }
960
+ function assertSafeHttpEndpoint(endpoint, surface, dangerouslyAllowInsecureHttp = false, exposedData = "sensitive data") {
961
+ let url;
962
+ try {
963
+ url = new URL(endpoint);
964
+ } catch {
965
+ throw unsafeTransportError(surface, "a valid absolute HTTP(S) URL", exposedData);
966
+ }
967
+ if (url.protocol === "https:") return;
968
+ if (url.protocol !== "http:") {
969
+ throw unsafeTransportError(surface, "an HTTPS URL", exposedData);
970
+ }
971
+ if (dangerouslyAllowInsecureHttp || isLiteralLoopbackHost(url.hostname)) return;
972
+ throw unsafeTransportError(
973
+ surface,
974
+ "an HTTPS URL, or set dangerouslyAllowInsecureHttp: true only for a deliberately insecure endpoint",
975
+ exposedData
976
+ );
977
+ }
978
+ function isLiteralLoopbackHost(hostname) {
979
+ return hostname === "localhost" || hostname === "localhost." || /^127(?:\.\d{1,3}){3}$/.test(hostname) || hostname === "[::1]";
980
+ }
981
+ function unsafeTransportError(surface, remediation, exposedData) {
982
+ return new AxlError(
983
+ "UNSAFE_TRANSPORT",
984
+ `${surface} requires ${remediation}. Remote HTTP can expose ${exposedData} in transit.`
985
+ );
986
+ }
987
+
705
988
  // src/providers/openai-compatible.ts
706
989
  function resolvePerModel(value, model, fallback) {
707
990
  if (value === void 0) return fallback;
@@ -845,6 +1128,11 @@ var OpenAICompatibleProvider = class {
845
1128
  const envBase = p.envBaseUrl ? process.env[p.envBaseUrl] : void 0;
846
1129
  const explicitBase = options.baseUrl ?? envBase;
847
1130
  this.baseUrl = (explicitBase ?? p.defaultBaseUrl).replace(/\/$/, "");
1131
+ assertSafeProviderBaseUrl(
1132
+ this.baseUrl,
1133
+ `${p.label ?? p.name} provider`,
1134
+ options.dangerouslyAllowInsecureHttp
1135
+ );
848
1136
  this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
849
1137
  const label = p.label ?? p.name;
850
1138
  if (p.requireExplicitBaseUrl && explicitBase === void 0) {
@@ -1745,6 +2033,11 @@ var OpenAIResponsesProvider = class {
1745
2033
  constructor(options = {}) {
1746
2034
  this.apiKeySource = options.apiKey ?? process.env.OPENAI_API_KEY ?? "";
1747
2035
  this.baseUrl = (options.baseUrl ?? process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1").replace(/\/$/, "");
2036
+ assertSafeProviderBaseUrl(
2037
+ this.baseUrl,
2038
+ "OpenAI Responses provider",
2039
+ options.dangerouslyAllowInsecureHttp
2040
+ );
1748
2041
  this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
1749
2042
  if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
1750
2043
  throw new Error("OpenAI API key is required. Set OPENAI_API_KEY or pass apiKey in options.");
@@ -2500,6 +2793,11 @@ var AnthropicProvider = class {
2500
2793
  constructor(options = {}) {
2501
2794
  this.apiKeySource = options.apiKey ?? process.env.ANTHROPIC_API_KEY ?? "";
2502
2795
  this.baseUrl = (options.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
2796
+ assertSafeProviderBaseUrl(
2797
+ this.baseUrl,
2798
+ "Anthropic provider",
2799
+ options.dangerouslyAllowInsecureHttp
2800
+ );
2503
2801
  this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
2504
2802
  if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
2505
2803
  throw new Error(
@@ -3190,6 +3488,11 @@ var GeminiProvider = class {
3190
3488
  /\/$/,
3191
3489
  ""
3192
3490
  );
3491
+ assertSafeProviderBaseUrl(
3492
+ this.baseUrl,
3493
+ "Google provider",
3494
+ options.dangerouslyAllowInsecureHttp
3495
+ );
3193
3496
  this.governor = options.rateLimit ? new RateLimiter(options.rateLimit) : void 0;
3194
3497
  if (typeof this.apiKeySource === "string" && !this.apiKeySource) {
3195
3498
  throw new Error("Google API key is required. Set GOOGLE_API_KEY or pass apiKey in options.");
@@ -4077,6 +4380,7 @@ var builtinFactories = {
4077
4380
  return new OpenAIProvider({
4078
4381
  apiKey: opts.apiKey,
4079
4382
  baseUrl: opts.baseUrl,
4383
+ dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
4080
4384
  rateLimit: opts.rateLimit
4081
4385
  });
4082
4386
  },
@@ -4089,6 +4393,7 @@ var builtinFactories = {
4089
4393
  return new OpenAIResponsesProvider({
4090
4394
  apiKey: opts.apiKey,
4091
4395
  baseUrl: opts.baseUrl,
4396
+ dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
4092
4397
  rateLimit: opts.rateLimit
4093
4398
  });
4094
4399
  },
@@ -4097,6 +4402,7 @@ var builtinFactories = {
4097
4402
  return new AnthropicProvider({
4098
4403
  apiKey: opts.apiKey,
4099
4404
  baseUrl: opts.baseUrl,
4405
+ dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
4100
4406
  rateLimit: opts.rateLimit
4101
4407
  });
4102
4408
  },
@@ -4105,6 +4411,7 @@ var builtinFactories = {
4105
4411
  return new GeminiProvider({
4106
4412
  apiKey: opts.apiKey,
4107
4413
  baseUrl: opts.baseUrl,
4414
+ dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
4108
4415
  rateLimit: opts.rateLimit
4109
4416
  });
4110
4417
  }
@@ -4116,6 +4423,7 @@ function presetFactory(profile) {
4116
4423
  profile,
4117
4424
  apiKey: opts.apiKey,
4118
4425
  baseUrl: opts.baseUrl,
4426
+ dangerouslyAllowInsecureHttp: opts.dangerouslyAllowInsecureHttp,
4119
4427
  authHeader: opts.authHeader,
4120
4428
  rateLimit: opts.rateLimit
4121
4429
  });
@@ -6273,7 +6581,15 @@ function cloneToolArguments(value) {
6273
6581
  }
6274
6582
  }
6275
6583
  function parseToolInvocation(options) {
6276
- const { toolCall, configuredTool, override, mcpCall, mcpTraceName, availableTools } = options;
6584
+ const {
6585
+ toolCall,
6586
+ configuredTool,
6587
+ override,
6588
+ mcpCall,
6589
+ mcpTraceName,
6590
+ availableTools,
6591
+ providerVisibleSchema
6592
+ } = options;
6277
6593
  const requestedTool = toolCall.function.name;
6278
6594
  if (!override && !configuredTool && !mcpCall) {
6279
6595
  return {
@@ -6333,7 +6649,7 @@ function parseToolInvocation(options) {
6333
6649
  args: parsedArguments,
6334
6650
  issues: toolArgumentIssues(error)
6335
6651
  },
6336
- modelMessage: INVALID_ARGUMENTS_MODEL_MESSAGE
6652
+ modelMessage: toolArgumentModelMessage(error, providerVisibleSchema) ?? INVALID_ARGUMENTS_MODEL_MESSAGE
6337
6653
  };
6338
6654
  }
6339
6655
  }
@@ -7236,7 +7552,7 @@ var WorkflowContext = class _WorkflowContext {
7236
7552
  /** Resolve, execute, and observe one non-handoff tool request. Rejections
7237
7553
  * never start. Every accepted request emits one terminal event before a
7238
7554
  * continuation message or abort escapes this coordinator. */
7239
- async observeToolInvocation(agent2, toolCall, currentMessages, availableTools) {
7555
+ async observeToolInvocation(agent2, toolCall, currentMessages, toolDefs) {
7240
7556
  const signal = this.currentSignal;
7241
7557
  signal?.throwIfAborted();
7242
7558
  const requestedTool = toolCall.function.name;
@@ -7246,6 +7562,7 @@ var WorkflowContext = class _WorkflowContext {
7246
7562
  const override = this.toolOverrides?.get(requestedTool);
7247
7563
  const mcpManager = this.mcpManager;
7248
7564
  const mcpMatch = !configuredTool ? mcpManager?.getToolsForAgent(agent2._config.mcp, agent2._config.mcpTools).find(({ tool: tool2 }) => tool2.name === requestedTool) : void 0;
7565
+ const providerVisibleSchema = toolDefs.find((tool2) => tool2.function.name === requestedTool)?.function.parameters;
7249
7566
  const parsed = parseToolInvocation({
7250
7567
  toolCall,
7251
7568
  configuredTool,
@@ -7254,7 +7571,8 @@ var WorkflowContext = class _WorkflowContext {
7254
7571
  mcpCall: (args) => mcpManager.callTool(requestedTool, args, mcpMatch.server),
7255
7572
  mcpTraceName: `${mcpMatch.server}:${requestedTool}`
7256
7573
  } : {},
7257
- availableTools
7574
+ availableTools: toolDefs.map((tool2) => tool2.function.name),
7575
+ ...configuredTool ? { providerVisibleSchema } : {}
7258
7576
  });
7259
7577
  signal?.throwIfAborted();
7260
7578
  if ("kind" in parsed) {
@@ -8065,12 +8383,7 @@ ${appendedSchemaText}`;
8065
8383
  return await handoffFn();
8066
8384
  }
8067
8385
  }
8068
- await this.observeToolInvocation(
8069
- agent2,
8070
- toolCall,
8071
- currentMessages,
8072
- toolDefs.map((tool2) => tool2.function.name)
8073
- );
8386
+ await this.observeToolInvocation(agent2, toolCall, currentMessages, toolDefs);
8074
8387
  }
8075
8388
  continue;
8076
8389
  }
@@ -11126,6 +11439,14 @@ var HttpMcpClient = class {
11126
11439
  this.config = config;
11127
11440
  this.name = config.name;
11128
11441
  this.baseUrl = (config.uri ?? "").replace(/\/$/, "");
11442
+ if (this.baseUrl) {
11443
+ assertSafeHttpEndpoint(
11444
+ this.baseUrl,
11445
+ `MCP server "${this.name}"`,
11446
+ config.dangerouslyAllowInsecureHttp,
11447
+ "tool arguments and results"
11448
+ );
11449
+ }
11129
11450
  }
11130
11451
  name;
11131
11452
  tools = [];
@@ -11155,7 +11476,9 @@ var HttpMcpClient = class {
11155
11476
  method: "POST",
11156
11477
  headers: { "Content-Type": "application/json" },
11157
11478
  body: JSON.stringify(request),
11158
- signal: AbortSignal.timeout(timeoutMs)
11479
+ signal: AbortSignal.timeout(timeoutMs),
11480
+ // Tool calls can be non-idempotent: never forward or retry their body.
11481
+ redirect: "manual"
11159
11482
  });
11160
11483
  if (!res.ok) {
11161
11484
  throw new Error(`MCP HTTP error (${res.status}): ${await res.text()}`);
@@ -13947,6 +14270,11 @@ var OpenAIEmbedder = class {
13947
14270
  }
13948
14271
  this.model = options.model ?? "text-embedding-3-small";
13949
14272
  this.baseUrl = options.baseUrl ?? "https://api.openai.com";
14273
+ assertSafeProviderBaseUrl(
14274
+ this.baseUrl,
14275
+ "OpenAI embedder",
14276
+ options.dangerouslyAllowInsecureHttp
14277
+ );
13950
14278
  this.dimensions = options.dimensions ?? 1536;
13951
14279
  }
13952
14280
  async embed(texts, signal) {