pi-web-providers 3.3.0 → 3.5.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 +48 -17
- package/dist/index.js +2305 -794
- package/package.json +17 -17
package/dist/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
-
import {
|
|
3
|
-
import { mkdir as mkdir2, writeFile as writeFile2 } from "node:fs/promises";
|
|
2
|
+
import { mkdir as mkdir3, writeFile as writeFile3 } from "node:fs/promises";
|
|
4
3
|
import { tmpdir } from "node:os";
|
|
5
|
-
import { basename,
|
|
4
|
+
import { basename, join as join3 } from "node:path";
|
|
6
5
|
import {
|
|
6
|
+
copyToClipboard,
|
|
7
7
|
DEFAULT_MAX_BYTES,
|
|
8
8
|
DEFAULT_MAX_LINES,
|
|
9
9
|
formatSize as formatSize2,
|
|
10
|
-
getMarkdownTheme,
|
|
10
|
+
getMarkdownTheme as getMarkdownTheme2,
|
|
11
11
|
truncateHead
|
|
12
12
|
} from "@earendil-works/pi-coding-agent";
|
|
13
13
|
import {
|
|
14
14
|
Box,
|
|
15
15
|
Editor,
|
|
16
|
-
getKeybindings,
|
|
17
|
-
Key,
|
|
18
|
-
Markdown,
|
|
19
|
-
matchesKey,
|
|
16
|
+
getKeybindings as getKeybindings2,
|
|
17
|
+
Key as Key2,
|
|
18
|
+
Markdown as Markdown2,
|
|
19
|
+
matchesKey as matchesKey2,
|
|
20
20
|
Text,
|
|
21
|
-
truncateToWidth,
|
|
22
|
-
visibleWidth,
|
|
21
|
+
truncateToWidth as truncateToWidth2,
|
|
22
|
+
visibleWidth as visibleWidth2,
|
|
23
23
|
wrapTextWithAnsi
|
|
24
24
|
} from "@earendil-works/pi-tui";
|
|
25
25
|
import { Type as Type16 } from "typebox";
|
|
@@ -1276,6 +1276,12 @@ function literalUnion(values, options) {
|
|
|
1276
1276
|
options
|
|
1277
1277
|
);
|
|
1278
1278
|
}
|
|
1279
|
+
function boolOrConfig(options) {
|
|
1280
|
+
return Type2.Union(
|
|
1281
|
+
[Type2.Boolean(), Type2.Record(Type2.String(), Type2.Any())],
|
|
1282
|
+
options
|
|
1283
|
+
);
|
|
1284
|
+
}
|
|
1279
1285
|
|
|
1280
1286
|
// src/providers/claude.ts
|
|
1281
1287
|
var SEARCH_OUTPUT_SCHEMA = {
|
|
@@ -1358,6 +1364,11 @@ var claudeOptionsSchema = Type3.Object(
|
|
|
1358
1364
|
},
|
|
1359
1365
|
{ description: "Claude options." }
|
|
1360
1366
|
);
|
|
1367
|
+
var claudeSearchPromptGuidelines = [
|
|
1368
|
+
"Use Claude search when Claude Code's agentic web-browsing and synthesis are useful for finding likely sources.",
|
|
1369
|
+
"Increase effort or maxTurns only for difficult, ambiguous, or multi-step source discovery; keep defaults for simple searches.",
|
|
1370
|
+
"Prefer web_contents after Claude search when the task requires direct inspection of a small set of selected URLs."
|
|
1371
|
+
];
|
|
1361
1372
|
var claudeImplementation = {
|
|
1362
1373
|
id: "claude",
|
|
1363
1374
|
label: "Claude",
|
|
@@ -1615,6 +1626,7 @@ var claudeProvider = defineProvider({
|
|
|
1615
1626
|
capabilities: {
|
|
1616
1627
|
search: defineCapability({
|
|
1617
1628
|
options: claudeImplementation.getToolOptionsSchema?.("search"),
|
|
1629
|
+
promptGuidelines: claudeSearchPromptGuidelines,
|
|
1618
1630
|
async execute(input, ctx) {
|
|
1619
1631
|
const { query: query2, maxResults, options } = input;
|
|
1620
1632
|
return await claudeImplementation.search(
|
|
@@ -1833,6 +1845,11 @@ var codexSearchOptionsSchema = Type5.Object(
|
|
|
1833
1845
|
},
|
|
1834
1846
|
{ description: "Codex search options." }
|
|
1835
1847
|
);
|
|
1848
|
+
var codexSearchPromptGuidelines = [
|
|
1849
|
+
"Use Codex search when the local Codex SDK should perform web-backed source discovery, especially for coding or developer-oriented investigations.",
|
|
1850
|
+
"Use webSearchMode='live' for current information and 'cached' when freshness is less important; do not set 'disabled' for normal web_search calls.",
|
|
1851
|
+
"Increase modelReasoningEffort only for difficult or ambiguous searches where deeper reasoning is worth the extra latency."
|
|
1852
|
+
];
|
|
1836
1853
|
var codexImplementation = {
|
|
1837
1854
|
id: "codex",
|
|
1838
1855
|
label: "Codex",
|
|
@@ -2013,6 +2030,7 @@ var codexProvider = defineProvider({
|
|
|
2013
2030
|
capabilities: {
|
|
2014
2031
|
search: defineCapability({
|
|
2015
2032
|
options: codexImplementation.getToolOptionsSchema?.("search"),
|
|
2033
|
+
promptGuidelines: codexSearchPromptGuidelines,
|
|
2016
2034
|
async execute(input, ctx) {
|
|
2017
2035
|
const { query: query2, maxResults, options } = input;
|
|
2018
2036
|
return await codexImplementation.search(
|
|
@@ -2454,39 +2472,39 @@ function createDefaultExecutionSettings(overrides = {}) {
|
|
|
2454
2472
|
function normalizeDiagnosticDetail(detail) {
|
|
2455
2473
|
return detail.trim().replace(/[.\s]+$/u, "");
|
|
2456
2474
|
}
|
|
2457
|
-
function startsWithProviderLabel(
|
|
2458
|
-
return detail.toLowerCase().startsWith(
|
|
2475
|
+
function startsWithProviderLabel(providerLabel2, detail) {
|
|
2476
|
+
return detail.toLowerCase().startsWith(providerLabel2.toLowerCase());
|
|
2459
2477
|
}
|
|
2460
2478
|
function readsLikeProviderClause(detail) {
|
|
2461
2479
|
return /^(is|has|was|returned|did|does|could|cannot|must|should|search\b|contents\b|answer\b|research\b|output\b|response\b|result\b|query\b|no\b|missing\b|deep research\b)/iu.test(
|
|
2462
2480
|
detail
|
|
2463
2481
|
);
|
|
2464
2482
|
}
|
|
2465
|
-
function formatProviderDiagnostic(
|
|
2483
|
+
function formatProviderDiagnostic(providerLabel2, detail) {
|
|
2466
2484
|
const normalized = normalizeDiagnosticDetail(detail);
|
|
2467
2485
|
if (!normalized) {
|
|
2468
|
-
return `${
|
|
2486
|
+
return `${providerLabel2} failed.`;
|
|
2469
2487
|
}
|
|
2470
|
-
if (startsWithProviderLabel(
|
|
2488
|
+
if (startsWithProviderLabel(providerLabel2, normalized)) {
|
|
2471
2489
|
return `${normalized}.`;
|
|
2472
2490
|
}
|
|
2473
2491
|
if (readsLikeProviderClause(normalized)) {
|
|
2474
|
-
return `${
|
|
2492
|
+
return `${providerLabel2} ${normalized}.`;
|
|
2475
2493
|
}
|
|
2476
|
-
return `${
|
|
2494
|
+
return `${providerLabel2}: ${normalized}.`;
|
|
2477
2495
|
}
|
|
2478
|
-
function formatResearchTerminalDiagnostic(
|
|
2496
|
+
function formatResearchTerminalDiagnostic(providerLabel2, status, detail) {
|
|
2479
2497
|
const normalized = detail ? normalizeDiagnosticDetail(detail) : "";
|
|
2480
2498
|
if (!normalized) {
|
|
2481
|
-
return status === "cancelled" ? `${
|
|
2499
|
+
return status === "cancelled" ? `${providerLabel2} research was canceled.` : `${providerLabel2} research failed.`;
|
|
2482
2500
|
}
|
|
2483
|
-
if (startsWithProviderLabel(
|
|
2501
|
+
if (startsWithProviderLabel(providerLabel2, normalized)) {
|
|
2484
2502
|
return `${normalized}.`;
|
|
2485
2503
|
}
|
|
2486
2504
|
if (/^research\b/iu.test(normalized)) {
|
|
2487
|
-
return `${
|
|
2505
|
+
return `${providerLabel2} ${normalized}.`;
|
|
2488
2506
|
}
|
|
2489
|
-
return status === "cancelled" ? `${
|
|
2507
|
+
return status === "cancelled" ? `${providerLabel2} research was canceled: ${normalized}.` : `${providerLabel2} research failed: ${normalized}.`;
|
|
2490
2508
|
}
|
|
2491
2509
|
|
|
2492
2510
|
// src/execution-policy.ts
|
|
@@ -2532,7 +2550,7 @@ async function runWithExecutionPolicy(label, operation, settings, context) {
|
|
|
2532
2550
|
throw new Error(`${label} failed.`);
|
|
2533
2551
|
}
|
|
2534
2552
|
async function executeAsyncResearch({
|
|
2535
|
-
providerLabel,
|
|
2553
|
+
providerLabel: providerLabel2,
|
|
2536
2554
|
providerId,
|
|
2537
2555
|
context,
|
|
2538
2556
|
pollIntervalMs = DEFAULT_RESEARCH_POLL_INTERVAL_MS,
|
|
@@ -2541,7 +2559,7 @@ async function executeAsyncResearch({
|
|
|
2541
2559
|
start,
|
|
2542
2560
|
poll
|
|
2543
2561
|
}) {
|
|
2544
|
-
const timeoutMessage = `${
|
|
2562
|
+
const timeoutMessage = `${providerLabel2} research exceeded ${formatDuration(timeoutMs)}.`;
|
|
2545
2563
|
const deadline = createDeadlineSignal(
|
|
2546
2564
|
context.signal,
|
|
2547
2565
|
timeoutMs,
|
|
@@ -2555,7 +2573,7 @@ async function executeAsyncResearch({
|
|
|
2555
2573
|
let lastProgressStatus;
|
|
2556
2574
|
const startedAt = Date.now();
|
|
2557
2575
|
try {
|
|
2558
|
-
researchContext.onProgress?.(`Starting research via ${
|
|
2576
|
+
researchContext.onProgress?.(`Starting research via ${providerLabel2}`);
|
|
2559
2577
|
const job = await withAbortAndOptionalTimeout(
|
|
2560
2578
|
start(researchContext),
|
|
2561
2579
|
void 0,
|
|
@@ -2564,14 +2582,14 @@ async function executeAsyncResearch({
|
|
|
2564
2582
|
);
|
|
2565
2583
|
const jobId = job.id;
|
|
2566
2584
|
if (!jobId) {
|
|
2567
|
-
throw new Error(`${
|
|
2585
|
+
throw new Error(`${providerLabel2} research did not return a job id.`);
|
|
2568
2586
|
}
|
|
2569
|
-
researchContext.onProgress?.(`${
|
|
2587
|
+
researchContext.onProgress?.(`${providerLabel2} research started: ${jobId}`);
|
|
2570
2588
|
let consecutivePollErrors = 0;
|
|
2571
2589
|
while (true) {
|
|
2572
2590
|
throwIfAborted(
|
|
2573
2591
|
researchContext.signal,
|
|
2574
|
-
`${
|
|
2592
|
+
`${providerLabel2} research aborted.`
|
|
2575
2593
|
);
|
|
2576
2594
|
try {
|
|
2577
2595
|
const result = await withAbortAndOptionalTimeout(
|
|
@@ -2584,7 +2602,7 @@ async function executeAsyncResearch({
|
|
|
2584
2602
|
const progressStatus = result.statusText ?? result.status;
|
|
2585
2603
|
if (result.status !== lastStatus || progressStatus !== lastProgressStatus) {
|
|
2586
2604
|
researchContext.onProgress?.(
|
|
2587
|
-
`Research via ${
|
|
2605
|
+
`Research via ${providerLabel2}: ${progressStatus} (${formatElapsed(Date.now() - startedAt)} elapsed)`
|
|
2588
2606
|
);
|
|
2589
2607
|
lastStatus = result.status;
|
|
2590
2608
|
lastProgressStatus = progressStatus;
|
|
@@ -2592,13 +2610,13 @@ async function executeAsyncResearch({
|
|
|
2592
2610
|
if (result.status === "completed") {
|
|
2593
2611
|
return result.output ?? {
|
|
2594
2612
|
provider: providerId,
|
|
2595
|
-
text: `${
|
|
2613
|
+
text: `${providerLabel2} research completed without textual output.`
|
|
2596
2614
|
};
|
|
2597
2615
|
}
|
|
2598
2616
|
if (result.status === "failed" || result.status === "cancelled") {
|
|
2599
2617
|
throw new Error(
|
|
2600
2618
|
formatResearchTerminalDiagnostic(
|
|
2601
|
-
|
|
2619
|
+
providerLabel2,
|
|
2602
2620
|
result.status,
|
|
2603
2621
|
result.error
|
|
2604
2622
|
)
|
|
@@ -2614,11 +2632,11 @@ async function executeAsyncResearch({
|
|
|
2614
2632
|
consecutivePollErrors += 1;
|
|
2615
2633
|
if (consecutivePollErrors >= maxConsecutivePollErrors) {
|
|
2616
2634
|
throw new Error(
|
|
2617
|
-
`${
|
|
2635
|
+
`${providerLabel2} research polling failed too many times in a row: ${formatErrorMessage(error)}`
|
|
2618
2636
|
);
|
|
2619
2637
|
}
|
|
2620
2638
|
researchContext.onProgress?.(
|
|
2621
|
-
`${
|
|
2639
|
+
`${providerLabel2} research poll is still retrying after transient errors (${consecutivePollErrors}/${maxConsecutivePollErrors} consecutive poll failures). Background job id: ${jobId}`
|
|
2622
2640
|
);
|
|
2623
2641
|
}
|
|
2624
2642
|
await sleep(pollIntervalMs, researchContext.signal);
|
|
@@ -2626,11 +2644,11 @@ async function executeAsyncResearch({
|
|
|
2626
2644
|
} catch (error) {
|
|
2627
2645
|
if (isAbortErrorFromSignal(researchContext.signal, error)) {
|
|
2628
2646
|
throw new Error(
|
|
2629
|
-
formatProviderDiagnostic(
|
|
2647
|
+
formatProviderDiagnostic(providerLabel2, formatErrorMessage(error))
|
|
2630
2648
|
);
|
|
2631
2649
|
}
|
|
2632
2650
|
throw new Error(
|
|
2633
|
-
formatProviderDiagnostic(
|
|
2651
|
+
formatProviderDiagnostic(providerLabel2, formatErrorMessage(error))
|
|
2634
2652
|
);
|
|
2635
2653
|
} finally {
|
|
2636
2654
|
deadline.cleanup();
|
|
@@ -2806,9 +2824,9 @@ var exaSearchOptionsSchema = Type6.Object(
|
|
|
2806
2824
|
"hybrid",
|
|
2807
2825
|
"fast",
|
|
2808
2826
|
"instant",
|
|
2827
|
+
"deep-lite",
|
|
2809
2828
|
"deep",
|
|
2810
|
-
"deep-reasoning"
|
|
2811
|
-
"deep-max"
|
|
2829
|
+
"deep-reasoning"
|
|
2812
2830
|
],
|
|
2813
2831
|
{ description: "Exa search mode." }
|
|
2814
2832
|
)
|
|
@@ -2826,6 +2844,14 @@ var exaSearchOptionsSchema = Type6.Object(
|
|
|
2826
2844
|
excludeDomains: Type6.Optional(
|
|
2827
2845
|
Type6.Array(Type6.String(), { description: "Exclude these domains." })
|
|
2828
2846
|
),
|
|
2847
|
+
startCrawlDate: Type6.Optional(
|
|
2848
|
+
Type6.String({
|
|
2849
|
+
description: "ISO date string for earliest crawl date."
|
|
2850
|
+
})
|
|
2851
|
+
),
|
|
2852
|
+
endCrawlDate: Type6.Optional(
|
|
2853
|
+
Type6.String({ description: "ISO date string for latest crawl date." })
|
|
2854
|
+
),
|
|
2829
2855
|
startPublishedDate: Type6.Optional(
|
|
2830
2856
|
Type6.String({
|
|
2831
2857
|
description: "ISO date string for earliest publish date."
|
|
@@ -2834,48 +2860,173 @@ var exaSearchOptionsSchema = Type6.Object(
|
|
|
2834
2860
|
endPublishedDate: Type6.Optional(
|
|
2835
2861
|
Type6.String({ description: "ISO date string for latest publish date." })
|
|
2836
2862
|
),
|
|
2863
|
+
includeText: Type6.Optional(
|
|
2864
|
+
Type6.Array(Type6.String(), {
|
|
2865
|
+
description: "Require result page text to contain these terms. Exa currently supports one short phrase."
|
|
2866
|
+
})
|
|
2867
|
+
),
|
|
2868
|
+
excludeText: Type6.Optional(
|
|
2869
|
+
Type6.Array(Type6.String(), {
|
|
2870
|
+
description: "Require result page text not to contain these terms. Exa currently supports one short phrase."
|
|
2871
|
+
})
|
|
2872
|
+
),
|
|
2873
|
+
systemPrompt: Type6.Optional(
|
|
2874
|
+
Type6.String({
|
|
2875
|
+
description: "Additional Exa instructions for deep search source selection and synthesis."
|
|
2876
|
+
})
|
|
2877
|
+
),
|
|
2878
|
+
additionalQueries: Type6.Optional(
|
|
2879
|
+
Type6.Array(Type6.String(), {
|
|
2880
|
+
maxItems: 5,
|
|
2881
|
+
description: "Alternative query formulations for Exa deep search variants."
|
|
2882
|
+
})
|
|
2883
|
+
),
|
|
2837
2884
|
userLocation: Type6.Optional(
|
|
2838
|
-
Type6.
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
Type6.String({ description: "Country hint for the user location." })
|
|
2842
|
-
),
|
|
2843
|
-
region: Type6.Optional(
|
|
2844
|
-
Type6.String({ description: "Region hint for the user location." })
|
|
2845
|
-
),
|
|
2846
|
-
city: Type6.Optional(
|
|
2847
|
-
Type6.String({ description: "City hint for the user location." })
|
|
2848
|
-
),
|
|
2849
|
-
timezone: Type6.Optional(
|
|
2850
|
-
Type6.String({
|
|
2851
|
-
description: "Timezone hint for the user location."
|
|
2852
|
-
})
|
|
2853
|
-
)
|
|
2854
|
-
},
|
|
2855
|
-
{
|
|
2856
|
-
description: "User location hint passed through to the Exa SDK."
|
|
2857
|
-
}
|
|
2858
|
-
)
|
|
2885
|
+
Type6.String({
|
|
2886
|
+
description: "Two-letter ISO country code for the user location, such as 'US'."
|
|
2887
|
+
})
|
|
2859
2888
|
),
|
|
2860
2889
|
contents: Type6.Optional(
|
|
2861
2890
|
Type6.Object(
|
|
2862
2891
|
{
|
|
2863
2892
|
text: Type6.Optional(
|
|
2864
|
-
Type6.
|
|
2893
|
+
Type6.Union(
|
|
2894
|
+
[
|
|
2895
|
+
Type6.Boolean(),
|
|
2896
|
+
Type6.Object(
|
|
2897
|
+
{
|
|
2898
|
+
maxCharacters: Type6.Optional(
|
|
2899
|
+
Type6.Integer({
|
|
2900
|
+
minimum: 1,
|
|
2901
|
+
description: "Maximum text characters per result."
|
|
2902
|
+
})
|
|
2903
|
+
),
|
|
2904
|
+
includeHtmlTags: Type6.Optional(
|
|
2905
|
+
Type6.Boolean({
|
|
2906
|
+
description: "Include HTML tags in returned text."
|
|
2907
|
+
})
|
|
2908
|
+
),
|
|
2909
|
+
verbosity: Type6.Optional(
|
|
2910
|
+
literalUnion(["compact", "standard", "full"], {
|
|
2911
|
+
description: "Verbosity level for returned text."
|
|
2912
|
+
})
|
|
2913
|
+
)
|
|
2914
|
+
},
|
|
2915
|
+
{ additionalProperties: false }
|
|
2916
|
+
)
|
|
2917
|
+
],
|
|
2918
|
+
{ description: "Include text content." }
|
|
2919
|
+
)
|
|
2865
2920
|
),
|
|
2866
2921
|
highlights: Type6.Optional(
|
|
2867
|
-
Type6.
|
|
2922
|
+
Type6.Union(
|
|
2923
|
+
[
|
|
2924
|
+
Type6.Boolean(),
|
|
2925
|
+
Type6.Object(
|
|
2926
|
+
{
|
|
2927
|
+
query: Type6.Optional(
|
|
2928
|
+
Type6.String({
|
|
2929
|
+
description: "Query to use for highlights."
|
|
2930
|
+
})
|
|
2931
|
+
),
|
|
2932
|
+
maxCharacters: Type6.Optional(
|
|
2933
|
+
Type6.Integer({
|
|
2934
|
+
minimum: 1,
|
|
2935
|
+
description: "Maximum highlight characters."
|
|
2936
|
+
})
|
|
2937
|
+
)
|
|
2938
|
+
},
|
|
2939
|
+
{ additionalProperties: false }
|
|
2940
|
+
)
|
|
2941
|
+
],
|
|
2942
|
+
{ description: "Include highlighted excerpts." }
|
|
2943
|
+
)
|
|
2868
2944
|
),
|
|
2869
2945
|
summary: Type6.Optional(
|
|
2870
|
-
Type6.
|
|
2946
|
+
Type6.Union(
|
|
2947
|
+
[
|
|
2948
|
+
Type6.Boolean(),
|
|
2949
|
+
Type6.Object(
|
|
2950
|
+
{
|
|
2951
|
+
query: Type6.Optional(
|
|
2952
|
+
Type6.String({
|
|
2953
|
+
description: "Query to guide summary generation."
|
|
2954
|
+
})
|
|
2955
|
+
)
|
|
2956
|
+
},
|
|
2957
|
+
{ additionalProperties: false }
|
|
2958
|
+
)
|
|
2959
|
+
],
|
|
2960
|
+
{ description: "Include AI-generated summary." }
|
|
2961
|
+
)
|
|
2962
|
+
),
|
|
2963
|
+
livecrawl: Type6.Optional(
|
|
2964
|
+
literalUnion(["never", "fallback", "always", "auto", "preferred"], {
|
|
2965
|
+
description: "Livecrawl mode for fetching fresh content."
|
|
2966
|
+
})
|
|
2967
|
+
),
|
|
2968
|
+
livecrawlTimeout: Type6.Optional(
|
|
2969
|
+
Type6.Integer({
|
|
2970
|
+
minimum: 0,
|
|
2971
|
+
description: "Livecrawl timeout in milliseconds."
|
|
2972
|
+
})
|
|
2973
|
+
),
|
|
2974
|
+
maxAgeHours: Type6.Optional(
|
|
2975
|
+
Type6.Number({
|
|
2976
|
+
description: "Maximum age of cached content in hours. Use 0 to always fetch fresh content."
|
|
2977
|
+
})
|
|
2978
|
+
),
|
|
2979
|
+
filterEmptyResults: Type6.Optional(
|
|
2980
|
+
Type6.Boolean({ description: "Filter results with no contents." })
|
|
2981
|
+
),
|
|
2982
|
+
subpages: Type6.Optional(
|
|
2983
|
+
Type6.Integer({
|
|
2984
|
+
minimum: 0,
|
|
2985
|
+
description: "Number of subpages to return for each result."
|
|
2986
|
+
})
|
|
2987
|
+
),
|
|
2988
|
+
subpageTarget: Type6.Optional(
|
|
2989
|
+
Type6.Union([Type6.String(), Type6.Array(Type6.String())], {
|
|
2990
|
+
description: "Text used to match/rank returned subpages."
|
|
2991
|
+
})
|
|
2992
|
+
),
|
|
2993
|
+
extras: Type6.Optional(
|
|
2994
|
+
Type6.Object(
|
|
2995
|
+
{
|
|
2996
|
+
links: Type6.Optional(
|
|
2997
|
+
Type6.Integer({
|
|
2998
|
+
minimum: 0,
|
|
2999
|
+
description: "Number of page links to include."
|
|
3000
|
+
})
|
|
3001
|
+
),
|
|
3002
|
+
imageLinks: Type6.Optional(
|
|
3003
|
+
Type6.Integer({
|
|
3004
|
+
minimum: 0,
|
|
3005
|
+
description: "Number of image links to include."
|
|
3006
|
+
})
|
|
3007
|
+
)
|
|
3008
|
+
},
|
|
3009
|
+
{ additionalProperties: false }
|
|
3010
|
+
)
|
|
2871
3011
|
)
|
|
2872
3012
|
},
|
|
2873
|
-
{
|
|
3013
|
+
{
|
|
3014
|
+
additionalProperties: false,
|
|
3015
|
+
description: "What content to include in results."
|
|
3016
|
+
}
|
|
2874
3017
|
)
|
|
2875
3018
|
)
|
|
2876
3019
|
},
|
|
2877
3020
|
{ description: "Exa search options." }
|
|
2878
3021
|
);
|
|
3022
|
+
var exaSearchPromptGuidelines = [
|
|
3023
|
+
"Use Exa's neural/auto search modes for semantic source discovery where exact keywords are uncertain; use keyword mode when exact terms, names, or identifiers matter.",
|
|
3024
|
+
"Use Exa category filters such as 'research paper' or 'company' when the user asks for a specific source type.",
|
|
3025
|
+
"Set includeDomains or excludeDomains when the task names preferred sources, requires primary sources, or needs noisy domains filtered out.",
|
|
3026
|
+
"Use startCrawlDate/endCrawlDate or contents.maxAgeHours when freshness of Exa's crawled content matters.",
|
|
3027
|
+
"Use includeText/excludeText for short required or forbidden phrases in page text.",
|
|
3028
|
+
"Request contents.text, contents.highlights, or contents.summary only when snippets are insufficient and richer source context is needed directly in search results."
|
|
3029
|
+
];
|
|
2879
3030
|
var exaImplementation = {
|
|
2880
3031
|
id: "exa",
|
|
2881
3032
|
label: "Exa",
|
|
@@ -3046,6 +3197,7 @@ var exaProvider = defineProvider({
|
|
|
3046
3197
|
capabilities: {
|
|
3047
3198
|
search: defineCapability({
|
|
3048
3199
|
options: exaImplementation.getToolOptionsSchema?.("search"),
|
|
3200
|
+
promptGuidelines: exaSearchPromptGuidelines,
|
|
3049
3201
|
async execute(input, ctx) {
|
|
3050
3202
|
const { query: query2, maxResults, options } = input;
|
|
3051
3203
|
return await exaImplementation.search(
|
|
@@ -3121,6 +3273,26 @@ var firecrawlSearchOptionsSchema = Type7.Object(
|
|
|
3121
3273
|
description: "Search categories to include."
|
|
3122
3274
|
})
|
|
3123
3275
|
),
|
|
3276
|
+
includeDomains: Type7.Optional(
|
|
3277
|
+
Type7.Array(Type7.String(), {
|
|
3278
|
+
description: "Restrict results to these domains."
|
|
3279
|
+
})
|
|
3280
|
+
),
|
|
3281
|
+
excludeDomains: Type7.Optional(
|
|
3282
|
+
Type7.Array(Type7.String(), {
|
|
3283
|
+
description: "Exclude these domains."
|
|
3284
|
+
})
|
|
3285
|
+
),
|
|
3286
|
+
tbs: Type7.Optional(
|
|
3287
|
+
Type7.String({
|
|
3288
|
+
description: "Google-style time-based search filter."
|
|
3289
|
+
})
|
|
3290
|
+
),
|
|
3291
|
+
ignoreInvalidURLs: Type7.Optional(
|
|
3292
|
+
Type7.Boolean({
|
|
3293
|
+
description: "Ignore invalid result URLs returned by search."
|
|
3294
|
+
})
|
|
3295
|
+
),
|
|
3124
3296
|
location: Type7.Optional(
|
|
3125
3297
|
Type7.Object(
|
|
3126
3298
|
{
|
|
@@ -3141,9 +3313,22 @@ var firecrawlSearchOptionsSchema = Type7.Object(
|
|
|
3141
3313
|
Type7.Object(
|
|
3142
3314
|
{
|
|
3143
3315
|
formats: Type7.Optional(
|
|
3144
|
-
Type7.Array(
|
|
3145
|
-
|
|
3146
|
-
|
|
3316
|
+
Type7.Array(
|
|
3317
|
+
literalUnion([
|
|
3318
|
+
"markdown",
|
|
3319
|
+
"html",
|
|
3320
|
+
"rawHtml",
|
|
3321
|
+
"links",
|
|
3322
|
+
"images",
|
|
3323
|
+
"screenshot",
|
|
3324
|
+
"summary",
|
|
3325
|
+
"json",
|
|
3326
|
+
"attributes"
|
|
3327
|
+
]),
|
|
3328
|
+
{
|
|
3329
|
+
description: "Output formats."
|
|
3330
|
+
}
|
|
3331
|
+
)
|
|
3147
3332
|
),
|
|
3148
3333
|
onlyMainContent: Type7.Optional(
|
|
3149
3334
|
Type7.Boolean({ description: "Extract only the main content." })
|
|
@@ -3157,51 +3342,133 @@ var firecrawlSearchOptionsSchema = Type7.Object(
|
|
|
3157
3342
|
},
|
|
3158
3343
|
{ description: "Firecrawl search options." }
|
|
3159
3344
|
);
|
|
3345
|
+
var firecrawlSearchPromptGuidelines = [
|
|
3346
|
+
"Use Firecrawl search when the task benefits from searchable results that can also include scraped page content through scrapeOptions.",
|
|
3347
|
+
"Set scrapeOptions.formats=['markdown'] and onlyMainContent=true when source snippets are not enough and the user needs extracted page context in the search results.",
|
|
3348
|
+
"Use includeDomains/excludeDomains when search should stay within or avoid specific sites.",
|
|
3349
|
+
"Use lang, country, or location when the user asks for language-specific, country-specific, or local results.",
|
|
3350
|
+
"Prefer web_contents with Firecrawl scrape options after search when only a small set of known URLs needs full extraction."
|
|
3351
|
+
];
|
|
3352
|
+
var firecrawlScrapeTuningProperties = {
|
|
3353
|
+
onlyMainContent: Type7.Optional(
|
|
3354
|
+
Type7.Boolean({ description: "Extract only the main content." })
|
|
3355
|
+
),
|
|
3356
|
+
includeTags: Type7.Optional(
|
|
3357
|
+
Type7.Array(Type7.String(), { description: "CSS selectors to include." })
|
|
3358
|
+
),
|
|
3359
|
+
excludeTags: Type7.Optional(
|
|
3360
|
+
Type7.Array(Type7.String(), { description: "CSS selectors to exclude." })
|
|
3361
|
+
),
|
|
3362
|
+
waitFor: Type7.Optional(
|
|
3363
|
+
Type7.Integer({
|
|
3364
|
+
minimum: 0,
|
|
3365
|
+
description: "Milliseconds to wait before scraping."
|
|
3366
|
+
})
|
|
3367
|
+
),
|
|
3368
|
+
headers: Type7.Optional(
|
|
3369
|
+
Type7.Record(Type7.String(), Type7.String(), {
|
|
3370
|
+
description: "Headers to send when scraping."
|
|
3371
|
+
})
|
|
3372
|
+
),
|
|
3373
|
+
location: Type7.Optional(
|
|
3374
|
+
Type7.Object(
|
|
3375
|
+
{
|
|
3376
|
+
country: Type7.Optional(Type7.String({ description: "Country hint." })),
|
|
3377
|
+
region: Type7.Optional(Type7.String({ description: "Region hint." })),
|
|
3378
|
+
city: Type7.Optional(Type7.String({ description: "City hint." }))
|
|
3379
|
+
},
|
|
3380
|
+
{ description: "Location hint for scraping." }
|
|
3381
|
+
)
|
|
3382
|
+
),
|
|
3383
|
+
mobile: Type7.Optional(
|
|
3384
|
+
Type7.Boolean({ description: "Use a mobile browser profile." })
|
|
3385
|
+
),
|
|
3386
|
+
proxy: Type7.Optional(
|
|
3387
|
+
Type7.String({
|
|
3388
|
+
description: "Proxy mode passed through to the Firecrawl SDK."
|
|
3389
|
+
})
|
|
3390
|
+
),
|
|
3391
|
+
fastMode: Type7.Optional(
|
|
3392
|
+
Type7.Boolean({ description: "Use Firecrawl fast mode." })
|
|
3393
|
+
),
|
|
3394
|
+
blockAds: Type7.Optional(
|
|
3395
|
+
Type7.Boolean({ description: "Block ads while scraping." })
|
|
3396
|
+
),
|
|
3397
|
+
removeBase64Images: Type7.Optional(
|
|
3398
|
+
Type7.Boolean({
|
|
3399
|
+
description: "Remove base64 image data from scraped output."
|
|
3400
|
+
})
|
|
3401
|
+
),
|
|
3402
|
+
redactPII: Type7.Optional(
|
|
3403
|
+
Type7.Union(
|
|
3404
|
+
[
|
|
3405
|
+
Type7.Boolean(),
|
|
3406
|
+
Type7.Object(
|
|
3407
|
+
{
|
|
3408
|
+
entities: Type7.Optional(
|
|
3409
|
+
Type7.Array(
|
|
3410
|
+
literalUnion([
|
|
3411
|
+
"PERSON",
|
|
3412
|
+
"EMAIL",
|
|
3413
|
+
"PHONE",
|
|
3414
|
+
"LOCATION",
|
|
3415
|
+
"FINANCIAL",
|
|
3416
|
+
"SECRET"
|
|
3417
|
+
])
|
|
3418
|
+
)
|
|
3419
|
+
)
|
|
3420
|
+
},
|
|
3421
|
+
{ additionalProperties: false }
|
|
3422
|
+
)
|
|
3423
|
+
],
|
|
3424
|
+
{ description: "Redact personal or sensitive data from output." }
|
|
3425
|
+
)
|
|
3426
|
+
),
|
|
3427
|
+
maxAge: Type7.Optional(
|
|
3428
|
+
Type7.Number({
|
|
3429
|
+
description: "Maximum age of cached scrape data in milliseconds."
|
|
3430
|
+
})
|
|
3431
|
+
),
|
|
3432
|
+
minAge: Type7.Optional(
|
|
3433
|
+
Type7.Number({
|
|
3434
|
+
description: "Minimum age of cached scrape data in milliseconds."
|
|
3435
|
+
})
|
|
3436
|
+
),
|
|
3437
|
+
storeInCache: Type7.Optional(
|
|
3438
|
+
Type7.Boolean({ description: "Store scrape result in Firecrawl cache." })
|
|
3439
|
+
),
|
|
3440
|
+
skipTlsVerification: Type7.Optional(
|
|
3441
|
+
Type7.Boolean({ description: "Skip TLS certificate verification." })
|
|
3442
|
+
)
|
|
3443
|
+
};
|
|
3160
3444
|
var firecrawlScrapeOptionsSchema = Type7.Object(
|
|
3161
3445
|
{
|
|
3162
3446
|
formats: Type7.Optional(
|
|
3163
|
-
Type7.Array(
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3447
|
+
Type7.Array(
|
|
3448
|
+
literalUnion([
|
|
3449
|
+
"markdown",
|
|
3450
|
+
"html",
|
|
3451
|
+
"rawHtml",
|
|
3452
|
+
"links",
|
|
3453
|
+
"images",
|
|
3454
|
+
"screenshot",
|
|
3455
|
+
"summary",
|
|
3456
|
+
"json",
|
|
3457
|
+
"attributes",
|
|
3458
|
+
"changeTracking"
|
|
3459
|
+
]),
|
|
3460
|
+
{
|
|
3461
|
+
description: "Output formats for scraping."
|
|
3462
|
+
}
|
|
3463
|
+
)
|
|
3175
3464
|
),
|
|
3176
|
-
|
|
3465
|
+
timeout: Type7.Optional(
|
|
3177
3466
|
Type7.Integer({
|
|
3178
3467
|
minimum: 0,
|
|
3179
|
-
description: "
|
|
3180
|
-
})
|
|
3181
|
-
),
|
|
3182
|
-
headers: Type7.Optional(
|
|
3183
|
-
Type7.Record(Type7.String(), Type7.String(), {
|
|
3184
|
-
description: "Headers to send when scraping."
|
|
3468
|
+
description: "Request timeout in milliseconds."
|
|
3185
3469
|
})
|
|
3186
3470
|
),
|
|
3187
|
-
|
|
3188
|
-
Type7.Object(
|
|
3189
|
-
{
|
|
3190
|
-
country: Type7.Optional(Type7.String({ description: "Country hint." })),
|
|
3191
|
-
region: Type7.Optional(Type7.String({ description: "Region hint." })),
|
|
3192
|
-
city: Type7.Optional(Type7.String({ description: "City hint." }))
|
|
3193
|
-
},
|
|
3194
|
-
{ description: "Location hint for scraping." }
|
|
3195
|
-
)
|
|
3196
|
-
),
|
|
3197
|
-
mobile: Type7.Optional(
|
|
3198
|
-
Type7.Boolean({ description: "Use a mobile browser profile." })
|
|
3199
|
-
),
|
|
3200
|
-
proxy: Type7.Optional(
|
|
3201
|
-
Type7.String({
|
|
3202
|
-
description: "Proxy mode passed through to the Firecrawl SDK."
|
|
3203
|
-
})
|
|
3204
|
-
)
|
|
3471
|
+
...firecrawlScrapeTuningProperties
|
|
3205
3472
|
},
|
|
3206
3473
|
{ description: "Firecrawl scrape options." }
|
|
3207
3474
|
);
|
|
@@ -3211,44 +3478,7 @@ var firecrawlAnswerOptionsSchema = Type7.Object(
|
|
|
3211
3478
|
minLength: 1,
|
|
3212
3479
|
description: "URL of the page to ask about."
|
|
3213
3480
|
}),
|
|
3214
|
-
|
|
3215
|
-
Type7.Boolean({ description: "Extract only the main content." })
|
|
3216
|
-
),
|
|
3217
|
-
includeTags: Type7.Optional(
|
|
3218
|
-
Type7.Array(Type7.String(), { description: "CSS selectors to include." })
|
|
3219
|
-
),
|
|
3220
|
-
excludeTags: Type7.Optional(
|
|
3221
|
-
Type7.Array(Type7.String(), { description: "CSS selectors to exclude." })
|
|
3222
|
-
),
|
|
3223
|
-
waitFor: Type7.Optional(
|
|
3224
|
-
Type7.Integer({
|
|
3225
|
-
minimum: 0,
|
|
3226
|
-
description: "Milliseconds to wait before scraping."
|
|
3227
|
-
})
|
|
3228
|
-
),
|
|
3229
|
-
headers: Type7.Optional(
|
|
3230
|
-
Type7.Record(Type7.String(), Type7.String(), {
|
|
3231
|
-
description: "Headers to send when scraping."
|
|
3232
|
-
})
|
|
3233
|
-
),
|
|
3234
|
-
location: Type7.Optional(
|
|
3235
|
-
Type7.Object(
|
|
3236
|
-
{
|
|
3237
|
-
country: Type7.Optional(Type7.String({ description: "Country hint." })),
|
|
3238
|
-
region: Type7.Optional(Type7.String({ description: "Region hint." })),
|
|
3239
|
-
city: Type7.Optional(Type7.String({ description: "City hint." }))
|
|
3240
|
-
},
|
|
3241
|
-
{ description: "Location hint for scraping." }
|
|
3242
|
-
)
|
|
3243
|
-
),
|
|
3244
|
-
mobile: Type7.Optional(
|
|
3245
|
-
Type7.Boolean({ description: "Use a mobile browser profile." })
|
|
3246
|
-
),
|
|
3247
|
-
proxy: Type7.Optional(
|
|
3248
|
-
Type7.String({
|
|
3249
|
-
description: "Proxy mode passed through to Firecrawl."
|
|
3250
|
-
})
|
|
3251
|
-
)
|
|
3481
|
+
...firecrawlScrapeTuningProperties
|
|
3252
3482
|
},
|
|
3253
3483
|
{
|
|
3254
3484
|
description: "Firecrawl page-question options. The URL is required; the question comes from the web_answer query."
|
|
@@ -3531,6 +3761,7 @@ var firecrawlProvider = defineProvider({
|
|
|
3531
3761
|
capabilities: {
|
|
3532
3762
|
search: defineCapability({
|
|
3533
3763
|
options: firecrawlImplementation.getToolOptionsSchema?.("search"),
|
|
3764
|
+
promptGuidelines: firecrawlSearchPromptGuidelines,
|
|
3534
3765
|
async execute(input, ctx) {
|
|
3535
3766
|
const { query: query2, maxResults, options } = input;
|
|
3536
3767
|
return await firecrawlImplementation.search(
|
|
@@ -3653,6 +3884,12 @@ var geminiSearchOptionsSchema = Type8.Object(
|
|
|
3653
3884
|
},
|
|
3654
3885
|
{ description: "Gemini search options." }
|
|
3655
3886
|
);
|
|
3887
|
+
var geminiSearchPromptGuidelines = [
|
|
3888
|
+
"Use Gemini search when a grounded model should perform web-backed source discovery and return likely sources.",
|
|
3889
|
+
"Change model only when the user requests a specific Gemini model or when project configuration requires it.",
|
|
3890
|
+
"Tune generation_config only for explicit output-control needs; avoid disabling tool use for search tasks.",
|
|
3891
|
+
"Prefer web_contents after Gemini search when selected sources need direct extraction or closer reading."
|
|
3892
|
+
];
|
|
3656
3893
|
var geminiAnswerOptionsSchema = Type8.Object(
|
|
3657
3894
|
{
|
|
3658
3895
|
model: Type8.Optional(
|
|
@@ -3713,7 +3950,7 @@ var geminiImplementation = {
|
|
|
3713
3950
|
context.signal
|
|
3714
3951
|
);
|
|
3715
3952
|
const results = await Promise.all(
|
|
3716
|
-
extractGoogleSearchResults(interaction
|
|
3953
|
+
extractGoogleSearchResults(readInteractionSteps(interaction)).slice(0, maxResults).map(async (result) => {
|
|
3717
3954
|
const resolvedUrl = await resolveGoogleSearchUrl(
|
|
3718
3955
|
result.url,
|
|
3719
3956
|
context.signal
|
|
@@ -3800,7 +4037,7 @@ var geminiImplementation = {
|
|
|
3800
4037
|
);
|
|
3801
4038
|
const status = readNonEmptyString3(interaction.status) ?? "unknown";
|
|
3802
4039
|
if (status === "completed") {
|
|
3803
|
-
const text =
|
|
4040
|
+
const text = formatInteractionSteps(readInteractionSteps(interaction));
|
|
3804
4041
|
return {
|
|
3805
4042
|
status: "completed",
|
|
3806
4043
|
output: {
|
|
@@ -3830,7 +4067,7 @@ var geminiImplementation = {
|
|
|
3830
4067
|
if (status === "requires_action") {
|
|
3831
4068
|
return {
|
|
3832
4069
|
status: "failed",
|
|
3833
|
-
error: describeGeminiRequiredAction(interaction
|
|
4070
|
+
error: describeGeminiRequiredAction(readInteractionSteps(interaction))
|
|
3834
4071
|
};
|
|
3835
4072
|
}
|
|
3836
4073
|
return status === "in_progress" ? { status: "in_progress" } : { status: "in_progress", statusText: status };
|
|
@@ -3864,17 +4101,20 @@ function addAbortSignalToGeminiConfig(config, signal) {
|
|
|
3864
4101
|
abortSignal: signal
|
|
3865
4102
|
};
|
|
3866
4103
|
}
|
|
3867
|
-
function
|
|
4104
|
+
function readInteractionSteps(interaction) {
|
|
4105
|
+
return typeof interaction === "object" && interaction !== null ? interaction.steps : void 0;
|
|
4106
|
+
}
|
|
4107
|
+
function extractGoogleSearchResults(steps) {
|
|
3868
4108
|
const seen = /* @__PURE__ */ new Set();
|
|
3869
4109
|
const results = [];
|
|
3870
|
-
if (!Array.isArray(
|
|
4110
|
+
if (!Array.isArray(steps)) {
|
|
3871
4111
|
return results;
|
|
3872
4112
|
}
|
|
3873
|
-
for (const
|
|
3874
|
-
if (typeof
|
|
4113
|
+
for (const step of steps) {
|
|
4114
|
+
if (typeof step !== "object" || step === null) {
|
|
3875
4115
|
continue;
|
|
3876
4116
|
}
|
|
3877
|
-
const content =
|
|
4117
|
+
const content = step;
|
|
3878
4118
|
if (content.type !== "google_search_result") {
|
|
3879
4119
|
continue;
|
|
3880
4120
|
}
|
|
@@ -4040,16 +4280,21 @@ function extractGroundingSources(chunks) {
|
|
|
4040
4280
|
}
|
|
4041
4281
|
return sources;
|
|
4042
4282
|
}
|
|
4043
|
-
function
|
|
4283
|
+
function formatInteractionSteps(steps) {
|
|
4044
4284
|
const lines = [];
|
|
4045
|
-
if (!Array.isArray(
|
|
4285
|
+
if (!Array.isArray(steps)) {
|
|
4046
4286
|
return "";
|
|
4047
4287
|
}
|
|
4048
|
-
for (const
|
|
4049
|
-
if (typeof
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4288
|
+
for (const step of steps) {
|
|
4289
|
+
if (typeof step !== "object" || step === null || !("type" in step) || step.type !== "model_output" || !("content" in step) || !Array.isArray(step.content)) {
|
|
4290
|
+
continue;
|
|
4291
|
+
}
|
|
4292
|
+
for (const part of step.content) {
|
|
4293
|
+
if (typeof part === "object" && part !== null && "type" in part && part.type === "text" && "text" in part && typeof part.text === "string") {
|
|
4294
|
+
const text = part.text.trim();
|
|
4295
|
+
if (text) {
|
|
4296
|
+
lines.push(text);
|
|
4297
|
+
}
|
|
4053
4298
|
}
|
|
4054
4299
|
}
|
|
4055
4300
|
}
|
|
@@ -4244,14 +4489,12 @@ function buildGeminiGenerateContentRequest({
|
|
|
4244
4489
|
}
|
|
4245
4490
|
};
|
|
4246
4491
|
}
|
|
4247
|
-
function describeGeminiRequiredAction(
|
|
4248
|
-
if (!Array.isArray(
|
|
4492
|
+
function describeGeminiRequiredAction(steps) {
|
|
4493
|
+
if (!Array.isArray(steps) || steps.length === 0) {
|
|
4249
4494
|
return "research requires additional action";
|
|
4250
4495
|
}
|
|
4251
|
-
const
|
|
4252
|
-
|
|
4253
|
-
);
|
|
4254
|
-
const type = readNonEmptyString3(firstOutput?.type);
|
|
4496
|
+
const lastStep = [...steps].reverse().find((value) => typeof value === "object" && value !== null);
|
|
4497
|
+
const type = readNonEmptyString3(lastStep?.type);
|
|
4255
4498
|
if (!type) {
|
|
4256
4499
|
return "research requires additional action";
|
|
4257
4500
|
}
|
|
@@ -4335,6 +4578,7 @@ var geminiProvider = defineProvider({
|
|
|
4335
4578
|
capabilities: {
|
|
4336
4579
|
search: defineCapability({
|
|
4337
4580
|
options: geminiImplementation.getToolOptionsSchema?.("search"),
|
|
4581
|
+
promptGuidelines: geminiSearchPromptGuidelines,
|
|
4338
4582
|
async execute(input, ctx) {
|
|
4339
4583
|
const { query: query2, maxResults, options } = input;
|
|
4340
4584
|
return await geminiImplementation.search(
|
|
@@ -4403,6 +4647,12 @@ var linkupSearchOptionsSchema = Type9.Object(
|
|
|
4403
4647
|
},
|
|
4404
4648
|
{ description: "Linkup search options." }
|
|
4405
4649
|
);
|
|
4650
|
+
var linkupSearchPromptGuidelines = [
|
|
4651
|
+
"Use Linkup depth='deep' for exploratory or high-recall source discovery, and 'standard' for quick direct searches.",
|
|
4652
|
+
"Use includeDomains or excludeDomains when the user names source constraints or when limiting the search space improves precision.",
|
|
4653
|
+
"Use fromDate and toDate when the user asks for recent, historical, or bounded-by-date results.",
|
|
4654
|
+
"Enable includeImages only when images are directly useful; otherwise keep search focused on textual source discovery."
|
|
4655
|
+
];
|
|
4406
4656
|
var linkupContentsOptionsSchema = Type9.Object(
|
|
4407
4657
|
{
|
|
4408
4658
|
renderJs: Type9.Optional(
|
|
@@ -4772,6 +5022,7 @@ var linkupProvider = defineProvider({
|
|
|
4772
5022
|
capabilities: {
|
|
4773
5023
|
search: defineCapability({
|
|
4774
5024
|
options: linkupImplementation.getToolOptionsSchema?.("search"),
|
|
5025
|
+
promptGuidelines: linkupSearchPromptGuidelines,
|
|
4775
5026
|
async execute(input, ctx) {
|
|
4776
5027
|
const { query: query2, maxResults, options } = input;
|
|
4777
5028
|
return await linkupImplementation.search(
|
|
@@ -4981,11 +5232,40 @@ function buildFetchMetadata(data) {
|
|
|
4981
5232
|
}
|
|
4982
5233
|
|
|
4983
5234
|
// src/providers/openai.ts
|
|
4984
|
-
import { Type as Type10 } from "typebox";
|
|
4985
5235
|
import OpenAI from "openai";
|
|
5236
|
+
import { Type as Type10 } from "typebox";
|
|
4986
5237
|
var DEFAULT_SEARCH_MODEL2 = "gpt-4.1";
|
|
4987
5238
|
var DEFAULT_ANSWER_MODEL2 = "gpt-4.1";
|
|
4988
5239
|
var DEFAULT_RESEARCH_MODEL = "o4-mini-deep-research";
|
|
5240
|
+
var openaiWebSearchToolProperties = {
|
|
5241
|
+
searchContextSize: Type10.Optional(
|
|
5242
|
+
literalUnion(["low", "medium", "high"], {
|
|
5243
|
+
description: "Amount of context OpenAI web search should retrieve per search."
|
|
5244
|
+
})
|
|
5245
|
+
),
|
|
5246
|
+
allowedDomains: Type10.Optional(
|
|
5247
|
+
Type10.Array(Type10.String(), {
|
|
5248
|
+
description: "Restrict OpenAI web search to these domains."
|
|
5249
|
+
})
|
|
5250
|
+
),
|
|
5251
|
+
userLocation: Type10.Optional(
|
|
5252
|
+
Type10.Object(
|
|
5253
|
+
{
|
|
5254
|
+
city: Type10.Optional(Type10.String({ description: "User city hint." })),
|
|
5255
|
+
country: Type10.Optional(
|
|
5256
|
+
Type10.String({ description: "Two-letter user country code." })
|
|
5257
|
+
),
|
|
5258
|
+
region: Type10.Optional(
|
|
5259
|
+
Type10.String({ description: "User region hint." })
|
|
5260
|
+
),
|
|
5261
|
+
timezone: Type10.Optional(
|
|
5262
|
+
Type10.String({ description: "IANA timezone hint." })
|
|
5263
|
+
)
|
|
5264
|
+
},
|
|
5265
|
+
{ description: "Approximate user location for OpenAI web search." }
|
|
5266
|
+
)
|
|
5267
|
+
)
|
|
5268
|
+
};
|
|
4989
5269
|
var openaiSearchOptionsSchema = Type10.Object(
|
|
4990
5270
|
{
|
|
4991
5271
|
model: Type10.Optional(
|
|
@@ -4997,10 +5277,19 @@ var openaiSearchOptionsSchema = Type10.Object(
|
|
|
4997
5277
|
Type10.String({
|
|
4998
5278
|
description: "Optional instructions that shape source selection and result style."
|
|
4999
5279
|
})
|
|
5000
|
-
)
|
|
5280
|
+
),
|
|
5281
|
+
...openaiWebSearchToolProperties
|
|
5001
5282
|
},
|
|
5002
5283
|
{ description: "OpenAI search options." }
|
|
5003
5284
|
);
|
|
5285
|
+
var openaiSearchPromptGuidelines = [
|
|
5286
|
+
"Use OpenAI web search when an LLM-mediated search pass should identify likely sources from the live web.",
|
|
5287
|
+
"Use instructions to constrain source selection, freshness, geography, or output style only when the user explicitly needs that control.",
|
|
5288
|
+
"Use allowedDomains when the user asks to search only specific sites or primary-source domains.",
|
|
5289
|
+
"Use searchContextSize='high' only when the query needs richer source context; use 'low' for quick source discovery.",
|
|
5290
|
+
"Use userLocation for local, regional, or jurisdiction-specific searches.",
|
|
5291
|
+
"Prefer web_contents after OpenAI search when the task requires direct inspection of selected primary sources."
|
|
5292
|
+
];
|
|
5004
5293
|
var openaiAnswerOptionsSchema = Type10.Object(
|
|
5005
5294
|
{
|
|
5006
5295
|
model: Type10.Optional(
|
|
@@ -5012,7 +5301,8 @@ var openaiAnswerOptionsSchema = Type10.Object(
|
|
|
5012
5301
|
Type10.String({
|
|
5013
5302
|
description: "Optional instructions that shape the answer structure, tone, and source selection."
|
|
5014
5303
|
})
|
|
5015
|
-
)
|
|
5304
|
+
),
|
|
5305
|
+
...openaiWebSearchToolProperties
|
|
5016
5306
|
},
|
|
5017
5307
|
{ description: "OpenAI answer options." }
|
|
5018
5308
|
);
|
|
@@ -5033,7 +5323,8 @@ var openaiResearchOptionsSchema = Type10.Object(
|
|
|
5033
5323
|
minimum: 1,
|
|
5034
5324
|
description: "Maximum number of built-in tool calls the model may make during the research run."
|
|
5035
5325
|
})
|
|
5036
|
-
)
|
|
5326
|
+
),
|
|
5327
|
+
...openaiWebSearchToolProperties
|
|
5037
5328
|
},
|
|
5038
5329
|
{ description: "OpenAI deep research options." }
|
|
5039
5330
|
);
|
|
@@ -5194,7 +5485,7 @@ function buildOpenAISearchRequest(query2, maxResults, config, options) {
|
|
|
5194
5485
|
"",
|
|
5195
5486
|
`User query: ${query2}`
|
|
5196
5487
|
].join("\n"),
|
|
5197
|
-
tools: [
|
|
5488
|
+
tools: [buildOpenAIWebSearchTool(mergedOptions)],
|
|
5198
5489
|
text: {
|
|
5199
5490
|
format: {
|
|
5200
5491
|
type: "json_schema",
|
|
@@ -5213,7 +5504,7 @@ function buildOpenAIAnswerRequest(query2, config, options) {
|
|
|
5213
5504
|
return {
|
|
5214
5505
|
model,
|
|
5215
5506
|
input: query2,
|
|
5216
|
-
tools: [
|
|
5507
|
+
tools: [buildOpenAIWebSearchTool(mergedOptions)],
|
|
5217
5508
|
...instructions ? { instructions } : {}
|
|
5218
5509
|
};
|
|
5219
5510
|
}
|
|
@@ -5226,11 +5517,41 @@ function buildOpenAIResearchRequest(input, config, options) {
|
|
|
5226
5517
|
model,
|
|
5227
5518
|
input,
|
|
5228
5519
|
background: true,
|
|
5229
|
-
tools: [
|
|
5520
|
+
tools: [buildOpenAIWebSearchTool(mergedOptions)],
|
|
5230
5521
|
...instructions ? { instructions } : {},
|
|
5231
5522
|
...maxToolCalls ? { max_tool_calls: maxToolCalls } : {}
|
|
5232
5523
|
};
|
|
5233
5524
|
}
|
|
5525
|
+
function buildOpenAIWebSearchTool(options) {
|
|
5526
|
+
const tool = { type: "web_search" };
|
|
5527
|
+
if (options.searchContextSize) {
|
|
5528
|
+
tool.search_context_size = options.searchContextSize;
|
|
5529
|
+
}
|
|
5530
|
+
if (options.allowedDomains && options.allowedDomains.length > 0) {
|
|
5531
|
+
tool.filters = { allowed_domains: options.allowedDomains };
|
|
5532
|
+
}
|
|
5533
|
+
if (options.userLocation) {
|
|
5534
|
+
tool.user_location = {
|
|
5535
|
+
type: "approximate",
|
|
5536
|
+
...options.userLocation
|
|
5537
|
+
};
|
|
5538
|
+
}
|
|
5539
|
+
return tool;
|
|
5540
|
+
}
|
|
5541
|
+
function resolveOpenAIWebSearchToolOptions(merged) {
|
|
5542
|
+
const searchContextSize = readStringUnion(merged.searchContextSize, [
|
|
5543
|
+
"low",
|
|
5544
|
+
"medium",
|
|
5545
|
+
"high"
|
|
5546
|
+
]);
|
|
5547
|
+
const allowedDomains = readStringArray(merged.allowedDomains);
|
|
5548
|
+
const userLocation = readUserLocation(merged.userLocation);
|
|
5549
|
+
return {
|
|
5550
|
+
...searchContextSize ? { searchContextSize } : {},
|
|
5551
|
+
...allowedDomains ? { allowedDomains } : {},
|
|
5552
|
+
...userLocation ? { userLocation } : {}
|
|
5553
|
+
};
|
|
5554
|
+
}
|
|
5234
5555
|
function resolveOpenAISearchOptions(config, options) {
|
|
5235
5556
|
const mergedOptions = {
|
|
5236
5557
|
...config.options?.search ?? {},
|
|
@@ -5240,7 +5561,8 @@ function resolveOpenAISearchOptions(config, options) {
|
|
|
5240
5561
|
const instructions = readNonEmptyString4(mergedOptions.instructions);
|
|
5241
5562
|
return {
|
|
5242
5563
|
...model ? { model } : {},
|
|
5243
|
-
...instructions ? { instructions } : {}
|
|
5564
|
+
...instructions ? { instructions } : {},
|
|
5565
|
+
...resolveOpenAIWebSearchToolOptions(mergedOptions)
|
|
5244
5566
|
};
|
|
5245
5567
|
}
|
|
5246
5568
|
function resolveOpenAIAnswerOptions(config, options) {
|
|
@@ -5252,7 +5574,8 @@ function resolveOpenAIAnswerOptions(config, options) {
|
|
|
5252
5574
|
const instructions = readNonEmptyString4(mergedOptions.instructions);
|
|
5253
5575
|
return {
|
|
5254
5576
|
...model ? { model } : {},
|
|
5255
|
-
...instructions ? { instructions } : {}
|
|
5577
|
+
...instructions ? { instructions } : {},
|
|
5578
|
+
...resolveOpenAIWebSearchToolOptions(mergedOptions)
|
|
5256
5579
|
};
|
|
5257
5580
|
}
|
|
5258
5581
|
function resolveOpenAIResearchOptions(config, options) {
|
|
@@ -5266,7 +5589,8 @@ function resolveOpenAIResearchOptions(config, options) {
|
|
|
5266
5589
|
return {
|
|
5267
5590
|
...model ? { model } : {},
|
|
5268
5591
|
...instructions ? { instructions } : {},
|
|
5269
|
-
...maxToolCalls ? { max_tool_calls: maxToolCalls } : {}
|
|
5592
|
+
...maxToolCalls ? { max_tool_calls: maxToolCalls } : {},
|
|
5593
|
+
...resolveOpenAIWebSearchToolOptions(mergedOptions)
|
|
5270
5594
|
};
|
|
5271
5595
|
}
|
|
5272
5596
|
function buildRequestOptions2(signal, idempotencyKey) {
|
|
@@ -5431,6 +5755,35 @@ function formatIncompleteError(response, operation) {
|
|
|
5431
5755
|
function readNonEmptyString4(value) {
|
|
5432
5756
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
5433
5757
|
}
|
|
5758
|
+
function readStringArray(value) {
|
|
5759
|
+
if (!Array.isArray(value)) {
|
|
5760
|
+
return void 0;
|
|
5761
|
+
}
|
|
5762
|
+
const strings = value.filter(
|
|
5763
|
+
(item) => typeof item === "string" && item.trim() !== ""
|
|
5764
|
+
);
|
|
5765
|
+
return strings.length > 0 ? strings : void 0;
|
|
5766
|
+
}
|
|
5767
|
+
function readStringUnion(value, values) {
|
|
5768
|
+
return typeof value === "string" && values.includes(value) ? value : void 0;
|
|
5769
|
+
}
|
|
5770
|
+
function readUserLocation(value) {
|
|
5771
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
5772
|
+
return void 0;
|
|
5773
|
+
}
|
|
5774
|
+
const location = value;
|
|
5775
|
+
const city = readNonEmptyString4(location.city);
|
|
5776
|
+
const country = readNonEmptyString4(location.country);
|
|
5777
|
+
const region = readNonEmptyString4(location.region);
|
|
5778
|
+
const timezone = readNonEmptyString4(location.timezone);
|
|
5779
|
+
const result = {
|
|
5780
|
+
...city ? { city } : {},
|
|
5781
|
+
...country ? { country } : {},
|
|
5782
|
+
...region ? { region } : {},
|
|
5783
|
+
...timezone ? { timezone } : {}
|
|
5784
|
+
};
|
|
5785
|
+
return Object.keys(result).length > 0 ? result : void 0;
|
|
5786
|
+
}
|
|
5434
5787
|
function readPositiveInteger2(value) {
|
|
5435
5788
|
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : void 0;
|
|
5436
5789
|
}
|
|
@@ -5455,6 +5808,7 @@ var openaiProvider = defineProvider({
|
|
|
5455
5808
|
capabilities: {
|
|
5456
5809
|
search: defineCapability({
|
|
5457
5810
|
options: openaiImplementation.getToolOptionsSchema?.("search"),
|
|
5811
|
+
promptGuidelines: openaiSearchPromptGuidelines,
|
|
5458
5812
|
async execute(input, ctx) {
|
|
5459
5813
|
const { query: query2, maxResults, options } = input;
|
|
5460
5814
|
return await openaiImplementation.search(
|
|
@@ -5497,13 +5851,19 @@ import { Type as Type11 } from "typebox";
|
|
|
5497
5851
|
var parallelSearchOptionsSchema = Type11.Object(
|
|
5498
5852
|
{
|
|
5499
5853
|
mode: Type11.Optional(
|
|
5500
|
-
literalUnion(["
|
|
5501
|
-
description: "Parallel search mode. Use '
|
|
5854
|
+
literalUnion(["advanced", "basic", "turbo"], {
|
|
5855
|
+
description: "Parallel search mode. Use 'advanced' for higher quality, 'basic' for lower latency, or 'turbo' for the fastest responses."
|
|
5502
5856
|
})
|
|
5503
5857
|
)
|
|
5504
5858
|
},
|
|
5505
5859
|
{ description: "Parallel search options." }
|
|
5506
5860
|
);
|
|
5861
|
+
var parallelSearchPromptGuidelines = [
|
|
5862
|
+
"Use Parallel mode='advanced' for exploratory, ambiguous, or multi-hop source discovery where the provider should plan the search.",
|
|
5863
|
+
"Use Parallel mode='basic' for direct factual lookups and simple source finding where low latency is preferred.",
|
|
5864
|
+
"Use Parallel mode='turbo' only when fastest responses matter more than recall or depth.",
|
|
5865
|
+
"Prefer web_contents with Parallel extraction when a URL set is already known and the task needs full page content rather than more source discovery."
|
|
5866
|
+
];
|
|
5507
5867
|
var parallelExtractOptionsSchema = Type11.Object(
|
|
5508
5868
|
{
|
|
5509
5869
|
excerpts: Type11.Optional(
|
|
@@ -5536,7 +5896,7 @@ var parallelImplementation = {
|
|
|
5536
5896
|
credentials: { api: "PARALLEL_API_KEY" },
|
|
5537
5897
|
options: {
|
|
5538
5898
|
search: {
|
|
5539
|
-
mode: "
|
|
5899
|
+
mode: "advanced"
|
|
5540
5900
|
},
|
|
5541
5901
|
extract: {
|
|
5542
5902
|
excerpts: false,
|
|
@@ -5551,13 +5911,11 @@ var parallelImplementation = {
|
|
|
5551
5911
|
async search(query2, maxResults, config, context, options) {
|
|
5552
5912
|
const client = createClient6(config);
|
|
5553
5913
|
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
5554
|
-
const response = await client.
|
|
5555
|
-
{
|
|
5914
|
+
const response = await client.search(
|
|
5915
|
+
buildParallelSearchParams(query2, maxResults, {
|
|
5556
5916
|
...defaults,
|
|
5557
|
-
...options ?? {}
|
|
5558
|
-
|
|
5559
|
-
max_results: maxResults
|
|
5560
|
-
},
|
|
5917
|
+
...options ?? {}
|
|
5918
|
+
}),
|
|
5561
5919
|
buildRequestOptions3(context)
|
|
5562
5920
|
);
|
|
5563
5921
|
return {
|
|
@@ -5572,12 +5930,11 @@ var parallelImplementation = {
|
|
|
5572
5930
|
async contents(urls, config, context, options) {
|
|
5573
5931
|
const client = createClient6(config);
|
|
5574
5932
|
const defaults = asJsonObject(config.options?.extract) ?? {};
|
|
5575
|
-
const response = await client.
|
|
5576
|
-
{
|
|
5933
|
+
const response = await client.extract(
|
|
5934
|
+
buildParallelExtractParams(urls, {
|
|
5577
5935
|
...defaults,
|
|
5578
|
-
...options ?? {}
|
|
5579
|
-
|
|
5580
|
-
},
|
|
5936
|
+
...options ?? {}
|
|
5937
|
+
}),
|
|
5581
5938
|
buildRequestOptions3(context)
|
|
5582
5939
|
);
|
|
5583
5940
|
const resultsByUrl = new Map(
|
|
@@ -5609,6 +5966,62 @@ var parallelImplementation = {
|
|
|
5609
5966
|
};
|
|
5610
5967
|
}
|
|
5611
5968
|
};
|
|
5969
|
+
function buildParallelSearchParams(query2, maxResults, options) {
|
|
5970
|
+
const {
|
|
5971
|
+
advanced_settings: advancedSettingsValue,
|
|
5972
|
+
max_results: _legacyMaxResults,
|
|
5973
|
+
mode: modeValue,
|
|
5974
|
+
objective: objectiveValue,
|
|
5975
|
+
search_queries: _searchQueries,
|
|
5976
|
+
...rest
|
|
5977
|
+
} = options;
|
|
5978
|
+
const advancedSettings = readObjectOption(advancedSettingsValue);
|
|
5979
|
+
const mode = normalizeParallelSearchMode(modeValue);
|
|
5980
|
+
const objective = typeof objectiveValue === "string" && objectiveValue.trim() ? objectiveValue.trim() : query2;
|
|
5981
|
+
return {
|
|
5982
|
+
...rest,
|
|
5983
|
+
search_queries: [query2],
|
|
5984
|
+
objective,
|
|
5985
|
+
...mode ? { mode } : {},
|
|
5986
|
+
advanced_settings: {
|
|
5987
|
+
...advancedSettings,
|
|
5988
|
+
max_results: maxResults
|
|
5989
|
+
}
|
|
5990
|
+
};
|
|
5991
|
+
}
|
|
5992
|
+
function buildParallelExtractParams(urls, options) {
|
|
5993
|
+
const {
|
|
5994
|
+
advanced_settings: advancedSettingsValue,
|
|
5995
|
+
excerpts: excerptsValue,
|
|
5996
|
+
full_content: fullContentValue,
|
|
5997
|
+
...rest
|
|
5998
|
+
} = options;
|
|
5999
|
+
const advancedSettings = readObjectOption(advancedSettingsValue);
|
|
6000
|
+
if (typeof fullContentValue === "boolean") {
|
|
6001
|
+
advancedSettings.full_content = fullContentValue;
|
|
6002
|
+
}
|
|
6003
|
+
if (typeof excerptsValue === "boolean" && advancedSettings.excerpt_settings === void 0) {
|
|
6004
|
+
advancedSettings.excerpt_settings = excerptsValue ? {} : { max_chars_per_result: 0 };
|
|
6005
|
+
}
|
|
6006
|
+
return {
|
|
6007
|
+
...rest,
|
|
6008
|
+
urls,
|
|
6009
|
+
advanced_settings: advancedSettings
|
|
6010
|
+
};
|
|
6011
|
+
}
|
|
6012
|
+
function normalizeParallelSearchMode(value) {
|
|
6013
|
+
switch (value) {
|
|
6014
|
+
case "advanced":
|
|
6015
|
+
case "basic":
|
|
6016
|
+
case "turbo":
|
|
6017
|
+
return value;
|
|
6018
|
+
default:
|
|
6019
|
+
return void 0;
|
|
6020
|
+
}
|
|
6021
|
+
}
|
|
6022
|
+
function readObjectOption(value) {
|
|
6023
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? { ...value } : {};
|
|
6024
|
+
}
|
|
5612
6025
|
function createClient6(config) {
|
|
5613
6026
|
const apiKey = resolveConfigValue(config.credentials?.api);
|
|
5614
6027
|
if (!apiKey) {
|
|
@@ -5639,6 +6052,7 @@ var parallelProvider = defineProvider({
|
|
|
5639
6052
|
capabilities: {
|
|
5640
6053
|
search: defineCapability({
|
|
5641
6054
|
options: parallelImplementation.getToolOptionsSchema?.("search"),
|
|
6055
|
+
promptGuidelines: parallelSearchPromptGuidelines,
|
|
5642
6056
|
async execute(input, ctx) {
|
|
5643
6057
|
const { query: query2, maxResults, options } = input;
|
|
5644
6058
|
return await parallelImplementation.search(
|
|
@@ -5690,6 +6104,12 @@ var perplexitySearchOptionsSchema = Type12.Object(
|
|
|
5690
6104
|
},
|
|
5691
6105
|
{ description: "Perplexity search options." }
|
|
5692
6106
|
);
|
|
6107
|
+
var perplexitySearchPromptGuidelines = [
|
|
6108
|
+
"Use Perplexity search for concise web result retrieval with recency and domain filters, not for synthesized answers.",
|
|
6109
|
+
"Set search_recency_filter when freshness matters, such as breaking news, recently updated documentation, prices, or current events.",
|
|
6110
|
+
"Set search_domain_filter when the user asks for primary sources, official documentation, or domain-scoped retrieval.",
|
|
6111
|
+
"Use web_answer or web_research with Perplexity when the user wants synthesis rather than a list of candidate sources."
|
|
6112
|
+
];
|
|
5693
6113
|
var perplexityAnswerOptionsSchema = Type12.Object(
|
|
5694
6114
|
{
|
|
5695
6115
|
model: Type12.Optional(
|
|
@@ -5959,6 +6379,7 @@ var perplexityProvider = defineProvider({
|
|
|
5959
6379
|
capabilities: {
|
|
5960
6380
|
search: defineCapability({
|
|
5961
6381
|
options: perplexityImplementation.getToolOptionsSchema?.("search"),
|
|
6382
|
+
promptGuidelines: perplexitySearchPromptGuidelines,
|
|
5962
6383
|
async execute(input, ctx) {
|
|
5963
6384
|
const { query: query2, maxResults, options } = input;
|
|
5964
6385
|
return await perplexityImplementation.search(
|
|
@@ -6687,6 +7108,12 @@ var tavilySearchOptionsSchema = Type14.Object(
|
|
|
6687
7108
|
},
|
|
6688
7109
|
{ description: "Tavily search options." }
|
|
6689
7110
|
);
|
|
7111
|
+
var tavilySearchPromptGuidelines = [
|
|
7112
|
+
"Use Tavily topic='news' for recent journalism or current events and topic='finance' for market or company-finance research; otherwise leave topic as general.",
|
|
7113
|
+
"Use searchDepth='advanced' for broader or higher-recall source discovery, and 'basic' for quick direct lookups.",
|
|
7114
|
+
"Set timeRange, days, or country when the user asks for freshness, recency, or geography-specific results.",
|
|
7115
|
+
"Set includeRawContent or includeAnswer only when the search response itself should carry more context; prefer web_contents for selected source inspection."
|
|
7116
|
+
];
|
|
6690
7117
|
var tavilyExtractOptionsSchema = Type14.Object(
|
|
6691
7118
|
{
|
|
6692
7119
|
extractDepth: Type14.Optional(
|
|
@@ -6848,6 +7275,7 @@ var tavilyProvider = defineProvider({
|
|
|
6848
7275
|
capabilities: {
|
|
6849
7276
|
search: defineCapability({
|
|
6850
7277
|
options: tavilyImplementation.getToolOptionsSchema?.("search"),
|
|
7278
|
+
promptGuidelines: tavilySearchPromptGuidelines,
|
|
6851
7279
|
async execute(input, ctx) {
|
|
6852
7280
|
const { query: query2, maxResults, options } = input;
|
|
6853
7281
|
return await tavilyImplementation.search(
|
|
@@ -6890,32 +7318,230 @@ var valyuSearchOptionsSchema = Type15.Object(
|
|
|
6890
7318
|
),
|
|
6891
7319
|
countryCode: Type15.Optional(
|
|
6892
7320
|
Type15.String({ description: "Country code to scope search results." })
|
|
6893
|
-
)
|
|
6894
|
-
},
|
|
6895
|
-
{ description: "Valyu search options." }
|
|
6896
|
-
);
|
|
6897
|
-
var valyuAnswerOptionsSchema = Type15.Object(
|
|
6898
|
-
{
|
|
6899
|
-
responseLength: Type15.Optional(
|
|
6900
|
-
literalUnion(["short", "medium", "large", "max"], {
|
|
6901
|
-
description: "Response length for answers."
|
|
6902
|
-
})
|
|
6903
7321
|
),
|
|
6904
|
-
|
|
6905
|
-
Type15.
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
)
|
|
6910
|
-
|
|
6911
|
-
|
|
6912
|
-
|
|
6913
|
-
|
|
6914
|
-
description: "
|
|
7322
|
+
maxPrice: Type15.Optional(
|
|
7323
|
+
Type15.Number({
|
|
7324
|
+
minimum: 0,
|
|
7325
|
+
description: "Maximum price per thousand characters (CPM)."
|
|
7326
|
+
})
|
|
7327
|
+
),
|
|
7328
|
+
relevanceThreshold: Type15.Optional(
|
|
7329
|
+
Type15.Number({
|
|
7330
|
+
minimum: 0,
|
|
7331
|
+
maximum: 1,
|
|
7332
|
+
description: "Minimum result relevance score."
|
|
7333
|
+
})
|
|
7334
|
+
),
|
|
7335
|
+
includedSources: Type15.Optional(
|
|
7336
|
+
Type15.Array(Type15.String(), {
|
|
7337
|
+
description: "Restrict retrieval to these Valyu sources."
|
|
7338
|
+
})
|
|
7339
|
+
),
|
|
7340
|
+
excludeSources: Type15.Optional(
|
|
7341
|
+
Type15.Array(Type15.String(), {
|
|
7342
|
+
description: "Exclude these Valyu sources."
|
|
7343
|
+
})
|
|
7344
|
+
),
|
|
7345
|
+
sourceBiases: Type15.Optional(
|
|
7346
|
+
Type15.Record(Type15.String(), Type15.Number(), {
|
|
7347
|
+
description: "Per-source relevance bias weights."
|
|
7348
|
+
})
|
|
7349
|
+
),
|
|
7350
|
+
category: Type15.Optional(
|
|
7351
|
+
Type15.String({ description: "Valyu source category to search." })
|
|
7352
|
+
),
|
|
7353
|
+
startDate: Type15.Optional(
|
|
7354
|
+
Type15.String({ description: "ISO date string for earliest result date." })
|
|
7355
|
+
),
|
|
7356
|
+
endDate: Type15.Optional(
|
|
7357
|
+
Type15.String({ description: "ISO date string for latest result date." })
|
|
7358
|
+
),
|
|
7359
|
+
historicalCache: Type15.Optional(
|
|
7360
|
+
Type15.Boolean({
|
|
7361
|
+
description: "Allow Valyu historical cache usage when supported."
|
|
7362
|
+
})
|
|
7363
|
+
),
|
|
7364
|
+
fastMode: Type15.Optional(
|
|
7365
|
+
Type15.Boolean({
|
|
7366
|
+
description: "Use Valyu fast mode when lower latency is preferred."
|
|
7367
|
+
})
|
|
7368
|
+
),
|
|
7369
|
+
urlOnly: Type15.Optional(
|
|
7370
|
+
Type15.Boolean({
|
|
7371
|
+
description: "Return URL-focused results with less content."
|
|
7372
|
+
})
|
|
7373
|
+
),
|
|
7374
|
+
instructions: Type15.Optional(
|
|
7375
|
+
Type15.String({
|
|
7376
|
+
description: "Provider instructions for retrieval and result selection."
|
|
7377
|
+
})
|
|
7378
|
+
)
|
|
7379
|
+
},
|
|
7380
|
+
{ description: "Valyu search options." }
|
|
7381
|
+
);
|
|
7382
|
+
var valyuSearchPromptGuidelines = [
|
|
7383
|
+
"Use Valyu searchType='news' for recent journalism or current events, 'web' for public web results, and 'proprietary' when proprietary Valyu sources are required.",
|
|
7384
|
+
"Use includedSources, excludeSources, category, or source biases from configuration when the user asks for source-specific retrieval.",
|
|
7385
|
+
"Use startDate/endDate and countryCode when the task requires temporal or geographic scoping.",
|
|
7386
|
+
"Set responseLength higher only when search results need richer inline context; otherwise prefer concise results and follow up with web_contents."
|
|
7387
|
+
];
|
|
7388
|
+
var valyuContentsOptionsSchema = Type15.Object(
|
|
7389
|
+
{
|
|
7390
|
+
summary: Type15.Optional(
|
|
7391
|
+
Type15.Union(
|
|
7392
|
+
[Type15.Boolean(), Type15.String(), Type15.Record(Type15.String(), Type15.Any())],
|
|
7393
|
+
{
|
|
7394
|
+
description: "Whether to include a summary, or instructions for the summary."
|
|
7395
|
+
}
|
|
7396
|
+
)
|
|
7397
|
+
),
|
|
7398
|
+
extractEffort: Type15.Optional(
|
|
7399
|
+
literalUnion(["normal", "high", "auto"], {
|
|
7400
|
+
description: "Extraction effort. Use 'high' for difficult pages and 'normal' for faster extraction."
|
|
7401
|
+
})
|
|
7402
|
+
),
|
|
7403
|
+
responseLength: Type15.Optional(
|
|
7404
|
+
Type15.Union(
|
|
7405
|
+
[
|
|
7406
|
+
literalUnion(["short", "medium", "large", "max"]),
|
|
7407
|
+
Type15.Number({ minimum: 0 })
|
|
7408
|
+
],
|
|
7409
|
+
{
|
|
7410
|
+
description: "Content response length."
|
|
7411
|
+
}
|
|
7412
|
+
)
|
|
7413
|
+
),
|
|
7414
|
+
maxPriceDollars: Type15.Optional(
|
|
7415
|
+
Type15.Number({
|
|
7416
|
+
minimum: 0,
|
|
7417
|
+
description: "Maximum extraction cost in USD."
|
|
7418
|
+
})
|
|
7419
|
+
),
|
|
7420
|
+
screenshot: Type15.Optional(
|
|
7421
|
+
Type15.Boolean({
|
|
7422
|
+
description: "Include screenshot capture when supported."
|
|
7423
|
+
})
|
|
7424
|
+
),
|
|
7425
|
+
startDate: Type15.Optional(
|
|
7426
|
+
Type15.String({
|
|
7427
|
+
description: "ISO date string for earliest content date."
|
|
7428
|
+
})
|
|
7429
|
+
),
|
|
7430
|
+
endDate: Type15.Optional(
|
|
7431
|
+
Type15.String({ description: "ISO date string for latest content date." })
|
|
7432
|
+
),
|
|
7433
|
+
historicalCache: Type15.Optional(
|
|
7434
|
+
Type15.Boolean({
|
|
7435
|
+
description: "Allow Valyu historical cache usage when supported."
|
|
7436
|
+
})
|
|
7437
|
+
)
|
|
7438
|
+
},
|
|
7439
|
+
{ description: "Valyu contents options." }
|
|
7440
|
+
);
|
|
7441
|
+
var valyuAnswerOptionsSchema = Type15.Object(
|
|
7442
|
+
{
|
|
7443
|
+
structuredOutput: Type15.Optional(
|
|
7444
|
+
Type15.Record(Type15.String(), Type15.Any(), {
|
|
7445
|
+
description: "JSON schema-like structured output specification."
|
|
7446
|
+
})
|
|
7447
|
+
),
|
|
7448
|
+
systemInstructions: Type15.Optional(
|
|
7449
|
+
Type15.String({
|
|
7450
|
+
description: "System instructions that guide Valyu answer generation."
|
|
7451
|
+
})
|
|
7452
|
+
),
|
|
7453
|
+
searchType: Type15.Optional(
|
|
7454
|
+
literalUnion(["all", "web", "proprietary", "news"], {
|
|
7455
|
+
description: "Valyu search type for answer grounding."
|
|
7456
|
+
})
|
|
7457
|
+
),
|
|
7458
|
+
dataMaxPrice: Type15.Optional(
|
|
7459
|
+
Type15.Number({
|
|
7460
|
+
minimum: 0,
|
|
7461
|
+
description: "Maximum data retrieval price for answer grounding."
|
|
6915
7462
|
})
|
|
6916
7463
|
),
|
|
6917
7464
|
countryCode: Type15.Optional(
|
|
6918
|
-
Type15.String({ description: "Country code to scope
|
|
7465
|
+
Type15.String({ description: "Country code to scope answer results." })
|
|
7466
|
+
),
|
|
7467
|
+
includedSources: Type15.Optional(
|
|
7468
|
+
Type15.Array(Type15.String(), {
|
|
7469
|
+
description: "Restrict answer grounding to these Valyu sources."
|
|
7470
|
+
})
|
|
7471
|
+
),
|
|
7472
|
+
excludedSources: Type15.Optional(
|
|
7473
|
+
Type15.Array(Type15.String(), {
|
|
7474
|
+
description: "Exclude these Valyu sources from answer grounding."
|
|
7475
|
+
})
|
|
7476
|
+
),
|
|
7477
|
+
startDate: Type15.Optional(
|
|
7478
|
+
Type15.String({ description: "ISO date string for earliest source date." })
|
|
7479
|
+
),
|
|
7480
|
+
endDate: Type15.Optional(
|
|
7481
|
+
Type15.String({ description: "ISO date string for latest source date." })
|
|
7482
|
+
),
|
|
7483
|
+
fastMode: Type15.Optional(
|
|
7484
|
+
Type15.Boolean({
|
|
7485
|
+
description: "Use Valyu fast mode when lower latency is preferred."
|
|
7486
|
+
})
|
|
7487
|
+
)
|
|
7488
|
+
},
|
|
7489
|
+
{ description: "Valyu answer options." }
|
|
7490
|
+
);
|
|
7491
|
+
var valyuResearchOptionsSchema = Type15.Object(
|
|
7492
|
+
{
|
|
7493
|
+
mode: Type15.Optional(
|
|
7494
|
+
literalUnion(["fast", "standard", "lite", "heavy", "max"], {
|
|
7495
|
+
description: "Valyu deep research mode."
|
|
7496
|
+
})
|
|
7497
|
+
),
|
|
7498
|
+
outputFormats: Type15.Optional(
|
|
7499
|
+
Type15.Array(
|
|
7500
|
+
Type15.Union([
|
|
7501
|
+
literalUnion(["markdown", "pdf", "toon"]),
|
|
7502
|
+
Type15.Record(Type15.String(), Type15.Any())
|
|
7503
|
+
]),
|
|
7504
|
+
{ description: "Requested Valyu research output formats." }
|
|
7505
|
+
)
|
|
7506
|
+
),
|
|
7507
|
+
search: Type15.Optional(
|
|
7508
|
+
Type15.Object(
|
|
7509
|
+
{
|
|
7510
|
+
searchType: Type15.Optional(
|
|
7511
|
+
literalUnion(["all", "web", "proprietary"], {
|
|
7512
|
+
description: "Valyu source pool for research."
|
|
7513
|
+
})
|
|
7514
|
+
),
|
|
7515
|
+
includedSources: Type15.Optional(Type15.Array(Type15.String())),
|
|
7516
|
+
excludedSources: Type15.Optional(Type15.Array(Type15.String())),
|
|
7517
|
+
sourceBiases: Type15.Optional(
|
|
7518
|
+
Type15.Record(Type15.String(), Type15.Number())
|
|
7519
|
+
),
|
|
7520
|
+
startDate: Type15.Optional(Type15.String()),
|
|
7521
|
+
endDate: Type15.Optional(Type15.String()),
|
|
7522
|
+
historicalCache: Type15.Optional(Type15.Boolean()),
|
|
7523
|
+
category: Type15.Optional(Type15.String()),
|
|
7524
|
+
countryCode: Type15.Optional(Type15.String())
|
|
7525
|
+
},
|
|
7526
|
+
{
|
|
7527
|
+
additionalProperties: false,
|
|
7528
|
+
description: "Valyu deep research search configuration."
|
|
7529
|
+
}
|
|
7530
|
+
)
|
|
7531
|
+
),
|
|
7532
|
+
tools: Type15.Optional(
|
|
7533
|
+
Type15.Object(
|
|
7534
|
+
{
|
|
7535
|
+
code_execution: Type15.Optional(boolOrConfig()),
|
|
7536
|
+
screenshots: Type15.Optional(boolOrConfig()),
|
|
7537
|
+
browser_use: Type15.Optional(boolOrConfig()),
|
|
7538
|
+
charts: Type15.Optional(boolOrConfig())
|
|
7539
|
+
},
|
|
7540
|
+
{
|
|
7541
|
+
additionalProperties: false,
|
|
7542
|
+
description: "Valyu deep research tool configuration."
|
|
7543
|
+
}
|
|
7544
|
+
)
|
|
6919
7545
|
)
|
|
6920
7546
|
},
|
|
6921
7547
|
{ description: "Valyu research options." }
|
|
@@ -6928,6 +7554,8 @@ var valyuImplementation = {
|
|
|
6928
7554
|
switch (capability) {
|
|
6929
7555
|
case "search":
|
|
6930
7556
|
return valyuSearchOptionsSchema;
|
|
7557
|
+
case "contents":
|
|
7558
|
+
return valyuContentsOptionsSchema;
|
|
6931
7559
|
case "answer":
|
|
6932
7560
|
return valyuAnswerOptionsSchema;
|
|
6933
7561
|
case "research":
|
|
@@ -6975,7 +7603,10 @@ var valyuImplementation = {
|
|
|
6975
7603
|
},
|
|
6976
7604
|
async contents(urls, config, _context, options) {
|
|
6977
7605
|
const client = createClient9(config);
|
|
6978
|
-
const response = await client.contents(urls,
|
|
7606
|
+
const response = await client.contents(urls, {
|
|
7607
|
+
...asJsonObject(config.options?.contents) ?? {},
|
|
7608
|
+
...options ?? {}
|
|
7609
|
+
});
|
|
6979
7610
|
const finalResponse = "jobId" in response ? await client.waitForJob(response.jobId, {}) : response;
|
|
6980
7611
|
if (!finalResponse.success) {
|
|
6981
7612
|
throw new Error(finalResponse.error || "contents failed");
|
|
@@ -7121,7 +7752,7 @@ var valyuProvider = defineProvider({
|
|
|
7121
7752
|
config: {
|
|
7122
7753
|
createTemplate: () => valyuImplementation.createTemplate(),
|
|
7123
7754
|
fields: ["credentials", "baseUrl", "options", "settings"],
|
|
7124
|
-
optionCapabilities: ["search", "answer", "research"]
|
|
7755
|
+
optionCapabilities: ["search", "contents", "answer", "research"]
|
|
7125
7756
|
},
|
|
7126
7757
|
getCapabilityStatus: (config, cwd, tool, options) => valyuImplementation.getCapabilityStatus(
|
|
7127
7758
|
config,
|
|
@@ -7132,6 +7763,7 @@ var valyuProvider = defineProvider({
|
|
|
7132
7763
|
capabilities: {
|
|
7133
7764
|
search: defineCapability({
|
|
7134
7765
|
options: valyuImplementation.getToolOptionsSchema?.("search"),
|
|
7766
|
+
promptGuidelines: valyuSearchPromptGuidelines,
|
|
7135
7767
|
async execute(input, ctx) {
|
|
7136
7768
|
const { query: query2, maxResults, options } = input;
|
|
7137
7769
|
return await valyuImplementation.search(
|
|
@@ -7849,45 +8481,6 @@ ${JSON.stringify(value, null, 2).trim()}
|
|
|
7849
8481
|
\`\`\``;
|
|
7850
8482
|
}
|
|
7851
8483
|
|
|
7852
|
-
// src/options.ts
|
|
7853
|
-
function buildToolOptionsSchema(_capability, providerSchema) {
|
|
7854
|
-
if (!providerSchema || Object.keys(providerSchema.properties).length === 0) {
|
|
7855
|
-
return void 0;
|
|
7856
|
-
}
|
|
7857
|
-
return closeObjectSchemas(providerSchema);
|
|
7858
|
-
}
|
|
7859
|
-
function closeObjectSchemas(schema) {
|
|
7860
|
-
if (!isSchemaRecord(schema)) {
|
|
7861
|
-
return schema;
|
|
7862
|
-
}
|
|
7863
|
-
const properties = isSchemaRecord(schema.properties) ? Object.fromEntries(
|
|
7864
|
-
Object.entries(schema.properties).map(([key, value]) => [
|
|
7865
|
-
key,
|
|
7866
|
-
closeObjectSchemas(value)
|
|
7867
|
-
])
|
|
7868
|
-
) : schema.properties;
|
|
7869
|
-
const items = isSchemaRecord(schema.items) ? closeObjectSchemas(schema.items) : Array.isArray(schema.items) ? schema.items.map((item) => closeObjectSchemas(item)) : schema.items;
|
|
7870
|
-
return {
|
|
7871
|
-
...schema,
|
|
7872
|
-
...properties ? { properties } : {},
|
|
7873
|
-
...items ? { items } : {},
|
|
7874
|
-
...mapSchemaArray(schema, "anyOf"),
|
|
7875
|
-
...mapSchemaArray(schema, "oneOf"),
|
|
7876
|
-
...mapSchemaArray(schema, "allOf"),
|
|
7877
|
-
...schema.type === "object" && isSchemaRecord(schema.properties) ? { additionalProperties: false } : {}
|
|
7878
|
-
};
|
|
7879
|
-
}
|
|
7880
|
-
function mapSchemaArray(schema, key) {
|
|
7881
|
-
const value = schema[key];
|
|
7882
|
-
return Array.isArray(value) ? { [key]: value.map((entry) => closeObjectSchemas(entry)) } : {};
|
|
7883
|
-
}
|
|
7884
|
-
function isSchemaRecord(value) {
|
|
7885
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7886
|
-
}
|
|
7887
|
-
|
|
7888
|
-
// src/prefetch-manager.ts
|
|
7889
|
-
import { createHash } from "node:crypto";
|
|
7890
|
-
|
|
7891
8484
|
// src/provider-resolution.ts
|
|
7892
8485
|
function supportsTool2(provider, tool) {
|
|
7893
8486
|
return provider.capabilities[tool] !== void 0;
|
|
@@ -8029,68 +8622,202 @@ function resolveProviderForTool(config, cwd, tool, explicit) {
|
|
|
8029
8622
|
return provider;
|
|
8030
8623
|
}
|
|
8031
8624
|
|
|
8032
|
-
// src/
|
|
8033
|
-
|
|
8034
|
-
|
|
8625
|
+
// src/managed-tools.ts
|
|
8626
|
+
var CAPABILITY_TOOL_NAMES = {
|
|
8627
|
+
search: "web_search",
|
|
8628
|
+
contents: "web_contents",
|
|
8629
|
+
answer: "web_answer",
|
|
8630
|
+
research: "web_research"
|
|
8631
|
+
};
|
|
8632
|
+
var MANAGED_TOOL_NAMES = Object.values(CAPABILITY_TOOL_NAMES);
|
|
8633
|
+
function getAvailableProviderIdsForCapability(config, cwd, capability) {
|
|
8634
|
+
const providerId = getMappedProviderIdForTool(config, capability);
|
|
8635
|
+
if (!providerId) {
|
|
8636
|
+
return [];
|
|
8637
|
+
}
|
|
8638
|
+
const provider = PROVIDERS_BY_ID[providerId];
|
|
8639
|
+
if (!supportsTool2(provider, capability)) {
|
|
8640
|
+
return [];
|
|
8641
|
+
}
|
|
8642
|
+
const status = getProviderCapabilityStatus(
|
|
8643
|
+
config,
|
|
8644
|
+
cwd,
|
|
8645
|
+
providerId,
|
|
8646
|
+
capability,
|
|
8035
8647
|
{
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
settings: config.settings,
|
|
8039
|
-
execute: (executionContext) => executeProviderCapability(
|
|
8040
|
-
provider,
|
|
8041
|
-
request.capability,
|
|
8042
|
-
providerInputFromRequest(request),
|
|
8043
|
-
{
|
|
8044
|
-
...executionContext,
|
|
8045
|
-
config
|
|
8046
|
-
}
|
|
8047
|
-
)
|
|
8048
|
-
},
|
|
8049
|
-
context
|
|
8648
|
+
resolveSecrets: false
|
|
8649
|
+
}
|
|
8050
8650
|
);
|
|
8651
|
+
return isProviderCapabilityExposable(status) ? [providerId] : [];
|
|
8051
8652
|
}
|
|
8052
|
-
|
|
8053
|
-
|
|
8054
|
-
|
|
8055
|
-
|
|
8056
|
-
|
|
8057
|
-
|
|
8058
|
-
|
|
8059
|
-
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
formatErrorMessage(error)
|
|
8070
|
-
)
|
|
8071
|
-
);
|
|
8072
|
-
} finally {
|
|
8073
|
-
deadline?.cleanup();
|
|
8653
|
+
function getProviderStatusForTool(config, cwd, providerId, capability) {
|
|
8654
|
+
return getProviderCapabilityStatus(config, cwd, providerId, capability);
|
|
8655
|
+
}
|
|
8656
|
+
function getAvailableManagedToolNames(config, cwd) {
|
|
8657
|
+
return Object.keys(CAPABILITY_TOOL_NAMES).filter(
|
|
8658
|
+
(capability) => getAvailableProviderIdsForCapability(config, cwd, capability).length > 0
|
|
8659
|
+
).map((capability) => CAPABILITY_TOOL_NAMES[capability]);
|
|
8660
|
+
}
|
|
8661
|
+
function getSyncedActiveTools(config, cwd, activeToolNames, options) {
|
|
8662
|
+
const availableToolNames = new Set(getAvailableManagedToolNames(config, cwd));
|
|
8663
|
+
const nextActiveTools = new Set(activeToolNames);
|
|
8664
|
+
for (const toolName of MANAGED_TOOL_NAMES) {
|
|
8665
|
+
if (availableToolNames.has(toolName)) {
|
|
8666
|
+
if (options.addAvailable) {
|
|
8667
|
+
nextActiveTools.add(toolName);
|
|
8668
|
+
}
|
|
8669
|
+
continue;
|
|
8074
8670
|
}
|
|
8671
|
+
nextActiveTools.delete(toolName);
|
|
8075
8672
|
}
|
|
8076
|
-
|
|
8673
|
+
return nextActiveTools;
|
|
8674
|
+
}
|
|
8675
|
+
async function refreshManagedTools(pi, registerManagedTools2, cwd, options) {
|
|
8676
|
+
const config = await loadConfig();
|
|
8677
|
+
const nextActiveTools = getSyncedActiveTools(
|
|
8678
|
+
config,
|
|
8679
|
+
cwd,
|
|
8680
|
+
pi.getActiveTools(),
|
|
8681
|
+
options
|
|
8682
|
+
);
|
|
8683
|
+
registerManagedTools2({
|
|
8684
|
+
search: getAvailableProviderIdsForCapability(config, cwd, "search"),
|
|
8685
|
+
contents: getAvailableProviderIdsForCapability(config, cwd, "contents"),
|
|
8686
|
+
answer: getAvailableProviderIdsForCapability(config, cwd, "answer"),
|
|
8687
|
+
research: getAvailableProviderIdsForCapability(config, cwd, "research")
|
|
8688
|
+
});
|
|
8689
|
+
await syncManagedToolAvailability(pi, nextActiveTools);
|
|
8690
|
+
}
|
|
8691
|
+
async function refreshManagedToolsOnStartup(pi, registerManagedTools2, cwd, options) {
|
|
8077
8692
|
try {
|
|
8078
|
-
|
|
8079
|
-
`${execution.providerLabel} ${execution.capability} request`,
|
|
8080
|
-
execution.execute,
|
|
8081
|
-
requestPolicy,
|
|
8082
|
-
context
|
|
8083
|
-
);
|
|
8693
|
+
await refreshManagedTools(pi, registerManagedTools2, cwd, options);
|
|
8084
8694
|
} catch (error) {
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
|
|
8088
|
-
|
|
8695
|
+
pi.sendMessage({
|
|
8696
|
+
customType: "web-providers-config-error",
|
|
8697
|
+
content: formatStartupConfigError(error),
|
|
8698
|
+
display: true
|
|
8699
|
+
});
|
|
8700
|
+
await syncManagedToolAvailability(
|
|
8701
|
+
pi,
|
|
8702
|
+
new Set(
|
|
8703
|
+
pi.getActiveTools().filter((toolName) => !MANAGED_TOOL_NAMES.includes(toolName))
|
|
8089
8704
|
)
|
|
8090
8705
|
);
|
|
8091
8706
|
}
|
|
8092
8707
|
}
|
|
8093
|
-
function
|
|
8708
|
+
function formatStartupConfigError(error) {
|
|
8709
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
8710
|
+
return `web-providers config error: ${detail.replace(getConfigPath(), "~/.pi/agent/web-providers.json")}`;
|
|
8711
|
+
}
|
|
8712
|
+
async function syncManagedToolAvailability(pi, nextActiveTools) {
|
|
8713
|
+
const activeTools = pi.getActiveTools();
|
|
8714
|
+
const changed = activeTools.length !== nextActiveTools.size || activeTools.some((toolName) => !nextActiveTools.has(toolName));
|
|
8715
|
+
if (changed) {
|
|
8716
|
+
pi.setActiveTools(Array.from(nextActiveTools));
|
|
8717
|
+
}
|
|
8718
|
+
}
|
|
8719
|
+
|
|
8720
|
+
// src/options.ts
|
|
8721
|
+
function buildToolOptionsSchema(_capability, providerSchema) {
|
|
8722
|
+
if (!providerSchema || Object.keys(providerSchema.properties).length === 0) {
|
|
8723
|
+
return void 0;
|
|
8724
|
+
}
|
|
8725
|
+
return closeObjectSchemas(providerSchema);
|
|
8726
|
+
}
|
|
8727
|
+
function closeObjectSchemas(schema) {
|
|
8728
|
+
if (!isSchemaRecord(schema)) {
|
|
8729
|
+
return schema;
|
|
8730
|
+
}
|
|
8731
|
+
const properties = isSchemaRecord(schema.properties) ? Object.fromEntries(
|
|
8732
|
+
Object.entries(schema.properties).map(([key, value]) => [
|
|
8733
|
+
key,
|
|
8734
|
+
closeObjectSchemas(value)
|
|
8735
|
+
])
|
|
8736
|
+
) : schema.properties;
|
|
8737
|
+
const items = isSchemaRecord(schema.items) ? closeObjectSchemas(schema.items) : Array.isArray(schema.items) ? schema.items.map((item) => closeObjectSchemas(item)) : schema.items;
|
|
8738
|
+
return {
|
|
8739
|
+
...schema,
|
|
8740
|
+
...properties ? { properties } : {},
|
|
8741
|
+
...items ? { items } : {},
|
|
8742
|
+
...mapSchemaArray(schema, "anyOf"),
|
|
8743
|
+
...mapSchemaArray(schema, "oneOf"),
|
|
8744
|
+
...mapSchemaArray(schema, "allOf"),
|
|
8745
|
+
...schema.type === "object" && isSchemaRecord(schema.properties) ? { additionalProperties: false } : {}
|
|
8746
|
+
};
|
|
8747
|
+
}
|
|
8748
|
+
function mapSchemaArray(schema, key) {
|
|
8749
|
+
const value = schema[key];
|
|
8750
|
+
return Array.isArray(value) ? { [key]: value.map((entry) => closeObjectSchemas(entry)) } : {};
|
|
8751
|
+
}
|
|
8752
|
+
function isSchemaRecord(value) {
|
|
8753
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8754
|
+
}
|
|
8755
|
+
|
|
8756
|
+
// src/prefetch-manager.ts
|
|
8757
|
+
import { createHash } from "node:crypto";
|
|
8758
|
+
|
|
8759
|
+
// src/provider-runtime.ts
|
|
8760
|
+
async function executeProviderRequest(provider, config, request, context) {
|
|
8761
|
+
return await executeProviderExecution(
|
|
8762
|
+
{
|
|
8763
|
+
capability: request.capability,
|
|
8764
|
+
providerLabel: provider.label,
|
|
8765
|
+
settings: config.settings,
|
|
8766
|
+
execute: (executionContext) => executeProviderCapability(
|
|
8767
|
+
provider,
|
|
8768
|
+
request.capability,
|
|
8769
|
+
providerInputFromRequest(request),
|
|
8770
|
+
{
|
|
8771
|
+
...executionContext,
|
|
8772
|
+
config
|
|
8773
|
+
}
|
|
8774
|
+
)
|
|
8775
|
+
},
|
|
8776
|
+
context
|
|
8777
|
+
);
|
|
8778
|
+
}
|
|
8779
|
+
async function executeProviderExecution(execution, context) {
|
|
8780
|
+
if (execution.capability === "research") {
|
|
8781
|
+
const deadline = createResearchDeadlineSignal(
|
|
8782
|
+
context.signal,
|
|
8783
|
+
execution.providerLabel,
|
|
8784
|
+
execution.settings?.researchTimeoutMs
|
|
8785
|
+
);
|
|
8786
|
+
try {
|
|
8787
|
+
const researchContext = deadline ? { ...context, signal: deadline.signal } : context;
|
|
8788
|
+
return await withAbortSignal(
|
|
8789
|
+
execution.execute(researchContext),
|
|
8790
|
+
researchContext.signal
|
|
8791
|
+
);
|
|
8792
|
+
} catch (error) {
|
|
8793
|
+
throw new Error(
|
|
8794
|
+
formatProviderDiagnostic(
|
|
8795
|
+
execution.providerLabel,
|
|
8796
|
+
formatErrorMessage(error)
|
|
8797
|
+
)
|
|
8798
|
+
);
|
|
8799
|
+
} finally {
|
|
8800
|
+
deadline?.cleanup();
|
|
8801
|
+
}
|
|
8802
|
+
}
|
|
8803
|
+
const requestPolicy = resolveExecutionPolicy(execution.settings);
|
|
8804
|
+
try {
|
|
8805
|
+
return await runWithExecutionPolicy(
|
|
8806
|
+
`${execution.providerLabel} ${execution.capability} request`,
|
|
8807
|
+
execution.execute,
|
|
8808
|
+
requestPolicy,
|
|
8809
|
+
context
|
|
8810
|
+
);
|
|
8811
|
+
} catch (error) {
|
|
8812
|
+
throw new Error(
|
|
8813
|
+
formatProviderDiagnostic(
|
|
8814
|
+
execution.providerLabel,
|
|
8815
|
+
formatErrorMessage(error)
|
|
8816
|
+
)
|
|
8817
|
+
);
|
|
8818
|
+
}
|
|
8819
|
+
}
|
|
8820
|
+
function providerInputFromRequest(request) {
|
|
8094
8821
|
switch (request.capability) {
|
|
8095
8822
|
case "search":
|
|
8096
8823
|
return {
|
|
@@ -8122,7 +8849,7 @@ function resolveExecutionPolicy(defaults) {
|
|
|
8122
8849
|
retryDelayMs: defaults?.retryDelayMs ?? 2e3
|
|
8123
8850
|
};
|
|
8124
8851
|
}
|
|
8125
|
-
function createResearchDeadlineSignal(signal,
|
|
8852
|
+
function createResearchDeadlineSignal(signal, providerLabel2, timeoutMs) {
|
|
8126
8853
|
if (timeoutMs === void 0) {
|
|
8127
8854
|
return void 0;
|
|
8128
8855
|
}
|
|
@@ -8137,7 +8864,7 @@ function createResearchDeadlineSignal(signal, providerLabel, timeoutMs) {
|
|
|
8137
8864
|
const timer = setTimeout(() => {
|
|
8138
8865
|
controller.abort(
|
|
8139
8866
|
new Error(
|
|
8140
|
-
`${
|
|
8867
|
+
`${providerLabel2} research exceeded ${formatDuration(timeoutMs)}.`
|
|
8141
8868
|
)
|
|
8142
8869
|
);
|
|
8143
8870
|
}, timeoutMs);
|
|
@@ -8961,9 +9688,9 @@ var PROVIDER_SETTINGS = {
|
|
|
8961
9688
|
"hybrid",
|
|
8962
9689
|
"fast",
|
|
8963
9690
|
"instant",
|
|
9691
|
+
"deep-lite",
|
|
8964
9692
|
"deep",
|
|
8965
|
-
"deep-reasoning"
|
|
8966
|
-
"deep-max"
|
|
9693
|
+
"deep-reasoning"
|
|
8967
9694
|
],
|
|
8968
9695
|
getValue: (config) => readString5(getExaSearchOptions(config)?.type) ?? "default",
|
|
8969
9696
|
setValue: (config, value) => {
|
|
@@ -9191,7 +9918,7 @@ var PROVIDER_SETTINGS = {
|
|
|
9191
9918
|
id: "parallelSearchMode",
|
|
9192
9919
|
label: "Search mode",
|
|
9193
9920
|
help: "Parallel search mode. 'default' uses the SDK default.",
|
|
9194
|
-
values: ["default", "
|
|
9921
|
+
values: ["default", "advanced", "basic", "turbo"],
|
|
9195
9922
|
getValue: (config) => readString5(getParallelOptions(config)?.search?.mode) ?? "default",
|
|
9196
9923
|
setValue: (config, value) => {
|
|
9197
9924
|
const options = ensureParallelOptions(config);
|
|
@@ -9285,27 +10012,19 @@ var PROVIDER_SETTINGS = {
|
|
|
9285
10012
|
}
|
|
9286
10013
|
}),
|
|
9287
10014
|
valuesSetting({
|
|
9288
|
-
id: "
|
|
9289
|
-
label: "
|
|
9290
|
-
help: "Valyu
|
|
9291
|
-
values: ["default", "
|
|
9292
|
-
getValue: (config) => readString5(
|
|
9293
|
-
getValyuCapabilityOptions(config, "answer")?.responseLength
|
|
9294
|
-
) ?? "default",
|
|
9295
|
-
setValue: (config, value) => {
|
|
9296
|
-
setValyuResponseLength(config, "answer", value);
|
|
9297
|
-
}
|
|
9298
|
-
}),
|
|
9299
|
-
valuesSetting({
|
|
9300
|
-
id: "valyuResearchResponseLength",
|
|
9301
|
-
label: "Research response length",
|
|
9302
|
-
help: "Valyu research response length. 'default' uses the SDK default.",
|
|
9303
|
-
values: ["default", "short", "medium", "large", "max"],
|
|
9304
|
-
getValue: (config) => readString5(
|
|
9305
|
-
getValyuCapabilityOptions(config, "research")?.responseLength
|
|
9306
|
-
) ?? "default",
|
|
10015
|
+
id: "valyuResearchMode",
|
|
10016
|
+
label: "Research mode",
|
|
10017
|
+
help: "Valyu research mode. 'default' uses the SDK default.",
|
|
10018
|
+
values: ["default", "fast", "standard", "lite", "heavy", "max"],
|
|
10019
|
+
getValue: (config) => readString5(getValyuCapabilityOptions(config, "research")?.mode) ?? "default",
|
|
9307
10020
|
setValue: (config, value) => {
|
|
9308
|
-
|
|
10021
|
+
const options = ensureValyuCapabilityOptions(config, "research");
|
|
10022
|
+
if (value === "default") {
|
|
10023
|
+
delete options.mode;
|
|
10024
|
+
} else {
|
|
10025
|
+
options.mode = value;
|
|
10026
|
+
}
|
|
10027
|
+
cleanupCapabilityOptions(config, ["search", "answer", "research"]);
|
|
9309
10028
|
}
|
|
9310
10029
|
})
|
|
9311
10030
|
]
|
|
@@ -9792,76 +10511,1030 @@ function getFirstLine(text) {
|
|
|
9792
10511
|
return text?.split("\n").map((line) => line.trim()).find((line) => line.length > 0);
|
|
9793
10512
|
}
|
|
9794
10513
|
|
|
9795
|
-
// src/
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
var RESEARCH_HEARTBEAT_MS = 15e3;
|
|
9800
|
-
var WEB_RESEARCH_RESULT_MESSAGE_TYPE = "web-research-result";
|
|
9801
|
-
var WEB_RESEARCH_WIDGET_KEY = "web-research-jobs";
|
|
10514
|
+
// src/web-research-lifecycle.ts
|
|
10515
|
+
import { randomUUID } from "node:crypto";
|
|
10516
|
+
import { mkdir as mkdir2, readdir, readFile as readFile2, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
10517
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
9802
10518
|
var RESEARCH_ARTIFACTS_DIR = join2(".pi", "artifacts", "research");
|
|
10519
|
+
var MAX_RESEARCH_HISTORY_ITEMS = 20;
|
|
10520
|
+
var RESEARCH_PREVIEW_MAX_BYTES = 5e4;
|
|
10521
|
+
var RESEARCH_REPORT_MAX_BYTES = 2e5;
|
|
9803
10522
|
var pendingResearchTasks = /* @__PURE__ */ new Set();
|
|
9804
|
-
|
|
9805
|
-
|
|
9806
|
-
|
|
9807
|
-
|
|
9808
|
-
|
|
9809
|
-
|
|
9810
|
-
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
9817
|
-
|
|
9818
|
-
|
|
9819
|
-
|
|
9820
|
-
|
|
9821
|
-
|
|
9822
|
-
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
const
|
|
9826
|
-
|
|
9827
|
-
|
|
10523
|
+
async function dispatchWebResearch({
|
|
10524
|
+
activeWebResearchRequests,
|
|
10525
|
+
config,
|
|
10526
|
+
explicitProvider,
|
|
10527
|
+
ctx,
|
|
10528
|
+
options,
|
|
10529
|
+
input,
|
|
10530
|
+
executionOverride,
|
|
10531
|
+
executeResearch,
|
|
10532
|
+
deliverResult,
|
|
10533
|
+
onJobsChanged,
|
|
10534
|
+
resultMessageType
|
|
10535
|
+
}) {
|
|
10536
|
+
await cleanupContentStore();
|
|
10537
|
+
const provider = resolveProviderForTool(
|
|
10538
|
+
config,
|
|
10539
|
+
ctx.cwd,
|
|
10540
|
+
"research",
|
|
10541
|
+
explicitProvider
|
|
10542
|
+
);
|
|
10543
|
+
const request = createWebResearchRequest(ctx.cwd, provider.id, input);
|
|
10544
|
+
const abortController = new AbortController();
|
|
10545
|
+
const task = { request, abortController };
|
|
10546
|
+
const providerConfig = getEffectiveProviderConfig(config, provider.id);
|
|
10547
|
+
activeWebResearchRequests.set(request.id, task);
|
|
10548
|
+
onJobsChanged();
|
|
10549
|
+
trackPendingResearchTask(
|
|
10550
|
+
runDispatchedWebResearch({
|
|
10551
|
+
activeWebResearchRequests,
|
|
10552
|
+
task,
|
|
10553
|
+
config,
|
|
10554
|
+
provider,
|
|
10555
|
+
providerConfig,
|
|
10556
|
+
ctx,
|
|
10557
|
+
options,
|
|
10558
|
+
executionOverride,
|
|
10559
|
+
executeResearch,
|
|
10560
|
+
deliverResult,
|
|
10561
|
+
onJobsChanged,
|
|
10562
|
+
resultMessageType
|
|
10563
|
+
})
|
|
10564
|
+
);
|
|
10565
|
+
return {
|
|
10566
|
+
content: [
|
|
10567
|
+
{
|
|
10568
|
+
type: "text",
|
|
10569
|
+
text: `Started web research via ${provider.label}.`
|
|
10570
|
+
}
|
|
10571
|
+
],
|
|
10572
|
+
details: request,
|
|
10573
|
+
display: {
|
|
10574
|
+
provider: { id: provider.id, label: provider.label },
|
|
10575
|
+
outcome: { success: "started" }
|
|
9828
10576
|
}
|
|
9829
|
-
webResearchWidgetTimer = setInterval(() => {
|
|
9830
|
-
updateWebResearchWidget();
|
|
9831
|
-
}, 1e3);
|
|
9832
10577
|
};
|
|
9833
|
-
|
|
9834
|
-
|
|
9835
|
-
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
9846
|
-
|
|
10578
|
+
}
|
|
10579
|
+
async function runDispatchedWebResearch({
|
|
10580
|
+
activeWebResearchRequests,
|
|
10581
|
+
task,
|
|
10582
|
+
config,
|
|
10583
|
+
provider,
|
|
10584
|
+
providerConfig,
|
|
10585
|
+
ctx,
|
|
10586
|
+
options,
|
|
10587
|
+
executionOverride,
|
|
10588
|
+
executeResearch,
|
|
10589
|
+
deliverResult,
|
|
10590
|
+
onJobsChanged,
|
|
10591
|
+
resultMessageType
|
|
10592
|
+
}) {
|
|
10593
|
+
const { request, abortController } = task;
|
|
10594
|
+
let result;
|
|
10595
|
+
let reportText = "";
|
|
10596
|
+
try {
|
|
10597
|
+
const response = await executeResearch({
|
|
10598
|
+
config,
|
|
10599
|
+
provider,
|
|
10600
|
+
providerConfig,
|
|
10601
|
+
ctx,
|
|
10602
|
+
signal: abortController.signal,
|
|
10603
|
+
options,
|
|
10604
|
+
input: request.input,
|
|
10605
|
+
onProgress: (message) => {
|
|
10606
|
+
request.progress = summarizeWebResearchProgress(
|
|
10607
|
+
message,
|
|
10608
|
+
provider.label
|
|
10609
|
+
);
|
|
10610
|
+
onJobsChanged();
|
|
10611
|
+
},
|
|
10612
|
+
executionOverride
|
|
10613
|
+
});
|
|
10614
|
+
result = buildWebResearchResult(request, abortController, task, response);
|
|
10615
|
+
if (result.status === "completed") {
|
|
10616
|
+
reportText = response.text;
|
|
9847
10617
|
}
|
|
9848
|
-
|
|
9849
|
-
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
9853
|
-
|
|
9854
|
-
)
|
|
9855
|
-
);
|
|
9856
|
-
};
|
|
9857
|
-
if ("registerMessageRenderer" in pi) {
|
|
9858
|
-
pi.registerMessageRenderer(
|
|
9859
|
-
WEB_RESEARCH_RESULT_MESSAGE_TYPE,
|
|
9860
|
-
(message, state, theme) => renderWebResearchResultMessage(message, state, theme)
|
|
10618
|
+
} catch (error) {
|
|
10619
|
+
result = buildFailedWebResearchResult(
|
|
10620
|
+
request,
|
|
10621
|
+
abortController,
|
|
10622
|
+
task,
|
|
10623
|
+
error
|
|
9861
10624
|
);
|
|
9862
10625
|
}
|
|
9863
|
-
|
|
9864
|
-
|
|
10626
|
+
try {
|
|
10627
|
+
await writeWebResearchArtifact(result, reportText);
|
|
10628
|
+
deliverResult({
|
|
10629
|
+
customType: resultMessageType,
|
|
10630
|
+
content: formatWebResearchResultMessage(result, reportText),
|
|
10631
|
+
display: true,
|
|
10632
|
+
details: result
|
|
10633
|
+
});
|
|
10634
|
+
} finally {
|
|
10635
|
+
activeWebResearchRequests.delete(request.id);
|
|
10636
|
+
onJobsChanged();
|
|
10637
|
+
}
|
|
10638
|
+
}
|
|
10639
|
+
function buildWebResearchResult(request, abortController, task, response) {
|
|
10640
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10641
|
+
if (abortController.signal.aborted && task.cancelRequestedAt !== void 0) {
|
|
10642
|
+
return {
|
|
10643
|
+
...request,
|
|
10644
|
+
status: "cancelled",
|
|
10645
|
+
completedAt,
|
|
10646
|
+
elapsedMs: elapsedMs(request.startedAt, completedAt),
|
|
10647
|
+
error: "web research was cancelled by the user."
|
|
10648
|
+
};
|
|
10649
|
+
}
|
|
10650
|
+
return {
|
|
10651
|
+
...request,
|
|
10652
|
+
status: "completed",
|
|
10653
|
+
completedAt,
|
|
10654
|
+
elapsedMs: elapsedMs(request.startedAt, completedAt),
|
|
10655
|
+
itemCount: response.itemCount
|
|
10656
|
+
};
|
|
10657
|
+
}
|
|
10658
|
+
function buildFailedWebResearchResult(request, abortController, task, error) {
|
|
10659
|
+
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10660
|
+
const cancelled = abortController.signal.aborted && task.cancelRequestedAt !== void 0;
|
|
10661
|
+
return {
|
|
10662
|
+
...request,
|
|
10663
|
+
status: cancelled ? "cancelled" : "failed",
|
|
10664
|
+
completedAt,
|
|
10665
|
+
elapsedMs: elapsedMs(request.startedAt, completedAt),
|
|
10666
|
+
error: cancelled ? "web research was cancelled by the user." : formatErrorMessage(error)
|
|
10667
|
+
};
|
|
10668
|
+
}
|
|
10669
|
+
function elapsedMs(startedAt, completedAt) {
|
|
10670
|
+
return Math.max(0, Date.parse(completedAt) - Date.parse(startedAt));
|
|
10671
|
+
}
|
|
10672
|
+
function getActiveWebResearchRequests(tasks) {
|
|
10673
|
+
return [...tasks.values()].map((task) => task.request);
|
|
10674
|
+
}
|
|
10675
|
+
function getWebResearchTaskSnapshots(tasks) {
|
|
10676
|
+
return [...tasks.values()].map((task) => ({
|
|
10677
|
+
request: task.request,
|
|
10678
|
+
cancelRequestedAt: task.cancelRequestedAt
|
|
10679
|
+
}));
|
|
10680
|
+
}
|
|
10681
|
+
function cancelWebResearchTask(tasks, id) {
|
|
10682
|
+
const task = tasks.get(id);
|
|
10683
|
+
if (!task || task.abortController.signal.aborted) return false;
|
|
10684
|
+
task.cancelRequestedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10685
|
+
task.request.progress = "cancelling";
|
|
10686
|
+
task.abortController.abort(
|
|
10687
|
+
new Error("web research was cancelled by the user.")
|
|
10688
|
+
);
|
|
10689
|
+
return true;
|
|
10690
|
+
}
|
|
10691
|
+
function createWebResearchRequest(cwd, provider, input) {
|
|
10692
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10693
|
+
return {
|
|
10694
|
+
tool: "web_research",
|
|
10695
|
+
id: randomUUID(),
|
|
10696
|
+
provider,
|
|
10697
|
+
input,
|
|
10698
|
+
outputPath: buildWebResearchArtifactPath(cwd, input, startedAt),
|
|
10699
|
+
startedAt
|
|
10700
|
+
};
|
|
10701
|
+
}
|
|
10702
|
+
function buildWebResearchArtifactPath(cwd, input, startedAt) {
|
|
10703
|
+
const timestamp = startedAt.replaceAll(":", "-").replace(".", "-");
|
|
10704
|
+
const slug = slugifyWebResearchInput(input);
|
|
10705
|
+
return join2(cwd, RESEARCH_ARTIFACTS_DIR, `${timestamp}-${slug}.md`);
|
|
10706
|
+
}
|
|
10707
|
+
function slugifyWebResearchInput(input) {
|
|
10708
|
+
const slug = input.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
10709
|
+
return slug.length > 0 ? slug : "research";
|
|
10710
|
+
}
|
|
10711
|
+
function getWebResearchProgressIcon(request) {
|
|
10712
|
+
if (request.progress === "poll retrying after transient errors") {
|
|
10713
|
+
return "\u27F3";
|
|
10714
|
+
}
|
|
10715
|
+
if (request.progress === "queued" || request.progress === "cancelling") {
|
|
10716
|
+
return "\u25CC";
|
|
10717
|
+
}
|
|
10718
|
+
if (request.progress === "starting") {
|
|
10719
|
+
return "\u25D4";
|
|
10720
|
+
}
|
|
10721
|
+
if (request.progress?.startsWith("started:")) {
|
|
10722
|
+
return "\u25D1";
|
|
10723
|
+
}
|
|
10724
|
+
return "\u25CF";
|
|
10725
|
+
}
|
|
10726
|
+
function summarizeWebResearchProgress(message, providerLabel2) {
|
|
10727
|
+
const startingMessage = `Starting research via ${providerLabel2}`;
|
|
10728
|
+
if (message === startingMessage) {
|
|
10729
|
+
return "starting";
|
|
10730
|
+
}
|
|
10731
|
+
const startedPrefix = `${providerLabel2} research started: `;
|
|
10732
|
+
if (message.startsWith(startedPrefix)) {
|
|
10733
|
+
return `started: ${message.slice(startedPrefix.length)}`;
|
|
10734
|
+
}
|
|
10735
|
+
const statusPrefix = `Research via ${providerLabel2}: `;
|
|
10736
|
+
if (message.startsWith(statusPrefix)) {
|
|
10737
|
+
return message.slice(statusPrefix.length).replace(/\s+\([^)]* elapsed\)$/u, "").trim();
|
|
10738
|
+
}
|
|
10739
|
+
const retryPrefix = `${providerLabel2} research poll is still retrying after transient errors`;
|
|
10740
|
+
if (message.startsWith(retryPrefix)) {
|
|
10741
|
+
return "poll retrying after transient errors";
|
|
10742
|
+
}
|
|
10743
|
+
return message.trim();
|
|
10744
|
+
}
|
|
10745
|
+
function formatWebResearchResultMessage(result, reportText) {
|
|
10746
|
+
const text = reportText.trim();
|
|
10747
|
+
if (text.length > 0) {
|
|
10748
|
+
return `${text}
|
|
10749
|
+
`;
|
|
10750
|
+
}
|
|
10751
|
+
if (result.error) {
|
|
10752
|
+
return `${result.error}
|
|
10753
|
+
`;
|
|
10754
|
+
}
|
|
10755
|
+
return "";
|
|
10756
|
+
}
|
|
10757
|
+
async function writeWebResearchArtifact(result, reportText) {
|
|
10758
|
+
await mkdir2(dirname2(result.outputPath), { recursive: true });
|
|
10759
|
+
await writeFile2(
|
|
10760
|
+
result.outputPath,
|
|
10761
|
+
formatWebResearchArtifact(result, reportText),
|
|
10762
|
+
"utf-8"
|
|
10763
|
+
);
|
|
10764
|
+
}
|
|
10765
|
+
function formatWebResearchArtifact(result, reportText) {
|
|
10766
|
+
const providerLabel2 = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
10767
|
+
const metadata = {
|
|
10768
|
+
query: result.input,
|
|
10769
|
+
provider: providerLabel2,
|
|
10770
|
+
providerId: result.provider,
|
|
10771
|
+
status: result.status,
|
|
10772
|
+
startedAt: result.startedAt,
|
|
10773
|
+
completedAt: result.completedAt,
|
|
10774
|
+
elapsedMs: result.elapsedMs,
|
|
10775
|
+
itemCount: result.itemCount,
|
|
10776
|
+
error: result.error
|
|
10777
|
+
};
|
|
10778
|
+
const lines = [
|
|
10779
|
+
"---",
|
|
10780
|
+
...Object.entries(metadata).flatMap(
|
|
10781
|
+
([key, value]) => value === void 0 ? [] : [`${key}: ${formatYamlScalar(value)}`]
|
|
10782
|
+
),
|
|
10783
|
+
"---",
|
|
10784
|
+
"",
|
|
10785
|
+
"# Web research report"
|
|
10786
|
+
];
|
|
10787
|
+
if (reportText) {
|
|
10788
|
+
lines.push("", reportText);
|
|
10789
|
+
}
|
|
10790
|
+
return `${lines.join("\n")}
|
|
10791
|
+
`;
|
|
10792
|
+
}
|
|
10793
|
+
function formatYamlScalar(value) {
|
|
10794
|
+
if (typeof value === "number") {
|
|
10795
|
+
return String(value);
|
|
10796
|
+
}
|
|
10797
|
+
return JSON.stringify(value);
|
|
10798
|
+
}
|
|
10799
|
+
async function loadWebResearchHistory(cwd, maxItems = MAX_RESEARCH_HISTORY_ITEMS) {
|
|
10800
|
+
const dir = join2(cwd, RESEARCH_ARTIFACTS_DIR);
|
|
10801
|
+
let entries;
|
|
10802
|
+
try {
|
|
10803
|
+
entries = await readdir(dir);
|
|
10804
|
+
} catch {
|
|
10805
|
+
return [];
|
|
10806
|
+
}
|
|
10807
|
+
const markdown = entries.filter((name) => name.endsWith(".md"));
|
|
10808
|
+
const withStats = await Promise.all(
|
|
10809
|
+
markdown.map(async (fileName) => {
|
|
10810
|
+
const outputPath = join2(dir, fileName);
|
|
10811
|
+
try {
|
|
10812
|
+
return { fileName, outputPath, stat: await stat(outputPath) };
|
|
10813
|
+
} catch {
|
|
10814
|
+
return void 0;
|
|
10815
|
+
}
|
|
10816
|
+
})
|
|
10817
|
+
);
|
|
10818
|
+
const newest = withStats.filter((item) => item !== void 0).sort((left, right) => right.stat.mtimeMs - left.stat.mtimeMs).slice(0, maxItems);
|
|
10819
|
+
return Promise.all(
|
|
10820
|
+
newest.map(async ({ fileName, outputPath, stat: stat2 }) => {
|
|
10821
|
+
let content = "";
|
|
10822
|
+
try {
|
|
10823
|
+
content = await readFile2(outputPath, "utf-8");
|
|
10824
|
+
} catch {
|
|
10825
|
+
}
|
|
10826
|
+
const metadata = parseWebResearchArtifactMetadata(content);
|
|
10827
|
+
return {
|
|
10828
|
+
outputPath,
|
|
10829
|
+
fileName,
|
|
10830
|
+
query: metadata.query ?? "",
|
|
10831
|
+
title: deriveWebResearchTitle(content, metadata.query ?? ""),
|
|
10832
|
+
provider: metadata.provider ?? "",
|
|
10833
|
+
status: metadata.status ?? "unknown",
|
|
10834
|
+
startedAt: metadata.startedAt ?? "",
|
|
10835
|
+
completedAt: metadata.completedAt ?? "",
|
|
10836
|
+
elapsedMs: computeHistoryElapsedMs(metadata),
|
|
10837
|
+
mtimeMs: stat2.mtimeMs
|
|
10838
|
+
};
|
|
10839
|
+
})
|
|
10840
|
+
);
|
|
10841
|
+
}
|
|
10842
|
+
function parseWebResearchArtifactMetadata(content) {
|
|
10843
|
+
return parseWebResearchFrontmatter(content) ?? parseLegacyWebResearchArtifactMetadata(content);
|
|
10844
|
+
}
|
|
10845
|
+
function parseWebResearchFrontmatter(content) {
|
|
10846
|
+
if (!content.startsWith("---\n")) {
|
|
10847
|
+
return void 0;
|
|
10848
|
+
}
|
|
10849
|
+
const end = content.indexOf("\n---", 4);
|
|
10850
|
+
if (end === -1) {
|
|
10851
|
+
return void 0;
|
|
10852
|
+
}
|
|
10853
|
+
const result = {};
|
|
10854
|
+
const frontmatter = content.slice(4, end);
|
|
10855
|
+
for (const line of frontmatter.split(/\r?\n/u)) {
|
|
10856
|
+
const match = /^([A-Za-z][A-Za-z0-9_-]*):\s*(.*)$/u.exec(line);
|
|
10857
|
+
if (!match) {
|
|
10858
|
+
continue;
|
|
10859
|
+
}
|
|
10860
|
+
result[match[1] ?? ""] = parseYamlScalar(match[2] ?? "");
|
|
10861
|
+
}
|
|
10862
|
+
return result;
|
|
10863
|
+
}
|
|
10864
|
+
function parseYamlScalar(value) {
|
|
10865
|
+
const trimmed = value.trim();
|
|
10866
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
10867
|
+
try {
|
|
10868
|
+
const parsed = JSON.parse(trimmed);
|
|
10869
|
+
return typeof parsed === "string" ? parsed : String(parsed);
|
|
10870
|
+
} catch {
|
|
10871
|
+
}
|
|
10872
|
+
}
|
|
10873
|
+
return trimmed;
|
|
10874
|
+
}
|
|
10875
|
+
function parseLegacyWebResearchArtifactMetadata(content) {
|
|
10876
|
+
const result = {};
|
|
10877
|
+
const metadataHeadings = /* @__PURE__ */ new Map([
|
|
10878
|
+
["Query", "query"],
|
|
10879
|
+
["Provider", "provider"],
|
|
10880
|
+
["Status", "status"],
|
|
10881
|
+
["Started", "startedAt"],
|
|
10882
|
+
["Completed", "completedAt"]
|
|
10883
|
+
]);
|
|
10884
|
+
const artifactBodyHeadings = /* @__PURE__ */ new Set(["Elapsed", "Items", "Error", "Report"]);
|
|
10885
|
+
const lines = content.split(/\r?\n/u);
|
|
10886
|
+
for (let index = 0; index < lines.length; index++) {
|
|
10887
|
+
const match = /^##\s+(.+)$/u.exec(lines[index] ?? "");
|
|
10888
|
+
if (!match) continue;
|
|
10889
|
+
const heading = match[1] ?? "";
|
|
10890
|
+
if (artifactBodyHeadings.has(heading)) break;
|
|
10891
|
+
const key = metadataHeadings.get(heading);
|
|
10892
|
+
if (key === void 0 || result[key] !== void 0) continue;
|
|
10893
|
+
const values = [];
|
|
10894
|
+
for (let next = index + 1; next < lines.length; next++) {
|
|
10895
|
+
if (/^##\s+/u.test(lines[next] ?? "")) break;
|
|
10896
|
+
if ((lines[next] ?? "").trim() || values.length > 0)
|
|
10897
|
+
values.push(lines[next] ?? "");
|
|
10898
|
+
}
|
|
10899
|
+
result[key] = values.join("\n").trim();
|
|
10900
|
+
}
|
|
10901
|
+
return result;
|
|
10902
|
+
}
|
|
10903
|
+
var RESEARCH_TITLE_MAX_LENGTH = 80;
|
|
10904
|
+
var NON_TITLE_HEADINGS = /* @__PURE__ */ new Set([
|
|
10905
|
+
"Web research report",
|
|
10906
|
+
"Query",
|
|
10907
|
+
"Provider",
|
|
10908
|
+
"Status",
|
|
10909
|
+
"Started",
|
|
10910
|
+
"Completed",
|
|
10911
|
+
"Elapsed",
|
|
10912
|
+
"Items",
|
|
10913
|
+
"Error",
|
|
10914
|
+
"Report"
|
|
10915
|
+
]);
|
|
10916
|
+
function deriveWebResearchTitle(content, query2) {
|
|
10917
|
+
const body = getWebResearchBody(content);
|
|
10918
|
+
for (const line of body.split(/\r?\n/u)) {
|
|
10919
|
+
const match = /^#{1,2}\s+(.+?)\s*$/u.exec(line);
|
|
10920
|
+
if (!match) continue;
|
|
10921
|
+
const heading = (match[1] ?? "").trim();
|
|
10922
|
+
if (heading.length > 0 && !NON_TITLE_HEADINGS.has(heading)) {
|
|
10923
|
+
return heading;
|
|
10924
|
+
}
|
|
10925
|
+
}
|
|
10926
|
+
const fallback = query2.replace(/\s+/g, " ").trim();
|
|
10927
|
+
if (fallback.length <= RESEARCH_TITLE_MAX_LENGTH) {
|
|
10928
|
+
return fallback;
|
|
10929
|
+
}
|
|
10930
|
+
return `${fallback.slice(0, RESEARCH_TITLE_MAX_LENGTH - 1)}\u2026`;
|
|
10931
|
+
}
|
|
10932
|
+
function computeHistoryElapsedMs(metadata) {
|
|
10933
|
+
if (metadata.elapsedMs !== void 0) {
|
|
10934
|
+
const parsed = Number(metadata.elapsedMs);
|
|
10935
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
10936
|
+
return parsed;
|
|
10937
|
+
}
|
|
10938
|
+
}
|
|
10939
|
+
const started = Date.parse(metadata.startedAt ?? "");
|
|
10940
|
+
const completed = Date.parse(metadata.completedAt ?? "");
|
|
10941
|
+
if (Number.isFinite(started) && Number.isFinite(completed)) {
|
|
10942
|
+
return Math.max(0, completed - started);
|
|
10943
|
+
}
|
|
10944
|
+
return void 0;
|
|
10945
|
+
}
|
|
10946
|
+
function getWebResearchBody(content) {
|
|
10947
|
+
if (!content.startsWith("---\n")) {
|
|
10948
|
+
return content;
|
|
10949
|
+
}
|
|
10950
|
+
const end = content.indexOf("\n---", 4);
|
|
10951
|
+
if (end === -1) {
|
|
10952
|
+
return content;
|
|
10953
|
+
}
|
|
10954
|
+
const afterClose = content.indexOf("\n", end + 4);
|
|
10955
|
+
return afterClose === -1 ? "" : content.slice(afterClose + 1);
|
|
10956
|
+
}
|
|
10957
|
+
async function loadWebResearchReport(outputPath) {
|
|
10958
|
+
const buffer = await readFile2(outputPath);
|
|
10959
|
+
const truncated = buffer.byteLength > RESEARCH_REPORT_MAX_BYTES;
|
|
10960
|
+
const content = buffer.subarray(0, RESEARCH_REPORT_MAX_BYTES).toString("utf-8");
|
|
10961
|
+
return {
|
|
10962
|
+
body: getWebResearchBody(content).trim(),
|
|
10963
|
+
metadata: parseWebResearchArtifactMetadata(content),
|
|
10964
|
+
truncated
|
|
10965
|
+
};
|
|
10966
|
+
}
|
|
10967
|
+
async function loadWebResearchPreview(outputPath) {
|
|
10968
|
+
const buffer = await readFile2(outputPath);
|
|
10969
|
+
const truncated = buffer.byteLength > RESEARCH_PREVIEW_MAX_BYTES;
|
|
10970
|
+
const text = buffer.subarray(0, RESEARCH_PREVIEW_MAX_BYTES).toString("utf-8");
|
|
10971
|
+
return truncated ? `${text}
|
|
10972
|
+
|
|
10973
|
+
---
|
|
10974
|
+
Preview truncated. Open the full report at \`${outputPath}\`.` : `${text}
|
|
10975
|
+
|
|
10976
|
+
---
|
|
10977
|
+
Full report: \`${outputPath}\``;
|
|
10978
|
+
}
|
|
10979
|
+
function trackPendingResearchTask(task) {
|
|
10980
|
+
const tracked = task.catch(() => {
|
|
10981
|
+
}).finally(() => {
|
|
10982
|
+
pendingResearchTasks.delete(tracked);
|
|
10983
|
+
});
|
|
10984
|
+
pendingResearchTasks.add(tracked);
|
|
10985
|
+
}
|
|
10986
|
+
async function waitForPendingResearchTasks() {
|
|
10987
|
+
await Promise.all([...pendingResearchTasks]);
|
|
10988
|
+
}
|
|
10989
|
+
|
|
10990
|
+
// src/web-research-view.ts
|
|
10991
|
+
import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
|
|
10992
|
+
import {
|
|
10993
|
+
getKeybindings,
|
|
10994
|
+
Key,
|
|
10995
|
+
Markdown,
|
|
10996
|
+
matchesKey,
|
|
10997
|
+
truncateToWidth,
|
|
10998
|
+
visibleWidth
|
|
10999
|
+
} from "@earendil-works/pi-tui";
|
|
11000
|
+
var STATUS_MESSAGE_TTL_MS = 1500;
|
|
11001
|
+
var PAGE_JUMP = 10;
|
|
11002
|
+
var WebResearchManagerView = class {
|
|
11003
|
+
constructor(tui, theme, done, cwd, tasks, onChange, actions, loadHistory = loadWebResearchHistory) {
|
|
11004
|
+
this.tui = tui;
|
|
11005
|
+
this.theme = theme;
|
|
11006
|
+
this.done = done;
|
|
11007
|
+
this.cwd = cwd;
|
|
11008
|
+
this.tasks = tasks;
|
|
11009
|
+
this.onChange = onChange;
|
|
11010
|
+
this.actions = actions;
|
|
11011
|
+
this.loadHistory = loadHistory;
|
|
11012
|
+
void this.reloadHistory();
|
|
11013
|
+
}
|
|
11014
|
+
tui;
|
|
11015
|
+
theme;
|
|
11016
|
+
done;
|
|
11017
|
+
cwd;
|
|
11018
|
+
tasks;
|
|
11019
|
+
onChange;
|
|
11020
|
+
actions;
|
|
11021
|
+
loadHistory;
|
|
11022
|
+
selectedIndex = 0;
|
|
11023
|
+
history = [];
|
|
11024
|
+
confirmCancelId;
|
|
11025
|
+
open;
|
|
11026
|
+
scrollOffset = 0;
|
|
11027
|
+
statusMessage;
|
|
11028
|
+
statusMessageTimer;
|
|
11029
|
+
isReportOpen() {
|
|
11030
|
+
return this.open !== void 0;
|
|
11031
|
+
}
|
|
11032
|
+
render(width) {
|
|
11033
|
+
if (this.open?.kind === "report") {
|
|
11034
|
+
return this.renderReport(this.open, width);
|
|
11035
|
+
}
|
|
11036
|
+
if (this.open?.kind === "detail") {
|
|
11037
|
+
const snapshot = this.findSnapshot(this.open.taskId);
|
|
11038
|
+
if (snapshot) return this.renderDetail(snapshot, width);
|
|
11039
|
+
this.open = void 0;
|
|
11040
|
+
}
|
|
11041
|
+
return this.renderTable(width);
|
|
11042
|
+
}
|
|
11043
|
+
invalidate() {
|
|
11044
|
+
}
|
|
11045
|
+
dispose() {
|
|
11046
|
+
if (this.statusMessageTimer) clearTimeout(this.statusMessageTimer);
|
|
11047
|
+
}
|
|
11048
|
+
handleInput(data) {
|
|
11049
|
+
if (this.open) {
|
|
11050
|
+
this.handleOpenInput(data);
|
|
11051
|
+
} else {
|
|
11052
|
+
this.handleTableInput(data);
|
|
11053
|
+
}
|
|
11054
|
+
this.tui.requestRender();
|
|
11055
|
+
}
|
|
11056
|
+
refresh() {
|
|
11057
|
+
if (this.open?.kind === "detail" && !this.findSnapshot(this.open.taskId)) {
|
|
11058
|
+
this.open = void 0;
|
|
11059
|
+
this.confirmCancelId = void 0;
|
|
11060
|
+
}
|
|
11061
|
+
void this.reloadHistory();
|
|
11062
|
+
this.tui.requestRender();
|
|
11063
|
+
}
|
|
11064
|
+
// --- table mode ---
|
|
11065
|
+
getRows() {
|
|
11066
|
+
const snapshots = getWebResearchTaskSnapshots(this.tasks).sort(
|
|
11067
|
+
(a, b) => a.request.startedAt.localeCompare(b.request.startedAt)
|
|
11068
|
+
);
|
|
11069
|
+
const runningPaths = new Set(
|
|
11070
|
+
snapshots.map((snapshot) => snapshot.request.outputPath)
|
|
11071
|
+
);
|
|
11072
|
+
const rows = snapshots.map((snapshot) => ({
|
|
11073
|
+
kind: "running",
|
|
11074
|
+
snapshot
|
|
11075
|
+
}));
|
|
11076
|
+
for (const item of this.history) {
|
|
11077
|
+
if (runningPaths.has(item.outputPath)) continue;
|
|
11078
|
+
rows.push({ kind: "history", item });
|
|
11079
|
+
}
|
|
11080
|
+
return rows;
|
|
11081
|
+
}
|
|
11082
|
+
border(width) {
|
|
11083
|
+
return this.theme.fg("border", "\u2500".repeat(Math.max(1, width)));
|
|
11084
|
+
}
|
|
11085
|
+
renderTable(width) {
|
|
11086
|
+
const rows = this.getRows();
|
|
11087
|
+
this.selectedIndex = clamp2(this.selectedIndex, 0, rows.length - 1);
|
|
11088
|
+
const lines = [
|
|
11089
|
+
this.border(width),
|
|
11090
|
+
this.theme.fg("accent", " Web research"),
|
|
11091
|
+
""
|
|
11092
|
+
];
|
|
11093
|
+
if (rows.length === 0) {
|
|
11094
|
+
lines.push(this.theme.fg("muted", " No research jobs or reports"));
|
|
11095
|
+
} else {
|
|
11096
|
+
const layout = computeTableLayout(rows, width);
|
|
11097
|
+
const now = Date.now();
|
|
11098
|
+
rows.forEach((row, index) => {
|
|
11099
|
+
lines.push(
|
|
11100
|
+
formatResearchTableRow(
|
|
11101
|
+
row,
|
|
11102
|
+
layout,
|
|
11103
|
+
this.theme,
|
|
11104
|
+
index === this.selectedIndex,
|
|
11105
|
+
now
|
|
11106
|
+
)
|
|
11107
|
+
);
|
|
11108
|
+
if (row.kind === "running" && this.confirmCancelId === row.snapshot.request.id) {
|
|
11109
|
+
lines.push(
|
|
11110
|
+
this.theme.fg(
|
|
11111
|
+
"warning",
|
|
11112
|
+
truncateToWidth(
|
|
11113
|
+
` Press c again to cancel this ${providerLabel(row.snapshot.request.provider)} research`,
|
|
11114
|
+
width
|
|
11115
|
+
)
|
|
11116
|
+
)
|
|
11117
|
+
);
|
|
11118
|
+
}
|
|
11119
|
+
});
|
|
11120
|
+
}
|
|
11121
|
+
lines.push("");
|
|
11122
|
+
if (this.statusMessage) {
|
|
11123
|
+
lines.push(this.theme.fg("accent", ` ${this.statusMessage}`));
|
|
11124
|
+
}
|
|
11125
|
+
lines.push(
|
|
11126
|
+
this.theme.fg(
|
|
11127
|
+
"dim",
|
|
11128
|
+
" \u2191\u2193 move \xB7 Enter open \xB7 c cancel running \xB7 Esc close"
|
|
11129
|
+
),
|
|
11130
|
+
this.border(width)
|
|
11131
|
+
);
|
|
11132
|
+
return lines;
|
|
11133
|
+
}
|
|
11134
|
+
handleTableInput(data) {
|
|
11135
|
+
const kb = getKeybindings();
|
|
11136
|
+
const rows = this.getRows();
|
|
11137
|
+
if (kb.matches(data, "tui.select.up")) this.move(rows.length, -1);
|
|
11138
|
+
else if (kb.matches(data, "tui.select.down")) this.move(rows.length, 1);
|
|
11139
|
+
else if (kb.matches(data, "tui.select.pageUp"))
|
|
11140
|
+
this.move(rows.length, -PAGE_JUMP, false);
|
|
11141
|
+
else if (kb.matches(data, "tui.select.pageDown"))
|
|
11142
|
+
this.move(rows.length, PAGE_JUMP, false);
|
|
11143
|
+
else if (kb.matches(data, "tui.select.confirm"))
|
|
11144
|
+
void this.openRow(rows[this.selectedIndex]);
|
|
11145
|
+
else if (data === "c") this.cancelRow(rows[this.selectedIndex]);
|
|
11146
|
+
else if (kb.matches(data, "tui.select.cancel")) {
|
|
11147
|
+
if (this.confirmCancelId) this.confirmCancelId = void 0;
|
|
11148
|
+
else this.done(void 0);
|
|
11149
|
+
}
|
|
11150
|
+
}
|
|
11151
|
+
move(count, delta, wrap = true) {
|
|
11152
|
+
this.confirmCancelId = void 0;
|
|
11153
|
+
if (count === 0) return;
|
|
11154
|
+
const next = this.selectedIndex + delta;
|
|
11155
|
+
this.selectedIndex = wrap ? (next + count) % count : clamp2(next, 0, count - 1);
|
|
11156
|
+
}
|
|
11157
|
+
async openRow(row) {
|
|
11158
|
+
if (!row) return;
|
|
11159
|
+
this.confirmCancelId = void 0;
|
|
11160
|
+
if (row.kind === "running") {
|
|
11161
|
+
this.open = { kind: "detail", taskId: row.snapshot.request.id };
|
|
11162
|
+
this.scrollOffset = 0;
|
|
11163
|
+
return;
|
|
11164
|
+
}
|
|
11165
|
+
try {
|
|
11166
|
+
const report = await loadWebResearchReport(row.item.outputPath);
|
|
11167
|
+
this.open = {
|
|
11168
|
+
kind: "report",
|
|
11169
|
+
title: row.item.title || row.item.query,
|
|
11170
|
+
body: report.body,
|
|
11171
|
+
truncated: report.truncated,
|
|
11172
|
+
item: row.item,
|
|
11173
|
+
markdown: new Markdown(report.body, 1, 0, getMarkdownTheme())
|
|
11174
|
+
};
|
|
11175
|
+
this.scrollOffset = 0;
|
|
11176
|
+
} catch (error) {
|
|
11177
|
+
this.showStatusMessage(
|
|
11178
|
+
`Failed to read ${row.item.outputPath}: ${formatErrorMessage(error)}`
|
|
11179
|
+
);
|
|
11180
|
+
}
|
|
11181
|
+
this.tui.requestRender();
|
|
11182
|
+
}
|
|
11183
|
+
cancelRow(row) {
|
|
11184
|
+
if (!row || row.kind !== "running") return;
|
|
11185
|
+
const id = row.snapshot.request.id;
|
|
11186
|
+
if (this.confirmCancelId !== id) {
|
|
11187
|
+
this.confirmCancelId = id;
|
|
11188
|
+
return;
|
|
11189
|
+
}
|
|
11190
|
+
cancelWebResearchTask(this.tasks, id);
|
|
11191
|
+
this.confirmCancelId = void 0;
|
|
11192
|
+
this.onChange();
|
|
11193
|
+
}
|
|
11194
|
+
// --- report / detail mode ---
|
|
11195
|
+
renderReport(open, width) {
|
|
11196
|
+
const lines = [
|
|
11197
|
+
this.border(width),
|
|
11198
|
+
this.theme.fg("accent", truncateToWidth(` ${open.title}`, width)),
|
|
11199
|
+
this.theme.fg(
|
|
11200
|
+
"dim",
|
|
11201
|
+
" c copy markdown \xB7 i inject into context \xB7 \u2191\u2193/PgUp/PgDn scroll \xB7 Esc back"
|
|
11202
|
+
),
|
|
11203
|
+
this.border(width),
|
|
11204
|
+
""
|
|
11205
|
+
];
|
|
11206
|
+
let body;
|
|
11207
|
+
try {
|
|
11208
|
+
body = open.markdown.render(Math.max(20, width - 2));
|
|
11209
|
+
} catch {
|
|
11210
|
+
body = open.body.split("\n").map((line) => truncateToWidth(line, width));
|
|
11211
|
+
}
|
|
11212
|
+
const viewport = this.viewportHeight();
|
|
11213
|
+
const maxOffset = Math.max(0, body.length - viewport);
|
|
11214
|
+
this.scrollOffset = clamp2(this.scrollOffset, 0, maxOffset);
|
|
11215
|
+
lines.push(...body.slice(this.scrollOffset, this.scrollOffset + viewport));
|
|
11216
|
+
lines.push("");
|
|
11217
|
+
const footer = [];
|
|
11218
|
+
if (body.length > viewport) {
|
|
11219
|
+
const end = Math.min(body.length, this.scrollOffset + viewport);
|
|
11220
|
+
footer.push(`lines ${this.scrollOffset + 1}\u2013${end} of ${body.length}`);
|
|
11221
|
+
}
|
|
11222
|
+
if (open.truncated) {
|
|
11223
|
+
footer.push(`report truncated \xB7 full text: ${open.item.outputPath}`);
|
|
11224
|
+
}
|
|
11225
|
+
if (this.statusMessage) {
|
|
11226
|
+
lines.push(this.theme.fg("accent", ` ${this.statusMessage}`));
|
|
11227
|
+
}
|
|
11228
|
+
if (footer.length > 0) {
|
|
11229
|
+
lines.push(
|
|
11230
|
+
this.theme.fg("dim", truncateToWidth(` ${footer.join(" \xB7 ")}`, width))
|
|
11231
|
+
);
|
|
11232
|
+
}
|
|
11233
|
+
lines.push(this.border(width));
|
|
11234
|
+
return lines;
|
|
11235
|
+
}
|
|
11236
|
+
renderDetail(snapshot, width) {
|
|
11237
|
+
const request = snapshot.request;
|
|
11238
|
+
const elapsed = formatCompactElapsed(
|
|
11239
|
+
Date.now() - Date.parse(request.startedAt)
|
|
11240
|
+
);
|
|
11241
|
+
const progress = snapshot.cancelRequestedAt ? "cancelling" : request.progress ?? "running";
|
|
11242
|
+
const lines = [
|
|
11243
|
+
this.border(width),
|
|
11244
|
+
this.theme.fg(
|
|
11245
|
+
"accent",
|
|
11246
|
+
truncateToWidth(
|
|
11247
|
+
` Running research via ${providerLabel(request.provider)}`,
|
|
11248
|
+
width
|
|
11249
|
+
)
|
|
11250
|
+
),
|
|
11251
|
+
this.theme.fg("dim", " c cancel \xB7 Esc back"),
|
|
11252
|
+
this.border(width),
|
|
11253
|
+
"",
|
|
11254
|
+
truncateToWidth(` Status: ${progress} (${elapsed} elapsed)`, width),
|
|
11255
|
+
truncateToWidth(` Report path: ${request.outputPath}`, width),
|
|
11256
|
+
"",
|
|
11257
|
+
this.theme.fg("accent", " Research brief"),
|
|
11258
|
+
""
|
|
11259
|
+
];
|
|
11260
|
+
for (const line of request.input.split("\n").slice(0, 100)) {
|
|
11261
|
+
lines.push(truncateToWidth(` ${line}`, width));
|
|
11262
|
+
}
|
|
11263
|
+
if (this.confirmCancelId === request.id) {
|
|
11264
|
+
lines.push(
|
|
11265
|
+
"",
|
|
11266
|
+
this.theme.fg("warning", " Press c again to cancel this research")
|
|
11267
|
+
);
|
|
11268
|
+
}
|
|
11269
|
+
if (this.statusMessage) {
|
|
11270
|
+
lines.push("", this.theme.fg("accent", ` ${this.statusMessage}`));
|
|
11271
|
+
}
|
|
11272
|
+
lines.push(this.border(width));
|
|
11273
|
+
return lines;
|
|
11274
|
+
}
|
|
11275
|
+
handleOpenInput(data) {
|
|
11276
|
+
const kb = getKeybindings();
|
|
11277
|
+
const open = this.open;
|
|
11278
|
+
if (!open) return;
|
|
11279
|
+
if (kb.matches(data, "tui.select.cancel")) {
|
|
11280
|
+
if (this.confirmCancelId) {
|
|
11281
|
+
this.confirmCancelId = void 0;
|
|
11282
|
+
return;
|
|
11283
|
+
}
|
|
11284
|
+
this.open = void 0;
|
|
11285
|
+
this.scrollOffset = 0;
|
|
11286
|
+
return;
|
|
11287
|
+
}
|
|
11288
|
+
if (open.kind === "detail") {
|
|
11289
|
+
if (data === "c") {
|
|
11290
|
+
const snapshot = this.findSnapshot(open.taskId);
|
|
11291
|
+
if (!snapshot) return;
|
|
11292
|
+
if (this.confirmCancelId !== open.taskId) {
|
|
11293
|
+
this.confirmCancelId = open.taskId;
|
|
11294
|
+
return;
|
|
11295
|
+
}
|
|
11296
|
+
cancelWebResearchTask(this.tasks, open.taskId);
|
|
11297
|
+
this.confirmCancelId = void 0;
|
|
11298
|
+
this.onChange();
|
|
11299
|
+
this.showStatusMessage("Cancellation requested");
|
|
11300
|
+
}
|
|
11301
|
+
return;
|
|
11302
|
+
}
|
|
11303
|
+
if (kb.matches(data, "tui.select.up")) this.scrollOffset -= 1;
|
|
11304
|
+
else if (kb.matches(data, "tui.select.down")) this.scrollOffset += 1;
|
|
11305
|
+
else if (kb.matches(data, "tui.select.pageUp"))
|
|
11306
|
+
this.scrollOffset -= this.viewportHeight();
|
|
11307
|
+
else if (kb.matches(data, "tui.select.pageDown"))
|
|
11308
|
+
this.scrollOffset += this.viewportHeight();
|
|
11309
|
+
else if (matchesKey(data, Key.home)) this.scrollOffset = 0;
|
|
11310
|
+
else if (matchesKey(data, Key.end))
|
|
11311
|
+
this.scrollOffset = Number.MAX_SAFE_INTEGER;
|
|
11312
|
+
else if (data === "c") {
|
|
11313
|
+
void this.actions.copyToClipboard(open.body).then(() => this.showStatusMessage("Copied report to clipboard")).catch((error) => {
|
|
11314
|
+
const message = `Copy failed: ${formatErrorMessage(error)}`;
|
|
11315
|
+
this.showStatusMessage(message);
|
|
11316
|
+
this.actions.notify(message, "error");
|
|
11317
|
+
});
|
|
11318
|
+
} else if (data === "i") {
|
|
11319
|
+
this.actions.injectReport({
|
|
11320
|
+
title: open.title,
|
|
11321
|
+
body: open.body,
|
|
11322
|
+
item: open.item
|
|
11323
|
+
});
|
|
11324
|
+
this.showStatusMessage("Report added to conversation context");
|
|
11325
|
+
}
|
|
11326
|
+
}
|
|
11327
|
+
viewportHeight() {
|
|
11328
|
+
return Math.max(5, Math.floor(this.tui.terminal.rows * 0.85) - 6);
|
|
11329
|
+
}
|
|
11330
|
+
showStatusMessage(message) {
|
|
11331
|
+
this.statusMessage = message;
|
|
11332
|
+
if (this.statusMessageTimer) clearTimeout(this.statusMessageTimer);
|
|
11333
|
+
this.statusMessageTimer = setTimeout(() => {
|
|
11334
|
+
this.statusMessage = void 0;
|
|
11335
|
+
this.statusMessageTimer = void 0;
|
|
11336
|
+
this.tui.requestRender();
|
|
11337
|
+
}, STATUS_MESSAGE_TTL_MS);
|
|
11338
|
+
}
|
|
11339
|
+
findSnapshot(taskId) {
|
|
11340
|
+
return getWebResearchTaskSnapshots(this.tasks).find(
|
|
11341
|
+
(snapshot) => snapshot.request.id === taskId
|
|
11342
|
+
);
|
|
11343
|
+
}
|
|
11344
|
+
async reloadHistory() {
|
|
11345
|
+
this.history = await this.loadHistory(this.cwd);
|
|
11346
|
+
this.tui.requestRender();
|
|
11347
|
+
}
|
|
11348
|
+
};
|
|
11349
|
+
function computeTableLayout(rows, width) {
|
|
11350
|
+
const providerWidth = clamp2(
|
|
11351
|
+
Math.max(0, ...rows.map((row) => visibleWidth(rowProvider(row)))),
|
|
11352
|
+
4,
|
|
11353
|
+
12
|
|
11354
|
+
);
|
|
11355
|
+
const date = 10;
|
|
11356
|
+
const duration = 7;
|
|
11357
|
+
const fixed = 2 + 2 + date + 1 + providerWidth + 1 + duration + 1;
|
|
11358
|
+
return {
|
|
11359
|
+
date,
|
|
11360
|
+
provider: providerWidth,
|
|
11361
|
+
duration,
|
|
11362
|
+
title: Math.max(10, width - fixed)
|
|
11363
|
+
};
|
|
11364
|
+
}
|
|
11365
|
+
function formatResearchTableRow(row, layout, theme, selected, now = Date.now()) {
|
|
11366
|
+
const cursor = selected ? "\u203A " : " ";
|
|
11367
|
+
const glyph = statusGlyph(row, theme);
|
|
11368
|
+
const date = padCell(
|
|
11369
|
+
formatRelativeDate(rowTimestampMs(row), now),
|
|
11370
|
+
layout.date
|
|
11371
|
+
);
|
|
11372
|
+
const provider = padCell(
|
|
11373
|
+
truncateToWidth(rowProvider(row), layout.provider),
|
|
11374
|
+
layout.provider
|
|
11375
|
+
);
|
|
11376
|
+
const duration = padCell(rowDuration(row, now), layout.duration, "right");
|
|
11377
|
+
const title = truncateToWidth(rowTitle(row), layout.title);
|
|
11378
|
+
const dim = (text) => row.kind === "history" ? text : theme.fg("dim", text);
|
|
11379
|
+
return `${cursor}${glyph} ${dim(date)} ${provider} ${theme.fg("muted", duration)} ${title}`;
|
|
11380
|
+
}
|
|
11381
|
+
function statusGlyph(row, theme) {
|
|
11382
|
+
if (row.kind === "running") {
|
|
11383
|
+
const icon = row.snapshot.cancelRequestedAt ? "\u25CC" : getWebResearchProgressIcon(row.snapshot.request);
|
|
11384
|
+
return theme.fg("accent", icon);
|
|
11385
|
+
}
|
|
11386
|
+
switch (row.item.status) {
|
|
11387
|
+
case "completed":
|
|
11388
|
+
return theme.fg("success", "\u2714");
|
|
11389
|
+
case "failed":
|
|
11390
|
+
return theme.fg("error", "\u2718");
|
|
11391
|
+
case "cancelled":
|
|
11392
|
+
return theme.fg("warning", "\u2718");
|
|
11393
|
+
default:
|
|
11394
|
+
return theme.fg("dim", "?");
|
|
11395
|
+
}
|
|
11396
|
+
}
|
|
11397
|
+
function rowProvider(row) {
|
|
11398
|
+
return row.kind === "running" ? providerLabel(row.snapshot.request.provider) : row.item.provider || "?";
|
|
11399
|
+
}
|
|
11400
|
+
function rowTimestampMs(row) {
|
|
11401
|
+
if (row.kind === "running") {
|
|
11402
|
+
return Date.parse(row.snapshot.request.startedAt);
|
|
11403
|
+
}
|
|
11404
|
+
const completed = Date.parse(row.item.completedAt);
|
|
11405
|
+
if (Number.isFinite(completed)) return completed;
|
|
11406
|
+
const started = Date.parse(row.item.startedAt);
|
|
11407
|
+
if (Number.isFinite(started)) return started;
|
|
11408
|
+
return row.item.mtimeMs;
|
|
11409
|
+
}
|
|
11410
|
+
function rowDuration(row, now) {
|
|
11411
|
+
if (row.kind === "running") {
|
|
11412
|
+
return formatCompactElapsed(
|
|
11413
|
+
now - Date.parse(row.snapshot.request.startedAt)
|
|
11414
|
+
);
|
|
11415
|
+
}
|
|
11416
|
+
return row.item.elapsedMs === void 0 ? "\u2014" : formatCompactElapsed(row.item.elapsedMs);
|
|
11417
|
+
}
|
|
11418
|
+
var REDUNDANT_PROGRESS = /* @__PURE__ */ new Set([
|
|
11419
|
+
"in_progress",
|
|
11420
|
+
"running",
|
|
11421
|
+
"starting",
|
|
11422
|
+
"queued",
|
|
11423
|
+
"cancelling"
|
|
11424
|
+
]);
|
|
11425
|
+
function rowTitle(row) {
|
|
11426
|
+
if (row.kind === "running") {
|
|
11427
|
+
const request = row.snapshot.request;
|
|
11428
|
+
const progress = row.snapshot.cancelRequestedAt ? void 0 : request.progress;
|
|
11429
|
+
const prefix = progress && !REDUNDANT_PROGRESS.has(progress) ? `${progress} \u2014 ` : "";
|
|
11430
|
+
return `${prefix}${cleanSingleLine(request.input)}`;
|
|
11431
|
+
}
|
|
11432
|
+
return row.item.title || cleanSingleLine(row.item.query);
|
|
11433
|
+
}
|
|
11434
|
+
function formatRelativeDate(timestampMs, now) {
|
|
11435
|
+
if (!Number.isFinite(timestampMs)) return "?";
|
|
11436
|
+
const diff = Math.max(0, now - timestampMs);
|
|
11437
|
+
if (diff < 6e4) return "now";
|
|
11438
|
+
if (diff < 36e5) return `${Math.floor(diff / 6e4)}m ago`;
|
|
11439
|
+
if (diff < 864e5) return `${Math.floor(diff / 36e5)}h ago`;
|
|
11440
|
+
if (diff < 7 * 864e5) return `${Math.floor(diff / 864e5)}d ago`;
|
|
11441
|
+
const date = new Date(timestampMs);
|
|
11442
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
11443
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
11444
|
+
if (date.getFullYear() === new Date(now).getFullYear()) {
|
|
11445
|
+
return `${month}-${day}`;
|
|
11446
|
+
}
|
|
11447
|
+
return `${date.getFullYear()}-${month}-${day}`;
|
|
11448
|
+
}
|
|
11449
|
+
function providerLabel(providerId) {
|
|
11450
|
+
return PROVIDERS_BY_ID[providerId]?.label ?? providerId;
|
|
11451
|
+
}
|
|
11452
|
+
function padCell(text, width, align = "left") {
|
|
11453
|
+
const pad = " ".repeat(Math.max(0, width - visibleWidth(text)));
|
|
11454
|
+
return align === "left" ? `${text}${pad}` : `${pad}${text}`;
|
|
11455
|
+
}
|
|
11456
|
+
function clamp2(value, min, max) {
|
|
11457
|
+
return Math.min(Math.max(value, min), Math.max(min, max));
|
|
11458
|
+
}
|
|
11459
|
+
function formatCompactElapsed(ms) {
|
|
11460
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
11461
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
11462
|
+
const seconds = totalSeconds % 60;
|
|
11463
|
+
if (minutes > 0) {
|
|
11464
|
+
return `${minutes}m${seconds}s`;
|
|
11465
|
+
}
|
|
11466
|
+
return `${totalSeconds}s`;
|
|
11467
|
+
}
|
|
11468
|
+
function cleanSingleLine(text) {
|
|
11469
|
+
return text.replace(/\s+/g, " ").trim();
|
|
11470
|
+
}
|
|
11471
|
+
|
|
11472
|
+
// src/index.ts
|
|
11473
|
+
var DEFAULT_MAX_RESULTS = 5;
|
|
11474
|
+
var MAX_ALLOWED_RESULTS = 20;
|
|
11475
|
+
var MAX_SEARCH_QUERIES = 10;
|
|
11476
|
+
var RESEARCH_HEARTBEAT_MS = 15e3;
|
|
11477
|
+
var WEB_RESEARCH_RESULT_MESSAGE_TYPE = "web-research-result";
|
|
11478
|
+
var WEB_RESEARCH_REPORT_MESSAGE_TYPE = "web-research-report";
|
|
11479
|
+
var WEB_RESEARCH_WIDGET_KEY = "web-research-jobs";
|
|
11480
|
+
var DEFAULT_SUMMARY_SYMBOLS = {
|
|
11481
|
+
success: "\u2714",
|
|
11482
|
+
failure: "\u2718"
|
|
11483
|
+
};
|
|
11484
|
+
function webProvidersExtension(pi) {
|
|
11485
|
+
const activeWebResearchRequests = /* @__PURE__ */ new Map();
|
|
11486
|
+
let latestWidgetContext;
|
|
11487
|
+
let webResearchWidgetTimer;
|
|
11488
|
+
const stopWebResearchWidgetTimer = () => {
|
|
11489
|
+
if (webResearchWidgetTimer) {
|
|
11490
|
+
clearInterval(webResearchWidgetTimer);
|
|
11491
|
+
webResearchWidgetTimer = void 0;
|
|
11492
|
+
}
|
|
11493
|
+
};
|
|
11494
|
+
const ensureWebResearchWidgetTimer = () => {
|
|
11495
|
+
if (webResearchWidgetTimer || activeWebResearchRequests.size === 0) {
|
|
11496
|
+
return;
|
|
11497
|
+
}
|
|
11498
|
+
webResearchWidgetTimer = setInterval(() => {
|
|
11499
|
+
updateWebResearchWidget();
|
|
11500
|
+
}, 1e3);
|
|
11501
|
+
};
|
|
11502
|
+
const updateWebResearchWidget = (ctx) => {
|
|
11503
|
+
const widgetContext = ctx ?? latestWidgetContext;
|
|
11504
|
+
if (!widgetContext) {
|
|
11505
|
+
return;
|
|
11506
|
+
}
|
|
11507
|
+
latestWidgetContext = widgetContext;
|
|
11508
|
+
if (!widgetContext.hasUI) {
|
|
11509
|
+
stopWebResearchWidgetTimer();
|
|
11510
|
+
return;
|
|
11511
|
+
}
|
|
11512
|
+
if (activeWebResearchRequests.size === 0) {
|
|
11513
|
+
stopWebResearchWidgetTimer();
|
|
11514
|
+
widgetContext.ui.setWidget(WEB_RESEARCH_WIDGET_KEY, void 0);
|
|
11515
|
+
return;
|
|
11516
|
+
}
|
|
11517
|
+
ensureWebResearchWidgetTimer();
|
|
11518
|
+
widgetContext.ui.setWidget(
|
|
11519
|
+
WEB_RESEARCH_WIDGET_KEY,
|
|
11520
|
+
buildWebResearchWidgetLines(
|
|
11521
|
+
getActiveWebResearchRequests(activeWebResearchRequests),
|
|
11522
|
+
widgetContext.ui.theme
|
|
11523
|
+
)
|
|
11524
|
+
);
|
|
11525
|
+
};
|
|
11526
|
+
if ("registerMessageRenderer" in pi) {
|
|
11527
|
+
pi.registerMessageRenderer(
|
|
11528
|
+
WEB_RESEARCH_RESULT_MESSAGE_TYPE,
|
|
11529
|
+
(message, state, theme) => renderWebResearchResultMessage(message, state, theme)
|
|
11530
|
+
);
|
|
11531
|
+
pi.registerMessageRenderer(
|
|
11532
|
+
WEB_RESEARCH_REPORT_MESSAGE_TYPE,
|
|
11533
|
+
(message, state, theme) => renderWebResearchReportMessage(message, state, theme)
|
|
11534
|
+
);
|
|
11535
|
+
}
|
|
11536
|
+
pi.registerCommand("web-providers", {
|
|
11537
|
+
description: "Configure web search providers",
|
|
9865
11538
|
handler: async (_args, ctx) => {
|
|
9866
11539
|
if (!ctx.hasUI) {
|
|
9867
11540
|
ctx.ui.notify("web-providers requires interactive mode", "error");
|
|
@@ -9874,11 +11547,71 @@ function webProvidersExtension(pi) {
|
|
|
9874
11547
|
);
|
|
9875
11548
|
}
|
|
9876
11549
|
});
|
|
11550
|
+
pi.registerCommand("web-research", {
|
|
11551
|
+
description: "Browse, inspect, and manage web researches",
|
|
11552
|
+
handler: async (_args, ctx) => {
|
|
11553
|
+
if (!ctx.hasUI) {
|
|
11554
|
+
ctx.ui.notify("web-research requires interactive mode", "error");
|
|
11555
|
+
return;
|
|
11556
|
+
}
|
|
11557
|
+
const actions = {
|
|
11558
|
+
copyToClipboard,
|
|
11559
|
+
injectReport: ({ title, body, item }) => {
|
|
11560
|
+
pi.sendMessage(
|
|
11561
|
+
{
|
|
11562
|
+
customType: WEB_RESEARCH_REPORT_MESSAGE_TYPE,
|
|
11563
|
+
content: formatWebResearchReportMessage(title, body, item),
|
|
11564
|
+
display: true,
|
|
11565
|
+
details: {
|
|
11566
|
+
title,
|
|
11567
|
+
outputPath: item.outputPath,
|
|
11568
|
+
provider: item.provider,
|
|
11569
|
+
query: item.query,
|
|
11570
|
+
status: item.status
|
|
11571
|
+
}
|
|
11572
|
+
},
|
|
11573
|
+
{ triggerTurn: false }
|
|
11574
|
+
);
|
|
11575
|
+
},
|
|
11576
|
+
notify: (message, type) => ctx.ui.notify(message, type ?? "info")
|
|
11577
|
+
};
|
|
11578
|
+
let timer;
|
|
11579
|
+
let view;
|
|
11580
|
+
try {
|
|
11581
|
+
await ctx.ui.custom(
|
|
11582
|
+
(tui, theme, _keybindings, done) => {
|
|
11583
|
+
view = new WebResearchManagerView(
|
|
11584
|
+
tui,
|
|
11585
|
+
theme,
|
|
11586
|
+
done,
|
|
11587
|
+
ctx.cwd,
|
|
11588
|
+
activeWebResearchRequests,
|
|
11589
|
+
() => updateWebResearchWidget(ctx),
|
|
11590
|
+
actions
|
|
11591
|
+
);
|
|
11592
|
+
timer = setInterval(() => view?.refresh(), 1e3);
|
|
11593
|
+
return view;
|
|
11594
|
+
},
|
|
11595
|
+
{
|
|
11596
|
+
overlay: true,
|
|
11597
|
+
overlayOptions: () => view?.isReportOpen() ? { anchor: "center", width: "85%", maxHeight: "85%" } : {
|
|
11598
|
+
anchor: "center",
|
|
11599
|
+
width: "75%",
|
|
11600
|
+
maxHeight: "60%",
|
|
11601
|
+
minWidth: 60
|
|
11602
|
+
}
|
|
11603
|
+
}
|
|
11604
|
+
);
|
|
11605
|
+
} finally {
|
|
11606
|
+
if (timer) clearInterval(timer);
|
|
11607
|
+
}
|
|
11608
|
+
}
|
|
11609
|
+
});
|
|
9877
11610
|
pi.on("session_start", async (_event, ctx) => {
|
|
9878
11611
|
latestWidgetContext = ctx;
|
|
9879
11612
|
resetContentStore();
|
|
9880
11613
|
updateWebResearchWidget(ctx);
|
|
9881
|
-
await
|
|
11614
|
+
await refreshManagedToolsOnStartup2(
|
|
9882
11615
|
pi,
|
|
9883
11616
|
{ activeWebResearchRequests, updateWebResearchWidget },
|
|
9884
11617
|
ctx.cwd,
|
|
@@ -9889,7 +11622,7 @@ function webProvidersExtension(pi) {
|
|
|
9889
11622
|
latestWidgetContext = ctx;
|
|
9890
11623
|
await cleanupContentStore();
|
|
9891
11624
|
updateWebResearchWidget(ctx);
|
|
9892
|
-
await
|
|
11625
|
+
await refreshManagedToolsOnStartup2(
|
|
9893
11626
|
pi,
|
|
9894
11627
|
{ activeWebResearchRequests, updateWebResearchWidget },
|
|
9895
11628
|
ctx.cwd,
|
|
@@ -10112,7 +11845,7 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
|
|
|
10112
11845
|
"Do not expect the final report in the same turn; tell the user that web research has started and wait for the completion message with the saved report path."
|
|
10113
11846
|
]),
|
|
10114
11847
|
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
10115
|
-
return
|
|
11848
|
+
return dispatchWebResearch2({
|
|
10116
11849
|
pi,
|
|
10117
11850
|
activeWebResearchRequests: webResearchLifecycle.activeWebResearchRequests,
|
|
10118
11851
|
updateWebResearchWidget: webResearchLifecycle.updateWebResearchWidget,
|
|
@@ -10137,109 +11870,38 @@ function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
|
|
|
10137
11870
|
}
|
|
10138
11871
|
});
|
|
10139
11872
|
}
|
|
10140
|
-
async function runWebProvidersConfig(pi, webResearchLifecycle, ctx) {
|
|
10141
|
-
const config = await loadConfig();
|
|
10142
|
-
const activeProvider = getInitialProviderSelection(config);
|
|
10143
|
-
await ctx.ui.custom(
|
|
10144
|
-
(tui, theme, _keybindings, done) => new WebProvidersSettingsView(
|
|
10145
|
-
tui,
|
|
10146
|
-
theme,
|
|
10147
|
-
done,
|
|
10148
|
-
ctx,
|
|
10149
|
-
config,
|
|
10150
|
-
activeProvider
|
|
10151
|
-
)
|
|
10152
|
-
);
|
|
10153
|
-
await refreshManagedTools(pi, webResearchLifecycle, ctx.cwd, {
|
|
10154
|
-
addAvailable: true
|
|
10155
|
-
});
|
|
10156
|
-
}
|
|
10157
|
-
function formatStartupConfigError(error) {
|
|
10158
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
10159
|
-
return `web-providers config error: ${detail.replace(getConfigPath(), "~/.pi/agent/web-providers.json")}`;
|
|
10160
|
-
}
|
|
10161
|
-
function getAvailableProviderIdsForCapability(config, cwd, capability) {
|
|
10162
|
-
const providerId = getMappedProviderIdForTool(config, capability);
|
|
10163
|
-
if (!providerId) {
|
|
10164
|
-
return [];
|
|
10165
|
-
}
|
|
10166
|
-
const provider = PROVIDERS_BY_ID[providerId];
|
|
10167
|
-
if (!supportsTool2(provider, capability)) {
|
|
10168
|
-
return [];
|
|
10169
|
-
}
|
|
10170
|
-
const status = getProviderCapabilityStatus(
|
|
10171
|
-
config,
|
|
10172
|
-
cwd,
|
|
10173
|
-
providerId,
|
|
10174
|
-
capability,
|
|
10175
|
-
{
|
|
10176
|
-
resolveSecrets: false
|
|
10177
|
-
}
|
|
10178
|
-
);
|
|
10179
|
-
return isProviderCapabilityExposable(status) ? [providerId] : [];
|
|
10180
|
-
}
|
|
10181
|
-
function getProviderStatusForTool(config, cwd, providerId, capability) {
|
|
10182
|
-
return getProviderCapabilityStatus(config, cwd, providerId, capability);
|
|
10183
|
-
}
|
|
10184
|
-
function getAvailableManagedToolNames(config, cwd) {
|
|
10185
|
-
return Object.keys(CAPABILITY_TOOL_NAMES).filter(
|
|
10186
|
-
(capability) => getAvailableProviderIdsForCapability(config, cwd, capability).length > 0
|
|
10187
|
-
).map((capability) => CAPABILITY_TOOL_NAMES[capability]);
|
|
10188
|
-
}
|
|
10189
|
-
function getSyncedActiveTools(config, cwd, activeToolNames, options) {
|
|
10190
|
-
const availableToolNames = new Set(getAvailableManagedToolNames(config, cwd));
|
|
10191
|
-
const nextActiveTools = new Set(activeToolNames);
|
|
10192
|
-
for (const toolName of MANAGED_TOOL_NAMES) {
|
|
10193
|
-
if (availableToolNames.has(toolName)) {
|
|
10194
|
-
if (options.addAvailable) {
|
|
10195
|
-
nextActiveTools.add(toolName);
|
|
10196
|
-
}
|
|
10197
|
-
continue;
|
|
10198
|
-
}
|
|
10199
|
-
nextActiveTools.delete(toolName);
|
|
10200
|
-
}
|
|
10201
|
-
return nextActiveTools;
|
|
10202
|
-
}
|
|
10203
|
-
async function refreshManagedTools(pi, webResearchLifecycle, cwd, options) {
|
|
11873
|
+
async function runWebProvidersConfig(pi, webResearchLifecycle, ctx) {
|
|
10204
11874
|
const config = await loadConfig();
|
|
10205
|
-
const
|
|
10206
|
-
|
|
10207
|
-
|
|
10208
|
-
|
|
10209
|
-
|
|
11875
|
+
const activeProvider = getInitialProviderSelection(config);
|
|
11876
|
+
await ctx.ui.custom(
|
|
11877
|
+
(tui, theme, _keybindings, done) => new WebProvidersSettingsView(
|
|
11878
|
+
tui,
|
|
11879
|
+
theme,
|
|
11880
|
+
done,
|
|
11881
|
+
ctx,
|
|
11882
|
+
config,
|
|
11883
|
+
activeProvider
|
|
11884
|
+
)
|
|
10210
11885
|
);
|
|
10211
|
-
|
|
10212
|
-
|
|
10213
|
-
contents: getAvailableProviderIdsForCapability(config, cwd, "contents"),
|
|
10214
|
-
answer: getAvailableProviderIdsForCapability(config, cwd, "answer"),
|
|
10215
|
-
research: getAvailableProviderIdsForCapability(config, cwd, "research")
|
|
11886
|
+
await refreshManagedTools2(pi, webResearchLifecycle, ctx.cwd, {
|
|
11887
|
+
addAvailable: true
|
|
10216
11888
|
});
|
|
10217
|
-
await syncManagedToolAvailability(pi, nextActiveTools);
|
|
10218
11889
|
}
|
|
10219
|
-
async function
|
|
10220
|
-
|
|
10221
|
-
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
content: message,
|
|
10227
|
-
display: true
|
|
10228
|
-
});
|
|
10229
|
-
await syncManagedToolAvailability(
|
|
10230
|
-
pi,
|
|
10231
|
-
new Set(
|
|
10232
|
-
pi.getActiveTools().filter((toolName) => !MANAGED_TOOL_NAMES.includes(toolName))
|
|
10233
|
-
)
|
|
10234
|
-
);
|
|
10235
|
-
}
|
|
11890
|
+
async function refreshManagedTools2(pi, webResearchLifecycle, cwd, options) {
|
|
11891
|
+
await refreshManagedTools(
|
|
11892
|
+
pi,
|
|
11893
|
+
(providerIdsByCapability) => registerManagedTools(pi, webResearchLifecycle, providerIdsByCapability),
|
|
11894
|
+
cwd,
|
|
11895
|
+
options
|
|
11896
|
+
);
|
|
10236
11897
|
}
|
|
10237
|
-
async function
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
11898
|
+
async function refreshManagedToolsOnStartup2(pi, webResearchLifecycle, cwd, options) {
|
|
11899
|
+
await refreshManagedToolsOnStartup(
|
|
11900
|
+
pi,
|
|
11901
|
+
(providerIdsByCapability) => registerManagedTools(pi, webResearchLifecycle, providerIdsByCapability),
|
|
11902
|
+
cwd,
|
|
11903
|
+
options
|
|
11904
|
+
);
|
|
10243
11905
|
}
|
|
10244
11906
|
function getSearchMaxResultsLimit(providerId) {
|
|
10245
11907
|
const capabilities = PROVIDERS_BY_ID[providerId].capabilities;
|
|
@@ -10456,12 +12118,12 @@ async function executeRawProviderRequest({
|
|
|
10456
12118
|
input
|
|
10457
12119
|
});
|
|
10458
12120
|
}
|
|
10459
|
-
function buildSearchBatchError(outcomes,
|
|
12121
|
+
function buildSearchBatchError(outcomes, providerLabel2) {
|
|
10460
12122
|
const failed = outcomes.filter((outcome) => outcome.error !== void 0);
|
|
10461
12123
|
if (failed.length === 1) {
|
|
10462
12124
|
return new Error(
|
|
10463
12125
|
formatProviderCapabilityFailure(
|
|
10464
|
-
|
|
12126
|
+
providerLabel2,
|
|
10465
12127
|
"search",
|
|
10466
12128
|
failed[0]?.error ?? ""
|
|
10467
12129
|
)
|
|
@@ -10471,7 +12133,7 @@ function buildSearchBatchError(outcomes, providerLabel) {
|
|
|
10471
12133
|
(outcome, index) => `${index + 1}. ${formatQuotedPreview(outcome.query, 40)} \u2014 ${outcome.error}`
|
|
10472
12134
|
).join("; ");
|
|
10473
12135
|
return new Error(
|
|
10474
|
-
`${
|
|
12136
|
+
`${providerLabel2} search failed for ${failed.length} queries: ${summary}`
|
|
10475
12137
|
);
|
|
10476
12138
|
}
|
|
10477
12139
|
async function executeSingleSearchQuery({
|
|
@@ -10608,12 +12270,12 @@ async function executeAnswerToolInternal({
|
|
|
10608
12270
|
})
|
|
10609
12271
|
};
|
|
10610
12272
|
}
|
|
10611
|
-
function buildAnswerBatchError(outcomes,
|
|
12273
|
+
function buildAnswerBatchError(outcomes, providerLabel2) {
|
|
10612
12274
|
const failed = outcomes.filter((outcome) => outcome.error !== void 0);
|
|
10613
12275
|
if (failed.length === 1) {
|
|
10614
12276
|
return new Error(
|
|
10615
12277
|
formatProviderCapabilityFailure(
|
|
10616
|
-
|
|
12278
|
+
providerLabel2,
|
|
10617
12279
|
"answer",
|
|
10618
12280
|
failed[0]?.error ?? ""
|
|
10619
12281
|
)
|
|
@@ -10623,7 +12285,7 @@ function buildAnswerBatchError(outcomes, providerLabel) {
|
|
|
10623
12285
|
(outcome, index) => `${index + 1}. ${formatQuotedPreview(outcome.query, 40)} \u2014 ${outcome.error}`
|
|
10624
12286
|
).join("; ");
|
|
10625
12287
|
return new Error(
|
|
10626
|
-
`${
|
|
12288
|
+
`${providerLabel2} answer failed for ${failed.length} questions: ${summary}`
|
|
10627
12289
|
);
|
|
10628
12290
|
}
|
|
10629
12291
|
function formatAnswerResponses(outcomes) {
|
|
@@ -10839,7 +12501,7 @@ async function executeProviderToolInternal({
|
|
|
10839
12501
|
})
|
|
10840
12502
|
};
|
|
10841
12503
|
}
|
|
10842
|
-
async function
|
|
12504
|
+
async function dispatchWebResearch2({
|
|
10843
12505
|
pi,
|
|
10844
12506
|
activeWebResearchRequests,
|
|
10845
12507
|
updateWebResearchWidget,
|
|
@@ -10868,188 +12530,59 @@ async function dispatchWebResearchInternal({
|
|
|
10868
12530
|
input,
|
|
10869
12531
|
executionOverride
|
|
10870
12532
|
}) {
|
|
10871
|
-
|
|
10872
|
-
|
|
12533
|
+
return dispatchWebResearch({
|
|
12534
|
+
activeWebResearchRequests,
|
|
10873
12535
|
config,
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
updateWebResearchWidget(ctx);
|
|
10882
|
-
trackPendingResearchTask(
|
|
10883
|
-
runDispatchedWebResearch({
|
|
10884
|
-
pi,
|
|
10885
|
-
activeWebResearchRequests,
|
|
10886
|
-
updateWebResearchWidget,
|
|
10887
|
-
request,
|
|
10888
|
-
config,
|
|
12536
|
+
explicitProvider,
|
|
12537
|
+
ctx: { cwd: ctx.cwd },
|
|
12538
|
+
options: providerOptions,
|
|
12539
|
+
input,
|
|
12540
|
+
executionOverride,
|
|
12541
|
+
executeResearch: async ({
|
|
12542
|
+
config: config2,
|
|
10889
12543
|
provider,
|
|
10890
12544
|
providerConfig,
|
|
10891
|
-
ctx,
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
{
|
|
10899
|
-
type: "text",
|
|
10900
|
-
text: `Started web research via ${provider.label}.`
|
|
10901
|
-
}
|
|
10902
|
-
],
|
|
10903
|
-
details: request,
|
|
10904
|
-
display: buildProviderToolDisplay2({
|
|
10905
|
-
capability: "research",
|
|
10906
|
-
providerId: provider.id,
|
|
10907
|
-
details: { tool: "web_research", provider: provider.id },
|
|
10908
|
-
text: "started"
|
|
10909
|
-
})
|
|
10910
|
-
};
|
|
10911
|
-
}
|
|
10912
|
-
async function runDispatchedWebResearch({
|
|
10913
|
-
pi,
|
|
10914
|
-
activeWebResearchRequests,
|
|
10915
|
-
updateWebResearchWidget,
|
|
10916
|
-
request,
|
|
10917
|
-
config,
|
|
10918
|
-
provider,
|
|
10919
|
-
providerConfig,
|
|
10920
|
-
ctx,
|
|
10921
|
-
options,
|
|
10922
|
-
executionOverride
|
|
10923
|
-
}) {
|
|
10924
|
-
let result;
|
|
10925
|
-
let reportText = "";
|
|
10926
|
-
try {
|
|
10927
|
-
const response = await executeProviderOperation({
|
|
12545
|
+
ctx: ctx2,
|
|
12546
|
+
signal,
|
|
12547
|
+
options,
|
|
12548
|
+
input: input2,
|
|
12549
|
+
onProgress,
|
|
12550
|
+
executionOverride: executionOverride2
|
|
12551
|
+
}) => executeProviderOperation({
|
|
10928
12552
|
capability: "research",
|
|
10929
|
-
config,
|
|
12553
|
+
config: config2,
|
|
10930
12554
|
provider,
|
|
10931
12555
|
providerConfig,
|
|
10932
|
-
ctx,
|
|
10933
|
-
signal
|
|
12556
|
+
ctx: ctx2,
|
|
12557
|
+
signal,
|
|
10934
12558
|
options,
|
|
10935
|
-
input:
|
|
10936
|
-
onProgress
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
10943
|
-
executionOverride
|
|
10944
|
-
});
|
|
10945
|
-
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10946
|
-
result = {
|
|
10947
|
-
...request,
|
|
10948
|
-
status: "completed",
|
|
10949
|
-
completedAt,
|
|
10950
|
-
elapsedMs: Math.max(
|
|
10951
|
-
0,
|
|
10952
|
-
Date.parse(completedAt) - Date.parse(request.startedAt)
|
|
10953
|
-
),
|
|
10954
|
-
itemCount: response.itemCount
|
|
10955
|
-
};
|
|
10956
|
-
reportText = response.text;
|
|
10957
|
-
} catch (error) {
|
|
10958
|
-
const completedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10959
|
-
result = {
|
|
10960
|
-
...request,
|
|
10961
|
-
status: "failed",
|
|
10962
|
-
completedAt,
|
|
10963
|
-
elapsedMs: Math.max(
|
|
10964
|
-
0,
|
|
10965
|
-
Date.parse(completedAt) - Date.parse(request.startedAt)
|
|
10966
|
-
),
|
|
10967
|
-
error: formatErrorMessage(error)
|
|
10968
|
-
};
|
|
10969
|
-
}
|
|
10970
|
-
try {
|
|
10971
|
-
await writeWebResearchArtifact(result, reportText);
|
|
10972
|
-
pi.sendMessage({
|
|
10973
|
-
customType: WEB_RESEARCH_RESULT_MESSAGE_TYPE,
|
|
10974
|
-
content: formatWebResearchResultMessage(result, reportText),
|
|
10975
|
-
display: true,
|
|
10976
|
-
details: result
|
|
10977
|
-
});
|
|
10978
|
-
} finally {
|
|
10979
|
-
activeWebResearchRequests.delete(request.id);
|
|
10980
|
-
updateWebResearchWidget();
|
|
10981
|
-
}
|
|
10982
|
-
}
|
|
10983
|
-
function createWebResearchRequest(cwd, provider, input) {
|
|
10984
|
-
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
10985
|
-
return {
|
|
10986
|
-
tool: "web_research",
|
|
10987
|
-
id: randomUUID(),
|
|
10988
|
-
provider,
|
|
10989
|
-
input,
|
|
10990
|
-
outputPath: buildWebResearchArtifactPath(cwd, input, startedAt),
|
|
10991
|
-
startedAt
|
|
10992
|
-
};
|
|
10993
|
-
}
|
|
10994
|
-
function buildWebResearchArtifactPath(cwd, input, startedAt) {
|
|
10995
|
-
const timestamp = startedAt.replaceAll(":", "-").replace(".", "-");
|
|
10996
|
-
const slug = slugifyWebResearchInput(input);
|
|
10997
|
-
return join2(cwd, RESEARCH_ARTIFACTS_DIR, `${timestamp}-${slug}.md`);
|
|
10998
|
-
}
|
|
10999
|
-
function slugifyWebResearchInput(input) {
|
|
11000
|
-
const slug = input.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
11001
|
-
return slug.length > 0 ? slug : "research";
|
|
12559
|
+
input: input2,
|
|
12560
|
+
onProgress,
|
|
12561
|
+
executionOverride: executionOverride2
|
|
12562
|
+
}),
|
|
12563
|
+
deliverResult: (message) => pi.sendMessage(message),
|
|
12564
|
+
onJobsChanged: () => updateWebResearchWidget(ctx),
|
|
12565
|
+
resultMessageType: WEB_RESEARCH_RESULT_MESSAGE_TYPE
|
|
12566
|
+
});
|
|
11002
12567
|
}
|
|
11003
12568
|
function buildWebResearchWidgetLines(requests, theme, now = Date.now()) {
|
|
11004
|
-
const
|
|
11005
|
-
|
|
11006
|
-
const
|
|
11007
|
-
const elapsed =
|
|
11008
|
-
const icon =
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11014
|
-
lines.push(theme.fg("muted", `+${requests.length - 3} more`));
|
|
11015
|
-
}
|
|
11016
|
-
return lines;
|
|
11017
|
-
}
|
|
11018
|
-
function getWebResearchWidgetIcon(request, _now) {
|
|
11019
|
-
if (request.progress === "poll retrying after transient errors") {
|
|
11020
|
-
return "\u27F3 ";
|
|
11021
|
-
}
|
|
11022
|
-
if (request.progress === "queued") {
|
|
11023
|
-
return "\u25CC ";
|
|
11024
|
-
}
|
|
11025
|
-
if (request.progress === "starting") {
|
|
11026
|
-
return "\u25D4 ";
|
|
11027
|
-
}
|
|
11028
|
-
if (request.progress?.startsWith("started:")) {
|
|
11029
|
-
return "\u25D1 ";
|
|
11030
|
-
}
|
|
11031
|
-
return "\u25CF ";
|
|
11032
|
-
}
|
|
11033
|
-
function summarizeWebResearchProgress(message, providerLabel) {
|
|
11034
|
-
const startingMessage = `Starting research via ${providerLabel}`;
|
|
11035
|
-
if (message === startingMessage) {
|
|
11036
|
-
return "starting";
|
|
11037
|
-
}
|
|
11038
|
-
const startedPrefix = `${providerLabel} research started: `;
|
|
11039
|
-
if (message.startsWith(startedPrefix)) {
|
|
11040
|
-
return `started: ${message.slice(startedPrefix.length)}`;
|
|
11041
|
-
}
|
|
11042
|
-
const statusPrefix = `Research via ${providerLabel}: `;
|
|
11043
|
-
if (message.startsWith(statusPrefix)) {
|
|
11044
|
-
return message.slice(statusPrefix.length).replace(/\s+\([^)]* elapsed\)$/u, "").trim();
|
|
11045
|
-
}
|
|
11046
|
-
const retryPrefix = `${providerLabel} research poll is still retrying after transient errors`;
|
|
11047
|
-
if (message.startsWith(retryPrefix)) {
|
|
11048
|
-
return "poll retrying after transient errors";
|
|
12569
|
+
const sorted = requests.slice().sort((left, right) => left.startedAt.localeCompare(right.startedAt));
|
|
12570
|
+
const jobs = sorted.slice(0, 3).map((request) => {
|
|
12571
|
+
const providerLabel2 = PROVIDERS_BY_ID[request.provider]?.label ?? request.provider;
|
|
12572
|
+
const elapsed = formatCompactElapsed2(now - Date.parse(request.startedAt));
|
|
12573
|
+
const icon = getWebResearchProgressIcon(request);
|
|
12574
|
+
const status = request.progress === "cancelling" ? " cancelling" : "";
|
|
12575
|
+
return `${icon} ${providerLabel2} ${elapsed}${status}`;
|
|
12576
|
+
});
|
|
12577
|
+
if (sorted.length > 3) {
|
|
12578
|
+
jobs.push(`+${sorted.length - 3} more`);
|
|
11049
12579
|
}
|
|
11050
|
-
|
|
12580
|
+
const count = sorted.length === 1 ? "1 research" : `${sorted.length} researches`;
|
|
12581
|
+
return [
|
|
12582
|
+
`${theme.fg("accent", `${count} running`)}${theme.fg("muted", ` \xB7 ${jobs.join(" \xB7 ")} \xB7 /web-research`)}`
|
|
12583
|
+
];
|
|
11051
12584
|
}
|
|
11052
|
-
function
|
|
12585
|
+
function formatCompactElapsed2(ms) {
|
|
11053
12586
|
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
11054
12587
|
const minutes = Math.floor(totalSeconds / 60);
|
|
11055
12588
|
const seconds = totalSeconds % 60;
|
|
@@ -11058,68 +12591,6 @@ function formatCompactElapsed(ms) {
|
|
|
11058
12591
|
}
|
|
11059
12592
|
return `${totalSeconds}s`;
|
|
11060
12593
|
}
|
|
11061
|
-
function formatWebResearchResultMessage(result, reportText) {
|
|
11062
|
-
const text = reportText.trim();
|
|
11063
|
-
if (text.length > 0) {
|
|
11064
|
-
return `${text}
|
|
11065
|
-
`;
|
|
11066
|
-
}
|
|
11067
|
-
if (result.error) {
|
|
11068
|
-
return `${result.error}
|
|
11069
|
-
`;
|
|
11070
|
-
}
|
|
11071
|
-
return "";
|
|
11072
|
-
}
|
|
11073
|
-
async function writeWebResearchArtifact(result, reportText) {
|
|
11074
|
-
await mkdir2(dirname2(result.outputPath), { recursive: true });
|
|
11075
|
-
await writeFile2(
|
|
11076
|
-
result.outputPath,
|
|
11077
|
-
formatWebResearchArtifact(result, reportText),
|
|
11078
|
-
"utf-8"
|
|
11079
|
-
);
|
|
11080
|
-
}
|
|
11081
|
-
function formatWebResearchArtifact(result, reportText) {
|
|
11082
|
-
const providerLabel = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
11083
|
-
const lines = [
|
|
11084
|
-
"# Web research report",
|
|
11085
|
-
"",
|
|
11086
|
-
"## Query",
|
|
11087
|
-
result.input,
|
|
11088
|
-
"",
|
|
11089
|
-
"## Provider",
|
|
11090
|
-
providerLabel,
|
|
11091
|
-
"",
|
|
11092
|
-
"## Status",
|
|
11093
|
-
result.status,
|
|
11094
|
-
"",
|
|
11095
|
-
"## Started",
|
|
11096
|
-
result.startedAt,
|
|
11097
|
-
"",
|
|
11098
|
-
"## Completed",
|
|
11099
|
-
result.completedAt,
|
|
11100
|
-
"",
|
|
11101
|
-
"## Elapsed",
|
|
11102
|
-
formatElapsed(result.elapsedMs)
|
|
11103
|
-
];
|
|
11104
|
-
if (typeof result.itemCount === "number") {
|
|
11105
|
-
lines.push("", "## Items", String(result.itemCount));
|
|
11106
|
-
}
|
|
11107
|
-
if (result.error) {
|
|
11108
|
-
lines.push("", "## Error", result.error);
|
|
11109
|
-
}
|
|
11110
|
-
if (reportText) {
|
|
11111
|
-
lines.push("", "## Report", reportText);
|
|
11112
|
-
}
|
|
11113
|
-
return `${lines.join("\n")}
|
|
11114
|
-
`;
|
|
11115
|
-
}
|
|
11116
|
-
function trackPendingResearchTask(task) {
|
|
11117
|
-
const tracked = task.catch(() => {
|
|
11118
|
-
}).finally(() => {
|
|
11119
|
-
pendingResearchTasks.delete(tracked);
|
|
11120
|
-
});
|
|
11121
|
-
pendingResearchTasks.add(tracked);
|
|
11122
|
-
}
|
|
11123
12594
|
async function executeBatchedContentsTool({
|
|
11124
12595
|
config,
|
|
11125
12596
|
provider,
|
|
@@ -11281,10 +12752,10 @@ function createToolProgressReporter(capability, providerId, progress) {
|
|
|
11281
12752
|
if (Date.now() - lastUpdateAt < RESEARCH_HEARTBEAT_MS) {
|
|
11282
12753
|
return;
|
|
11283
12754
|
}
|
|
11284
|
-
const
|
|
12755
|
+
const providerLabel2 = PROVIDERS_BY_ID[providerId]?.label ?? providerId;
|
|
11285
12756
|
const elapsed = formatElapsed(Date.now() - startedAt);
|
|
11286
12757
|
emit(
|
|
11287
|
-
`Researching via ${
|
|
12758
|
+
`Researching via ${providerLabel2} (${elapsed} elapsed)`,
|
|
11288
12759
|
buildProgressDisplay2(providerId, `Researching ${elapsed}`)
|
|
11289
12760
|
);
|
|
11290
12761
|
lastUpdateAt = Date.now();
|
|
@@ -11307,38 +12778,38 @@ function renderListCallHeader(toolName, items, theme, suffix, options = {}) {
|
|
|
11307
12778
|
invalidate() {
|
|
11308
12779
|
},
|
|
11309
12780
|
render(width) {
|
|
11310
|
-
const normalizedItems = items.map((item) =>
|
|
12781
|
+
const normalizedItems = items.map((item) => cleanSingleLine2(item)).filter((item) => item.length > 0);
|
|
11311
12782
|
const toolTitle = theme.fg("toolTitle", theme.bold(toolName));
|
|
11312
12783
|
const mutedSuffix = suffix ? theme.fg("muted", suffix) : "";
|
|
11313
12784
|
if (!options.forceMultiline && normalizedItems.length === 1) {
|
|
11314
12785
|
const singleItem = options.quoteSingleItem ? formatQuotedPreview(normalizedItems[0], 80) : truncateInline(normalizedItems[0], 120);
|
|
11315
12786
|
const inline = `${toolTitle} ${theme.fg("accent", singleItem)}${mutedSuffix}`;
|
|
11316
|
-
const line =
|
|
11317
|
-
return [line + " ".repeat(Math.max(0, width -
|
|
12787
|
+
const line = truncateToWidth2(inline.trimEnd(), width);
|
|
12788
|
+
return [line + " ".repeat(Math.max(0, width - visibleWidth2(line)))];
|
|
11318
12789
|
}
|
|
11319
12790
|
let header = toolTitle;
|
|
11320
12791
|
if (mutedSuffix) {
|
|
11321
12792
|
header += mutedSuffix;
|
|
11322
12793
|
}
|
|
11323
12794
|
const lines = [];
|
|
11324
|
-
const headerLine =
|
|
12795
|
+
const headerLine = truncateToWidth2(header.trimEnd(), width);
|
|
11325
12796
|
lines.push(
|
|
11326
|
-
headerLine + " ".repeat(Math.max(0, width -
|
|
12797
|
+
headerLine + " ".repeat(Math.max(0, width - visibleWidth2(headerLine)))
|
|
11327
12798
|
);
|
|
11328
12799
|
for (const item of normalizedItems) {
|
|
11329
12800
|
const itemLines = options.forceMultiline ? wrapTextWithAnsi(
|
|
11330
12801
|
theme.fg("accent", item),
|
|
11331
12802
|
Math.max(1, width - 2)
|
|
11332
12803
|
).map((line) => ` ${line}`) : [
|
|
11333
|
-
|
|
12804
|
+
truncateToWidth2(
|
|
11334
12805
|
` ${theme.fg("accent", truncateInline(item, 120))}`,
|
|
11335
12806
|
width
|
|
11336
12807
|
)
|
|
11337
12808
|
];
|
|
11338
12809
|
for (const itemLine of itemLines) {
|
|
11339
|
-
const line =
|
|
12810
|
+
const line = truncateToWidth2(itemLine, width);
|
|
11340
12811
|
lines.push(
|
|
11341
|
-
line + " ".repeat(Math.max(0, width -
|
|
12812
|
+
line + " ".repeat(Math.max(0, width - visibleWidth2(line)))
|
|
11342
12813
|
);
|
|
11343
12814
|
}
|
|
11344
12815
|
}
|
|
@@ -11433,7 +12904,8 @@ function renderWebResearchResultMessage(message, { expanded }, theme, symbols =
|
|
|
11433
12904
|
const text = typeof message.content === "string" ? message.content : extractTextContent(message.content);
|
|
11434
12905
|
const details = isWebResearchResult(message.details) ? message.details : void 0;
|
|
11435
12906
|
const isSuccess = details?.status === "completed";
|
|
11436
|
-
const
|
|
12907
|
+
const isCancelled = details?.status === "cancelled";
|
|
12908
|
+
const accent = isSuccess ? "success" : isCancelled ? "warning" : "error";
|
|
11437
12909
|
const box = new Box(1, 1, (value) => theme.bg("customMessageBg", value));
|
|
11438
12910
|
if (!expanded) {
|
|
11439
12911
|
const summary = details ? buildWebResearchResultSummaryLine(details, theme, symbols) : theme.fg(accent, "Web research update");
|
|
@@ -11443,10 +12915,42 @@ function renderWebResearchResultMessage(message, { expanded }, theme, symbols =
|
|
|
11443
12915
|
return box;
|
|
11444
12916
|
}
|
|
11445
12917
|
box.addChild(
|
|
11446
|
-
details ? renderMarkdownBlock(renderWebResearchResultMarkdown(details)) : isSuccess ? renderMarkdownBlock(text ?? "") : renderBlockText(
|
|
12918
|
+
details ? renderMarkdownBlock(renderWebResearchResultMarkdown(details)) : isSuccess ? renderMarkdownBlock(text ?? "") : renderBlockText(
|
|
12919
|
+
text ?? "",
|
|
12920
|
+
theme,
|
|
12921
|
+
isCancelled ? "toolOutput" : "error"
|
|
12922
|
+
)
|
|
11447
12923
|
);
|
|
11448
12924
|
return box;
|
|
11449
12925
|
}
|
|
12926
|
+
function formatWebResearchReportMessage(title, body, item) {
|
|
12927
|
+
const provenance = `Saved web research report "${title}" (provider: ${item.provider || "unknown"}, status: ${item.status}, artifact: \`${item.outputPath}\`):`;
|
|
12928
|
+
return `${provenance}
|
|
12929
|
+
|
|
12930
|
+
${body}
|
|
12931
|
+
`;
|
|
12932
|
+
}
|
|
12933
|
+
function renderWebResearchReportMessage(message, { expanded }, theme) {
|
|
12934
|
+
const text = typeof message.content === "string" ? message.content : extractTextContent(message.content);
|
|
12935
|
+
const details = isWebResearchReportDetails(message.details) ? message.details : void 0;
|
|
12936
|
+
const box = new Box(1, 1, (value) => theme.bg("customMessageBg", value));
|
|
12937
|
+
if (!expanded) {
|
|
12938
|
+
const title = details?.title ?? "saved report";
|
|
12939
|
+
box.addChild(
|
|
12940
|
+
new Text(
|
|
12941
|
+
`${theme.fg("success", `Injected research report: ${title}`)}${theme.fg("muted", ` (${getExpandHint()})`)}`,
|
|
12942
|
+
0,
|
|
12943
|
+
0
|
|
12944
|
+
)
|
|
12945
|
+
);
|
|
12946
|
+
return box;
|
|
12947
|
+
}
|
|
12948
|
+
box.addChild(renderMarkdownBlock(text ?? ""));
|
|
12949
|
+
return box;
|
|
12950
|
+
}
|
|
12951
|
+
function isWebResearchReportDetails(details) {
|
|
12952
|
+
return typeof details === "object" && details !== null && "title" in details && "outputPath" in details && !("tool" in details);
|
|
12953
|
+
}
|
|
11450
12954
|
function renderWebResearchRequestMarkdown(request) {
|
|
11451
12955
|
return [
|
|
11452
12956
|
"### Web research",
|
|
@@ -11472,7 +12976,7 @@ function renderWebResearchResultMarkdown(result) {
|
|
|
11472
12976
|
].join("\n");
|
|
11473
12977
|
}
|
|
11474
12978
|
function buildWebResearchResultSummaryLine(result, theme, symbols) {
|
|
11475
|
-
const
|
|
12979
|
+
const providerLabel2 = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
11476
12980
|
if (result.status === "completed") {
|
|
11477
12981
|
return renderSuccessSummary(
|
|
11478
12982
|
`${formatSummaryElapsed(result.elapsedMs)} \xB7 ${basename(result.outputPath)}`,
|
|
@@ -11480,8 +12984,8 @@ function buildWebResearchResultSummaryLine(result, theme, symbols) {
|
|
|
11480
12984
|
symbols
|
|
11481
12985
|
);
|
|
11482
12986
|
}
|
|
11483
|
-
const statusText = result.status === "cancelled" ? `${
|
|
11484
|
-
const errorSuffix = result.error ? `: ${normalizeProviderFailureDetail(
|
|
12987
|
+
const statusText = result.status === "cancelled" ? `${providerLabel2} research canceled after ${formatSummaryElapsed(result.elapsedMs)}` : `${providerLabel2} research failed after ${formatSummaryElapsed(result.elapsedMs)}`;
|
|
12988
|
+
const errorSuffix = result.error ? `: ${normalizeProviderFailureDetail(providerLabel2, result.error)}` : "";
|
|
11485
12989
|
return renderFailureSummary(`${statusText}${errorSuffix}`, theme, symbols);
|
|
11486
12990
|
}
|
|
11487
12991
|
function isWebResearchRequest(details) {
|
|
@@ -11580,7 +13084,7 @@ function renderEntryList(width, theme, entries, selection) {
|
|
|
11580
13084
|
const paddedLabel = entry.label.padEnd(labelWidth, " ");
|
|
11581
13085
|
const label = selected ? theme.fg("accent", paddedLabel) : paddedLabel;
|
|
11582
13086
|
const value = selected ? theme.fg("accent", entry.currentValue) : theme.fg("muted", entry.currentValue);
|
|
11583
|
-
return
|
|
13087
|
+
return truncateToWidth2(`${prefix}${label} ${value}`, width);
|
|
11584
13088
|
});
|
|
11585
13089
|
}
|
|
11586
13090
|
function renderSelectedEntryDescription(width, theme, entry) {
|
|
@@ -11588,7 +13092,7 @@ function renderSelectedEntryDescription(width, theme, entry) {
|
|
|
11588
13092
|
return [];
|
|
11589
13093
|
}
|
|
11590
13094
|
return wrapTextWithAnsi(entry.description, Math.max(10, width - 2)).map(
|
|
11591
|
-
(line) =>
|
|
13095
|
+
(line) => truncateToWidth2(theme.fg("dim", line), width)
|
|
11592
13096
|
);
|
|
11593
13097
|
}
|
|
11594
13098
|
function formatProviderCapabilityChecks(providerId, theme) {
|
|
@@ -11769,7 +13273,7 @@ var WebProvidersSettingsView = class {
|
|
|
11769
13273
|
}
|
|
11770
13274
|
lines.push("");
|
|
11771
13275
|
lines.push(
|
|
11772
|
-
|
|
13276
|
+
truncateToWidth2(
|
|
11773
13277
|
this.theme.fg(
|
|
11774
13278
|
"dim",
|
|
11775
13279
|
"\u2191\u2193 move \xB7 Tab/Shift+Tab switch section \xB7 Enter edit/open \xB7 Esc close"
|
|
@@ -11788,7 +13292,7 @@ var WebProvidersSettingsView = class {
|
|
|
11788
13292
|
this.tui.requestRender();
|
|
11789
13293
|
return;
|
|
11790
13294
|
}
|
|
11791
|
-
const kb =
|
|
13295
|
+
const kb = getKeybindings2();
|
|
11792
13296
|
const entries = this.getActiveSectionEntries();
|
|
11793
13297
|
if (kb.matches(data, "tui.select.up")) {
|
|
11794
13298
|
if (entries.length > 0) {
|
|
@@ -11798,9 +13302,9 @@ var WebProvidersSettingsView = class {
|
|
|
11798
13302
|
if (entries.length > 0) {
|
|
11799
13303
|
this.moveSelection(1);
|
|
11800
13304
|
}
|
|
11801
|
-
} else if (
|
|
13305
|
+
} else if (matchesKey2(data, Key2.tab)) {
|
|
11802
13306
|
this.moveSection(1);
|
|
11803
|
-
} else if (
|
|
13307
|
+
} else if (matchesKey2(data, Key2.shift("tab"))) {
|
|
11804
13308
|
this.moveSection(-1);
|
|
11805
13309
|
} else if (kb.matches(data, "tui.select.confirm") || data === " ") {
|
|
11806
13310
|
void this.activateCurrentEntry();
|
|
@@ -11933,14 +13437,14 @@ var WebProvidersSettingsView = class {
|
|
|
11933
13437
|
Math.max(20, Math.floor(width * 0.45))
|
|
11934
13438
|
);
|
|
11935
13439
|
const lines = [
|
|
11936
|
-
|
|
13440
|
+
truncateToWidth2(
|
|
11937
13441
|
this.activeSection === section ? this.theme.fg("accent", this.theme.bold(title)) : this.theme.bold(title),
|
|
11938
13442
|
width
|
|
11939
13443
|
)
|
|
11940
13444
|
];
|
|
11941
13445
|
if (section === "provider") {
|
|
11942
13446
|
lines.push(
|
|
11943
|
-
|
|
13447
|
+
truncateToWidth2(
|
|
11944
13448
|
this.theme.fg(
|
|
11945
13449
|
"dim",
|
|
11946
13450
|
` ${"Provider".padEnd(labelWidth, " ")} S C A R Status`
|
|
@@ -11955,15 +13459,15 @@ var WebProvidersSettingsView = class {
|
|
|
11955
13459
|
const paddedLabel = entry.label.padEnd(labelWidth, " ");
|
|
11956
13460
|
const label = selected ? this.theme.fg("accent", paddedLabel) : paddedLabel;
|
|
11957
13461
|
if (entry.currentValue.trim().length === 0) {
|
|
11958
|
-
lines.push(
|
|
13462
|
+
lines.push(truncateToWidth2(`${prefix}${label}`, width));
|
|
11959
13463
|
continue;
|
|
11960
13464
|
}
|
|
11961
13465
|
const value = entry.preserveValueStyle ? entry.currentValue : selected ? this.theme.fg("accent", entry.currentValue) : this.theme.fg("muted", entry.currentValue);
|
|
11962
|
-
lines.push(
|
|
13466
|
+
lines.push(truncateToWidth2(`${prefix}${label} ${value}`, width));
|
|
11963
13467
|
}
|
|
11964
13468
|
if (section === "provider") {
|
|
11965
13469
|
lines.push(
|
|
11966
|
-
|
|
13470
|
+
truncateToWidth2(
|
|
11967
13471
|
this.theme.fg("dim", " S=Search C=Contents A=Answer R=Research"),
|
|
11968
13472
|
width
|
|
11969
13473
|
)
|
|
@@ -12098,7 +13602,7 @@ var ToolSettingsSubmenu = class {
|
|
|
12098
13602
|
}
|
|
12099
13603
|
const entries = this.getEntries();
|
|
12100
13604
|
const lines = [
|
|
12101
|
-
|
|
13605
|
+
truncateToWidth2(
|
|
12102
13606
|
this.theme.fg("accent", TOOL_INFO[this.toolId].label),
|
|
12103
13607
|
width
|
|
12104
13608
|
),
|
|
@@ -12114,7 +13618,7 @@ var ToolSettingsSubmenu = class {
|
|
|
12114
13618
|
}
|
|
12115
13619
|
lines.push("");
|
|
12116
13620
|
lines.push(
|
|
12117
|
-
|
|
13621
|
+
truncateToWidth2(
|
|
12118
13622
|
this.theme.fg("dim", "\u2191\u2193 move \xB7 Enter edit/toggle \xB7 Esc back"),
|
|
12119
13623
|
width
|
|
12120
13624
|
)
|
|
@@ -12130,7 +13634,7 @@ var ToolSettingsSubmenu = class {
|
|
|
12130
13634
|
this.tui.requestRender();
|
|
12131
13635
|
return;
|
|
12132
13636
|
}
|
|
12133
|
-
const kb =
|
|
13637
|
+
const kb = getKeybindings2();
|
|
12134
13638
|
const entries = this.getEntries();
|
|
12135
13639
|
if (kb.matches(data, "tui.select.up")) {
|
|
12136
13640
|
if (this.selection > 0) {
|
|
@@ -12333,7 +13837,7 @@ var ProviderSettingsSubmenu = class {
|
|
|
12333
13837
|
const providerConfig = this.getProviderConfig();
|
|
12334
13838
|
const entries = this.getEntries();
|
|
12335
13839
|
const lines = [
|
|
12336
|
-
|
|
13840
|
+
truncateToWidth2(this.theme.fg("accent", provider.label), width),
|
|
12337
13841
|
"",
|
|
12338
13842
|
...renderEntryList(width, this.theme, entries, this.selection)
|
|
12339
13843
|
];
|
|
@@ -12350,10 +13854,10 @@ var ProviderSettingsSubmenu = class {
|
|
|
12350
13854
|
);
|
|
12351
13855
|
lines.push("");
|
|
12352
13856
|
lines.push(
|
|
12353
|
-
|
|
13857
|
+
truncateToWidth2(this.theme.fg("dim", `Status: ${status}`), width)
|
|
12354
13858
|
);
|
|
12355
13859
|
lines.push(
|
|
12356
|
-
|
|
13860
|
+
truncateToWidth2(
|
|
12357
13861
|
this.theme.fg("dim", "\u2191\u2193 move \xB7 Enter edit/toggle \xB7 Esc back"),
|
|
12358
13862
|
width
|
|
12359
13863
|
)
|
|
@@ -12369,7 +13873,7 @@ var ProviderSettingsSubmenu = class {
|
|
|
12369
13873
|
this.tui.requestRender();
|
|
12370
13874
|
return;
|
|
12371
13875
|
}
|
|
12372
|
-
const kb =
|
|
13876
|
+
const kb = getKeybindings2();
|
|
12373
13877
|
const entries = this.getEntries();
|
|
12374
13878
|
if (kb.matches(data, "tui.select.up")) {
|
|
12375
13879
|
if (this.selection > 0) {
|
|
@@ -12504,12 +14008,12 @@ var TextValueSubmenu = class {
|
|
|
12504
14008
|
editor;
|
|
12505
14009
|
render(width) {
|
|
12506
14010
|
return [
|
|
12507
|
-
|
|
14011
|
+
truncateToWidth2(this.theme.fg("accent", this.title), width),
|
|
12508
14012
|
"",
|
|
12509
14013
|
...this.editor.render(width),
|
|
12510
14014
|
"",
|
|
12511
|
-
|
|
12512
|
-
|
|
14015
|
+
truncateToWidth2(this.theme.fg("dim", this.help), width),
|
|
14016
|
+
truncateToWidth2(
|
|
12513
14017
|
this.theme.fg(
|
|
12514
14018
|
"dim",
|
|
12515
14019
|
"Enter to save \xB7 Shift+Enter for newline \xB7 Esc to cancel"
|
|
@@ -12522,7 +14026,7 @@ var TextValueSubmenu = class {
|
|
|
12522
14026
|
this.editor.invalidate();
|
|
12523
14027
|
}
|
|
12524
14028
|
handleInput(data) {
|
|
12525
|
-
if (
|
|
14029
|
+
if (matchesKey2(data, Key2.escape)) {
|
|
12526
14030
|
this.done(void 0);
|
|
12527
14031
|
return;
|
|
12528
14032
|
}
|
|
@@ -12646,7 +14150,7 @@ function getSearchQueriesForDisplay(queries) {
|
|
|
12646
14150
|
function getAnswerQueriesForDisplay(queries) {
|
|
12647
14151
|
return getSearchQueriesForDisplay(queries);
|
|
12648
14152
|
}
|
|
12649
|
-
function createBatchCompletionReporter(verb, providerId,
|
|
14153
|
+
function createBatchCompletionReporter(verb, providerId, providerLabel2, total, report) {
|
|
12650
14154
|
if (!report) {
|
|
12651
14155
|
return {
|
|
12652
14156
|
start: () => {
|
|
@@ -12660,7 +14164,7 @@ function createBatchCompletionReporter(verb, providerId, providerLabel, total, r
|
|
|
12660
14164
|
let completedCount = 0;
|
|
12661
14165
|
let failedCount = 0;
|
|
12662
14166
|
const emit = () => {
|
|
12663
|
-
let message = `${verb} via ${
|
|
14167
|
+
let message = `${verb} via ${providerLabel2}: ${completedCount}/${total} completed`;
|
|
12664
14168
|
if (failedCount > 0) {
|
|
12665
14169
|
message += `, ${failedCount} failed`;
|
|
12666
14170
|
}
|
|
@@ -12712,8 +14216,8 @@ function renderMarkdownBlock(text) {
|
|
|
12712
14216
|
if (!text) {
|
|
12713
14217
|
return new Text("", 0, 0);
|
|
12714
14218
|
}
|
|
12715
|
-
return new
|
|
12716
|
-
${text}`, 0, 0,
|
|
14219
|
+
return new Markdown2(`
|
|
14220
|
+
${text}`, 0, 0, getMarkdownTheme2());
|
|
12717
14221
|
}
|
|
12718
14222
|
function renderBlockText(text, theme, color) {
|
|
12719
14223
|
if (!text) {
|
|
@@ -12747,12 +14251,12 @@ function prefixWithSymbol(text, symbol) {
|
|
|
12747
14251
|
}
|
|
12748
14252
|
function renderToolProgress(display, fallbackText, theme) {
|
|
12749
14253
|
const progress = display?.progress;
|
|
12750
|
-
const
|
|
12751
|
-
if (!progress || !
|
|
14254
|
+
const providerLabel2 = display?.provider?.label;
|
|
14255
|
+
if (!progress || !providerLabel2) {
|
|
12752
14256
|
return renderSimpleText(fallbackText ?? "Working\u2026", theme, "warning");
|
|
12753
14257
|
}
|
|
12754
14258
|
return new Text(
|
|
12755
|
-
`${theme.fg("warning", progress.action)} ${theme.fg("muted", `via ${
|
|
14259
|
+
`${theme.fg("warning", progress.action)} ${theme.fg("muted", `via ${providerLabel2}`)}`,
|
|
12756
14260
|
0,
|
|
12757
14261
|
0
|
|
12758
14262
|
);
|
|
@@ -12804,15 +14308,15 @@ function buildFailureSummary({
|
|
|
12804
14308
|
fallback
|
|
12805
14309
|
}) {
|
|
12806
14310
|
const detail = stripTrailingSentencePunctuation(getFirstLine2(text) ?? "");
|
|
12807
|
-
const
|
|
12808
|
-
if (!
|
|
14311
|
+
const providerLabel2 = details?.provider !== void 0 ? PROVIDERS_BY_ID[details.provider]?.label ?? details.provider : void 0;
|
|
14312
|
+
if (!providerLabel2) {
|
|
12809
14313
|
return detail || fallback;
|
|
12810
14314
|
}
|
|
12811
|
-
return formatProviderCapabilityFailure(
|
|
14315
|
+
return formatProviderCapabilityFailure(providerLabel2, capability, detail);
|
|
12812
14316
|
}
|
|
12813
|
-
function formatProviderCapabilityFailure(
|
|
14317
|
+
function formatProviderCapabilityFailure(providerLabel2, capability, detail) {
|
|
12814
14318
|
const action = getFailureAction(capability);
|
|
12815
|
-
const base2 = `${
|
|
14319
|
+
const base2 = `${providerLabel2} ${action} failed`;
|
|
12816
14320
|
if (!detail || detail === base2) {
|
|
12817
14321
|
return base2;
|
|
12818
14322
|
}
|
|
@@ -12820,14 +14324,14 @@ function formatProviderCapabilityFailure(providerLabel, capability, detail) {
|
|
|
12820
14324
|
return detail;
|
|
12821
14325
|
}
|
|
12822
14326
|
const normalizedDetail = normalizeProviderFailureDetail(
|
|
12823
|
-
|
|
14327
|
+
providerLabel2,
|
|
12824
14328
|
detail
|
|
12825
14329
|
);
|
|
12826
14330
|
return `${base2}: ${normalizedDetail}`;
|
|
12827
14331
|
}
|
|
12828
|
-
function normalizeProviderFailureDetail(
|
|
14332
|
+
function normalizeProviderFailureDetail(providerLabel2, detail) {
|
|
12829
14333
|
const normalized = stripTrailingSentencePunctuation(detail);
|
|
12830
|
-
const providerPrefix = `${
|
|
14334
|
+
const providerPrefix = `${providerLabel2}:`;
|
|
12831
14335
|
return normalized.toLowerCase().startsWith(providerPrefix.toLowerCase()) ? normalized.slice(providerPrefix.length).trim() : normalized;
|
|
12832
14336
|
}
|
|
12833
14337
|
function getFailureAction(capability) {
|
|
@@ -12876,7 +14380,7 @@ function getFirstLine2(text) {
|
|
|
12876
14380
|
}
|
|
12877
14381
|
function getExpandHint() {
|
|
12878
14382
|
try {
|
|
12879
|
-
const keys =
|
|
14383
|
+
const keys = getKeybindings2().getKeys("app.tools.expand");
|
|
12880
14384
|
if (keys.length > 0) {
|
|
12881
14385
|
return `${keys.join("/")} to expand`;
|
|
12882
14386
|
}
|
|
@@ -12884,11 +14388,11 @@ function getExpandHint() {
|
|
|
12884
14388
|
}
|
|
12885
14389
|
return "ctrl+o to expand";
|
|
12886
14390
|
}
|
|
12887
|
-
function
|
|
14391
|
+
function cleanSingleLine2(text) {
|
|
12888
14392
|
return text.replace(/\s+/g, " ").trim();
|
|
12889
14393
|
}
|
|
12890
14394
|
function formatQuotedPreview(text, maxLength = 80) {
|
|
12891
|
-
return `"${truncateInline(
|
|
14395
|
+
return `"${truncateInline(cleanSingleLine2(text), maxLength)}"`;
|
|
12892
14396
|
}
|
|
12893
14397
|
function formatSearchResponses(outcomes, prefetch) {
|
|
12894
14398
|
const body = outcomes.map(
|
|
@@ -12914,10 +14418,10 @@ function formatSearchOutcomeSection(outcome, index, total) {
|
|
|
12914
14418
|
${body}`;
|
|
12915
14419
|
}
|
|
12916
14420
|
function formatSearchHeading(query2) {
|
|
12917
|
-
return `"${escapeMarkdownText(
|
|
14421
|
+
return `"${escapeMarkdownText(cleanSingleLine2(query2))}"`;
|
|
12918
14422
|
}
|
|
12919
14423
|
function formatAnswerHeading(query2) {
|
|
12920
|
-
return `"${escapeMarkdownText(
|
|
14424
|
+
return `"${escapeMarkdownText(cleanSingleLine2(query2))}"`;
|
|
12921
14425
|
}
|
|
12922
14426
|
function collectSearchResultUrls(outcomes) {
|
|
12923
14427
|
return outcomes.flatMap(
|
|
@@ -12933,7 +14437,7 @@ function formatSearchResponseMarkdown(response) {
|
|
|
12933
14437
|
`${index + 1}. ${formatMarkdownLink(result.title, result.url)}`
|
|
12934
14438
|
];
|
|
12935
14439
|
if (result.snippet) {
|
|
12936
|
-
lines.push(` ${escapeMarkdownText(
|
|
14440
|
+
lines.push(` ${escapeMarkdownText(cleanSingleLine2(result.snippet))}`);
|
|
12937
14441
|
}
|
|
12938
14442
|
return lines.join("\n");
|
|
12939
14443
|
}).join("\n\n");
|
|
@@ -12942,7 +14446,7 @@ function formatMarkdownLink(label, url2) {
|
|
|
12942
14446
|
return `[${escapeMarkdownLinkLabel(label)}](<${url2}>)`;
|
|
12943
14447
|
}
|
|
12944
14448
|
function escapeMarkdownLinkLabel(text) {
|
|
12945
|
-
return
|
|
14449
|
+
return cleanSingleLine2(text).replaceAll("\\", "\\\\").replaceAll("]", "\\]");
|
|
12946
14450
|
}
|
|
12947
14451
|
function escapeMarkdownText(text) {
|
|
12948
14452
|
return text.replaceAll("\\", "\\\\").replaceAll("*", "\\*").replaceAll("_", "\\_").replaceAll("`", "\\`").replaceAll("#", "\\#").replaceAll("[", "\\[").replaceAll("]", "\\]");
|
|
@@ -12963,10 +14467,10 @@ async function truncateAndSaveWithMetadata(text, prefix) {
|
|
|
12963
14467
|
truncated: false
|
|
12964
14468
|
};
|
|
12965
14469
|
}
|
|
12966
|
-
const dir =
|
|
12967
|
-
await
|
|
12968
|
-
const fullPath =
|
|
12969
|
-
await
|
|
14470
|
+
const dir = join3(tmpdir(), `pi-web-providers-${prefix}-${Date.now()}`);
|
|
14471
|
+
await mkdir3(dir, { recursive: true });
|
|
14472
|
+
const fullPath = join3(dir, "output.txt");
|
|
14473
|
+
await writeFile3(fullPath, text, "utf-8");
|
|
12970
14474
|
return {
|
|
12971
14475
|
text: truncation.content + `
|
|
12972
14476
|
|
|
@@ -13073,6 +14577,12 @@ var __test__ = {
|
|
|
13073
14577
|
}),
|
|
13074
14578
|
extractTextContent,
|
|
13075
14579
|
formatWebResearchResultMessage,
|
|
14580
|
+
getActiveWebResearchRequests,
|
|
14581
|
+
getWebResearchTaskSnapshots,
|
|
14582
|
+
cancelWebResearchTask,
|
|
14583
|
+
loadWebResearchHistory,
|
|
14584
|
+
loadWebResearchPreview,
|
|
14585
|
+
loadWebResearchReport,
|
|
13076
14586
|
getAvailableManagedToolNames,
|
|
13077
14587
|
getReadyCompatibleProvidersForTool,
|
|
13078
14588
|
getEnabledCompatibleProvidersForTool: getReadyCompatibleProvidersForTool,
|
|
@@ -13090,9 +14600,10 @@ var __test__ = {
|
|
|
13090
14600
|
renderProviderToolResult,
|
|
13091
14601
|
renderWebResearchDispatchResult,
|
|
13092
14602
|
renderWebResearchResultMessage,
|
|
13093
|
-
|
|
13094
|
-
|
|
13095
|
-
|
|
14603
|
+
renderWebResearchReportMessage,
|
|
14604
|
+
formatWebResearchReportMessage,
|
|
14605
|
+
buildWebResearchWidgetLines,
|
|
14606
|
+
waitForPendingResearchTasks,
|
|
13096
14607
|
formatSearchResponses,
|
|
13097
14608
|
formatAnswerResponses
|
|
13098
14609
|
};
|