pi-web-providers 2.4.2 → 2.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 +80 -46
- package/dist/index.js +2204 -1590
- package/package.json +21 -20
package/dist/index.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
visibleWidth,
|
|
23
23
|
wrapTextWithAnsi
|
|
24
24
|
} from "@mariozechner/pi-tui";
|
|
25
|
-
import { Type as
|
|
25
|
+
import { Type as Type15 } from "typebox";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
28
28
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
@@ -95,6 +95,13 @@ function trimSnippet(input, maxLength = 300) {
|
|
|
95
95
|
if (text.length <= maxLength) return text;
|
|
96
96
|
return `${text.slice(0, maxLength - 1)}\u2026`;
|
|
97
97
|
}
|
|
98
|
+
function normalizeContentText(input) {
|
|
99
|
+
const text = (input ?? "").replace(/\r/g, "").trim();
|
|
100
|
+
if (!text) {
|
|
101
|
+
return "";
|
|
102
|
+
}
|
|
103
|
+
return text.split("\n").map((line) => line.replace(/[ \t]+$/g, "")).join("\n").replace(/\n{3,}/g, "\n\n");
|
|
104
|
+
}
|
|
98
105
|
function asJsonObject(value) {
|
|
99
106
|
return value ? { ...value } : {};
|
|
100
107
|
}
|
|
@@ -102,7 +109,38 @@ function formatJson(value) {
|
|
|
102
109
|
return JSON.stringify(value, null, 2);
|
|
103
110
|
}
|
|
104
111
|
function getApiKeyStatus(apiKeyReference) {
|
|
105
|
-
|
|
112
|
+
try {
|
|
113
|
+
return resolveConfigValue(apiKeyReference) ? { state: "ready" } : { state: "missing_api_key" };
|
|
114
|
+
} catch (error) {
|
|
115
|
+
return {
|
|
116
|
+
state: "invalid_config",
|
|
117
|
+
detail: formatConfigValueError(error)
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function formatConfigValueError(error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
return message.replace(/\s+/g, " ").trim() || "Failed to resolve config value";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// src/providers/definition.ts
|
|
127
|
+
function defineCapability(definition) {
|
|
128
|
+
return definition;
|
|
129
|
+
}
|
|
130
|
+
function defineProvider(definition) {
|
|
131
|
+
return definition;
|
|
132
|
+
}
|
|
133
|
+
function defineProviders(providers) {
|
|
134
|
+
return providers;
|
|
135
|
+
}
|
|
136
|
+
async function executeProviderCapability(definition, capability, input, context) {
|
|
137
|
+
const handler = definition.capabilities[capability];
|
|
138
|
+
if (!handler) {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`Provider '${definition.id}' does not support '${capability}'.`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return await handler.execute(input, context);
|
|
106
144
|
}
|
|
107
145
|
|
|
108
146
|
// src/providers/claude.ts
|
|
@@ -186,7 +224,7 @@ var claudeOptionsSchema = Type2.Object(
|
|
|
186
224
|
},
|
|
187
225
|
{ description: "Claude options." }
|
|
188
226
|
);
|
|
189
|
-
var
|
|
227
|
+
var claudeImplementation = {
|
|
190
228
|
id: "claude",
|
|
191
229
|
label: "Claude",
|
|
192
230
|
docsUrl: "https://github.com/anthropics/claude-agent-sdk-typescript",
|
|
@@ -427,533 +465,207 @@ function readString(value, key) {
|
|
|
427
465
|
}
|
|
428
466
|
return entry;
|
|
429
467
|
}
|
|
468
|
+
var claudeProvider = defineProvider({
|
|
469
|
+
id: "claude",
|
|
470
|
+
label: claudeImplementation.label,
|
|
471
|
+
docsUrl: claudeImplementation.docsUrl,
|
|
472
|
+
config: {
|
|
473
|
+
createTemplate: () => claudeImplementation.createTemplate(),
|
|
474
|
+
fields: ["pathToClaudeCodeExecutable", "options", "settings"]
|
|
475
|
+
},
|
|
476
|
+
getCapabilityStatus: (config, cwd, tool) => claudeImplementation.getCapabilityStatus(
|
|
477
|
+
config,
|
|
478
|
+
cwd,
|
|
479
|
+
tool
|
|
480
|
+
),
|
|
481
|
+
capabilities: {
|
|
482
|
+
search: defineCapability({
|
|
483
|
+
options: claudeImplementation.getToolOptionsSchema?.("search"),
|
|
484
|
+
async execute(input, ctx) {
|
|
485
|
+
const { query: query2, maxResults, options } = input;
|
|
486
|
+
return await claudeImplementation.search(
|
|
487
|
+
query2,
|
|
488
|
+
maxResults,
|
|
489
|
+
ctx.config,
|
|
490
|
+
ctx,
|
|
491
|
+
options
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
}),
|
|
495
|
+
answer: defineCapability({
|
|
496
|
+
options: claudeImplementation.getToolOptionsSchema?.("answer"),
|
|
497
|
+
async execute(input, ctx) {
|
|
498
|
+
return await claudeImplementation.answer(
|
|
499
|
+
input.query,
|
|
500
|
+
ctx.config,
|
|
501
|
+
ctx,
|
|
502
|
+
input.options
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
})
|
|
506
|
+
}
|
|
507
|
+
});
|
|
430
508
|
|
|
431
509
|
// src/providers/cloudflare.ts
|
|
432
|
-
import { Type as Type3 } from "typebox";
|
|
433
510
|
import CloudflareClient from "cloudflare";
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
return detail.trim().replace(/[.\s]+$/u, "");
|
|
456
|
-
}
|
|
457
|
-
function startsWithProviderLabel(providerLabel, detail) {
|
|
458
|
-
return detail.toLowerCase().startsWith(providerLabel.toLowerCase());
|
|
459
|
-
}
|
|
460
|
-
function readsLikeProviderClause(detail) {
|
|
461
|
-
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(
|
|
462
|
-
detail
|
|
463
|
-
);
|
|
464
|
-
}
|
|
465
|
-
function formatProviderDiagnostic(providerLabel, detail) {
|
|
466
|
-
const normalized = normalizeDiagnosticDetail(detail);
|
|
467
|
-
if (!normalized) {
|
|
468
|
-
return `${providerLabel} failed.`;
|
|
469
|
-
}
|
|
470
|
-
if (startsWithProviderLabel(providerLabel, normalized)) {
|
|
471
|
-
return `${normalized}.`;
|
|
472
|
-
}
|
|
473
|
-
if (readsLikeProviderClause(normalized)) {
|
|
474
|
-
return `${providerLabel} ${normalized}.`;
|
|
475
|
-
}
|
|
476
|
-
return `${providerLabel}: ${normalized}.`;
|
|
477
|
-
}
|
|
478
|
-
function formatResearchTerminalDiagnostic(providerLabel, status, detail) {
|
|
479
|
-
const normalized = detail ? normalizeDiagnosticDetail(detail) : "";
|
|
480
|
-
if (!normalized) {
|
|
481
|
-
return status === "cancelled" ? `${providerLabel} research was canceled.` : `${providerLabel} research failed.`;
|
|
482
|
-
}
|
|
483
|
-
if (startsWithProviderLabel(providerLabel, normalized)) {
|
|
484
|
-
return `${normalized}.`;
|
|
511
|
+
import { Type as Type3 } from "typebox";
|
|
512
|
+
var cloudflareContentsOptionsSchema = Type3.Object(
|
|
513
|
+
{
|
|
514
|
+
gotoOptions: Type3.Optional(
|
|
515
|
+
Type3.Object(
|
|
516
|
+
{
|
|
517
|
+
waitUntil: Type3.Optional(
|
|
518
|
+
literalUnion(
|
|
519
|
+
["load", "domcontentloaded", "networkidle0", "networkidle2"],
|
|
520
|
+
{ description: "When to consider navigation complete." }
|
|
521
|
+
)
|
|
522
|
+
)
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
description: "Navigation options."
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
)
|
|
529
|
+
},
|
|
530
|
+
{
|
|
531
|
+
description: "Cloudflare Browser Rendering options."
|
|
485
532
|
}
|
|
486
|
-
|
|
487
|
-
|
|
533
|
+
);
|
|
534
|
+
var cloudflareImplementation = {
|
|
535
|
+
id: "cloudflare",
|
|
536
|
+
label: "Cloudflare",
|
|
537
|
+
docsUrl: "https://developers.cloudflare.com/browser-rendering/rest-api/markdown-endpoint/",
|
|
538
|
+
getToolOptionsSchema(capability) {
|
|
539
|
+
switch (capability) {
|
|
540
|
+
case "contents":
|
|
541
|
+
return cloudflareContentsOptionsSchema;
|
|
542
|
+
default:
|
|
543
|
+
return void 0;
|
|
544
|
+
}
|
|
545
|
+
},
|
|
546
|
+
createTemplate() {
|
|
547
|
+
return {
|
|
548
|
+
apiToken: "CLOUDFLARE_API_TOKEN",
|
|
549
|
+
accountId: "CLOUDFLARE_ACCOUNT_ID",
|
|
550
|
+
options: {
|
|
551
|
+
gotoOptions: {
|
|
552
|
+
waitUntil: "networkidle0"
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
},
|
|
557
|
+
getCapabilityStatus(config) {
|
|
558
|
+
const apiTokenStatus = getApiKeyStatus(config?.apiToken);
|
|
559
|
+
if (apiTokenStatus.state !== "ready") {
|
|
560
|
+
return apiTokenStatus;
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
if (!resolveConfigValue(config?.accountId)) {
|
|
564
|
+
return { state: "invalid_config", detail: "Missing account ID" };
|
|
565
|
+
}
|
|
566
|
+
} catch (error) {
|
|
567
|
+
return {
|
|
568
|
+
state: "invalid_config",
|
|
569
|
+
detail: formatConfigValueError(error)
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
return { state: "ready" };
|
|
573
|
+
},
|
|
574
|
+
async contents(urls, config, context, options) {
|
|
575
|
+
const client = createClient(config);
|
|
576
|
+
const accountId = resolveConfigValue(config.accountId);
|
|
577
|
+
if (!accountId) {
|
|
578
|
+
throw new Error("is missing an account ID");
|
|
579
|
+
}
|
|
580
|
+
const defaults = asJsonObject(config.options);
|
|
581
|
+
const answers = await Promise.all(
|
|
582
|
+
urls.map(async (url) => {
|
|
583
|
+
try {
|
|
584
|
+
const markdown = await client.browserRendering.markdown.create(
|
|
585
|
+
{
|
|
586
|
+
...defaults ?? {},
|
|
587
|
+
...options ?? {},
|
|
588
|
+
account_id: accountId,
|
|
589
|
+
url
|
|
590
|
+
},
|
|
591
|
+
buildRequestOptions(context)
|
|
592
|
+
);
|
|
593
|
+
return {
|
|
594
|
+
url,
|
|
595
|
+
content: markdown
|
|
596
|
+
};
|
|
597
|
+
} catch (error) {
|
|
598
|
+
return {
|
|
599
|
+
url,
|
|
600
|
+
error: error instanceof Error ? error.message : String(error)
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
return {
|
|
606
|
+
provider: cloudflareImplementation.id,
|
|
607
|
+
answers
|
|
608
|
+
};
|
|
488
609
|
}
|
|
489
|
-
return status === "cancelled" ? `${providerLabel} research was canceled: ${normalized}.` : `${providerLabel} research failed: ${normalized}.`;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// src/execution-policy.ts
|
|
493
|
-
var MAX_RETRY_DELAY_MS = 3e4;
|
|
494
|
-
var RequestTimeoutError = class extends Error {
|
|
495
|
-
name = "RequestTimeoutError";
|
|
496
610
|
};
|
|
497
|
-
function
|
|
498
|
-
|
|
499
|
-
|
|
611
|
+
function createClient(config) {
|
|
612
|
+
const apiToken = resolveConfigValue(config.apiToken);
|
|
613
|
+
if (!apiToken) {
|
|
614
|
+
throw new Error("is missing an API token");
|
|
500
615
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
retryDelayMs: _retryDelayMs,
|
|
505
|
-
prefetch: _prefetch,
|
|
506
|
-
...rest
|
|
507
|
-
} = options;
|
|
508
|
-
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
616
|
+
return new CloudflareClient({
|
|
617
|
+
apiToken
|
|
618
|
+
});
|
|
509
619
|
}
|
|
510
|
-
function
|
|
511
|
-
return {
|
|
512
|
-
requestTimeoutMs: parseOptionalPositiveIntegerOption(
|
|
513
|
-
options,
|
|
514
|
-
"requestTimeoutMs"
|
|
515
|
-
),
|
|
516
|
-
retryCount: parseOptionalNonNegativeIntegerOption(options, "retryCount"),
|
|
517
|
-
retryDelayMs: parseOptionalPositiveIntegerOption(options, "retryDelayMs")
|
|
518
|
-
};
|
|
620
|
+
function buildRequestOptions(context) {
|
|
621
|
+
return context.signal ? { signal: context.signal } : void 0;
|
|
519
622
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
623
|
+
var cloudflareProvider = defineProvider({
|
|
624
|
+
id: "cloudflare",
|
|
625
|
+
label: cloudflareImplementation.label,
|
|
626
|
+
docsUrl: cloudflareImplementation.docsUrl,
|
|
627
|
+
config: {
|
|
628
|
+
createTemplate: () => cloudflareImplementation.createTemplate(),
|
|
629
|
+
fields: ["apiToken", "accountId", "options", "settings"]
|
|
630
|
+
},
|
|
631
|
+
getCapabilityStatus: (config, cwd, tool) => cloudflareImplementation.getCapabilityStatus(
|
|
632
|
+
config,
|
|
633
|
+
cwd,
|
|
634
|
+
tool
|
|
635
|
+
),
|
|
636
|
+
capabilities: {
|
|
637
|
+
contents: defineCapability({
|
|
638
|
+
options: cloudflareImplementation.getToolOptionsSchema?.("contents"),
|
|
639
|
+
async execute(input, ctx) {
|
|
640
|
+
return await cloudflareImplementation.contents(
|
|
641
|
+
input.urls,
|
|
642
|
+
ctx.config,
|
|
643
|
+
ctx,
|
|
644
|
+
input.options
|
|
645
|
+
);
|
|
542
646
|
}
|
|
543
|
-
|
|
544
|
-
settings.retryDelayMs * 2 ** (attempt - 1),
|
|
545
|
-
MAX_RETRY_DELAY_MS
|
|
546
|
-
);
|
|
547
|
-
context.onProgress?.(
|
|
548
|
-
`${label} failed (${formatErrorMessage(error)}). Retrying in ${formatDuration(delayMs)} (attempt ${attempt + 1}/${maxAttempts}).`
|
|
549
|
-
);
|
|
550
|
-
await sleep(delayMs, context.signal);
|
|
551
|
-
} finally {
|
|
552
|
-
cleanup();
|
|
553
|
-
}
|
|
647
|
+
})
|
|
554
648
|
}
|
|
555
|
-
|
|
556
|
-
}
|
|
557
|
-
async function executeAsyncResearch({
|
|
558
|
-
providerLabel,
|
|
559
|
-
providerId,
|
|
560
|
-
context,
|
|
561
|
-
pollIntervalMs = DEFAULT_RESEARCH_POLL_INTERVAL_MS,
|
|
562
|
-
timeoutMs = DEFAULT_RESEARCH_TIMEOUT_MS,
|
|
563
|
-
maxConsecutivePollErrors = DEFAULT_RESEARCH_MAX_CONSECUTIVE_POLL_ERRORS,
|
|
564
|
-
start,
|
|
565
|
-
poll
|
|
566
|
-
}) {
|
|
567
|
-
const timeoutMessage = `${providerLabel} research exceeded ${formatDuration(timeoutMs)}.`;
|
|
568
|
-
const deadline = createDeadlineSignal(
|
|
569
|
-
context.signal,
|
|
570
|
-
timeoutMs,
|
|
571
|
-
timeoutMessage
|
|
572
|
-
);
|
|
573
|
-
const researchContext = {
|
|
574
|
-
...context,
|
|
575
|
-
signal: deadline.signal
|
|
576
|
-
};
|
|
577
|
-
let lastStatus;
|
|
578
|
-
let lastProgressStatus;
|
|
579
|
-
const startedAt = Date.now();
|
|
580
|
-
try {
|
|
581
|
-
researchContext.onProgress?.(`Starting research via ${providerLabel}`);
|
|
582
|
-
const job = await withAbortAndOptionalTimeout(
|
|
583
|
-
start(researchContext),
|
|
584
|
-
void 0,
|
|
585
|
-
researchContext.signal,
|
|
586
|
-
void 0
|
|
587
|
-
);
|
|
588
|
-
const jobId = job.id;
|
|
589
|
-
if (!jobId) {
|
|
590
|
-
throw new Error(`${providerLabel} research did not return a job id.`);
|
|
591
|
-
}
|
|
592
|
-
researchContext.onProgress?.(`${providerLabel} research started: ${jobId}`);
|
|
593
|
-
let consecutivePollErrors = 0;
|
|
594
|
-
while (true) {
|
|
595
|
-
throwIfAborted(
|
|
596
|
-
researchContext.signal,
|
|
597
|
-
`${providerLabel} research aborted.`
|
|
598
|
-
);
|
|
599
|
-
try {
|
|
600
|
-
const result = await withAbortAndOptionalTimeout(
|
|
601
|
-
poll(jobId, researchContext),
|
|
602
|
-
void 0,
|
|
603
|
-
researchContext.signal,
|
|
604
|
-
void 0
|
|
605
|
-
);
|
|
606
|
-
consecutivePollErrors = 0;
|
|
607
|
-
const progressStatus = result.statusText ?? result.status;
|
|
608
|
-
if (result.status !== lastStatus || progressStatus !== lastProgressStatus) {
|
|
609
|
-
researchContext.onProgress?.(
|
|
610
|
-
`Research via ${providerLabel}: ${progressStatus} (${formatElapsed(Date.now() - startedAt)} elapsed)`
|
|
611
|
-
);
|
|
612
|
-
lastStatus = result.status;
|
|
613
|
-
lastProgressStatus = progressStatus;
|
|
614
|
-
}
|
|
615
|
-
if (result.status === "completed") {
|
|
616
|
-
return result.output ?? {
|
|
617
|
-
provider: providerId,
|
|
618
|
-
text: `${providerLabel} research completed without textual output.`
|
|
619
|
-
};
|
|
620
|
-
}
|
|
621
|
-
if (result.status === "failed" || result.status === "cancelled") {
|
|
622
|
-
throw new Error(
|
|
623
|
-
formatResearchTerminalDiagnostic(
|
|
624
|
-
providerLabel,
|
|
625
|
-
result.status,
|
|
626
|
-
result.error
|
|
627
|
-
)
|
|
628
|
-
);
|
|
629
|
-
}
|
|
630
|
-
} catch (error) {
|
|
631
|
-
if (isAbortErrorFromSignal(researchContext.signal, error)) {
|
|
632
|
-
throw error;
|
|
633
|
-
}
|
|
634
|
-
if (!isRetryableError(error)) {
|
|
635
|
-
throw normalizeError(error);
|
|
636
|
-
}
|
|
637
|
-
consecutivePollErrors += 1;
|
|
638
|
-
if (consecutivePollErrors >= maxConsecutivePollErrors) {
|
|
639
|
-
throw new Error(
|
|
640
|
-
`${providerLabel} research polling failed too many times in a row: ${formatErrorMessage(error)}`
|
|
641
|
-
);
|
|
642
|
-
}
|
|
643
|
-
researchContext.onProgress?.(
|
|
644
|
-
`${providerLabel} research poll is still retrying after transient errors (${consecutivePollErrors}/${maxConsecutivePollErrors} consecutive poll failures). Background job id: ${jobId}`
|
|
645
|
-
);
|
|
646
|
-
}
|
|
647
|
-
await sleep(pollIntervalMs, researchContext.signal);
|
|
648
|
-
}
|
|
649
|
-
} catch (error) {
|
|
650
|
-
if (isAbortErrorFromSignal(researchContext.signal, error)) {
|
|
651
|
-
throw new Error(
|
|
652
|
-
formatProviderDiagnostic(providerLabel, formatErrorMessage(error))
|
|
653
|
-
);
|
|
654
|
-
}
|
|
655
|
-
throw new Error(
|
|
656
|
-
formatProviderDiagnostic(providerLabel, formatErrorMessage(error))
|
|
657
|
-
);
|
|
658
|
-
} finally {
|
|
659
|
-
deadline.cleanup();
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
function shouldRetryError(error, settings) {
|
|
663
|
-
if (error instanceof RequestTimeoutError) {
|
|
664
|
-
return settings.retryOnTimeout === true;
|
|
665
|
-
}
|
|
666
|
-
return isRetryableError(error);
|
|
667
|
-
}
|
|
668
|
-
function isRetryableError(error) {
|
|
669
|
-
if (error instanceof RequestTimeoutError) {
|
|
670
|
-
return false;
|
|
671
|
-
}
|
|
672
|
-
const message = formatErrorMessage(error).toLowerCase();
|
|
673
|
-
if (!message || message === "operation aborted.") {
|
|
674
|
-
return false;
|
|
675
|
-
}
|
|
676
|
-
return /429|500|502|503|504|deadline exceeded|econnreset|ecanceled|ehostunreach|eai_again|enotfound|etimedout|fetch failed|gateway timeout|internal error|network|overloaded|rate limit|resource exhausted|socket hang up|temporarily unavailable|timeout|unavailable/.test(
|
|
677
|
-
message
|
|
678
|
-
);
|
|
679
|
-
}
|
|
680
|
-
function formatErrorMessage(error) {
|
|
681
|
-
if (error instanceof Error) {
|
|
682
|
-
return error.message;
|
|
683
|
-
}
|
|
684
|
-
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
685
|
-
return error.message;
|
|
686
|
-
}
|
|
687
|
-
return String(error);
|
|
688
|
-
}
|
|
689
|
-
function formatElapsed(ms) {
|
|
690
|
-
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
691
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
692
|
-
const seconds = totalSeconds % 60;
|
|
693
|
-
if (minutes > 0) {
|
|
694
|
-
return `${minutes}m ${seconds}s`;
|
|
695
|
-
}
|
|
696
|
-
return `${totalSeconds}s`;
|
|
697
|
-
}
|
|
698
|
-
function formatDuration(ms) {
|
|
699
|
-
if (ms >= 6e4) {
|
|
700
|
-
return formatElapsed(ms);
|
|
701
|
-
}
|
|
702
|
-
if (ms >= 1e3) {
|
|
703
|
-
return `${Math.floor(ms / 1e3)}s`;
|
|
704
|
-
}
|
|
705
|
-
return `${ms}ms`;
|
|
706
|
-
}
|
|
707
|
-
async function sleep(ms, signal) {
|
|
708
|
-
throwIfAborted(signal);
|
|
709
|
-
await new Promise((resolve2, reject) => {
|
|
710
|
-
const timer = setTimeout(() => {
|
|
711
|
-
signal?.removeEventListener("abort", onAbort);
|
|
712
|
-
resolve2();
|
|
713
|
-
}, ms);
|
|
714
|
-
const onAbort = () => {
|
|
715
|
-
clearTimeout(timer);
|
|
716
|
-
signal?.removeEventListener("abort", onAbort);
|
|
717
|
-
reject(getAbortError(signal));
|
|
718
|
-
};
|
|
719
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
function throwIfAborted(signal, message = "Operation aborted.") {
|
|
723
|
-
if (signal?.aborted) {
|
|
724
|
-
throw getAbortError(signal, message);
|
|
725
|
-
}
|
|
726
|
-
}
|
|
727
|
-
function createAttemptContext(context) {
|
|
728
|
-
const controller = new AbortController();
|
|
729
|
-
if (context.signal?.aborted) {
|
|
730
|
-
controller.abort(getAbortError(context.signal));
|
|
731
|
-
}
|
|
732
|
-
const onAbort = () => {
|
|
733
|
-
controller.abort(getAbortError(context.signal));
|
|
734
|
-
};
|
|
735
|
-
context.signal?.addEventListener("abort", onAbort, { once: true });
|
|
736
|
-
return {
|
|
737
|
-
context: {
|
|
738
|
-
...context,
|
|
739
|
-
signal: controller.signal
|
|
740
|
-
},
|
|
741
|
-
abort: (reason) => controller.abort(reason),
|
|
742
|
-
cleanup: () => context.signal?.removeEventListener("abort", onAbort)
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
async function withAbortAndOptionalTimeout(promise, timeoutMs, signal, message, onTimeout) {
|
|
746
|
-
if (timeoutMs === void 0 && !signal) {
|
|
747
|
-
return await promise;
|
|
748
|
-
}
|
|
749
|
-
throwIfAborted(signal);
|
|
750
|
-
return await new Promise((resolve2, reject) => {
|
|
751
|
-
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
752
|
-
onTimeout?.();
|
|
753
|
-
cleanup();
|
|
754
|
-
reject(
|
|
755
|
-
new RequestTimeoutError(
|
|
756
|
-
message ?? `Operation timed out after ${formatDuration(timeoutMs)}.`
|
|
757
|
-
)
|
|
758
|
-
);
|
|
759
|
-
}, timeoutMs);
|
|
760
|
-
const onAbort = () => {
|
|
761
|
-
cleanup();
|
|
762
|
-
reject(getAbortError(signal));
|
|
763
|
-
};
|
|
764
|
-
const cleanup = () => {
|
|
765
|
-
if (timer) {
|
|
766
|
-
clearTimeout(timer);
|
|
767
|
-
}
|
|
768
|
-
signal?.removeEventListener("abort", onAbort);
|
|
769
|
-
};
|
|
770
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
771
|
-
promise.then(
|
|
772
|
-
(value) => {
|
|
773
|
-
cleanup();
|
|
774
|
-
resolve2(value);
|
|
775
|
-
},
|
|
776
|
-
(error) => {
|
|
777
|
-
cleanup();
|
|
778
|
-
reject(error);
|
|
779
|
-
}
|
|
780
|
-
);
|
|
781
|
-
});
|
|
782
|
-
}
|
|
783
|
-
function getAbortError(signal, message = "Operation aborted.") {
|
|
784
|
-
const reason = signal?.reason;
|
|
785
|
-
if (reason instanceof Error) {
|
|
786
|
-
return reason;
|
|
787
|
-
}
|
|
788
|
-
if (typeof reason === "string" && reason.length > 0) {
|
|
789
|
-
return new Error(reason);
|
|
790
|
-
}
|
|
791
|
-
return new Error(message);
|
|
792
|
-
}
|
|
793
|
-
function isAbortErrorFromSignal(signal, error) {
|
|
794
|
-
return signal?.aborted === true && signal.reason === error;
|
|
795
|
-
}
|
|
796
|
-
function createDeadlineSignal(signal, timeoutMs, timeoutMessage) {
|
|
797
|
-
const controller = new AbortController();
|
|
798
|
-
if (signal?.aborted) {
|
|
799
|
-
controller.abort(getAbortError(signal));
|
|
800
|
-
}
|
|
801
|
-
const onAbort = () => {
|
|
802
|
-
controller.abort(getAbortError(signal));
|
|
803
|
-
};
|
|
804
|
-
signal?.addEventListener("abort", onAbort, { once: true });
|
|
805
|
-
const timer = setTimeout(() => {
|
|
806
|
-
controller.abort(new RequestTimeoutError(timeoutMessage));
|
|
807
|
-
}, timeoutMs);
|
|
808
|
-
return {
|
|
809
|
-
signal: controller.signal,
|
|
810
|
-
cleanup: () => {
|
|
811
|
-
clearTimeout(timer);
|
|
812
|
-
signal?.removeEventListener("abort", onAbort);
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
}
|
|
816
|
-
function normalizeError(error) {
|
|
817
|
-
return error instanceof Error ? error : new Error(formatErrorMessage(error));
|
|
818
|
-
}
|
|
819
|
-
function parseOptionalPositiveIntegerOption(options, key) {
|
|
820
|
-
const value = options?.[key];
|
|
821
|
-
if (value === void 0) {
|
|
822
|
-
return void 0;
|
|
823
|
-
}
|
|
824
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
|
|
825
|
-
throw new Error(`options.${key} must be a positive integer.`);
|
|
826
|
-
}
|
|
827
|
-
return value;
|
|
828
|
-
}
|
|
829
|
-
function parseOptionalNonNegativeIntegerOption(options, key) {
|
|
830
|
-
const value = options?.[key];
|
|
831
|
-
if (value === void 0) {
|
|
832
|
-
return void 0;
|
|
833
|
-
}
|
|
834
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
|
|
835
|
-
throw new Error(`options.${key} must be a non-negative integer.`);
|
|
836
|
-
}
|
|
837
|
-
return value;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
// src/providers/cloudflare.ts
|
|
841
|
-
var cloudflareContentsOptionsSchema = Type3.Object(
|
|
842
|
-
{
|
|
843
|
-
gotoOptions: Type3.Optional(
|
|
844
|
-
Type3.Object(
|
|
845
|
-
{
|
|
846
|
-
waitUntil: Type3.Optional(
|
|
847
|
-
literalUnion(
|
|
848
|
-
["load", "domcontentloaded", "networkidle0", "networkidle2"],
|
|
849
|
-
{ description: "When to consider navigation complete." }
|
|
850
|
-
)
|
|
851
|
-
)
|
|
852
|
-
},
|
|
853
|
-
{
|
|
854
|
-
description: "Navigation options."
|
|
855
|
-
}
|
|
856
|
-
)
|
|
857
|
-
)
|
|
858
|
-
},
|
|
859
|
-
{
|
|
860
|
-
description: "Cloudflare Browser Rendering options."
|
|
861
|
-
}
|
|
862
|
-
);
|
|
863
|
-
var cloudflareAdapter = {
|
|
864
|
-
id: "cloudflare",
|
|
865
|
-
label: "Cloudflare",
|
|
866
|
-
docsUrl: "https://developers.cloudflare.com/browser-rendering/rest-api/markdown-endpoint/",
|
|
867
|
-
getToolOptionsSchema(capability) {
|
|
868
|
-
switch (capability) {
|
|
869
|
-
case "contents":
|
|
870
|
-
return cloudflareContentsOptionsSchema;
|
|
871
|
-
default:
|
|
872
|
-
return void 0;
|
|
873
|
-
}
|
|
874
|
-
},
|
|
875
|
-
createTemplate() {
|
|
876
|
-
return {
|
|
877
|
-
apiToken: "CLOUDFLARE_API_TOKEN",
|
|
878
|
-
accountId: "CLOUDFLARE_ACCOUNT_ID",
|
|
879
|
-
options: {
|
|
880
|
-
gotoOptions: {
|
|
881
|
-
waitUntil: "networkidle0"
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
};
|
|
885
|
-
},
|
|
886
|
-
getCapabilityStatus(config) {
|
|
887
|
-
if (!resolveConfigValue(config?.apiToken)) {
|
|
888
|
-
return { state: "missing_api_key" };
|
|
889
|
-
}
|
|
890
|
-
if (!resolveConfigValue(config?.accountId)) {
|
|
891
|
-
return { state: "invalid_config", detail: "Missing account ID" };
|
|
892
|
-
}
|
|
893
|
-
return { state: "ready" };
|
|
894
|
-
},
|
|
895
|
-
async contents(urls, config, context, options) {
|
|
896
|
-
const client = createClient(config);
|
|
897
|
-
const accountId = resolveConfigValue(config.accountId);
|
|
898
|
-
if (!accountId) {
|
|
899
|
-
throw new Error("is missing an account ID");
|
|
900
|
-
}
|
|
901
|
-
const defaults = stripLocalExecutionOptions(asJsonObject(config.options));
|
|
902
|
-
const answers = await Promise.all(
|
|
903
|
-
urls.map(async (url) => {
|
|
904
|
-
try {
|
|
905
|
-
const markdown = await client.browserRendering.markdown.create(
|
|
906
|
-
{
|
|
907
|
-
...defaults ?? {},
|
|
908
|
-
...options ?? {},
|
|
909
|
-
account_id: accountId,
|
|
910
|
-
url
|
|
911
|
-
},
|
|
912
|
-
buildRequestOptions(context)
|
|
913
|
-
);
|
|
914
|
-
return {
|
|
915
|
-
url,
|
|
916
|
-
content: markdown
|
|
917
|
-
};
|
|
918
|
-
} catch (error) {
|
|
919
|
-
return {
|
|
920
|
-
url,
|
|
921
|
-
error: error instanceof Error ? error.message : String(error)
|
|
922
|
-
};
|
|
923
|
-
}
|
|
924
|
-
})
|
|
925
|
-
);
|
|
926
|
-
return {
|
|
927
|
-
provider: cloudflareAdapter.id,
|
|
928
|
-
answers
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
};
|
|
932
|
-
function createClient(config) {
|
|
933
|
-
const apiToken = resolveConfigValue(config.apiToken);
|
|
934
|
-
if (!apiToken) {
|
|
935
|
-
throw new Error("is missing an API token");
|
|
936
|
-
}
|
|
937
|
-
return new CloudflareClient({
|
|
938
|
-
apiToken
|
|
939
|
-
});
|
|
940
|
-
}
|
|
941
|
-
function buildRequestOptions(context) {
|
|
942
|
-
return context.signal ? { signal: context.signal } : void 0;
|
|
943
|
-
}
|
|
649
|
+
});
|
|
944
650
|
|
|
945
651
|
// src/providers/codex.ts
|
|
946
652
|
import { Codex as CodexClient } from "@openai/codex-sdk";
|
|
947
653
|
import { Type as Type4 } from "typebox";
|
|
948
|
-
var codexOutputSchema = Type4.Object(
|
|
949
|
-
|
|
950
|
-
Type4.
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
}
|
|
654
|
+
var codexOutputSchema = Type4.Object(
|
|
655
|
+
{
|
|
656
|
+
sources: Type4.Array(
|
|
657
|
+
Type4.Object(
|
|
658
|
+
{
|
|
659
|
+
title: Type4.String(),
|
|
660
|
+
url: Type4.String(),
|
|
661
|
+
snippet: Type4.String()
|
|
662
|
+
},
|
|
663
|
+
{ additionalProperties: false }
|
|
664
|
+
)
|
|
665
|
+
)
|
|
666
|
+
},
|
|
667
|
+
{ additionalProperties: false }
|
|
668
|
+
);
|
|
957
669
|
var codexSearchOptionsSchema = Type4.Object(
|
|
958
670
|
{
|
|
959
671
|
model: Type4.Optional(Type4.String({ description: "Codex model override." })),
|
|
@@ -982,7 +694,7 @@ var codexSearchOptionsSchema = Type4.Object(
|
|
|
982
694
|
},
|
|
983
695
|
{ description: "Codex search options." }
|
|
984
696
|
);
|
|
985
|
-
var
|
|
697
|
+
var codexImplementation = {
|
|
986
698
|
id: "codex",
|
|
987
699
|
label: "Codex",
|
|
988
700
|
docsUrl: "https://github.com/openai/codex/tree/main/sdk/typescript",
|
|
@@ -995,492 +707,955 @@ var codexAdapter = {
|
|
|
995
707
|
}
|
|
996
708
|
},
|
|
997
709
|
createTemplate() {
|
|
998
|
-
return {
|
|
999
|
-
options: {
|
|
1000
|
-
networkAccessEnabled: true,
|
|
1001
|
-
webSearchEnabled: true,
|
|
1002
|
-
webSearchMode: "live"
|
|
1003
|
-
}
|
|
1004
|
-
};
|
|
710
|
+
return {
|
|
711
|
+
options: {
|
|
712
|
+
networkAccessEnabled: true,
|
|
713
|
+
webSearchEnabled: true,
|
|
714
|
+
webSearchMode: "live"
|
|
715
|
+
}
|
|
716
|
+
};
|
|
717
|
+
},
|
|
718
|
+
getCapabilityStatus(config, _cwd) {
|
|
719
|
+
const effectiveConfig = config ?? codexImplementation.createTemplate();
|
|
720
|
+
try {
|
|
721
|
+
new CodexClient({
|
|
722
|
+
codexPathOverride: effectiveConfig.codexPath,
|
|
723
|
+
config: effectiveConfig.config
|
|
724
|
+
});
|
|
725
|
+
} catch (error) {
|
|
726
|
+
return {
|
|
727
|
+
state: "invalid_config",
|
|
728
|
+
detail: error.message
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
return { state: "ready" };
|
|
732
|
+
},
|
|
733
|
+
async search(query2, maxResults, config, context, options) {
|
|
734
|
+
const codex = new CodexClient({
|
|
735
|
+
codexPathOverride: config.codexPath,
|
|
736
|
+
baseUrl: config.baseUrl,
|
|
737
|
+
apiKey: resolveConfigValue(config.apiKey),
|
|
738
|
+
config: config.config,
|
|
739
|
+
env: resolveEnvMap(config.env)
|
|
740
|
+
});
|
|
741
|
+
const thread = codex.startThread(
|
|
742
|
+
buildCodexSearchThreadOptions(config, context.cwd, options)
|
|
743
|
+
);
|
|
744
|
+
const prompt = [
|
|
745
|
+
"You are performing web research for another coding agent.",
|
|
746
|
+
"Search the public web and return only a JSON object matching the provided schema.",
|
|
747
|
+
"Do not include markdown fences or extra commentary.",
|
|
748
|
+
`Return at most ${maxResults} sources.`,
|
|
749
|
+
"Prefer primary or official sources when they are available.",
|
|
750
|
+
"Each snippet should be short and specific.",
|
|
751
|
+
"",
|
|
752
|
+
`User query: ${query2}`
|
|
753
|
+
].join("\n");
|
|
754
|
+
const streamed = await thread.runStreamed(prompt, {
|
|
755
|
+
outputSchema: codexOutputSchema,
|
|
756
|
+
signal: context.signal
|
|
757
|
+
});
|
|
758
|
+
let finalResponse = "";
|
|
759
|
+
for await (const event of streamed.events) {
|
|
760
|
+
if (event.type === "item.completed" && event.item.type === "agent_message") {
|
|
761
|
+
finalResponse = event.item.text;
|
|
762
|
+
}
|
|
763
|
+
if (event.type === "turn.failed") {
|
|
764
|
+
throw new Error(event.error.message);
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
const parsed = parseOutput(finalResponse);
|
|
768
|
+
return {
|
|
769
|
+
provider: codexImplementation.id,
|
|
770
|
+
results: parsed.sources.slice(0, maxResults).map((source) => ({
|
|
771
|
+
title: source.title.trim(),
|
|
772
|
+
url: source.url.trim(),
|
|
773
|
+
snippet: trimSnippet(source.snippet)
|
|
774
|
+
}))
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
function buildCodexSearchThreadOptions(config, cwd, options) {
|
|
779
|
+
const callOptions = getCodexSearchCallOptions(options);
|
|
780
|
+
const providerOptions = config.options;
|
|
781
|
+
return {
|
|
782
|
+
additionalDirectories: providerOptions?.additionalDirectories,
|
|
783
|
+
approvalPolicy: "never",
|
|
784
|
+
model: callOptions.model ?? providerOptions?.model,
|
|
785
|
+
modelReasoningEffort: callOptions.modelReasoningEffort ?? providerOptions?.modelReasoningEffort,
|
|
786
|
+
networkAccessEnabled: providerOptions?.networkAccessEnabled ?? true,
|
|
787
|
+
sandboxMode: "read-only",
|
|
788
|
+
skipGitRepoCheck: true,
|
|
789
|
+
webSearchEnabled: providerOptions?.webSearchEnabled ?? true,
|
|
790
|
+
webSearchMode: callOptions.webSearchMode ?? providerOptions?.webSearchMode ?? "live",
|
|
791
|
+
workingDirectory: cwd
|
|
792
|
+
};
|
|
793
|
+
}
|
|
794
|
+
function getCodexSearchCallOptions(options) {
|
|
795
|
+
if (!options) {
|
|
796
|
+
return {};
|
|
797
|
+
}
|
|
798
|
+
const model = readNonEmptyString2(options.model);
|
|
799
|
+
const modelReasoningEffort = readEnum2(options.modelReasoningEffort, [
|
|
800
|
+
"minimal",
|
|
801
|
+
"low",
|
|
802
|
+
"medium",
|
|
803
|
+
"high",
|
|
804
|
+
"xhigh"
|
|
805
|
+
]);
|
|
806
|
+
const webSearchMode = readEnum2(options.webSearchMode, [
|
|
807
|
+
"disabled",
|
|
808
|
+
"cached",
|
|
809
|
+
"live"
|
|
810
|
+
]);
|
|
811
|
+
return {
|
|
812
|
+
...model ? { model } : {},
|
|
813
|
+
...modelReasoningEffort ? { modelReasoningEffort } : {},
|
|
814
|
+
...webSearchMode ? { webSearchMode } : {}
|
|
815
|
+
};
|
|
816
|
+
}
|
|
817
|
+
function readNonEmptyString2(value) {
|
|
818
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
819
|
+
}
|
|
820
|
+
function readEnum2(value, values) {
|
|
821
|
+
return typeof value === "string" && values.includes(value) ? value : void 0;
|
|
822
|
+
}
|
|
823
|
+
function isJsonObject(value) {
|
|
824
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
825
|
+
}
|
|
826
|
+
function parseOutput(raw) {
|
|
827
|
+
const json = extractJsonObject(raw);
|
|
828
|
+
if (!isJsonObject(json) || !Array.isArray(json.sources) || json.sources.some(
|
|
829
|
+
(source) => !isJsonObject(source) || typeof source.title !== "string" || typeof source.url !== "string" || typeof source.snippet !== "string"
|
|
830
|
+
)) {
|
|
831
|
+
throw new Error("returned invalid JSON output");
|
|
832
|
+
}
|
|
833
|
+
return json;
|
|
834
|
+
}
|
|
835
|
+
function extractJsonObject(raw) {
|
|
836
|
+
if (!raw.trim()) {
|
|
837
|
+
throw new Error("returned an empty response");
|
|
838
|
+
}
|
|
839
|
+
try {
|
|
840
|
+
return JSON.parse(raw);
|
|
841
|
+
} catch {
|
|
842
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
843
|
+
if (!match) {
|
|
844
|
+
throw new Error("returned invalid JSON output");
|
|
845
|
+
}
|
|
846
|
+
try {
|
|
847
|
+
return JSON.parse(match[0]);
|
|
848
|
+
} catch {
|
|
849
|
+
throw new Error("returned invalid JSON output");
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
var codexProvider = defineProvider({
|
|
854
|
+
id: "codex",
|
|
855
|
+
label: codexImplementation.label,
|
|
856
|
+
docsUrl: codexImplementation.docsUrl,
|
|
857
|
+
config: {
|
|
858
|
+
createTemplate: () => codexImplementation.createTemplate(),
|
|
859
|
+
fields: [
|
|
860
|
+
"codexPath",
|
|
861
|
+
"baseUrl",
|
|
862
|
+
"apiKey",
|
|
863
|
+
"env",
|
|
864
|
+
"config",
|
|
865
|
+
"options",
|
|
866
|
+
"settings"
|
|
867
|
+
]
|
|
868
|
+
},
|
|
869
|
+
getCapabilityStatus: (config, cwd, tool) => codexImplementation.getCapabilityStatus(
|
|
870
|
+
config,
|
|
871
|
+
cwd,
|
|
872
|
+
tool
|
|
873
|
+
),
|
|
874
|
+
capabilities: {
|
|
875
|
+
search: defineCapability({
|
|
876
|
+
options: codexImplementation.getToolOptionsSchema?.("search"),
|
|
877
|
+
async execute(input, ctx) {
|
|
878
|
+
const { query: query2, maxResults, options } = input;
|
|
879
|
+
return await codexImplementation.search(
|
|
880
|
+
query2,
|
|
881
|
+
maxResults,
|
|
882
|
+
ctx.config,
|
|
883
|
+
ctx,
|
|
884
|
+
options
|
|
885
|
+
);
|
|
886
|
+
}
|
|
887
|
+
})
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
// src/providers/cli-json.ts
|
|
892
|
+
import { spawn } from "node:child_process";
|
|
893
|
+
import { isAbsolute, resolve } from "node:path";
|
|
894
|
+
async function runCliJsonCommand({
|
|
895
|
+
command,
|
|
896
|
+
payload,
|
|
897
|
+
context,
|
|
898
|
+
label
|
|
899
|
+
}) {
|
|
900
|
+
const argv = normalizeArgv(command);
|
|
901
|
+
const cwd = resolveCommandCwd(command.cwd, context.cwd);
|
|
902
|
+
const env = {
|
|
903
|
+
...process.env,
|
|
904
|
+
...resolveEnvMap(command.env) ?? {}
|
|
905
|
+
};
|
|
906
|
+
return await new Promise((resolvePromise, rejectPromise) => {
|
|
907
|
+
let settled = false;
|
|
908
|
+
let stdout = "";
|
|
909
|
+
let stderr = "";
|
|
910
|
+
let abortTimer;
|
|
911
|
+
const child = spawn(argv[0], argv.slice(1), {
|
|
912
|
+
cwd,
|
|
913
|
+
env,
|
|
914
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
915
|
+
});
|
|
916
|
+
const rejectOnce = (error) => {
|
|
917
|
+
if (settled) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
settled = true;
|
|
921
|
+
if (abortTimer) {
|
|
922
|
+
clearTimeout(abortTimer);
|
|
923
|
+
}
|
|
924
|
+
context.signal?.removeEventListener("abort", onAbort);
|
|
925
|
+
rejectPromise(error);
|
|
926
|
+
};
|
|
927
|
+
const resolveOnce = (value) => {
|
|
928
|
+
if (settled) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
settled = true;
|
|
932
|
+
if (abortTimer) {
|
|
933
|
+
clearTimeout(abortTimer);
|
|
934
|
+
}
|
|
935
|
+
context.signal?.removeEventListener("abort", onAbort);
|
|
936
|
+
resolvePromise(value);
|
|
937
|
+
};
|
|
938
|
+
const onAbort = () => {
|
|
939
|
+
child.kill("SIGTERM");
|
|
940
|
+
abortTimer = setTimeout(() => {
|
|
941
|
+
child.kill("SIGKILL");
|
|
942
|
+
}, 1e3);
|
|
943
|
+
};
|
|
944
|
+
if (context.signal?.aborted) {
|
|
945
|
+
onAbort();
|
|
946
|
+
} else {
|
|
947
|
+
context.signal?.addEventListener("abort", onAbort, { once: true });
|
|
948
|
+
}
|
|
949
|
+
child.stdout.setEncoding("utf8");
|
|
950
|
+
child.stdout.on("data", (chunk) => {
|
|
951
|
+
stdout += chunk;
|
|
952
|
+
});
|
|
953
|
+
child.stderr.setEncoding("utf8");
|
|
954
|
+
child.stderr.on("data", (chunk) => {
|
|
955
|
+
stderr += chunk;
|
|
956
|
+
});
|
|
957
|
+
child.on("error", (error) => {
|
|
958
|
+
rejectOnce(
|
|
959
|
+
new Error(
|
|
960
|
+
`${label} failed to start: ${error.message || String(error)}`
|
|
961
|
+
)
|
|
962
|
+
);
|
|
963
|
+
});
|
|
964
|
+
child.on("close", (code, signal) => {
|
|
965
|
+
if (context.signal?.aborted) {
|
|
966
|
+
rejectOnce(new Error(`${label} was aborted.`));
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
if (code !== 0) {
|
|
970
|
+
const detail = stderr.trim() || `exit code ${code ?? "unknown"}`;
|
|
971
|
+
rejectOnce(
|
|
972
|
+
new Error(
|
|
973
|
+
signal ? `${label} exited via signal ${signal}: ${detail}` : `${label} failed with exit code ${code}: ${detail}`
|
|
974
|
+
)
|
|
975
|
+
);
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const trimmed = stdout.trim();
|
|
979
|
+
if (!trimmed) {
|
|
980
|
+
rejectOnce(new Error(`${label} did not write JSON to stdout.`));
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
resolveOnce(JSON.parse(trimmed));
|
|
985
|
+
} catch (error) {
|
|
986
|
+
rejectOnce(
|
|
987
|
+
new Error(
|
|
988
|
+
`${label} returned invalid JSON: ${error.message}`
|
|
989
|
+
)
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
});
|
|
993
|
+
child.stdin.on("error", () => {
|
|
994
|
+
});
|
|
995
|
+
child.stdin.end(`${JSON.stringify(payload)}
|
|
996
|
+
`);
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
function normalizeArgv(command) {
|
|
1000
|
+
const argv = command.argv?.filter((entry) => entry.trim().length > 0) ?? [];
|
|
1001
|
+
if (argv.length === 0) {
|
|
1002
|
+
throw new Error("command is missing argv");
|
|
1003
|
+
}
|
|
1004
|
+
return argv;
|
|
1005
|
+
}
|
|
1006
|
+
function resolveCommandCwd(commandCwd, fallbackCwd) {
|
|
1007
|
+
if (!commandCwd || commandCwd.trim().length === 0) {
|
|
1008
|
+
return fallbackCwd;
|
|
1009
|
+
}
|
|
1010
|
+
return isAbsolute(commandCwd) ? commandCwd : resolve(fallbackCwd, commandCwd);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
// src/providers/custom.ts
|
|
1014
|
+
var customImplementation = {
|
|
1015
|
+
id: "custom",
|
|
1016
|
+
label: "Custom",
|
|
1017
|
+
docsUrl: "https://github.com/mavam/pi-web-providers#custom-provider",
|
|
1018
|
+
getToolOptionsSchema(_capability) {
|
|
1019
|
+
return void 0;
|
|
1020
|
+
},
|
|
1021
|
+
createTemplate() {
|
|
1022
|
+
return {};
|
|
1005
1023
|
},
|
|
1006
|
-
getCapabilityStatus(config, _cwd) {
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
new CodexClient({
|
|
1010
|
-
codexPathOverride: effectiveConfig.codexPath,
|
|
1011
|
-
config: effectiveConfig.config
|
|
1012
|
-
});
|
|
1013
|
-
} catch (error) {
|
|
1014
|
-
return {
|
|
1015
|
-
state: "invalid_config",
|
|
1016
|
-
detail: error.message
|
|
1017
|
-
};
|
|
1024
|
+
getCapabilityStatus(config, _cwd, capability) {
|
|
1025
|
+
if (capability) {
|
|
1026
|
+
return hasCommandForCapability(config, capability) ? { state: "ready" } : { state: "missing_command" };
|
|
1018
1027
|
}
|
|
1019
|
-
return { state: "ready" };
|
|
1028
|
+
return hasAnyCommand(config) ? { state: "ready" } : { state: "missing_command" };
|
|
1020
1029
|
},
|
|
1021
1030
|
async search(query2, maxResults, config, context, options) {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1031
|
+
const output = await runCommand({
|
|
1032
|
+
capability: "search",
|
|
1033
|
+
payload: {
|
|
1034
|
+
capability: "search",
|
|
1035
|
+
query: query2,
|
|
1036
|
+
maxResults,
|
|
1037
|
+
...options ? { options } : {}
|
|
1038
|
+
},
|
|
1039
|
+
config,
|
|
1040
|
+
context
|
|
1028
1041
|
});
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
].join("\n");
|
|
1042
|
-
const streamed = await thread.runStreamed(prompt, {
|
|
1043
|
-
outputSchema: codexOutputSchema,
|
|
1044
|
-
signal: context.signal
|
|
1042
|
+
return parseSearchResponse(output, customImplementation.id);
|
|
1043
|
+
},
|
|
1044
|
+
async contents(urls, config, context, options) {
|
|
1045
|
+
const output = await runCommand({
|
|
1046
|
+
capability: "contents",
|
|
1047
|
+
payload: {
|
|
1048
|
+
capability: "contents",
|
|
1049
|
+
urls,
|
|
1050
|
+
...options ? { options } : {}
|
|
1051
|
+
},
|
|
1052
|
+
config,
|
|
1053
|
+
context
|
|
1045
1054
|
});
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1055
|
+
return parseContentsResponse(output, customImplementation.id);
|
|
1056
|
+
},
|
|
1057
|
+
async answer(query2, config, context, options) {
|
|
1058
|
+
const output = await runCommand({
|
|
1059
|
+
capability: "answer",
|
|
1060
|
+
payload: {
|
|
1061
|
+
capability: "answer",
|
|
1062
|
+
query: query2,
|
|
1063
|
+
...options ? { options } : {}
|
|
1064
|
+
},
|
|
1065
|
+
config,
|
|
1066
|
+
context
|
|
1067
|
+
});
|
|
1068
|
+
return parseToolOutput(output, customImplementation.id);
|
|
1069
|
+
},
|
|
1070
|
+
async research(input, config, context, options) {
|
|
1071
|
+
const output = await runCommand({
|
|
1072
|
+
capability: "research",
|
|
1073
|
+
payload: {
|
|
1074
|
+
capability: "research",
|
|
1075
|
+
input,
|
|
1076
|
+
...options ? { options } : {}
|
|
1077
|
+
},
|
|
1078
|
+
config,
|
|
1079
|
+
context
|
|
1080
|
+
});
|
|
1081
|
+
return parseToolOutput(output, customImplementation.id);
|
|
1064
1082
|
}
|
|
1065
1083
|
};
|
|
1066
|
-
function
|
|
1067
|
-
|
|
1068
|
-
|
|
1084
|
+
async function runCommand({
|
|
1085
|
+
capability,
|
|
1086
|
+
payload,
|
|
1087
|
+
config,
|
|
1088
|
+
context
|
|
1089
|
+
}) {
|
|
1090
|
+
const command = getCommandConfig(config, capability);
|
|
1091
|
+
if (!command) {
|
|
1092
|
+
throw new Error(`has no command configured for ${capability}`);
|
|
1093
|
+
}
|
|
1094
|
+
return await runCliJsonCommand({
|
|
1095
|
+
command,
|
|
1096
|
+
payload: {
|
|
1097
|
+
...payload,
|
|
1098
|
+
cwd: context.cwd
|
|
1099
|
+
},
|
|
1100
|
+
context,
|
|
1101
|
+
label: `Custom ${capability}`
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
function getCommandConfig(config, capability) {
|
|
1105
|
+
return config?.options?.[capability];
|
|
1106
|
+
}
|
|
1107
|
+
function hasCommandForCapability(config, capability) {
|
|
1108
|
+
return normalizeConfiguredArgv(getCommandConfig(config, capability)).length > 0;
|
|
1109
|
+
}
|
|
1110
|
+
function hasAnyCommand(config) {
|
|
1111
|
+
return hasCommandForCapability(config, "search") || hasCommandForCapability(config, "contents") || hasCommandForCapability(config, "answer") || hasCommandForCapability(config, "research");
|
|
1112
|
+
}
|
|
1113
|
+
function normalizeConfiguredArgv(command) {
|
|
1114
|
+
return command?.argv?.filter((entry) => entry.trim().length > 0) ?? [];
|
|
1115
|
+
}
|
|
1116
|
+
function parseSearchResponse(value, providerId) {
|
|
1117
|
+
const response = requireObject(value, "search output must be a JSON object");
|
|
1118
|
+
if (!Array.isArray(response.results)) {
|
|
1119
|
+
throw new Error("search output must include a 'results' array");
|
|
1120
|
+
}
|
|
1069
1121
|
return {
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1122
|
+
provider: providerId,
|
|
1123
|
+
results: response.results.map(
|
|
1124
|
+
(entry, index) => parseSearchResult(entry, index)
|
|
1125
|
+
)
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
function parseSearchResult(entry, index) {
|
|
1129
|
+
const value = requireObject(
|
|
1130
|
+
entry,
|
|
1131
|
+
`search result at index ${index} must be a JSON object`
|
|
1132
|
+
);
|
|
1133
|
+
const metadata = readLenientJsonObject(value.metadata);
|
|
1134
|
+
return {
|
|
1135
|
+
title: readRequiredString(value.title, `results[${index}].title`),
|
|
1136
|
+
url: readRequiredString(value.url, `results[${index}].url`),
|
|
1137
|
+
snippet: readRequiredString(value.snippet, `results[${index}].snippet`),
|
|
1138
|
+
...typeof value.score === "number" ? { score: value.score } : {},
|
|
1139
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
function parseContentsResponse(value, providerId) {
|
|
1143
|
+
const response = requireObject(
|
|
1144
|
+
value,
|
|
1145
|
+
"contents output must be a JSON object"
|
|
1146
|
+
);
|
|
1147
|
+
if (!Array.isArray(response.answers)) {
|
|
1148
|
+
throw new Error("contents output must include an 'answers' array");
|
|
1149
|
+
}
|
|
1150
|
+
return {
|
|
1151
|
+
provider: providerId,
|
|
1152
|
+
answers: response.answers.map(
|
|
1153
|
+
(entry, index) => parseContentsAnswer(entry, index)
|
|
1154
|
+
)
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
function parseContentsAnswer(entry, index) {
|
|
1158
|
+
const value = requireObject(
|
|
1159
|
+
entry,
|
|
1160
|
+
`contents answer at index ${index} must be a JSON object`
|
|
1161
|
+
);
|
|
1162
|
+
const url = readRequiredString(value.url, `answers[${index}].url`);
|
|
1163
|
+
const content = readOptionalString(
|
|
1164
|
+
value.content,
|
|
1165
|
+
`answers[${index}].content`
|
|
1166
|
+
);
|
|
1167
|
+
const summary = value.summary;
|
|
1168
|
+
const metadata = readRequiredJsonObject(
|
|
1169
|
+
value.metadata,
|
|
1170
|
+
`answers[${index}].metadata`
|
|
1171
|
+
);
|
|
1172
|
+
const error = readOptionalString(value.error, `answers[${index}].error`);
|
|
1173
|
+
if (content === void 0 && error === void 0) {
|
|
1174
|
+
throw new Error(
|
|
1175
|
+
`contents answer at index ${index} must include 'content' or 'error'`
|
|
1176
|
+
);
|
|
1177
|
+
}
|
|
1178
|
+
return {
|
|
1179
|
+
url,
|
|
1180
|
+
...content !== void 0 ? { content } : {},
|
|
1181
|
+
...summary !== void 0 ? { summary } : {},
|
|
1182
|
+
...metadata !== void 0 ? { metadata } : {},
|
|
1183
|
+
...error !== void 0 ? { error } : {}
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
function parseToolOutput(value, providerId) {
|
|
1187
|
+
const output = requireObject(value, "output must be a JSON object");
|
|
1188
|
+
const metadata = readLenientJsonObject(output.metadata);
|
|
1189
|
+
return {
|
|
1190
|
+
provider: providerId,
|
|
1191
|
+
text: readRequiredString(output.text, "text"),
|
|
1192
|
+
...readOptionalNonNegativeInteger(output.itemCount),
|
|
1193
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
1080
1194
|
};
|
|
1081
1195
|
}
|
|
1082
|
-
function
|
|
1083
|
-
if (
|
|
1084
|
-
return
|
|
1196
|
+
function readRequiredJsonObject(value, field) {
|
|
1197
|
+
if (value === void 0) {
|
|
1198
|
+
return void 0;
|
|
1085
1199
|
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1200
|
+
return requireObject(value, `output field '${field}' must be a JSON object`);
|
|
1201
|
+
}
|
|
1202
|
+
function readLenientJsonObject(value) {
|
|
1203
|
+
return isJsonObject2(value) ? value : void 0;
|
|
1204
|
+
}
|
|
1205
|
+
function readRequiredString(value, field) {
|
|
1206
|
+
if (typeof value !== "string") {
|
|
1207
|
+
throw new Error(`output field '${field}' must be a string`);
|
|
1208
|
+
}
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
function readOptionalString(value, field) {
|
|
1212
|
+
if (value === void 0) {
|
|
1213
|
+
return void 0;
|
|
1214
|
+
}
|
|
1215
|
+
return readRequiredString(value, field);
|
|
1216
|
+
}
|
|
1217
|
+
function readOptionalNonNegativeInteger(value) {
|
|
1218
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 0 ? { itemCount: value } : {};
|
|
1219
|
+
}
|
|
1220
|
+
function requireObject(value, message) {
|
|
1221
|
+
if (!isJsonObject2(value)) {
|
|
1222
|
+
throw new Error(message);
|
|
1223
|
+
}
|
|
1224
|
+
return value;
|
|
1225
|
+
}
|
|
1226
|
+
function isJsonObject2(value) {
|
|
1227
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1228
|
+
}
|
|
1229
|
+
var customProvider = defineProvider({
|
|
1230
|
+
id: "custom",
|
|
1231
|
+
label: customImplementation.label,
|
|
1232
|
+
docsUrl: customImplementation.docsUrl,
|
|
1233
|
+
config: {
|
|
1234
|
+
createTemplate: () => customImplementation.createTemplate(),
|
|
1235
|
+
fields: ["customOptions", "settings"]
|
|
1236
|
+
},
|
|
1237
|
+
getCapabilityStatus: (config, cwd, tool) => customImplementation.getCapabilityStatus(
|
|
1238
|
+
config,
|
|
1239
|
+
cwd,
|
|
1240
|
+
tool
|
|
1241
|
+
),
|
|
1242
|
+
capabilities: {
|
|
1243
|
+
search: defineCapability({
|
|
1244
|
+
options: customImplementation.getToolOptionsSchema?.("search"),
|
|
1245
|
+
async execute(input, ctx) {
|
|
1246
|
+
const { query: query2, maxResults, options } = input;
|
|
1247
|
+
return await customImplementation.search(
|
|
1248
|
+
query2,
|
|
1249
|
+
maxResults,
|
|
1250
|
+
ctx.config,
|
|
1251
|
+
ctx,
|
|
1252
|
+
options
|
|
1253
|
+
);
|
|
1254
|
+
}
|
|
1255
|
+
}),
|
|
1256
|
+
contents: defineCapability({
|
|
1257
|
+
options: customImplementation.getToolOptionsSchema?.("contents"),
|
|
1258
|
+
async execute(input, ctx) {
|
|
1259
|
+
return await customImplementation.contents(
|
|
1260
|
+
input.urls,
|
|
1261
|
+
ctx.config,
|
|
1262
|
+
ctx,
|
|
1263
|
+
input.options
|
|
1264
|
+
);
|
|
1265
|
+
}
|
|
1266
|
+
}),
|
|
1267
|
+
answer: defineCapability({
|
|
1268
|
+
options: customImplementation.getToolOptionsSchema?.("answer"),
|
|
1269
|
+
async execute(input, ctx) {
|
|
1270
|
+
return await customImplementation.answer(
|
|
1271
|
+
input.query,
|
|
1272
|
+
ctx.config,
|
|
1273
|
+
ctx,
|
|
1274
|
+
input.options
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
}),
|
|
1278
|
+
research: defineCapability({
|
|
1279
|
+
options: customImplementation.getToolOptionsSchema?.("research"),
|
|
1280
|
+
async execute(input, ctx) {
|
|
1281
|
+
return await customImplementation.research(
|
|
1282
|
+
input.input,
|
|
1283
|
+
ctx.config,
|
|
1284
|
+
ctx,
|
|
1285
|
+
input.options
|
|
1286
|
+
);
|
|
1287
|
+
}
|
|
1288
|
+
})
|
|
1289
|
+
}
|
|
1290
|
+
});
|
|
1291
|
+
|
|
1292
|
+
// src/providers/exa.ts
|
|
1293
|
+
import { Exa as ExaClient } from "exa-js";
|
|
1294
|
+
import { Type as Type5 } from "typebox";
|
|
1295
|
+
|
|
1296
|
+
// src/execution-policy-defaults.ts
|
|
1297
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
1298
|
+
var DEFAULT_RETRY_COUNT = 3;
|
|
1299
|
+
var DEFAULT_RETRY_DELAY_MS = 2e3;
|
|
1300
|
+
var DEFAULT_RESEARCH_POLL_INTERVAL_MS = 3e3;
|
|
1301
|
+
var DEFAULT_RESEARCH_TIMEOUT_MS = 18e5;
|
|
1302
|
+
var DEFAULT_RESEARCH_MAX_CONSECUTIVE_POLL_ERRORS = 3;
|
|
1303
|
+
var DEFAULT_GEMINI_RESEARCH_MAX_CONSECUTIVE_POLL_ERRORS = 10;
|
|
1304
|
+
function createDefaultExecutionSettings(overrides = {}) {
|
|
1099
1305
|
return {
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1306
|
+
requestTimeoutMs: DEFAULT_REQUEST_TIMEOUT_MS,
|
|
1307
|
+
retryCount: DEFAULT_RETRY_COUNT,
|
|
1308
|
+
retryDelayMs: DEFAULT_RETRY_DELAY_MS,
|
|
1309
|
+
researchTimeoutMs: DEFAULT_RESEARCH_TIMEOUT_MS,
|
|
1310
|
+
...overrides
|
|
1103
1311
|
};
|
|
1104
1312
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1313
|
+
|
|
1314
|
+
// src/provider-diagnostics.ts
|
|
1315
|
+
function normalizeDiagnosticDetail(detail) {
|
|
1316
|
+
return detail.trim().replace(/[.\s]+$/u, "");
|
|
1107
1317
|
}
|
|
1108
|
-
function
|
|
1109
|
-
return
|
|
1318
|
+
function startsWithProviderLabel(providerLabel, detail) {
|
|
1319
|
+
return detail.toLowerCase().startsWith(providerLabel.toLowerCase());
|
|
1110
1320
|
}
|
|
1111
|
-
function
|
|
1112
|
-
return
|
|
1321
|
+
function readsLikeProviderClause(detail) {
|
|
1322
|
+
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(
|
|
1323
|
+
detail
|
|
1324
|
+
);
|
|
1113
1325
|
}
|
|
1114
|
-
function
|
|
1115
|
-
const
|
|
1116
|
-
if (!
|
|
1117
|
-
|
|
1118
|
-
)) {
|
|
1119
|
-
throw new Error("returned invalid JSON output");
|
|
1326
|
+
function formatProviderDiagnostic(providerLabel, detail) {
|
|
1327
|
+
const normalized = normalizeDiagnosticDetail(detail);
|
|
1328
|
+
if (!normalized) {
|
|
1329
|
+
return `${providerLabel} failed.`;
|
|
1120
1330
|
}
|
|
1121
|
-
|
|
1331
|
+
if (startsWithProviderLabel(providerLabel, normalized)) {
|
|
1332
|
+
return `${normalized}.`;
|
|
1333
|
+
}
|
|
1334
|
+
if (readsLikeProviderClause(normalized)) {
|
|
1335
|
+
return `${providerLabel} ${normalized}.`;
|
|
1336
|
+
}
|
|
1337
|
+
return `${providerLabel}: ${normalized}.`;
|
|
1122
1338
|
}
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
|
|
1339
|
+
function formatResearchTerminalDiagnostic(providerLabel, status, detail) {
|
|
1340
|
+
const normalized = detail ? normalizeDiagnosticDetail(detail) : "";
|
|
1341
|
+
if (!normalized) {
|
|
1342
|
+
return status === "cancelled" ? `${providerLabel} research was canceled.` : `${providerLabel} research failed.`;
|
|
1126
1343
|
}
|
|
1127
|
-
|
|
1128
|
-
return
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
throw new Error("returned invalid JSON output");
|
|
1133
|
-
}
|
|
1134
|
-
try {
|
|
1135
|
-
return JSON.parse(match[0]);
|
|
1136
|
-
} catch {
|
|
1137
|
-
throw new Error("returned invalid JSON output");
|
|
1138
|
-
}
|
|
1344
|
+
if (startsWithProviderLabel(providerLabel, normalized)) {
|
|
1345
|
+
return `${normalized}.`;
|
|
1346
|
+
}
|
|
1347
|
+
if (/^research\b/iu.test(normalized)) {
|
|
1348
|
+
return `${providerLabel} ${normalized}.`;
|
|
1139
1349
|
}
|
|
1350
|
+
return status === "cancelled" ? `${providerLabel} research was canceled: ${normalized}.` : `${providerLabel} research failed: ${normalized}.`;
|
|
1140
1351
|
}
|
|
1141
1352
|
|
|
1142
|
-
// src/
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
})
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
return;
|
|
1170
|
-
}
|
|
1171
|
-
settled = true;
|
|
1172
|
-
if (abortTimer) {
|
|
1173
|
-
clearTimeout(abortTimer);
|
|
1174
|
-
}
|
|
1175
|
-
context.signal?.removeEventListener("abort", onAbort);
|
|
1176
|
-
rejectPromise(error);
|
|
1177
|
-
};
|
|
1178
|
-
const resolveOnce = (value) => {
|
|
1179
|
-
if (settled) {
|
|
1180
|
-
return;
|
|
1181
|
-
}
|
|
1182
|
-
settled = true;
|
|
1183
|
-
if (abortTimer) {
|
|
1184
|
-
clearTimeout(abortTimer);
|
|
1353
|
+
// src/execution-policy.ts
|
|
1354
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
1355
|
+
var RequestTimeoutError = class extends Error {
|
|
1356
|
+
name = "RequestTimeoutError";
|
|
1357
|
+
};
|
|
1358
|
+
async function runWithExecutionPolicy(label, operation, settings, context) {
|
|
1359
|
+
const maxAttempts = Math.max(1, settings.retryCount + 1);
|
|
1360
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
1361
|
+
throwIfAborted(context.signal);
|
|
1362
|
+
const {
|
|
1363
|
+
context: attemptContext,
|
|
1364
|
+
abort,
|
|
1365
|
+
cleanup
|
|
1366
|
+
} = createAttemptContext(context);
|
|
1367
|
+
try {
|
|
1368
|
+
const result = operation(attemptContext);
|
|
1369
|
+
const timeoutMessage = settings.requestTimeoutMs === void 0 ? void 0 : `${label} timed out after ${formatDuration(settings.requestTimeoutMs)}.`;
|
|
1370
|
+
return await withAbortAndOptionalTimeout(
|
|
1371
|
+
result,
|
|
1372
|
+
settings.requestTimeoutMs,
|
|
1373
|
+
context.signal,
|
|
1374
|
+
timeoutMessage,
|
|
1375
|
+
timeoutMessage ? () => abort(new RequestTimeoutError(timeoutMessage)) : void 0
|
|
1376
|
+
);
|
|
1377
|
+
} catch (error) {
|
|
1378
|
+
if (!shouldRetryError(error, settings) || attempt >= maxAttempts) {
|
|
1379
|
+
throw error;
|
|
1185
1380
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1381
|
+
const delayMs = Math.min(
|
|
1382
|
+
settings.retryDelayMs * 2 ** (attempt - 1),
|
|
1383
|
+
MAX_RETRY_DELAY_MS
|
|
1384
|
+
);
|
|
1385
|
+
context.onProgress?.(
|
|
1386
|
+
`${label} failed (${formatErrorMessage(error)}). Retrying in ${formatDuration(delayMs)} (attempt ${attempt + 1}/${maxAttempts}).`
|
|
1387
|
+
);
|
|
1388
|
+
await sleep(delayMs, context.signal);
|
|
1389
|
+
} finally {
|
|
1390
|
+
cleanup();
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
throw new Error(`${label} failed.`);
|
|
1394
|
+
}
|
|
1395
|
+
async function executeAsyncResearch({
|
|
1396
|
+
providerLabel,
|
|
1397
|
+
providerId,
|
|
1398
|
+
context,
|
|
1399
|
+
pollIntervalMs = DEFAULT_RESEARCH_POLL_INTERVAL_MS,
|
|
1400
|
+
timeoutMs = DEFAULT_RESEARCH_TIMEOUT_MS,
|
|
1401
|
+
maxConsecutivePollErrors = DEFAULT_RESEARCH_MAX_CONSECUTIVE_POLL_ERRORS,
|
|
1402
|
+
start,
|
|
1403
|
+
poll
|
|
1404
|
+
}) {
|
|
1405
|
+
const timeoutMessage = `${providerLabel} research exceeded ${formatDuration(timeoutMs)}.`;
|
|
1406
|
+
const deadline = createDeadlineSignal(
|
|
1407
|
+
context.signal,
|
|
1408
|
+
timeoutMs,
|
|
1409
|
+
timeoutMessage
|
|
1410
|
+
);
|
|
1411
|
+
const researchContext = {
|
|
1412
|
+
...context,
|
|
1413
|
+
signal: deadline.signal
|
|
1414
|
+
};
|
|
1415
|
+
let lastStatus;
|
|
1416
|
+
let lastProgressStatus;
|
|
1417
|
+
const startedAt = Date.now();
|
|
1418
|
+
try {
|
|
1419
|
+
researchContext.onProgress?.(`Starting research via ${providerLabel}`);
|
|
1420
|
+
const job = await withAbortAndOptionalTimeout(
|
|
1421
|
+
start(researchContext),
|
|
1422
|
+
void 0,
|
|
1423
|
+
researchContext.signal,
|
|
1424
|
+
void 0
|
|
1425
|
+
);
|
|
1426
|
+
const jobId = job.id;
|
|
1427
|
+
if (!jobId) {
|
|
1428
|
+
throw new Error(`${providerLabel} research did not return a job id.`);
|
|
1199
1429
|
}
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
stderr += chunk;
|
|
1207
|
-
});
|
|
1208
|
-
child.on("error", (error) => {
|
|
1209
|
-
rejectOnce(
|
|
1210
|
-
new Error(
|
|
1211
|
-
`${label} failed to start: ${error.message || String(error)}`
|
|
1212
|
-
)
|
|
1430
|
+
researchContext.onProgress?.(`${providerLabel} research started: ${jobId}`);
|
|
1431
|
+
let consecutivePollErrors = 0;
|
|
1432
|
+
while (true) {
|
|
1433
|
+
throwIfAborted(
|
|
1434
|
+
researchContext.signal,
|
|
1435
|
+
`${providerLabel} research aborted.`
|
|
1213
1436
|
);
|
|
1214
|
-
});
|
|
1215
|
-
child.on("close", (code, signal) => {
|
|
1216
|
-
if (context.signal?.aborted) {
|
|
1217
|
-
rejectOnce(new Error(`${label} was aborted.`));
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
if (code !== 0) {
|
|
1221
|
-
const detail = stderr.trim() || `exit code ${code ?? "unknown"}`;
|
|
1222
|
-
rejectOnce(
|
|
1223
|
-
new Error(
|
|
1224
|
-
signal ? `${label} exited via signal ${signal}: ${detail}` : `${label} failed with exit code ${code}: ${detail}`
|
|
1225
|
-
)
|
|
1226
|
-
);
|
|
1227
|
-
return;
|
|
1228
|
-
}
|
|
1229
|
-
const trimmed = stdout.trim();
|
|
1230
|
-
if (!trimmed) {
|
|
1231
|
-
rejectOnce(new Error(`${label} did not write JSON to stdout.`));
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
1437
|
try {
|
|
1235
|
-
|
|
1438
|
+
const result = await withAbortAndOptionalTimeout(
|
|
1439
|
+
poll(jobId, researchContext),
|
|
1440
|
+
void 0,
|
|
1441
|
+
researchContext.signal,
|
|
1442
|
+
void 0
|
|
1443
|
+
);
|
|
1444
|
+
consecutivePollErrors = 0;
|
|
1445
|
+
const progressStatus = result.statusText ?? result.status;
|
|
1446
|
+
if (result.status !== lastStatus || progressStatus !== lastProgressStatus) {
|
|
1447
|
+
researchContext.onProgress?.(
|
|
1448
|
+
`Research via ${providerLabel}: ${progressStatus} (${formatElapsed(Date.now() - startedAt)} elapsed)`
|
|
1449
|
+
);
|
|
1450
|
+
lastStatus = result.status;
|
|
1451
|
+
lastProgressStatus = progressStatus;
|
|
1452
|
+
}
|
|
1453
|
+
if (result.status === "completed") {
|
|
1454
|
+
return result.output ?? {
|
|
1455
|
+
provider: providerId,
|
|
1456
|
+
text: `${providerLabel} research completed without textual output.`
|
|
1457
|
+
};
|
|
1458
|
+
}
|
|
1459
|
+
if (result.status === "failed" || result.status === "cancelled") {
|
|
1460
|
+
throw new Error(
|
|
1461
|
+
formatResearchTerminalDiagnostic(
|
|
1462
|
+
providerLabel,
|
|
1463
|
+
result.status,
|
|
1464
|
+
result.error
|
|
1465
|
+
)
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1236
1468
|
} catch (error) {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1469
|
+
if (isAbortErrorFromSignal(researchContext.signal, error)) {
|
|
1470
|
+
throw error;
|
|
1471
|
+
}
|
|
1472
|
+
if (!isRetryableError(error)) {
|
|
1473
|
+
throw normalizeError(error);
|
|
1474
|
+
}
|
|
1475
|
+
consecutivePollErrors += 1;
|
|
1476
|
+
if (consecutivePollErrors >= maxConsecutivePollErrors) {
|
|
1477
|
+
throw new Error(
|
|
1478
|
+
`${providerLabel} research polling failed too many times in a row: ${formatErrorMessage(error)}`
|
|
1479
|
+
);
|
|
1480
|
+
}
|
|
1481
|
+
researchContext.onProgress?.(
|
|
1482
|
+
`${providerLabel} research poll is still retrying after transient errors (${consecutivePollErrors}/${maxConsecutivePollErrors} consecutive poll failures). Background job id: ${jobId}`
|
|
1241
1483
|
);
|
|
1242
1484
|
}
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1485
|
+
await sleep(pollIntervalMs, researchContext.signal);
|
|
1486
|
+
}
|
|
1487
|
+
} catch (error) {
|
|
1488
|
+
if (isAbortErrorFromSignal(researchContext.signal, error)) {
|
|
1489
|
+
throw new Error(
|
|
1490
|
+
formatProviderDiagnostic(providerLabel, formatErrorMessage(error))
|
|
1491
|
+
);
|
|
1492
|
+
}
|
|
1493
|
+
throw new Error(
|
|
1494
|
+
formatProviderDiagnostic(providerLabel, formatErrorMessage(error))
|
|
1495
|
+
);
|
|
1496
|
+
} finally {
|
|
1497
|
+
deadline.cleanup();
|
|
1254
1498
|
}
|
|
1255
|
-
return argv;
|
|
1256
1499
|
}
|
|
1257
|
-
function
|
|
1258
|
-
if (
|
|
1259
|
-
return
|
|
1500
|
+
function shouldRetryError(error, settings) {
|
|
1501
|
+
if (error instanceof RequestTimeoutError) {
|
|
1502
|
+
return settings.retryOnTimeout === true;
|
|
1260
1503
|
}
|
|
1261
|
-
return
|
|
1504
|
+
return isRetryableError(error);
|
|
1262
1505
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
id: "custom",
|
|
1267
|
-
label: "Custom",
|
|
1268
|
-
docsUrl: "https://github.com/mavam/pi-web-providers#custom-provider",
|
|
1269
|
-
getToolOptionsSchema(_capability) {
|
|
1270
|
-
return void 0;
|
|
1271
|
-
},
|
|
1272
|
-
createTemplate() {
|
|
1273
|
-
return {};
|
|
1274
|
-
},
|
|
1275
|
-
getCapabilityStatus(config, _cwd, capability) {
|
|
1276
|
-
if (capability) {
|
|
1277
|
-
return hasCommandForCapability(config, capability) ? { state: "ready" } : { state: "missing_command" };
|
|
1278
|
-
}
|
|
1279
|
-
return hasAnyCommand(config) ? { state: "ready" } : { state: "missing_command" };
|
|
1280
|
-
},
|
|
1281
|
-
async search(query2, maxResults, config, context, options) {
|
|
1282
|
-
const output = await runCommand({
|
|
1283
|
-
capability: "search",
|
|
1284
|
-
payload: {
|
|
1285
|
-
capability: "search",
|
|
1286
|
-
query: query2,
|
|
1287
|
-
maxResults,
|
|
1288
|
-
...options ? { options } : {}
|
|
1289
|
-
},
|
|
1290
|
-
config,
|
|
1291
|
-
context
|
|
1292
|
-
});
|
|
1293
|
-
return parseSearchResponse(output, customAdapter.id);
|
|
1294
|
-
},
|
|
1295
|
-
async contents(urls, config, context, options) {
|
|
1296
|
-
const output = await runCommand({
|
|
1297
|
-
capability: "contents",
|
|
1298
|
-
payload: {
|
|
1299
|
-
capability: "contents",
|
|
1300
|
-
urls,
|
|
1301
|
-
...options ? { options } : {}
|
|
1302
|
-
},
|
|
1303
|
-
config,
|
|
1304
|
-
context
|
|
1305
|
-
});
|
|
1306
|
-
return parseContentsResponse(output, customAdapter.id);
|
|
1307
|
-
},
|
|
1308
|
-
async answer(query2, config, context, options) {
|
|
1309
|
-
const output = await runCommand({
|
|
1310
|
-
capability: "answer",
|
|
1311
|
-
payload: {
|
|
1312
|
-
capability: "answer",
|
|
1313
|
-
query: query2,
|
|
1314
|
-
...options ? { options } : {}
|
|
1315
|
-
},
|
|
1316
|
-
config,
|
|
1317
|
-
context
|
|
1318
|
-
});
|
|
1319
|
-
return parseToolOutput(output, customAdapter.id);
|
|
1320
|
-
},
|
|
1321
|
-
async research(input, config, context, options) {
|
|
1322
|
-
const output = await runCommand({
|
|
1323
|
-
capability: "research",
|
|
1324
|
-
payload: {
|
|
1325
|
-
capability: "research",
|
|
1326
|
-
input,
|
|
1327
|
-
...options ? { options } : {}
|
|
1328
|
-
},
|
|
1329
|
-
config,
|
|
1330
|
-
context
|
|
1331
|
-
});
|
|
1332
|
-
return parseToolOutput(output, customAdapter.id);
|
|
1506
|
+
function isRetryableError(error) {
|
|
1507
|
+
if (error instanceof RequestTimeoutError) {
|
|
1508
|
+
return false;
|
|
1333
1509
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
payload,
|
|
1338
|
-
config,
|
|
1339
|
-
context
|
|
1340
|
-
}) {
|
|
1341
|
-
const command = getCommandConfig(config, capability);
|
|
1342
|
-
if (!command) {
|
|
1343
|
-
throw new Error(`has no command configured for ${capability}`);
|
|
1510
|
+
const message = formatErrorMessage(error).toLowerCase();
|
|
1511
|
+
if (!message || message === "operation aborted.") {
|
|
1512
|
+
return false;
|
|
1344
1513
|
}
|
|
1345
|
-
return
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
...payload,
|
|
1349
|
-
cwd: context.cwd
|
|
1350
|
-
},
|
|
1351
|
-
context,
|
|
1352
|
-
label: `Custom ${capability}`
|
|
1353
|
-
});
|
|
1354
|
-
}
|
|
1355
|
-
function getCommandConfig(config, capability) {
|
|
1356
|
-
return config?.options?.[capability];
|
|
1357
|
-
}
|
|
1358
|
-
function hasCommandForCapability(config, capability) {
|
|
1359
|
-
return normalizeConfiguredArgv(getCommandConfig(config, capability)).length > 0;
|
|
1514
|
+
return /429|500|502|503|504|deadline exceeded|econnreset|ecanceled|ehostunreach|eai_again|enotfound|etimedout|fetch failed|gateway timeout|internal error|network|overloaded|rate limit|resource exhausted|socket hang up|temporarily unavailable|timeout|unavailable/.test(
|
|
1515
|
+
message
|
|
1516
|
+
);
|
|
1360
1517
|
}
|
|
1361
|
-
function
|
|
1362
|
-
|
|
1518
|
+
function formatErrorMessage(error) {
|
|
1519
|
+
if (error instanceof Error) {
|
|
1520
|
+
return error.message;
|
|
1521
|
+
}
|
|
1522
|
+
if (typeof error === "object" && error !== null && "message" in error && typeof error.message === "string") {
|
|
1523
|
+
return error.message;
|
|
1524
|
+
}
|
|
1525
|
+
return String(error);
|
|
1363
1526
|
}
|
|
1364
|
-
function
|
|
1365
|
-
|
|
1527
|
+
function formatElapsed(ms) {
|
|
1528
|
+
const totalSeconds = Math.max(0, Math.floor(ms / 1e3));
|
|
1529
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
1530
|
+
const seconds = totalSeconds % 60;
|
|
1531
|
+
if (minutes > 0) {
|
|
1532
|
+
return `${minutes}m ${seconds}s`;
|
|
1533
|
+
}
|
|
1534
|
+
return `${totalSeconds}s`;
|
|
1366
1535
|
}
|
|
1367
|
-
function
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
throw new Error("search output must include a 'results' array");
|
|
1536
|
+
function formatDuration(ms) {
|
|
1537
|
+
if (ms >= 6e4) {
|
|
1538
|
+
return formatElapsed(ms);
|
|
1371
1539
|
}
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
)
|
|
1377
|
-
};
|
|
1540
|
+
if (ms >= 1e3) {
|
|
1541
|
+
return `${Math.floor(ms / 1e3)}s`;
|
|
1542
|
+
}
|
|
1543
|
+
return `${ms}ms`;
|
|
1378
1544
|
}
|
|
1379
|
-
function
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1545
|
+
async function sleep(ms, signal) {
|
|
1546
|
+
throwIfAborted(signal);
|
|
1547
|
+
await new Promise((resolve2, reject) => {
|
|
1548
|
+
const timer = setTimeout(() => {
|
|
1549
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1550
|
+
resolve2();
|
|
1551
|
+
}, ms);
|
|
1552
|
+
const onAbort = () => {
|
|
1553
|
+
clearTimeout(timer);
|
|
1554
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1555
|
+
reject(getAbortError(signal));
|
|
1556
|
+
};
|
|
1557
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1558
|
+
});
|
|
1392
1559
|
}
|
|
1393
|
-
function
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
"contents output must be a JSON object"
|
|
1397
|
-
);
|
|
1398
|
-
if (!Array.isArray(response.answers)) {
|
|
1399
|
-
throw new Error("contents output must include an 'answers' array");
|
|
1560
|
+
function throwIfAborted(signal, message = "Operation aborted.") {
|
|
1561
|
+
if (signal?.aborted) {
|
|
1562
|
+
throw getAbortError(signal, message);
|
|
1400
1563
|
}
|
|
1401
|
-
return {
|
|
1402
|
-
provider: providerId,
|
|
1403
|
-
answers: response.answers.map(
|
|
1404
|
-
(entry, index) => parseContentsAnswer(entry, index)
|
|
1405
|
-
)
|
|
1406
|
-
};
|
|
1407
1564
|
}
|
|
1408
|
-
function
|
|
1409
|
-
const
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
);
|
|
1413
|
-
const url = readRequiredString(value.url, `answers[${index}].url`);
|
|
1414
|
-
const content = readOptionalString(
|
|
1415
|
-
value.content,
|
|
1416
|
-
`answers[${index}].content`
|
|
1417
|
-
);
|
|
1418
|
-
const summary = value.summary;
|
|
1419
|
-
const metadata = readRequiredJsonObject(
|
|
1420
|
-
value.metadata,
|
|
1421
|
-
`answers[${index}].metadata`
|
|
1422
|
-
);
|
|
1423
|
-
const error = readOptionalString(value.error, `answers[${index}].error`);
|
|
1424
|
-
if (content === void 0 && error === void 0) {
|
|
1425
|
-
throw new Error(
|
|
1426
|
-
`contents answer at index ${index} must include 'content' or 'error'`
|
|
1427
|
-
);
|
|
1565
|
+
function createAttemptContext(context) {
|
|
1566
|
+
const controller = new AbortController();
|
|
1567
|
+
if (context.signal?.aborted) {
|
|
1568
|
+
controller.abort(getAbortError(context.signal));
|
|
1428
1569
|
}
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
...content !== void 0 ? { content } : {},
|
|
1432
|
-
...summary !== void 0 ? { summary } : {},
|
|
1433
|
-
...metadata !== void 0 ? { metadata } : {},
|
|
1434
|
-
...error !== void 0 ? { error } : {}
|
|
1570
|
+
const onAbort = () => {
|
|
1571
|
+
controller.abort(getAbortError(context.signal));
|
|
1435
1572
|
};
|
|
1436
|
-
}
|
|
1437
|
-
function parseToolOutput(value, providerId) {
|
|
1438
|
-
const output = requireObject(value, "output must be a JSON object");
|
|
1439
|
-
const metadata = readLenientJsonObject(output.metadata);
|
|
1573
|
+
context.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1440
1574
|
return {
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1575
|
+
context: {
|
|
1576
|
+
...context,
|
|
1577
|
+
signal: controller.signal
|
|
1578
|
+
},
|
|
1579
|
+
abort: (reason) => controller.abort(reason),
|
|
1580
|
+
cleanup: () => context.signal?.removeEventListener("abort", onAbort)
|
|
1445
1581
|
};
|
|
1446
1582
|
}
|
|
1447
|
-
function
|
|
1448
|
-
if (
|
|
1449
|
-
return
|
|
1583
|
+
async function withAbortAndOptionalTimeout(promise, timeoutMs, signal, message, onTimeout) {
|
|
1584
|
+
if (timeoutMs === void 0 && !signal) {
|
|
1585
|
+
return await promise;
|
|
1450
1586
|
}
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1587
|
+
throwIfAborted(signal);
|
|
1588
|
+
return await new Promise((resolve2, reject) => {
|
|
1589
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
1590
|
+
onTimeout?.();
|
|
1591
|
+
cleanup();
|
|
1592
|
+
reject(
|
|
1593
|
+
new RequestTimeoutError(
|
|
1594
|
+
message ?? `Operation timed out after ${formatDuration(timeoutMs)}.`
|
|
1595
|
+
)
|
|
1596
|
+
);
|
|
1597
|
+
}, timeoutMs);
|
|
1598
|
+
const onAbort = () => {
|
|
1599
|
+
cleanup();
|
|
1600
|
+
reject(getAbortError(signal));
|
|
1601
|
+
};
|
|
1602
|
+
const cleanup = () => {
|
|
1603
|
+
if (timer) {
|
|
1604
|
+
clearTimeout(timer);
|
|
1605
|
+
}
|
|
1606
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1607
|
+
};
|
|
1608
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1609
|
+
promise.then(
|
|
1610
|
+
(value) => {
|
|
1611
|
+
cleanup();
|
|
1612
|
+
resolve2(value);
|
|
1613
|
+
},
|
|
1614
|
+
(error) => {
|
|
1615
|
+
cleanup();
|
|
1616
|
+
reject(error);
|
|
1617
|
+
}
|
|
1618
|
+
);
|
|
1619
|
+
});
|
|
1455
1620
|
}
|
|
1456
|
-
function
|
|
1457
|
-
|
|
1458
|
-
|
|
1621
|
+
function getAbortError(signal, message = "Operation aborted.") {
|
|
1622
|
+
const reason = signal?.reason;
|
|
1623
|
+
if (reason instanceof Error) {
|
|
1624
|
+
return reason;
|
|
1459
1625
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
function readOptionalString(value, field) {
|
|
1463
|
-
if (value === void 0) {
|
|
1464
|
-
return void 0;
|
|
1626
|
+
if (typeof reason === "string" && reason.length > 0) {
|
|
1627
|
+
return new Error(reason);
|
|
1465
1628
|
}
|
|
1466
|
-
return
|
|
1629
|
+
return new Error(message);
|
|
1467
1630
|
}
|
|
1468
|
-
function
|
|
1469
|
-
return
|
|
1631
|
+
function isAbortErrorFromSignal(signal, error) {
|
|
1632
|
+
return signal?.aborted === true && signal.reason === error;
|
|
1470
1633
|
}
|
|
1471
|
-
function
|
|
1472
|
-
|
|
1473
|
-
|
|
1634
|
+
function createDeadlineSignal(signal, timeoutMs, timeoutMessage) {
|
|
1635
|
+
const controller = new AbortController();
|
|
1636
|
+
if (signal?.aborted) {
|
|
1637
|
+
controller.abort(getAbortError(signal));
|
|
1474
1638
|
}
|
|
1475
|
-
|
|
1639
|
+
const onAbort = () => {
|
|
1640
|
+
controller.abort(getAbortError(signal));
|
|
1641
|
+
};
|
|
1642
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1643
|
+
const timer = setTimeout(() => {
|
|
1644
|
+
controller.abort(new RequestTimeoutError(timeoutMessage));
|
|
1645
|
+
}, timeoutMs);
|
|
1646
|
+
return {
|
|
1647
|
+
signal: controller.signal,
|
|
1648
|
+
cleanup: () => {
|
|
1649
|
+
clearTimeout(timer);
|
|
1650
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1651
|
+
}
|
|
1652
|
+
};
|
|
1476
1653
|
}
|
|
1477
|
-
function
|
|
1478
|
-
return
|
|
1654
|
+
function normalizeError(error) {
|
|
1655
|
+
return error instanceof Error ? error : new Error(formatErrorMessage(error));
|
|
1479
1656
|
}
|
|
1480
1657
|
|
|
1481
1658
|
// src/providers/exa.ts
|
|
1482
|
-
import { Type as Type5 } from "typebox";
|
|
1483
|
-
import { Exa as ExaClient } from "exa-js";
|
|
1484
1659
|
var exaSearchOptionsSchema = Type5.Object(
|
|
1485
1660
|
{
|
|
1486
1661
|
type: Type5.Optional(
|
|
@@ -1562,7 +1737,7 @@ var exaSearchOptionsSchema = Type5.Object(
|
|
|
1562
1737
|
},
|
|
1563
1738
|
{ description: "Exa search options." }
|
|
1564
1739
|
);
|
|
1565
|
-
var
|
|
1740
|
+
var exaImplementation = {
|
|
1566
1741
|
id: "exa",
|
|
1567
1742
|
label: "Exa",
|
|
1568
1743
|
docsUrl: "https://exa.ai/docs/sdks/typescript-sdk-specification",
|
|
@@ -1593,13 +1768,13 @@ var exaAdapter = {
|
|
|
1593
1768
|
async search(query2, maxResults, config, _context, searchOptions) {
|
|
1594
1769
|
const client = createClient2(config);
|
|
1595
1770
|
const options = {
|
|
1596
|
-
...
|
|
1771
|
+
...asJsonObject(config.options?.search) ?? {},
|
|
1597
1772
|
...searchOptions ?? {},
|
|
1598
1773
|
numResults: maxResults
|
|
1599
1774
|
};
|
|
1600
1775
|
const response = await client.search(query2, options);
|
|
1601
1776
|
return {
|
|
1602
|
-
provider:
|
|
1777
|
+
provider: exaImplementation.id,
|
|
1603
1778
|
results: (response.results ?? []).slice(0, maxResults).map((result) => ({
|
|
1604
1779
|
title: String(result.title ?? result.url ?? "Untitled"),
|
|
1605
1780
|
url: String(result.url ?? ""),
|
|
@@ -1615,7 +1790,7 @@ var exaAdapter = {
|
|
|
1615
1790
|
const response = await client.getContents(urls, options);
|
|
1616
1791
|
const results = response.results ?? [];
|
|
1617
1792
|
return {
|
|
1618
|
-
provider:
|
|
1793
|
+
provider: exaImplementation.id,
|
|
1619
1794
|
answers: urls.map((url, index) => {
|
|
1620
1795
|
const result = results[index];
|
|
1621
1796
|
if (!result) {
|
|
@@ -1652,18 +1827,23 @@ var exaAdapter = {
|
|
|
1652
1827
|
}
|
|
1653
1828
|
}
|
|
1654
1829
|
return {
|
|
1655
|
-
provider:
|
|
1830
|
+
provider: exaImplementation.id,
|
|
1656
1831
|
text: lines.join("\n").trimEnd(),
|
|
1657
1832
|
itemCount: citations.length
|
|
1658
1833
|
};
|
|
1659
1834
|
},
|
|
1660
1835
|
async research(input, config, context, options) {
|
|
1661
1836
|
return await executeAsyncResearch({
|
|
1662
|
-
providerLabel:
|
|
1663
|
-
providerId:
|
|
1837
|
+
providerLabel: exaImplementation.label,
|
|
1838
|
+
providerId: exaImplementation.id,
|
|
1664
1839
|
context,
|
|
1665
|
-
start: (researchContext) =>
|
|
1666
|
-
|
|
1840
|
+
start: (researchContext) => exaImplementation.startResearch(
|
|
1841
|
+
input,
|
|
1842
|
+
config,
|
|
1843
|
+
researchContext,
|
|
1844
|
+
options
|
|
1845
|
+
),
|
|
1846
|
+
poll: (id, researchContext) => exaImplementation.pollResearch(id, config, researchContext, options)
|
|
1667
1847
|
});
|
|
1668
1848
|
},
|
|
1669
1849
|
async startResearch(input, config, _context, options) {
|
|
@@ -1682,7 +1862,7 @@ var exaAdapter = {
|
|
|
1682
1862
|
return {
|
|
1683
1863
|
status: "completed",
|
|
1684
1864
|
output: {
|
|
1685
|
-
provider:
|
|
1865
|
+
provider: exaImplementation.id,
|
|
1686
1866
|
text: typeof content === "string" ? content : content !== void 0 ? formatJson(content) : "Exa research completed without textual output."
|
|
1687
1867
|
}
|
|
1688
1868
|
};
|
|
@@ -1709,6 +1889,69 @@ function createClient2(config) {
|
|
|
1709
1889
|
}
|
|
1710
1890
|
return new ExaClient(apiKey, resolveConfigValue(config.baseUrl));
|
|
1711
1891
|
}
|
|
1892
|
+
var exaProvider = defineProvider({
|
|
1893
|
+
id: "exa",
|
|
1894
|
+
label: exaImplementation.label,
|
|
1895
|
+
docsUrl: exaImplementation.docsUrl,
|
|
1896
|
+
config: {
|
|
1897
|
+
createTemplate: () => exaImplementation.createTemplate(),
|
|
1898
|
+
fields: ["apiKey", "baseUrl", "options", "settings"],
|
|
1899
|
+
optionCapabilities: ["search"]
|
|
1900
|
+
},
|
|
1901
|
+
getCapabilityStatus: (config, cwd, tool) => exaImplementation.getCapabilityStatus(
|
|
1902
|
+
config,
|
|
1903
|
+
cwd,
|
|
1904
|
+
tool
|
|
1905
|
+
),
|
|
1906
|
+
capabilities: {
|
|
1907
|
+
search: defineCapability({
|
|
1908
|
+
options: exaImplementation.getToolOptionsSchema?.("search"),
|
|
1909
|
+
async execute(input, ctx) {
|
|
1910
|
+
const { query: query2, maxResults, options } = input;
|
|
1911
|
+
return await exaImplementation.search(
|
|
1912
|
+
query2,
|
|
1913
|
+
maxResults,
|
|
1914
|
+
ctx.config,
|
|
1915
|
+
ctx,
|
|
1916
|
+
options
|
|
1917
|
+
);
|
|
1918
|
+
}
|
|
1919
|
+
}),
|
|
1920
|
+
contents: defineCapability({
|
|
1921
|
+
options: exaImplementation.getToolOptionsSchema?.("contents"),
|
|
1922
|
+
async execute(input, ctx) {
|
|
1923
|
+
return await exaImplementation.contents(
|
|
1924
|
+
input.urls,
|
|
1925
|
+
ctx.config,
|
|
1926
|
+
ctx,
|
|
1927
|
+
input.options
|
|
1928
|
+
);
|
|
1929
|
+
}
|
|
1930
|
+
}),
|
|
1931
|
+
answer: defineCapability({
|
|
1932
|
+
options: exaImplementation.getToolOptionsSchema?.("answer"),
|
|
1933
|
+
async execute(input, ctx) {
|
|
1934
|
+
return await exaImplementation.answer(
|
|
1935
|
+
input.query,
|
|
1936
|
+
ctx.config,
|
|
1937
|
+
ctx,
|
|
1938
|
+
input.options
|
|
1939
|
+
);
|
|
1940
|
+
}
|
|
1941
|
+
}),
|
|
1942
|
+
research: defineCapability({
|
|
1943
|
+
options: exaImplementation.getToolOptionsSchema?.("research"),
|
|
1944
|
+
async execute(input, ctx) {
|
|
1945
|
+
return await exaImplementation.research(
|
|
1946
|
+
input.input,
|
|
1947
|
+
ctx.config,
|
|
1948
|
+
ctx,
|
|
1949
|
+
input.options
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
})
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1712
1955
|
|
|
1713
1956
|
// src/providers/firecrawl.ts
|
|
1714
1957
|
import FirecrawlClient from "@mendable/firecrawl-js";
|
|
@@ -1819,7 +2062,7 @@ var firecrawlScrapeOptionsSchema = Type6.Object(
|
|
|
1819
2062
|
},
|
|
1820
2063
|
{ description: "Firecrawl scrape options." }
|
|
1821
2064
|
);
|
|
1822
|
-
var
|
|
2065
|
+
var firecrawlImplementation = {
|
|
1823
2066
|
id: "firecrawl",
|
|
1824
2067
|
label: "Firecrawl",
|
|
1825
2068
|
docsUrl: "https://docs.firecrawl.dev/sdks/node",
|
|
@@ -1849,20 +2092,20 @@ var firecrawlAdapter = {
|
|
|
1849
2092
|
},
|
|
1850
2093
|
async search(query2, maxResults, config, _context, options) {
|
|
1851
2094
|
const client = createClient3(config);
|
|
1852
|
-
const defaults =
|
|
2095
|
+
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
1853
2096
|
const response = await client.search(query2, {
|
|
1854
2097
|
...defaults,
|
|
1855
2098
|
...options ?? {},
|
|
1856
2099
|
limit: maxResults
|
|
1857
2100
|
});
|
|
1858
2101
|
return {
|
|
1859
|
-
provider:
|
|
2102
|
+
provider: firecrawlImplementation.id,
|
|
1860
2103
|
results: flattenSearchResults(response).slice(0, maxResults)
|
|
1861
2104
|
};
|
|
1862
2105
|
},
|
|
1863
2106
|
async contents(urls, config, _context, options) {
|
|
1864
2107
|
const client = createClient3(config);
|
|
1865
|
-
const defaults =
|
|
2108
|
+
const defaults = asJsonObject(config.options?.scrape) ?? {};
|
|
1866
2109
|
const scrapeOptions = {
|
|
1867
2110
|
formats: ["markdown"],
|
|
1868
2111
|
onlyMainContent: true,
|
|
@@ -1870,7 +2113,7 @@ var firecrawlAdapter = {
|
|
|
1870
2113
|
...options ?? {}
|
|
1871
2114
|
};
|
|
1872
2115
|
return {
|
|
1873
|
-
provider:
|
|
2116
|
+
provider: firecrawlImplementation.id,
|
|
1874
2117
|
answers: await Promise.all(
|
|
1875
2118
|
urls.map(async (url) => {
|
|
1876
2119
|
try {
|
|
@@ -1956,6 +2199,46 @@ function asRecord(value) {
|
|
|
1956
2199
|
function readString2(value) {
|
|
1957
2200
|
return typeof value === "string" ? value : void 0;
|
|
1958
2201
|
}
|
|
2202
|
+
var firecrawlProvider = defineProvider({
|
|
2203
|
+
id: "firecrawl",
|
|
2204
|
+
label: firecrawlImplementation.label,
|
|
2205
|
+
docsUrl: firecrawlImplementation.docsUrl,
|
|
2206
|
+
config: {
|
|
2207
|
+
createTemplate: () => firecrawlImplementation.createTemplate(),
|
|
2208
|
+
fields: ["apiKey", "baseUrl", "options", "settings"]
|
|
2209
|
+
},
|
|
2210
|
+
getCapabilityStatus: (config, cwd, tool) => firecrawlImplementation.getCapabilityStatus(
|
|
2211
|
+
config,
|
|
2212
|
+
cwd,
|
|
2213
|
+
tool
|
|
2214
|
+
),
|
|
2215
|
+
capabilities: {
|
|
2216
|
+
search: defineCapability({
|
|
2217
|
+
options: firecrawlImplementation.getToolOptionsSchema?.("search"),
|
|
2218
|
+
async execute(input, ctx) {
|
|
2219
|
+
const { query: query2, maxResults, options } = input;
|
|
2220
|
+
return await firecrawlImplementation.search(
|
|
2221
|
+
query2,
|
|
2222
|
+
maxResults,
|
|
2223
|
+
ctx.config,
|
|
2224
|
+
ctx,
|
|
2225
|
+
options
|
|
2226
|
+
);
|
|
2227
|
+
}
|
|
2228
|
+
}),
|
|
2229
|
+
contents: defineCapability({
|
|
2230
|
+
options: firecrawlImplementation.getToolOptionsSchema?.("contents"),
|
|
2231
|
+
async execute(input, ctx) {
|
|
2232
|
+
return await firecrawlImplementation.contents(
|
|
2233
|
+
input.urls,
|
|
2234
|
+
ctx.config,
|
|
2235
|
+
ctx,
|
|
2236
|
+
input.options
|
|
2237
|
+
);
|
|
2238
|
+
}
|
|
2239
|
+
})
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
1959
2242
|
|
|
1960
2243
|
// src/providers/gemini.ts
|
|
1961
2244
|
import { GoogleGenAI } from "@google/genai";
|
|
@@ -2025,7 +2308,7 @@ var geminiAgentConfigSchema = Type7.Object(
|
|
|
2025
2308
|
},
|
|
2026
2309
|
{
|
|
2027
2310
|
additionalProperties: false,
|
|
2028
|
-
description: "Safe Gemini deep-research agent configuration. The
|
|
2311
|
+
description: "Safe Gemini deep-research agent configuration. The provider adds the required type field."
|
|
2029
2312
|
}
|
|
2030
2313
|
);
|
|
2031
2314
|
var geminiSearchOptionsSchema = Type7.Object(
|
|
@@ -2056,7 +2339,7 @@ var geminiResearchOptionsSchema = Type7.Object(
|
|
|
2056
2339
|
},
|
|
2057
2340
|
{ additionalProperties: false, description: "Gemini research options." }
|
|
2058
2341
|
);
|
|
2059
|
-
var
|
|
2342
|
+
var geminiImplementation = {
|
|
2060
2343
|
id: "gemini",
|
|
2061
2344
|
label: "Gemini",
|
|
2062
2345
|
docsUrl: "https://github.com/googleapis/js-genai",
|
|
@@ -2704,12 +2987,63 @@ function getGeminiOptions(config) {
|
|
|
2704
2987
|
function readNonEmptyString3(value) {
|
|
2705
2988
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
2706
2989
|
}
|
|
2990
|
+
var geminiProvider = defineProvider({
|
|
2991
|
+
id: "gemini",
|
|
2992
|
+
label: geminiImplementation.label,
|
|
2993
|
+
docsUrl: geminiImplementation.docsUrl,
|
|
2994
|
+
config: {
|
|
2995
|
+
createTemplate: () => geminiImplementation.createTemplate(),
|
|
2996
|
+
fields: ["apiKey", "options", "settings"]
|
|
2997
|
+
},
|
|
2998
|
+
getCapabilityStatus: (config, cwd, tool) => geminiImplementation.getCapabilityStatus(
|
|
2999
|
+
config,
|
|
3000
|
+
cwd,
|
|
3001
|
+
tool
|
|
3002
|
+
),
|
|
3003
|
+
capabilities: {
|
|
3004
|
+
search: defineCapability({
|
|
3005
|
+
options: geminiImplementation.getToolOptionsSchema?.("search"),
|
|
3006
|
+
async execute(input, ctx) {
|
|
3007
|
+
const { query: query2, maxResults, options } = input;
|
|
3008
|
+
return await geminiImplementation.search(
|
|
3009
|
+
query2,
|
|
3010
|
+
maxResults,
|
|
3011
|
+
ctx.config,
|
|
3012
|
+
ctx,
|
|
3013
|
+
options
|
|
3014
|
+
);
|
|
3015
|
+
}
|
|
3016
|
+
}),
|
|
3017
|
+
answer: defineCapability({
|
|
3018
|
+
options: geminiImplementation.getToolOptionsSchema?.("answer"),
|
|
3019
|
+
async execute(input, ctx) {
|
|
3020
|
+
return await geminiImplementation.answer(
|
|
3021
|
+
input.query,
|
|
3022
|
+
ctx.config,
|
|
3023
|
+
ctx,
|
|
3024
|
+
input.options
|
|
3025
|
+
);
|
|
3026
|
+
}
|
|
3027
|
+
}),
|
|
3028
|
+
research: defineCapability({
|
|
3029
|
+
options: geminiImplementation.getToolOptionsSchema?.("research"),
|
|
3030
|
+
async execute(input, ctx) {
|
|
3031
|
+
return await geminiImplementation.research(
|
|
3032
|
+
input.input,
|
|
3033
|
+
ctx.config,
|
|
3034
|
+
ctx,
|
|
3035
|
+
input.options
|
|
3036
|
+
);
|
|
3037
|
+
}
|
|
3038
|
+
})
|
|
3039
|
+
}
|
|
3040
|
+
});
|
|
2707
3041
|
|
|
2708
3042
|
// src/providers/linkup.ts
|
|
2709
|
-
import { Type as Type8 } from "typebox";
|
|
2710
3043
|
import {
|
|
2711
3044
|
LinkupClient
|
|
2712
3045
|
} from "linkup-sdk";
|
|
3046
|
+
import { Type as Type8 } from "typebox";
|
|
2713
3047
|
var linkupSearchOptionsSchema = Type8.Object(
|
|
2714
3048
|
{
|
|
2715
3049
|
depth: Type8.Optional(
|
|
@@ -2753,7 +3087,7 @@ var linkupContentsOptionsSchema = Type8.Object(
|
|
|
2753
3087
|
},
|
|
2754
3088
|
{ description: "Linkup fetch options." }
|
|
2755
3089
|
);
|
|
2756
|
-
var
|
|
3090
|
+
var linkupImplementation = {
|
|
2757
3091
|
id: "linkup",
|
|
2758
3092
|
label: "Linkup",
|
|
2759
3093
|
docsUrl: "https://docs.linkup.so/pages/sdk/js/js",
|
|
@@ -2777,30 +3111,30 @@ var linkupAdapter = {
|
|
|
2777
3111
|
},
|
|
2778
3112
|
async search(query2, maxResults, config, _context, options) {
|
|
2779
3113
|
const client = createClient4(config);
|
|
2780
|
-
const defaults =
|
|
3114
|
+
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
2781
3115
|
const response = await client.search(
|
|
2782
3116
|
buildSearchParams(query2, maxResults, {
|
|
2783
3117
|
...defaults,
|
|
2784
|
-
...
|
|
3118
|
+
...options ?? {}
|
|
2785
3119
|
})
|
|
2786
3120
|
);
|
|
2787
3121
|
return {
|
|
2788
|
-
provider:
|
|
3122
|
+
provider: linkupImplementation.id,
|
|
2789
3123
|
results: (response.results ?? []).map(toSearchResult2).filter((result) => result !== null).slice(0, maxResults)
|
|
2790
3124
|
};
|
|
2791
3125
|
},
|
|
2792
3126
|
async contents(urls, config, _context, options) {
|
|
2793
3127
|
const client = createClient4(config);
|
|
2794
|
-
const defaults =
|
|
3128
|
+
const defaults = asJsonObject(config.options?.fetch) ?? {};
|
|
2795
3129
|
return {
|
|
2796
|
-
provider:
|
|
3130
|
+
provider: linkupImplementation.id,
|
|
2797
3131
|
answers: await Promise.all(
|
|
2798
3132
|
urls.map(async (url) => {
|
|
2799
3133
|
try {
|
|
2800
3134
|
const response = await client.fetch(
|
|
2801
3135
|
buildFetchParams(url, {
|
|
2802
3136
|
...defaults,
|
|
2803
|
-
...
|
|
3137
|
+
...options ?? {}
|
|
2804
3138
|
})
|
|
2805
3139
|
);
|
|
2806
3140
|
return response.markdown ? {
|
|
@@ -2909,6 +3243,218 @@ function toDate(value, name) {
|
|
|
2909
3243
|
}
|
|
2910
3244
|
return date;
|
|
2911
3245
|
}
|
|
3246
|
+
var linkupProvider = defineProvider({
|
|
3247
|
+
id: "linkup",
|
|
3248
|
+
label: linkupImplementation.label,
|
|
3249
|
+
docsUrl: linkupImplementation.docsUrl,
|
|
3250
|
+
config: {
|
|
3251
|
+
createTemplate: () => linkupImplementation.createTemplate(),
|
|
3252
|
+
fields: ["apiKey", "baseUrl", "options", "settings"]
|
|
3253
|
+
},
|
|
3254
|
+
getCapabilityStatus: (config, cwd, tool) => linkupImplementation.getCapabilityStatus(
|
|
3255
|
+
config,
|
|
3256
|
+
cwd,
|
|
3257
|
+
tool
|
|
3258
|
+
),
|
|
3259
|
+
capabilities: {
|
|
3260
|
+
search: defineCapability({
|
|
3261
|
+
options: linkupImplementation.getToolOptionsSchema?.("search"),
|
|
3262
|
+
async execute(input, ctx) {
|
|
3263
|
+
const { query: query2, maxResults, options } = input;
|
|
3264
|
+
return await linkupImplementation.search(
|
|
3265
|
+
query2,
|
|
3266
|
+
maxResults,
|
|
3267
|
+
ctx.config,
|
|
3268
|
+
ctx,
|
|
3269
|
+
options
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
}),
|
|
3273
|
+
contents: defineCapability({
|
|
3274
|
+
options: linkupImplementation.getToolOptionsSchema?.("contents"),
|
|
3275
|
+
async execute(input, ctx) {
|
|
3276
|
+
return await linkupImplementation.contents(
|
|
3277
|
+
input.urls,
|
|
3278
|
+
ctx.config,
|
|
3279
|
+
ctx,
|
|
3280
|
+
input.options
|
|
3281
|
+
);
|
|
3282
|
+
}
|
|
3283
|
+
})
|
|
3284
|
+
}
|
|
3285
|
+
});
|
|
3286
|
+
|
|
3287
|
+
// src/providers/ollama.ts
|
|
3288
|
+
var DEFAULT_BASE_URL = "https://ollama.com";
|
|
3289
|
+
var WEB_SEARCH_PATH = "/api/web_search";
|
|
3290
|
+
var WEB_FETCH_PATH = "/api/web_fetch";
|
|
3291
|
+
var ollamaProvider = defineProvider({
|
|
3292
|
+
id: "ollama",
|
|
3293
|
+
label: "Ollama",
|
|
3294
|
+
docsUrl: "https://docs.ollama.com/capabilities/web-search",
|
|
3295
|
+
config: {
|
|
3296
|
+
createTemplate() {
|
|
3297
|
+
return {
|
|
3298
|
+
apiKey: "OLLAMA_API_KEY"
|
|
3299
|
+
};
|
|
3300
|
+
},
|
|
3301
|
+
fields: ["apiKey", "baseUrl", "settings"]
|
|
3302
|
+
},
|
|
3303
|
+
getCapabilityStatus(config) {
|
|
3304
|
+
return getApiKeyStatus(config?.apiKey);
|
|
3305
|
+
},
|
|
3306
|
+
capabilities: {
|
|
3307
|
+
search: defineCapability({
|
|
3308
|
+
limits: {
|
|
3309
|
+
maxResults: 10
|
|
3310
|
+
},
|
|
3311
|
+
async execute({ query: query2, maxResults }, { config, signal }) {
|
|
3312
|
+
return await searchOllama(query2, maxResults, config, {
|
|
3313
|
+
signal
|
|
3314
|
+
});
|
|
3315
|
+
}
|
|
3316
|
+
}),
|
|
3317
|
+
contents: defineCapability({
|
|
3318
|
+
async execute({ urls }, { config, signal }) {
|
|
3319
|
+
return await fetchOllamaContents(urls, config, { signal });
|
|
3320
|
+
}
|
|
3321
|
+
})
|
|
3322
|
+
}
|
|
3323
|
+
});
|
|
3324
|
+
async function searchOllama(query2, maxResults, config, context) {
|
|
3325
|
+
const apiKey = resolveApiKey(config);
|
|
3326
|
+
const response = await fetch(
|
|
3327
|
+
resolveEndpoint(config.baseUrl, WEB_SEARCH_PATH),
|
|
3328
|
+
{
|
|
3329
|
+
method: "POST",
|
|
3330
|
+
headers: buildHeaders(apiKey),
|
|
3331
|
+
body: JSON.stringify({
|
|
3332
|
+
query: query2,
|
|
3333
|
+
max_results: clampMaxResults(maxResults)
|
|
3334
|
+
}),
|
|
3335
|
+
signal: context.signal
|
|
3336
|
+
}
|
|
3337
|
+
);
|
|
3338
|
+
if (!response.ok) {
|
|
3339
|
+
throw new Error(await buildHttpError(response));
|
|
3340
|
+
}
|
|
3341
|
+
const data = await response.json();
|
|
3342
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
3343
|
+
return {
|
|
3344
|
+
provider: ollamaProvider.id,
|
|
3345
|
+
results: results.slice(0, clampMaxResults(maxResults)).map((result) => ({
|
|
3346
|
+
title: result.title || result.url || "Untitled",
|
|
3347
|
+
url: result.url ?? "",
|
|
3348
|
+
snippet: trimSnippet(result.content)
|
|
3349
|
+
}))
|
|
3350
|
+
};
|
|
3351
|
+
}
|
|
3352
|
+
async function fetchOllamaContents(urls, config, context) {
|
|
3353
|
+
const apiKey = resolveApiKey(config);
|
|
3354
|
+
const endpoint = resolveEndpoint(config.baseUrl, WEB_FETCH_PATH);
|
|
3355
|
+
return {
|
|
3356
|
+
provider: ollamaProvider.id,
|
|
3357
|
+
answers: await Promise.all(
|
|
3358
|
+
urls.map(async (url) => {
|
|
3359
|
+
try {
|
|
3360
|
+
const response = await fetch(endpoint, {
|
|
3361
|
+
method: "POST",
|
|
3362
|
+
headers: buildHeaders(apiKey),
|
|
3363
|
+
body: JSON.stringify({
|
|
3364
|
+
url
|
|
3365
|
+
}),
|
|
3366
|
+
signal: context.signal
|
|
3367
|
+
});
|
|
3368
|
+
if (!response.ok) {
|
|
3369
|
+
return {
|
|
3370
|
+
url,
|
|
3371
|
+
error: await buildHttpError(response)
|
|
3372
|
+
};
|
|
3373
|
+
}
|
|
3374
|
+
const data = await response.json();
|
|
3375
|
+
const content = normalizeContentText(data.content);
|
|
3376
|
+
if (!content) {
|
|
3377
|
+
return {
|
|
3378
|
+
url,
|
|
3379
|
+
error: "No content returned for this URL."
|
|
3380
|
+
};
|
|
3381
|
+
}
|
|
3382
|
+
const metadata = buildFetchMetadata(data);
|
|
3383
|
+
return {
|
|
3384
|
+
url,
|
|
3385
|
+
content,
|
|
3386
|
+
...metadata ? { metadata } : {}
|
|
3387
|
+
};
|
|
3388
|
+
} catch (error) {
|
|
3389
|
+
return {
|
|
3390
|
+
url,
|
|
3391
|
+
error: error instanceof Error ? error.message : String(error)
|
|
3392
|
+
};
|
|
3393
|
+
}
|
|
3394
|
+
})
|
|
3395
|
+
)
|
|
3396
|
+
};
|
|
3397
|
+
}
|
|
3398
|
+
function resolveApiKey(config) {
|
|
3399
|
+
const apiKey = resolveConfigValue(config.apiKey);
|
|
3400
|
+
if (!apiKey) {
|
|
3401
|
+
throw new Error("is missing an API key");
|
|
3402
|
+
}
|
|
3403
|
+
return apiKey;
|
|
3404
|
+
}
|
|
3405
|
+
function buildHeaders(apiKey) {
|
|
3406
|
+
return {
|
|
3407
|
+
authorization: `Bearer ${apiKey}`,
|
|
3408
|
+
"content-type": "application/json"
|
|
3409
|
+
};
|
|
3410
|
+
}
|
|
3411
|
+
function resolveEndpoint(baseUrlReference, endpointPath) {
|
|
3412
|
+
const baseUrl = resolveConfigValue(baseUrlReference) ?? DEFAULT_BASE_URL;
|
|
3413
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
3414
|
+
const apiPath = endpointPath.replace(/^\/api\//, "");
|
|
3415
|
+
if (base.endsWith(endpointPath)) {
|
|
3416
|
+
return base;
|
|
3417
|
+
}
|
|
3418
|
+
if (base.endsWith("/api")) {
|
|
3419
|
+
return `${base}/${apiPath}`;
|
|
3420
|
+
}
|
|
3421
|
+
return `${base}${endpointPath}`;
|
|
3422
|
+
}
|
|
3423
|
+
function clampMaxResults(value) {
|
|
3424
|
+
return Math.max(1, Math.min(10, Math.trunc(value || 0)));
|
|
3425
|
+
}
|
|
3426
|
+
async function buildHttpError(response) {
|
|
3427
|
+
const detail = await readErrorDetail(response);
|
|
3428
|
+
const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
3429
|
+
return detail ? `Ollama API request failed (${status}): ${detail}` : `Ollama API request failed (${status}).`;
|
|
3430
|
+
}
|
|
3431
|
+
async function readErrorDetail(response) {
|
|
3432
|
+
const text = (await response.text()).trim();
|
|
3433
|
+
if (!text) {
|
|
3434
|
+
return void 0;
|
|
3435
|
+
}
|
|
3436
|
+
try {
|
|
3437
|
+
const parsed = JSON.parse(text);
|
|
3438
|
+
for (const key of ["message", "error", "detail"]) {
|
|
3439
|
+
if (typeof parsed[key] === "string" && parsed[key].trim()) {
|
|
3440
|
+
return parsed[key];
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
return JSON.stringify(parsed);
|
|
3444
|
+
} catch {
|
|
3445
|
+
return text;
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
function buildFetchMetadata(data) {
|
|
3449
|
+
const metadata = {};
|
|
3450
|
+
if (data.title) {
|
|
3451
|
+
metadata.title = data.title;
|
|
3452
|
+
}
|
|
3453
|
+
if (data.links?.length) {
|
|
3454
|
+
metadata.links = data.links;
|
|
3455
|
+
}
|
|
3456
|
+
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
3457
|
+
}
|
|
2912
3458
|
|
|
2913
3459
|
// src/providers/openai.ts
|
|
2914
3460
|
import { Type as Type9 } from "typebox";
|
|
@@ -2987,7 +3533,7 @@ var searchResultSchema = {
|
|
|
2987
3533
|
}
|
|
2988
3534
|
}
|
|
2989
3535
|
};
|
|
2990
|
-
var
|
|
3536
|
+
var openaiImplementation = {
|
|
2991
3537
|
id: "openai",
|
|
2992
3538
|
label: "OpenAI",
|
|
2993
3539
|
docsUrl: "https://platform.openai.com/docs/guides/deep-research",
|
|
@@ -3040,11 +3586,16 @@ var openaiAdapter = {
|
|
|
3040
3586
|
},
|
|
3041
3587
|
async research(input, config, context, options) {
|
|
3042
3588
|
return await executeAsyncResearch({
|
|
3043
|
-
providerLabel:
|
|
3044
|
-
providerId:
|
|
3589
|
+
providerLabel: openaiImplementation.label,
|
|
3590
|
+
providerId: openaiImplementation.id,
|
|
3045
3591
|
context,
|
|
3046
|
-
start: (researchContext) =>
|
|
3047
|
-
|
|
3592
|
+
start: (researchContext) => openaiImplementation.startResearch(
|
|
3593
|
+
input,
|
|
3594
|
+
config,
|
|
3595
|
+
researchContext,
|
|
3596
|
+
options
|
|
3597
|
+
),
|
|
3598
|
+
poll: (id, researchContext) => openaiImplementation.pollResearch(id, config, researchContext, options)
|
|
3048
3599
|
});
|
|
3049
3600
|
},
|
|
3050
3601
|
async startResearch(input, config, context, options) {
|
|
@@ -3219,7 +3770,7 @@ function parseSearchResponse2(response, maxResults) {
|
|
|
3219
3770
|
}
|
|
3220
3771
|
const payload = parseSearchPayload(response.output_text);
|
|
3221
3772
|
return {
|
|
3222
|
-
provider:
|
|
3773
|
+
provider: openaiImplementation.id,
|
|
3223
3774
|
results: payload.sources.slice(0, maxResults).map((source) => ({
|
|
3224
3775
|
title: source.title.trim(),
|
|
3225
3776
|
url: source.url.trim(),
|
|
@@ -3258,7 +3809,7 @@ function formatResponseOutput(response, operation) {
|
|
|
3258
3809
|
}
|
|
3259
3810
|
}
|
|
3260
3811
|
return {
|
|
3261
|
-
provider:
|
|
3812
|
+
provider: openaiImplementation.id,
|
|
3262
3813
|
text: lines.join("\n").trimEnd(),
|
|
3263
3814
|
itemCount: citations.length,
|
|
3264
3815
|
metadata: {
|
|
@@ -3362,6 +3913,58 @@ function readPositiveInteger2(value) {
|
|
|
3362
3913
|
function readInteger(value) {
|
|
3363
3914
|
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
3364
3915
|
}
|
|
3916
|
+
var openaiProvider = defineProvider({
|
|
3917
|
+
id: "openai",
|
|
3918
|
+
label: openaiImplementation.label,
|
|
3919
|
+
docsUrl: openaiImplementation.docsUrl,
|
|
3920
|
+
config: {
|
|
3921
|
+
createTemplate: () => openaiImplementation.createTemplate(),
|
|
3922
|
+
fields: ["apiKey", "baseUrl", "options", "settings"],
|
|
3923
|
+
optionCapabilities: ["search", "answer", "research"]
|
|
3924
|
+
},
|
|
3925
|
+
getCapabilityStatus: (config, cwd, tool) => openaiImplementation.getCapabilityStatus(
|
|
3926
|
+
config,
|
|
3927
|
+
cwd,
|
|
3928
|
+
tool
|
|
3929
|
+
),
|
|
3930
|
+
capabilities: {
|
|
3931
|
+
search: defineCapability({
|
|
3932
|
+
options: openaiImplementation.getToolOptionsSchema?.("search"),
|
|
3933
|
+
async execute(input, ctx) {
|
|
3934
|
+
const { query: query2, maxResults, options } = input;
|
|
3935
|
+
return await openaiImplementation.search(
|
|
3936
|
+
query2,
|
|
3937
|
+
maxResults,
|
|
3938
|
+
ctx.config,
|
|
3939
|
+
ctx,
|
|
3940
|
+
options
|
|
3941
|
+
);
|
|
3942
|
+
}
|
|
3943
|
+
}),
|
|
3944
|
+
answer: defineCapability({
|
|
3945
|
+
options: openaiImplementation.getToolOptionsSchema?.("answer"),
|
|
3946
|
+
async execute(input, ctx) {
|
|
3947
|
+
return await openaiImplementation.answer(
|
|
3948
|
+
input.query,
|
|
3949
|
+
ctx.config,
|
|
3950
|
+
ctx,
|
|
3951
|
+
input.options
|
|
3952
|
+
);
|
|
3953
|
+
}
|
|
3954
|
+
}),
|
|
3955
|
+
research: defineCapability({
|
|
3956
|
+
options: openaiImplementation.getToolOptionsSchema?.("research"),
|
|
3957
|
+
async execute(input, ctx) {
|
|
3958
|
+
return await openaiImplementation.research(
|
|
3959
|
+
input.input,
|
|
3960
|
+
ctx.config,
|
|
3961
|
+
ctx,
|
|
3962
|
+
input.options
|
|
3963
|
+
);
|
|
3964
|
+
}
|
|
3965
|
+
})
|
|
3966
|
+
}
|
|
3967
|
+
});
|
|
3365
3968
|
|
|
3366
3969
|
// src/providers/parallel.ts
|
|
3367
3970
|
import { Type as Type10 } from "typebox";
|
|
@@ -3389,7 +3992,7 @@ var parallelExtractOptionsSchema = Type10.Object(
|
|
|
3389
3992
|
},
|
|
3390
3993
|
{ description: "Parallel extract options." }
|
|
3391
3994
|
);
|
|
3392
|
-
var
|
|
3995
|
+
var parallelImplementation = {
|
|
3393
3996
|
id: "parallel",
|
|
3394
3997
|
label: "Parallel",
|
|
3395
3998
|
docsUrl: "https://github.com/parallel-web/parallel-sdk-typescript",
|
|
@@ -3422,7 +4025,7 @@ var parallelAdapter = {
|
|
|
3422
4025
|
},
|
|
3423
4026
|
async search(query2, maxResults, config, context, options) {
|
|
3424
4027
|
const client = createClient6(config);
|
|
3425
|
-
const defaults =
|
|
4028
|
+
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
3426
4029
|
const response = await client.beta.search(
|
|
3427
4030
|
{
|
|
3428
4031
|
...defaults,
|
|
@@ -3433,7 +4036,7 @@ var parallelAdapter = {
|
|
|
3433
4036
|
buildRequestOptions3(context)
|
|
3434
4037
|
);
|
|
3435
4038
|
return {
|
|
3436
|
-
provider:
|
|
4039
|
+
provider: parallelImplementation.id,
|
|
3437
4040
|
results: response.results.slice(0, maxResults).map((result) => ({
|
|
3438
4041
|
title: result.title ?? result.url,
|
|
3439
4042
|
url: result.url,
|
|
@@ -3443,7 +4046,7 @@ var parallelAdapter = {
|
|
|
3443
4046
|
},
|
|
3444
4047
|
async contents(urls, config, context, options) {
|
|
3445
4048
|
const client = createClient6(config);
|
|
3446
|
-
const defaults =
|
|
4049
|
+
const defaults = asJsonObject(config.options?.extract) ?? {};
|
|
3447
4050
|
const response = await client.beta.extract(
|
|
3448
4051
|
{
|
|
3449
4052
|
...defaults,
|
|
@@ -3459,7 +4062,7 @@ var parallelAdapter = {
|
|
|
3459
4062
|
response.errors.map((error) => [error.url, error])
|
|
3460
4063
|
);
|
|
3461
4064
|
return {
|
|
3462
|
-
provider:
|
|
4065
|
+
provider: parallelImplementation.id,
|
|
3463
4066
|
answers: urls.map((url) => {
|
|
3464
4067
|
const result = resultsByUrl.get(url);
|
|
3465
4068
|
if (result) {
|
|
@@ -3494,6 +4097,46 @@ function createClient6(config) {
|
|
|
3494
4097
|
function buildRequestOptions3(context) {
|
|
3495
4098
|
return context.signal ? { signal: context.signal } : void 0;
|
|
3496
4099
|
}
|
|
4100
|
+
var parallelProvider = defineProvider({
|
|
4101
|
+
id: "parallel",
|
|
4102
|
+
label: parallelImplementation.label,
|
|
4103
|
+
docsUrl: parallelImplementation.docsUrl,
|
|
4104
|
+
config: {
|
|
4105
|
+
createTemplate: () => parallelImplementation.createTemplate(),
|
|
4106
|
+
fields: ["apiKey", "baseUrl", "options", "settings"]
|
|
4107
|
+
},
|
|
4108
|
+
getCapabilityStatus: (config, cwd, tool) => parallelImplementation.getCapabilityStatus(
|
|
4109
|
+
config,
|
|
4110
|
+
cwd,
|
|
4111
|
+
tool
|
|
4112
|
+
),
|
|
4113
|
+
capabilities: {
|
|
4114
|
+
search: defineCapability({
|
|
4115
|
+
options: parallelImplementation.getToolOptionsSchema?.("search"),
|
|
4116
|
+
async execute(input, ctx) {
|
|
4117
|
+
const { query: query2, maxResults, options } = input;
|
|
4118
|
+
return await parallelImplementation.search(
|
|
4119
|
+
query2,
|
|
4120
|
+
maxResults,
|
|
4121
|
+
ctx.config,
|
|
4122
|
+
ctx,
|
|
4123
|
+
options
|
|
4124
|
+
);
|
|
4125
|
+
}
|
|
4126
|
+
}),
|
|
4127
|
+
contents: defineCapability({
|
|
4128
|
+
options: parallelImplementation.getToolOptionsSchema?.("contents"),
|
|
4129
|
+
async execute(input, ctx) {
|
|
4130
|
+
return await parallelImplementation.contents(
|
|
4131
|
+
input.urls,
|
|
4132
|
+
ctx.config,
|
|
4133
|
+
ctx,
|
|
4134
|
+
input.options
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
4137
|
+
})
|
|
4138
|
+
}
|
|
4139
|
+
});
|
|
3497
4140
|
|
|
3498
4141
|
// src/providers/perplexity.ts
|
|
3499
4142
|
import PerplexityClient from "@perplexity-ai/perplexity_ai";
|
|
@@ -3539,7 +4182,7 @@ var perplexityResearchOptionsSchema = Type11.Object(
|
|
|
3539
4182
|
},
|
|
3540
4183
|
{ description: "Perplexity research options." }
|
|
3541
4184
|
);
|
|
3542
|
-
var
|
|
4185
|
+
var perplexityImplementation = {
|
|
3543
4186
|
id: "perplexity",
|
|
3544
4187
|
label: "Perplexity",
|
|
3545
4188
|
docsUrl: "https://docs.perplexity.ai/docs/sdk/overview.md",
|
|
@@ -3574,7 +4217,7 @@ var perplexityAdapter = {
|
|
|
3574
4217
|
async search(query2, maxResults, config, context, options) {
|
|
3575
4218
|
const client = createClient7(config);
|
|
3576
4219
|
const request = {
|
|
3577
|
-
...
|
|
4220
|
+
...asJsonObject(config.options?.search) ?? {},
|
|
3578
4221
|
...options ?? {},
|
|
3579
4222
|
query: query2,
|
|
3580
4223
|
max_results: maxResults
|
|
@@ -3584,7 +4227,7 @@ var perplexityAdapter = {
|
|
|
3584
4227
|
buildRequestOptions4(context)
|
|
3585
4228
|
);
|
|
3586
4229
|
return {
|
|
3587
|
-
provider:
|
|
4230
|
+
provider: perplexityImplementation.id,
|
|
3588
4231
|
results: response.results.slice(0, maxResults).map((result) => ({
|
|
3589
4232
|
title: result.title,
|
|
3590
4233
|
url: result.url,
|
|
@@ -3619,9 +4262,7 @@ var perplexityAdapter = {
|
|
|
3619
4262
|
};
|
|
3620
4263
|
async function runSilentForegroundChatTool(input, config, context, fallbackModel, label, options, isResearch = false) {
|
|
3621
4264
|
const client = createClient7(config);
|
|
3622
|
-
const defaults =
|
|
3623
|
-
isResearch ? asJsonObject(config.options?.research) : asJsonObject(config.options?.answer)
|
|
3624
|
-
) ?? {};
|
|
4265
|
+
const defaults = (isResearch ? asJsonObject(config.options?.research) : asJsonObject(config.options?.answer)) ?? {};
|
|
3625
4266
|
const request = {
|
|
3626
4267
|
...defaults,
|
|
3627
4268
|
...options ?? {},
|
|
@@ -3646,14 +4287,14 @@ async function runSilentForegroundChatTool(input, config, context, fallbackModel
|
|
|
3646
4287
|
}
|
|
3647
4288
|
}
|
|
3648
4289
|
return {
|
|
3649
|
-
provider:
|
|
4290
|
+
provider: perplexityImplementation.id,
|
|
3650
4291
|
text: lines.join("\n").trimEnd(),
|
|
3651
4292
|
itemCount: sources.length
|
|
3652
4293
|
};
|
|
3653
4294
|
}
|
|
3654
4295
|
async function runStreamingForegroundChatTool(input, config, context, fallbackModel, label, options) {
|
|
3655
4296
|
const client = createClient7(config);
|
|
3656
|
-
const defaults =
|
|
4297
|
+
const defaults = asJsonObject(config.options?.research) ?? {};
|
|
3657
4298
|
const request = {
|
|
3658
4299
|
...defaults,
|
|
3659
4300
|
...options ?? {},
|
|
@@ -3688,7 +4329,7 @@ async function runStreamingForegroundChatTool(input, config, context, fallbackMo
|
|
|
3688
4329
|
}
|
|
3689
4330
|
}
|
|
3690
4331
|
return {
|
|
3691
|
-
provider:
|
|
4332
|
+
provider: perplexityImplementation.id,
|
|
3692
4333
|
text: lines.join("\n").trimEnd(),
|
|
3693
4334
|
itemCount: dedupedSources.length
|
|
3694
4335
|
};
|
|
@@ -3773,10 +4414,61 @@ function extractSources(response) {
|
|
|
3773
4414
|
function buildRequestOptions4(context) {
|
|
3774
4415
|
return context.signal ? { signal: context.signal } : void 0;
|
|
3775
4416
|
}
|
|
4417
|
+
var perplexityProvider = defineProvider({
|
|
4418
|
+
id: "perplexity",
|
|
4419
|
+
label: perplexityImplementation.label,
|
|
4420
|
+
docsUrl: perplexityImplementation.docsUrl,
|
|
4421
|
+
config: {
|
|
4422
|
+
createTemplate: () => perplexityImplementation.createTemplate(),
|
|
4423
|
+
fields: ["apiKey", "baseUrl", "options", "settings"]
|
|
4424
|
+
},
|
|
4425
|
+
getCapabilityStatus: (config, cwd, tool) => perplexityImplementation.getCapabilityStatus(
|
|
4426
|
+
config,
|
|
4427
|
+
cwd,
|
|
4428
|
+
tool
|
|
4429
|
+
),
|
|
4430
|
+
capabilities: {
|
|
4431
|
+
search: defineCapability({
|
|
4432
|
+
options: perplexityImplementation.getToolOptionsSchema?.("search"),
|
|
4433
|
+
async execute(input, ctx) {
|
|
4434
|
+
const { query: query2, maxResults, options } = input;
|
|
4435
|
+
return await perplexityImplementation.search(
|
|
4436
|
+
query2,
|
|
4437
|
+
maxResults,
|
|
4438
|
+
ctx.config,
|
|
4439
|
+
ctx,
|
|
4440
|
+
options
|
|
4441
|
+
);
|
|
4442
|
+
}
|
|
4443
|
+
}),
|
|
4444
|
+
answer: defineCapability({
|
|
4445
|
+
options: perplexityImplementation.getToolOptionsSchema?.("answer"),
|
|
4446
|
+
async execute(input, ctx) {
|
|
4447
|
+
return await perplexityImplementation.answer(
|
|
4448
|
+
input.query,
|
|
4449
|
+
ctx.config,
|
|
4450
|
+
ctx,
|
|
4451
|
+
input.options
|
|
4452
|
+
);
|
|
4453
|
+
}
|
|
4454
|
+
}),
|
|
4455
|
+
research: defineCapability({
|
|
4456
|
+
options: perplexityImplementation.getToolOptionsSchema?.("research"),
|
|
4457
|
+
async execute(input, ctx) {
|
|
4458
|
+
return await perplexityImplementation.research(
|
|
4459
|
+
input.input,
|
|
4460
|
+
ctx.config,
|
|
4461
|
+
ctx,
|
|
4462
|
+
input.options
|
|
4463
|
+
);
|
|
4464
|
+
}
|
|
4465
|
+
})
|
|
4466
|
+
}
|
|
4467
|
+
});
|
|
3776
4468
|
|
|
3777
4469
|
// src/providers/serper.ts
|
|
3778
4470
|
import { Type as Type12 } from "typebox";
|
|
3779
|
-
var
|
|
4471
|
+
var DEFAULT_BASE_URL2 = "https://google.serper.dev";
|
|
3780
4472
|
var serperSearchOptionsSchema = Type12.Object(
|
|
3781
4473
|
{
|
|
3782
4474
|
gl: Type12.Optional(
|
|
@@ -3808,7 +4500,7 @@ var serperSearchOptionsSchema = Type12.Object(
|
|
|
3808
4500
|
},
|
|
3809
4501
|
{ description: "Serper search options." }
|
|
3810
4502
|
);
|
|
3811
|
-
var
|
|
4503
|
+
var serperImplementation = {
|
|
3812
4504
|
id: "serper",
|
|
3813
4505
|
label: "Serper",
|
|
3814
4506
|
docsUrl: "https://serper.dev/",
|
|
@@ -3834,15 +4526,15 @@ var serperAdapter = {
|
|
|
3834
4526
|
if (!apiKey) {
|
|
3835
4527
|
throw new Error("is missing an API key");
|
|
3836
4528
|
}
|
|
3837
|
-
const defaults =
|
|
3838
|
-
const
|
|
4529
|
+
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
4530
|
+
const callOptions = asJsonObject(options);
|
|
3839
4531
|
const {
|
|
3840
4532
|
q: _ignoredQuery,
|
|
3841
4533
|
num: _ignoredNum,
|
|
3842
4534
|
...providerOptions
|
|
3843
4535
|
} = {
|
|
3844
4536
|
...defaults,
|
|
3845
|
-
...
|
|
4537
|
+
...callOptions ?? {}
|
|
3846
4538
|
};
|
|
3847
4539
|
const response = await fetch(joinUrl(resolveConfigValue(config.baseUrl)), {
|
|
3848
4540
|
method: "POST",
|
|
@@ -3852,39 +4544,39 @@ var serperAdapter = {
|
|
|
3852
4544
|
},
|
|
3853
4545
|
body: JSON.stringify({
|
|
3854
4546
|
q: query2,
|
|
3855
|
-
num:
|
|
4547
|
+
num: clampMaxResults2(maxResults),
|
|
3856
4548
|
...providerOptions
|
|
3857
4549
|
}),
|
|
3858
4550
|
signal: context.signal
|
|
3859
4551
|
});
|
|
3860
4552
|
if (!response.ok) {
|
|
3861
|
-
throw new Error(await
|
|
4553
|
+
throw new Error(await buildHttpError2(response));
|
|
3862
4554
|
}
|
|
3863
4555
|
const payload = await response.json();
|
|
3864
4556
|
const responseRecord = asRecord3(payload) ?? {};
|
|
3865
4557
|
const organic = asArray(responseRecord.organic) ?? [];
|
|
3866
4558
|
const searchContext = buildSearchContext(responseRecord);
|
|
3867
4559
|
return {
|
|
3868
|
-
provider:
|
|
4560
|
+
provider: serperImplementation.id,
|
|
3869
4561
|
results: organic.map((entry) => toSearchResult3(entry, searchContext)).filter(
|
|
3870
4562
|
(result) => result !== null
|
|
3871
|
-
).slice(0,
|
|
4563
|
+
).slice(0, clampMaxResults2(maxResults))
|
|
3872
4564
|
};
|
|
3873
4565
|
}
|
|
3874
4566
|
};
|
|
3875
4567
|
function joinUrl(baseUrl) {
|
|
3876
|
-
const base = (baseUrl ??
|
|
4568
|
+
const base = (baseUrl ?? DEFAULT_BASE_URL2).replace(/\/+$/, "");
|
|
3877
4569
|
return `${base}/search`;
|
|
3878
4570
|
}
|
|
3879
|
-
function
|
|
4571
|
+
function clampMaxResults2(value) {
|
|
3880
4572
|
return Math.max(1, Math.min(20, Math.trunc(value || 0)));
|
|
3881
4573
|
}
|
|
3882
|
-
async function
|
|
3883
|
-
const detail = await
|
|
4574
|
+
async function buildHttpError2(response) {
|
|
4575
|
+
const detail = await readErrorDetail2(response);
|
|
3884
4576
|
const status = `${response.status}${response.statusText ? ` ${response.statusText}` : ""}`;
|
|
3885
4577
|
return detail ? `Serper API request failed (${status}): ${detail}` : `Serper API request failed (${status}).`;
|
|
3886
4578
|
}
|
|
3887
|
-
async function
|
|
4579
|
+
async function readErrorDetail2(response) {
|
|
3888
4580
|
const text = (await response.text()).trim();
|
|
3889
4581
|
if (!text) {
|
|
3890
4582
|
return void 0;
|
|
@@ -3971,6 +4663,36 @@ function readString4(value) {
|
|
|
3971
4663
|
function readNumber(value) {
|
|
3972
4664
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3973
4665
|
}
|
|
4666
|
+
var serperProvider = defineProvider({
|
|
4667
|
+
id: "serper",
|
|
4668
|
+
label: serperImplementation.label,
|
|
4669
|
+
docsUrl: serperImplementation.docsUrl,
|
|
4670
|
+
config: {
|
|
4671
|
+
createTemplate: () => serperImplementation.createTemplate(),
|
|
4672
|
+
fields: ["apiKey", "baseUrl", "options", "settings"],
|
|
4673
|
+
optionCapabilities: ["search"]
|
|
4674
|
+
},
|
|
4675
|
+
getCapabilityStatus: (config, cwd, tool) => serperImplementation.getCapabilityStatus(
|
|
4676
|
+
config,
|
|
4677
|
+
cwd,
|
|
4678
|
+
tool
|
|
4679
|
+
),
|
|
4680
|
+
capabilities: {
|
|
4681
|
+
search: defineCapability({
|
|
4682
|
+
options: serperImplementation.getToolOptionsSchema?.("search"),
|
|
4683
|
+
async execute(input, ctx) {
|
|
4684
|
+
const { query: query2, maxResults, options } = input;
|
|
4685
|
+
return await serperImplementation.search(
|
|
4686
|
+
query2,
|
|
4687
|
+
maxResults,
|
|
4688
|
+
ctx.config,
|
|
4689
|
+
ctx,
|
|
4690
|
+
options
|
|
4691
|
+
);
|
|
4692
|
+
}
|
|
4693
|
+
})
|
|
4694
|
+
}
|
|
4695
|
+
});
|
|
3974
4696
|
|
|
3975
4697
|
// src/providers/tavily.ts
|
|
3976
4698
|
import { Type as Type13 } from "typebox";
|
|
@@ -4054,7 +4776,7 @@ var tavilyExtractOptionsSchema = Type13.Object(
|
|
|
4054
4776
|
},
|
|
4055
4777
|
{ description: "Tavily extract options." }
|
|
4056
4778
|
);
|
|
4057
|
-
var
|
|
4779
|
+
var tavilyImplementation = {
|
|
4058
4780
|
id: "tavily",
|
|
4059
4781
|
label: "Tavily",
|
|
4060
4782
|
docsUrl: "https://docs.tavily.com/sdk/javascript/reference",
|
|
@@ -4087,14 +4809,14 @@ var tavilyAdapter = {
|
|
|
4087
4809
|
},
|
|
4088
4810
|
async search(query2, maxResults, config, _context, options) {
|
|
4089
4811
|
const client = createClient8(config);
|
|
4090
|
-
const defaults =
|
|
4812
|
+
const defaults = asJsonObject(config.options?.search) ?? {};
|
|
4091
4813
|
const response = await client.search(query2, {
|
|
4092
4814
|
...defaults,
|
|
4093
4815
|
...options ?? {},
|
|
4094
4816
|
maxResults
|
|
4095
4817
|
});
|
|
4096
4818
|
return {
|
|
4097
|
-
provider:
|
|
4819
|
+
provider: tavilyImplementation.id,
|
|
4098
4820
|
results: response.results.slice(0, maxResults).map((result) => ({
|
|
4099
4821
|
title: result.title || result.url || "Untitled",
|
|
4100
4822
|
url: result.url || "",
|
|
@@ -4106,7 +4828,7 @@ var tavilyAdapter = {
|
|
|
4106
4828
|
},
|
|
4107
4829
|
async contents(urls, config, _context, options) {
|
|
4108
4830
|
const client = createClient8(config);
|
|
4109
|
-
const defaults =
|
|
4831
|
+
const defaults = asJsonObject(config.options?.extract) ?? {};
|
|
4110
4832
|
const response = await client.extract(urls, {
|
|
4111
4833
|
...defaults,
|
|
4112
4834
|
...options ?? {}
|
|
@@ -4118,7 +4840,7 @@ var tavilyAdapter = {
|
|
|
4118
4840
|
response.failedResults.map((result) => [result.url, result])
|
|
4119
4841
|
);
|
|
4120
4842
|
return {
|
|
4121
|
-
provider:
|
|
4843
|
+
provider: tavilyImplementation.id,
|
|
4122
4844
|
answers: urls.map((url) => {
|
|
4123
4845
|
const result = resultsByUrl.get(url);
|
|
4124
4846
|
if (result) {
|
|
@@ -4173,6 +4895,46 @@ function buildExtractMetadata(response, result) {
|
|
|
4173
4895
|
};
|
|
4174
4896
|
return Object.keys(metadata).length > 0 ? metadata : void 0;
|
|
4175
4897
|
}
|
|
4898
|
+
var tavilyProvider = defineProvider({
|
|
4899
|
+
id: "tavily",
|
|
4900
|
+
label: tavilyImplementation.label,
|
|
4901
|
+
docsUrl: tavilyImplementation.docsUrl,
|
|
4902
|
+
config: {
|
|
4903
|
+
createTemplate: () => tavilyImplementation.createTemplate(),
|
|
4904
|
+
fields: ["apiKey", "baseUrl", "options", "settings"]
|
|
4905
|
+
},
|
|
4906
|
+
getCapabilityStatus: (config, cwd, tool) => tavilyImplementation.getCapabilityStatus(
|
|
4907
|
+
config,
|
|
4908
|
+
cwd,
|
|
4909
|
+
tool
|
|
4910
|
+
),
|
|
4911
|
+
capabilities: {
|
|
4912
|
+
search: defineCapability({
|
|
4913
|
+
options: tavilyImplementation.getToolOptionsSchema?.("search"),
|
|
4914
|
+
async execute(input, ctx) {
|
|
4915
|
+
const { query: query2, maxResults, options } = input;
|
|
4916
|
+
return await tavilyImplementation.search(
|
|
4917
|
+
query2,
|
|
4918
|
+
maxResults,
|
|
4919
|
+
ctx.config,
|
|
4920
|
+
ctx,
|
|
4921
|
+
options
|
|
4922
|
+
);
|
|
4923
|
+
}
|
|
4924
|
+
}),
|
|
4925
|
+
contents: defineCapability({
|
|
4926
|
+
options: tavilyImplementation.getToolOptionsSchema?.("contents"),
|
|
4927
|
+
async execute(input, ctx) {
|
|
4928
|
+
return await tavilyImplementation.contents(
|
|
4929
|
+
input.urls,
|
|
4930
|
+
ctx.config,
|
|
4931
|
+
ctx,
|
|
4932
|
+
input.options
|
|
4933
|
+
);
|
|
4934
|
+
}
|
|
4935
|
+
})
|
|
4936
|
+
}
|
|
4937
|
+
});
|
|
4176
4938
|
|
|
4177
4939
|
// src/providers/valyu.ts
|
|
4178
4940
|
import { Type as Type14 } from "typebox";
|
|
@@ -4221,7 +4983,7 @@ var valyuResearchOptionsSchema = Type14.Object(
|
|
|
4221
4983
|
},
|
|
4222
4984
|
{ description: "Valyu research options." }
|
|
4223
4985
|
);
|
|
4224
|
-
var
|
|
4986
|
+
var valyuImplementation = {
|
|
4225
4987
|
id: "valyu",
|
|
4226
4988
|
label: "Valyu",
|
|
4227
4989
|
docsUrl: "https://docs.valyu.ai/sdk/typescript-sdk",
|
|
@@ -4254,7 +5016,7 @@ var valyuAdapter = {
|
|
|
4254
5016
|
async search(query2, maxResults, config, _context, searchOptions) {
|
|
4255
5017
|
const client = createClient9(config);
|
|
4256
5018
|
const options = {
|
|
4257
|
-
...
|
|
5019
|
+
...asJsonObject(config.options?.search) ?? {},
|
|
4258
5020
|
...searchOptions ?? {},
|
|
4259
5021
|
maxNumResults: maxResults
|
|
4260
5022
|
};
|
|
@@ -4263,7 +5025,7 @@ var valyuAdapter = {
|
|
|
4263
5025
|
throw new Error(response.error || "search failed");
|
|
4264
5026
|
}
|
|
4265
5027
|
return {
|
|
4266
|
-
provider:
|
|
5028
|
+
provider: valyuImplementation.id,
|
|
4267
5029
|
results: (response.results ?? []).slice(0, maxResults).map((result) => ({
|
|
4268
5030
|
title: result.title,
|
|
4269
5031
|
url: result.url,
|
|
@@ -4287,7 +5049,7 @@ var valyuAdapter = {
|
|
|
4287
5049
|
)
|
|
4288
5050
|
);
|
|
4289
5051
|
return {
|
|
4290
|
-
provider:
|
|
5052
|
+
provider: valyuImplementation.id,
|
|
4291
5053
|
answers: urls.map((url) => {
|
|
4292
5054
|
const result = resultsByUrl.get(url);
|
|
4293
5055
|
if (!result) {
|
|
@@ -4311,7 +5073,7 @@ var valyuAdapter = {
|
|
|
4311
5073
|
async answer(query2, config, _context, options) {
|
|
4312
5074
|
const client = createClient9(config);
|
|
4313
5075
|
const response = await client.answer(query2, {
|
|
4314
|
-
...
|
|
5076
|
+
...asJsonObject(config.options?.answer) ?? {},
|
|
4315
5077
|
...options ?? {},
|
|
4316
5078
|
streaming: false
|
|
4317
5079
|
});
|
|
@@ -4333,25 +5095,30 @@ var valyuAdapter = {
|
|
|
4333
5095
|
}
|
|
4334
5096
|
}
|
|
4335
5097
|
return {
|
|
4336
|
-
provider:
|
|
5098
|
+
provider: valyuImplementation.id,
|
|
4337
5099
|
text: lines.join("\n").trimEnd(),
|
|
4338
5100
|
itemCount: sources.length
|
|
4339
5101
|
};
|
|
4340
5102
|
},
|
|
4341
5103
|
async research(input, config, context, options) {
|
|
4342
5104
|
return await executeAsyncResearch({
|
|
4343
|
-
providerLabel:
|
|
4344
|
-
providerId:
|
|
5105
|
+
providerLabel: valyuImplementation.label,
|
|
5106
|
+
providerId: valyuImplementation.id,
|
|
4345
5107
|
context,
|
|
4346
|
-
start: (researchContext) =>
|
|
4347
|
-
|
|
5108
|
+
start: (researchContext) => valyuImplementation.startResearch(
|
|
5109
|
+
input,
|
|
5110
|
+
config,
|
|
5111
|
+
researchContext,
|
|
5112
|
+
options
|
|
5113
|
+
),
|
|
5114
|
+
poll: (id, researchContext) => valyuImplementation.pollResearch(id, config, researchContext, options)
|
|
4348
5115
|
});
|
|
4349
5116
|
},
|
|
4350
5117
|
async startResearch(input, config, _context, options) {
|
|
4351
5118
|
const client = createClient9(config);
|
|
4352
5119
|
const task = await client.deepresearch.create({
|
|
4353
5120
|
input,
|
|
4354
|
-
...
|
|
5121
|
+
...asJsonObject(config.options?.research) ?? {},
|
|
4355
5122
|
...options ?? {}
|
|
4356
5123
|
});
|
|
4357
5124
|
if (!task.success || !task.deepresearch_id) {
|
|
@@ -4382,7 +5149,7 @@ var valyuAdapter = {
|
|
|
4382
5149
|
return {
|
|
4383
5150
|
status: "completed",
|
|
4384
5151
|
output: {
|
|
4385
|
-
provider:
|
|
5152
|
+
provider: valyuImplementation.id,
|
|
4386
5153
|
text: lines.join("\n").trimEnd(),
|
|
4387
5154
|
itemCount: sources.length
|
|
4388
5155
|
}
|
|
@@ -4410,43 +5177,93 @@ function createClient9(config) {
|
|
|
4410
5177
|
}
|
|
4411
5178
|
return new ValyuClient(apiKey, resolveConfigValue(config.baseUrl));
|
|
4412
5179
|
}
|
|
5180
|
+
var valyuProvider = defineProvider({
|
|
5181
|
+
id: "valyu",
|
|
5182
|
+
label: valyuImplementation.label,
|
|
5183
|
+
docsUrl: valyuImplementation.docsUrl,
|
|
5184
|
+
config: {
|
|
5185
|
+
createTemplate: () => valyuImplementation.createTemplate(),
|
|
5186
|
+
fields: ["apiKey", "baseUrl", "options", "settings"],
|
|
5187
|
+
optionCapabilities: ["search", "answer", "research"]
|
|
5188
|
+
},
|
|
5189
|
+
getCapabilityStatus: (config, cwd, tool) => valyuImplementation.getCapabilityStatus(
|
|
5190
|
+
config,
|
|
5191
|
+
cwd,
|
|
5192
|
+
tool
|
|
5193
|
+
),
|
|
5194
|
+
capabilities: {
|
|
5195
|
+
search: defineCapability({
|
|
5196
|
+
options: valyuImplementation.getToolOptionsSchema?.("search"),
|
|
5197
|
+
async execute(input, ctx) {
|
|
5198
|
+
const { query: query2, maxResults, options } = input;
|
|
5199
|
+
return await valyuImplementation.search(
|
|
5200
|
+
query2,
|
|
5201
|
+
maxResults,
|
|
5202
|
+
ctx.config,
|
|
5203
|
+
ctx,
|
|
5204
|
+
options
|
|
5205
|
+
);
|
|
5206
|
+
}
|
|
5207
|
+
}),
|
|
5208
|
+
contents: defineCapability({
|
|
5209
|
+
options: valyuImplementation.getToolOptionsSchema?.("contents"),
|
|
5210
|
+
async execute(input, ctx) {
|
|
5211
|
+
return await valyuImplementation.contents(
|
|
5212
|
+
input.urls,
|
|
5213
|
+
ctx.config,
|
|
5214
|
+
ctx,
|
|
5215
|
+
input.options
|
|
5216
|
+
);
|
|
5217
|
+
}
|
|
5218
|
+
}),
|
|
5219
|
+
answer: defineCapability({
|
|
5220
|
+
options: valyuImplementation.getToolOptionsSchema?.("answer"),
|
|
5221
|
+
async execute(input, ctx) {
|
|
5222
|
+
return await valyuImplementation.answer(
|
|
5223
|
+
input.query,
|
|
5224
|
+
ctx.config,
|
|
5225
|
+
ctx,
|
|
5226
|
+
input.options
|
|
5227
|
+
);
|
|
5228
|
+
}
|
|
5229
|
+
}),
|
|
5230
|
+
research: defineCapability({
|
|
5231
|
+
options: valyuImplementation.getToolOptionsSchema?.("research"),
|
|
5232
|
+
async execute(input, ctx) {
|
|
5233
|
+
return await valyuImplementation.research(
|
|
5234
|
+
input.input,
|
|
5235
|
+
ctx.config,
|
|
5236
|
+
ctx,
|
|
5237
|
+
input.options
|
|
5238
|
+
);
|
|
5239
|
+
}
|
|
5240
|
+
})
|
|
5241
|
+
}
|
|
5242
|
+
});
|
|
4413
5243
|
|
|
4414
5244
|
// src/providers/index.ts
|
|
4415
|
-
var
|
|
4416
|
-
claude:
|
|
4417
|
-
codex:
|
|
4418
|
-
cloudflare:
|
|
4419
|
-
custom:
|
|
4420
|
-
exa:
|
|
4421
|
-
firecrawl:
|
|
4422
|
-
gemini:
|
|
4423
|
-
linkup:
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
5245
|
+
var PROVIDERS = defineProviders({
|
|
5246
|
+
claude: claudeProvider,
|
|
5247
|
+
codex: codexProvider,
|
|
5248
|
+
cloudflare: cloudflareProvider,
|
|
5249
|
+
custom: customProvider,
|
|
5250
|
+
exa: exaProvider,
|
|
5251
|
+
firecrawl: firecrawlProvider,
|
|
5252
|
+
gemini: geminiProvider,
|
|
5253
|
+
linkup: linkupProvider,
|
|
5254
|
+
ollama: ollamaProvider,
|
|
5255
|
+
openai: openaiProvider,
|
|
5256
|
+
parallel: parallelProvider,
|
|
5257
|
+
perplexity: perplexityProvider,
|
|
5258
|
+
serper: serperProvider,
|
|
5259
|
+
tavily: tavilyProvider,
|
|
5260
|
+
valyu: valyuProvider
|
|
5261
|
+
});
|
|
5262
|
+
var PROVIDERS_BY_ID = PROVIDERS;
|
|
5263
|
+
var PROVIDER_LIST = Object.values(PROVIDERS);
|
|
5264
|
+
var PROVIDER_IDS = Object.keys(PROVIDERS);
|
|
4432
5265
|
|
|
4433
5266
|
// src/types.ts
|
|
4434
|
-
var PROVIDER_IDS = [
|
|
4435
|
-
"claude",
|
|
4436
|
-
"cloudflare",
|
|
4437
|
-
"codex",
|
|
4438
|
-
"custom",
|
|
4439
|
-
"exa",
|
|
4440
|
-
"firecrawl",
|
|
4441
|
-
"gemini",
|
|
4442
|
-
"linkup",
|
|
4443
|
-
"openai",
|
|
4444
|
-
"parallel",
|
|
4445
|
-
"perplexity",
|
|
4446
|
-
"serper",
|
|
4447
|
-
"tavily",
|
|
4448
|
-
"valyu"
|
|
4449
|
-
];
|
|
4450
5267
|
var TOOLS = ["search", "contents", "answer", "research"];
|
|
4451
5268
|
|
|
4452
5269
|
// src/provider-tools.ts
|
|
@@ -4469,16 +5286,16 @@ var TOOL_INFO = {
|
|
|
4469
5286
|
}
|
|
4470
5287
|
};
|
|
4471
5288
|
function supportsTool(providerId, toolId) {
|
|
4472
|
-
|
|
5289
|
+
const capabilities = PROVIDERS[providerId].capabilities;
|
|
5290
|
+
return capabilities[toolId] !== void 0;
|
|
4473
5291
|
}
|
|
4474
5292
|
function getProviderTools(providerId) {
|
|
4475
|
-
|
|
4476
|
-
return TOOLS.filter((tool) => typeof provider[tool] === "function");
|
|
5293
|
+
return TOOLS.filter((tool) => supportsTool(providerId, tool));
|
|
4477
5294
|
}
|
|
4478
5295
|
function getCompatibleProviders(toolId) {
|
|
4479
|
-
return
|
|
4480
|
-
(provider) => provider.id
|
|
4481
|
-
);
|
|
5296
|
+
return PROVIDER_LIST.filter(
|
|
5297
|
+
(provider) => supportsTool(provider.id, toolId)
|
|
5298
|
+
).map((provider) => provider.id);
|
|
4482
5299
|
}
|
|
4483
5300
|
function getMappedProviderForTool(config, tool) {
|
|
4484
5301
|
return config.tools?.[tool];
|
|
@@ -4565,111 +5382,52 @@ function normalizeConfig(raw, source) {
|
|
|
4565
5382
|
return config;
|
|
4566
5383
|
}
|
|
4567
5384
|
function normalizeProvider(providerId, raw, source) {
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
case "
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
options: (value, innerSource, field) => parseOptionalCapabilityOptions(
|
|
4609
|
-
value,
|
|
4610
|
-
innerSource,
|
|
4611
|
-
field,
|
|
4612
|
-
["search", "answer", "research"]
|
|
4613
|
-
),
|
|
4614
|
-
settings: parseOptionalExecutionSettings
|
|
4615
|
-
});
|
|
4616
|
-
case "gemini":
|
|
4617
|
-
return parseProviderWithShape(raw, source, providerId, {
|
|
4618
|
-
apiKey: readOptionalString2,
|
|
4619
|
-
options: readOptionalObject,
|
|
4620
|
-
settings: parseOptionalExecutionSettings
|
|
4621
|
-
});
|
|
4622
|
-
case "openai":
|
|
4623
|
-
return parseProviderWithShape(raw, source, providerId, {
|
|
4624
|
-
apiKey: readOptionalString2,
|
|
4625
|
-
baseUrl: readOptionalString2,
|
|
4626
|
-
options: (value, innerSource, field) => parseOptionalCapabilityOptions(
|
|
4627
|
-
value,
|
|
4628
|
-
innerSource,
|
|
4629
|
-
field,
|
|
4630
|
-
["search", "answer", "research"]
|
|
4631
|
-
),
|
|
4632
|
-
settings: parseOptionalExecutionSettings
|
|
4633
|
-
});
|
|
4634
|
-
case "firecrawl":
|
|
4635
|
-
case "linkup":
|
|
4636
|
-
case "parallel":
|
|
4637
|
-
case "perplexity":
|
|
4638
|
-
return parseProviderWithShape(
|
|
4639
|
-
raw,
|
|
5385
|
+
const definition = PROVIDERS[providerId];
|
|
5386
|
+
return parseProviderWithShape(
|
|
5387
|
+
raw,
|
|
5388
|
+
source,
|
|
5389
|
+
providerId,
|
|
5390
|
+
buildProviderConfigShape(
|
|
5391
|
+
definition.config.fields,
|
|
5392
|
+
definition.config.optionCapabilities
|
|
5393
|
+
)
|
|
5394
|
+
);
|
|
5395
|
+
}
|
|
5396
|
+
function buildProviderConfigShape(fields, optionCapabilities) {
|
|
5397
|
+
return Object.fromEntries(
|
|
5398
|
+
fields.map((field) => [
|
|
5399
|
+
toProviderConfigKey(field),
|
|
5400
|
+
getProviderConfigFieldParser(field, optionCapabilities)
|
|
5401
|
+
])
|
|
5402
|
+
);
|
|
5403
|
+
}
|
|
5404
|
+
function toProviderConfigKey(field) {
|
|
5405
|
+
return field === "customOptions" ? "options" : field;
|
|
5406
|
+
}
|
|
5407
|
+
function getProviderConfigFieldParser(field, optionCapabilities) {
|
|
5408
|
+
switch (field) {
|
|
5409
|
+
case "accountId":
|
|
5410
|
+
case "apiKey":
|
|
5411
|
+
case "apiToken":
|
|
5412
|
+
case "baseUrl":
|
|
5413
|
+
case "codexPath":
|
|
5414
|
+
case "pathToClaudeCodeExecutable":
|
|
5415
|
+
return readOptionalString2;
|
|
5416
|
+
case "config":
|
|
5417
|
+
return readOptionalObject;
|
|
5418
|
+
case "customOptions":
|
|
5419
|
+
return parseOptionalCustomProviderOptions;
|
|
5420
|
+
case "env":
|
|
5421
|
+
return readOptionalStringMap;
|
|
5422
|
+
case "options":
|
|
5423
|
+
return optionCapabilities ? (value, source, field2) => parseOptionalCapabilityOptions(
|
|
5424
|
+
value,
|
|
4640
5425
|
source,
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
settings: parseOptionalExecutionSettings
|
|
4647
|
-
}
|
|
4648
|
-
);
|
|
4649
|
-
case "serper":
|
|
4650
|
-
return parseProviderWithShape(raw, source, providerId, {
|
|
4651
|
-
apiKey: readOptionalString2,
|
|
4652
|
-
baseUrl: readOptionalString2,
|
|
4653
|
-
options: (value, innerSource, field) => parseOptionalCapabilityOptions(
|
|
4654
|
-
value,
|
|
4655
|
-
innerSource,
|
|
4656
|
-
field,
|
|
4657
|
-
["search"]
|
|
4658
|
-
),
|
|
4659
|
-
settings: parseOptionalExecutionSettings
|
|
4660
|
-
});
|
|
4661
|
-
case "tavily":
|
|
4662
|
-
return parseProviderWithShape(raw, source, providerId, {
|
|
4663
|
-
apiKey: readOptionalString2,
|
|
4664
|
-
baseUrl: readOptionalString2,
|
|
4665
|
-
options: readOptionalObject,
|
|
4666
|
-
settings: parseOptionalExecutionSettings
|
|
4667
|
-
});
|
|
4668
|
-
case "custom":
|
|
4669
|
-
return parseProviderWithShape(raw, source, providerId, {
|
|
4670
|
-
options: parseOptionalCustomProviderOptions,
|
|
4671
|
-
settings: parseOptionalExecutionSettings
|
|
4672
|
-
});
|
|
5426
|
+
field2,
|
|
5427
|
+
optionCapabilities
|
|
5428
|
+
) : readOptionalObject;
|
|
5429
|
+
case "settings":
|
|
5430
|
+
return parseOptionalExecutionSettings;
|
|
4673
5431
|
}
|
|
4674
5432
|
}
|
|
4675
5433
|
function parseProviderWithShape(raw, source, providerId, shape) {
|
|
@@ -4755,36 +5513,47 @@ function parseExecutionSettings(value, source, field, allowSearch) {
|
|
|
4755
5513
|
if (unknownKeys.length > 0) {
|
|
4756
5514
|
throw new Error(`'${field}' in ${source} must be a JSON object.`);
|
|
4757
5515
|
}
|
|
4758
|
-
const parsed = {
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
|
|
4763
|
-
|
|
4764
|
-
|
|
4765
|
-
|
|
4766
|
-
|
|
4767
|
-
|
|
4768
|
-
|
|
4769
|
-
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
5516
|
+
const parsed = {};
|
|
5517
|
+
const requestTimeoutMs = parseOptionalPositiveInteger(
|
|
5518
|
+
settings.requestTimeoutMs,
|
|
5519
|
+
source,
|
|
5520
|
+
`${field}.requestTimeoutMs`
|
|
5521
|
+
);
|
|
5522
|
+
if (requestTimeoutMs !== void 0) {
|
|
5523
|
+
parsed.requestTimeoutMs = requestTimeoutMs;
|
|
5524
|
+
}
|
|
5525
|
+
const retryCount = parseOptionalNonNegativeInteger(
|
|
5526
|
+
settings.retryCount,
|
|
5527
|
+
source,
|
|
5528
|
+
`${field}.retryCount`
|
|
5529
|
+
);
|
|
5530
|
+
if (retryCount !== void 0) {
|
|
5531
|
+
parsed.retryCount = retryCount;
|
|
5532
|
+
}
|
|
5533
|
+
const retryDelayMs = parseOptionalPositiveInteger(
|
|
5534
|
+
settings.retryDelayMs,
|
|
5535
|
+
source,
|
|
5536
|
+
`${field}.retryDelayMs`
|
|
5537
|
+
);
|
|
5538
|
+
if (retryDelayMs !== void 0) {
|
|
5539
|
+
parsed.retryDelayMs = retryDelayMs;
|
|
5540
|
+
}
|
|
5541
|
+
const researchTimeoutMs = parseOptionalPositiveInteger(
|
|
5542
|
+
settings.researchTimeoutMs,
|
|
5543
|
+
source,
|
|
5544
|
+
`${field}.researchTimeoutMs`
|
|
5545
|
+
);
|
|
5546
|
+
if (researchTimeoutMs !== void 0) {
|
|
5547
|
+
parsed.researchTimeoutMs = researchTimeoutMs;
|
|
5548
|
+
}
|
|
5549
|
+
if (allowSearch && settings.search !== void 0) {
|
|
5550
|
+
parsed.search = parseSearchSettings(
|
|
5551
|
+
settings.search,
|
|
4776
5552
|
source,
|
|
4777
|
-
`${field}.
|
|
4778
|
-
)
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
settings.search,
|
|
4782
|
-
source,
|
|
4783
|
-
`${field}.search`
|
|
4784
|
-
)
|
|
4785
|
-
} : {}
|
|
4786
|
-
};
|
|
4787
|
-
return Object.values(parsed).some((entry) => entry !== void 0) ? parsed : {};
|
|
5553
|
+
`${field}.search`
|
|
5554
|
+
);
|
|
5555
|
+
}
|
|
5556
|
+
return parsed;
|
|
4788
5557
|
}
|
|
4789
5558
|
function parseToolProviderMapping(value, source, field) {
|
|
4790
5559
|
const mapping = requireObject2(
|
|
@@ -5118,93 +5887,39 @@ ${JSON.stringify(value, null, 2).trim()}
|
|
|
5118
5887
|
}
|
|
5119
5888
|
|
|
5120
5889
|
// src/options.ts
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
requestTimeoutMs: Type15.Optional(
|
|
5124
|
-
Type15.Integer({
|
|
5125
|
-
minimum: 1,
|
|
5126
|
-
description: "Maximum time in milliseconds to wait for a single provider request."
|
|
5127
|
-
})
|
|
5128
|
-
),
|
|
5129
|
-
retryCount: Type15.Optional(
|
|
5130
|
-
Type15.Integer({
|
|
5131
|
-
minimum: 0,
|
|
5132
|
-
description: "Number of times to retry transient failures."
|
|
5133
|
-
})
|
|
5134
|
-
),
|
|
5135
|
-
retryDelayMs: Type15.Optional(
|
|
5136
|
-
Type15.Integer({
|
|
5137
|
-
minimum: 1,
|
|
5138
|
-
description: "Initial delay in milliseconds before retrying. Later retries back off automatically."
|
|
5139
|
-
})
|
|
5140
|
-
)
|
|
5141
|
-
};
|
|
5142
|
-
var runtimeOptionsSchema = Type15.Object(runtimeOptionFields, {
|
|
5143
|
-
additionalProperties: false,
|
|
5144
|
-
description: "Local runtime controls for this tool call."
|
|
5145
|
-
});
|
|
5146
|
-
var searchPrefetchOptionsSchema = Type15.Object(
|
|
5147
|
-
{
|
|
5148
|
-
provider: Type15.Optional(
|
|
5149
|
-
Type15.Union([...PROVIDER_IDS.map((id) => Type15.Literal(id)), Type15.Null()], {
|
|
5150
|
-
description: "Contents-capable provider to prefetch search result URLs. Set to enable prefetch."
|
|
5151
|
-
})
|
|
5152
|
-
),
|
|
5153
|
-
maxUrls: Type15.Optional(
|
|
5154
|
-
Type15.Integer({
|
|
5155
|
-
minimum: 1,
|
|
5156
|
-
description: "Maximum number of search result URLs to prefetch."
|
|
5157
|
-
})
|
|
5158
|
-
),
|
|
5159
|
-
ttlMs: Type15.Optional(
|
|
5160
|
-
Type15.Integer({
|
|
5161
|
-
minimum: 1,
|
|
5162
|
-
description: "How long prefetched contents stay reusable in the local cache, in milliseconds."
|
|
5163
|
-
})
|
|
5164
|
-
)
|
|
5165
|
-
},
|
|
5166
|
-
{
|
|
5167
|
-
additionalProperties: false,
|
|
5168
|
-
description: "Background contents prefetch for search result URLs. Only runs when provider is set."
|
|
5169
|
-
}
|
|
5170
|
-
);
|
|
5171
|
-
var searchRuntimeOptionsSchema = Type15.Object(
|
|
5172
|
-
{
|
|
5173
|
-
...runtimeOptionFields,
|
|
5174
|
-
prefetch: Type15.Optional(searchPrefetchOptionsSchema)
|
|
5175
|
-
},
|
|
5176
|
-
{
|
|
5177
|
-
additionalProperties: false,
|
|
5178
|
-
description: "Local runtime controls for search."
|
|
5179
|
-
}
|
|
5180
|
-
);
|
|
5181
|
-
function buildToolOptionsSchema(capability, providerSchema) {
|
|
5182
|
-
const properties = {};
|
|
5183
|
-
const runtimeSchema = getRuntimeOptionsSchema(capability);
|
|
5184
|
-
if (runtimeSchema) {
|
|
5185
|
-
properties.runtime = Type15.Optional(runtimeSchema);
|
|
5186
|
-
}
|
|
5187
|
-
if (providerSchema && Object.keys(providerSchema.properties).length > 0) {
|
|
5188
|
-
properties.provider = Type15.Optional(providerSchema);
|
|
5189
|
-
}
|
|
5190
|
-
if (Object.keys(properties).length === 0) {
|
|
5890
|
+
function buildToolOptionsSchema(_capability, providerSchema) {
|
|
5891
|
+
if (!providerSchema || Object.keys(providerSchema.properties).length === 0) {
|
|
5191
5892
|
return void 0;
|
|
5192
5893
|
}
|
|
5193
|
-
return
|
|
5194
|
-
additionalProperties: false,
|
|
5195
|
-
description: "Options for this tool call split into provider-facing settings and local runtime controls."
|
|
5196
|
-
});
|
|
5894
|
+
return closeObjectSchemas(providerSchema);
|
|
5197
5895
|
}
|
|
5198
|
-
function
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
return searchRuntimeOptionsSchema;
|
|
5202
|
-
case "contents":
|
|
5203
|
-
case "answer":
|
|
5204
|
-
return runtimeOptionsSchema;
|
|
5205
|
-
default:
|
|
5206
|
-
return void 0;
|
|
5896
|
+
function closeObjectSchemas(schema) {
|
|
5897
|
+
if (!isSchemaRecord(schema)) {
|
|
5898
|
+
return schema;
|
|
5207
5899
|
}
|
|
5900
|
+
const properties = isSchemaRecord(schema.properties) ? Object.fromEntries(
|
|
5901
|
+
Object.entries(schema.properties).map(([key, value]) => [
|
|
5902
|
+
key,
|
|
5903
|
+
closeObjectSchemas(value)
|
|
5904
|
+
])
|
|
5905
|
+
) : schema.properties;
|
|
5906
|
+
const items = isSchemaRecord(schema.items) ? closeObjectSchemas(schema.items) : Array.isArray(schema.items) ? schema.items.map((item) => closeObjectSchemas(item)) : schema.items;
|
|
5907
|
+
return {
|
|
5908
|
+
...schema,
|
|
5909
|
+
...properties ? { properties } : {},
|
|
5910
|
+
...items ? { items } : {},
|
|
5911
|
+
...mapSchemaArray(schema, "anyOf"),
|
|
5912
|
+
...mapSchemaArray(schema, "oneOf"),
|
|
5913
|
+
...mapSchemaArray(schema, "allOf"),
|
|
5914
|
+
...schema.type === "object" && isSchemaRecord(schema.properties) ? { additionalProperties: false } : {}
|
|
5915
|
+
};
|
|
5916
|
+
}
|
|
5917
|
+
function mapSchemaArray(schema, key) {
|
|
5918
|
+
const value = schema[key];
|
|
5919
|
+
return Array.isArray(value) ? { [key]: value.map((entry) => closeObjectSchemas(entry)) } : {};
|
|
5920
|
+
}
|
|
5921
|
+
function isSchemaRecord(value) {
|
|
5922
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5208
5923
|
}
|
|
5209
5924
|
|
|
5210
5925
|
// src/prefetch-manager.ts
|
|
@@ -5212,19 +5927,16 @@ import { createHash } from "node:crypto";
|
|
|
5212
5927
|
|
|
5213
5928
|
// src/provider-resolution.ts
|
|
5214
5929
|
function supportsTool2(provider, tool) {
|
|
5215
|
-
return
|
|
5930
|
+
return provider.capabilities[tool] !== void 0;
|
|
5216
5931
|
}
|
|
5217
5932
|
function resolveSearchProvider(config, cwd, explicit) {
|
|
5218
5933
|
return resolveProviderForTool(config, cwd, "search", explicit);
|
|
5219
5934
|
}
|
|
5220
5935
|
function getEffectiveSharedSettings(config) {
|
|
5221
|
-
return
|
|
5222
|
-
...createDefaultExecutionSettings(),
|
|
5223
|
-
...config.settings ?? {}
|
|
5224
|
-
};
|
|
5936
|
+
return mergeExecutionSettings(createDefaultExecutionSettings(), config.settings) ?? createDefaultExecutionSettings();
|
|
5225
5937
|
}
|
|
5226
5938
|
function getEffectiveProviderConfig(config, providerId) {
|
|
5227
|
-
const defaults =
|
|
5939
|
+
const defaults = PROVIDERS[providerId].config.createTemplate();
|
|
5228
5940
|
const overrides = config.providers?.[providerId] ?? {};
|
|
5229
5941
|
const providerSettings = mergeExecutionSettings(
|
|
5230
5942
|
defaults.settings,
|
|
@@ -5279,7 +5991,7 @@ function getMappedProviderIdForTool(config, tool) {
|
|
|
5279
5991
|
return getMappedProviderForTool(config, tool);
|
|
5280
5992
|
}
|
|
5281
5993
|
function getProviderCapabilityStatus(config, cwd, providerId, tool) {
|
|
5282
|
-
const provider =
|
|
5994
|
+
const provider = PROVIDERS[providerId];
|
|
5283
5995
|
return provider.getCapabilityStatus(
|
|
5284
5996
|
getEffectiveProviderConfig(config, providerId),
|
|
5285
5997
|
cwd,
|
|
@@ -5326,7 +6038,7 @@ function resolveProviderForTool(config, cwd, tool, explicit) {
|
|
|
5326
6038
|
`No provider is configured for '${tool}'. Run /web-providers to configure tool mappings.`
|
|
5327
6039
|
);
|
|
5328
6040
|
}
|
|
5329
|
-
const provider =
|
|
6041
|
+
const provider = PROVIDERS[providerId];
|
|
5330
6042
|
if (!supportsTool2(provider, tool)) {
|
|
5331
6043
|
throw new Error(`Provider '${providerId}' does not support '${tool}'.`);
|
|
5332
6044
|
}
|
|
@@ -5342,21 +6054,27 @@ function resolveProviderForTool(config, cwd, tool, explicit) {
|
|
|
5342
6054
|
}
|
|
5343
6055
|
|
|
5344
6056
|
// src/provider-runtime.ts
|
|
5345
|
-
async function executeProviderRequest(provider, config, request,
|
|
6057
|
+
async function executeProviderRequest(provider, config, request, context) {
|
|
5346
6058
|
return await executeProviderExecution(
|
|
5347
6059
|
{
|
|
5348
6060
|
capability: request.capability,
|
|
5349
6061
|
providerLabel: provider.label,
|
|
5350
6062
|
settings: config.settings,
|
|
5351
|
-
execute: (executionContext) =>
|
|
6063
|
+
execute: (executionContext) => executeProviderCapability(
|
|
6064
|
+
provider,
|
|
6065
|
+
request.capability,
|
|
6066
|
+
providerInputFromRequest(request),
|
|
6067
|
+
{
|
|
6068
|
+
...executionContext,
|
|
6069
|
+
config
|
|
6070
|
+
}
|
|
6071
|
+
)
|
|
5352
6072
|
},
|
|
5353
|
-
options,
|
|
5354
6073
|
context
|
|
5355
6074
|
);
|
|
5356
6075
|
}
|
|
5357
|
-
async function executeProviderExecution(execution,
|
|
6076
|
+
async function executeProviderExecution(execution, context) {
|
|
5358
6077
|
if (execution.capability === "research") {
|
|
5359
|
-
rejectResearchExecutionControls(execution.providerLabel, options);
|
|
5360
6078
|
const deadline = createResearchDeadlineSignal(
|
|
5361
6079
|
context.signal,
|
|
5362
6080
|
execution.providerLabel,
|
|
@@ -5379,7 +6097,7 @@ async function executeProviderExecution(execution, options, context) {
|
|
|
5379
6097
|
deadline?.cleanup();
|
|
5380
6098
|
}
|
|
5381
6099
|
}
|
|
5382
|
-
const requestPolicy = resolveExecutionPolicy(execution.settings
|
|
6100
|
+
const requestPolicy = resolveExecutionPolicy(execution.settings);
|
|
5383
6101
|
try {
|
|
5384
6102
|
return await runWithExecutionPolicy(
|
|
5385
6103
|
`${execution.providerLabel} ${execution.capability} request`,
|
|
@@ -5396,65 +6114,36 @@ async function executeProviderExecution(execution, options, context) {
|
|
|
5396
6114
|
);
|
|
5397
6115
|
}
|
|
5398
6116
|
}
|
|
5399
|
-
|
|
6117
|
+
function providerInputFromRequest(request) {
|
|
5400
6118
|
switch (request.capability) {
|
|
5401
|
-
case "search":
|
|
5402
|
-
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
request.options
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
}
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
);
|
|
5423
|
-
}
|
|
5424
|
-
case "answer": {
|
|
5425
|
-
if (!provider.answer) {
|
|
5426
|
-
throw unsupportedProviderTool(provider, request.capability);
|
|
5427
|
-
}
|
|
5428
|
-
return await provider.answer(
|
|
5429
|
-
request.query,
|
|
5430
|
-
config,
|
|
5431
|
-
context,
|
|
5432
|
-
request.options
|
|
5433
|
-
);
|
|
5434
|
-
}
|
|
5435
|
-
case "research": {
|
|
5436
|
-
if (!provider.research) {
|
|
5437
|
-
throw unsupportedProviderTool(provider, request.capability);
|
|
5438
|
-
}
|
|
5439
|
-
return await provider.research(
|
|
5440
|
-
request.input,
|
|
5441
|
-
config,
|
|
5442
|
-
context,
|
|
5443
|
-
request.options
|
|
5444
|
-
);
|
|
5445
|
-
}
|
|
6119
|
+
case "search":
|
|
6120
|
+
return {
|
|
6121
|
+
query: request.query,
|
|
6122
|
+
maxResults: request.maxResults,
|
|
6123
|
+
options: request.options
|
|
6124
|
+
};
|
|
6125
|
+
case "contents":
|
|
6126
|
+
return {
|
|
6127
|
+
urls: request.urls,
|
|
6128
|
+
options: request.options
|
|
6129
|
+
};
|
|
6130
|
+
case "answer":
|
|
6131
|
+
return {
|
|
6132
|
+
query: request.query,
|
|
6133
|
+
options: request.options
|
|
6134
|
+
};
|
|
6135
|
+
case "research":
|
|
6136
|
+
return {
|
|
6137
|
+
input: request.input,
|
|
6138
|
+
options: request.options
|
|
6139
|
+
};
|
|
5446
6140
|
}
|
|
5447
6141
|
}
|
|
5448
|
-
function
|
|
5449
|
-
return new Error(`Provider '${provider.id}' does not support '${tool}'.`);
|
|
5450
|
-
}
|
|
5451
|
-
function resolveExecutionPolicy(defaults, options) {
|
|
5452
|
-
validateRuntimeOptions(options);
|
|
5453
|
-
const localOptions = parseLocalExecutionOptions(options);
|
|
6142
|
+
function resolveExecutionPolicy(defaults) {
|
|
5454
6143
|
return {
|
|
5455
|
-
requestTimeoutMs:
|
|
5456
|
-
retryCount:
|
|
5457
|
-
retryDelayMs:
|
|
6144
|
+
requestTimeoutMs: defaults?.requestTimeoutMs,
|
|
6145
|
+
retryCount: defaults?.retryCount ?? 0,
|
|
6146
|
+
retryDelayMs: defaults?.retryDelayMs ?? 2e3
|
|
5458
6147
|
};
|
|
5459
6148
|
}
|
|
5460
6149
|
function createResearchDeadlineSignal(signal, providerLabel, timeoutMs) {
|
|
@@ -5522,26 +6211,6 @@ async function withAbortSignal(promise, signal) {
|
|
|
5522
6211
|
);
|
|
5523
6212
|
});
|
|
5524
6213
|
}
|
|
5525
|
-
function rejectResearchExecutionControls(providerLabel, options) {
|
|
5526
|
-
if (!options || Object.keys(options).length === 0) {
|
|
5527
|
-
return;
|
|
5528
|
-
}
|
|
5529
|
-
throw new Error(`${providerLabel} research does not accept options.runtime.`);
|
|
5530
|
-
}
|
|
5531
|
-
function validateRuntimeOptions(options) {
|
|
5532
|
-
if (!options) {
|
|
5533
|
-
return;
|
|
5534
|
-
}
|
|
5535
|
-
const unsupportedKeys = Object.keys(options).filter(
|
|
5536
|
-
(key) => key !== "requestTimeoutMs" && key !== "retryCount" && key !== "retryDelayMs"
|
|
5537
|
-
);
|
|
5538
|
-
if (unsupportedKeys.length === 0) {
|
|
5539
|
-
return;
|
|
5540
|
-
}
|
|
5541
|
-
throw new Error(
|
|
5542
|
-
`Unsupported runtime options: ${unsupportedKeys.join(", ")}.`
|
|
5543
|
-
);
|
|
5544
|
-
}
|
|
5545
6214
|
|
|
5546
6215
|
// src/prefetch-manager.ts
|
|
5547
6216
|
var CONTENT_CACHE_VERSION = 2;
|
|
@@ -5596,7 +6265,6 @@ async function resolveContentsFromStore({
|
|
|
5596
6265
|
config,
|
|
5597
6266
|
cwd,
|
|
5598
6267
|
options,
|
|
5599
|
-
runtimeOptions,
|
|
5600
6268
|
signal,
|
|
5601
6269
|
onProgress
|
|
5602
6270
|
}) {
|
|
@@ -5608,7 +6276,6 @@ async function resolveContentsFromStore({
|
|
|
5608
6276
|
config,
|
|
5609
6277
|
cwd,
|
|
5610
6278
|
options,
|
|
5611
|
-
runtimeOptions,
|
|
5612
6279
|
signal,
|
|
5613
6280
|
onProgress
|
|
5614
6281
|
});
|
|
@@ -5619,28 +6286,10 @@ async function resolveContentsFromStore({
|
|
|
5619
6286
|
config,
|
|
5620
6287
|
cwd,
|
|
5621
6288
|
options,
|
|
5622
|
-
runtimeOptions,
|
|
5623
6289
|
signal,
|
|
5624
6290
|
onProgress
|
|
5625
6291
|
});
|
|
5626
6292
|
}
|
|
5627
|
-
function parseSearchContentsPrefetchOptions(options) {
|
|
5628
|
-
const raw = options?.prefetch;
|
|
5629
|
-
if (raw === void 0) {
|
|
5630
|
-
return void 0;
|
|
5631
|
-
}
|
|
5632
|
-
if (!isJsonObject3(raw)) {
|
|
5633
|
-
throw new Error("prefetch must be an object.");
|
|
5634
|
-
}
|
|
5635
|
-
const maxUrls = parseOptionalPositiveInteger2(raw.maxUrls, "maxUrls");
|
|
5636
|
-
const provider = parseOptionalProviderId(raw.provider);
|
|
5637
|
-
const ttlMs = parseOptionalPositiveInteger2(raw.ttlMs, "ttlMs");
|
|
5638
|
-
return {
|
|
5639
|
-
maxUrls,
|
|
5640
|
-
provider,
|
|
5641
|
-
ttlMs
|
|
5642
|
-
};
|
|
5643
|
-
}
|
|
5644
6293
|
function mergeSearchContentsPrefetchOptions(defaults, overrides) {
|
|
5645
6294
|
if (!defaults && !overrides) {
|
|
5646
6295
|
return void 0;
|
|
@@ -5651,13 +6300,6 @@ function mergeSearchContentsPrefetchOptions(defaults, overrides) {
|
|
|
5651
6300
|
ttlMs: overrides?.ttlMs !== void 0 ? overrides.ttlMs : defaults?.ttlMs
|
|
5652
6301
|
};
|
|
5653
6302
|
}
|
|
5654
|
-
function stripSearchContentsPrefetchOptions(options) {
|
|
5655
|
-
if (!options) {
|
|
5656
|
-
return void 0;
|
|
5657
|
-
}
|
|
5658
|
-
const { prefetch: _prefetch, ...rest } = options;
|
|
5659
|
-
return Object.keys(rest).length > 0 ? rest : void 0;
|
|
5660
|
-
}
|
|
5661
6303
|
function resetContentStore() {
|
|
5662
6304
|
contentStoreGeneration += 1;
|
|
5663
6305
|
contentCache.clear();
|
|
@@ -5669,7 +6311,6 @@ async function resolvePerUrlContents({
|
|
|
5669
6311
|
config,
|
|
5670
6312
|
cwd,
|
|
5671
6313
|
options,
|
|
5672
|
-
runtimeOptions,
|
|
5673
6314
|
signal,
|
|
5674
6315
|
onProgress
|
|
5675
6316
|
}) {
|
|
@@ -5681,7 +6322,6 @@ async function resolvePerUrlContents({
|
|
|
5681
6322
|
config,
|
|
5682
6323
|
cwd,
|
|
5683
6324
|
options,
|
|
5684
|
-
runtimeOptions,
|
|
5685
6325
|
signal,
|
|
5686
6326
|
onProgress
|
|
5687
6327
|
})
|
|
@@ -5728,7 +6368,6 @@ async function fetchBatchContents({
|
|
|
5728
6368
|
config,
|
|
5729
6369
|
cwd,
|
|
5730
6370
|
options,
|
|
5731
|
-
runtimeOptions,
|
|
5732
6371
|
signal,
|
|
5733
6372
|
onProgress,
|
|
5734
6373
|
ttlMs = DEFAULT_CONTENT_TTL_MS,
|
|
@@ -5744,7 +6383,6 @@ async function fetchBatchContents({
|
|
|
5744
6383
|
config,
|
|
5745
6384
|
cwd,
|
|
5746
6385
|
options,
|
|
5747
|
-
runtimeOptions,
|
|
5748
6386
|
signal,
|
|
5749
6387
|
onProgress
|
|
5750
6388
|
});
|
|
@@ -5775,7 +6413,6 @@ async function ensureContentsStored({
|
|
|
5775
6413
|
config,
|
|
5776
6414
|
cwd,
|
|
5777
6415
|
options,
|
|
5778
|
-
runtimeOptions,
|
|
5779
6416
|
signal,
|
|
5780
6417
|
onProgress,
|
|
5781
6418
|
ttlMs = DEFAULT_CONTENT_TTL_MS,
|
|
@@ -5800,7 +6437,6 @@ async function ensureContentsStored({
|
|
|
5800
6437
|
config,
|
|
5801
6438
|
cwd,
|
|
5802
6439
|
options,
|
|
5803
|
-
runtimeOptions,
|
|
5804
6440
|
signal,
|
|
5805
6441
|
onProgress
|
|
5806
6442
|
});
|
|
@@ -5831,11 +6467,10 @@ async function fetchContentsViaProvider({
|
|
|
5831
6467
|
config,
|
|
5832
6468
|
cwd,
|
|
5833
6469
|
options,
|
|
5834
|
-
runtimeOptions,
|
|
5835
6470
|
signal,
|
|
5836
6471
|
onProgress
|
|
5837
6472
|
}) {
|
|
5838
|
-
const provider =
|
|
6473
|
+
const provider = PROVIDERS_BY_ID[providerId];
|
|
5839
6474
|
const providerConfig = getEffectiveProviderConfig(config, providerId);
|
|
5840
6475
|
onProgress?.(
|
|
5841
6476
|
`Fetching contents via ${provider.label} for ${urls.length} URL(s)`
|
|
@@ -5848,7 +6483,6 @@ async function fetchContentsViaProvider({
|
|
|
5848
6483
|
urls,
|
|
5849
6484
|
options
|
|
5850
6485
|
},
|
|
5851
|
-
runtimeOptions,
|
|
5852
6486
|
{
|
|
5853
6487
|
cwd,
|
|
5854
6488
|
signal,
|
|
@@ -5948,7 +6582,7 @@ function resolveContentsProvider(config, cwd, explicitProvider) {
|
|
|
5948
6582
|
if (!explicitProvider) {
|
|
5949
6583
|
return void 0;
|
|
5950
6584
|
}
|
|
5951
|
-
const provider =
|
|
6585
|
+
const provider = PROVIDERS_BY_ID[explicitProvider];
|
|
5952
6586
|
if (!supportsTool(explicitProvider, "contents")) {
|
|
5953
6587
|
return void 0;
|
|
5954
6588
|
}
|
|
@@ -6018,27 +6652,6 @@ function formatUnknownError(error) {
|
|
|
6018
6652
|
function isJsonObject3(value) {
|
|
6019
6653
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6020
6654
|
}
|
|
6021
|
-
function parseOptionalPositiveInteger2(value, field) {
|
|
6022
|
-
if (value === void 0) {
|
|
6023
|
-
return void 0;
|
|
6024
|
-
}
|
|
6025
|
-
if (!Number.isInteger(value) || Number(value) <= 0) {
|
|
6026
|
-
throw new Error(`prefetch.${field} must be a positive integer.`);
|
|
6027
|
-
}
|
|
6028
|
-
return Number(value);
|
|
6029
|
-
}
|
|
6030
|
-
function parseOptionalProviderId(value) {
|
|
6031
|
-
if (value === void 0) {
|
|
6032
|
-
return void 0;
|
|
6033
|
-
}
|
|
6034
|
-
if (value === null) {
|
|
6035
|
-
return null;
|
|
6036
|
-
}
|
|
6037
|
-
if (isProviderId(value)) {
|
|
6038
|
-
return value;
|
|
6039
|
-
}
|
|
6040
|
-
throw new Error("prefetch.provider must be a valid provider id or null.");
|
|
6041
|
-
}
|
|
6042
6655
|
function isProviderId(value) {
|
|
6043
6656
|
return typeof value === "string" && PROVIDER_IDS.includes(value);
|
|
6044
6657
|
}
|
|
@@ -6050,7 +6663,7 @@ function isContentsAnswer(value) {
|
|
|
6050
6663
|
}
|
|
6051
6664
|
|
|
6052
6665
|
// src/provider-config-manifests.ts
|
|
6053
|
-
var
|
|
6666
|
+
var PROVIDER_SETTINGS = {
|
|
6054
6667
|
claude: {
|
|
6055
6668
|
settings: [
|
|
6056
6669
|
stringSetting({
|
|
@@ -6475,6 +7088,9 @@ var PROVIDER_CONFIG_MANIFESTS = {
|
|
|
6475
7088
|
linkup: {
|
|
6476
7089
|
settings: [apiKeySetting(), baseUrlSetting()]
|
|
6477
7090
|
},
|
|
7091
|
+
ollama: {
|
|
7092
|
+
settings: [apiKeySetting(), baseUrlSetting()]
|
|
7093
|
+
},
|
|
6478
7094
|
openai: {
|
|
6479
7095
|
settings: [
|
|
6480
7096
|
apiKeySetting(),
|
|
@@ -6711,9 +7327,20 @@ var PROVIDER_CONFIG_MANIFESTS = {
|
|
|
6711
7327
|
]
|
|
6712
7328
|
}
|
|
6713
7329
|
};
|
|
7330
|
+
var PROVIDER_CONFIG_MANIFESTS = deriveProviderConfigManifests();
|
|
6714
7331
|
function getProviderConfigManifest(providerId) {
|
|
6715
7332
|
return PROVIDER_CONFIG_MANIFESTS[providerId];
|
|
6716
7333
|
}
|
|
7334
|
+
function deriveProviderConfigManifests() {
|
|
7335
|
+
return Object.fromEntries(
|
|
7336
|
+
Object.keys(PROVIDERS).map((providerId) => [
|
|
7337
|
+
providerId,
|
|
7338
|
+
{
|
|
7339
|
+
settings: PROVIDER_SETTINGS[providerId]?.settings ?? []
|
|
7340
|
+
}
|
|
7341
|
+
])
|
|
7342
|
+
);
|
|
7343
|
+
}
|
|
6717
7344
|
function stringSetting(setting) {
|
|
6718
7345
|
return {
|
|
6719
7346
|
kind: "text",
|
|
@@ -7084,10 +7711,6 @@ function webProvidersExtension(pi) {
|
|
|
7084
7711
|
)
|
|
7085
7712
|
);
|
|
7086
7713
|
};
|
|
7087
|
-
registerManagedTools(pi, {
|
|
7088
|
-
activeWebResearchRequests,
|
|
7089
|
-
updateWebResearchWidget
|
|
7090
|
-
});
|
|
7091
7714
|
if ("registerMessageRenderer" in pi) {
|
|
7092
7715
|
pi.registerMessageRenderer(
|
|
7093
7716
|
WEB_RESEARCH_RESULT_MESSAGE_TYPE,
|
|
@@ -7134,29 +7757,21 @@ function webProvidersExtension(pi) {
|
|
|
7134
7757
|
stopWebResearchWidgetTimer();
|
|
7135
7758
|
latestWidgetContext?.ui.setWidget(WEB_RESEARCH_WIDGET_KEY, void 0);
|
|
7136
7759
|
});
|
|
7137
|
-
registerManagedTools(pi, {
|
|
7138
|
-
activeWebResearchRequests,
|
|
7139
|
-
updateWebResearchWidget
|
|
7140
|
-
});
|
|
7141
7760
|
}
|
|
7142
7761
|
function registerManagedTools(pi, webResearchLifecycle, providerIdsByCapability = {}) {
|
|
7143
|
-
registerWebSearchTool(pi, providerIdsByCapability.search ??
|
|
7144
|
-
registerWebContentsTool(
|
|
7145
|
-
|
|
7146
|
-
providerIdsByCapability.contents ?? getProviderIdsForCapability("contents")
|
|
7147
|
-
);
|
|
7148
|
-
registerWebAnswerTool(
|
|
7149
|
-
pi,
|
|
7150
|
-
providerIdsByCapability.answer ?? getProviderIdsForCapability("answer")
|
|
7151
|
-
);
|
|
7762
|
+
registerWebSearchTool(pi, providerIdsByCapability.search ?? []);
|
|
7763
|
+
registerWebContentsTool(pi, providerIdsByCapability.contents ?? []);
|
|
7764
|
+
registerWebAnswerTool(pi, providerIdsByCapability.answer ?? []);
|
|
7152
7765
|
registerWebResearchTool(
|
|
7153
7766
|
pi,
|
|
7154
7767
|
webResearchLifecycle,
|
|
7155
|
-
providerIdsByCapability.research ??
|
|
7768
|
+
providerIdsByCapability.research ?? []
|
|
7156
7769
|
);
|
|
7157
7770
|
}
|
|
7158
7771
|
function registerWebSearchTool(pi, providerIds) {
|
|
7159
|
-
|
|
7772
|
+
if (providerIds.length !== 1) return;
|
|
7773
|
+
const selectedProviderId = providerIds[0];
|
|
7774
|
+
const maxAllowedResults = getSearchMaxResultsLimit(selectedProviderId);
|
|
7160
7775
|
pi.registerTool({
|
|
7161
7776
|
name: "web_search",
|
|
7162
7777
|
label: "Web Search",
|
|
@@ -7164,24 +7779,27 @@ function registerWebSearchTool(pi, providerIds) {
|
|
|
7164
7779
|
promptGuidelines: [
|
|
7165
7780
|
"Batch related searches when grouped comparison matters; use separate sibling web_search calls when independent results should surface as soon as they are ready."
|
|
7166
7781
|
],
|
|
7167
|
-
parameters:
|
|
7168
|
-
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
|
|
7172
|
-
|
|
7173
|
-
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
|
|
7782
|
+
parameters: Type15.Object(
|
|
7783
|
+
{
|
|
7784
|
+
queries: Type15.Array(Type15.String({ minLength: 1 }), {
|
|
7785
|
+
minItems: 1,
|
|
7786
|
+
maxItems: MAX_SEARCH_QUERIES,
|
|
7787
|
+
description: `One or more search queries to run in one call (max ${MAX_SEARCH_QUERIES})`
|
|
7788
|
+
}),
|
|
7789
|
+
maxResults: Type15.Optional(
|
|
7790
|
+
Type15.Integer({
|
|
7791
|
+
minimum: 1,
|
|
7792
|
+
maximum: maxAllowedResults,
|
|
7793
|
+
description: `Maximum number of results to return (default: ${DEFAULT_MAX_RESULTS})`
|
|
7794
|
+
})
|
|
7795
|
+
),
|
|
7796
|
+
...optionalField(
|
|
7797
|
+
"options",
|
|
7798
|
+
buildStructuredOptionsSchema("search", selectedProviderId)
|
|
7799
|
+
)
|
|
7800
|
+
},
|
|
7801
|
+
{ additionalProperties: false }
|
|
7802
|
+
),
|
|
7185
7803
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
7186
7804
|
return executeSearchTool({
|
|
7187
7805
|
config: await loadConfig(),
|
|
@@ -7214,22 +7832,25 @@ function registerWebSearchTool(pi, providerIds) {
|
|
|
7214
7832
|
});
|
|
7215
7833
|
}
|
|
7216
7834
|
function registerWebContentsTool(pi, providerIds) {
|
|
7217
|
-
if (providerIds.length
|
|
7218
|
-
const selectedProviderId = providerIds
|
|
7835
|
+
if (providerIds.length !== 1) return;
|
|
7836
|
+
const selectedProviderId = providerIds[0];
|
|
7219
7837
|
pi.registerTool({
|
|
7220
7838
|
name: "web_contents",
|
|
7221
7839
|
label: "Web Contents",
|
|
7222
7840
|
description: "Read and extract the main contents of one or more web pages. Batch related pages together, or use separate sibling calls when each page can be acted on independently.",
|
|
7223
|
-
parameters:
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7841
|
+
parameters: Type15.Object(
|
|
7842
|
+
{
|
|
7843
|
+
urls: Type15.Array(Type15.String({ minLength: 1 }), {
|
|
7844
|
+
minItems: 1,
|
|
7845
|
+
description: "One or more URLs to extract"
|
|
7846
|
+
}),
|
|
7847
|
+
...optionalField(
|
|
7848
|
+
"options",
|
|
7849
|
+
buildStructuredOptionsSchema("contents", selectedProviderId)
|
|
7850
|
+
)
|
|
7851
|
+
},
|
|
7852
|
+
{ additionalProperties: false }
|
|
7853
|
+
),
|
|
7233
7854
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
7234
7855
|
return executeProviderTool({
|
|
7235
7856
|
config: await loadConfig(),
|
|
@@ -7265,24 +7886,29 @@ function registerWebContentsTool(pi, providerIds) {
|
|
|
7265
7886
|
});
|
|
7266
7887
|
}
|
|
7267
7888
|
function registerWebAnswerTool(pi, providerIds) {
|
|
7268
|
-
if (providerIds.length
|
|
7269
|
-
const selectedProviderId = providerIds
|
|
7889
|
+
if (providerIds.length !== 1) return;
|
|
7890
|
+
const selectedProviderId = providerIds[0];
|
|
7270
7891
|
pi.registerTool({
|
|
7271
7892
|
name: "web_answer",
|
|
7272
7893
|
label: "Web Answer",
|
|
7273
|
-
description: `Answer one or more questions using web-grounded evidence (up to ${MAX_SEARCH_QUERIES} per call).`,
|
|
7274
|
-
parameters:
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
|
|
7280
|
-
|
|
7281
|
-
|
|
7282
|
-
|
|
7283
|
-
|
|
7284
|
-
|
|
7894
|
+
description: `Answer one or more simple factual questions using web-grounded evidence (up to ${MAX_SEARCH_QUERIES} per call). Prefer web_search plus web_contents when source selection matters, and web_research for multi-step investigations.`,
|
|
7895
|
+
parameters: Type15.Object(
|
|
7896
|
+
{
|
|
7897
|
+
queries: Type15.Array(Type15.String({ minLength: 1 }), {
|
|
7898
|
+
minItems: 1,
|
|
7899
|
+
maxItems: MAX_SEARCH_QUERIES,
|
|
7900
|
+
description: `One or more simple factual questions to answer in one call (max ${MAX_SEARCH_QUERIES})`
|
|
7901
|
+
}),
|
|
7902
|
+
...optionalField(
|
|
7903
|
+
"options",
|
|
7904
|
+
buildStructuredOptionsSchema("answer", selectedProviderId)
|
|
7905
|
+
)
|
|
7906
|
+
},
|
|
7907
|
+
{ additionalProperties: false }
|
|
7908
|
+
),
|
|
7285
7909
|
promptGuidelines: [
|
|
7910
|
+
"Use web_answer as a quick grounded-answer shortcut for simple factual questions, not as a replacement for inspecting sources or doing deeper research.",
|
|
7911
|
+
"Prefer web_search plus web_contents when source selection matters or primary sources need direct inspection; prefer web_research for open-ended, controversial, or multi-step investigations.",
|
|
7286
7912
|
"Batch related questions when the answers belong together; use separate sibling web_answer calls when earlier independent answers can unblock the next step."
|
|
7287
7913
|
],
|
|
7288
7914
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
@@ -7320,19 +7946,22 @@ function registerWebAnswerTool(pi, providerIds) {
|
|
|
7320
7946
|
});
|
|
7321
7947
|
}
|
|
7322
7948
|
function registerWebResearchTool(pi, webResearchLifecycle, providerIds) {
|
|
7323
|
-
if (providerIds.length
|
|
7324
|
-
const selectedProviderId = providerIds
|
|
7949
|
+
if (providerIds.length !== 1) return;
|
|
7950
|
+
const selectedProviderId = providerIds[0];
|
|
7325
7951
|
pi.registerTool({
|
|
7326
7952
|
name: "web_research",
|
|
7327
7953
|
label: "Web Research",
|
|
7328
7954
|
description: "Start a long-running web research job. Returns immediately with a dispatch notice; the final report is saved to a file and posted later as a custom message.",
|
|
7329
|
-
parameters:
|
|
7330
|
-
|
|
7331
|
-
|
|
7332
|
-
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7955
|
+
parameters: Type15.Object(
|
|
7956
|
+
{
|
|
7957
|
+
input: Type15.String({ description: "Research brief or question" }),
|
|
7958
|
+
...optionalField(
|
|
7959
|
+
"options",
|
|
7960
|
+
buildStructuredOptionsSchema("research", selectedProviderId)
|
|
7961
|
+
)
|
|
7962
|
+
},
|
|
7963
|
+
{ additionalProperties: false }
|
|
7964
|
+
),
|
|
7336
7965
|
promptGuidelines: [
|
|
7337
7966
|
"Use this tool for deep investigations that can finish asynchronously.",
|
|
7338
7967
|
"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."
|
|
@@ -7459,10 +8088,9 @@ async function syncManagedToolAvailability(pi, nextActiveTools) {
|
|
|
7459
8088
|
pi.setActiveTools(Array.from(nextActiveTools));
|
|
7460
8089
|
}
|
|
7461
8090
|
}
|
|
7462
|
-
function
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
);
|
|
8091
|
+
function getSearchMaxResultsLimit(providerId) {
|
|
8092
|
+
const capabilities = PROVIDERS_BY_ID[providerId].capabilities;
|
|
8093
|
+
return capabilities.search?.limits?.maxResults ?? MAX_ALLOWED_RESULTS;
|
|
7466
8094
|
}
|
|
7467
8095
|
function optionalField(name, schema) {
|
|
7468
8096
|
return schema ? { [name]: schema } : {};
|
|
@@ -7470,14 +8098,14 @@ function optionalField(name, schema) {
|
|
|
7470
8098
|
function buildStructuredOptionsSchema(capability, providerId) {
|
|
7471
8099
|
const providerSchema = resolveProviderOptionsSchema(capability, providerId);
|
|
7472
8100
|
const schema = buildToolOptionsSchema(capability, providerSchema);
|
|
7473
|
-
return schema ?
|
|
8101
|
+
return schema ? Type15.Optional(schema) : void 0;
|
|
7474
8102
|
}
|
|
7475
8103
|
function resolveProviderOptionsSchema(capability, providerId) {
|
|
7476
8104
|
if (!providerId) {
|
|
7477
8105
|
return void 0;
|
|
7478
8106
|
}
|
|
7479
|
-
const
|
|
7480
|
-
return
|
|
8107
|
+
const provider = PROVIDERS_BY_ID[providerId];
|
|
8108
|
+
return provider.capabilities[capability]?.options;
|
|
7481
8109
|
}
|
|
7482
8110
|
async function executeSearchTool({
|
|
7483
8111
|
config,
|
|
@@ -7489,8 +8117,7 @@ async function executeSearchTool({
|
|
|
7489
8117
|
ctx: { cwd: context.cwd },
|
|
7490
8118
|
signal: context.signal,
|
|
7491
8119
|
progress: context.progress,
|
|
7492
|
-
providerOptions: request.options
|
|
7493
|
-
runtimeOptions: request.options?.runtime,
|
|
8120
|
+
providerOptions: request.options,
|
|
7494
8121
|
maxResults: request.maxResults,
|
|
7495
8122
|
queries: request.queries
|
|
7496
8123
|
});
|
|
@@ -7502,7 +8129,6 @@ async function executeSearchToolInternal({
|
|
|
7502
8129
|
signal,
|
|
7503
8130
|
progress,
|
|
7504
8131
|
providerOptions,
|
|
7505
|
-
runtimeOptions,
|
|
7506
8132
|
maxResults,
|
|
7507
8133
|
queries,
|
|
7508
8134
|
executionOverrides
|
|
@@ -7512,7 +8138,7 @@ async function executeSearchToolInternal({
|
|
|
7512
8138
|
const providerConfig = getEffectiveProviderConfig(config, provider.id);
|
|
7513
8139
|
const prefetchOptions = mergeSearchContentsPrefetchOptions(
|
|
7514
8140
|
getSearchPrefetchDefaults(config),
|
|
7515
|
-
|
|
8141
|
+
void 0
|
|
7516
8142
|
);
|
|
7517
8143
|
const searchQueries = resolveSearchQueries(queries);
|
|
7518
8144
|
if (executionOverrides !== void 0 && executionOverrides.length !== searchQueries.length) {
|
|
@@ -7535,7 +8161,10 @@ async function executeSearchToolInternal({
|
|
|
7535
8161
|
cwd: ctx.cwd,
|
|
7536
8162
|
signal: signal ?? void 0
|
|
7537
8163
|
};
|
|
7538
|
-
const clampedMaxResults = clampResults(
|
|
8164
|
+
const clampedMaxResults = clampResults(
|
|
8165
|
+
maxResults,
|
|
8166
|
+
getSearchMaxResultsLimit(provider.id)
|
|
8167
|
+
);
|
|
7539
8168
|
let outcomes;
|
|
7540
8169
|
try {
|
|
7541
8170
|
batchProgress?.start();
|
|
@@ -7547,7 +8176,6 @@ async function executeSearchToolInternal({
|
|
|
7547
8176
|
query: searchQuery,
|
|
7548
8177
|
maxResults: clampedMaxResults,
|
|
7549
8178
|
options: providerOptions,
|
|
7550
|
-
runtimeOptions,
|
|
7551
8179
|
providerContext,
|
|
7552
8180
|
onProgress: searchQueries.length > 1 ? void 0 : progressReporter.report,
|
|
7553
8181
|
executionOverride: executionOverrides?.[index]
|
|
@@ -7597,7 +8225,6 @@ async function executeRawProviderRequest({
|
|
|
7597
8225
|
ctx,
|
|
7598
8226
|
signal,
|
|
7599
8227
|
options,
|
|
7600
|
-
runtimeOptions,
|
|
7601
8228
|
maxResults,
|
|
7602
8229
|
urls,
|
|
7603
8230
|
query: query2,
|
|
@@ -7610,9 +8237,11 @@ async function executeRawProviderRequest({
|
|
|
7610
8237
|
provider: provider2,
|
|
7611
8238
|
providerConfig: providerConfig2,
|
|
7612
8239
|
query: query2 ?? "",
|
|
7613
|
-
maxResults: clampResults(
|
|
8240
|
+
maxResults: clampResults(
|
|
8241
|
+
maxResults,
|
|
8242
|
+
getSearchMaxResultsLimit(provider2.id)
|
|
8243
|
+
),
|
|
7614
8244
|
options,
|
|
7615
|
-
runtimeOptions,
|
|
7616
8245
|
providerContext: {
|
|
7617
8246
|
cwd: ctx.cwd,
|
|
7618
8247
|
signal: signal ?? void 0
|
|
@@ -7635,7 +8264,6 @@ async function executeRawProviderRequest({
|
|
|
7635
8264
|
ctx,
|
|
7636
8265
|
signal,
|
|
7637
8266
|
options,
|
|
7638
|
-
runtimeOptions,
|
|
7639
8267
|
urls
|
|
7640
8268
|
});
|
|
7641
8269
|
}
|
|
@@ -7648,7 +8276,6 @@ async function executeRawProviderRequest({
|
|
|
7648
8276
|
ctx,
|
|
7649
8277
|
signal,
|
|
7650
8278
|
options,
|
|
7651
|
-
runtimeOptions,
|
|
7652
8279
|
query: query2
|
|
7653
8280
|
});
|
|
7654
8281
|
}
|
|
@@ -7660,7 +8287,6 @@ async function executeRawProviderRequest({
|
|
|
7660
8287
|
ctx,
|
|
7661
8288
|
signal,
|
|
7662
8289
|
options,
|
|
7663
|
-
runtimeOptions,
|
|
7664
8290
|
input
|
|
7665
8291
|
});
|
|
7666
8292
|
}
|
|
@@ -7682,7 +8308,6 @@ async function executeSingleSearchQuery({
|
|
|
7682
8308
|
query: query2,
|
|
7683
8309
|
maxResults,
|
|
7684
8310
|
options,
|
|
7685
|
-
runtimeOptions,
|
|
7686
8311
|
providerContext,
|
|
7687
8312
|
onProgress,
|
|
7688
8313
|
executionOverride
|
|
@@ -7694,23 +8319,13 @@ async function executeSingleSearchQuery({
|
|
|
7694
8319
|
options
|
|
7695
8320
|
};
|
|
7696
8321
|
onProgress?.(`Searching via ${provider.label}: ${query2}`);
|
|
7697
|
-
const result = executionOverride ? await executeProviderExecution(
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7702
|
-
|
|
7703
|
-
|
|
7704
|
-
) : await executeProviderRequest(
|
|
7705
|
-
provider,
|
|
7706
|
-
providerConfig,
|
|
7707
|
-
request,
|
|
7708
|
-
stripSearchContentsPrefetchOptions(runtimeOptions),
|
|
7709
|
-
{
|
|
7710
|
-
...providerContext,
|
|
7711
|
-
onProgress
|
|
7712
|
-
}
|
|
7713
|
-
);
|
|
8322
|
+
const result = executionOverride ? await executeProviderExecution(executionOverride, {
|
|
8323
|
+
...providerContext,
|
|
8324
|
+
onProgress
|
|
8325
|
+
}) : await executeProviderRequest(provider, providerConfig, request, {
|
|
8326
|
+
...providerContext,
|
|
8327
|
+
onProgress
|
|
8328
|
+
});
|
|
7714
8329
|
if (!isSearchResponse(result)) {
|
|
7715
8330
|
throw new Error(`${provider.label} search returned an invalid result.`);
|
|
7716
8331
|
}
|
|
@@ -7726,8 +8341,7 @@ async function executeAnswerTool({
|
|
|
7726
8341
|
ctx: { cwd: context.cwd },
|
|
7727
8342
|
signal: context.signal,
|
|
7728
8343
|
progress: context.progress,
|
|
7729
|
-
providerOptions: request.options
|
|
7730
|
-
runtimeOptions: request.options?.runtime,
|
|
8344
|
+
providerOptions: request.options,
|
|
7731
8345
|
queries: request.queries
|
|
7732
8346
|
});
|
|
7733
8347
|
}
|
|
@@ -7738,7 +8352,6 @@ async function executeAnswerToolInternal({
|
|
|
7738
8352
|
signal,
|
|
7739
8353
|
progress,
|
|
7740
8354
|
providerOptions,
|
|
7741
|
-
runtimeOptions,
|
|
7742
8355
|
queries,
|
|
7743
8356
|
executionOverrides
|
|
7744
8357
|
}) {
|
|
@@ -7779,7 +8392,6 @@ async function executeAnswerToolInternal({
|
|
|
7779
8392
|
ctx,
|
|
7780
8393
|
signal,
|
|
7781
8394
|
options: providerOptions,
|
|
7782
|
-
runtimeOptions,
|
|
7783
8395
|
query: answerQuery,
|
|
7784
8396
|
onProgress: answerQueries.length > 1 ? void 0 : progressReporter.report,
|
|
7785
8397
|
executionOverride: executionOverrides?.[index]
|
|
@@ -7861,7 +8473,6 @@ async function executeProviderOperation({
|
|
|
7861
8473
|
ctx,
|
|
7862
8474
|
signal,
|
|
7863
8475
|
options,
|
|
7864
|
-
runtimeOptions,
|
|
7865
8476
|
urls,
|
|
7866
8477
|
query: query2,
|
|
7867
8478
|
input,
|
|
@@ -7881,7 +8492,6 @@ async function executeProviderOperation({
|
|
|
7881
8492
|
config,
|
|
7882
8493
|
cwd: ctx.cwd,
|
|
7883
8494
|
options,
|
|
7884
|
-
runtimeOptions,
|
|
7885
8495
|
signal: signal ?? void 0,
|
|
7886
8496
|
onProgress
|
|
7887
8497
|
});
|
|
@@ -7895,21 +8505,15 @@ async function executeProviderOperation({
|
|
|
7895
8505
|
} else if (capability === "research") {
|
|
7896
8506
|
onProgress?.(`Researching via ${provider.label}`);
|
|
7897
8507
|
}
|
|
7898
|
-
const result = executionOverride ? await executeProviderExecution(executionOverride,
|
|
8508
|
+
const result = executionOverride ? await executeProviderExecution(executionOverride, {
|
|
7899
8509
|
cwd: ctx.cwd,
|
|
7900
8510
|
signal: signal ?? void 0,
|
|
7901
8511
|
onProgress
|
|
7902
|
-
}) : await executeProviderRequest(
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
{
|
|
7908
|
-
cwd: ctx.cwd,
|
|
7909
|
-
signal: signal ?? void 0,
|
|
7910
|
-
onProgress
|
|
7911
|
-
}
|
|
7912
|
-
);
|
|
8512
|
+
}) : await executeProviderRequest(provider, providerConfig, request, {
|
|
8513
|
+
cwd: ctx.cwd,
|
|
8514
|
+
signal: signal ?? void 0,
|
|
8515
|
+
onProgress
|
|
8516
|
+
});
|
|
7913
8517
|
if (isSearchResponse(result)) {
|
|
7914
8518
|
throw new Error(
|
|
7915
8519
|
`${provider.label} ${capability} returned an invalid result.`
|
|
@@ -7928,8 +8532,7 @@ async function executeProviderTool({
|
|
|
7928
8532
|
ctx: { cwd: context.cwd },
|
|
7929
8533
|
signal: context.signal,
|
|
7930
8534
|
progress: context.progress,
|
|
7931
|
-
providerOptions: request.options
|
|
7932
|
-
runtimeOptions: request.options?.runtime,
|
|
8535
|
+
providerOptions: request.options,
|
|
7933
8536
|
urls: request.urls,
|
|
7934
8537
|
query: request.query,
|
|
7935
8538
|
input: request.input
|
|
@@ -7943,7 +8546,6 @@ async function executeProviderToolInternal({
|
|
|
7943
8546
|
signal,
|
|
7944
8547
|
progress,
|
|
7945
8548
|
providerOptions,
|
|
7946
|
-
runtimeOptions,
|
|
7947
8549
|
urls,
|
|
7948
8550
|
query: query2,
|
|
7949
8551
|
input,
|
|
@@ -7973,7 +8575,6 @@ async function executeProviderToolInternal({
|
|
|
7973
8575
|
ctx,
|
|
7974
8576
|
signal,
|
|
7975
8577
|
options: providerOptions,
|
|
7976
|
-
runtimeOptions,
|
|
7977
8578
|
urls: urls ?? [],
|
|
7978
8579
|
progressReport: progressReporter.report,
|
|
7979
8580
|
executionOverrides
|
|
@@ -7985,7 +8586,6 @@ async function executeProviderToolInternal({
|
|
|
7985
8586
|
ctx,
|
|
7986
8587
|
signal,
|
|
7987
8588
|
options: providerOptions,
|
|
7988
|
-
runtimeOptions,
|
|
7989
8589
|
urls,
|
|
7990
8590
|
onProgress: progressReporter.report,
|
|
7991
8591
|
executionOverride
|
|
@@ -7999,7 +8599,6 @@ async function executeProviderToolInternal({
|
|
|
7999
8599
|
ctx,
|
|
8000
8600
|
signal,
|
|
8001
8601
|
options: providerOptions,
|
|
8002
|
-
runtimeOptions,
|
|
8003
8602
|
query: query2,
|
|
8004
8603
|
input,
|
|
8005
8604
|
onProgress: progressReporter.report,
|
|
@@ -8037,7 +8636,7 @@ async function dispatchWebResearch({
|
|
|
8037
8636
|
updateWebResearchWidget,
|
|
8038
8637
|
config,
|
|
8039
8638
|
ctx: context,
|
|
8040
|
-
providerOptions: request.options
|
|
8639
|
+
providerOptions: request.options,
|
|
8041
8640
|
input: request.input
|
|
8042
8641
|
});
|
|
8043
8642
|
}
|
|
@@ -8181,7 +8780,7 @@ function slugifyWebResearchInput(input) {
|
|
|
8181
8780
|
function buildWebResearchWidgetLines(requests, theme, now = Date.now()) {
|
|
8182
8781
|
const lines = [theme.fg("accent", "Research jobs:")];
|
|
8183
8782
|
for (const request of requests.slice().sort((left, right) => left.startedAt.localeCompare(right.startedAt)).slice(0, 3)) {
|
|
8184
|
-
const providerLabel =
|
|
8783
|
+
const providerLabel = PROVIDERS_BY_ID[request.provider]?.label ?? request.provider;
|
|
8185
8784
|
const elapsed = formatCompactElapsed(now - Date.parse(request.startedAt));
|
|
8186
8785
|
const icon = getWebResearchWidgetIcon(request, now);
|
|
8187
8786
|
lines.push(
|
|
@@ -8257,7 +8856,7 @@ async function writeWebResearchArtifact(result, reportText) {
|
|
|
8257
8856
|
);
|
|
8258
8857
|
}
|
|
8259
8858
|
function formatWebResearchArtifact(result, reportText) {
|
|
8260
|
-
const providerLabel =
|
|
8859
|
+
const providerLabel = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
8261
8860
|
const lines = [
|
|
8262
8861
|
"# Web research report",
|
|
8263
8862
|
"",
|
|
@@ -8305,7 +8904,6 @@ async function executeBatchedContentsTool({
|
|
|
8305
8904
|
ctx,
|
|
8306
8905
|
signal,
|
|
8307
8906
|
options,
|
|
8308
|
-
runtimeOptions,
|
|
8309
8907
|
urls,
|
|
8310
8908
|
progressReport,
|
|
8311
8909
|
executionOverrides
|
|
@@ -8332,7 +8930,6 @@ async function executeBatchedContentsTool({
|
|
|
8332
8930
|
ctx,
|
|
8333
8931
|
signal,
|
|
8334
8932
|
options,
|
|
8335
|
-
runtimeOptions,
|
|
8336
8933
|
urls: [url],
|
|
8337
8934
|
onProgress: void 0,
|
|
8338
8935
|
executionOverride: executionOverrides?.[index]
|
|
@@ -8455,7 +9052,7 @@ function createToolProgressReporter(capability, providerId, progress) {
|
|
|
8455
9052
|
if (Date.now() - lastUpdateAt < RESEARCH_HEARTBEAT_MS) {
|
|
8456
9053
|
return;
|
|
8457
9054
|
}
|
|
8458
|
-
const providerLabel =
|
|
9055
|
+
const providerLabel = PROVIDERS_BY_ID[providerId]?.label ?? providerId;
|
|
8459
9056
|
const elapsed = formatElapsed(Date.now() - startedAt);
|
|
8460
9057
|
emit(`Researching via ${providerLabel} (${elapsed} elapsed)`);
|
|
8461
9058
|
lastUpdateAt = Date.now();
|
|
@@ -8558,7 +9155,7 @@ function renderWebResearchDispatchResult(result, expanded, theme) {
|
|
|
8558
9155
|
if (expanded) {
|
|
8559
9156
|
return renderBlockText(details?.input ?? text, theme, "toolOutput");
|
|
8560
9157
|
}
|
|
8561
|
-
const summary = details ? `Started web research via ${
|
|
9158
|
+
const summary = details ? `Started web research via ${PROVIDERS_BY_ID[details.provider]?.label ?? details.provider}` : text;
|
|
8562
9159
|
let summaryText = theme.fg("success", summary);
|
|
8563
9160
|
summaryText += theme.fg("muted", ` (${getExpandHint()})`);
|
|
8564
9161
|
return new Text(summaryText, 0, 0);
|
|
@@ -8581,7 +9178,7 @@ function renderWebResearchResultMessage(message, { expanded }, theme) {
|
|
|
8581
9178
|
return box;
|
|
8582
9179
|
}
|
|
8583
9180
|
function buildWebResearchResultSummaryLines(result, theme) {
|
|
8584
|
-
const providerLabel =
|
|
9181
|
+
const providerLabel = PROVIDERS_BY_ID[result.provider]?.label ?? result.provider;
|
|
8585
9182
|
const statusLine = result.status === "completed" ? `Web research completed via ${providerLabel}` : `Web research failed via ${providerLabel}`;
|
|
8586
9183
|
const lines = [
|
|
8587
9184
|
theme.fg(result.status === "completed" ? "success" : "error", statusLine)
|
|
@@ -8621,7 +9218,7 @@ function renderProviderToolResult(result, expanded, isPartial, failureText, them
|
|
|
8621
9218
|
}
|
|
8622
9219
|
function renderCollapsedProviderToolSummary(details, text) {
|
|
8623
9220
|
if (details?.tool === "web_answer" && typeof details.queryCount === "number" && details.queryCount > 1) {
|
|
8624
|
-
const providerLabel =
|
|
9221
|
+
const providerLabel = PROVIDERS_BY_ID[details.provider]?.label ?? details.provider;
|
|
8625
9222
|
const failureSuffix = details.failedQueryCount && details.failedQueryCount > 0 ? `, ${details.failedQueryCount} failed` : "";
|
|
8626
9223
|
return `${details.queryCount} questions via ${providerLabel}${failureSuffix}`;
|
|
8627
9224
|
}
|
|
@@ -8695,12 +9292,12 @@ function renderSelectedEntryDescription(width, theme, entry) {
|
|
|
8695
9292
|
}
|
|
8696
9293
|
function formatProviderCapabilityChecks(providerId, theme) {
|
|
8697
9294
|
return ["search", "contents", "answer", "research"].map(
|
|
8698
|
-
(tool) => supportsTool2(
|
|
9295
|
+
(tool) => supportsTool2(PROVIDERS_BY_ID[providerId], tool) ? theme.fg("success", "\u2714") : " "
|
|
8699
9296
|
).join(" ");
|
|
8700
9297
|
}
|
|
8701
9298
|
function resolveProviderSelectionValue(providerIds, value) {
|
|
8702
9299
|
return providerIds.find(
|
|
8703
|
-
(candidate) =>
|
|
9300
|
+
(candidate) => PROVIDERS_BY_ID[candidate].label === value
|
|
8704
9301
|
);
|
|
8705
9302
|
}
|
|
8706
9303
|
function getReadyCompatibleProvidersForTool(config, cwd, toolId) {
|
|
@@ -8714,7 +9311,7 @@ function getReadyCompatibleProvidersForTool(config, cwd, toolId) {
|
|
|
8714
9311
|
}
|
|
8715
9312
|
function sortProviderIdsForSettings(providerIds) {
|
|
8716
9313
|
const displayOrder = new Map(
|
|
8717
|
-
|
|
9314
|
+
PROVIDER_LIST.map((provider, index) => [provider.id, index])
|
|
8718
9315
|
);
|
|
8719
9316
|
return [...providerIds].sort(
|
|
8720
9317
|
(left, right) => (displayOrder.get(left) ?? Number.MAX_SAFE_INTEGER) - (displayOrder.get(right) ?? Number.MAX_SAFE_INTEGER)
|
|
@@ -8819,9 +9416,13 @@ var WebProvidersSettingsView = class {
|
|
|
8819
9416
|
this.activeProvider = initialProvider;
|
|
8820
9417
|
this.selection.provider = Math.max(
|
|
8821
9418
|
0,
|
|
8822
|
-
|
|
9419
|
+
PROVIDER_LIST.findIndex((provider) => provider.id === initialProvider)
|
|
8823
9420
|
);
|
|
8824
9421
|
}
|
|
9422
|
+
tui;
|
|
9423
|
+
theme;
|
|
9424
|
+
done;
|
|
9425
|
+
ctx;
|
|
8825
9426
|
config;
|
|
8826
9427
|
activeProvider;
|
|
8827
9428
|
activeSection = "tools";
|
|
@@ -8899,7 +9500,7 @@ var WebProvidersSettingsView = class {
|
|
|
8899
9500
|
this.tui.requestRender();
|
|
8900
9501
|
}
|
|
8901
9502
|
buildProviderSectionItems() {
|
|
8902
|
-
return
|
|
9503
|
+
return PROVIDER_LIST.map((provider) => {
|
|
8903
9504
|
const setupState = getProviderSetupState(this.config, provider.id);
|
|
8904
9505
|
const statusSummary = getProviderReadinessSummary(
|
|
8905
9506
|
this.config,
|
|
@@ -8924,9 +9525,9 @@ var WebProvidersSettingsView = class {
|
|
|
8924
9525
|
toolId
|
|
8925
9526
|
);
|
|
8926
9527
|
const mappedProviderId = getMappedProviderIdForTool(this.config, toolId);
|
|
8927
|
-
const currentValue = mappedProviderId && readyCompatibleProviders.includes(mappedProviderId) ?
|
|
9528
|
+
const currentValue = mappedProviderId && readyCompatibleProviders.includes(mappedProviderId) ? PROVIDERS_BY_ID[mappedProviderId].label : "off";
|
|
8928
9529
|
const compatibleLabels = readyCompatibleProviders.map(
|
|
8929
|
-
(providerId) =>
|
|
9530
|
+
(providerId) => PROVIDERS_BY_ID[providerId].label
|
|
8930
9531
|
);
|
|
8931
9532
|
return {
|
|
8932
9533
|
id: `tool:${toolId}`,
|
|
@@ -9007,7 +9608,7 @@ var WebProvidersSettingsView = class {
|
|
|
9007
9608
|
if (this.activeSection !== "provider") {
|
|
9008
9609
|
return;
|
|
9009
9610
|
}
|
|
9010
|
-
const provider =
|
|
9611
|
+
const provider = PROVIDER_LIST[this.selection.provider];
|
|
9011
9612
|
if (!provider) {
|
|
9012
9613
|
return;
|
|
9013
9614
|
}
|
|
@@ -9169,6 +9770,13 @@ var ToolSettingsSubmenu = class {
|
|
|
9169
9770
|
this.persist = persist;
|
|
9170
9771
|
this.done = done;
|
|
9171
9772
|
}
|
|
9773
|
+
tui;
|
|
9774
|
+
theme;
|
|
9775
|
+
toolId;
|
|
9776
|
+
cwd;
|
|
9777
|
+
getConfig;
|
|
9778
|
+
persist;
|
|
9779
|
+
done;
|
|
9172
9780
|
selection = 0;
|
|
9173
9781
|
submenu;
|
|
9174
9782
|
render(width) {
|
|
@@ -9237,9 +9845,11 @@ var ToolSettingsSubmenu = class {
|
|
|
9237
9845
|
);
|
|
9238
9846
|
const providerValues = [
|
|
9239
9847
|
"off",
|
|
9240
|
-
...readyProviderIds.map(
|
|
9848
|
+
...readyProviderIds.map(
|
|
9849
|
+
(providerId) => PROVIDERS_BY_ID[providerId].label
|
|
9850
|
+
)
|
|
9241
9851
|
];
|
|
9242
|
-
const currentProviderValue = mappedProviderId && readyProviderIds.includes(mappedProviderId) ?
|
|
9852
|
+
const currentProviderValue = mappedProviderId && readyProviderIds.includes(mappedProviderId) ? PROVIDERS_BY_ID[mappedProviderId].label : "off";
|
|
9243
9853
|
const entries = [
|
|
9244
9854
|
{
|
|
9245
9855
|
id: "provider",
|
|
@@ -9261,10 +9871,10 @@ var ToolSettingsSubmenu = class {
|
|
|
9261
9871
|
const prefetchValues = [
|
|
9262
9872
|
"off",
|
|
9263
9873
|
...prefetchProviderIds.map(
|
|
9264
|
-
(providerId) =>
|
|
9874
|
+
(providerId) => PROVIDERS_BY_ID[providerId].label
|
|
9265
9875
|
)
|
|
9266
9876
|
];
|
|
9267
|
-
const currentPrefetchProviderValue = prefetch?.provider && prefetchProviderIds.includes(prefetch.provider) ?
|
|
9877
|
+
const currentPrefetchProviderValue = prefetch?.provider && prefetchProviderIds.includes(prefetch.provider) ? PROVIDERS_BY_ID[prefetch.provider].label : "off";
|
|
9268
9878
|
entries.push(
|
|
9269
9879
|
{
|
|
9270
9880
|
id: "prefetchProvider",
|
|
@@ -9394,13 +10004,19 @@ var ProviderSettingsSubmenu = class {
|
|
|
9394
10004
|
this.persist = persist;
|
|
9395
10005
|
this.done = done;
|
|
9396
10006
|
}
|
|
10007
|
+
tui;
|
|
10008
|
+
theme;
|
|
10009
|
+
providerId;
|
|
10010
|
+
getProviderConfig;
|
|
10011
|
+
persist;
|
|
10012
|
+
done;
|
|
9397
10013
|
selection = 0;
|
|
9398
10014
|
submenu;
|
|
9399
10015
|
render(width) {
|
|
9400
10016
|
if (this.submenu) {
|
|
9401
10017
|
return this.submenu.render(width);
|
|
9402
10018
|
}
|
|
9403
|
-
const provider =
|
|
10019
|
+
const provider = PROVIDERS_BY_ID[this.providerId];
|
|
9404
10020
|
const providerConfig = this.getProviderConfig();
|
|
9405
10021
|
const entries = this.getEntries();
|
|
9406
10022
|
const lines = [
|
|
@@ -9568,6 +10184,10 @@ var TextValueSubmenu = class {
|
|
|
9568
10184
|
this.done(text.trim());
|
|
9569
10185
|
};
|
|
9570
10186
|
}
|
|
10187
|
+
theme;
|
|
10188
|
+
title;
|
|
10189
|
+
help;
|
|
10190
|
+
done;
|
|
9571
10191
|
editor;
|
|
9572
10192
|
render(width) {
|
|
9573
10193
|
return [
|
|
@@ -9613,7 +10233,7 @@ function didContentsCacheInputsChange(previous, next) {
|
|
|
9613
10233
|
}
|
|
9614
10234
|
function getContentsCacheInputs(config) {
|
|
9615
10235
|
const providers = {};
|
|
9616
|
-
for (const provider of
|
|
10236
|
+
for (const provider of PROVIDER_LIST) {
|
|
9617
10237
|
if (!supportsTool2(provider, "contents")) {
|
|
9618
10238
|
continue;
|
|
9619
10239
|
}
|
|
@@ -9655,8 +10275,8 @@ function getProviderReadinessSummary(config, cwd, providerId) {
|
|
|
9655
10275
|
return formatProviderCapabilityStatus(statuses[0], providerId, tools[0]);
|
|
9656
10276
|
}
|
|
9657
10277
|
function getProviderReadinessSummaryForProviderConfig(providerId, providerConfig) {
|
|
9658
|
-
const status =
|
|
9659
|
-
providerConfig ??
|
|
10278
|
+
const status = PROVIDERS_BY_ID[providerId].getCapabilityStatus(
|
|
10279
|
+
providerConfig ?? PROVIDERS_BY_ID[providerId].config.createTemplate(),
|
|
9660
10280
|
""
|
|
9661
10281
|
);
|
|
9662
10282
|
return formatProviderCapabilityStatus(status, providerId);
|
|
@@ -9670,9 +10290,9 @@ function summarizeStringValue(value, secret) {
|
|
|
9670
10290
|
}
|
|
9671
10291
|
return truncateInline(value, 40);
|
|
9672
10292
|
}
|
|
9673
|
-
function clampResults(value) {
|
|
9674
|
-
if (value === void 0) return DEFAULT_MAX_RESULTS;
|
|
9675
|
-
return Math.min(Math.max(Math.trunc(value), 1),
|
|
10293
|
+
function clampResults(value, maximum = MAX_ALLOWED_RESULTS) {
|
|
10294
|
+
if (value === void 0) return Math.min(DEFAULT_MAX_RESULTS, maximum);
|
|
10295
|
+
return Math.min(Math.max(Math.trunc(value), 1), maximum);
|
|
9676
10296
|
}
|
|
9677
10297
|
function resolveSearchQueries(queries) {
|
|
9678
10298
|
if (queries.length === 0) {
|
|
@@ -9789,7 +10409,7 @@ function renderCollapsedSearchSummary(details, text, theme) {
|
|
|
9789
10409
|
const queryCount = typeof details?.queryCount === "number" ? details.queryCount : inferSearchQueryCount(text);
|
|
9790
10410
|
const resultCount = typeof details?.resultCount === "number" ? details.resultCount : inferSearchResultCount(text);
|
|
9791
10411
|
const failedQueryCount = typeof details?.failedQueryCount === "number" ? details.failedQueryCount : inferSearchFailureCount(text);
|
|
9792
|
-
const providerLabel = typeof details?.provider === "string" ?
|
|
10412
|
+
const providerLabel = typeof details?.provider === "string" ? PROVIDERS_BY_ID[details.provider]?.label ?? details.provider : void 0;
|
|
9793
10413
|
let base = buildSearchSummaryText({
|
|
9794
10414
|
queryCount,
|
|
9795
10415
|
resultCount
|
|
@@ -9839,7 +10459,7 @@ function inferSearchFailureCount(text) {
|
|
|
9839
10459
|
return failureMatches?.length;
|
|
9840
10460
|
}
|
|
9841
10461
|
function appendProviderSummary(summary, provider) {
|
|
9842
|
-
const providerLabel =
|
|
10462
|
+
const providerLabel = PROVIDERS_BY_ID[provider]?.label ?? provider;
|
|
9843
10463
|
const providerSuffix = `via ${providerLabel}`;
|
|
9844
10464
|
return summary.toLowerCase().includes(providerSuffix.toLowerCase()) ? summary : `${summary} ${providerSuffix}`;
|
|
9845
10465
|
}
|
|
@@ -9969,7 +10589,6 @@ var __test__ = {
|
|
|
9969
10589
|
signal,
|
|
9970
10590
|
onUpdate,
|
|
9971
10591
|
options,
|
|
9972
|
-
runtimeOptions,
|
|
9973
10592
|
queries,
|
|
9974
10593
|
executionOverrides
|
|
9975
10594
|
}) => executeAnswerToolInternal({
|
|
@@ -9979,7 +10598,6 @@ var __test__ = {
|
|
|
9979
10598
|
signal,
|
|
9980
10599
|
progress: createProgressEmitter(onUpdate),
|
|
9981
10600
|
providerOptions: options,
|
|
9982
|
-
runtimeOptions,
|
|
9983
10601
|
queries,
|
|
9984
10602
|
executionOverrides
|
|
9985
10603
|
}),
|
|
@@ -9992,7 +10610,6 @@ var __test__ = {
|
|
|
9992
10610
|
signal,
|
|
9993
10611
|
onUpdate,
|
|
9994
10612
|
options,
|
|
9995
|
-
runtimeOptions,
|
|
9996
10613
|
urls,
|
|
9997
10614
|
query: query2,
|
|
9998
10615
|
input,
|
|
@@ -10006,7 +10623,6 @@ var __test__ = {
|
|
|
10006
10623
|
signal,
|
|
10007
10624
|
progress: createProgressEmitter(onUpdate),
|
|
10008
10625
|
providerOptions: options,
|
|
10009
|
-
runtimeOptions,
|
|
10010
10626
|
urls,
|
|
10011
10627
|
query: query2,
|
|
10012
10628
|
input,
|
|
@@ -10020,7 +10636,6 @@ var __test__ = {
|
|
|
10020
10636
|
signal,
|
|
10021
10637
|
onUpdate,
|
|
10022
10638
|
options,
|
|
10023
|
-
runtimeOptions,
|
|
10024
10639
|
maxResults,
|
|
10025
10640
|
queries,
|
|
10026
10641
|
executionOverrides
|
|
@@ -10031,7 +10646,6 @@ var __test__ = {
|
|
|
10031
10646
|
signal,
|
|
10032
10647
|
progress: createProgressEmitter(onUpdate),
|
|
10033
10648
|
providerOptions: options,
|
|
10034
|
-
runtimeOptions,
|
|
10035
10649
|
maxResults,
|
|
10036
10650
|
queries,
|
|
10037
10651
|
executionOverrides
|