aieo 0.1.35 → 0.1.37

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
@@ -36,13 +36,20 @@ __export(index_exports, {
36
36
  OUTRO: () => OUTRO,
37
37
  PROVIDERS: () => PROVIDERS,
38
38
  SYSTEM: () => SYSTEM,
39
+ WEB_FETCH_TOOL_NAME: () => WEB_FETCH_TOOL_NAME,
40
+ WEB_SEARCH_TOOL_NAME: () => WEB_SEARCH_TOOL_NAME,
39
41
  addUsage: () => addUsage,
40
42
  callGenerateObject: () => callGenerateObject,
41
43
  callGenerateText: () => callGenerateText,
42
44
  callModel: () => callModel,
45
+ captureNativeFetchResults: () => captureNativeFetchResults,
46
+ captureNativeResults: () => captureNativeResults,
43
47
  computeSessionCost: () => computeSessionCost,
44
48
  consoleLogger: () => consoleLogger,
49
+ createWebFetch: () => createWebFetch,
50
+ createWebSearch: () => createWebSearch,
45
51
  emptyUsage: () => emptyUsage,
52
+ fetchUrl: () => fetchUrl,
46
53
  gatewayEnvForProviders: () => gatewayEnvForProviders,
47
54
  gatewayUrlFor: () => gatewayUrlFor,
48
55
  gatewayUrlForModel: () => gatewayUrlForModel,
@@ -55,13 +62,23 @@ __export(index_exports, {
55
62
  getProviderForModel: () => getProviderForModel,
56
63
  getProviderOptions: () => getProviderOptions,
57
64
  getProviderTool: () => getProviderTool,
65
+ getSearchApiKey: () => getSearchApiKey,
58
66
  getTokenPricing: () => getTokenPricing,
59
67
  hasApiKeyForProvider: () => hasApiKeyForProvider,
68
+ hasSearchApiKey: () => hasSearchApiKey,
69
+ htmlToText: () => htmlToText,
70
+ isPrivateAddress: () => isPrivateAddress,
71
+ linkifyCitations: () => linkifyCitations,
60
72
  loadModelPricing: () => loadModelPricing,
61
73
  normalizeApiKey: () => normalizeApiKey,
62
74
  normalizeCallerBaseURL: () => normalizeCallerBaseURL,
63
75
  normalizeUsage: () => normalizeUsage,
76
+ resolveFetchBackend: () => resolveFetchBackend,
64
77
  resolveLLMConfig: () => resolveLLMConfig,
78
+ resolveSearchBackend: () => resolveSearchBackend,
79
+ searchWeb: () => searchWeb,
80
+ stripCitations: () => stripCitations,
81
+ validateFetchUrl: () => validateFetchUrl,
65
82
  withLegacyUsage: () => withLegacyUsage,
66
83
  withProviderCacheUsage: () => withProviderCacheUsage
67
84
  });
@@ -4181,35 +4198,35 @@ async function prepareTools({
4181
4198
  return { tools: void 0, toolChoice: void 0, toolWarnings, betas };
4182
4199
  }
4183
4200
  const anthropicTools2 = [];
4184
- for (const tool4 of tools) {
4185
- switch (tool4.type) {
4201
+ for (const tool6 of tools) {
4202
+ switch (tool6.type) {
4186
4203
  case "function": {
4187
- const cacheControl = validator.getCacheControl(tool4.providerOptions, {
4204
+ const cacheControl = validator.getCacheControl(tool6.providerOptions, {
4188
4205
  type: "tool definition",
4189
4206
  canCache: true
4190
4207
  });
4191
- const anthropicOptions = (_a20 = tool4.providerOptions) == null ? void 0 : _a20.anthropic;
4208
+ const anthropicOptions = (_a20 = tool6.providerOptions) == null ? void 0 : _a20.anthropic;
4192
4209
  const eagerInputStreaming = (_b20 = anthropicOptions == null ? void 0 : anthropicOptions.eagerInputStreaming) != null ? _b20 : defaultEagerInputStreaming;
4193
4210
  const deferLoading = anthropicOptions == null ? void 0 : anthropicOptions.deferLoading;
4194
4211
  const allowedCallers = anthropicOptions == null ? void 0 : anthropicOptions.allowedCallers;
4195
- if (!supportsStrictTools && tool4.strict != null) {
4212
+ if (!supportsStrictTools && tool6.strict != null) {
4196
4213
  toolWarnings.push({
4197
4214
  type: "unsupported",
4198
4215
  feature: "strict",
4199
- details: `Tool '${tool4.name}' has strict: ${tool4.strict}, but strict mode is not supported by this provider. The strict property will be ignored.`
4216
+ details: `Tool '${tool6.name}' has strict: ${tool6.strict}, but strict mode is not supported by this provider. The strict property will be ignored.`
4200
4217
  });
4201
4218
  }
4202
4219
  anthropicTools2.push({
4203
- name: tool4.name,
4204
- description: tool4.description,
4205
- input_schema: tool4.inputSchema,
4220
+ name: tool6.name,
4221
+ description: tool6.description,
4222
+ input_schema: tool6.inputSchema,
4206
4223
  cache_control: cacheControl,
4207
4224
  ...eagerInputStreaming ? { eager_input_streaming: true } : {},
4208
- ...supportsStrictTools === true && tool4.strict != null ? { strict: tool4.strict } : {},
4225
+ ...supportsStrictTools === true && tool6.strict != null ? { strict: tool6.strict } : {},
4209
4226
  ...deferLoading != null ? { defer_loading: deferLoading } : {},
4210
4227
  ...allowedCallers != null ? { allowed_callers: allowedCallers } : {},
4211
- ...tool4.inputExamples != null ? {
4212
- input_examples: tool4.inputExamples.map(
4228
+ ...tool6.inputExamples != null ? {
4229
+ input_examples: tool6.inputExamples.map(
4213
4230
  (example) => example.input
4214
4231
  )
4215
4232
  } : {}
@@ -4217,13 +4234,13 @@ async function prepareTools({
4217
4234
  if (supportsStructuredOutput === true) {
4218
4235
  betas.add("structured-outputs-2025-11-13");
4219
4236
  }
4220
- if (tool4.inputExamples != null || allowedCallers != null) {
4237
+ if (tool6.inputExamples != null || allowedCallers != null) {
4221
4238
  betas.add("advanced-tool-use-2025-11-20");
4222
4239
  }
4223
4240
  break;
4224
4241
  }
4225
4242
  case "provider": {
4226
- switch (tool4.id) {
4243
+ switch (tool6.id) {
4227
4244
  case "anthropic.code_execution_20250522": {
4228
4245
  betas.add("code-execution-2025-05-22");
4229
4246
  anthropicTools2.push({
@@ -4253,9 +4270,9 @@ async function prepareTools({
4253
4270
  anthropicTools2.push({
4254
4271
  name: "computer",
4255
4272
  type: "computer_20250124",
4256
- display_width_px: tool4.args.displayWidthPx,
4257
- display_height_px: tool4.args.displayHeightPx,
4258
- display_number: tool4.args.displayNumber,
4273
+ display_width_px: tool6.args.displayWidthPx,
4274
+ display_height_px: tool6.args.displayHeightPx,
4275
+ display_number: tool6.args.displayNumber,
4259
4276
  cache_control: void 0
4260
4277
  });
4261
4278
  break;
@@ -4265,10 +4282,10 @@ async function prepareTools({
4265
4282
  anthropicTools2.push({
4266
4283
  name: "computer",
4267
4284
  type: "computer_20251124",
4268
- display_width_px: tool4.args.displayWidthPx,
4269
- display_height_px: tool4.args.displayHeightPx,
4270
- display_number: tool4.args.displayNumber,
4271
- enable_zoom: tool4.args.enableZoom,
4285
+ display_width_px: tool6.args.displayWidthPx,
4286
+ display_height_px: tool6.args.displayHeightPx,
4287
+ display_number: tool6.args.displayNumber,
4288
+ enable_zoom: tool6.args.enableZoom,
4272
4289
  cache_control: void 0
4273
4290
  });
4274
4291
  break;
@@ -4278,9 +4295,9 @@ async function prepareTools({
4278
4295
  anthropicTools2.push({
4279
4296
  name: "computer",
4280
4297
  type: "computer_20241022",
4281
- display_width_px: tool4.args.displayWidthPx,
4282
- display_height_px: tool4.args.displayHeightPx,
4283
- display_number: tool4.args.displayNumber,
4298
+ display_width_px: tool6.args.displayWidthPx,
4299
+ display_height_px: tool6.args.displayHeightPx,
4300
+ display_number: tool6.args.displayNumber,
4284
4301
  cache_control: void 0
4285
4302
  });
4286
4303
  break;
@@ -4314,7 +4331,7 @@ async function prepareTools({
4314
4331
  }
4315
4332
  case "anthropic.text_editor_20250728": {
4316
4333
  const args = await validateTypes({
4317
- value: tool4.args,
4334
+ value: tool6.args,
4318
4335
  schema: textEditor_20250728ArgsSchema
4319
4336
  });
4320
4337
  anthropicTools2.push({
@@ -4354,7 +4371,7 @@ async function prepareTools({
4354
4371
  case "anthropic.web_fetch_20250910": {
4355
4372
  betas.add("web-fetch-2025-09-10");
4356
4373
  const args = await validateTypes({
4357
- value: tool4.args,
4374
+ value: tool6.args,
4358
4375
  schema: webFetch_20250910ArgsSchema
4359
4376
  });
4360
4377
  anthropicTools2.push({
@@ -4372,7 +4389,7 @@ async function prepareTools({
4372
4389
  case "anthropic.web_fetch_20260209": {
4373
4390
  betas.add("code-execution-web-tools-2026-02-09");
4374
4391
  const args = await validateTypes({
4375
- value: tool4.args,
4392
+ value: tool6.args,
4376
4393
  schema: webFetch_20260209ArgsSchema
4377
4394
  });
4378
4395
  anthropicTools2.push({
@@ -4389,7 +4406,7 @@ async function prepareTools({
4389
4406
  }
4390
4407
  case "anthropic.web_search_20250305": {
4391
4408
  const args = await validateTypes({
4392
- value: tool4.args,
4409
+ value: tool6.args,
4393
4410
  schema: webSearch_20250305ArgsSchema
4394
4411
  });
4395
4412
  anthropicTools2.push({
@@ -4406,7 +4423,7 @@ async function prepareTools({
4406
4423
  case "anthropic.web_search_20260209": {
4407
4424
  betas.add("code-execution-web-tools-2026-02-09");
4408
4425
  const args = await validateTypes({
4409
- value: tool4.args,
4426
+ value: tool6.args,
4410
4427
  schema: webSearch_20260209ArgsSchema
4411
4428
  });
4412
4429
  anthropicTools2.push({
@@ -4437,7 +4454,7 @@ async function prepareTools({
4437
4454
  case "anthropic.advisor_20260301": {
4438
4455
  betas.add("advisor-tool-2026-03-01");
4439
4456
  const args = await validateTypes({
4440
- value: tool4.args,
4457
+ value: tool6.args,
4441
4458
  schema: advisor_20260301ArgsSchema
4442
4459
  });
4443
4460
  anthropicTools2.push({
@@ -4452,7 +4469,7 @@ async function prepareTools({
4452
4469
  default: {
4453
4470
  toolWarnings.push({
4454
4471
  type: "unsupported",
4455
- feature: `provider-defined tool ${tool4.id}`
4472
+ feature: `provider-defined tool ${tool6.id}`
4456
4473
  });
4457
4474
  break;
4458
4475
  }
@@ -4462,7 +4479,7 @@ async function prepareTools({
4462
4479
  default: {
4463
4480
  toolWarnings.push({
4464
4481
  type: "unsupported",
4465
- feature: `tool ${tool4}`
4482
+ feature: `tool ${tool6}`
4466
4483
  });
4467
4484
  break;
4468
4485
  }
@@ -6432,7 +6449,7 @@ var AnthropicMessagesLanguageModel = class {
6432
6449
  betas.add("skills-2025-10-02");
6433
6450
  betas.add("files-api-2025-04-14");
6434
6451
  if (!(tools == null ? void 0 : tools.some(
6435
- (tool4) => tool4.type === "provider" && (tool4.id === "anthropic.code_execution_20250825" || tool4.id === "anthropic.code_execution_20260120")
6452
+ (tool6) => tool6.type === "provider" && (tool6.id === "anthropic.code_execution_20250825" || tool6.id === "anthropic.code_execution_20260120")
6436
6453
  ))) {
6437
6454
  warnings.push({
6438
6455
  type: "other",
@@ -8035,12 +8052,12 @@ function hasWebTool20260209WithoutCodeExecution(tools) {
8035
8052
  }
8036
8053
  let hasWebTool20260209 = false;
8037
8054
  let hasCodeExecutionTool = false;
8038
- for (const tool4 of tools) {
8039
- if ("type" in tool4 && (tool4.type === "web_fetch_20260209" || tool4.type === "web_search_20260209")) {
8055
+ for (const tool6 of tools) {
8056
+ if ("type" in tool6 && (tool6.type === "web_fetch_20260209" || tool6.type === "web_search_20260209")) {
8040
8057
  hasWebTool20260209 = true;
8041
8058
  continue;
8042
8059
  }
8043
- if (tool4.name === "code_execution") {
8060
+ if (tool6.name === "code_execution") {
8044
8061
  hasCodeExecutionTool = true;
8045
8062
  break;
8046
8063
  }
@@ -11749,8 +11766,8 @@ function prepareTools2({
11749
11766
  if (tools == null) {
11750
11767
  return { tools: void 0, toolConfig: void 0, toolWarnings };
11751
11768
  }
11752
- const hasFunctionTools = tools.some((tool4) => tool4.type === "function");
11753
- const hasProviderTools = tools.some((tool4) => tool4.type === "provider");
11769
+ const hasFunctionTools = tools.some((tool6) => tool6.type === "function");
11770
+ const hasProviderTools = tools.some((tool6) => tool6.type === "provider");
11754
11771
  if (hasFunctionTools && hasProviderTools && !isGemini3orNewer) {
11755
11772
  toolWarnings.push({
11756
11773
  type: "unsupported",
@@ -11759,16 +11776,16 @@ function prepareTools2({
11759
11776
  }
11760
11777
  if (hasProviderTools) {
11761
11778
  const googleTools2 = [];
11762
- const ProviderTools = tools.filter((tool4) => tool4.type === "provider");
11763
- ProviderTools.forEach((tool4) => {
11764
- switch (tool4.id) {
11779
+ const ProviderTools = tools.filter((tool6) => tool6.type === "provider");
11780
+ ProviderTools.forEach((tool6) => {
11781
+ switch (tool6.id) {
11765
11782
  case "google.google_search":
11766
11783
  if (isGemini2orNewer) {
11767
- googleTools2.push({ googleSearch: { ...tool4.args } });
11784
+ googleTools2.push({ googleSearch: { ...tool6.args } });
11768
11785
  } else {
11769
11786
  toolWarnings.push({
11770
11787
  type: "unsupported",
11771
- feature: `provider-defined tool ${tool4.id}`,
11788
+ feature: `provider-defined tool ${tool6.id}`,
11772
11789
  details: "Google Search requires Gemini 2.0 or newer."
11773
11790
  });
11774
11791
  }
@@ -11779,7 +11796,7 @@ function prepareTools2({
11779
11796
  } else {
11780
11797
  toolWarnings.push({
11781
11798
  type: "unsupported",
11782
- feature: `provider-defined tool ${tool4.id}`,
11799
+ feature: `provider-defined tool ${tool6.id}`,
11783
11800
  details: "Enterprise Web Search requires Gemini 2.0 or newer."
11784
11801
  });
11785
11802
  }
@@ -11790,7 +11807,7 @@ function prepareTools2({
11790
11807
  } else {
11791
11808
  toolWarnings.push({
11792
11809
  type: "unsupported",
11793
- feature: `provider-defined tool ${tool4.id}`,
11810
+ feature: `provider-defined tool ${tool6.id}`,
11794
11811
  details: "The URL context tool is not supported with other Gemini models than Gemini 2."
11795
11812
  });
11796
11813
  }
@@ -11801,18 +11818,18 @@ function prepareTools2({
11801
11818
  } else {
11802
11819
  toolWarnings.push({
11803
11820
  type: "unsupported",
11804
- feature: `provider-defined tool ${tool4.id}`,
11821
+ feature: `provider-defined tool ${tool6.id}`,
11805
11822
  details: "The code execution tool is not supported with other Gemini models than Gemini 2."
11806
11823
  });
11807
11824
  }
11808
11825
  break;
11809
11826
  case "google.file_search":
11810
11827
  if (supportsFileSearch) {
11811
- googleTools2.push({ fileSearch: { ...tool4.args } });
11828
+ googleTools2.push({ fileSearch: { ...tool6.args } });
11812
11829
  } else {
11813
11830
  toolWarnings.push({
11814
11831
  type: "unsupported",
11815
- feature: `provider-defined tool ${tool4.id}`,
11832
+ feature: `provider-defined tool ${tool6.id}`,
11816
11833
  details: "The file search tool is only supported with Gemini 2.5 models and Gemini 3 models."
11817
11834
  });
11818
11835
  }
@@ -11823,16 +11840,16 @@ function prepareTools2({
11823
11840
  retrieval: {
11824
11841
  vertex_rag_store: {
11825
11842
  rag_resources: {
11826
- rag_corpus: tool4.args.ragCorpus
11843
+ rag_corpus: tool6.args.ragCorpus
11827
11844
  },
11828
- similarity_top_k: tool4.args.topK
11845
+ similarity_top_k: tool6.args.topK
11829
11846
  }
11830
11847
  }
11831
11848
  });
11832
11849
  } else {
11833
11850
  toolWarnings.push({
11834
11851
  type: "unsupported",
11835
- feature: `provider-defined tool ${tool4.id}`,
11852
+ feature: `provider-defined tool ${tool6.id}`,
11836
11853
  details: "The RAG store tool is not supported with other Gemini models than Gemini 2."
11837
11854
  });
11838
11855
  }
@@ -11843,7 +11860,7 @@ function prepareTools2({
11843
11860
  } else {
11844
11861
  toolWarnings.push({
11845
11862
  type: "unsupported",
11846
- feature: `provider-defined tool ${tool4.id}`,
11863
+ feature: `provider-defined tool ${tool6.id}`,
11847
11864
  details: "The Google Maps grounding tool is not supported with Gemini models other than Gemini 2 or newer."
11848
11865
  });
11849
11866
  }
@@ -11851,19 +11868,19 @@ function prepareTools2({
11851
11868
  default:
11852
11869
  toolWarnings.push({
11853
11870
  type: "unsupported",
11854
- feature: `provider-defined tool ${tool4.id}`
11871
+ feature: `provider-defined tool ${tool6.id}`
11855
11872
  });
11856
11873
  break;
11857
11874
  }
11858
11875
  });
11859
11876
  if (hasFunctionTools && isGemini3orNewer && googleTools2.length > 0) {
11860
11877
  const functionDeclarations2 = [];
11861
- for (const tool4 of tools) {
11862
- if (tool4.type === "function") {
11878
+ for (const tool6 of tools) {
11879
+ if (tool6.type === "function") {
11863
11880
  functionDeclarations2.push({
11864
- name: tool4.name,
11865
- description: (_a20 = tool4.description) != null ? _a20 : "",
11866
- parameters: convertJSONSchemaToOpenAPISchema(tool4.inputSchema)
11881
+ name: tool6.name,
11882
+ description: (_a20 = tool6.description) != null ? _a20 : "",
11883
+ parameters: convertJSONSchemaToOpenAPISchema(tool6.inputSchema)
11867
11884
  });
11868
11885
  }
11869
11886
  }
@@ -11903,22 +11920,22 @@ function prepareTools2({
11903
11920
  }
11904
11921
  const functionDeclarations = [];
11905
11922
  let hasStrictTools = false;
11906
- for (const tool4 of tools) {
11907
- switch (tool4.type) {
11923
+ for (const tool6 of tools) {
11924
+ switch (tool6.type) {
11908
11925
  case "function":
11909
11926
  functionDeclarations.push({
11910
- name: tool4.name,
11911
- description: (_b20 = tool4.description) != null ? _b20 : "",
11912
- parameters: convertJSONSchemaToOpenAPISchema(tool4.inputSchema)
11927
+ name: tool6.name,
11928
+ description: (_b20 = tool6.description) != null ? _b20 : "",
11929
+ parameters: convertJSONSchemaToOpenAPISchema(tool6.inputSchema)
11913
11930
  });
11914
- if (tool4.strict === true) {
11931
+ if (tool6.strict === true) {
11915
11932
  hasStrictTools = true;
11916
11933
  }
11917
11934
  break;
11918
11935
  default:
11919
11936
  toolWarnings.push({
11920
11937
  type: "unsupported",
11921
- feature: `function tool ${tool4.name}`
11938
+ feature: `function tool ${tool6.name}`
11922
11939
  });
11923
11940
  break;
11924
11941
  }
@@ -12261,7 +12278,7 @@ var GoogleGenerativeAILanguageModel = class {
12261
12278
  }
12262
12279
  const isVertexProvider = this.config.provider.startsWith("google.vertex.");
12263
12280
  if ((tools == null ? void 0 : tools.some(
12264
- (tool4) => tool4.type === "provider" && tool4.id === "google.vertex_rag_store"
12281
+ (tool6) => tool6.type === "provider" && tool6.id === "google.vertex_rag_store"
12265
12282
  )) && !isVertexProvider) {
12266
12283
  warnings.push({
12267
12284
  type: "other",
@@ -16994,23 +17011,23 @@ function prepareChatTools({
16994
17011
  return { tools: void 0, toolChoice: void 0, toolWarnings };
16995
17012
  }
16996
17013
  const openaiTools2 = [];
16997
- for (const tool4 of tools) {
16998
- switch (tool4.type) {
17014
+ for (const tool6 of tools) {
17015
+ switch (tool6.type) {
16999
17016
  case "function":
17000
17017
  openaiTools2.push({
17001
17018
  type: "function",
17002
17019
  function: {
17003
- name: tool4.name,
17004
- description: tool4.description,
17005
- parameters: tool4.inputSchema,
17006
- ...tool4.strict != null ? { strict: tool4.strict } : {}
17020
+ name: tool6.name,
17021
+ description: tool6.description,
17022
+ parameters: tool6.inputSchema,
17023
+ ...tool6.strict != null ? { strict: tool6.strict } : {}
17007
17024
  }
17008
17025
  });
17009
17026
  break;
17010
17027
  default:
17011
17028
  toolWarnings.push({
17012
17029
  type: "unsupported",
17013
- feature: `tool type: ${tool4.type}`
17030
+ feature: `tool type: ${tool6.type}`
17014
17031
  });
17015
17032
  break;
17016
17033
  }
@@ -20721,26 +20738,26 @@ async function prepareResponsesTools({
20721
20738
  }
20722
20739
  const openaiTools2 = [];
20723
20740
  const resolvedCustomProviderToolNames = customProviderToolNames != null ? customProviderToolNames : /* @__PURE__ */ new Set();
20724
- for (const tool4 of tools) {
20725
- switch (tool4.type) {
20741
+ for (const tool6 of tools) {
20742
+ switch (tool6.type) {
20726
20743
  case "function": {
20727
- const openaiOptions = (_a20 = tool4.providerOptions) == null ? void 0 : _a20.openai;
20744
+ const openaiOptions = (_a20 = tool6.providerOptions) == null ? void 0 : _a20.openai;
20728
20745
  const deferLoading = openaiOptions == null ? void 0 : openaiOptions.deferLoading;
20729
20746
  openaiTools2.push({
20730
20747
  type: "function",
20731
- name: tool4.name,
20732
- description: tool4.description,
20733
- parameters: tool4.inputSchema,
20734
- ...tool4.strict != null ? { strict: tool4.strict } : {},
20748
+ name: tool6.name,
20749
+ description: tool6.description,
20750
+ parameters: tool6.inputSchema,
20751
+ ...tool6.strict != null ? { strict: tool6.strict } : {},
20735
20752
  ...deferLoading != null ? { defer_loading: deferLoading } : {}
20736
20753
  });
20737
20754
  break;
20738
20755
  }
20739
20756
  case "provider": {
20740
- switch (tool4.id) {
20757
+ switch (tool6.id) {
20741
20758
  case "openai.file_search": {
20742
20759
  const args = await validateTypes3({
20743
- value: tool4.args,
20760
+ value: tool6.args,
20744
20761
  schema: fileSearchArgsSchema2
20745
20762
  });
20746
20763
  openaiTools2.push({
@@ -20763,7 +20780,7 @@ async function prepareResponsesTools({
20763
20780
  }
20764
20781
  case "openai.shell": {
20765
20782
  const args = await validateTypes3({
20766
- value: tool4.args,
20783
+ value: tool6.args,
20767
20784
  schema: shellArgsSchema
20768
20785
  });
20769
20786
  openaiTools2.push({
@@ -20782,7 +20799,7 @@ async function prepareResponsesTools({
20782
20799
  }
20783
20800
  case "openai.web_search_preview": {
20784
20801
  const args = await validateTypes3({
20785
- value: tool4.args,
20802
+ value: tool6.args,
20786
20803
  schema: webSearchPreviewArgsSchema
20787
20804
  });
20788
20805
  openaiTools2.push({
@@ -20794,7 +20811,7 @@ async function prepareResponsesTools({
20794
20811
  }
20795
20812
  case "openai.web_search": {
20796
20813
  const args = await validateTypes3({
20797
- value: tool4.args,
20814
+ value: tool6.args,
20798
20815
  schema: webSearchArgsSchema
20799
20816
  });
20800
20817
  openaiTools2.push({
@@ -20808,7 +20825,7 @@ async function prepareResponsesTools({
20808
20825
  }
20809
20826
  case "openai.code_interpreter": {
20810
20827
  const args = await validateTypes3({
20811
- value: tool4.args,
20828
+ value: tool6.args,
20812
20829
  schema: codeInterpreterArgsSchema
20813
20830
  });
20814
20831
  openaiTools2.push({
@@ -20819,7 +20836,7 @@ async function prepareResponsesTools({
20819
20836
  }
20820
20837
  case "openai.image_generation": {
20821
20838
  const args = await validateTypes3({
20822
- value: tool4.args,
20839
+ value: tool6.args,
20823
20840
  schema: imageGenerationArgsSchema
20824
20841
  });
20825
20842
  openaiTools2.push({
@@ -20842,7 +20859,7 @@ async function prepareResponsesTools({
20842
20859
  }
20843
20860
  case "openai.mcp": {
20844
20861
  const args = await validateTypes3({
20845
- value: tool4.args,
20862
+ value: tool6.args,
20846
20863
  schema: mcpArgsSchema
20847
20864
  });
20848
20865
  const mapApprovalFilter = (filter4) => ({
@@ -20868,7 +20885,7 @@ async function prepareResponsesTools({
20868
20885
  }
20869
20886
  case "openai.custom": {
20870
20887
  const args = await validateTypes3({
20871
- value: tool4.args,
20888
+ value: tool6.args,
20872
20889
  schema: customArgsSchema
20873
20890
  });
20874
20891
  openaiTools2.push({
@@ -20882,7 +20899,7 @@ async function prepareResponsesTools({
20882
20899
  }
20883
20900
  case "openai.tool_search": {
20884
20901
  const args = await validateTypes3({
20885
- value: tool4.args,
20902
+ value: tool6.args,
20886
20903
  schema: toolSearchArgsSchema
20887
20904
  });
20888
20905
  openaiTools2.push({
@@ -20899,7 +20916,7 @@ async function prepareResponsesTools({
20899
20916
  default:
20900
20917
  toolWarnings.push({
20901
20918
  type: "unsupported",
20902
- feature: `function tool ${tool4}`
20919
+ feature: `function tool ${tool6}`
20903
20920
  });
20904
20921
  break;
20905
20922
  }
@@ -21071,7 +21088,7 @@ var OpenAIResponsesLanguageModel = class {
21071
21088
  "openai.apply_patch": "apply_patch",
21072
21089
  "openai.tool_search": "tool_search"
21073
21090
  },
21074
- resolveProviderToolName: (tool4) => tool4.id === "openai.custom" ? tool4.args.name : void 0
21091
+ resolveProviderToolName: (tool6) => tool6.id === "openai.custom" ? tool6.args.name : void 0
21075
21092
  });
21076
21093
  const customProviderToolNames = /* @__PURE__ */ new Set();
21077
21094
  const {
@@ -21108,14 +21125,14 @@ var OpenAIResponsesLanguageModel = class {
21108
21125
  }
21109
21126
  }
21110
21127
  function hasOpenAITool(id) {
21111
- return (tools == null ? void 0 : tools.find((tool4) => tool4.type === "provider" && tool4.id === id)) != null;
21128
+ return (tools == null ? void 0 : tools.find((tool6) => tool6.type === "provider" && tool6.id === id)) != null;
21112
21129
  }
21113
21130
  const topLogprobs = typeof (openaiOptions == null ? void 0 : openaiOptions.logprobs) === "number" ? openaiOptions == null ? void 0 : openaiOptions.logprobs : (openaiOptions == null ? void 0 : openaiOptions.logprobs) === true ? TOP_LOGPROBS_MAX : void 0;
21114
21131
  if (topLogprobs) {
21115
21132
  addInclude("message.output_text.logprobs");
21116
21133
  }
21117
21134
  const webSearchToolName = (_e = tools == null ? void 0 : tools.find(
21118
- (tool4) => tool4.type === "provider" && (tool4.id === "openai.web_search" || tool4.id === "openai.web_search_preview")
21135
+ (tool6) => tool6.type === "provider" && (tool6.id === "openai.web_search" || tool6.id === "openai.web_search_preview")
21119
21136
  )) == null ? void 0 : _e.name;
21120
21137
  if (webSearchToolName) {
21121
21138
  addInclude("web_search_call.action.sources");
@@ -21229,7 +21246,7 @@ var OpenAIResponsesLanguageModel = class {
21229
21246
  delete baseArgs.service_tier;
21230
21247
  }
21231
21248
  const shellToolEnvType = (_i = (_h = (_g = tools == null ? void 0 : tools.find(
21232
- (tool4) => tool4.type === "provider" && tool4.id === "openai.shell"
21249
+ (tool6) => tool6.type === "provider" && tool6.id === "openai.shell"
21233
21250
  )) == null ? void 0 : _g.args) == null ? void 0 : _h.environment) == null ? void 0 : _i.type;
21234
21251
  const isShellProviderExecuted = shellToolEnvType === "containerAuto" || shellToolEnvType === "containerReference";
21235
21252
  return {
@@ -23895,8 +23912,604 @@ var OUTRO = `
23895
23912
  Please write all the necessary code to fully implement the feature end-to-end. Do not make mock data or example placeholders!!!
23896
23913
  `;
23897
23914
 
23915
+ // src/search.ts
23916
+ var import_ai2 = require("ai");
23917
+ var import_zod = require("zod");
23918
+ var WEB_SEARCH_TOOL_NAME = "web_search";
23919
+ var EXA_SEARCH_URL = "https://api.exa.ai/search";
23920
+ var DEFAULT_MAX_USES = 3;
23921
+ var DEFAULT_NUM_RESULTS = 5;
23922
+ var DEFAULT_MAX_CHARACTERS = 4e3;
23923
+ function getSearchApiKey() {
23924
+ return normalizeApiKey(process.env.EXA_API_KEY);
23925
+ }
23926
+ function hasSearchApiKey() {
23927
+ return !!getSearchApiKey();
23928
+ }
23929
+ function resolveSearchBackend(provider) {
23930
+ return provider === "anthropic" ? "anthropic" : "exa";
23931
+ }
23932
+ async function searchWeb(query, opts = {}) {
23933
+ const apiKey = normalizeApiKey(opts.apiKey) || getSearchApiKey();
23934
+ if (!apiKey) throw new Error("EXA_API_KEY not configured");
23935
+ const res = await fetch(EXA_SEARCH_URL, {
23936
+ method: "POST",
23937
+ headers: { "content-type": "application/json", "x-api-key": apiKey },
23938
+ signal: opts.abortSignal,
23939
+ body: JSON.stringify({
23940
+ query,
23941
+ type: "auto",
23942
+ numResults: opts.numResults ?? DEFAULT_NUM_RESULTS,
23943
+ contents: {
23944
+ text: { maxCharacters: opts.maxCharacters ?? DEFAULT_MAX_CHARACTERS }
23945
+ },
23946
+ ...opts.allowedDomains?.length ? { includeDomains: opts.allowedDomains } : {},
23947
+ ...opts.blockedDomains?.length ? { excludeDomains: opts.blockedDomains } : {}
23948
+ })
23949
+ });
23950
+ if (!res.ok) {
23951
+ const body = await res.text().catch(() => "");
23952
+ throw new Error(`Exa search failed (${res.status}): ${body.slice(0, 300)}`);
23953
+ }
23954
+ const json = await res.json();
23955
+ const results = Array.isArray(json.results) ? json.results : [];
23956
+ return results.filter((r) => typeof r.url === "string").map((r) => ({
23957
+ url: r.url,
23958
+ title: r.title ?? null,
23959
+ pageAge: r.publishedDate ?? null,
23960
+ ...r.text ? { text: r.text } : {},
23961
+ type: "web_search_result"
23962
+ }));
23963
+ }
23964
+ function citationSnippet(backend) {
23965
+ const indexRule = backend === "exa" ? "Every result carries an `index` field. Use that number exactly as returned \u2014 never invent or renumber one. Indices are global across all searches in this conversation." : "Use the index Anthropic assigns to the search result you are citing.";
23966
+ return `
23967
+
23968
+ ## Citing web search results
23969
+ When you use information from a \`${WEB_SEARCH_TOOL_NAME}\` result, cite it inline as \`<cite index="N">anchor text</cite>\`. ${indexRule}
23970
+
23971
+ The anchor text is REQUIRED and becomes the link text \u2014 put the words being sourced inside the tag, e.g. \`<cite index="3">runs on the Lightning Network</cite>\`. Never emit an empty tag like \`<cite index="3"></cite>\`; a trailing marker with no anchor produces a broken link.
23972
+
23973
+ Do NOT name sources parenthetically (e.g. \`(Bitcoin Magazine)\`) or as bare URLs \u2014 observed models fall back to that style and it produces no link. Every source reference must be a cite tag.`;
23974
+ }
23975
+ function createWebSearch(opts) {
23976
+ const results = [];
23977
+ const backend = opts.backend ?? resolveSearchBackend(opts.provider);
23978
+ const maxUses = opts.maxUses ?? DEFAULT_MAX_USES;
23979
+ if (backend === "anthropic") {
23980
+ const apiKey = normalizeApiKey(opts.apiKey) || normalizeApiKey(process.env.ANTHROPIC_API_KEY);
23981
+ if (!apiKey) {
23982
+ return emptyHandle(results);
23983
+ }
23984
+ const baseURL = getGatewayBaseURL("anthropic");
23985
+ const anthropic2 = createAnthropic({ apiKey, ...baseURL && { baseURL } });
23986
+ return {
23987
+ tool: anthropic2.tools.webSearch_20250305({
23988
+ maxUses,
23989
+ ...opts.allowedDomains?.length ? { allowedDomains: opts.allowedDomains } : {},
23990
+ ...opts.blockedDomains?.length ? { blockedDomains: opts.blockedDomains } : {}
23991
+ }),
23992
+ backend,
23993
+ native: true,
23994
+ results,
23995
+ capture: (stepContent) => captureNativeResults(stepContent, results),
23996
+ promptSnippet: opts.citations ? citationSnippet("anthropic") : "",
23997
+ formatOutput: (markdown) => formatOutput(markdown, results, !!opts.citations)
23998
+ };
23999
+ }
24000
+ const searchApiKey = normalizeApiKey(opts.searchApiKey) || getSearchApiKey();
24001
+ if (!searchApiKey) {
24002
+ return emptyHandle(results);
24003
+ }
24004
+ let uses = 0;
24005
+ return {
24006
+ tool: (0, import_ai2.tool)({
24007
+ description: "Search the web for current information. Returns numbered results with page text. Prefer one specific query per call over broad multi-topic queries.",
24008
+ inputSchema: import_zod.z.object({
24009
+ query: import_zod.z.string().describe("The search query."),
24010
+ num_results: import_zod.z.number().int().min(1).max(10).optional().describe("How many results to return. Default 5.")
24011
+ }),
24012
+ execute: async ({
24013
+ query,
24014
+ num_results
24015
+ }) => {
24016
+ if (uses >= maxUses) {
24017
+ return {
24018
+ error: `web_search budget exhausted (${maxUses} calls). Answer with what you have.`
24019
+ };
24020
+ }
24021
+ uses++;
24022
+ try {
24023
+ const hits = await searchWeb(query, {
24024
+ numResults: num_results ?? opts.numResults,
24025
+ maxCharacters: opts.maxCharacters,
24026
+ allowedDomains: opts.allowedDomains,
24027
+ blockedDomains: opts.blockedDomains,
24028
+ apiKey: searchApiKey,
24029
+ abortSignal: opts.abortSignal
24030
+ });
24031
+ return hits.map((hit) => {
24032
+ const indexed = { ...hit, index: results.length + 1 };
24033
+ results.push(indexed);
24034
+ return indexed;
24035
+ });
24036
+ } catch (err) {
24037
+ return {
24038
+ error: `Search failed: ${err instanceof Error ? err.message : String(err)}`
24039
+ };
24040
+ }
24041
+ }
24042
+ }),
24043
+ backend,
24044
+ native: false,
24045
+ results,
24046
+ // Exa results are appended by `execute` above; walking the step
24047
+ // would double-count them.
24048
+ capture: () => {
24049
+ },
24050
+ promptSnippet: opts.citations ? citationSnippet("exa") : "",
24051
+ formatOutput: (markdown) => formatOutput(markdown, results, !!opts.citations)
24052
+ };
24053
+ }
24054
+ function emptyHandle(results) {
24055
+ return {
24056
+ tool: void 0,
24057
+ backend: void 0,
24058
+ native: false,
24059
+ results,
24060
+ capture: () => {
24061
+ },
24062
+ promptSnippet: "",
24063
+ formatOutput: (markdown) => formatOutput(markdown, results, false)
24064
+ };
24065
+ }
24066
+ function formatOutput(markdown, results, citations) {
24067
+ return citations ? linkifyCitations(markdown, results) : { content: stripCitations(markdown), converted: 0, skipped: 0 };
24068
+ }
24069
+ function captureNativeResults(stepContent, target) {
24070
+ if (!Array.isArray(stepContent)) return;
24071
+ for (const content of stepContent) {
24072
+ if (content?.type !== "tool-result") continue;
24073
+ if (content?.toolName !== WEB_SEARCH_TOOL_NAME) continue;
24074
+ const body = content.output ?? content.result ?? null;
24075
+ if (!Array.isArray(body)) continue;
24076
+ for (const r of body) {
24077
+ if (r && typeof r === "object" && typeof r.url === "string") {
24078
+ target.push({
24079
+ url: r.url,
24080
+ title: r.title ?? null,
24081
+ pageAge: r.pageAge ?? null,
24082
+ // Assigned here too, so `results` is shape-identical to the
24083
+ // Exa path. Anthropic's model derives its own indices from
24084
+ // the same ordering, so these agree by construction.
24085
+ index: target.length + 1,
24086
+ type: "web_search_result"
24087
+ });
24088
+ }
24089
+ }
24090
+ }
24091
+ }
24092
+ function stripCitations(markdown) {
24093
+ return markdown.replace(/[ \t]*<cite\b[^>]*>\s*<\/cite>/g, "").replace(/<cite\b[^>]*>(.*?)<\/cite>/g, "$1").replace(/<\/?cite\b[^>]*>/g, "");
24094
+ }
24095
+ function linkifyCitations(markdown, results) {
24096
+ let converted = 0;
24097
+ let skipped = 0;
24098
+ const content = markdown.replace(
24099
+ /<cite index="(\d+)(?:-\d+(?:,\d+-\d+)*)?">(.*?)<\/cite>/g,
24100
+ (_match, indexStr, anchor) => {
24101
+ const index = parseInt(indexStr, 10);
24102
+ const hit = results[index - 1];
24103
+ if (!hit || typeof hit.url !== "string") {
24104
+ skipped++;
24105
+ return anchor;
24106
+ }
24107
+ converted++;
24108
+ const text = anchor.trim() ? anchor.replace(/\]/g, "\\]") : `[${index}]`;
24109
+ return `[${text}](${hit.url})`;
24110
+ }
24111
+ );
24112
+ return { content, converted, skipped };
24113
+ }
24114
+
24115
+ // src/fetch.ts
24116
+ var import_ai3 = require("ai");
24117
+ var import_zod2 = require("zod");
24118
+ var import_node_net = require("net");
24119
+ var import_promises = require("dns/promises");
24120
+ var WEB_FETCH_TOOL_NAME = "web_fetch";
24121
+ var DEFAULT_MAX_USES2 = 5;
24122
+ var DEFAULT_MAX_CHARACTERS2 = 4e4;
24123
+ var CHARS_PER_TOKEN = 4;
24124
+ var DEFAULT_TIMEOUT_MS = 2e4;
24125
+ var MAX_REDIRECTS = 5;
24126
+ var MAX_BODY_BYTES = 4 * 1024 * 1024;
24127
+ var USER_AGENT = "aieo-web-fetch/1 (+https://github.com/stakwork/stakgraph)";
24128
+ var ACCEPT = "text/html, application/xhtml+xml, text/plain;q=0.9, application/json;q=0.9, text/*;q=0.8, */*;q=0.5";
24129
+ function resolveFetchBackend(provider) {
24130
+ return provider === "anthropic" ? "anthropic" : "http";
24131
+ }
24132
+ var PRIVATE_ADDRESSES = buildBlockList();
24133
+ function buildBlockList() {
24134
+ const b = new import_node_net.BlockList();
24135
+ const v42 = [
24136
+ ["0.0.0.0", 8],
24137
+ // "this" network
24138
+ ["10.0.0.0", 8],
24139
+ // RFC 1918
24140
+ ["100.64.0.0", 10],
24141
+ // carrier-grade NAT
24142
+ ["127.0.0.0", 8],
24143
+ // loopback
24144
+ ["169.254.0.0", 16],
24145
+ // link-local, incl. cloud metadata at 169.254.169.254
24146
+ ["172.16.0.0", 12],
24147
+ // RFC 1918
24148
+ ["192.0.0.0", 24],
24149
+ // IETF protocol assignments
24150
+ ["192.168.0.0", 16],
24151
+ // RFC 1918
24152
+ ["198.18.0.0", 15],
24153
+ // benchmarking
24154
+ ["224.0.0.0", 4],
24155
+ // multicast
24156
+ ["240.0.0.0", 4]
24157
+ // reserved, incl. broadcast
24158
+ ];
24159
+ for (const [net, prefix] of v42) b.addSubnet(net, prefix, "ipv4");
24160
+ b.addAddress("::", "ipv6");
24161
+ b.addAddress("::1", "ipv6");
24162
+ const v6 = [
24163
+ ["64:ff9b::", 96],
24164
+ // NAT64
24165
+ ["2002::", 16],
24166
+ // 6to4
24167
+ ["fc00::", 7],
24168
+ // unique local
24169
+ ["fe80::", 10],
24170
+ // link-local
24171
+ ["fec0::", 10],
24172
+ // site-local (deprecated, still routed on old gear)
24173
+ ["ff00::", 8]
24174
+ // multicast
24175
+ ];
24176
+ for (const [net, prefix] of v6) b.addSubnet(net, prefix, "ipv6");
24177
+ return b;
24178
+ }
24179
+ function isPrivateAddress(ip) {
24180
+ const bare = ip.startsWith("[") && ip.endsWith("]") ? ip.slice(1, -1) : ip;
24181
+ const family = (0, import_node_net.isIP)(bare);
24182
+ try {
24183
+ if (family === 4) return PRIVATE_ADDRESSES.check(bare, "ipv4");
24184
+ if (family === 6) return PRIVATE_ADDRESSES.check(bare, "ipv6");
24185
+ } catch {
24186
+ }
24187
+ return true;
24188
+ }
24189
+ var defaultLookup = async (hostname) => {
24190
+ const addresses = await (0, import_promises.lookup)(hostname, { all: true });
24191
+ return addresses.map((a) => a.address);
24192
+ };
24193
+ function matchesDomain(host, domains) {
24194
+ return domains.some((d) => {
24195
+ const dom = d.trim().toLowerCase().replace(/^https?:\/\//, "").replace(/\/.*$/, "").replace(/^\*\./, "").replace(/\.$/, "");
24196
+ return !!dom && (host === dom || host.endsWith("." + dom));
24197
+ });
24198
+ }
24199
+ async function validateFetchUrl(raw, opts = {}) {
24200
+ let url;
24201
+ try {
24202
+ url = new URL(raw);
24203
+ } catch {
24204
+ throw new Error(`Not a valid absolute URL: ${raw}`);
24205
+ }
24206
+ if (url.protocol !== "http:" && url.protocol !== "https:") {
24207
+ throw new Error(`Only http(s) URLs can be fetched, got ${url.protocol}`);
24208
+ }
24209
+ if (url.username || url.password) {
24210
+ throw new Error("URLs with embedded credentials are not fetched");
24211
+ }
24212
+ const host = url.hostname.replace(/\.$/, "").toLowerCase();
24213
+ if (!host) throw new Error("URL has no host");
24214
+ if (host === "localhost" || host.endsWith(".localhost")) {
24215
+ throw new Error("Refusing to fetch a local address");
24216
+ }
24217
+ if (opts.allowedDomains?.length && !matchesDomain(host, opts.allowedDomains)) {
24218
+ throw new Error(`${host} is not in the allowed domains`);
24219
+ }
24220
+ if (opts.blockedDomains?.length && matchesDomain(host, opts.blockedDomains)) {
24221
+ throw new Error(`${host} is a blocked domain`);
24222
+ }
24223
+ const literal = host.startsWith("[") ? host.slice(1, -1) : host;
24224
+ if ((0, import_node_net.isIP)(literal)) {
24225
+ if (isPrivateAddress(literal)) {
24226
+ throw new Error("Refusing to fetch a private or reserved address");
24227
+ }
24228
+ return url;
24229
+ }
24230
+ let addresses;
24231
+ try {
24232
+ addresses = await (opts.lookup ?? defaultLookup)(host);
24233
+ } catch (err) {
24234
+ throw new Error(
24235
+ `Could not resolve ${host}: ${err instanceof Error ? err.message : String(err)}`
24236
+ );
24237
+ }
24238
+ if (!addresses.length) throw new Error(`Could not resolve ${host}`);
24239
+ if (addresses.some(isPrivateAddress)) {
24240
+ throw new Error(`Refusing to fetch ${host}: it resolves to a private or reserved address`);
24241
+ }
24242
+ return url;
24243
+ }
24244
+ async function fetchUrl(rawUrl, opts = {}) {
24245
+ const maxCharacters = opts.maxCharacters ?? DEFAULT_MAX_CHARACTERS2;
24246
+ const signals = [AbortSignal.timeout(opts.timeoutMs ?? DEFAULT_TIMEOUT_MS)];
24247
+ if (opts.abortSignal) signals.push(opts.abortSignal);
24248
+ const signal = AbortSignal.any(signals);
24249
+ let url = await validateFetchUrl(rawUrl, opts);
24250
+ let res;
24251
+ for (let hop = 0; ; hop++) {
24252
+ try {
24253
+ res = await fetch(url, {
24254
+ method: "GET",
24255
+ redirect: "manual",
24256
+ signal,
24257
+ headers: { "user-agent": USER_AGENT, accept: ACCEPT }
24258
+ });
24259
+ } catch (err) {
24260
+ throw new Error(`Could not connect to ${url.hostname}: ${describeTransportError(err)}`);
24261
+ }
24262
+ if (!isRedirect(res.status)) break;
24263
+ const location = res.headers.get("location");
24264
+ await discard(res);
24265
+ if (!location) throw new Error(`Redirect (${res.status}) without a Location header`);
24266
+ if (hop >= MAX_REDIRECTS) throw new Error(`Too many redirects (more than ${MAX_REDIRECTS})`);
24267
+ let next;
24268
+ try {
24269
+ next = new URL(location, url).toString();
24270
+ } catch {
24271
+ throw new Error(`Redirect to an invalid URL: ${location}`);
24272
+ }
24273
+ url = await validateFetchUrl(next, opts);
24274
+ }
24275
+ if (!res.ok) {
24276
+ await discard(res);
24277
+ throw new Error(`HTTP ${res.status} fetching ${url.hostname}`);
24278
+ }
24279
+ const contentType = res.headers.get("content-type") ?? "";
24280
+ const mediaType = contentType.split(";")[0].trim().toLowerCase() || null;
24281
+ if (mediaType === "application/pdf") {
24282
+ await discard(res);
24283
+ throw new Error(
24284
+ "PDF documents are not supported by this fetch backend (only Anthropic's native web_fetch reads PDFs)"
24285
+ );
24286
+ }
24287
+ if (!isTextual(mediaType)) {
24288
+ await discard(res);
24289
+ throw new Error(`Unsupported content type: ${mediaType}`);
24290
+ }
24291
+ const { bytes, capped } = await readCapped(res, MAX_BODY_BYTES);
24292
+ const raw = decode(bytes, contentType);
24293
+ const isHtml = mediaType === "text/html" || mediaType === "application/xhtml+xml" || looksLikeHtml(raw);
24294
+ const { title, text } = isHtml ? htmlToText(raw) : { title: null, text: raw.trim() };
24295
+ const truncated = capped || text.length > maxCharacters;
24296
+ return {
24297
+ url: url.toString(),
24298
+ title,
24299
+ text: truncated ? text.slice(0, maxCharacters) : text,
24300
+ mediaType,
24301
+ retrievedAt: (/* @__PURE__ */ new Date()).toISOString(),
24302
+ truncated,
24303
+ type: "web_fetch_result"
24304
+ };
24305
+ }
24306
+ function describeTransportError(err) {
24307
+ const e = err;
24308
+ if (e?.name === "TimeoutError") return "timed out";
24309
+ if (e?.name === "AbortError") return "aborted";
24310
+ const cause = e?.cause;
24311
+ if (cause?.message) return cause.code ? `${cause.message} (${cause.code})` : cause.message;
24312
+ return e?.message ?? String(err);
24313
+ }
24314
+ function isRedirect(status) {
24315
+ return status === 301 || status === 302 || status === 303 || status === 307 || status === 308;
24316
+ }
24317
+ async function discard(res) {
24318
+ await res.body?.cancel().catch(() => {
24319
+ });
24320
+ }
24321
+ function isTextual(mediaType) {
24322
+ if (!mediaType) return true;
24323
+ if (mediaType.startsWith("text/")) return true;
24324
+ if (mediaType.endsWith("+json") || mediaType.endsWith("+xml")) return true;
24325
+ return mediaType === "application/json" || mediaType === "application/xml" || mediaType === "application/xhtml+xml" || mediaType === "application/javascript";
24326
+ }
24327
+ async function readCapped(res, maxBytes) {
24328
+ const body = res.body;
24329
+ if (!body) return { bytes: new Uint8Array(await res.arrayBuffer()), capped: false };
24330
+ const reader = body.getReader();
24331
+ const chunks = [];
24332
+ let total = 0;
24333
+ let capped = false;
24334
+ for (; ; ) {
24335
+ const { done, value } = await reader.read();
24336
+ if (done) break;
24337
+ if (!value) continue;
24338
+ if (total + value.byteLength > maxBytes) {
24339
+ chunks.push(value.subarray(0, maxBytes - total));
24340
+ total = maxBytes;
24341
+ capped = true;
24342
+ await reader.cancel().catch(() => {
24343
+ });
24344
+ break;
24345
+ }
24346
+ chunks.push(value);
24347
+ total += value.byteLength;
24348
+ }
24349
+ const out = new Uint8Array(total);
24350
+ let offset = 0;
24351
+ for (const c of chunks) {
24352
+ out.set(c, offset);
24353
+ offset += c.byteLength;
24354
+ }
24355
+ return { bytes: out, capped };
24356
+ }
24357
+ function decode(bytes, contentType) {
24358
+ const charset = /charset=["']?([\w.:-]+)/i.exec(contentType)?.[1] ?? "utf-8";
24359
+ try {
24360
+ return new TextDecoder(charset).decode(bytes);
24361
+ } catch {
24362
+ return new TextDecoder("utf-8").decode(bytes);
24363
+ }
24364
+ }
24365
+ function looksLikeHtml(s) {
24366
+ return /^\s*(?:<!doctype\s+html|<html[\s>]|<head[\s>]|<body[\s>])/i.test(s.slice(0, 1024));
24367
+ }
24368
+ var NAMED_ENTITIES = {
24369
+ nbsp: " ",
24370
+ lt: "<",
24371
+ gt: ">",
24372
+ quot: '"',
24373
+ apos: "'",
24374
+ mdash: "\u2014",
24375
+ ndash: "\u2013",
24376
+ hellip: "\u2026",
24377
+ copy: "\xA9",
24378
+ reg: "\xAE",
24379
+ trade: "\u2122",
24380
+ laquo: "\xAB",
24381
+ raquo: "\xBB",
24382
+ ldquo: "\u201C",
24383
+ rdquo: "\u201D",
24384
+ lsquo: "\u2018",
24385
+ rsquo: "\u2019",
24386
+ bull: "\u2022",
24387
+ middot: "\xB7",
24388
+ times: "\xD7",
24389
+ deg: "\xB0"
24390
+ };
24391
+ function codePoint(cp, fallback) {
24392
+ if (!Number.isFinite(cp) || cp <= 0 || cp > 1114111 || cp >= 55296 && cp <= 57343) {
24393
+ return fallback;
24394
+ }
24395
+ return String.fromCodePoint(cp);
24396
+ }
24397
+ function decodeEntities(s) {
24398
+ return s.replace(/&#x([0-9a-f]{1,6});/gi, (m, hex) => codePoint(parseInt(hex, 16), m)).replace(/&#(\d{1,7});/g, (m, dec) => codePoint(parseInt(dec, 10), m)).replace(/&([a-z]+);/gi, (m, name19) => NAMED_ENTITIES[name19.toLowerCase()] ?? m).replace(/&amp;/g, "&");
24399
+ }
24400
+ var BLOCK_TAGS = "p|div|section|article|header|footer|main|aside|nav|h[1-6]|ul|ol|tr|table|thead|tbody|tfoot|blockquote|pre|hr|dd|dt|dl|figure|figcaption|form|fieldset|address|details|summary";
24401
+ function htmlToText(html) {
24402
+ const titleMatch = /<title[^>]*>([\s\S]*?)<\/title>/i.exec(html);
24403
+ const title = titleMatch ? decodeEntities(titleMatch[1]).replace(/\s+/g, " ").trim() || null : null;
24404
+ const stripped = html.replace(/<!--[\s\S]*?-->/g, " ").replace(/<(script|style|noscript|svg|template|head)\b[\s\S]*?<\/\1\s*>/gi, " ").replace(/<br\s*\/?>/gi, "\n").replace(/<li\b[^>]*>/gi, "\n- ").replace(new RegExp(`<\\/?(?:${BLOCK_TAGS})\\b[^>]*>`, "gi"), "\n").replace(/<[^>]+>/g, " ");
24405
+ const text = decodeEntities(stripped).replace(/[ \t\r\f\v ]+/g, " ").replace(/ ?\n ?/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
24406
+ return { title, text };
24407
+ }
24408
+ function createWebFetch(opts) {
24409
+ const results = [];
24410
+ const backend = opts.backend ?? resolveFetchBackend(opts.provider);
24411
+ const maxUses = opts.maxUses ?? DEFAULT_MAX_USES2;
24412
+ const maxCharacters = opts.maxCharacters ?? (opts.maxContentTokens ? opts.maxContentTokens * CHARS_PER_TOKEN : DEFAULT_MAX_CHARACTERS2);
24413
+ const maxContentTokens = opts.maxContentTokens ?? (opts.maxCharacters ? Math.ceil(opts.maxCharacters / CHARS_PER_TOKEN) : void 0);
24414
+ if (backend === "anthropic") {
24415
+ const apiKey = normalizeApiKey(opts.apiKey) || normalizeApiKey(process.env.ANTHROPIC_API_KEY);
24416
+ if (!apiKey) {
24417
+ return emptyFetchHandle(results);
24418
+ }
24419
+ const baseURL = getGatewayBaseURL("anthropic");
24420
+ const anthropic2 = createAnthropic({ apiKey, ...baseURL && { baseURL } });
24421
+ return {
24422
+ tool: anthropic2.tools.webFetch_20250910({
24423
+ maxUses,
24424
+ ...maxContentTokens ? { maxContentTokens } : {},
24425
+ ...opts.allowedDomains?.length ? { allowedDomains: opts.allowedDomains } : {},
24426
+ ...opts.blockedDomains?.length ? { blockedDomains: opts.blockedDomains } : {}
24427
+ }),
24428
+ backend,
24429
+ native: true,
24430
+ results,
24431
+ capture: (stepContent) => captureNativeFetchResults(stepContent, results)
24432
+ };
24433
+ }
24434
+ let uses = 0;
24435
+ return {
24436
+ tool: (0, import_ai3.tool)({
24437
+ description: "Fetch a specific web page by URL and return its text. HTML is converted to plain text; JSON and plain text come back as-is. Use this to read a page whose address you already have \u2014 one the user gave you, or one returned by web_search. It is not a search engine: it needs a full http(s) URL. PDFs and other binary content are not supported.",
24438
+ inputSchema: import_zod2.z.object({
24439
+ url: import_zod2.z.string().describe("The absolute http(s) URL to fetch.")
24440
+ }),
24441
+ execute: async ({ url }, ctx) => {
24442
+ if (uses >= maxUses) {
24443
+ return {
24444
+ error: `web_fetch budget exhausted (${maxUses} calls). Work with what you have.`
24445
+ };
24446
+ }
24447
+ uses++;
24448
+ try {
24449
+ const page = await fetchUrl(url, {
24450
+ maxCharacters,
24451
+ allowedDomains: opts.allowedDomains,
24452
+ blockedDomains: opts.blockedDomains,
24453
+ abortSignal: ctx?.abortSignal ?? opts.abortSignal,
24454
+ lookup: opts.lookup
24455
+ });
24456
+ results.push(page);
24457
+ return page;
24458
+ } catch (err) {
24459
+ return {
24460
+ error: `Fetch failed: ${err instanceof Error ? err.message : String(err)}`
24461
+ };
24462
+ }
24463
+ }
24464
+ }),
24465
+ backend,
24466
+ native: false,
24467
+ results,
24468
+ // HTTP results are appended by `execute` above; walking the step
24469
+ // would double-count them.
24470
+ capture: () => {
24471
+ }
24472
+ };
24473
+ }
24474
+ function emptyFetchHandle(results) {
24475
+ return {
24476
+ tool: void 0,
24477
+ backend: void 0,
24478
+ native: false,
24479
+ results,
24480
+ capture: () => {
24481
+ }
24482
+ };
24483
+ }
24484
+ function captureNativeFetchResults(stepContent, target) {
24485
+ if (!Array.isArray(stepContent)) return;
24486
+ for (const content of stepContent) {
24487
+ if (content?.type !== "tool-result") continue;
24488
+ if (content?.toolName !== WEB_FETCH_TOOL_NAME) continue;
24489
+ const body = content.output ?? content.result ?? null;
24490
+ if (!body || typeof body !== "object") continue;
24491
+ if (body.type !== "web_fetch_result" || typeof body.url !== "string") continue;
24492
+ const doc = body.content ?? {};
24493
+ const source = doc.source ?? {};
24494
+ target.push({
24495
+ url: body.url,
24496
+ title: doc.title ?? null,
24497
+ ...source.type === "text" && typeof source.data === "string" ? { text: source.data } : {},
24498
+ mediaType: source.mediaType ?? source.media_type ?? null,
24499
+ retrievedAt: body.retrievedAt ?? body.retrieved_at ?? null,
24500
+ type: "web_fetch_result"
24501
+ });
24502
+ }
24503
+ }
24504
+
23898
24505
  // src/tools.ts
23899
24506
  function getProviderTool(provider, apiKey, toolName) {
24507
+ if (toolName === "webSearch") {
24508
+ return createWebSearch({ provider, apiKey }).tool;
24509
+ }
24510
+ if (toolName === "webFetch") {
24511
+ return createWebFetch({ provider, apiKey }).tool;
24512
+ }
23900
24513
  switch (provider) {
23901
24514
  case "anthropic":
23902
24515
  return getAnthropicTool(apiKey, toolName);
@@ -23953,13 +24566,20 @@ function consoleLogger(name19) {
23953
24566
  OUTRO,
23954
24567
  PROVIDERS,
23955
24568
  SYSTEM,
24569
+ WEB_FETCH_TOOL_NAME,
24570
+ WEB_SEARCH_TOOL_NAME,
23956
24571
  addUsage,
23957
24572
  callGenerateObject,
23958
24573
  callGenerateText,
23959
24574
  callModel,
24575
+ captureNativeFetchResults,
24576
+ captureNativeResults,
23960
24577
  computeSessionCost,
23961
24578
  consoleLogger,
24579
+ createWebFetch,
24580
+ createWebSearch,
23962
24581
  emptyUsage,
24582
+ fetchUrl,
23963
24583
  gatewayEnvForProviders,
23964
24584
  gatewayUrlFor,
23965
24585
  gatewayUrlForModel,
@@ -23972,13 +24592,23 @@ function consoleLogger(name19) {
23972
24592
  getProviderForModel,
23973
24593
  getProviderOptions,
23974
24594
  getProviderTool,
24595
+ getSearchApiKey,
23975
24596
  getTokenPricing,
23976
24597
  hasApiKeyForProvider,
24598
+ hasSearchApiKey,
24599
+ htmlToText,
24600
+ isPrivateAddress,
24601
+ linkifyCitations,
23977
24602
  loadModelPricing,
23978
24603
  normalizeApiKey,
23979
24604
  normalizeCallerBaseURL,
23980
24605
  normalizeUsage,
24606
+ resolveFetchBackend,
23981
24607
  resolveLLMConfig,
24608
+ resolveSearchBackend,
24609
+ searchWeb,
24610
+ stripCitations,
24611
+ validateFetchUrl,
23982
24612
  withLegacyUsage,
23983
24613
  withProviderCacheUsage
23984
24614
  });