pi-web-providers 3.0.0 → 3.2.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/README.md +38 -26
- package/dist/index.js +1361 -306
- package/package.json +8 -8
package/dist/index.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { dirname as dirname2, join as join2, relative } from "node:path";
|
|
5
|
+
import { basename, dirname as dirname2, join as join2, relative } from "node:path";
|
|
6
6
|
import {
|
|
7
7
|
DEFAULT_MAX_BYTES,
|
|
8
8
|
DEFAULT_MAX_LINES,
|
|
9
|
-
formatSize,
|
|
9
|
+
formatSize as formatSize2,
|
|
10
10
|
getMarkdownTheme,
|
|
11
11
|
truncateHead
|
|
12
|
-
} from "@
|
|
12
|
+
} from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import {
|
|
14
14
|
Box,
|
|
15
15
|
Editor,
|
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
truncateToWidth,
|
|
22
22
|
visibleWidth,
|
|
23
23
|
wrapTextWithAnsi
|
|
24
|
-
} from "@
|
|
24
|
+
} from "@earendil-works/pi-tui";
|
|
25
25
|
import { Type as Type16 } from "typebox";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
28
28
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
29
29
|
import { dirname, join } from "node:path";
|
|
30
|
-
import { getAgentDir } from "@
|
|
30
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
31
31
|
|
|
32
32
|
// src/config-values.ts
|
|
33
33
|
import { execSync } from "node:child_process";
|
|
@@ -117,7 +117,13 @@ function asJsonObject(value) {
|
|
|
117
117
|
function formatJson(value) {
|
|
118
118
|
return JSON.stringify(value, null, 2);
|
|
119
119
|
}
|
|
120
|
-
function getApiKeyStatus(apiKeyReference) {
|
|
120
|
+
function getApiKeyStatus(apiKeyReference, options = {}) {
|
|
121
|
+
if (!apiKeyReference) {
|
|
122
|
+
return { state: "missing_api_key" };
|
|
123
|
+
}
|
|
124
|
+
if (options.resolveSecrets === false && isSecretReference(apiKeyReference)) {
|
|
125
|
+
return { state: "deferred_secret" };
|
|
126
|
+
}
|
|
121
127
|
try {
|
|
122
128
|
return resolveConfigValue(apiKeyReference) ? { state: "ready" } : { state: "missing_api_key" };
|
|
123
129
|
} catch (error) {
|
|
@@ -127,6 +133,9 @@ function getApiKeyStatus(apiKeyReference) {
|
|
|
127
133
|
};
|
|
128
134
|
}
|
|
129
135
|
}
|
|
136
|
+
function isSecretReference(reference) {
|
|
137
|
+
return reference.startsWith("!") || /^[A-Z][A-Z0-9_]*$/.test(reference);
|
|
138
|
+
}
|
|
130
139
|
function formatConfigValueError(error) {
|
|
131
140
|
const message = error instanceof Error ? error.message : String(error);
|
|
132
141
|
return message.replace(/\s+/g, " ").trim() || "Failed to resolve config value";
|
|
@@ -385,8 +394,14 @@ var braveSearchPromptGuidelines = [
|
|
|
385
394
|
];
|
|
386
395
|
var braveAnswerOptionsSchema = Type.Object(
|
|
387
396
|
{
|
|
397
|
+
model: Type.Optional(
|
|
398
|
+
Type.Enum({ brave: "brave", bravePro: "brave-pro" }, {
|
|
399
|
+
description: "Brave Answers model. Defaults to 'brave'."
|
|
400
|
+
})
|
|
401
|
+
),
|
|
388
402
|
country: Type.Optional(Type.String()),
|
|
389
403
|
language: Type.Optional(Type.String()),
|
|
404
|
+
safesearch: safesearchOption,
|
|
390
405
|
enable_citations: Type.Optional(Type.Boolean()),
|
|
391
406
|
enable_entities: Type.Optional(Type.Boolean()),
|
|
392
407
|
max_completion_tokens: Type.Optional(Type.Integer({ minimum: 1 }))
|
|
@@ -395,8 +410,14 @@ var braveAnswerOptionsSchema = Type.Object(
|
|
|
395
410
|
);
|
|
396
411
|
var braveResearchOptionsSchema = Type.Object(
|
|
397
412
|
{
|
|
413
|
+
model: Type.Optional(
|
|
414
|
+
Type.Enum({ brave: "brave", bravePro: "brave-pro" }, {
|
|
415
|
+
description: "Brave Answers model. Defaults to 'brave'."
|
|
416
|
+
})
|
|
417
|
+
),
|
|
398
418
|
country: Type.Optional(Type.String()),
|
|
399
419
|
language: Type.Optional(Type.String()),
|
|
420
|
+
safesearch: safesearchOption,
|
|
400
421
|
enable_entities: Type.Optional(Type.Boolean()),
|
|
401
422
|
enable_citations: Type.Optional(
|
|
402
423
|
Type.Boolean({
|
|
@@ -448,19 +469,19 @@ var braveImplementation = {
|
|
|
448
469
|
options: {}
|
|
449
470
|
};
|
|
450
471
|
},
|
|
451
|
-
getCapabilityStatus(config, _cwd, tool) {
|
|
472
|
+
getCapabilityStatus(config, _cwd, tool, options) {
|
|
452
473
|
const key = tool === "answer" || tool === "research" ? config?.credentials?.answers : config?.credentials?.search;
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
474
|
+
if (tool) {
|
|
475
|
+
return getApiKeyStatus(key, options);
|
|
476
|
+
}
|
|
477
|
+
const statuses = [
|
|
478
|
+
config?.credentials?.search,
|
|
479
|
+
config?.credentials?.answers,
|
|
480
|
+
config?.credentials?.autosuggest
|
|
481
|
+
].map((value) => getApiKeyStatus(value, options));
|
|
482
|
+
return statuses.find((status) => status.state === "ready") ?? statuses.find((status) => status.state === "deferred_secret") ?? statuses.find((status) => status.state === "invalid_config") ?? {
|
|
483
|
+
state: "missing_api_key"
|
|
484
|
+
};
|
|
464
485
|
},
|
|
465
486
|
async search(query2, maxResults, config, context, options) {
|
|
466
487
|
const apiKey = requireKey(config.credentials?.search, "Brave search");
|
|
@@ -945,25 +966,25 @@ function placeRating(place, details) {
|
|
|
945
966
|
return rating === void 0 ? void 0 : `Rating: ${rating}`;
|
|
946
967
|
}
|
|
947
968
|
function buildAnswerRequest(raw) {
|
|
948
|
-
const
|
|
969
|
+
const webSearchOptions = pick(raw, [
|
|
949
970
|
"country",
|
|
950
971
|
"language",
|
|
951
972
|
"safesearch",
|
|
952
973
|
"enable_entities",
|
|
953
974
|
"enable_citations"
|
|
954
975
|
]);
|
|
955
|
-
if (
|
|
956
|
-
|
|
976
|
+
if (webSearchOptions.enable_citations === void 0) {
|
|
977
|
+
webSearchOptions.enable_citations = true;
|
|
957
978
|
}
|
|
958
|
-
const stream =
|
|
979
|
+
const stream = webSearchOptions.enable_citations === true || webSearchOptions.enable_entities === true;
|
|
959
980
|
return {
|
|
960
981
|
stream,
|
|
961
|
-
...pick(raw, ["max_completion_tokens", "metadata", "seed"]),
|
|
962
|
-
|
|
982
|
+
...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
|
|
983
|
+
web_search_options: webSearchOptions
|
|
963
984
|
};
|
|
964
985
|
}
|
|
965
986
|
function buildResearchRequest(raw) {
|
|
966
|
-
const
|
|
987
|
+
const webSearchOptions = pick(raw, [
|
|
967
988
|
"country",
|
|
968
989
|
"language",
|
|
969
990
|
"safesearch",
|
|
@@ -975,12 +996,12 @@ function buildResearchRequest(raw) {
|
|
|
975
996
|
"research_maximum_number_of_seconds",
|
|
976
997
|
"research_maximum_number_of_results_per_query"
|
|
977
998
|
]);
|
|
999
|
+
webSearchOptions.enable_research = true;
|
|
1000
|
+
webSearchOptions.enable_citations = false;
|
|
978
1001
|
return {
|
|
979
1002
|
stream: true,
|
|
980
|
-
...pick(raw, ["max_completion_tokens", "metadata", "seed"]),
|
|
981
|
-
|
|
982
|
-
enable_research: true,
|
|
983
|
-
enable_citations: false
|
|
1003
|
+
...pick(raw, ["model", "max_completion_tokens", "metadata", "seed"]),
|
|
1004
|
+
web_search_options: webSearchOptions
|
|
984
1005
|
};
|
|
985
1006
|
}
|
|
986
1007
|
async function completion(input, config, context, request) {
|
|
@@ -1197,10 +1218,11 @@ var braveProvider = defineProvider({
|
|
|
1197
1218
|
},
|
|
1198
1219
|
optionCapabilities: ["search", "answer", "research"]
|
|
1199
1220
|
},
|
|
1200
|
-
getCapabilityStatus: (config, cwd, tool) => braveImplementation.getCapabilityStatus(
|
|
1221
|
+
getCapabilityStatus: (config, cwd, tool, options) => braveImplementation.getCapabilityStatus(
|
|
1201
1222
|
config,
|
|
1202
1223
|
cwd,
|
|
1203
|
-
tool
|
|
1224
|
+
tool,
|
|
1225
|
+
options
|
|
1204
1226
|
),
|
|
1205
1227
|
capabilities: {
|
|
1206
1228
|
search: defineCapability({
|
|
@@ -1666,10 +1688,12 @@ var cloudflareImplementation = {
|
|
|
1666
1688
|
}
|
|
1667
1689
|
};
|
|
1668
1690
|
},
|
|
1669
|
-
getCapabilityStatus(config) {
|
|
1670
|
-
const apiTokenStatus = getApiKeyStatus(config?.credentials?.api);
|
|
1691
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
1692
|
+
const apiTokenStatus = getApiKeyStatus(config?.credentials?.api, options);
|
|
1671
1693
|
if (apiTokenStatus.state !== "ready") {
|
|
1672
|
-
|
|
1694
|
+
if (apiTokenStatus.state !== "deferred_secret") {
|
|
1695
|
+
return apiTokenStatus;
|
|
1696
|
+
}
|
|
1673
1697
|
}
|
|
1674
1698
|
try {
|
|
1675
1699
|
if (!resolveConfigValue(config?.accountId)) {
|
|
@@ -1681,7 +1705,7 @@ var cloudflareImplementation = {
|
|
|
1681
1705
|
detail: formatConfigValueError(error)
|
|
1682
1706
|
};
|
|
1683
1707
|
}
|
|
1684
|
-
return { state: "ready" };
|
|
1708
|
+
return apiTokenStatus.state === "deferred_secret" ? apiTokenStatus : { state: "ready" };
|
|
1685
1709
|
},
|
|
1686
1710
|
async contents(urls, config, context, options) {
|
|
1687
1711
|
const client = createClient(config);
|
|
@@ -1740,10 +1764,11 @@ var cloudflareProvider = defineProvider({
|
|
|
1740
1764
|
createTemplate: () => cloudflareImplementation.createTemplate(),
|
|
1741
1765
|
fields: ["credentials", "accountId", "options", "settings"]
|
|
1742
1766
|
},
|
|
1743
|
-
getCapabilityStatus: (config, cwd, tool) => cloudflareImplementation.getCapabilityStatus(
|
|
1767
|
+
getCapabilityStatus: (config, cwd, tool, options) => cloudflareImplementation.getCapabilityStatus(
|
|
1744
1768
|
config,
|
|
1745
1769
|
cwd,
|
|
1746
|
-
tool
|
|
1770
|
+
tool,
|
|
1771
|
+
options
|
|
1747
1772
|
),
|
|
1748
1773
|
capabilities: {
|
|
1749
1774
|
contents: defineCapability({
|
|
@@ -2876,8 +2901,8 @@ var exaImplementation = {
|
|
|
2876
2901
|
}
|
|
2877
2902
|
};
|
|
2878
2903
|
},
|
|
2879
|
-
getCapabilityStatus(config) {
|
|
2880
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
2904
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
2905
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
2881
2906
|
},
|
|
2882
2907
|
async search(query2, maxResults, config, _context, searchOptions) {
|
|
2883
2908
|
const client = createClient2(config);
|
|
@@ -3012,10 +3037,11 @@ var exaProvider = defineProvider({
|
|
|
3012
3037
|
fields: ["credentials", "baseUrl", "options", "settings"],
|
|
3013
3038
|
optionCapabilities: ["search"]
|
|
3014
3039
|
},
|
|
3015
|
-
getCapabilityStatus: (config, cwd, tool) => exaImplementation.getCapabilityStatus(
|
|
3040
|
+
getCapabilityStatus: (config, cwd, tool, options) => exaImplementation.getCapabilityStatus(
|
|
3016
3041
|
config,
|
|
3017
3042
|
cwd,
|
|
3018
|
-
tool
|
|
3043
|
+
tool,
|
|
3044
|
+
options
|
|
3019
3045
|
),
|
|
3020
3046
|
capabilities: {
|
|
3021
3047
|
search: defineCapability({
|
|
@@ -3070,6 +3096,7 @@ var exaProvider = defineProvider({
|
|
|
3070
3096
|
// src/providers/firecrawl.ts
|
|
3071
3097
|
import FirecrawlClient from "@mendable/firecrawl-js";
|
|
3072
3098
|
import { Type as Type7 } from "typebox";
|
|
3099
|
+
var FIRECRAWL_CLOUD_HOST = "api.firecrawl.dev";
|
|
3073
3100
|
var firecrawlSearchOptionsSchema = Type7.Object(
|
|
3074
3101
|
{
|
|
3075
3102
|
lang: Type7.Optional(
|
|
@@ -3201,8 +3228,8 @@ var firecrawlImplementation = {
|
|
|
3201
3228
|
}
|
|
3202
3229
|
};
|
|
3203
3230
|
},
|
|
3204
|
-
getCapabilityStatus(config) {
|
|
3205
|
-
return
|
|
3231
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
3232
|
+
return getFirecrawlCapabilityStatus(config, options);
|
|
3206
3233
|
},
|
|
3207
3234
|
async search(query2, maxResults, config, _context, options) {
|
|
3208
3235
|
const client = createClient3(config);
|
|
@@ -3255,15 +3282,26 @@ var firecrawlImplementation = {
|
|
|
3255
3282
|
}
|
|
3256
3283
|
};
|
|
3257
3284
|
function createClient3(config) {
|
|
3285
|
+
const apiUrl = resolveConfigValue(config.baseUrl);
|
|
3258
3286
|
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
3259
|
-
if (!apiKey) {
|
|
3287
|
+
if (isFirecrawlCloudApiUrl(apiUrl) && !apiKey) {
|
|
3260
3288
|
throw new Error("is missing an API key");
|
|
3261
3289
|
}
|
|
3262
3290
|
return new FirecrawlClient({
|
|
3263
3291
|
apiKey,
|
|
3264
|
-
apiUrl
|
|
3292
|
+
apiUrl
|
|
3265
3293
|
});
|
|
3266
3294
|
}
|
|
3295
|
+
function getFirecrawlCapabilityStatus(config, options) {
|
|
3296
|
+
if (!config?.baseUrl || isFirecrawlCloudApiUrl(config.baseUrl)) {
|
|
3297
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
3298
|
+
}
|
|
3299
|
+
const apiKeyStatus = getApiKeyStatus(config.credentials?.api, options);
|
|
3300
|
+
return apiKeyStatus.state === "missing_api_key" ? { state: "ready" } : apiKeyStatus;
|
|
3301
|
+
}
|
|
3302
|
+
function isFirecrawlCloudApiUrl(apiUrl) {
|
|
3303
|
+
return !apiUrl || apiUrl.includes(FIRECRAWL_CLOUD_HOST);
|
|
3304
|
+
}
|
|
3267
3305
|
function flattenSearchResults(response) {
|
|
3268
3306
|
return ["web", "news", "images"].flatMap(
|
|
3269
3307
|
(source) => (response[source] ?? []).map((entry) => toSearchResult(source, entry)).filter((entry) => entry !== null)
|
|
@@ -3321,10 +3359,11 @@ var firecrawlProvider = defineProvider({
|
|
|
3321
3359
|
createTemplate: () => firecrawlImplementation.createTemplate(),
|
|
3322
3360
|
fields: ["credentials", "baseUrl", "options", "settings"]
|
|
3323
3361
|
},
|
|
3324
|
-
getCapabilityStatus: (config, cwd, tool) => firecrawlImplementation.getCapabilityStatus(
|
|
3362
|
+
getCapabilityStatus: (config, cwd, tool, options) => firecrawlImplementation.getCapabilityStatus(
|
|
3325
3363
|
config,
|
|
3326
3364
|
cwd,
|
|
3327
|
-
tool
|
|
3365
|
+
tool,
|
|
3366
|
+
options
|
|
3328
3367
|
),
|
|
3329
3368
|
capabilities: {
|
|
3330
3369
|
search: defineCapability({
|
|
@@ -3479,8 +3518,8 @@ var geminiImplementation = {
|
|
|
3479
3518
|
}
|
|
3480
3519
|
};
|
|
3481
3520
|
},
|
|
3482
|
-
getCapabilityStatus(config) {
|
|
3483
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
3521
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
3522
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
3484
3523
|
},
|
|
3485
3524
|
async search(query2, maxResults, config, context, options) {
|
|
3486
3525
|
const ai = this.createClient(config);
|
|
@@ -4109,10 +4148,11 @@ var geminiProvider = defineProvider({
|
|
|
4109
4148
|
createTemplate: () => geminiImplementation.createTemplate(),
|
|
4110
4149
|
fields: ["credentials", "options", "settings"]
|
|
4111
4150
|
},
|
|
4112
|
-
getCapabilityStatus: (config, cwd, tool) => geminiImplementation.getCapabilityStatus(
|
|
4151
|
+
getCapabilityStatus: (config, cwd, tool, options) => geminiImplementation.getCapabilityStatus(
|
|
4113
4152
|
config,
|
|
4114
4153
|
cwd,
|
|
4115
|
-
tool
|
|
4154
|
+
tool,
|
|
4155
|
+
options
|
|
4116
4156
|
),
|
|
4117
4157
|
capabilities: {
|
|
4118
4158
|
search: defineCapability({
|
|
@@ -4201,6 +4241,45 @@ var linkupContentsOptionsSchema = Type9.Object(
|
|
|
4201
4241
|
},
|
|
4202
4242
|
{ description: "Linkup fetch options." }
|
|
4203
4243
|
);
|
|
4244
|
+
var linkupResearchOptionsSchema = Type9.Object(
|
|
4245
|
+
{
|
|
4246
|
+
outputType: Type9.Optional(
|
|
4247
|
+
literalUnion(["sourcedAnswer", "structured"], {
|
|
4248
|
+
description: "Research output type. Defaults to 'sourcedAnswer' unless structuredOutputSchema is provided."
|
|
4249
|
+
})
|
|
4250
|
+
),
|
|
4251
|
+
mode: Type9.Optional(
|
|
4252
|
+
literalUnion(["answer", "auto", "investigate", "research"], {
|
|
4253
|
+
description: "Research mode. Use 'answer' for precise verified answers, 'investigate' for focused deep dives, 'research' for broad reports, or omit/auto to let Linkup classify the task."
|
|
4254
|
+
})
|
|
4255
|
+
),
|
|
4256
|
+
reasoningDepth: Type9.Optional(
|
|
4257
|
+
literalUnion(["S", "M", "L", "XL"], {
|
|
4258
|
+
description: "Reasoning depth. Higher values trade latency for more thorough investigation."
|
|
4259
|
+
})
|
|
4260
|
+
),
|
|
4261
|
+
includeDomains: Type9.Optional(
|
|
4262
|
+
Type9.Array(Type9.String(), {
|
|
4263
|
+
description: "Restrict research to these domains."
|
|
4264
|
+
})
|
|
4265
|
+
),
|
|
4266
|
+
excludeDomains: Type9.Optional(
|
|
4267
|
+
Type9.Array(Type9.String(), { description: "Exclude these domains." })
|
|
4268
|
+
),
|
|
4269
|
+
fromDate: Type9.Optional(
|
|
4270
|
+
Type9.String({ description: "ISO date string for earliest result date." })
|
|
4271
|
+
),
|
|
4272
|
+
toDate: Type9.Optional(
|
|
4273
|
+
Type9.String({ description: "ISO date string for latest result date." })
|
|
4274
|
+
),
|
|
4275
|
+
structuredOutputSchema: Type9.Optional(
|
|
4276
|
+
Type9.Record(Type9.String(), Type9.Any(), {
|
|
4277
|
+
description: "JSON schema object required when outputType is 'structured'."
|
|
4278
|
+
})
|
|
4279
|
+
)
|
|
4280
|
+
},
|
|
4281
|
+
{ description: "Linkup research options." }
|
|
4282
|
+
);
|
|
4204
4283
|
var linkupImplementation = {
|
|
4205
4284
|
id: "linkup",
|
|
4206
4285
|
label: "Linkup",
|
|
@@ -4211,6 +4290,8 @@ var linkupImplementation = {
|
|
|
4211
4290
|
return linkupSearchOptionsSchema;
|
|
4212
4291
|
case "contents":
|
|
4213
4292
|
return linkupContentsOptionsSchema;
|
|
4293
|
+
case "research":
|
|
4294
|
+
return linkupResearchOptionsSchema;
|
|
4214
4295
|
default:
|
|
4215
4296
|
return void 0;
|
|
4216
4297
|
}
|
|
@@ -4220,8 +4301,8 @@ var linkupImplementation = {
|
|
|
4220
4301
|
credentials: { api: "LINKUP_API_KEY" }
|
|
4221
4302
|
};
|
|
4222
4303
|
},
|
|
4223
|
-
getCapabilityStatus(config) {
|
|
4224
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
4304
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
4305
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
4225
4306
|
},
|
|
4226
4307
|
async search(query2, maxResults, config, _context, options) {
|
|
4227
4308
|
const client = createClient4(config);
|
|
@@ -4267,6 +4348,51 @@ var linkupImplementation = {
|
|
|
4267
4348
|
})
|
|
4268
4349
|
)
|
|
4269
4350
|
};
|
|
4351
|
+
},
|
|
4352
|
+
async research(input, config, context, options) {
|
|
4353
|
+
return await executeAsyncResearch({
|
|
4354
|
+
providerLabel: linkupImplementation.label,
|
|
4355
|
+
providerId: linkupImplementation.id,
|
|
4356
|
+
context,
|
|
4357
|
+
start: (researchContext) => linkupImplementation.startResearch(
|
|
4358
|
+
input,
|
|
4359
|
+
config,
|
|
4360
|
+
researchContext,
|
|
4361
|
+
options
|
|
4362
|
+
),
|
|
4363
|
+
poll: (id, researchContext) => linkupImplementation.pollResearch(id, config, researchContext)
|
|
4364
|
+
});
|
|
4365
|
+
},
|
|
4366
|
+
async startResearch(input, config, _context, options) {
|
|
4367
|
+
const client = createClient4(config);
|
|
4368
|
+
const defaults = asJsonObject(config.options?.research) ?? {};
|
|
4369
|
+
const task = await client.research(
|
|
4370
|
+
buildResearchParams(input, {
|
|
4371
|
+
...defaults,
|
|
4372
|
+
...options ?? {}
|
|
4373
|
+
})
|
|
4374
|
+
);
|
|
4375
|
+
return { id: task.id };
|
|
4376
|
+
},
|
|
4377
|
+
async pollResearch(id, config, _context) {
|
|
4378
|
+
const client = createClient4(config);
|
|
4379
|
+
const task = await client.getResearch(id);
|
|
4380
|
+
if (task.status === "completed") {
|
|
4381
|
+
return {
|
|
4382
|
+
status: "completed",
|
|
4383
|
+
output: formatResearchTaskOutput(task)
|
|
4384
|
+
};
|
|
4385
|
+
}
|
|
4386
|
+
if (task.status === "failed") {
|
|
4387
|
+
return {
|
|
4388
|
+
status: "failed",
|
|
4389
|
+
error: task.error ?? "research failed"
|
|
4390
|
+
};
|
|
4391
|
+
}
|
|
4392
|
+
return {
|
|
4393
|
+
status: "in_progress",
|
|
4394
|
+
statusText: task.status
|
|
4395
|
+
};
|
|
4270
4396
|
}
|
|
4271
4397
|
};
|
|
4272
4398
|
function buildSearchParams(query2, maxResults, options) {
|
|
@@ -4311,6 +4437,45 @@ function buildFetchParams(url2, options) {
|
|
|
4311
4437
|
...fetchOptions.extractImages !== void 0 ? { extractImages: fetchOptions.extractImages } : {}
|
|
4312
4438
|
};
|
|
4313
4439
|
}
|
|
4440
|
+
function buildResearchParams(input, options) {
|
|
4441
|
+
const researchOptions = options;
|
|
4442
|
+
if (researchOptions.q !== void 0 || researchOptions.query !== void 0 || researchOptions.input !== void 0) {
|
|
4443
|
+
throw new Error(
|
|
4444
|
+
"Linkup research options cannot override the managed input."
|
|
4445
|
+
);
|
|
4446
|
+
}
|
|
4447
|
+
const outputType = researchOptions.outputType ?? (researchOptions.structuredOutputSchema !== void 0 ? "structured" : "sourcedAnswer");
|
|
4448
|
+
if (outputType === "structured" && researchOptions.structuredOutputSchema === void 0) {
|
|
4449
|
+
throw new Error(
|
|
4450
|
+
"Linkup research outputType 'structured' requires structuredOutputSchema."
|
|
4451
|
+
);
|
|
4452
|
+
}
|
|
4453
|
+
if (outputType === "sourcedAnswer" && researchOptions.structuredOutputSchema !== void 0) {
|
|
4454
|
+
throw new Error(
|
|
4455
|
+
"Linkup research structuredOutputSchema requires outputType 'structured'."
|
|
4456
|
+
);
|
|
4457
|
+
}
|
|
4458
|
+
const commonParams = {
|
|
4459
|
+
query: input,
|
|
4460
|
+
...researchOptions.includeDomains !== void 0 ? { includeDomains: researchOptions.includeDomains } : {},
|
|
4461
|
+
...researchOptions.excludeDomains !== void 0 ? { excludeDomains: researchOptions.excludeDomains } : {},
|
|
4462
|
+
...researchOptions.fromDate !== void 0 ? { fromDate: toDate(researchOptions.fromDate, "fromDate") } : {},
|
|
4463
|
+
...researchOptions.toDate !== void 0 ? { toDate: toDate(researchOptions.toDate, "toDate") } : {},
|
|
4464
|
+
...researchOptions.mode !== void 0 ? { mode: researchOptions.mode } : {},
|
|
4465
|
+
...researchOptions.reasoningDepth !== void 0 ? { reasoningDepth: researchOptions.reasoningDepth } : {}
|
|
4466
|
+
};
|
|
4467
|
+
if (outputType === "structured") {
|
|
4468
|
+
return {
|
|
4469
|
+
...commonParams,
|
|
4470
|
+
outputType,
|
|
4471
|
+
structuredOutputSchema: researchOptions.structuredOutputSchema
|
|
4472
|
+
};
|
|
4473
|
+
}
|
|
4474
|
+
return {
|
|
4475
|
+
...commonParams,
|
|
4476
|
+
outputType
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4314
4479
|
function createClient4(config) {
|
|
4315
4480
|
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
4316
4481
|
if (!apiKey) {
|
|
@@ -4321,6 +4486,40 @@ function createClient4(config) {
|
|
|
4321
4486
|
baseUrl: resolveConfigValue(config.baseUrl)
|
|
4322
4487
|
});
|
|
4323
4488
|
}
|
|
4489
|
+
function formatResearchTaskOutput(task) {
|
|
4490
|
+
const output = task.output;
|
|
4491
|
+
if (!output) {
|
|
4492
|
+
return {
|
|
4493
|
+
provider: linkupImplementation.id,
|
|
4494
|
+
text: "Linkup research completed without textual output."
|
|
4495
|
+
};
|
|
4496
|
+
}
|
|
4497
|
+
const outputRecord = asRecord2(output);
|
|
4498
|
+
const inputRecord = asRecord2(task.input);
|
|
4499
|
+
const outputType = inputRecord ? readString3(inputRecord.outputType) : void 0;
|
|
4500
|
+
const answer = outputRecord ? readString3(outputRecord.answer) : void 0;
|
|
4501
|
+
const sources = outputRecord ? readSources(outputRecord.sources) : [];
|
|
4502
|
+
if (outputType !== "structured" && answer !== void 0) {
|
|
4503
|
+
const lines = [answer];
|
|
4504
|
+
if (sources.length > 0) {
|
|
4505
|
+
lines.push("");
|
|
4506
|
+
lines.push("Sources:");
|
|
4507
|
+
for (const [index, source] of sources.entries()) {
|
|
4508
|
+
lines.push(`${index + 1}. ${source.title}`);
|
|
4509
|
+
lines.push(` ${source.url}`);
|
|
4510
|
+
}
|
|
4511
|
+
}
|
|
4512
|
+
return {
|
|
4513
|
+
provider: linkupImplementation.id,
|
|
4514
|
+
text: lines.join("\n").trimEnd(),
|
|
4515
|
+
itemCount: sources.length
|
|
4516
|
+
};
|
|
4517
|
+
}
|
|
4518
|
+
return {
|
|
4519
|
+
provider: linkupImplementation.id,
|
|
4520
|
+
text: formatJson(output)
|
|
4521
|
+
};
|
|
4522
|
+
}
|
|
4324
4523
|
function toSearchResult2(value) {
|
|
4325
4524
|
const entry = asRecord2(value);
|
|
4326
4525
|
if (!entry) {
|
|
@@ -4342,6 +4541,27 @@ function toSearchResult2(value) {
|
|
|
4342
4541
|
metadata: Object.keys(metadata).length > 0 ? metadata : void 0
|
|
4343
4542
|
};
|
|
4344
4543
|
}
|
|
4544
|
+
function readSources(value) {
|
|
4545
|
+
if (!Array.isArray(value)) {
|
|
4546
|
+
return [];
|
|
4547
|
+
}
|
|
4548
|
+
return value.flatMap((entry) => {
|
|
4549
|
+
const source = asRecord2(entry);
|
|
4550
|
+
if (!source) {
|
|
4551
|
+
return [];
|
|
4552
|
+
}
|
|
4553
|
+
const url2 = readString3(source.url);
|
|
4554
|
+
if (!url2) {
|
|
4555
|
+
return [];
|
|
4556
|
+
}
|
|
4557
|
+
return [
|
|
4558
|
+
{
|
|
4559
|
+
title: readString3(source.name) ?? url2,
|
|
4560
|
+
url: url2
|
|
4561
|
+
}
|
|
4562
|
+
];
|
|
4563
|
+
});
|
|
4564
|
+
}
|
|
4345
4565
|
function asRecord2(value) {
|
|
4346
4566
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
4347
4567
|
}
|
|
@@ -4365,10 +4585,11 @@ var linkupProvider = defineProvider({
|
|
|
4365
4585
|
createTemplate: () => linkupImplementation.createTemplate(),
|
|
4366
4586
|
fields: ["credentials", "baseUrl", "options", "settings"]
|
|
4367
4587
|
},
|
|
4368
|
-
getCapabilityStatus: (config, cwd, tool) => linkupImplementation.getCapabilityStatus(
|
|
4588
|
+
getCapabilityStatus: (config, cwd, tool, options) => linkupImplementation.getCapabilityStatus(
|
|
4369
4589
|
config,
|
|
4370
4590
|
cwd,
|
|
4371
|
-
tool
|
|
4591
|
+
tool,
|
|
4592
|
+
options
|
|
4372
4593
|
),
|
|
4373
4594
|
capabilities: {
|
|
4374
4595
|
search: defineCapability({
|
|
@@ -4394,6 +4615,17 @@ var linkupProvider = defineProvider({
|
|
|
4394
4615
|
input.options
|
|
4395
4616
|
);
|
|
4396
4617
|
}
|
|
4618
|
+
}),
|
|
4619
|
+
research: defineCapability({
|
|
4620
|
+
options: linkupImplementation.getToolOptionsSchema?.("research"),
|
|
4621
|
+
async execute(input, ctx) {
|
|
4622
|
+
return await linkupImplementation.research(
|
|
4623
|
+
input.input,
|
|
4624
|
+
ctx.config,
|
|
4625
|
+
ctx,
|
|
4626
|
+
input.options
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4397
4629
|
})
|
|
4398
4630
|
}
|
|
4399
4631
|
});
|
|
@@ -4414,8 +4646,8 @@ var ollamaProvider = defineProvider({
|
|
|
4414
4646
|
},
|
|
4415
4647
|
fields: ["credentials", "baseUrl", "settings"]
|
|
4416
4648
|
},
|
|
4417
|
-
getCapabilityStatus(config) {
|
|
4418
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
4649
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
4650
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
4419
4651
|
},
|
|
4420
4652
|
capabilities: {
|
|
4421
4653
|
search: defineCapability({
|
|
@@ -4679,8 +4911,8 @@ var openaiImplementation = {
|
|
|
4679
4911
|
}
|
|
4680
4912
|
};
|
|
4681
4913
|
},
|
|
4682
|
-
getCapabilityStatus(config) {
|
|
4683
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
4914
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
4915
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
4684
4916
|
},
|
|
4685
4917
|
async search(query2, maxResults, config, context, options) {
|
|
4686
4918
|
const client = createClient5(config);
|
|
@@ -5036,10 +5268,11 @@ var openaiProvider = defineProvider({
|
|
|
5036
5268
|
fields: ["credentials", "baseUrl", "options", "settings"],
|
|
5037
5269
|
optionCapabilities: ["search", "answer", "research"]
|
|
5038
5270
|
},
|
|
5039
|
-
getCapabilityStatus: (config, cwd, tool) => openaiImplementation.getCapabilityStatus(
|
|
5271
|
+
getCapabilityStatus: (config, cwd, tool, options) => openaiImplementation.getCapabilityStatus(
|
|
5040
5272
|
config,
|
|
5041
5273
|
cwd,
|
|
5042
|
-
tool
|
|
5274
|
+
tool,
|
|
5275
|
+
options
|
|
5043
5276
|
),
|
|
5044
5277
|
capabilities: {
|
|
5045
5278
|
search: defineCapability({
|
|
@@ -5134,8 +5367,8 @@ var parallelImplementation = {
|
|
|
5134
5367
|
}
|
|
5135
5368
|
};
|
|
5136
5369
|
},
|
|
5137
|
-
getCapabilityStatus(config) {
|
|
5138
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
5370
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
5371
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
5139
5372
|
},
|
|
5140
5373
|
async search(query2, maxResults, config, context, options) {
|
|
5141
5374
|
const client = createClient6(config);
|
|
@@ -5219,10 +5452,11 @@ var parallelProvider = defineProvider({
|
|
|
5219
5452
|
createTemplate: () => parallelImplementation.createTemplate(),
|
|
5220
5453
|
fields: ["credentials", "baseUrl", "options", "settings"]
|
|
5221
5454
|
},
|
|
5222
|
-
getCapabilityStatus: (config, cwd, tool) => parallelImplementation.getCapabilityStatus(
|
|
5455
|
+
getCapabilityStatus: (config, cwd, tool, options) => parallelImplementation.getCapabilityStatus(
|
|
5223
5456
|
config,
|
|
5224
5457
|
cwd,
|
|
5225
|
-
tool
|
|
5458
|
+
tool,
|
|
5459
|
+
options
|
|
5226
5460
|
),
|
|
5227
5461
|
capabilities: {
|
|
5228
5462
|
search: defineCapability({
|
|
@@ -5327,8 +5561,8 @@ var perplexityImplementation = {
|
|
|
5327
5561
|
}
|
|
5328
5562
|
};
|
|
5329
5563
|
},
|
|
5330
|
-
getCapabilityStatus(config) {
|
|
5331
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
5564
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
5565
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
5332
5566
|
},
|
|
5333
5567
|
async search(query2, maxResults, config, context, options) {
|
|
5334
5568
|
const client = createClient7(config);
|
|
@@ -5538,10 +5772,11 @@ var perplexityProvider = defineProvider({
|
|
|
5538
5772
|
createTemplate: () => perplexityImplementation.createTemplate(),
|
|
5539
5773
|
fields: ["credentials", "baseUrl", "options", "settings"]
|
|
5540
5774
|
},
|
|
5541
|
-
getCapabilityStatus: (config, cwd, tool) => perplexityImplementation.getCapabilityStatus(
|
|
5775
|
+
getCapabilityStatus: (config, cwd, tool, options) => perplexityImplementation.getCapabilityStatus(
|
|
5542
5776
|
config,
|
|
5543
5777
|
cwd,
|
|
5544
|
-
tool
|
|
5778
|
+
tool,
|
|
5779
|
+
options
|
|
5545
5780
|
),
|
|
5546
5781
|
capabilities: {
|
|
5547
5782
|
search: defineCapability({
|
|
@@ -5584,9 +5819,103 @@ var perplexityProvider = defineProvider({
|
|
|
5584
5819
|
|
|
5585
5820
|
// src/providers/serper.ts
|
|
5586
5821
|
import { Type as Type13 } from "typebox";
|
|
5822
|
+
|
|
5823
|
+
// src/types.ts
|
|
5824
|
+
var TOOLS = ["search", "contents", "answer", "research"];
|
|
5825
|
+
var SERPER_SEARCH_MODE_VALUES = {
|
|
5826
|
+
search: "search",
|
|
5827
|
+
images: "images",
|
|
5828
|
+
videos: "videos",
|
|
5829
|
+
places: "places",
|
|
5830
|
+
maps: "maps",
|
|
5831
|
+
reviews: "reviews",
|
|
5832
|
+
news: "news",
|
|
5833
|
+
shopping: "shopping",
|
|
5834
|
+
productReviews: "product-reviews",
|
|
5835
|
+
lens: "lens",
|
|
5836
|
+
scholar: "scholar",
|
|
5837
|
+
patents: "patents",
|
|
5838
|
+
autocomplete: "autocomplete",
|
|
5839
|
+
webpage: "webpage"
|
|
5840
|
+
};
|
|
5841
|
+
|
|
5842
|
+
// src/providers/serper.ts
|
|
5587
5843
|
var DEFAULT_BASE_URL3 = "https://google.serper.dev";
|
|
5844
|
+
var DEFAULT_SCRAPE_URL = "https://scrape.serper.dev";
|
|
5845
|
+
var SERPER_SEARCH_MODES = Object.values(SERPER_SEARCH_MODE_VALUES);
|
|
5846
|
+
var SERPER_SEARCH_MODE_SET = new Set(SERPER_SEARCH_MODES);
|
|
5847
|
+
var RESERVED_REQUEST_OPTION_KEYS = [
|
|
5848
|
+
"q",
|
|
5849
|
+
"num",
|
|
5850
|
+
"mode",
|
|
5851
|
+
"url",
|
|
5852
|
+
"productId",
|
|
5853
|
+
"nextPageToken",
|
|
5854
|
+
"ll",
|
|
5855
|
+
"placeId",
|
|
5856
|
+
"cid",
|
|
5857
|
+
"fid",
|
|
5858
|
+
"sortBy",
|
|
5859
|
+
"topicId",
|
|
5860
|
+
"includeMarkdown",
|
|
5861
|
+
"includeImages",
|
|
5862
|
+
"includeLinks",
|
|
5863
|
+
"includeVideos",
|
|
5864
|
+
"location",
|
|
5865
|
+
"gl",
|
|
5866
|
+
"hl",
|
|
5867
|
+
"tbs",
|
|
5868
|
+
"page",
|
|
5869
|
+
"autocorrect"
|
|
5870
|
+
];
|
|
5871
|
+
var PRIMARY_RESULT_FIELDS_BY_MODE = {
|
|
5872
|
+
search: ["organic"],
|
|
5873
|
+
images: ["images"],
|
|
5874
|
+
videos: ["videos"],
|
|
5875
|
+
places: ["places"],
|
|
5876
|
+
maps: ["maps", "places"],
|
|
5877
|
+
reviews: ["reviews"],
|
|
5878
|
+
news: ["news"],
|
|
5879
|
+
shopping: ["shopping"],
|
|
5880
|
+
"product-reviews": ["reviews", "productReviews"],
|
|
5881
|
+
lens: ["visualMatches", "organic", "images"],
|
|
5882
|
+
scholar: ["organic"],
|
|
5883
|
+
patents: ["organic"],
|
|
5884
|
+
autocomplete: ["suggestions"],
|
|
5885
|
+
webpage: []
|
|
5886
|
+
};
|
|
5887
|
+
var CONTEXT_ARRAY_FIELDS = [
|
|
5888
|
+
"peopleAlsoAsk",
|
|
5889
|
+
"relatedSearches",
|
|
5890
|
+
"topStories",
|
|
5891
|
+
"news",
|
|
5892
|
+
"images",
|
|
5893
|
+
"videos",
|
|
5894
|
+
"places",
|
|
5895
|
+
"maps",
|
|
5896
|
+
"shopping",
|
|
5897
|
+
"reviews",
|
|
5898
|
+
"productReviews",
|
|
5899
|
+
"visualMatches",
|
|
5900
|
+
"suggestions"
|
|
5901
|
+
];
|
|
5902
|
+
var serperSearchPromptGuidelines = [
|
|
5903
|
+
"Use Serper news mode for recent journalism, current events, announcements, or time-sensitive reporting.",
|
|
5904
|
+
"Use Serper images or videos mode when the user asks for visual references, screenshots, diagrams, clips, tutorials, or media results.",
|
|
5905
|
+
"Use Serper places or maps mode for local businesses, venues, addresses, ratings, phone numbers, opening details, or nearby/in-location searches.",
|
|
5906
|
+
"Use Serper reviews mode when the task needs Google business reviews. Prefer cid, fid, or placeId from a maps or places result when available; otherwise use the search query as the place identifier.",
|
|
5907
|
+
"Use Serper shopping mode for product listings, prices, merchants, offers, or purchase comparisons, and use product-reviews mode when the task needs reviews for a known product ID.",
|
|
5908
|
+
"Use Serper scholar mode for academic papers and patents mode for patent searches.",
|
|
5909
|
+
"Use Serper autocomplete mode when the task is to discover search suggestions or query completions rather than source pages.",
|
|
5910
|
+
"Use Serper lens mode for reverse image search with an image URL, and use webpage mode to scrape a specific URL. Webpage mode includes Markdown by default."
|
|
5911
|
+
];
|
|
5588
5912
|
var serperSearchOptionsSchema = Type13.Object(
|
|
5589
5913
|
{
|
|
5914
|
+
mode: Type13.Optional(
|
|
5915
|
+
Type13.Enum(SERPER_SEARCH_MODE_VALUES, {
|
|
5916
|
+
description: "Serper search type. Use 'search' for web results, 'news' for recent journalism/current events, 'images' for visual references, 'videos' for clips/tutorials, 'places' or 'maps' for local businesses/venues, 'reviews' for Google business reviews by place ID/CID/FID or query, 'shopping' for products, 'product-reviews' for product reviews, 'lens' for reverse image search, 'scholar' for scholarly articles, 'patents' for patents, 'autocomplete' for suggestions, and 'webpage' to scrape a URL."
|
|
5917
|
+
})
|
|
5918
|
+
),
|
|
5590
5919
|
gl: Type13.Optional(
|
|
5591
5920
|
Type13.String({
|
|
5592
5921
|
description: "Country code hint for Google results (for example 'us')."
|
|
@@ -5608,10 +5937,63 @@ var serperSearchOptionsSchema = Type13.Object(
|
|
|
5608
5937
|
description: "1-based results page to request from Serper."
|
|
5609
5938
|
})
|
|
5610
5939
|
),
|
|
5940
|
+
tbs: Type13.Optional(
|
|
5941
|
+
Type13.String({
|
|
5942
|
+
description: "Google time/date or vertical-specific filter string passed through to Serper, for example 'qdr:d' for past day."
|
|
5943
|
+
})
|
|
5944
|
+
),
|
|
5611
5945
|
autocorrect: Type13.Optional(
|
|
5612
5946
|
Type13.Boolean({
|
|
5613
5947
|
description: "Enable or disable Serper query autocorrection."
|
|
5614
5948
|
})
|
|
5949
|
+
),
|
|
5950
|
+
url: Type13.Optional(
|
|
5951
|
+
Type13.String({
|
|
5952
|
+
description: "URL for modes that need one: image URL for 'lens', or page URL for 'webpage'. Defaults to the query string when omitted."
|
|
5953
|
+
})
|
|
5954
|
+
),
|
|
5955
|
+
ll: Type13.Optional(
|
|
5956
|
+
Type13.String({
|
|
5957
|
+
description: "Google Maps latitude/longitude/zoom hint, for example '@40.6973709,-74.1444871,11z'."
|
|
5958
|
+
})
|
|
5959
|
+
),
|
|
5960
|
+
placeId: Type13.Optional(
|
|
5961
|
+
Type13.String({ description: "Google place ID for maps or reviews." })
|
|
5962
|
+
),
|
|
5963
|
+
cid: Type13.Optional(
|
|
5964
|
+
Type13.String({ description: "Google CID for maps or reviews." })
|
|
5965
|
+
),
|
|
5966
|
+
fid: Type13.Optional(Type13.String({ description: "Google FID for reviews." })),
|
|
5967
|
+
sortBy: Type13.Optional(
|
|
5968
|
+
Type13.String({ description: "Review sort order for reviews mode." })
|
|
5969
|
+
),
|
|
5970
|
+
topicId: Type13.Optional(
|
|
5971
|
+
Type13.String({ description: "Review topic ID for reviews mode." })
|
|
5972
|
+
),
|
|
5973
|
+
productId: Type13.Optional(
|
|
5974
|
+
Type13.String({
|
|
5975
|
+
description: "Google product ID for product-reviews mode. Defaults to the query string when omitted."
|
|
5976
|
+
})
|
|
5977
|
+
),
|
|
5978
|
+
nextPageToken: Type13.Optional(
|
|
5979
|
+
Type13.String({
|
|
5980
|
+
description: "Pagination token for reviews or product-reviews modes."
|
|
5981
|
+
})
|
|
5982
|
+
),
|
|
5983
|
+
includeMarkdown: Type13.Optional(
|
|
5984
|
+
Type13.Boolean({
|
|
5985
|
+
default: true,
|
|
5986
|
+
description: "Include Markdown content in webpage mode. Defaults to true."
|
|
5987
|
+
})
|
|
5988
|
+
),
|
|
5989
|
+
includeImages: Type13.Optional(
|
|
5990
|
+
Type13.Boolean({ description: "Include image metadata in webpage mode." })
|
|
5991
|
+
),
|
|
5992
|
+
includeLinks: Type13.Optional(
|
|
5993
|
+
Type13.Boolean({ description: "Include link metadata in webpage mode." })
|
|
5994
|
+
),
|
|
5995
|
+
includeVideos: Type13.Optional(
|
|
5996
|
+
Type13.Boolean({ description: "Include video metadata in webpage mode." })
|
|
5615
5997
|
)
|
|
5616
5998
|
},
|
|
5617
5999
|
{ description: "Serper search options." }
|
|
@@ -5631,58 +6013,218 @@ var serperImplementation = {
|
|
|
5631
6013
|
createTemplate() {
|
|
5632
6014
|
return {
|
|
5633
6015
|
credentials: { api: "SERPER_API_KEY" },
|
|
5634
|
-
options: {
|
|
6016
|
+
options: {
|
|
6017
|
+
search: {
|
|
6018
|
+
includeMarkdown: true
|
|
6019
|
+
}
|
|
6020
|
+
}
|
|
5635
6021
|
};
|
|
5636
6022
|
},
|
|
5637
|
-
getCapabilityStatus(config) {
|
|
5638
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
6023
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
6024
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
5639
6025
|
},
|
|
5640
6026
|
async search(query2, maxResults, config, context, options) {
|
|
5641
6027
|
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
5642
6028
|
if (!apiKey) {
|
|
5643
6029
|
throw new Error("is missing an API key");
|
|
5644
6030
|
}
|
|
5645
|
-
const defaults = asJsonObject(config.options?.search)
|
|
6031
|
+
const defaults = asJsonObject(config.options?.search);
|
|
5646
6032
|
const callOptions = asJsonObject(options);
|
|
5647
|
-
const {
|
|
5648
|
-
q: _ignoredQuery,
|
|
5649
|
-
num: _ignoredNum,
|
|
5650
|
-
...providerOptions
|
|
5651
|
-
} = {
|
|
6033
|
+
const requestOptions = readRequestOptions({
|
|
5652
6034
|
...defaults,
|
|
5653
|
-
...callOptions
|
|
5654
|
-
};
|
|
5655
|
-
const response = await fetch(joinUrl(resolveConfigValue(config.baseUrl)), {
|
|
5656
|
-
method: "POST",
|
|
5657
|
-
headers: {
|
|
5658
|
-
"content-type": "application/json",
|
|
5659
|
-
"x-api-key": apiKey
|
|
5660
|
-
},
|
|
5661
|
-
body: JSON.stringify({
|
|
5662
|
-
q: query2,
|
|
5663
|
-
num: clampMaxResults2(maxResults),
|
|
5664
|
-
...providerOptions
|
|
5665
|
-
}),
|
|
5666
|
-
signal: context.signal
|
|
6035
|
+
...callOptions
|
|
5667
6036
|
});
|
|
6037
|
+
const requestBody = buildRequestBody(
|
|
6038
|
+
query2,
|
|
6039
|
+
clampMaxResults2(maxResults),
|
|
6040
|
+
requestOptions
|
|
6041
|
+
);
|
|
6042
|
+
const response = await fetch(
|
|
6043
|
+
joinUrl(resolveConfigValue(config.baseUrl), requestOptions.mode),
|
|
6044
|
+
{
|
|
6045
|
+
method: "POST",
|
|
6046
|
+
headers: {
|
|
6047
|
+
"content-type": "application/json",
|
|
6048
|
+
"x-api-key": apiKey
|
|
6049
|
+
},
|
|
6050
|
+
body: JSON.stringify(requestBody),
|
|
6051
|
+
signal: context.signal
|
|
6052
|
+
}
|
|
6053
|
+
);
|
|
5668
6054
|
if (!response.ok) {
|
|
5669
6055
|
throw new Error(await buildHttpError2(response));
|
|
5670
6056
|
}
|
|
5671
6057
|
const payload = await response.json();
|
|
5672
|
-
const responseRecord =
|
|
5673
|
-
|
|
5674
|
-
|
|
6058
|
+
const responseRecord = enrichResponseRecord(
|
|
6059
|
+
asRecord3(payload) ?? {},
|
|
6060
|
+
requestOptions.mode,
|
|
6061
|
+
requestBody
|
|
6062
|
+
);
|
|
6063
|
+
const results = readPrimaryResults(responseRecord, requestOptions.mode);
|
|
6064
|
+
const searchContext = buildSearchContext(
|
|
6065
|
+
responseRecord,
|
|
6066
|
+
requestOptions.mode
|
|
6067
|
+
);
|
|
5675
6068
|
return {
|
|
5676
6069
|
provider: serperImplementation.id,
|
|
5677
|
-
results:
|
|
6070
|
+
results: results.map(
|
|
6071
|
+
(entry) => toSearchResult3(entry, searchContext, requestOptions.mode)
|
|
6072
|
+
).filter(
|
|
5678
6073
|
(result) => result !== null
|
|
5679
6074
|
).slice(0, clampMaxResults2(maxResults))
|
|
5680
6075
|
};
|
|
5681
6076
|
}
|
|
5682
6077
|
};
|
|
5683
|
-
function joinUrl(baseUrl) {
|
|
6078
|
+
function joinUrl(baseUrl, mode = "search") {
|
|
5684
6079
|
const base2 = (baseUrl ?? DEFAULT_BASE_URL3).replace(/\/+$/, "");
|
|
5685
|
-
|
|
6080
|
+
if (mode === "webpage" && base2 === DEFAULT_BASE_URL3) {
|
|
6081
|
+
return DEFAULT_SCRAPE_URL;
|
|
6082
|
+
}
|
|
6083
|
+
return `${base2}/${mode}`;
|
|
6084
|
+
}
|
|
6085
|
+
function readRequestOptions(options) {
|
|
6086
|
+
const result = {
|
|
6087
|
+
mode: readSearchMode(options.mode),
|
|
6088
|
+
extra: extractExtraMetadata(options, RESERVED_REQUEST_OPTION_KEYS)
|
|
6089
|
+
};
|
|
6090
|
+
copyStringOption(result, "gl", options.gl);
|
|
6091
|
+
copyStringOption(result, "hl", options.hl);
|
|
6092
|
+
copyStringOption(result, "location", options.location);
|
|
6093
|
+
copyStringOption(result, "tbs", options.tbs);
|
|
6094
|
+
copyStringOption(result, "url", options.url);
|
|
6095
|
+
copyStringOption(result, "ll", options.ll);
|
|
6096
|
+
copyStringOption(result, "placeId", options.placeId);
|
|
6097
|
+
copyStringOption(result, "cid", options.cid);
|
|
6098
|
+
copyStringOption(result, "fid", options.fid);
|
|
6099
|
+
copyStringOption(result, "sortBy", options.sortBy);
|
|
6100
|
+
copyStringOption(result, "topicId", options.topicId);
|
|
6101
|
+
copyStringOption(result, "productId", options.productId);
|
|
6102
|
+
copyStringOption(result, "nextPageToken", options.nextPageToken);
|
|
6103
|
+
copyBooleanOption(result, "autocorrect", options.autocorrect);
|
|
6104
|
+
copyBooleanOption(result, "includeMarkdown", options.includeMarkdown);
|
|
6105
|
+
copyBooleanOption(result, "includeImages", options.includeImages);
|
|
6106
|
+
copyBooleanOption(result, "includeLinks", options.includeLinks);
|
|
6107
|
+
copyBooleanOption(result, "includeVideos", options.includeVideos);
|
|
6108
|
+
const page = readInteger2(options.page);
|
|
6109
|
+
if (page !== void 0) {
|
|
6110
|
+
result.page = Math.max(1, page);
|
|
6111
|
+
}
|
|
6112
|
+
return result;
|
|
6113
|
+
}
|
|
6114
|
+
function buildRequestBody(query2, maxResults, options) {
|
|
6115
|
+
const common = omitUndefined({
|
|
6116
|
+
location: options.location,
|
|
6117
|
+
gl: options.gl,
|
|
6118
|
+
hl: options.hl
|
|
6119
|
+
});
|
|
6120
|
+
const withExtra = (body) => ({
|
|
6121
|
+
...body,
|
|
6122
|
+
...options.extra
|
|
6123
|
+
});
|
|
6124
|
+
switch (options.mode) {
|
|
6125
|
+
case "webpage":
|
|
6126
|
+
return withExtra(
|
|
6127
|
+
omitUndefined({
|
|
6128
|
+
url: options.url ?? query2,
|
|
6129
|
+
includeMarkdown: options.includeMarkdown ?? true,
|
|
6130
|
+
includeImages: options.includeImages,
|
|
6131
|
+
includeLinks: options.includeLinks,
|
|
6132
|
+
includeVideos: options.includeVideos
|
|
6133
|
+
})
|
|
6134
|
+
);
|
|
6135
|
+
case "product-reviews":
|
|
6136
|
+
return withExtra(
|
|
6137
|
+
omitUndefined({
|
|
6138
|
+
productId: options.productId ?? query2,
|
|
6139
|
+
nextPageToken: options.nextPageToken,
|
|
6140
|
+
...common,
|
|
6141
|
+
num: maxResults
|
|
6142
|
+
})
|
|
6143
|
+
);
|
|
6144
|
+
case "autocomplete":
|
|
6145
|
+
return withExtra({ q: query2, ...common });
|
|
6146
|
+
case "maps":
|
|
6147
|
+
return withExtra(
|
|
6148
|
+
omitUndefined({
|
|
6149
|
+
q: query2,
|
|
6150
|
+
num: maxResults,
|
|
6151
|
+
...common,
|
|
6152
|
+
ll: options.ll,
|
|
6153
|
+
placeId: options.placeId,
|
|
6154
|
+
cid: options.cid,
|
|
6155
|
+
page: options.page
|
|
6156
|
+
})
|
|
6157
|
+
);
|
|
6158
|
+
case "reviews": {
|
|
6159
|
+
const hasExplicitPlaceIdentifier = firstNonEmptyString(options.cid, options.fid, options.placeId) !== void 0;
|
|
6160
|
+
return withExtra(
|
|
6161
|
+
omitUndefined({
|
|
6162
|
+
q: hasExplicitPlaceIdentifier ? void 0 : query2,
|
|
6163
|
+
cid: options.cid,
|
|
6164
|
+
fid: options.fid,
|
|
6165
|
+
placeId: options.placeId,
|
|
6166
|
+
gl: options.gl,
|
|
6167
|
+
hl: options.hl,
|
|
6168
|
+
sortBy: options.sortBy,
|
|
6169
|
+
topicId: options.topicId,
|
|
6170
|
+
nextPageToken: options.nextPageToken
|
|
6171
|
+
})
|
|
6172
|
+
);
|
|
6173
|
+
}
|
|
6174
|
+
case "lens":
|
|
6175
|
+
return withExtra(
|
|
6176
|
+
omitUndefined({
|
|
6177
|
+
url: options.url ?? query2,
|
|
6178
|
+
...common,
|
|
6179
|
+
tbs: options.tbs
|
|
6180
|
+
})
|
|
6181
|
+
);
|
|
6182
|
+
case "scholar":
|
|
6183
|
+
return withExtra(
|
|
6184
|
+
omitUndefined({
|
|
6185
|
+
q: query2,
|
|
6186
|
+
...common,
|
|
6187
|
+
autocorrect: options.autocorrect,
|
|
6188
|
+
tbs: options.tbs,
|
|
6189
|
+
page: options.page
|
|
6190
|
+
})
|
|
6191
|
+
);
|
|
6192
|
+
default:
|
|
6193
|
+
return withExtra(
|
|
6194
|
+
omitUndefined({
|
|
6195
|
+
q: query2,
|
|
6196
|
+
num: maxResults,
|
|
6197
|
+
...common,
|
|
6198
|
+
autocorrect: options.autocorrect,
|
|
6199
|
+
tbs: options.tbs,
|
|
6200
|
+
page: options.page
|
|
6201
|
+
})
|
|
6202
|
+
);
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
function enrichResponseRecord(response, mode, requestBody) {
|
|
6206
|
+
if (mode !== "webpage") {
|
|
6207
|
+
return response;
|
|
6208
|
+
}
|
|
6209
|
+
return omitUndefined({
|
|
6210
|
+
...response,
|
|
6211
|
+
url: readString4(response.url) ?? readString4(requestBody.url)
|
|
6212
|
+
});
|
|
6213
|
+
}
|
|
6214
|
+
function readSearchMode(value) {
|
|
6215
|
+
return typeof value === "string" && SERPER_SEARCH_MODE_SET.has(value) ? value : "search";
|
|
6216
|
+
}
|
|
6217
|
+
function readPrimaryResults(response, mode) {
|
|
6218
|
+
if (mode === "webpage") {
|
|
6219
|
+
return [response];
|
|
6220
|
+
}
|
|
6221
|
+
for (const field of PRIMARY_RESULT_FIELDS_BY_MODE[mode]) {
|
|
6222
|
+
const values = asArray(response[field]);
|
|
6223
|
+
if (values) {
|
|
6224
|
+
return values;
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
return [];
|
|
5686
6228
|
}
|
|
5687
6229
|
function clampMaxResults2(value) {
|
|
5688
6230
|
return Math.max(1, Math.min(20, Math.trunc(value || 0)));
|
|
@@ -5709,18 +6251,52 @@ async function readErrorDetail2(response) {
|
|
|
5709
6251
|
return text;
|
|
5710
6252
|
}
|
|
5711
6253
|
}
|
|
5712
|
-
function toSearchResult3(entry, searchContext) {
|
|
6254
|
+
function toSearchResult3(entry, searchContext, mode) {
|
|
6255
|
+
if (typeof entry === "string") {
|
|
6256
|
+
return {
|
|
6257
|
+
title: entry,
|
|
6258
|
+
url: mode === "autocomplete" ? toGoogleSearchUrl(entry) : "",
|
|
6259
|
+
snippet: entry,
|
|
6260
|
+
metadata: {
|
|
6261
|
+
source: mode,
|
|
6262
|
+
...searchContext ? { searchContext } : {}
|
|
6263
|
+
}
|
|
6264
|
+
};
|
|
6265
|
+
}
|
|
5713
6266
|
const record = asRecord3(entry);
|
|
5714
6267
|
if (!record) {
|
|
5715
6268
|
return null;
|
|
5716
6269
|
}
|
|
5717
|
-
const
|
|
5718
|
-
const
|
|
6270
|
+
const responseMetadata = asRecord3(record.metadata);
|
|
6271
|
+
const user = asRecord3(record.user);
|
|
6272
|
+
const resultUrl = firstString(record.link, record.website, record.url, record.imageUrl) ?? "";
|
|
6273
|
+
const title = firstNonEmptyString(
|
|
6274
|
+
record.title,
|
|
6275
|
+
responseMetadata?.title,
|
|
6276
|
+
record.name,
|
|
6277
|
+
record.query,
|
|
6278
|
+
record.value,
|
|
6279
|
+
user?.name,
|
|
6280
|
+
formatReviewTitle(record, user),
|
|
6281
|
+
resultUrl
|
|
6282
|
+
) ?? "Untitled";
|
|
6283
|
+
const url2 = resultUrl || (mode === "autocomplete" ? toGoogleSearchUrl(title) : "");
|
|
5719
6284
|
const snippet = trimSnippet(
|
|
5720
|
-
|
|
6285
|
+
firstNonEmptyString(
|
|
6286
|
+
record.snippet,
|
|
6287
|
+
record.richSnippet,
|
|
6288
|
+
record.markdown,
|
|
6289
|
+
record.text,
|
|
6290
|
+
record.address,
|
|
6291
|
+
record.price,
|
|
6292
|
+
record.date,
|
|
6293
|
+
record.name,
|
|
6294
|
+
record.value,
|
|
6295
|
+
record.url
|
|
6296
|
+
) ?? ""
|
|
5721
6297
|
);
|
|
5722
6298
|
const metadata = omitUndefined({
|
|
5723
|
-
source: "organic",
|
|
6299
|
+
source: readString4(record.source) ?? (mode === "search" ? "organic" : mode),
|
|
5724
6300
|
position: readNumber(record.position),
|
|
5725
6301
|
date: readString4(record.date),
|
|
5726
6302
|
attributes: asRecord3(record.attributes),
|
|
@@ -5728,7 +6304,16 @@ function toSearchResult3(entry, searchContext) {
|
|
|
5728
6304
|
rating: readNumber(record.rating),
|
|
5729
6305
|
ratingCount: readNumber(record.ratingCount),
|
|
5730
6306
|
cid: readString4(record.cid),
|
|
5731
|
-
...extractExtraMetadata(record, [
|
|
6307
|
+
...extractExtraMetadata(record, [
|
|
6308
|
+
"title",
|
|
6309
|
+
"name",
|
|
6310
|
+
"query",
|
|
6311
|
+
"value",
|
|
6312
|
+
"link",
|
|
6313
|
+
"website",
|
|
6314
|
+
"url",
|
|
6315
|
+
"snippet"
|
|
6316
|
+
]),
|
|
5732
6317
|
...searchContext ? { searchContext } : {}
|
|
5733
6318
|
});
|
|
5734
6319
|
return {
|
|
@@ -5738,29 +6323,78 @@ function toSearchResult3(entry, searchContext) {
|
|
|
5738
6323
|
...Object.keys(metadata).length > 0 ? { metadata } : {}
|
|
5739
6324
|
};
|
|
5740
6325
|
}
|
|
5741
|
-
function buildSearchContext(response) {
|
|
6326
|
+
function buildSearchContext(response, mode) {
|
|
5742
6327
|
const context = omitUndefined({
|
|
5743
6328
|
searchParameters: asRecord3(response.searchParameters),
|
|
5744
6329
|
searchInformation: asRecord3(response.searchInformation),
|
|
5745
6330
|
credits: readNumber(response.credits),
|
|
5746
6331
|
answerBox: asRecord3(response.answerBox),
|
|
5747
|
-
knowledgeGraph: asRecord3(response.knowledgeGraph)
|
|
5748
|
-
peopleAlsoAsk: asArray(response.peopleAlsoAsk),
|
|
5749
|
-
relatedSearches: asArray(response.relatedSearches),
|
|
5750
|
-
topStories: asArray(response.topStories),
|
|
5751
|
-
news: asArray(response.news),
|
|
5752
|
-
images: asArray(response.images),
|
|
5753
|
-
videos: asArray(response.videos),
|
|
5754
|
-
places: asArray(response.places)
|
|
6332
|
+
knowledgeGraph: asRecord3(response.knowledgeGraph)
|
|
5755
6333
|
});
|
|
6334
|
+
const primaryResultFields = new Set(
|
|
6335
|
+
PRIMARY_RESULT_FIELDS_BY_MODE[mode]
|
|
6336
|
+
);
|
|
6337
|
+
for (const field of CONTEXT_ARRAY_FIELDS) {
|
|
6338
|
+
if (primaryResultFields.has(field)) {
|
|
6339
|
+
continue;
|
|
6340
|
+
}
|
|
6341
|
+
const value = asArray(response[field]);
|
|
6342
|
+
if (value) {
|
|
6343
|
+
context[field] = value;
|
|
6344
|
+
}
|
|
6345
|
+
}
|
|
5756
6346
|
return Object.keys(context).length > 0 ? context : void 0;
|
|
5757
6347
|
}
|
|
5758
|
-
function
|
|
5759
|
-
|
|
5760
|
-
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
6348
|
+
function copyStringOption(target, key, value) {
|
|
6349
|
+
const text = readString4(value);
|
|
6350
|
+
if (text !== void 0) {
|
|
6351
|
+
target[key] = text;
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
function copyBooleanOption(target, key, value) {
|
|
6355
|
+
const flag = readBoolean(value);
|
|
6356
|
+
if (flag !== void 0) {
|
|
6357
|
+
target[key] = flag;
|
|
6358
|
+
}
|
|
6359
|
+
}
|
|
6360
|
+
function firstString(...values) {
|
|
6361
|
+
return values.find((value) => typeof value === "string");
|
|
6362
|
+
}
|
|
6363
|
+
function toGoogleSearchUrl(query2) {
|
|
6364
|
+
return `https://www.google.com/search?q=${encodeURIComponent(query2)}`;
|
|
6365
|
+
}
|
|
6366
|
+
function formatReviewTitle(record, user) {
|
|
6367
|
+
const userName = readString4(user?.name);
|
|
6368
|
+
const rating = readNumber(record.rating);
|
|
6369
|
+
const date = readString4(record.date) ?? readString4(record.isoDate);
|
|
6370
|
+
if (userName && rating !== void 0) {
|
|
6371
|
+
return `${userName} (${rating}-star review)`;
|
|
6372
|
+
}
|
|
6373
|
+
if (userName) {
|
|
6374
|
+
return `${userName}'s review`;
|
|
6375
|
+
}
|
|
6376
|
+
if (rating !== void 0 && date) {
|
|
6377
|
+
return `${rating}-star review from ${date}`;
|
|
6378
|
+
}
|
|
6379
|
+
if (rating !== void 0) {
|
|
6380
|
+
return `${rating}-star review`;
|
|
6381
|
+
}
|
|
6382
|
+
if (date) {
|
|
6383
|
+
return `Review from ${date}`;
|
|
6384
|
+
}
|
|
6385
|
+
return void 0;
|
|
6386
|
+
}
|
|
6387
|
+
function firstNonEmptyString(...values) {
|
|
6388
|
+
return values.find(
|
|
6389
|
+
(value) => typeof value === "string" && value.length > 0
|
|
6390
|
+
);
|
|
6391
|
+
}
|
|
6392
|
+
function extractExtraMetadata(record, ignoredKeys) {
|
|
6393
|
+
return Object.fromEntries(
|
|
6394
|
+
Object.entries(record).filter(
|
|
6395
|
+
([key, value]) => !ignoredKeys.includes(key) && value !== void 0
|
|
6396
|
+
)
|
|
6397
|
+
);
|
|
5764
6398
|
}
|
|
5765
6399
|
function omitUndefined(value) {
|
|
5766
6400
|
return Object.fromEntries(
|
|
@@ -5779,6 +6413,12 @@ function readString4(value) {
|
|
|
5779
6413
|
function readNumber(value) {
|
|
5780
6414
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
5781
6415
|
}
|
|
6416
|
+
function readInteger2(value) {
|
|
6417
|
+
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
6418
|
+
}
|
|
6419
|
+
function readBoolean(value) {
|
|
6420
|
+
return typeof value === "boolean" ? value : void 0;
|
|
6421
|
+
}
|
|
5782
6422
|
var serperProvider = defineProvider({
|
|
5783
6423
|
id: "serper",
|
|
5784
6424
|
label: serperImplementation.label,
|
|
@@ -5788,14 +6428,16 @@ var serperProvider = defineProvider({
|
|
|
5788
6428
|
fields: ["credentials", "baseUrl", "options", "settings"],
|
|
5789
6429
|
optionCapabilities: ["search"]
|
|
5790
6430
|
},
|
|
5791
|
-
getCapabilityStatus: (config, cwd, tool) => serperImplementation.getCapabilityStatus(
|
|
6431
|
+
getCapabilityStatus: (config, cwd, tool, options) => serperImplementation.getCapabilityStatus(
|
|
5792
6432
|
config,
|
|
5793
6433
|
cwd,
|
|
5794
|
-
tool
|
|
6434
|
+
tool,
|
|
6435
|
+
options
|
|
5795
6436
|
),
|
|
5796
6437
|
capabilities: {
|
|
5797
6438
|
search: defineCapability({
|
|
5798
6439
|
options: serperImplementation.getToolOptionsSchema?.("search"),
|
|
6440
|
+
promptGuidelines: serperSearchPromptGuidelines,
|
|
5799
6441
|
async execute(input, ctx) {
|
|
5800
6442
|
const { query: query2, maxResults, options } = input;
|
|
5801
6443
|
return await serperImplementation.search(
|
|
@@ -5920,8 +6562,8 @@ var tavilyImplementation = {
|
|
|
5920
6562
|
}
|
|
5921
6563
|
};
|
|
5922
6564
|
},
|
|
5923
|
-
getCapabilityStatus(config) {
|
|
5924
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
6565
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
6566
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
5925
6567
|
},
|
|
5926
6568
|
async search(query2, maxResults, config, _context, options) {
|
|
5927
6569
|
const client = createClient8(config);
|
|
@@ -6019,10 +6661,11 @@ var tavilyProvider = defineProvider({
|
|
|
6019
6661
|
createTemplate: () => tavilyImplementation.createTemplate(),
|
|
6020
6662
|
fields: ["credentials", "baseUrl", "options", "settings"]
|
|
6021
6663
|
},
|
|
6022
|
-
getCapabilityStatus: (config, cwd, tool) => tavilyImplementation.getCapabilityStatus(
|
|
6664
|
+
getCapabilityStatus: (config, cwd, tool, options) => tavilyImplementation.getCapabilityStatus(
|
|
6023
6665
|
config,
|
|
6024
6666
|
cwd,
|
|
6025
|
-
tool
|
|
6667
|
+
tool,
|
|
6668
|
+
options
|
|
6026
6669
|
),
|
|
6027
6670
|
capabilities: {
|
|
6028
6671
|
search: defineCapability({
|
|
@@ -6126,8 +6769,8 @@ var valyuImplementation = {
|
|
|
6126
6769
|
}
|
|
6127
6770
|
};
|
|
6128
6771
|
},
|
|
6129
|
-
getCapabilityStatus(config) {
|
|
6130
|
-
return getApiKeyStatus(config?.credentials?.api);
|
|
6772
|
+
getCapabilityStatus(config, _cwd, _tool, options) {
|
|
6773
|
+
return getApiKeyStatus(config?.credentials?.api, options);
|
|
6131
6774
|
},
|
|
6132
6775
|
async search(query2, maxResults, config, _context, searchOptions) {
|
|
6133
6776
|
const client = createClient9(config);
|
|
@@ -6302,10 +6945,11 @@ var valyuProvider = defineProvider({
|
|
|
6302
6945
|
fields: ["credentials", "baseUrl", "options", "settings"],
|
|
6303
6946
|
optionCapabilities: ["search", "answer", "research"]
|
|
6304
6947
|
},
|
|
6305
|
-
getCapabilityStatus: (config, cwd, tool) => valyuImplementation.getCapabilityStatus(
|
|
6948
|
+
getCapabilityStatus: (config, cwd, tool, options) => valyuImplementation.getCapabilityStatus(
|
|
6306
6949
|
config,
|
|
6307
6950
|
cwd,
|
|
6308
|
-
tool
|
|
6951
|
+
tool,
|
|
6952
|
+
options
|
|
6309
6953
|
),
|
|
6310
6954
|
capabilities: {
|
|
6311
6955
|
search: defineCapability({
|
|
@@ -6380,9 +7024,6 @@ var PROVIDERS_BY_ID = PROVIDERS;
|
|
|
6380
7024
|
var PROVIDER_LIST = Object.values(PROVIDERS);
|
|
6381
7025
|
var PROVIDER_IDS = Object.keys(PROVIDERS);
|
|
6382
7026
|
|
|
6383
|
-
// src/types.ts
|
|
6384
|
-
var TOOLS = ["search", "contents", "answer", "research"];
|
|
6385
|
-
|
|
6386
7027
|
// src/provider-tools.ts
|
|
6387
7028
|
var TOOL_INFO = {
|
|
6388
7029
|
search: {
|
|
@@ -7138,17 +7779,21 @@ function isPlainObject4(value) {
|
|
|
7138
7779
|
function getMappedProviderIdForTool(config, tool) {
|
|
7139
7780
|
return getMappedProviderForTool(config, tool);
|
|
7140
7781
|
}
|
|
7141
|
-
function getProviderCapabilityStatus(config, cwd, providerId, tool) {
|
|
7782
|
+
function getProviderCapabilityStatus(config, cwd, providerId, tool, options) {
|
|
7142
7783
|
const provider = PROVIDERS[providerId];
|
|
7143
7784
|
return provider.getCapabilityStatus(
|
|
7144
7785
|
getEffectiveProviderConfig(config, providerId),
|
|
7145
7786
|
cwd,
|
|
7146
|
-
tool
|
|
7787
|
+
tool,
|
|
7788
|
+
options
|
|
7147
7789
|
);
|
|
7148
7790
|
}
|
|
7149
7791
|
function isProviderCapabilityReady(status) {
|
|
7150
7792
|
return status.state === "ready";
|
|
7151
7793
|
}
|
|
7794
|
+
function isProviderCapabilityExposable(status) {
|
|
7795
|
+
return status.state === "ready" || status.state === "deferred_secret";
|
|
7796
|
+
}
|
|
7152
7797
|
function getProviderSetupState(config, providerId) {
|
|
7153
7798
|
if (providerId === "claude" || providerId === "codex") {
|
|
7154
7799
|
return "builtin";
|
|
@@ -7163,12 +7808,17 @@ function getProviderSetupState(config, providerId) {
|
|
|
7163
7808
|
if (providerId === "cloudflare") {
|
|
7164
7809
|
return providerConfig.credentials !== void 0 || providerConfig.accountId !== void 0 ? "configured" : "none";
|
|
7165
7810
|
}
|
|
7811
|
+
if (providerId === "firecrawl") {
|
|
7812
|
+
return providerConfig.credentials !== void 0 || providerConfig.baseUrl !== void 0 ? "configured" : "none";
|
|
7813
|
+
}
|
|
7166
7814
|
return providerConfig.credentials !== void 0 ? "configured" : "none";
|
|
7167
7815
|
}
|
|
7168
7816
|
function formatProviderCapabilityStatus(status, providerId, tool) {
|
|
7169
7817
|
switch (status.state) {
|
|
7170
7818
|
case "ready":
|
|
7171
7819
|
return "Ready";
|
|
7820
|
+
case "deferred_secret":
|
|
7821
|
+
return "Secret resolved on first use";
|
|
7172
7822
|
case "missing_api_key":
|
|
7173
7823
|
return "Missing API key";
|
|
7174
7824
|
case "missing_executable":
|
|
@@ -8812,6 +9462,158 @@ function cleanupCapabilityOptions(config, keys) {
|
|
|
8812
9462
|
cleanupEmpty(config, "options");
|
|
8813
9463
|
}
|
|
8814
9464
|
|
|
9465
|
+
// src/tool-display.ts
|
|
9466
|
+
import { formatSize } from "@earendil-works/pi-coding-agent";
|
|
9467
|
+
var ANSWER_EXCERPT_MAX_LENGTH = 100;
|
|
9468
|
+
function buildSearchToolDisplay(details) {
|
|
9469
|
+
return buildToolDisplay(details.provider, buildSearchSummaryParts(details));
|
|
9470
|
+
}
|
|
9471
|
+
function buildProgressDisplay(providerId, action) {
|
|
9472
|
+
return {
|
|
9473
|
+
provider: getProviderDisplay(providerId),
|
|
9474
|
+
progress: { action }
|
|
9475
|
+
};
|
|
9476
|
+
}
|
|
9477
|
+
function buildProviderToolDisplay({
|
|
9478
|
+
capability,
|
|
9479
|
+
providerId,
|
|
9480
|
+
details,
|
|
9481
|
+
text,
|
|
9482
|
+
outputBytes,
|
|
9483
|
+
outputTruncated,
|
|
9484
|
+
failedItemCount
|
|
9485
|
+
}) {
|
|
9486
|
+
const summary = capability === "contents" && details.tool === "web_contents" ? buildContentsDisplaySummary(details, text, {
|
|
9487
|
+
outputBytes,
|
|
9488
|
+
outputTruncated,
|
|
9489
|
+
failedItemCount
|
|
9490
|
+
}) : capability === "research" && text ? { success: text } : buildCollapsedProviderToolSummaryParts(details, text);
|
|
9491
|
+
return buildToolDisplay(providerId, summary);
|
|
9492
|
+
}
|
|
9493
|
+
function buildCollapsedProviderToolSummary(details, text) {
|
|
9494
|
+
const summary = buildCollapsedProviderToolSummaryParts(details, text);
|
|
9495
|
+
return summary.failure ? `${summary.success}, ${summary.failure}` : summary.success;
|
|
9496
|
+
}
|
|
9497
|
+
function buildCollapsedProviderToolSummaryParts(details, text) {
|
|
9498
|
+
if (details?.tool === "web_answer") {
|
|
9499
|
+
return buildAnswerCollapsedSummary(details, text);
|
|
9500
|
+
}
|
|
9501
|
+
if (details?.tool === "web_contents") {
|
|
9502
|
+
return buildContentsSummary(details, text);
|
|
9503
|
+
}
|
|
9504
|
+
const baseSummary = getCompactProviderToolSummary(details) ?? getFirstLine(text) ?? `${details?.tool ?? "tool"} output available`;
|
|
9505
|
+
return { success: baseSummary };
|
|
9506
|
+
}
|
|
9507
|
+
function buildSearchSummaryParts({
|
|
9508
|
+
queryCount,
|
|
9509
|
+
resultCount,
|
|
9510
|
+
failedQueryCount
|
|
9511
|
+
}) {
|
|
9512
|
+
const success = typeof resultCount === "number" ? `${resultCount} result${resultCount === 1 ? "" : "s"}` : "Search output available";
|
|
9513
|
+
if (failedQueryCount && failedQueryCount > 0 && queryCount) {
|
|
9514
|
+
return {
|
|
9515
|
+
success,
|
|
9516
|
+
failure: `${failedQueryCount} of ${queryCount} ${queryCount === 1 ? "query" : "queries"} failed`
|
|
9517
|
+
};
|
|
9518
|
+
}
|
|
9519
|
+
return { success };
|
|
9520
|
+
}
|
|
9521
|
+
function buildToolDisplay(providerId, outcome) {
|
|
9522
|
+
return {
|
|
9523
|
+
provider: getProviderDisplay(providerId),
|
|
9524
|
+
outcome
|
|
9525
|
+
};
|
|
9526
|
+
}
|
|
9527
|
+
function getProviderDisplay(providerId) {
|
|
9528
|
+
const provider = PROVIDERS_BY_ID[providerId];
|
|
9529
|
+
return { id: providerId, label: provider?.label ?? providerId };
|
|
9530
|
+
}
|
|
9531
|
+
function buildContentsDisplaySummary(details, text, metadata) {
|
|
9532
|
+
const totalCount = details.itemCount ?? inferContentsPageCount(text);
|
|
9533
|
+
const failedCount = metadata.failedItemCount ?? inferContentsFailureCount(text);
|
|
9534
|
+
const successCount = totalCount === void 0 ? void 0 : Math.max(0, totalCount - (failedCount ?? 0));
|
|
9535
|
+
const sizeSummary = typeof metadata.outputBytes === "number" ? `${formatSize(metadata.outputBytes)}${metadata.outputTruncated ? " (truncated)" : ""}` : void 0;
|
|
9536
|
+
const success = successCount === void 0 ? sizeSummary ?? "Contents output available" : successCount === 1 && sizeSummary ? sizeSummary : `${successCount} page${successCount === 1 ? "" : "s"}${sizeSummary ? `, ${sizeSummary}` : ""}`;
|
|
9537
|
+
if (failedCount && failedCount > 0 && totalCount) {
|
|
9538
|
+
return {
|
|
9539
|
+
success,
|
|
9540
|
+
failure: `${failedCount} of ${totalCount} ${totalCount === 1 ? "page" : "pages"} failed`
|
|
9541
|
+
};
|
|
9542
|
+
}
|
|
9543
|
+
return { success };
|
|
9544
|
+
}
|
|
9545
|
+
function buildAnswerCollapsedSummary(details, text) {
|
|
9546
|
+
if (typeof details.queryCount === "number" && (details.queryCount > 1 || (details.failedQueryCount ?? 0) > 0)) {
|
|
9547
|
+
return buildAnswerSummary(details);
|
|
9548
|
+
}
|
|
9549
|
+
return { success: buildAnswerExcerpt(text) ?? "Answer output available" };
|
|
9550
|
+
}
|
|
9551
|
+
function buildAnswerSummary(details) {
|
|
9552
|
+
const queryCount = details.queryCount ?? 0;
|
|
9553
|
+
const failedQueryCount = details.failedQueryCount ?? 0;
|
|
9554
|
+
const answerCount = Math.max(0, queryCount - failedQueryCount);
|
|
9555
|
+
const success = `${answerCount} answer${answerCount === 1 ? "" : "s"}`;
|
|
9556
|
+
if (failedQueryCount > 0) {
|
|
9557
|
+
return {
|
|
9558
|
+
success,
|
|
9559
|
+
failure: `${failedQueryCount} of ${queryCount} ${queryCount === 1 ? "question" : "questions"} failed`
|
|
9560
|
+
};
|
|
9561
|
+
}
|
|
9562
|
+
return { success };
|
|
9563
|
+
}
|
|
9564
|
+
function buildAnswerExcerpt(text) {
|
|
9565
|
+
const excerpt = getFirstLine(text);
|
|
9566
|
+
if (!excerpt) {
|
|
9567
|
+
return void 0;
|
|
9568
|
+
}
|
|
9569
|
+
if (excerpt.length <= ANSWER_EXCERPT_MAX_LENGTH) {
|
|
9570
|
+
return excerpt;
|
|
9571
|
+
}
|
|
9572
|
+
return `${excerpt.slice(0, ANSWER_EXCERPT_MAX_LENGTH - 1).trimEnd()}\u2026`;
|
|
9573
|
+
}
|
|
9574
|
+
function buildContentsSummary(details, text) {
|
|
9575
|
+
const totalCount = details.itemCount ?? inferContentsPageCount(text);
|
|
9576
|
+
const failedCount = inferContentsFailureCount(text);
|
|
9577
|
+
const successCount = totalCount === void 0 ? void 0 : Math.max(0, totalCount - (failedCount ?? 0));
|
|
9578
|
+
const success = successCount === void 0 ? "Contents output available" : `${successCount} page${successCount === 1 ? "" : "s"}`;
|
|
9579
|
+
if (failedCount && failedCount > 0 && totalCount) {
|
|
9580
|
+
return {
|
|
9581
|
+
success,
|
|
9582
|
+
failure: `${failedCount} of ${totalCount} ${totalCount === 1 ? "page" : "pages"} failed`
|
|
9583
|
+
};
|
|
9584
|
+
}
|
|
9585
|
+
return { success };
|
|
9586
|
+
}
|
|
9587
|
+
function getCompactProviderToolSummary(details) {
|
|
9588
|
+
if (!details) {
|
|
9589
|
+
return void 0;
|
|
9590
|
+
}
|
|
9591
|
+
if (details.tool === "web_contents" && typeof details.itemCount === "number") {
|
|
9592
|
+
return `${details.itemCount} page${details.itemCount === 1 ? "" : "s"}`;
|
|
9593
|
+
}
|
|
9594
|
+
if (details.tool === "web_research") {
|
|
9595
|
+
return "Research";
|
|
9596
|
+
}
|
|
9597
|
+
return void 0;
|
|
9598
|
+
}
|
|
9599
|
+
function inferContentsPageCount(text) {
|
|
9600
|
+
if (!text) {
|
|
9601
|
+
return void 0;
|
|
9602
|
+
}
|
|
9603
|
+
const pageMatches = text.match(/^##\s+/gm);
|
|
9604
|
+
return pageMatches?.length;
|
|
9605
|
+
}
|
|
9606
|
+
function inferContentsFailureCount(text) {
|
|
9607
|
+
if (!text) {
|
|
9608
|
+
return void 0;
|
|
9609
|
+
}
|
|
9610
|
+
const failureMatches = text.match(/^##\s+(?:\d+\.\s+)?Error:/gm);
|
|
9611
|
+
return failureMatches?.length;
|
|
9612
|
+
}
|
|
9613
|
+
function getFirstLine(text) {
|
|
9614
|
+
return text?.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
|
|
9615
|
+
}
|
|
9616
|
+
|
|
8815
9617
|
// src/index.ts
|
|
8816
9618
|
var DEFAULT_MAX_RESULTS = 5;
|
|
8817
9619
|
var MAX_ALLOWED_RESULTS = 20;
|
|
@@ -8828,6 +9630,10 @@ var CAPABILITY_TOOL_NAMES = {
|
|
|
8828
9630
|
research: "web_research"
|
|
8829
9631
|
};
|
|
8830
9632
|
var MANAGED_TOOL_NAMES = Object.values(CAPABILITY_TOOL_NAMES);
|
|
9633
|
+
var DEFAULT_SUMMARY_SYMBOLS = {
|
|
9634
|
+
success: "\u2714",
|
|
9635
|
+
failure: "\u2718"
|
|
9636
|
+
};
|
|
8831
9637
|
function webProvidersExtension(pi) {
|
|
8832
9638
|
const activeWebResearchRequests = /* @__PURE__ */ new Map();
|
|
8833
9639
|
let latestWidgetContext;
|
|
@@ -8873,7 +9679,7 @@ function webProvidersExtension(pi) {
|
|
|
8873
9679
|
if ("registerMessageRenderer" in pi) {
|
|
8874
9680
|
pi.registerMessageRenderer(
|
|
8875
9681
|
WEB_RESEARCH_RESULT_MESSAGE_TYPE,
|
|
8876
|
-
renderWebResearchResultMessage
|
|
9682
|
+
(message, state, theme) => renderWebResearchResultMessage(message, state, theme)
|
|
8877
9683
|
);
|
|
8878
9684
|
}
|
|
8879
9685
|
pi.registerCommand("web-providers", {
|
|
@@ -8934,7 +9740,7 @@ function registerWebSearchTool(pi, providerIds) {
|
|
|
8934
9740
|
pi.registerTool({
|
|
8935
9741
|
name: "web_search",
|
|
8936
9742
|
label: "Web Search",
|
|
8937
|
-
description: `Find likely sources on the public web for up to ${MAX_SEARCH_QUERIES} queries in a single call and return titles, URLs, and snippets grouped by query. Output is truncated to ${DEFAULT_MAX_LINES} lines or ${
|
|
9743
|
+
description: `Find likely sources on the public web for up to ${MAX_SEARCH_QUERIES} queries in a single call and return titles, URLs, and snippets grouped by query. Output is truncated to ${DEFAULT_MAX_LINES} lines or ${formatSize2(DEFAULT_MAX_BYTES)} when needed.`,
|
|
8938
9744
|
promptGuidelines: buildPromptGuidelines("search", selectedProviderId, [
|
|
8939
9745
|
"Batch related searches when grouped comparison matters; use separate sibling web_search calls when independent results should surface as soon as they are ready."
|
|
8940
9746
|
]),
|
|
@@ -9179,12 +9985,20 @@ function getAvailableProviderIdsForCapability(config, cwd, capability) {
|
|
|
9179
9985
|
if (!providerId) {
|
|
9180
9986
|
return [];
|
|
9181
9987
|
}
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
return [providerId];
|
|
9185
|
-
} catch {
|
|
9988
|
+
const provider = PROVIDERS_BY_ID[providerId];
|
|
9989
|
+
if (!supportsTool2(provider, capability)) {
|
|
9186
9990
|
return [];
|
|
9187
9991
|
}
|
|
9992
|
+
const status = getProviderCapabilityStatus(
|
|
9993
|
+
config,
|
|
9994
|
+
cwd,
|
|
9995
|
+
providerId,
|
|
9996
|
+
capability,
|
|
9997
|
+
{
|
|
9998
|
+
resolveSecrets: false
|
|
9999
|
+
}
|
|
10000
|
+
);
|
|
10001
|
+
return isProviderCapabilityExposable(status) ? [providerId] : [];
|
|
9188
10002
|
}
|
|
9189
10003
|
function getProviderStatusForTool(config, cwd, providerId, capability) {
|
|
9190
10004
|
return getProviderCapabilityStatus(config, cwd, providerId, capability);
|
|
@@ -9324,6 +10138,7 @@ async function executeSearchToolInternal({
|
|
|
9324
10138
|
);
|
|
9325
10139
|
const batchProgress = searchQueries.length > 1 ? createBatchCompletionReporter(
|
|
9326
10140
|
"Searching",
|
|
10141
|
+
provider.id,
|
|
9327
10142
|
provider.label,
|
|
9328
10143
|
searchQueries.length,
|
|
9329
10144
|
progressReporter.report
|
|
@@ -9372,7 +10187,7 @@ async function executeSearchToolInternal({
|
|
|
9372
10187
|
progressReporter.stop();
|
|
9373
10188
|
}
|
|
9374
10189
|
if (outcomes.every((outcome) => outcome.error !== void 0)) {
|
|
9375
|
-
throw buildSearchBatchError(outcomes);
|
|
10190
|
+
throw buildSearchBatchError(outcomes, provider.label);
|
|
9376
10191
|
}
|
|
9377
10192
|
const prefetch = prefetchOptions !== void 0 && executionOverrides === void 0 ? await startContentsPrefetch({
|
|
9378
10193
|
config,
|
|
@@ -9384,9 +10199,11 @@ async function executeSearchToolInternal({
|
|
|
9384
10199
|
formatSearchResponses(outcomes, prefetch),
|
|
9385
10200
|
"web-search"
|
|
9386
10201
|
);
|
|
10202
|
+
const details = buildWebSearchDetails(provider.id, outcomes);
|
|
9387
10203
|
return {
|
|
9388
10204
|
content: [{ type: "text", text: rendered }],
|
|
9389
|
-
details
|
|
10205
|
+
details,
|
|
10206
|
+
display: buildSearchToolDisplay2(details)
|
|
9390
10207
|
};
|
|
9391
10208
|
}
|
|
9392
10209
|
async function executeRawProviderRequest({
|
|
@@ -9461,16 +10278,22 @@ async function executeRawProviderRequest({
|
|
|
9461
10278
|
input
|
|
9462
10279
|
});
|
|
9463
10280
|
}
|
|
9464
|
-
function buildSearchBatchError(outcomes) {
|
|
10281
|
+
function buildSearchBatchError(outcomes, providerLabel) {
|
|
9465
10282
|
const failed = outcomes.filter((outcome) => outcome.error !== void 0);
|
|
9466
10283
|
if (failed.length === 1) {
|
|
9467
|
-
return new Error(
|
|
10284
|
+
return new Error(
|
|
10285
|
+
formatProviderCapabilityFailure(
|
|
10286
|
+
providerLabel,
|
|
10287
|
+
"search",
|
|
10288
|
+
failed[0]?.error ?? ""
|
|
10289
|
+
)
|
|
10290
|
+
);
|
|
9468
10291
|
}
|
|
9469
10292
|
const summary = failed.map(
|
|
9470
10293
|
(outcome, index) => `${index + 1}. ${formatQuotedPreview(outcome.query, 40)} \u2014 ${outcome.error}`
|
|
9471
10294
|
).join("; ");
|
|
9472
10295
|
return new Error(
|
|
9473
|
-
|
|
10296
|
+
`${providerLabel} search failed for ${failed.length} queries: ${summary}`
|
|
9474
10297
|
);
|
|
9475
10298
|
}
|
|
9476
10299
|
async function executeSingleSearchQuery({
|
|
@@ -9546,6 +10369,7 @@ async function executeAnswerToolInternal({
|
|
|
9546
10369
|
);
|
|
9547
10370
|
const batchProgress = answerQueries.length > 1 ? createBatchCompletionReporter(
|
|
9548
10371
|
"Answering",
|
|
10372
|
+
provider.id,
|
|
9549
10373
|
provider.label,
|
|
9550
10374
|
answerQueries.length,
|
|
9551
10375
|
progressReporter.report
|
|
@@ -9588,7 +10412,7 @@ async function executeAnswerToolInternal({
|
|
|
9588
10412
|
progressReporter.stop();
|
|
9589
10413
|
}
|
|
9590
10414
|
if (outcomes.every((outcome) => outcome.error !== void 0)) {
|
|
9591
|
-
throw buildAnswerBatchError(outcomes);
|
|
10415
|
+
throw buildAnswerBatchError(outcomes, provider.label);
|
|
9592
10416
|
}
|
|
9593
10417
|
const text = await truncateAndSave(
|
|
9594
10418
|
formatAnswerResponses(outcomes),
|
|
@@ -9597,19 +10421,31 @@ async function executeAnswerToolInternal({
|
|
|
9597
10421
|
const details = buildWebAnswerDetails(provider.id, outcomes);
|
|
9598
10422
|
return {
|
|
9599
10423
|
content: [{ type: "text", text }],
|
|
9600
|
-
details
|
|
10424
|
+
details,
|
|
10425
|
+
display: buildProviderToolDisplay2({
|
|
10426
|
+
capability: "answer",
|
|
10427
|
+
providerId: provider.id,
|
|
10428
|
+
details,
|
|
10429
|
+
text
|
|
10430
|
+
})
|
|
9601
10431
|
};
|
|
9602
10432
|
}
|
|
9603
|
-
function buildAnswerBatchError(outcomes) {
|
|
10433
|
+
function buildAnswerBatchError(outcomes, providerLabel) {
|
|
9604
10434
|
const failed = outcomes.filter((outcome) => outcome.error !== void 0);
|
|
9605
10435
|
if (failed.length === 1) {
|
|
9606
|
-
return new Error(
|
|
10436
|
+
return new Error(
|
|
10437
|
+
formatProviderCapabilityFailure(
|
|
10438
|
+
providerLabel,
|
|
10439
|
+
"answer",
|
|
10440
|
+
failed[0]?.error ?? ""
|
|
10441
|
+
)
|
|
10442
|
+
);
|
|
9607
10443
|
}
|
|
9608
10444
|
const summary = failed.map(
|
|
9609
10445
|
(outcome, index) => `${index + 1}. ${formatQuotedPreview(outcome.query, 40)} \u2014 ${outcome.error}`
|
|
9610
10446
|
).join("; ");
|
|
9611
10447
|
return new Error(
|
|
9612
|
-
|
|
10448
|
+
`${providerLabel} answer failed for ${failed.length} questions: ${summary}`
|
|
9613
10449
|
);
|
|
9614
10450
|
}
|
|
9615
10451
|
function formatAnswerResponses(outcomes) {
|
|
@@ -9671,13 +10507,24 @@ async function executeProviderOperation({
|
|
|
9671
10507
|
});
|
|
9672
10508
|
}
|
|
9673
10509
|
if (capability === "contents") {
|
|
10510
|
+
const urlCount = (urls ?? []).length;
|
|
9674
10511
|
onProgress?.(
|
|
9675
|
-
`Fetching contents via ${provider.label} for ${
|
|
10512
|
+
`Fetching contents via ${provider.label} for ${urlCount} URL(s)`,
|
|
10513
|
+
buildProgressDisplay2(
|
|
10514
|
+
provider.id,
|
|
10515
|
+
urlCount === 1 ? "Fetching page" : `Fetching ${urlCount} pages`
|
|
10516
|
+
)
|
|
9676
10517
|
);
|
|
9677
10518
|
} else if (capability === "answer") {
|
|
9678
|
-
onProgress?.(
|
|
10519
|
+
onProgress?.(
|
|
10520
|
+
`Answering via ${provider.label}`,
|
|
10521
|
+
buildProgressDisplay2(provider.id, "Answering")
|
|
10522
|
+
);
|
|
9679
10523
|
} else if (capability === "research") {
|
|
9680
|
-
onProgress?.(
|
|
10524
|
+
onProgress?.(
|
|
10525
|
+
`Researching via ${provider.label}`,
|
|
10526
|
+
buildProgressDisplay2(provider.id, "Researching")
|
|
10527
|
+
);
|
|
9681
10528
|
}
|
|
9682
10529
|
const result = executionOverride ? await executeProviderExecution(executionOverride, {
|
|
9683
10530
|
cwd: ctx.cwd,
|
|
@@ -9782,18 +10629,36 @@ async function executeProviderToolInternal({
|
|
|
9782
10629
|
} finally {
|
|
9783
10630
|
progressReporter.stop();
|
|
9784
10631
|
}
|
|
9785
|
-
const
|
|
9786
|
-
tool: `web_${capability}`,
|
|
9787
|
-
provider: response.provider,
|
|
9788
|
-
itemCount: isContentsResponse2(response) ? response.answers.length : response.itemCount
|
|
9789
|
-
};
|
|
9790
|
-
const text = await truncateAndSave(
|
|
10632
|
+
const rendered = await truncateAndSaveWithMetadata(
|
|
9791
10633
|
isContentsResponse2(response) ? formatContentsResponse(response) : response.text,
|
|
9792
10634
|
capability
|
|
9793
10635
|
);
|
|
10636
|
+
const details = isContentsResponse2(response) ? {
|
|
10637
|
+
tool: "web_contents",
|
|
10638
|
+
provider: response.provider,
|
|
10639
|
+
itemCount: response.answers.length
|
|
10640
|
+
} : capability === "answer" ? {
|
|
10641
|
+
tool: "web_answer",
|
|
10642
|
+
provider: response.provider,
|
|
10643
|
+
itemCount: response.itemCount,
|
|
10644
|
+
queryCount: 1,
|
|
10645
|
+
failedQueryCount: 0
|
|
10646
|
+
} : {
|
|
10647
|
+
tool: "web_research",
|
|
10648
|
+
provider: response.provider
|
|
10649
|
+
};
|
|
9794
10650
|
return {
|
|
9795
|
-
content: [{ type: "text", text }],
|
|
9796
|
-
details
|
|
10651
|
+
content: [{ type: "text", text: rendered.text }],
|
|
10652
|
+
details,
|
|
10653
|
+
display: buildProviderToolDisplay2({
|
|
10654
|
+
capability,
|
|
10655
|
+
providerId: response.provider,
|
|
10656
|
+
details,
|
|
10657
|
+
text: rendered.text,
|
|
10658
|
+
outputBytes: capability === "contents" ? rendered.totalBytes : void 0,
|
|
10659
|
+
outputTruncated: capability === "contents" ? rendered.truncated : void 0,
|
|
10660
|
+
failedItemCount: isContentsResponse2(response) ? response.answers.filter((answer) => answer.error !== void 0).length : void 0
|
|
10661
|
+
})
|
|
9797
10662
|
};
|
|
9798
10663
|
}
|
|
9799
10664
|
async function dispatchWebResearch({
|
|
@@ -9857,7 +10722,13 @@ async function dispatchWebResearchInternal({
|
|
|
9857
10722
|
text: `Started web research via ${provider.label}.`
|
|
9858
10723
|
}
|
|
9859
10724
|
],
|
|
9860
|
-
details: request
|
|
10725
|
+
details: request,
|
|
10726
|
+
display: buildProviderToolDisplay2({
|
|
10727
|
+
capability: "research",
|
|
10728
|
+
providerId: provider.id,
|
|
10729
|
+
details: { tool: "web_research", provider: provider.id },
|
|
10730
|
+
text: "started"
|
|
10731
|
+
})
|
|
9861
10732
|
};
|
|
9862
10733
|
}
|
|
9863
10734
|
async function runDispatchedWebResearch({
|
|
@@ -10089,6 +10960,7 @@ async function executeBatchedContentsTool({
|
|
|
10089
10960
|
}
|
|
10090
10961
|
const batchProgress = createBatchCompletionReporter(
|
|
10091
10962
|
"Fetching contents",
|
|
10963
|
+
provider.id,
|
|
10092
10964
|
provider.label,
|
|
10093
10965
|
urls.length,
|
|
10094
10966
|
progressReport
|
|
@@ -10140,7 +11012,11 @@ async function executeBatchedContentsTool({
|
|
|
10140
11012
|
);
|
|
10141
11013
|
if (successful.length === 0 && failures.length > 0) {
|
|
10142
11014
|
throw new Error(
|
|
10143
|
-
failures.length === 1 ?
|
|
11015
|
+
failures.length === 1 ? formatProviderCapabilityFailure(
|
|
11016
|
+
provider.label,
|
|
11017
|
+
"contents",
|
|
11018
|
+
failures[0]?.error ?? ""
|
|
11019
|
+
) : `${provider.label} fetch failed for ${failures.length} pages: ${failures.map(
|
|
10144
11020
|
(failure, index) => `${index + 1}. ${failure.url} \u2014 ${failure.error}`
|
|
10145
11021
|
).join("; ")}`
|
|
10146
11022
|
);
|
|
@@ -10205,10 +11081,11 @@ function createProgressEmitter(onUpdate) {
|
|
|
10205
11081
|
if (!onUpdate) {
|
|
10206
11082
|
return void 0;
|
|
10207
11083
|
}
|
|
10208
|
-
return (message) => {
|
|
11084
|
+
return (message, display) => {
|
|
10209
11085
|
onUpdate({
|
|
10210
11086
|
content: [{ type: "text", text: message }],
|
|
10211
|
-
details: {}
|
|
11087
|
+
details: {},
|
|
11088
|
+
display
|
|
10212
11089
|
});
|
|
10213
11090
|
};
|
|
10214
11091
|
}
|
|
@@ -10217,7 +11094,7 @@ function createToolProgressReporter(capability, providerId, progress) {
|
|
|
10217
11094
|
return { report: void 0, stop: () => {
|
|
10218
11095
|
} };
|
|
10219
11096
|
}
|
|
10220
|
-
const emit = (message) => progress(message);
|
|
11097
|
+
const emit = (message, display) => progress(message, display);
|
|
10221
11098
|
const startedAt = Date.now();
|
|
10222
11099
|
let lastUpdateAt = startedAt;
|
|
10223
11100
|
let timer;
|
|
@@ -10228,14 +11105,17 @@ function createToolProgressReporter(capability, providerId, progress) {
|
|
|
10228
11105
|
}
|
|
10229
11106
|
const providerLabel = PROVIDERS_BY_ID[providerId]?.label ?? providerId;
|
|
10230
11107
|
const elapsed = formatElapsed(Date.now() - startedAt);
|
|
10231
|
-
emit(
|
|
11108
|
+
emit(
|
|
11109
|
+
`Researching via ${providerLabel} (${elapsed} elapsed)`,
|
|
11110
|
+
buildProgressDisplay2(providerId, `Researching ${elapsed}`)
|
|
11111
|
+
);
|
|
10232
11112
|
lastUpdateAt = Date.now();
|
|
10233
11113
|
}, RESEARCH_HEARTBEAT_MS);
|
|
10234
11114
|
}
|
|
10235
11115
|
return {
|
|
10236
|
-
report: (message) => {
|
|
11116
|
+
report: (message, display) => {
|
|
10237
11117
|
lastUpdateAt = Date.now();
|
|
10238
|
-
emit(message);
|
|
11118
|
+
emit(message, display);
|
|
10239
11119
|
},
|
|
10240
11120
|
stop: () => {
|
|
10241
11121
|
if (timer) {
|
|
@@ -10306,77 +11186,125 @@ function renderQuestionCallHeader(params, theme) {
|
|
|
10306
11186
|
);
|
|
10307
11187
|
}
|
|
10308
11188
|
function renderResearchCallHeader(params, theme) {
|
|
10309
|
-
return renderListCallHeader(
|
|
11189
|
+
return renderListCallHeader(
|
|
11190
|
+
"web_research",
|
|
11191
|
+
[params.input],
|
|
11192
|
+
theme,
|
|
11193
|
+
void 0,
|
|
11194
|
+
{ quoteSingleItem: true }
|
|
11195
|
+
);
|
|
10310
11196
|
}
|
|
10311
|
-
function
|
|
11197
|
+
function renderWebToolResult(result, state, theme, config, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
10312
11198
|
const text = extractTextContent(result.content);
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
return renderSimpleText(text ?? "Working\u2026", theme, "warning");
|
|
11199
|
+
if (state.isPartial) {
|
|
11200
|
+
return renderToolProgress(result.display, text, theme);
|
|
10316
11201
|
}
|
|
10317
|
-
if (isError) {
|
|
10318
|
-
return
|
|
11202
|
+
if (result.isError) {
|
|
11203
|
+
return renderFailureText(
|
|
11204
|
+
buildFailureSummary({
|
|
11205
|
+
text,
|
|
11206
|
+
details: result.details,
|
|
11207
|
+
capability: config.capability,
|
|
11208
|
+
fallback: config.failureText
|
|
11209
|
+
}),
|
|
11210
|
+
theme,
|
|
11211
|
+
symbols
|
|
11212
|
+
);
|
|
10319
11213
|
}
|
|
10320
|
-
const details = result.details;
|
|
10321
|
-
if (
|
|
10322
|
-
return
|
|
11214
|
+
const details = config.getDetails(result.details);
|
|
11215
|
+
if (state.expanded) {
|
|
11216
|
+
return config.renderExpanded(details, text);
|
|
10323
11217
|
}
|
|
10324
|
-
|
|
11218
|
+
const summary = config.preferDisplaySummary === false ? config.getCollapsedSummary(details, text) : getDisplaySummaryParts(result.display) ?? config.getCollapsedSummary(details, text);
|
|
11219
|
+
return renderCollapsedSummary(summary, theme, symbols);
|
|
10325
11220
|
}
|
|
10326
|
-
function
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
"",
|
|
10333
|
-
"
|
|
10334
|
-
|
|
10335
|
-
details
|
|
10336
|
-
""
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
].join("\n") : text;
|
|
10341
|
-
return renderMarkdownBlock(expandedText);
|
|
10342
|
-
}
|
|
10343
|
-
const summary = details ? `Started web research via ${PROVIDERS_BY_ID[details.provider]?.label ?? details.provider}` : text;
|
|
10344
|
-
let summaryText = theme.fg("success", summary);
|
|
10345
|
-
summaryText += theme.fg("muted", ` (${getExpandHint()})`);
|
|
10346
|
-
return new Text(summaryText, 0, 0);
|
|
11221
|
+
function renderSearchToolResult(result, expanded, isPartial, theme, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
11222
|
+
return renderWebToolResult(
|
|
11223
|
+
result,
|
|
11224
|
+
{ expanded, isPartial },
|
|
11225
|
+
theme,
|
|
11226
|
+
{
|
|
11227
|
+
capability: "search",
|
|
11228
|
+
failureText: "web_search failed",
|
|
11229
|
+
getDetails: (details) => details,
|
|
11230
|
+
getCollapsedSummary: (details, text) => details ? buildSearchSummaryParts2(details) : { success: getFirstLine2(text) ?? "web_search output available" },
|
|
11231
|
+
renderExpanded: (_details, text) => renderMarkdownBlock(text ?? "")
|
|
11232
|
+
},
|
|
11233
|
+
symbols
|
|
11234
|
+
);
|
|
10347
11235
|
}
|
|
10348
|
-
function
|
|
11236
|
+
function renderWebResearchDispatchResult(result, expanded, theme, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
11237
|
+
return renderWebToolResult(
|
|
11238
|
+
result,
|
|
11239
|
+
{ expanded },
|
|
11240
|
+
theme,
|
|
11241
|
+
{
|
|
11242
|
+
capability: "research",
|
|
11243
|
+
failureText: "web_research failed",
|
|
11244
|
+
getDetails: (details) => isWebResearchRequest(details) ? details : void 0,
|
|
11245
|
+
getCollapsedSummary: () => ({ success: "started" }),
|
|
11246
|
+
renderExpanded: (details, text) => renderMarkdownBlock(
|
|
11247
|
+
details ? renderWebResearchRequestMarkdown(details) : text ?? "Started web research."
|
|
11248
|
+
),
|
|
11249
|
+
preferDisplaySummary: false
|
|
11250
|
+
},
|
|
11251
|
+
symbols
|
|
11252
|
+
);
|
|
11253
|
+
}
|
|
11254
|
+
function renderWebResearchResultMessage(message, { expanded }, theme, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
10349
11255
|
const text = typeof message.content === "string" ? message.content : extractTextContent(message.content);
|
|
10350
11256
|
const details = isWebResearchResult(message.details) ? message.details : void 0;
|
|
10351
11257
|
const isSuccess = details?.status === "completed";
|
|
10352
11258
|
const accent = isSuccess ? "success" : "error";
|
|
10353
11259
|
const box = new Box(1, 1, (value) => theme.bg("customMessageBg", value));
|
|
10354
11260
|
if (!expanded) {
|
|
10355
|
-
const
|
|
10356
|
-
|
|
10357
|
-
|
|
11261
|
+
const summary = details ? buildWebResearchResultSummaryLine(details, theme, symbols) : theme.fg(accent, "Web research update");
|
|
11262
|
+
box.addChild(
|
|
11263
|
+
new Text(`${summary}${theme.fg("muted", ` (${getExpandHint()})`)}`, 0, 0)
|
|
11264
|
+
);
|
|
10358
11265
|
return box;
|
|
10359
11266
|
}
|
|
10360
11267
|
box.addChild(
|
|
10361
|
-
isSuccess ? renderMarkdownBlock(text ?? "") : renderBlockText(text ?? "", theme, "error")
|
|
11268
|
+
details ? renderMarkdownBlock(renderWebResearchResultMarkdown(details)) : isSuccess ? renderMarkdownBlock(text ?? "") : renderBlockText(text ?? "", theme, "error")
|
|
10362
11269
|
);
|
|
10363
11270
|
return box;
|
|
10364
11271
|
}
|
|
10365
|
-
function
|
|
11272
|
+
function renderWebResearchRequestMarkdown(request) {
|
|
11273
|
+
return [
|
|
11274
|
+
"### Web research",
|
|
11275
|
+
"",
|
|
11276
|
+
`**Brief:** ${request.input}`,
|
|
11277
|
+
"",
|
|
11278
|
+
"**Status:** running ",
|
|
11279
|
+
`**Elapsed:** ${formatSummaryElapsed(Date.now() - Date.parse(request.startedAt))} `,
|
|
11280
|
+
`**Artifact:** \`${request.outputPath}\``
|
|
11281
|
+
].join("\n");
|
|
11282
|
+
}
|
|
11283
|
+
function renderWebResearchResultMarkdown(result) {
|
|
11284
|
+
const status = result.status === "completed" ? "completed" : result.status;
|
|
11285
|
+
return [
|
|
11286
|
+
"### Web research",
|
|
11287
|
+
"",
|
|
11288
|
+
`**Brief:** ${result.input}`,
|
|
11289
|
+
"",
|
|
11290
|
+
`**Status:** ${status} `,
|
|
11291
|
+
`**Duration:** ${formatSummaryElapsed(result.elapsedMs)} `,
|
|
11292
|
+
`**Artifact:** \`${result.outputPath}\``,
|
|
11293
|
+
...result.error ? ["", `**Error:** ${result.error}`] : []
|
|
11294
|
+
].join("\n");
|
|
11295
|
+
}
|
|
11296
|
+
function buildWebResearchResultSummaryLine(result, theme, symbols) {
|
|
10366
11297
|
const providerLabel = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
|
|
10371
|
-
|
|
10372
|
-
|
|
10373
|
-
theme.fg("muted", `\u25F4 duration: ${formatElapsed(result.elapsedMs)}`),
|
|
10374
|
-
theme.fg("muted", `\u21B3 file: ${result.outputPath}`)
|
|
10375
|
-
);
|
|
10376
|
-
if (result.error) {
|
|
10377
|
-
lines.push(theme.fg("muted", `\u2715 error: ${result.error}`));
|
|
11298
|
+
if (result.status === "completed") {
|
|
11299
|
+
return renderSuccessSummary(
|
|
11300
|
+
`${formatSummaryElapsed(result.elapsedMs)} \xB7 ${basename(result.outputPath)}`,
|
|
11301
|
+
theme,
|
|
11302
|
+
symbols
|
|
11303
|
+
);
|
|
10378
11304
|
}
|
|
10379
|
-
|
|
11305
|
+
const statusText = result.status === "cancelled" ? `${providerLabel} research canceled after ${formatSummaryElapsed(result.elapsedMs)}` : `${providerLabel} research failed after ${formatSummaryElapsed(result.elapsedMs)}`;
|
|
11306
|
+
const errorSuffix = result.error ? `: ${normalizeProviderFailureDetail(providerLabel, result.error)}` : "";
|
|
11307
|
+
return renderFailureSummary(`${statusText}${errorSuffix}`, theme, symbols);
|
|
10380
11308
|
}
|
|
10381
11309
|
function isWebResearchRequest(details) {
|
|
10382
11310
|
return typeof details === "object" && details !== null && "tool" in details && details.tool === "web_research" && "startedAt" in details && "outputPath" in details && !("status" in details);
|
|
@@ -10385,48 +11313,58 @@ function isWebResearchResult(details) {
|
|
|
10385
11313
|
return typeof details === "object" && details !== null && "tool" in details && details.tool === "web_research" && "status" in details && "completedAt" in details;
|
|
10386
11314
|
}
|
|
10387
11315
|
function renderProviderToolResult(result, expanded, isPartial, failureText, theme, options = {}) {
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
summaryText += theme.fg("muted", ` (${getExpandHint()})`);
|
|
10402
|
-
return new Text(summaryText, 0, 0);
|
|
11316
|
+
return renderWebToolResult(
|
|
11317
|
+
result,
|
|
11318
|
+
{ expanded, isPartial },
|
|
11319
|
+
theme,
|
|
11320
|
+
{
|
|
11321
|
+
capability: toolFromFailureText(failureText),
|
|
11322
|
+
failureText,
|
|
11323
|
+
getDetails: (details) => details,
|
|
11324
|
+
getCollapsedSummary: buildCollapsedProviderToolSummary2,
|
|
11325
|
+
renderExpanded: (_details, text) => options.markdownWhenExpanded ? renderMarkdownBlock(text ?? "") : renderBlockText(text ?? "", theme, "toolOutput")
|
|
11326
|
+
},
|
|
11327
|
+
options.symbols
|
|
11328
|
+
);
|
|
10403
11329
|
}
|
|
10404
11330
|
function renderCollapsedProviderToolSummary(details, text) {
|
|
10405
|
-
|
|
10406
|
-
const providerLabel = PROVIDERS_BY_ID[details.provider]?.label ?? details.provider;
|
|
10407
|
-
const failureSuffix = details.failedQueryCount && details.failedQueryCount > 0 ? `, ${details.failedQueryCount} failed` : "";
|
|
10408
|
-
return `${details.queryCount} questions via ${providerLabel}${failureSuffix}`;
|
|
10409
|
-
}
|
|
10410
|
-
const baseSummary = getCompactProviderToolSummary(details) ?? getFirstLine(text) ?? `${details?.tool ?? "tool"} output available`;
|
|
10411
|
-
if (!details?.provider) {
|
|
10412
|
-
return baseSummary;
|
|
10413
|
-
}
|
|
10414
|
-
return appendProviderSummary(baseSummary, details.provider);
|
|
11331
|
+
return buildCollapsedProviderToolSummary(details, text);
|
|
10415
11332
|
}
|
|
10416
|
-
function
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
|
|
10420
|
-
|
|
10421
|
-
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
11333
|
+
function buildCollapsedProviderToolSummary2(details, text) {
|
|
11334
|
+
return buildCollapsedProviderToolSummaryParts(details, text);
|
|
11335
|
+
}
|
|
11336
|
+
function renderCollapsedSummary(summary, theme, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
11337
|
+
let rendered = renderSummary(summary, theme, symbols);
|
|
11338
|
+
rendered += theme.fg("muted", ` (${getExpandHint()})`);
|
|
11339
|
+
return new Text(rendered, 0, 0);
|
|
11340
|
+
}
|
|
11341
|
+
function getDisplaySummaryParts(display) {
|
|
11342
|
+
return display?.outcome;
|
|
11343
|
+
}
|
|
11344
|
+
function buildSearchToolDisplay2(details) {
|
|
11345
|
+
return buildSearchToolDisplay(details);
|
|
11346
|
+
}
|
|
11347
|
+
function buildProgressDisplay2(providerId, action) {
|
|
11348
|
+
return buildProgressDisplay(providerId, action);
|
|
11349
|
+
}
|
|
11350
|
+
function buildProviderToolDisplay2({
|
|
11351
|
+
capability,
|
|
11352
|
+
providerId,
|
|
11353
|
+
details,
|
|
11354
|
+
text,
|
|
11355
|
+
outputBytes,
|
|
11356
|
+
outputTruncated,
|
|
11357
|
+
failedItemCount
|
|
11358
|
+
}) {
|
|
11359
|
+
return buildProviderToolDisplay({
|
|
11360
|
+
capability,
|
|
11361
|
+
providerId,
|
|
11362
|
+
details,
|
|
11363
|
+
text,
|
|
11364
|
+
outputBytes,
|
|
11365
|
+
outputTruncated,
|
|
11366
|
+
failedItemCount
|
|
11367
|
+
});
|
|
10430
11368
|
}
|
|
10431
11369
|
function getProviderSettings(providerId) {
|
|
10432
11370
|
return getProviderConfigManifest(providerId).settings;
|
|
@@ -10486,12 +11424,19 @@ function resolveProviderSelectionValue(providerIds, value) {
|
|
|
10486
11424
|
);
|
|
10487
11425
|
}
|
|
10488
11426
|
function getReadyCompatibleProvidersForTool(config, cwd, toolId) {
|
|
11427
|
+
const mappedProviderId = getMappedProviderIdForTool(config, toolId);
|
|
10489
11428
|
return sortProviderIdsForSettings(
|
|
10490
|
-
getCompatibleProviders(toolId).filter(
|
|
10491
|
-
(providerId)
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
11429
|
+
getCompatibleProviders(toolId).filter((providerId) => {
|
|
11430
|
+
const setupState = getProviderSetupState(config, providerId);
|
|
11431
|
+
if (setupState === "none" && providerId !== mappedProviderId) {
|
|
11432
|
+
return false;
|
|
11433
|
+
}
|
|
11434
|
+
return isProviderCapabilityExposable(
|
|
11435
|
+
getProviderCapabilityStatus(config, cwd, providerId, toolId, {
|
|
11436
|
+
resolveSecrets: false
|
|
11437
|
+
})
|
|
11438
|
+
);
|
|
11439
|
+
})
|
|
10495
11440
|
);
|
|
10496
11441
|
}
|
|
10497
11442
|
function sortProviderIdsForSettings(providerIds) {
|
|
@@ -10571,6 +11516,9 @@ function ensureSettings(config) {
|
|
|
10571
11516
|
return config.settings;
|
|
10572
11517
|
}
|
|
10573
11518
|
function cleanupSettings(config) {
|
|
11519
|
+
if (config.settings?.search && Object.keys(config.settings.search).length === 0) {
|
|
11520
|
+
delete config.settings.search;
|
|
11521
|
+
}
|
|
10574
11522
|
if (config.settings && Object.keys(config.settings).length === 0) {
|
|
10575
11523
|
delete config.settings;
|
|
10576
11524
|
}
|
|
@@ -10718,19 +11666,21 @@ var WebProvidersSettingsView = class {
|
|
|
10718
11666
|
id: `tool:${toolId}`,
|
|
10719
11667
|
label: TOOL_INFO[toolId].label,
|
|
10720
11668
|
currentValue,
|
|
10721
|
-
description: `Press Enter to configure web_${toolId}. ${TOOL_INFO[toolId].help} Route web_${toolId} to one compatible provider or turn it off.` + (compatibleLabels.length > 0 ? `
|
|
11669
|
+
description: `Press Enter to configure web_${toolId}. ${TOOL_INFO[toolId].help} Route web_${toolId} to one compatible provider or turn it off.` + (compatibleLabels.length > 0 ? ` Compatible providers: ${compatibleLabels.join(", ")}.` : ""),
|
|
10722
11670
|
kind: "action"
|
|
10723
11671
|
};
|
|
10724
11672
|
});
|
|
10725
11673
|
}
|
|
10726
11674
|
buildSettingsSectionItems() {
|
|
10727
|
-
return
|
|
10728
|
-
id
|
|
10729
|
-
|
|
10730
|
-
|
|
10731
|
-
|
|
10732
|
-
|
|
10733
|
-
|
|
11675
|
+
return [
|
|
11676
|
+
...SETTING_IDS.map((id) => ({
|
|
11677
|
+
id: `settings:${id}`,
|
|
11678
|
+
label: SETTING_META[id].label,
|
|
11679
|
+
currentValue: getSharedSettingDisplayValue(this.config, id),
|
|
11680
|
+
description: SETTING_META[id].help,
|
|
11681
|
+
kind: "text"
|
|
11682
|
+
}))
|
|
11683
|
+
];
|
|
10734
11684
|
}
|
|
10735
11685
|
getSectionEntries(section) {
|
|
10736
11686
|
if (section === "provider") return this.buildProviderSectionItems();
|
|
@@ -11040,7 +11990,7 @@ var ToolSettingsSubmenu = class {
|
|
|
11040
11990
|
id: "provider",
|
|
11041
11991
|
label: "Provider",
|
|
11042
11992
|
currentValue: currentProviderValue,
|
|
11043
|
-
description: `Route web_${this.toolId} to one compatible
|
|
11993
|
+
description: `Route web_${this.toolId} to one compatible provider or turn it off.`,
|
|
11044
11994
|
kind: "cycle",
|
|
11045
11995
|
values: providerValues
|
|
11046
11996
|
}
|
|
@@ -11452,17 +12402,24 @@ function formatProviderSetupState(state) {
|
|
|
11452
12402
|
function getProviderReadinessSummary(config, cwd, providerId) {
|
|
11453
12403
|
const tools = getProviderTools(providerId);
|
|
11454
12404
|
const statuses = tools.map(
|
|
11455
|
-
(tool) => getProviderCapabilityStatus(config, cwd, providerId, tool
|
|
12405
|
+
(tool) => getProviderCapabilityStatus(config, cwd, providerId, tool, {
|
|
12406
|
+
resolveSecrets: false
|
|
12407
|
+
})
|
|
11456
12408
|
);
|
|
11457
12409
|
if (statuses.some((status) => status.state === "ready")) {
|
|
11458
12410
|
return "Ready";
|
|
11459
12411
|
}
|
|
12412
|
+
if (statuses.some((status) => status.state === "deferred_secret")) {
|
|
12413
|
+
return "Secrets resolved on first use";
|
|
12414
|
+
}
|
|
11460
12415
|
return formatProviderCapabilityStatus(statuses[0], providerId, tools[0]);
|
|
11461
12416
|
}
|
|
11462
12417
|
function getProviderReadinessSummaryForProviderConfig(providerId, providerConfig) {
|
|
11463
12418
|
const status = PROVIDERS_BY_ID[providerId].getCapabilityStatus(
|
|
11464
12419
|
providerConfig ?? PROVIDERS_BY_ID[providerId].config.createTemplate(),
|
|
11465
|
-
""
|
|
12420
|
+
"",
|
|
12421
|
+
void 0,
|
|
12422
|
+
{ resolveSecrets: false }
|
|
11466
12423
|
);
|
|
11467
12424
|
return formatProviderCapabilityStatus(status, providerId);
|
|
11468
12425
|
}
|
|
@@ -11511,7 +12468,7 @@ function getSearchQueriesForDisplay(queries) {
|
|
|
11511
12468
|
function getAnswerQueriesForDisplay(queries) {
|
|
11512
12469
|
return getSearchQueriesForDisplay(queries);
|
|
11513
12470
|
}
|
|
11514
|
-
function createBatchCompletionReporter(verb, providerLabel, total, report) {
|
|
12471
|
+
function createBatchCompletionReporter(verb, providerId, providerLabel, total, report) {
|
|
11515
12472
|
if (!report) {
|
|
11516
12473
|
return {
|
|
11517
12474
|
start: () => {
|
|
@@ -11529,7 +12486,8 @@ function createBatchCompletionReporter(verb, providerLabel, total, report) {
|
|
|
11529
12486
|
if (failedCount > 0) {
|
|
11530
12487
|
message += `, ${failedCount} failed`;
|
|
11531
12488
|
}
|
|
11532
|
-
|
|
12489
|
+
const action = verb === "Fetching contents" ? `Fetching ${completedCount}/${total} pages` : `${verb} ${completedCount}/${total}`;
|
|
12490
|
+
report(message, buildProgressDisplay2(providerId, action));
|
|
11533
12491
|
};
|
|
11534
12492
|
return {
|
|
11535
12493
|
start: emit,
|
|
@@ -11590,34 +12548,52 @@ ${rendered}`, 0, 0);
|
|
|
11590
12548
|
function renderSimpleText(text, theme, color) {
|
|
11591
12549
|
return new Text(theme.fg(color, text), 0, 0);
|
|
11592
12550
|
}
|
|
11593
|
-
function
|
|
12551
|
+
function renderSummary(summary, theme, symbols) {
|
|
12552
|
+
let rendered = renderSuccessSummary(summary.success, theme, symbols);
|
|
12553
|
+
if (summary.failure) {
|
|
12554
|
+
rendered += `, ${renderFailureSummary(summary.failure, theme, symbols)}`;
|
|
12555
|
+
}
|
|
12556
|
+
return rendered;
|
|
12557
|
+
}
|
|
12558
|
+
function renderSuccessSummary(text, theme, symbols) {
|
|
12559
|
+
return theme.fg("success", prefixWithSymbol(text, symbols.success));
|
|
12560
|
+
}
|
|
12561
|
+
function renderFailureSummary(text, theme, symbols) {
|
|
12562
|
+
return theme.fg("error", prefixWithSymbol(text, symbols.failure));
|
|
12563
|
+
}
|
|
12564
|
+
function renderFailureText(text, theme, symbols) {
|
|
12565
|
+
return new Text(renderFailureSummary(text, theme, symbols), 0, 0);
|
|
12566
|
+
}
|
|
12567
|
+
function prefixWithSymbol(text, symbol) {
|
|
12568
|
+
return symbol ? `${symbol} ${text}` : text;
|
|
12569
|
+
}
|
|
12570
|
+
function renderToolProgress(display, fallbackText, theme) {
|
|
12571
|
+
const progress = display?.progress;
|
|
12572
|
+
const providerLabel = display?.provider?.label;
|
|
12573
|
+
if (!progress || !providerLabel) {
|
|
12574
|
+
return renderSimpleText(fallbackText ?? "Working\u2026", theme, "warning");
|
|
12575
|
+
}
|
|
12576
|
+
return new Text(
|
|
12577
|
+
`${theme.fg("warning", progress.action)} ${theme.fg("muted", `via ${providerLabel}`)}`,
|
|
12578
|
+
0,
|
|
12579
|
+
0
|
|
12580
|
+
);
|
|
12581
|
+
}
|
|
12582
|
+
function renderCollapsedSearchSummary(details, text, theme, symbols = DEFAULT_SUMMARY_SYMBOLS) {
|
|
11594
12583
|
const queryCount = typeof details?.queryCount === "number" ? details.queryCount : inferSearchQueryCount(text);
|
|
11595
12584
|
const resultCount = typeof details?.resultCount === "number" ? details.resultCount : inferSearchResultCount(text);
|
|
11596
12585
|
const failedQueryCount = typeof details?.failedQueryCount === "number" ? details.failedQueryCount : inferSearchFailureCount(text);
|
|
11597
|
-
const
|
|
11598
|
-
let base2 = buildSearchSummaryText({
|
|
12586
|
+
const summary = buildSearchSummaryParts2({
|
|
11599
12587
|
queryCount,
|
|
11600
|
-
resultCount
|
|
12588
|
+
resultCount,
|
|
12589
|
+
failedQueryCount
|
|
11601
12590
|
});
|
|
11602
|
-
|
|
11603
|
-
|
|
11604
|
-
|
|
11605
|
-
if (failedQueryCount && failedQueryCount > 0) {
|
|
11606
|
-
base2 += `, ${failedQueryCount} failed`;
|
|
11607
|
-
}
|
|
11608
|
-
let summary = theme.fg("success", base2);
|
|
11609
|
-
summary += theme.fg("muted", ` (${getExpandHint()})`);
|
|
11610
|
-
return new Text(summary, 0, 0);
|
|
12591
|
+
let rendered = renderSummary(summary, theme, symbols);
|
|
12592
|
+
rendered += theme.fg("muted", ` (${getExpandHint()})`);
|
|
12593
|
+
return new Text(rendered, 0, 0);
|
|
11611
12594
|
}
|
|
11612
|
-
function
|
|
11613
|
-
|
|
11614
|
-
resultCount
|
|
11615
|
-
}) {
|
|
11616
|
-
const countSummary = typeof resultCount === "number" ? `${resultCount} result${resultCount === 1 ? "" : "s"}` : "Search output available";
|
|
11617
|
-
if (queryCount && queryCount > 1) {
|
|
11618
|
-
return `${queryCount} queries, ${countSummary}`;
|
|
11619
|
-
}
|
|
11620
|
-
return countSummary;
|
|
12595
|
+
function buildSearchSummaryParts2(options) {
|
|
12596
|
+
return buildSearchSummaryParts(options);
|
|
11621
12597
|
}
|
|
11622
12598
|
function inferSearchQueryCount(text) {
|
|
11623
12599
|
if (!text) {
|
|
@@ -11643,12 +12619,77 @@ function inferSearchFailureCount(text) {
|
|
|
11643
12619
|
const failureMatches = text.match(/^Search failed:/gm);
|
|
11644
12620
|
return failureMatches?.length;
|
|
11645
12621
|
}
|
|
11646
|
-
function
|
|
11647
|
-
|
|
11648
|
-
|
|
11649
|
-
|
|
12622
|
+
function buildFailureSummary({
|
|
12623
|
+
text,
|
|
12624
|
+
details,
|
|
12625
|
+
capability,
|
|
12626
|
+
fallback
|
|
12627
|
+
}) {
|
|
12628
|
+
const detail = stripTrailingSentencePunctuation(getFirstLine2(text) ?? "");
|
|
12629
|
+
const providerLabel = details?.provider !== void 0 ? PROVIDERS_BY_ID[details.provider]?.label ?? details.provider : void 0;
|
|
12630
|
+
if (!providerLabel) {
|
|
12631
|
+
return detail || fallback;
|
|
12632
|
+
}
|
|
12633
|
+
return formatProviderCapabilityFailure(providerLabel, capability, detail);
|
|
12634
|
+
}
|
|
12635
|
+
function formatProviderCapabilityFailure(providerLabel, capability, detail) {
|
|
12636
|
+
const action = getFailureAction(capability);
|
|
12637
|
+
const base2 = `${providerLabel} ${action} failed`;
|
|
12638
|
+
if (!detail || detail === base2) {
|
|
12639
|
+
return base2;
|
|
12640
|
+
}
|
|
12641
|
+
if (detail.toLowerCase().startsWith(base2.toLowerCase())) {
|
|
12642
|
+
return detail;
|
|
12643
|
+
}
|
|
12644
|
+
const normalizedDetail = normalizeProviderFailureDetail(
|
|
12645
|
+
providerLabel,
|
|
12646
|
+
detail
|
|
12647
|
+
);
|
|
12648
|
+
return `${base2}: ${normalizedDetail}`;
|
|
12649
|
+
}
|
|
12650
|
+
function normalizeProviderFailureDetail(providerLabel, detail) {
|
|
12651
|
+
const normalized = stripTrailingSentencePunctuation(detail);
|
|
12652
|
+
const providerPrefix = `${providerLabel}:`;
|
|
12653
|
+
return normalized.toLowerCase().startsWith(providerPrefix.toLowerCase()) ? normalized.slice(providerPrefix.length).trim() : normalized;
|
|
11650
12654
|
}
|
|
11651
|
-
function
|
|
12655
|
+
function getFailureAction(capability) {
|
|
12656
|
+
switch (capability) {
|
|
12657
|
+
case "contents":
|
|
12658
|
+
return "fetch";
|
|
12659
|
+
case "search":
|
|
12660
|
+
case "answer":
|
|
12661
|
+
case "research":
|
|
12662
|
+
return capability;
|
|
12663
|
+
}
|
|
12664
|
+
}
|
|
12665
|
+
function toolFromFailureText(text) {
|
|
12666
|
+
if (text.startsWith("web_contents")) {
|
|
12667
|
+
return "contents";
|
|
12668
|
+
}
|
|
12669
|
+
if (text.startsWith("web_answer")) {
|
|
12670
|
+
return "answer";
|
|
12671
|
+
}
|
|
12672
|
+
if (text.startsWith("web_research")) {
|
|
12673
|
+
return "research";
|
|
12674
|
+
}
|
|
12675
|
+
return "search";
|
|
12676
|
+
}
|
|
12677
|
+
function stripTrailingSentencePunctuation(text) {
|
|
12678
|
+
return text.trim().replace(/[.\s]+$/u, "");
|
|
12679
|
+
}
|
|
12680
|
+
function formatSummaryElapsed(ms) {
|
|
12681
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
12682
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
12683
|
+
const seconds = totalSeconds % 60;
|
|
12684
|
+
if (minutes > 0 && seconds === 0) {
|
|
12685
|
+
return `${minutes}m`;
|
|
12686
|
+
}
|
|
12687
|
+
if (minutes > 0) {
|
|
12688
|
+
return `${minutes}m ${seconds}s`;
|
|
12689
|
+
}
|
|
12690
|
+
return `${seconds}s`;
|
|
12691
|
+
}
|
|
12692
|
+
function getFirstLine2(text) {
|
|
11652
12693
|
if (!text) {
|
|
11653
12694
|
return void 0;
|
|
11654
12695
|
}
|
|
@@ -11729,18 +12770,32 @@ function escapeMarkdownText(text) {
|
|
|
11729
12770
|
return text.replaceAll("\\", "\\\\").replaceAll("*", "\\*").replaceAll("_", "\\_").replaceAll("`", "\\`").replaceAll("#", "\\#").replaceAll("[", "\\[").replaceAll("]", "\\]");
|
|
11730
12771
|
}
|
|
11731
12772
|
async function truncateAndSave(text, prefix) {
|
|
12773
|
+
return (await truncateAndSaveWithMetadata(text, prefix)).text;
|
|
12774
|
+
}
|
|
12775
|
+
async function truncateAndSaveWithMetadata(text, prefix) {
|
|
12776
|
+
const totalBytes = Buffer.byteLength(text, "utf-8");
|
|
11732
12777
|
const truncation = truncateHead(text, {
|
|
11733
12778
|
maxLines: DEFAULT_MAX_LINES,
|
|
11734
12779
|
maxBytes: DEFAULT_MAX_BYTES
|
|
11735
12780
|
});
|
|
11736
|
-
if (!truncation.truncated)
|
|
12781
|
+
if (!truncation.truncated) {
|
|
12782
|
+
return {
|
|
12783
|
+
text: truncation.content,
|
|
12784
|
+
totalBytes,
|
|
12785
|
+
truncated: false
|
|
12786
|
+
};
|
|
12787
|
+
}
|
|
11737
12788
|
const dir = join2(tmpdir(), `pi-web-providers-${prefix}-${Date.now()}`);
|
|
11738
12789
|
await mkdir2(dir, { recursive: true });
|
|
11739
12790
|
const fullPath = join2(dir, "output.txt");
|
|
11740
12791
|
await writeFile2(fullPath, text, "utf-8");
|
|
11741
|
-
return
|
|
12792
|
+
return {
|
|
12793
|
+
text: truncation.content + `
|
|
11742
12794
|
|
|
11743
|
-
[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines (${
|
|
12795
|
+
[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines (${formatSize2(truncation.outputBytes)} of ${formatSize2(truncation.totalBytes)}). Full output saved to: ${fullPath}]`,
|
|
12796
|
+
totalBytes,
|
|
12797
|
+
truncated: true
|
|
12798
|
+
};
|
|
11744
12799
|
}
|
|
11745
12800
|
function truncateInline(text, maxLength) {
|
|
11746
12801
|
if (text.length <= maxLength) return text;
|