@oh-my-pi/pi-coding-agent 17.2.4 → 17.2.5
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/CHANGELOG.md +51 -0
- package/dist/{CHANGELOG-bpmhv26t.md → CHANGELOG-q2w43aw3.md} +51 -0
- package/dist/cli.js +4148 -3717
- package/dist/{template-8vdv6xb4.js → template-c2hyaytt.js} +8 -26
- package/dist/types/cli/args.d.ts +4 -1
- package/dist/types/cli/browser-relay-cli.d.ts +15 -0
- package/dist/types/cli/command-help.d.ts +99 -0
- package/dist/types/cli/help-extra.d.ts +2 -0
- package/dist/types/cli/thinking-levels.d.ts +5 -0
- package/dist/types/cli/update-cli.d.ts +13 -0
- package/dist/types/commands/browser-relay.d.ts +38 -0
- package/dist/types/commands/commit.d.ts +3 -0
- package/dist/types/commands/complete.d.ts +1 -1
- package/dist/types/commands/completions.d.ts +6 -0
- package/dist/types/commands/grep.d.ts +3 -0
- package/dist/types/commands/join.d.ts +4 -0
- package/dist/types/commands/launch-help.d.ts +171 -0
- package/dist/types/commands/launch.d.ts +5 -1
- package/dist/types/commands/models.d.ts +3 -0
- package/dist/types/commands/say.d.ts +10 -0
- package/dist/types/commands/worktree.d.ts +3 -0
- package/dist/types/config/keybindings.d.ts +5 -0
- package/dist/types/config/model-discovery.d.ts +2 -2
- package/dist/types/config/models-config-schema-bundle.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +2 -0
- package/dist/types/config/models-config.d.ts +1 -0
- package/dist/types/config/service-tier.d.ts +2 -0
- package/dist/types/config/settings-schema.d.ts +69 -21
- package/dist/types/cursor-bridge-tools.d.ts +1 -1
- package/dist/types/cursor.d.ts +7 -4
- package/dist/types/edit/index.d.ts +3 -3
- package/dist/types/edit/modes/replace.d.ts +22 -15
- package/dist/types/edit/renderer.d.ts +1 -1
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/spawn-options.d.ts +12 -16
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +8 -1
- package/dist/types/extensibility/legacy-pi-coding-agent-shim.d.ts +1 -1
- package/dist/types/extensibility/plugins/legacy-pi-compat.d.ts +2 -0
- package/dist/types/launch/broker.d.ts +1 -1
- package/dist/types/launch/client.d.ts +6 -3
- package/dist/types/launch/ensure.d.ts +8 -0
- package/dist/types/launch/paths.d.ts +1 -1
- package/dist/types/launch/protocol.d.ts +2 -2
- package/dist/types/lsp/client.d.ts +2 -0
- package/dist/types/lsp/mux/daemon.d.ts +15 -0
- package/dist/types/lsp/mux/protocol.d.ts +57 -0
- package/dist/types/lsp/mux/server.d.ts +18 -0
- package/dist/types/lsp/types.d.ts +22 -2
- package/dist/types/main.d.ts +2 -0
- package/dist/types/mnemopi/embed-client.d.ts +1 -1
- package/dist/types/mnemopi/state.d.ts +0 -23
- package/dist/types/modes/components/assistant-message.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +2 -1
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/stripped-tool-calls-placeholder.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- package/dist/types/modes/controllers/input-controller.d.ts +1 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/workflow.d.ts +2 -1
- package/dist/types/sdk.d.ts +3 -1
- package/dist/types/secrets/index.d.ts +1 -1
- package/dist/types/session/agent-session-types.d.ts +3 -1
- package/dist/types/session/indexed-session-storage.d.ts +1 -0
- package/dist/types/session/session-advisors.d.ts +1 -1
- package/dist/types/session/session-context.d.ts +2 -1
- package/dist/types/session/session-maintenance.d.ts +1 -0
- package/dist/types/session/session-manager.d.ts +16 -11
- package/dist/types/session/session-stats.d.ts +10 -0
- package/dist/types/session/session-storage.d.ts +15 -3
- package/dist/types/system-prompt.d.ts +4 -1
- package/dist/types/task/index.d.ts +2 -1
- package/dist/types/task/spawn-policy.d.ts +5 -0
- package/dist/types/thinking.d.ts +1 -7
- package/dist/types/tools/ast-grep.d.ts +1 -1
- package/dist/types/tools/browser/registry.d.ts +2 -1
- package/dist/types/tools/browser/relay/bridge.d.ts +35 -0
- package/dist/types/tools/browser/relay/daemon.d.ts +16 -0
- package/dist/types/tools/browser/relay/kind.d.ts +19 -0
- package/dist/types/tools/browser/relay/protocol.d.ts +97 -0
- package/dist/types/tools/browser/relay/server.d.ts +35 -0
- package/dist/types/tools/browser/render.d.ts +1 -0
- package/dist/types/tools/browser.d.ts +3 -0
- package/dist/types/tools/computer/exposure.d.ts +9 -7
- package/dist/types/tools/computer/protocol.d.ts +70 -17
- package/dist/types/tools/computer/supervisor.d.ts +23 -10
- package/dist/types/tools/computer/worker-entry.d.ts +1 -0
- package/dist/types/tools/computer/worker.d.ts +42 -7
- package/dist/types/tools/computer-renderer.d.ts +7 -5
- package/dist/types/tools/computer.d.ts +25 -44
- package/dist/types/tools/context.d.ts +3 -0
- package/dist/types/tools/glob.d.ts +1 -1
- package/dist/types/tools/grep.d.ts +1 -1
- package/dist/types/tools/render-utils.d.ts +2 -0
- package/dist/types/tools/{browser/run-cancellation.d.ts → run-scope.d.ts} +8 -8
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/tools/xdev.d.ts +8 -0
- package/dist/types/utils/clipboard.d.ts +2 -2
- package/dist/types/web/kagi.d.ts +1 -0
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/providers/anthropic.d.ts +1 -0
- package/dist/types/web/search/providers/base.d.ts +2 -0
- package/dist/types/web/search/providers/brave.d.ts +1 -0
- package/dist/types/web/search/providers/browser-page.d.ts +1 -0
- package/dist/types/web/search/providers/codex.d.ts +1 -0
- package/dist/types/web/search/providers/exa.d.ts +1 -0
- package/dist/types/web/search/providers/firecrawl.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/dist/types/web/search/providers/jina.d.ts +1 -0
- package/dist/types/web/search/providers/kagi.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -0
- package/dist/types/web/search/providers/parallel.d.ts +1 -0
- package/dist/types/web/search/providers/perplexity.d.ts +1 -0
- package/dist/types/web/search/providers/searxng.d.ts +1 -0
- package/dist/types/web/search/providers/tavily.d.ts +1 -0
- package/dist/types/web/search/providers/tinyfish.d.ts +1 -0
- package/dist/types/web/search/providers/utils.d.ts +6 -9
- package/dist/types/web/search/providers/zai.d.ts +1 -0
- package/dist/types/web/search/types.d.ts +4 -0
- package/package.json +12 -12
- package/src/cli/args.ts +7 -87
- package/src/cli/browser-relay-cli.ts +119 -0
- package/src/cli/command-help.ts +103 -0
- package/src/cli/completion-gen.ts +5 -5
- package/src/cli/flag-tables.ts +9 -0
- package/src/cli/help-extra.ts +89 -0
- package/src/cli/setup-cli.ts +13 -21
- package/src/cli/thinking-levels.ts +7 -0
- package/src/cli/update-cli.ts +26 -7
- package/src/cli-commands.ts +170 -33
- package/src/cli.ts +17 -5
- package/src/commands/acp.ts +3 -1
- package/src/commands/agents.ts +3 -2
- package/src/commands/auth-broker.ts +3 -2
- package/src/commands/auth-gateway.ts +3 -2
- package/src/commands/bench.ts +2 -3
- package/src/commands/browser-relay.ts +53 -0
- package/src/commands/cleanse.ts +2 -2
- package/src/commands/commit.ts +3 -2
- package/src/commands/complete.ts +2 -1
- package/src/commands/completions.ts +3 -2
- package/src/commands/config.ts +3 -2
- package/src/commands/dry-balance.ts +2 -2
- package/src/commands/gallery.ts +3 -2
- package/src/commands/gc.ts +3 -2
- package/src/commands/grep.ts +3 -2
- package/src/commands/grievances.ts +3 -2
- package/src/commands/install.ts +2 -2
- package/src/commands/join.ts +3 -2
- package/src/commands/launch-help.ts +116 -0
- package/src/commands/launch.ts +7 -191
- package/src/commands/models.ts +3 -2
- package/src/commands/plugin.ts +3 -2
- package/src/commands/read.ts +3 -2
- package/src/commands/say.ts +3 -2
- package/src/commands/setup.ts +3 -2
- package/src/commands/shell.ts +3 -2
- package/src/commands/ssh.ts +3 -2
- package/src/commands/stats.ts +3 -2
- package/src/commands/tiny-models.ts +2 -2
- package/src/commands/token.ts +2 -2
- package/src/commands/ttsr.ts +2 -2
- package/src/commands/update.ts +3 -2
- package/src/commands/usage.ts +3 -2
- package/src/commands/web-search.ts +3 -2
- package/src/commands/worktree.ts +3 -2
- package/src/config/keybindings.ts +5 -0
- package/src/config/model-discovery.ts +30 -13
- package/src/config/model-registry.ts +9 -5
- package/src/config/models-config-schema-bundle.ts +9 -0
- package/src/config/resolve-config-value.ts +2 -1
- package/src/config/service-tier.ts +5 -0
- package/src/config/settings-schema.ts +72 -20
- package/src/cursor-bridge-tools.ts +1 -1
- package/src/cursor.ts +10 -14
- package/src/edit/diff.ts +17 -8
- package/src/edit/index.ts +20 -7
- package/src/edit/modes/replace.ts +93 -40
- package/src/edit/renderer.ts +1 -1
- package/src/edit/streaming.ts +14 -17
- package/src/eval/py/kernel.ts +2 -1
- package/src/eval/py/spawn-options.ts +24 -29
- package/src/export/html/template.js +8 -26
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/extensibility/legacy-pi-ai-shim.ts +22 -1
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +7 -6
- package/src/extensibility/plugins/legacy-pi-compat.ts +124 -57
- package/src/extensibility/skills.ts +11 -0
- package/src/extensibility/utils.ts +10 -2
- package/src/launch/broker.ts +1 -1
- package/src/launch/client.ts +32 -13
- package/src/launch/ensure.ts +73 -0
- package/src/launch/paths.ts +1 -1
- package/src/launch/protocol.ts +2 -2
- package/src/lsp/client.ts +23 -2
- package/src/lsp/index.ts +8 -0
- package/src/lsp/mux/daemon.ts +348 -0
- package/src/lsp/mux/protocol.ts +96 -0
- package/src/lsp/mux/server.ts +797 -0
- package/src/lsp/types.ts +28 -2
- package/src/main.ts +7 -2
- package/src/mcp/oauth-flow.ts +11 -1
- package/src/mnemopi/embed-client.ts +49 -2
- package/src/mnemopi/state.ts +57 -23
- package/src/modes/components/assistant-message.ts +11 -0
- package/src/modes/components/chat-transcript-builder.ts +4 -0
- package/src/modes/components/custom-editor.ts +9 -0
- package/src/modes/components/read-tool-group.ts +10 -0
- package/src/modes/components/status-line/component.ts +1 -1
- package/src/modes/components/stripped-tool-calls-placeholder.ts +35 -0
- package/src/modes/components/tool-execution.ts +8 -0
- package/src/modes/components/tree-selector.ts +46 -48
- package/src/modes/controllers/event-controller.ts +17 -0
- package/src/modes/controllers/input-controller.ts +39 -1
- package/src/modes/controllers/mcp-command-controller.ts +11 -5
- package/src/modes/controllers/selector-controller.ts +20 -0
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/hotkeys-markdown.ts +1 -0
- package/src/modes/utils/interactive-context-helpers.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +6 -10
- package/src/modes/workflow.ts +8 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +2 -2
- package/src/prompts/system/system-prompt.md +26 -52
- package/src/prompts/system/workflow-notice.md +3 -3
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/bash.md +1 -1
- package/src/prompts/tools/browser.md +1 -0
- package/src/prompts/tools/computer.md +21 -21
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/prompts/tools/replace.md +5 -4
- package/src/prompts/tools/task.md +4 -4
- package/src/sdk.ts +34 -6
- package/src/secrets/index.ts +1 -1
- package/src/session/agent-session-types.ts +3 -1
- package/src/session/agent-session.ts +13 -1
- package/src/session/indexed-session-storage.ts +9 -0
- package/src/session/prewalk.ts +24 -2
- package/src/session/session-advisors.ts +2 -2
- package/src/session/session-context.ts +16 -12
- package/src/session/session-dump-format.ts +1 -1
- package/src/session/session-maintenance.ts +38 -4
- package/src/session/session-manager.ts +102 -67
- package/src/session/session-paths.ts +118 -55
- package/src/session/session-stats.ts +48 -3
- package/src/session/session-storage.ts +30 -22
- package/src/session/stream-guards.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +27 -24
- package/src/system-prompt.ts +8 -3
- package/src/task/index.ts +23 -8
- package/src/task/spawn-policy.ts +14 -0
- package/src/thinking.ts +2 -8
- package/src/tools/ast-grep.ts +10 -4
- package/src/tools/browser/cmux/cmux-tab.ts +5 -10
- package/src/tools/browser/cmux/rpc.ts +2 -8
- package/src/tools/browser/registry.ts +53 -2
- package/src/tools/browser/relay/bridge.ts +945 -0
- package/src/tools/browser/relay/daemon.ts +117 -0
- package/src/tools/browser/relay/extension-assets/background.js.txt +242 -0
- package/src/tools/browser/relay/extension-assets/manifest.json.txt +14 -0
- package/src/tools/browser/relay/extension-assets/options.html.txt +53 -0
- package/src/tools/browser/relay/extension-assets/options.js.txt +23 -0
- package/src/tools/browser/relay/kind.ts +41 -0
- package/src/tools/browser/relay/protocol.ts +54 -0
- package/src/tools/browser/relay/server.ts +141 -0
- package/src/tools/browser/render.ts +4 -1
- package/src/tools/browser/shared-daemon.ts +7 -58
- package/src/tools/browser/tab-supervisor.ts +5 -4
- package/src/tools/browser/tab-worker.ts +33 -12
- package/src/tools/browser.ts +26 -1
- package/src/tools/computer/exposure.ts +11 -35
- package/src/tools/computer/protocol.ts +51 -10
- package/src/tools/computer/supervisor.ts +227 -124
- package/src/tools/computer/worker-entry.ts +11 -6
- package/src/tools/computer/worker.ts +695 -81
- package/src/tools/computer-renderer.ts +104 -65
- package/src/tools/computer.ts +134 -428
- package/src/tools/context.ts +3 -0
- package/src/tools/glob.ts +9 -2
- package/src/tools/grep.ts +12 -6
- package/src/tools/inspect-image.ts +32 -6
- package/src/tools/render-utils.ts +2 -0
- package/src/tools/{browser/run-cancellation.ts → run-scope.ts} +11 -11
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/xdev.ts +29 -2
- package/src/utils/clipboard.ts +40 -14
- package/src/utils/command-args.ts +26 -28
- package/src/utils/tool-choice.ts +0 -13
- package/src/web/kagi.ts +2 -1
- package/src/web/search/index.ts +18 -2
- package/src/web/search/providers/anthropic.ts +4 -1
- package/src/web/search/providers/base.ts +2 -0
- package/src/web/search/providers/brave.ts +3 -1
- package/src/web/search/providers/browser-page.ts +6 -4
- package/src/web/search/providers/codex.ts +4 -1
- package/src/web/search/providers/duckduckgo.ts +2 -1
- package/src/web/search/providers/ecosia.ts +2 -1
- package/src/web/search/providers/exa.ts +4 -2
- package/src/web/search/providers/firecrawl.ts +3 -1
- package/src/web/search/providers/gemini.ts +8 -2
- package/src/web/search/providers/google.ts +2 -1
- package/src/web/search/providers/jina.ts +12 -2
- package/src/web/search/providers/kagi.ts +3 -0
- package/src/web/search/providers/kimi.ts +5 -1
- package/src/web/search/providers/mojeek.ts +2 -1
- package/src/web/search/providers/parallel.ts +5 -1
- package/src/web/search/providers/perplexity.ts +6 -3
- package/src/web/search/providers/public.ts +1 -1
- package/src/web/search/providers/searxng.ts +11 -5
- package/src/web/search/providers/startpage.ts +10 -4
- package/src/web/search/providers/synthetic.ts +11 -5
- package/src/web/search/providers/tavily.ts +3 -1
- package/src/web/search/providers/tinyfish.ts +3 -1
- package/src/web/search/providers/utils.ts +12 -10
- package/src/web/search/providers/xai.ts +2 -2
- package/src/web/search/providers/zai.ts +18 -3
- package/src/web/search/types.ts +6 -0
|
@@ -305,6 +305,7 @@ async function callDuckDuckGoHtml(params: SearchParams, form: URLSearchParams, s
|
|
|
305
305
|
const page = await browserFetch(DUCKDUCKGO_HTML_URL, {
|
|
306
306
|
fetch: params.fetch ?? fetch,
|
|
307
307
|
signal,
|
|
308
|
+
timeoutMs: params.timeoutMs,
|
|
308
309
|
referer: "https://html.duckduckgo.com/",
|
|
309
310
|
init: {
|
|
310
311
|
method: "POST",
|
|
@@ -334,7 +335,7 @@ async function callDuckDuckGoHtml(params: SearchParams, form: URLSearchParams, s
|
|
|
334
335
|
/** Execute a DuckDuckGo web search via the no-JS HTML frontend. */
|
|
335
336
|
export async function searchDuckDuckGo(params: SearchParams): Promise<SearchResponse> {
|
|
336
337
|
const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
337
|
-
const signal = withHardTimeout(params.signal);
|
|
338
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
338
339
|
const sources: SearchSource[] = [];
|
|
339
340
|
const seen = new Set<string>();
|
|
340
341
|
let form: URLSearchParams | undefined = createDuckDuckGoForm(params);
|
|
@@ -99,7 +99,7 @@ function isBlockedPage(page: LoadedHtmlPage): boolean {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
async function callEcosiaHtml(params: SearchParams): Promise<string> {
|
|
102
|
-
const signal = withHardTimeout(params.signal);
|
|
102
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
103
103
|
const url = new URL(ECOSIA_SEARCH_URL);
|
|
104
104
|
// Ecosia serves Google-backed results, so classic operators pass through
|
|
105
105
|
// inline; canonicalize aliases (domain: -> site:, since: -> after:) and
|
|
@@ -111,6 +111,7 @@ async function callEcosiaHtml(params: SearchParams): Promise<string> {
|
|
|
111
111
|
page = await browserFetch(url.href, {
|
|
112
112
|
fetch: params.fetch,
|
|
113
113
|
signal,
|
|
114
|
+
timeoutMs: params.timeoutMs,
|
|
114
115
|
referer: ECOSIA_HOME_URL,
|
|
115
116
|
browser: {
|
|
116
117
|
homeUrl: ECOSIA_HOME_URL,
|
|
@@ -115,6 +115,7 @@ export interface ExaSearchParams {
|
|
|
115
115
|
start_published_date?: string;
|
|
116
116
|
end_published_date?: string;
|
|
117
117
|
signal?: AbortSignal;
|
|
118
|
+
timeoutMs?: number;
|
|
118
119
|
fetch?: FetchImpl;
|
|
119
120
|
/**
|
|
120
121
|
* Credential source. Resolved before falling back to `EXA_API_KEY` so
|
|
@@ -315,7 +316,7 @@ async function callExaSearch(apiKey: string, params: ExaSearchParams): Promise<E
|
|
|
315
316
|
"x-api-key": apiKey,
|
|
316
317
|
},
|
|
317
318
|
body: JSON.stringify(body),
|
|
318
|
-
signal: withHardTimeout(params.signal),
|
|
319
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
319
320
|
});
|
|
320
321
|
|
|
321
322
|
if (!response.ok) {
|
|
@@ -360,7 +361,7 @@ async function callExaMcpSearch(params: ExaSearchParams): Promise<ExaSearchRespo
|
|
|
360
361
|
arguments: buildExaMcpArgs(params),
|
|
361
362
|
},
|
|
362
363
|
}),
|
|
363
|
-
signal: withHardTimeout(params.signal),
|
|
364
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
364
365
|
});
|
|
365
366
|
if (!response.ok) {
|
|
366
367
|
throw new Error(`MCP request failed: ${response.status} ${response.statusText}`);
|
|
@@ -478,6 +479,7 @@ export class ExaProvider extends SearchProvider {
|
|
|
478
479
|
...directiveParams(parsed),
|
|
479
480
|
num_results: params.numSearchResults ?? params.limit,
|
|
480
481
|
signal: params.signal,
|
|
482
|
+
timeoutMs: params.timeoutMs,
|
|
481
483
|
authStorage: params.authStorage,
|
|
482
484
|
sessionId: params.sessionId,
|
|
483
485
|
fetch: params.fetch,
|
|
@@ -38,6 +38,7 @@ export interface FirecrawlSearchParams {
|
|
|
38
38
|
/** Explicit `tbs` (custom date range); takes precedence over `recency`. */
|
|
39
39
|
tbs?: string;
|
|
40
40
|
signal?: AbortSignal;
|
|
41
|
+
timeoutMs?: number;
|
|
41
42
|
fetch?: FetchImpl;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -91,7 +92,7 @@ async function callFirecrawlSearch(
|
|
|
91
92
|
method: "POST",
|
|
92
93
|
headers,
|
|
93
94
|
body: JSON.stringify(buildRequestBody(params)),
|
|
94
|
-
signal: withHardTimeout(params.signal),
|
|
95
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
95
96
|
});
|
|
96
97
|
|
|
97
98
|
if (!response.ok) {
|
|
@@ -145,6 +146,7 @@ export async function searchFirecrawl(params: SearchParams): Promise<SearchRespo
|
|
|
145
146
|
recency: params.recency,
|
|
146
147
|
tbs,
|
|
147
148
|
signal: params.signal,
|
|
149
|
+
timeoutMs: params.timeoutMs,
|
|
148
150
|
fetch: params.fetch,
|
|
149
151
|
};
|
|
150
152
|
const keyResolver = params.authStorage.resolver("firecrawl", {
|
|
@@ -61,6 +61,7 @@ export interface GeminiSearchParams extends GeminiToolParams {
|
|
|
61
61
|
/** Sampling temperature (0–1). Lower = more focused/factual. */
|
|
62
62
|
temperature?: number;
|
|
63
63
|
signal?: AbortSignal;
|
|
64
|
+
timeoutMs?: number;
|
|
64
65
|
authStorage: AuthStorage;
|
|
65
66
|
sessionId?: string;
|
|
66
67
|
fetch?: FetchImpl;
|
|
@@ -309,6 +310,7 @@ async function callGeminiSearch(
|
|
|
309
310
|
toolParams: GeminiToolParams,
|
|
310
311
|
fetchImpl: FetchImpl | undefined,
|
|
311
312
|
signal: AbortSignal | undefined,
|
|
313
|
+
timeoutMs: number | undefined,
|
|
312
314
|
mode?: "auto" | "production" | "sandbox",
|
|
313
315
|
): Promise<GeminiSearchResult> {
|
|
314
316
|
let endpoints: string[];
|
|
@@ -383,7 +385,7 @@ async function callGeminiSearch(
|
|
|
383
385
|
...headers,
|
|
384
386
|
},
|
|
385
387
|
body: JSON.stringify(requestBody),
|
|
386
|
-
signal: withHardTimeout(signal),
|
|
388
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
387
389
|
});
|
|
388
390
|
|
|
389
391
|
let response: Response | undefined;
|
|
@@ -442,6 +444,7 @@ async function callGeminiDeveloperSearch(
|
|
|
442
444
|
toolParams: GeminiToolParams,
|
|
443
445
|
fetchImpl: FetchImpl | undefined,
|
|
444
446
|
signal: AbortSignal | undefined,
|
|
447
|
+
timeoutMs: number | undefined,
|
|
445
448
|
): Promise<GeminiSearchResult> {
|
|
446
449
|
const normalizedSystemPrompt = systemPrompt?.toWellFormed();
|
|
447
450
|
const requestBody: Record<string, unknown> = {
|
|
@@ -480,7 +483,7 @@ async function callGeminiDeveloperSearch(
|
|
|
480
483
|
Accept: "text/event-stream",
|
|
481
484
|
},
|
|
482
485
|
body: JSON.stringify(requestBody),
|
|
483
|
-
signal: withHardTimeout(signal),
|
|
486
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
484
487
|
fetch: fetchImpl,
|
|
485
488
|
maxAttempts: MAX_RETRIES + 1,
|
|
486
489
|
defaultDelayMs: attempt => BASE_DELAY_MS * 2 ** attempt,
|
|
@@ -548,6 +551,7 @@ export async function searchGemini(params: GeminiSearchParams): Promise<SearchRe
|
|
|
548
551
|
},
|
|
549
552
|
params.fetch,
|
|
550
553
|
params.signal,
|
|
554
|
+
params.timeoutMs,
|
|
551
555
|
params.antigravityEndpointMode,
|
|
552
556
|
),
|
|
553
557
|
{ sessionId: params.sessionId, signal: params.signal, seed: seed.access },
|
|
@@ -575,6 +579,7 @@ export async function searchGemini(params: GeminiSearchParams): Promise<SearchRe
|
|
|
575
579
|
},
|
|
576
580
|
params.fetch,
|
|
577
581
|
params.signal,
|
|
582
|
+
params.timeoutMs,
|
|
578
583
|
);
|
|
579
584
|
}
|
|
580
585
|
|
|
@@ -619,6 +624,7 @@ export class GeminiProvider extends SearchProvider {
|
|
|
619
624
|
code_execution: params.codeExecution,
|
|
620
625
|
url_context: params.urlContext,
|
|
621
626
|
signal: params.signal,
|
|
627
|
+
timeoutMs: params.timeoutMs,
|
|
622
628
|
authStorage: params.authStorage,
|
|
623
629
|
sessionId: params.sessionId,
|
|
624
630
|
fetch: params.fetch,
|
|
@@ -117,13 +117,14 @@ function blockReason(page: LoadedHtmlPage): "javascript" | "traffic" | undefined
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
async function callGoogleHtml(params: SearchParams, numResults: number): Promise<string> {
|
|
120
|
-
const signal = withHardTimeout(params.signal);
|
|
120
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
121
121
|
const url = buildSearchUrl(params, numResults);
|
|
122
122
|
let page: LoadedHtmlPage;
|
|
123
123
|
try {
|
|
124
124
|
page = await browserFetch(url, {
|
|
125
125
|
fetch: params.fetch,
|
|
126
126
|
signal,
|
|
127
|
+
timeoutMs: params.timeoutMs,
|
|
127
128
|
referer: GOOGLE_HOME_URL,
|
|
128
129
|
browser: {
|
|
129
130
|
homeUrl: GOOGLE_HOME_URL,
|
|
@@ -22,6 +22,7 @@ export interface JinaSearchParams {
|
|
|
22
22
|
/** Single bare host for Jina's `X-Site` in-site search header. */
|
|
23
23
|
site?: string;
|
|
24
24
|
signal?: AbortSignal;
|
|
25
|
+
timeoutMs?: number;
|
|
25
26
|
fetch?: FetchImpl;
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -45,6 +46,7 @@ async function callJinaSearch(
|
|
|
45
46
|
site?: string,
|
|
46
47
|
signal?: AbortSignal,
|
|
47
48
|
fetchImpl: FetchImpl = fetch,
|
|
49
|
+
timeoutMs?: number,
|
|
48
50
|
): Promise<JinaSearchResponse> {
|
|
49
51
|
const requestUrl = `${JINA_SEARCH_URL}/${encodeURIComponent(query)}`;
|
|
50
52
|
const headers: Record<string, string> = {
|
|
@@ -54,7 +56,7 @@ async function callJinaSearch(
|
|
|
54
56
|
if (site) headers["X-Site"] = site;
|
|
55
57
|
const response = await fetchImpl(requestUrl, {
|
|
56
58
|
headers,
|
|
57
|
-
signal: withHardTimeout(signal),
|
|
59
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
58
60
|
});
|
|
59
61
|
|
|
60
62
|
if (!response.ok) {
|
|
@@ -75,7 +77,14 @@ export async function searchJina(params: JinaSearchParams): Promise<SearchRespon
|
|
|
75
77
|
throw new Error("JINA_API_KEY not found. Set it in environment or .env file.");
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
const response = await callJinaSearch(
|
|
80
|
+
const response = await callJinaSearch(
|
|
81
|
+
apiKey,
|
|
82
|
+
params.query,
|
|
83
|
+
params.site,
|
|
84
|
+
params.signal,
|
|
85
|
+
params.fetch,
|
|
86
|
+
params.timeoutMs,
|
|
87
|
+
);
|
|
79
88
|
const sources: SearchSource[] = [];
|
|
80
89
|
|
|
81
90
|
for (const result of response) {
|
|
@@ -128,6 +137,7 @@ export class JinaProvider extends SearchProvider {
|
|
|
128
137
|
num_results: params.numSearchResults ?? params.limit,
|
|
129
138
|
site,
|
|
130
139
|
signal: params.signal,
|
|
140
|
+
timeoutMs: params.timeoutMs,
|
|
131
141
|
fetch: params.fetch,
|
|
132
142
|
});
|
|
133
143
|
}
|
|
@@ -26,6 +26,7 @@ export async function searchKagi(params: {
|
|
|
26
26
|
recency?: SearchParams["recency"];
|
|
27
27
|
parsedQuery?: StructuredQuery;
|
|
28
28
|
signal?: AbortSignal;
|
|
29
|
+
timeoutMs?: number;
|
|
29
30
|
authStorage: AuthStorage;
|
|
30
31
|
sessionId?: string;
|
|
31
32
|
fetch?: FetchImpl;
|
|
@@ -45,6 +46,7 @@ export async function searchKagi(params: {
|
|
|
45
46
|
recency: params.recency,
|
|
46
47
|
sessionId: params.sessionId,
|
|
47
48
|
signal: params.signal,
|
|
49
|
+
timeoutMs: params.timeoutMs,
|
|
48
50
|
fetch: params.fetch,
|
|
49
51
|
},
|
|
50
52
|
params.authStorage,
|
|
@@ -87,6 +89,7 @@ export class KagiProvider extends SearchProvider {
|
|
|
87
89
|
num_results: params.numSearchResults ?? params.limit,
|
|
88
90
|
recency: params.recency,
|
|
89
91
|
signal: params.signal,
|
|
92
|
+
timeoutMs: params.timeoutMs,
|
|
90
93
|
authStorage: params.authStorage,
|
|
91
94
|
sessionId: params.sessionId,
|
|
92
95
|
fetch: fetchImpl,
|
|
@@ -42,6 +42,7 @@ export interface KimiSearchParams {
|
|
|
42
42
|
num_results?: number;
|
|
43
43
|
include_content?: boolean;
|
|
44
44
|
signal?: AbortSignal;
|
|
45
|
+
timeoutMs?: number;
|
|
45
46
|
authStorage: AuthStorage;
|
|
46
47
|
sessionId?: string;
|
|
47
48
|
fetch?: FetchImpl;
|
|
@@ -105,6 +106,7 @@ async function callKimiSearch(
|
|
|
105
106
|
limit: number;
|
|
106
107
|
includeContent: boolean;
|
|
107
108
|
signal?: AbortSignal;
|
|
109
|
+
timeoutMs?: number;
|
|
108
110
|
fetch?: FetchImpl;
|
|
109
111
|
},
|
|
110
112
|
): Promise<{ response: KimiSearchResponse; requestId?: string }> {
|
|
@@ -122,7 +124,7 @@ async function callKimiSearch(
|
|
|
122
124
|
enable_page_crawling: params.includeContent,
|
|
123
125
|
timeout_seconds: DEFAULT_TIMEOUT_SECONDS,
|
|
124
126
|
}),
|
|
125
|
-
signal: withHardTimeout(params.signal),
|
|
127
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
126
128
|
});
|
|
127
129
|
|
|
128
130
|
if (!response.ok) {
|
|
@@ -161,6 +163,7 @@ export async function searchKimi(params: KimiSearchParams): Promise<SearchRespon
|
|
|
161
163
|
limit,
|
|
162
164
|
includeContent: params.include_content ?? false,
|
|
163
165
|
signal: params.signal,
|
|
166
|
+
timeoutMs: params.timeoutMs,
|
|
164
167
|
fetch: params.fetch,
|
|
165
168
|
}),
|
|
166
169
|
{ signal: params.signal },
|
|
@@ -209,6 +212,7 @@ export class KimiProvider extends SearchProvider {
|
|
|
209
212
|
parsedQuery: params.parsedQuery,
|
|
210
213
|
num_results: params.numSearchResults ?? params.limit,
|
|
211
214
|
signal: params.signal,
|
|
215
|
+
timeoutMs: params.timeoutMs,
|
|
212
216
|
authStorage: params.authStorage,
|
|
213
217
|
sessionId: params.sessionId,
|
|
214
218
|
fetch: fetchImpl,
|
|
@@ -137,13 +137,14 @@ function isRobotPage(page: LoadedHtmlPage): boolean {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
async function callMojeekHtml(params: SearchParams, numResults: number): Promise<string> {
|
|
140
|
-
const signal = withHardTimeout(params.signal);
|
|
140
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
141
141
|
const url = buildSearchUrl(params, numResults);
|
|
142
142
|
let page: LoadedHtmlPage;
|
|
143
143
|
try {
|
|
144
144
|
page = await browserFetch(url, {
|
|
145
145
|
fetch: params.fetch,
|
|
146
146
|
signal,
|
|
147
|
+
timeoutMs: params.timeoutMs,
|
|
147
148
|
randomizeHeaders: false,
|
|
148
149
|
referer: MOJEEK_HOME_URL,
|
|
149
150
|
browser: {
|
|
@@ -59,6 +59,7 @@ async function searchWithAuthStorage(
|
|
|
59
59
|
queries: string[],
|
|
60
60
|
params: {
|
|
61
61
|
signal?: AbortSignal;
|
|
62
|
+
timeoutMs?: number;
|
|
62
63
|
fetch?: FetchImpl;
|
|
63
64
|
},
|
|
64
65
|
authStorage: AuthStorage,
|
|
@@ -97,7 +98,7 @@ async function searchWithAuthStorage(
|
|
|
97
98
|
},
|
|
98
99
|
...(sourcePolicy && { source_policy: sourcePolicy }),
|
|
99
100
|
}),
|
|
100
|
-
signal: withHardTimeout(params.signal),
|
|
101
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
if (!response.ok) {
|
|
@@ -116,6 +117,7 @@ export async function searchParallel(
|
|
|
116
117
|
query: string;
|
|
117
118
|
num_results?: number;
|
|
118
119
|
signal?: AbortSignal;
|
|
120
|
+
timeoutMs?: number;
|
|
119
121
|
fetch?: FetchImpl;
|
|
120
122
|
parsedQuery?: StructuredQuery;
|
|
121
123
|
},
|
|
@@ -134,6 +136,7 @@ export async function searchParallel(
|
|
|
134
136
|
[query],
|
|
135
137
|
{
|
|
136
138
|
signal: params.signal,
|
|
139
|
+
timeoutMs: params.timeoutMs,
|
|
137
140
|
fetch: params.fetch,
|
|
138
141
|
},
|
|
139
142
|
authStorage,
|
|
@@ -172,6 +175,7 @@ export class ParallelProvider extends SearchProvider {
|
|
|
172
175
|
query: params.query,
|
|
173
176
|
num_results: params.numSearchResults ?? params.limit,
|
|
174
177
|
signal: params.signal,
|
|
178
|
+
timeoutMs: params.timeoutMs,
|
|
175
179
|
fetch: params.fetch,
|
|
176
180
|
parsedQuery: params.parsedQuery,
|
|
177
181
|
},
|
|
@@ -322,6 +322,7 @@ function sourcesFromTextPayload(text: string | undefined): SearchSource[] {
|
|
|
322
322
|
}
|
|
323
323
|
export interface PerplexitySearchParams {
|
|
324
324
|
signal?: AbortSignal;
|
|
325
|
+
timeoutMs?: number;
|
|
325
326
|
query: string;
|
|
326
327
|
system_prompt?: string;
|
|
327
328
|
/** Pre-parsed view of `query` from the search pipeline; parsed locally when absent. */
|
|
@@ -498,10 +499,11 @@ async function callPerplexityApi(
|
|
|
498
499
|
request: PerplexityRequest,
|
|
499
500
|
fetchImpl: FetchImpl | undefined,
|
|
500
501
|
signal?: AbortSignal,
|
|
502
|
+
timeoutMs?: number,
|
|
501
503
|
): Promise<SearchResponse> {
|
|
502
504
|
const metadata: PerplexityApiStreamMetadata = {};
|
|
503
505
|
const context = buildPerplexityContext(request);
|
|
504
|
-
const requestSignal = withHardTimeout(signal);
|
|
506
|
+
const requestSignal = withHardTimeout(signal, timeoutMs);
|
|
505
507
|
const onSseEvent = (event: { data: string }): void => {
|
|
506
508
|
collectPerplexityMetadata(metadata, event.data);
|
|
507
509
|
};
|
|
@@ -690,7 +692,7 @@ async function callPerplexityAsk(
|
|
|
690
692
|
query_str: effectiveQuery,
|
|
691
693
|
params: requestParams,
|
|
692
694
|
}),
|
|
693
|
-
signal: withHardTimeout(params.signal),
|
|
695
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
694
696
|
};
|
|
695
697
|
|
|
696
698
|
// The consumer ask endpoint intermittently drops the socket before sending an
|
|
@@ -902,7 +904,7 @@ export async function searchPerplexity(params: PerplexitySearchParams): Promise<
|
|
|
902
904
|
for (const auth of authMethods) {
|
|
903
905
|
if (auth.type === "api_key") {
|
|
904
906
|
try {
|
|
905
|
-
const result = await callPerplexityApi(auth, request, params.fetch, params.signal);
|
|
907
|
+
const result = await callPerplexityApi(auth, request, params.fetch, params.signal, params.timeoutMs);
|
|
906
908
|
result.authMode = "api_key";
|
|
907
909
|
return applySourceLimit(result, params.num_results);
|
|
908
910
|
} catch (error) {
|
|
@@ -978,6 +980,7 @@ export class PerplexityProvider extends SearchProvider {
|
|
|
978
980
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
979
981
|
return searchPerplexity({
|
|
980
982
|
signal: params.signal,
|
|
983
|
+
timeoutMs: params.timeoutMs,
|
|
981
984
|
query: params.query,
|
|
982
985
|
parsedQuery: params.parsedQuery,
|
|
983
986
|
temperature: params.temperature,
|
|
@@ -130,7 +130,7 @@ export async function searchPublicWeb(
|
|
|
130
130
|
// hard deadline; the straggler controller lets the aggregate cancel
|
|
131
131
|
// still-running engines once it decides to return.
|
|
132
132
|
const straggler = new AbortController();
|
|
133
|
-
const signal = AbortSignal.any([withHardTimeout(params.signal), straggler.signal]);
|
|
133
|
+
const signal = AbortSignal.any([withHardTimeout(params.signal, params.timeoutMs), straggler.signal]);
|
|
134
134
|
|
|
135
135
|
const responses: (SearchResponse | undefined)[] = new Array(engineIds.length);
|
|
136
136
|
const failures: { provider: { id: SearchProviderId; label: string }; error: unknown }[] = [];
|
|
@@ -197,11 +197,12 @@ async function fetchEngineNameMap(
|
|
|
197
197
|
auth: SearXNGAuth | null,
|
|
198
198
|
fetchImpl: FetchImpl | undefined,
|
|
199
199
|
signal: AbortSignal | undefined,
|
|
200
|
+
timeoutMs?: number,
|
|
200
201
|
): Promise<Map<string, string> | null> {
|
|
201
202
|
try {
|
|
202
203
|
const response = await (fetchImpl ?? fetch)(`${base}/config`, {
|
|
203
204
|
headers: buildHeaders(auth),
|
|
204
|
-
signal: withHardTimeout(signal),
|
|
205
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
205
206
|
});
|
|
206
207
|
if (!response.ok) return null;
|
|
207
208
|
const config = (await response.json()) as SearXNGConfig;
|
|
@@ -224,11 +225,12 @@ function getEngineNameMap(
|
|
|
224
225
|
auth: SearXNGAuth | null,
|
|
225
226
|
fetchImpl: FetchImpl | undefined,
|
|
226
227
|
signal: AbortSignal | undefined,
|
|
228
|
+
timeoutMs?: number,
|
|
227
229
|
): Promise<Map<string, string> | null> {
|
|
228
230
|
const base = endpoint.replace(/\/+$/, "");
|
|
229
231
|
let cached = engineNameMapCache.get(base);
|
|
230
232
|
if (!cached) {
|
|
231
|
-
cached = fetchEngineNameMap(base, auth, fetchImpl, signal).then(map => {
|
|
233
|
+
cached = fetchEngineNameMap(base, auth, fetchImpl, signal, timeoutMs).then(map => {
|
|
232
234
|
if (!map) engineNameMapCache.delete(base);
|
|
233
235
|
return map;
|
|
234
236
|
});
|
|
@@ -247,13 +249,14 @@ async function resolveEngineNames(
|
|
|
247
249
|
auth: SearXNGAuth | null,
|
|
248
250
|
fetchImpl: FetchImpl | undefined,
|
|
249
251
|
signal: AbortSignal | undefined,
|
|
252
|
+
timeoutMs?: number,
|
|
250
253
|
): Promise<string | undefined> {
|
|
251
254
|
const entries = raw
|
|
252
255
|
.split(",")
|
|
253
256
|
.map(entry => entry.trim())
|
|
254
257
|
.filter(Boolean);
|
|
255
258
|
if (!entries.length) return undefined;
|
|
256
|
-
const map = await getEngineNameMap(endpoint, auth, fetchImpl, signal);
|
|
259
|
+
const map = await getEngineNameMap(endpoint, auth, fetchImpl, signal, timeoutMs);
|
|
257
260
|
if (!map) return entries.join(",");
|
|
258
261
|
return entries.map(entry => map.get(entry.toLowerCase()) ?? entry).join(",");
|
|
259
262
|
}
|
|
@@ -324,6 +327,7 @@ async function callSearXNGSearch(
|
|
|
324
327
|
engines?: string;
|
|
325
328
|
language?: string;
|
|
326
329
|
signal?: AbortSignal;
|
|
330
|
+
timeoutMs?: number;
|
|
327
331
|
fetch?: FetchImpl;
|
|
328
332
|
},
|
|
329
333
|
auth: SearXNGAuth | null,
|
|
@@ -332,7 +336,7 @@ async function callSearXNGSearch(
|
|
|
332
336
|
|
|
333
337
|
const response = await (params.fetch ?? fetch)(url, {
|
|
334
338
|
headers,
|
|
335
|
-
signal: withHardTimeout(params.signal),
|
|
339
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
336
340
|
});
|
|
337
341
|
|
|
338
342
|
if (!response.ok) {
|
|
@@ -352,6 +356,7 @@ export async function searchSearXNG(params: {
|
|
|
352
356
|
num_results?: number;
|
|
353
357
|
recency?: "day" | "week" | "month" | "year";
|
|
354
358
|
signal?: AbortSignal;
|
|
359
|
+
timeoutMs?: number;
|
|
355
360
|
fetch?: FetchImpl;
|
|
356
361
|
}): Promise<SearchResponse> {
|
|
357
362
|
const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
@@ -386,7 +391,7 @@ export async function searchSearXNG(params: {
|
|
|
386
391
|
if (parsed.lang) language = parsed.lang;
|
|
387
392
|
|
|
388
393
|
const engines = configuredEngines
|
|
389
|
-
? await resolveEngineNames(configuredEngines, endpoint, auth, params.fetch, params.signal)
|
|
394
|
+
? await resolveEngineNames(configuredEngines, endpoint, auth, params.fetch, params.signal, params.timeoutMs)
|
|
390
395
|
: undefined;
|
|
391
396
|
|
|
392
397
|
const response = await callSearXNGSearch(
|
|
@@ -455,6 +460,7 @@ export class SearXNGProvider extends SearchProvider {
|
|
|
455
460
|
num_results: params.numSearchResults ?? params.limit,
|
|
456
461
|
recency: params.recency,
|
|
457
462
|
signal: params.signal,
|
|
463
|
+
timeoutMs: params.timeoutMs,
|
|
458
464
|
fetch: params.fetch,
|
|
459
465
|
});
|
|
460
466
|
}
|
|
@@ -121,10 +121,14 @@ function parseHtmlResults(html: string): ParsedResult[] {
|
|
|
121
121
|
* any failure (network, non-OK status, challenge shell, markup drift) yields
|
|
122
122
|
* `undefined` and the caller falls back to a direct GET.
|
|
123
123
|
*/
|
|
124
|
-
async function fetchFormInputs(
|
|
124
|
+
async function fetchFormInputs(
|
|
125
|
+
fetchImpl: FetchImpl,
|
|
126
|
+
signal: AbortSignal,
|
|
127
|
+
timeoutMs?: number,
|
|
128
|
+
): Promise<Record<string, string> | undefined> {
|
|
125
129
|
let page: LoadedHtmlPage;
|
|
126
130
|
try {
|
|
127
|
-
page = await browserFetch(STARTPAGE_HOME_URL, { fetch: fetchImpl, signal });
|
|
131
|
+
page = await browserFetch(STARTPAGE_HOME_URL, { fetch: fetchImpl, signal, timeoutMs });
|
|
128
132
|
} catch (error) {
|
|
129
133
|
if (signal.aborted) throw error;
|
|
130
134
|
return undefined;
|
|
@@ -135,7 +139,7 @@ async function fetchFormInputs(fetchImpl: FetchImpl, signal: AbortSignal): Promi
|
|
|
135
139
|
|
|
136
140
|
async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
137
141
|
const fetchImpl = params.fetch ?? fetch;
|
|
138
|
-
const signal = withHardTimeout(params.signal);
|
|
142
|
+
const signal = withHardTimeout(params.signal, params.timeoutMs);
|
|
139
143
|
const withDate = params.recency ? RECENCY_TO_STARTPAGE_WITH_DATE[params.recency] : undefined;
|
|
140
144
|
// Startpage proxies Google, so the operator set works inline; rebuild via
|
|
141
145
|
// the shared scraper formatter to canonicalize aliases (domain: → site:,
|
|
@@ -143,7 +147,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
143
147
|
// queries pass through byte-identical.
|
|
144
148
|
const query = formatScraperQuery(params.query, params.parsedQuery);
|
|
145
149
|
|
|
146
|
-
const formInputs = await fetchFormInputs(fetchImpl, signal);
|
|
150
|
+
const formInputs = await fetchFormInputs(fetchImpl, signal, params.timeoutMs);
|
|
147
151
|
let page: LoadedHtmlPage;
|
|
148
152
|
if (formInputs) {
|
|
149
153
|
const form = new URLSearchParams(formInputs);
|
|
@@ -152,6 +156,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
152
156
|
page = await browserFetch(STARTPAGE_SEARCH_URL, {
|
|
153
157
|
fetch: fetchImpl,
|
|
154
158
|
signal,
|
|
159
|
+
timeoutMs: params.timeoutMs,
|
|
155
160
|
referer: STARTPAGE_HOME_URL,
|
|
156
161
|
init: { method: "POST", body: form.toString() },
|
|
157
162
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
@@ -163,6 +168,7 @@ async function callStartpageHtml(params: SearchParams): Promise<string> {
|
|
|
163
168
|
page = await browserFetch(url.href, {
|
|
164
169
|
fetch: fetchImpl,
|
|
165
170
|
signal,
|
|
171
|
+
timeoutMs: params.timeoutMs,
|
|
166
172
|
referer: STARTPAGE_HOME_URL,
|
|
167
173
|
});
|
|
168
174
|
}
|
|
@@ -43,6 +43,7 @@ async function callSyntheticSearch(
|
|
|
43
43
|
query: string,
|
|
44
44
|
signal?: AbortSignal,
|
|
45
45
|
fetchImpl: FetchImpl = fetch,
|
|
46
|
+
timeoutMs?: number,
|
|
46
47
|
): Promise<SyntheticSearchResponse> {
|
|
47
48
|
const response = await fetchImpl(SYNTHETIC_SEARCH_URL, {
|
|
48
49
|
method: "POST",
|
|
@@ -51,7 +52,7 @@ async function callSyntheticSearch(
|
|
|
51
52
|
Authorization: `Bearer ${apiKey}`,
|
|
52
53
|
},
|
|
53
54
|
body: JSON.stringify({ query }),
|
|
54
|
-
signal: withHardTimeout(signal),
|
|
55
|
+
signal: withHardTimeout(signal, timeoutMs),
|
|
55
56
|
});
|
|
56
57
|
|
|
57
58
|
if (!response.ok) {
|
|
@@ -80,10 +81,15 @@ export async function searchSynthetic(params: SearchParamsWithFetch): Promise<Se
|
|
|
80
81
|
: params.query;
|
|
81
82
|
|
|
82
83
|
const fetchImpl = params.fetch;
|
|
83
|
-
const data = await withAuth(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
const data = await withAuth(
|
|
85
|
+
keyOrResolver,
|
|
86
|
+
key => callSyntheticSearch(key, query, params.signal, fetchImpl, params.timeoutMs),
|
|
87
|
+
{
|
|
88
|
+
signal: params.signal,
|
|
89
|
+
missingKeyMessage:
|
|
90
|
+
"Synthetic credentials not found. Set SYNTHETIC_API_KEY or login with 'omp /login synthetic'.",
|
|
91
|
+
},
|
|
92
|
+
);
|
|
87
93
|
const sources: SearchSource[] = [];
|
|
88
94
|
|
|
89
95
|
for (const result of data.results ?? []) {
|
|
@@ -30,6 +30,7 @@ export interface TavilySearchParams {
|
|
|
30
30
|
/** `before:` upper bound, ISO `YYYY-MM-DD`, mapped to `end_date`. */
|
|
31
31
|
end_date?: string;
|
|
32
32
|
signal?: AbortSignal;
|
|
33
|
+
timeoutMs?: number;
|
|
33
34
|
fetch?: FetchImpl;
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -120,7 +121,7 @@ async function callTavilySearch(apiKey: string, params: TavilySearchParams): Pro
|
|
|
120
121
|
Authorization: `Bearer ${apiKey}`,
|
|
121
122
|
},
|
|
122
123
|
body: JSON.stringify(buildRequestBody(params)),
|
|
123
|
-
signal: withHardTimeout(params.signal),
|
|
124
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
if (!response.ok) {
|
|
@@ -189,6 +190,7 @@ export async function searchTavily(params: SearchParams): Promise<SearchResponse
|
|
|
189
190
|
num_results: params.numSearchResults ?? params.limit,
|
|
190
191
|
recency: params.recency,
|
|
191
192
|
signal: params.signal,
|
|
193
|
+
timeoutMs: params.timeoutMs,
|
|
192
194
|
fetch: params.fetch,
|
|
193
195
|
};
|
|
194
196
|
if (parsed.hasDirectives) {
|
|
@@ -34,6 +34,7 @@ export interface TinyFishSearchParams {
|
|
|
34
34
|
recency?: SearchParams["recency"];
|
|
35
35
|
page?: number;
|
|
36
36
|
signal?: AbortSignal;
|
|
37
|
+
timeoutMs?: number;
|
|
37
38
|
fetch?: FetchImpl;
|
|
38
39
|
}
|
|
39
40
|
|
|
@@ -78,7 +79,7 @@ async function callTinyFishSearch(apiKey: string, params: TinyFishSearchParams):
|
|
|
78
79
|
Accept: "application/json",
|
|
79
80
|
"X-API-Key": apiKey,
|
|
80
81
|
},
|
|
81
|
-
signal: withHardTimeout(params.signal),
|
|
82
|
+
signal: withHardTimeout(params.signal, params.timeoutMs),
|
|
82
83
|
});
|
|
83
84
|
|
|
84
85
|
if (!response.ok) {
|
|
@@ -117,6 +118,7 @@ export async function searchTinyFish(params: SearchParams): Promise<SearchRespon
|
|
|
117
118
|
num_results: pageSize,
|
|
118
119
|
recency: params.recency,
|
|
119
120
|
signal: params.signal,
|
|
121
|
+
timeoutMs: params.timeoutMs,
|
|
120
122
|
fetch: params.fetch,
|
|
121
123
|
};
|
|
122
124
|
const keyOrResolver: ApiKey = params.authStorage.resolver("tinyfish", {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { AgentStorage } from "../../../session/agent-storage";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_WEB_SEARCH_TIMEOUT_SECONDS,
|
|
4
|
+
SearchProviderError,
|
|
5
|
+
type SearchProviderId,
|
|
6
|
+
type SearchSource,
|
|
7
|
+
} from "../../../web/search/types";
|
|
3
8
|
import { dateToAgeSeconds } from "../utils";
|
|
4
9
|
|
|
5
10
|
/**
|
|
@@ -44,16 +49,13 @@ export function findCredential(
|
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* Pure search APIs (brave, exa, jina, tavily, searxng, synthetic, zai)
|
|
53
|
-
* settle far faster in practice; reusing the same ceiling keeps the wiring
|
|
54
|
-
* uniform without compromising correctness.
|
|
52
|
+
* The 60-second default tolerates legitimate slow LLM-mediated responses
|
|
53
|
+
* (Anthropic web_search_20250305, Perplexity, Gemini, Codex) while bounding
|
|
54
|
+
* Windows stalls when Bun's `AbortSignal` fails to propagate. Callers may
|
|
55
|
+
* configure a longer provider deadline, capped at five minutes by the
|
|
56
|
+
* dispatcher; pure search APIs typically settle far faster.
|
|
55
57
|
*/
|
|
56
|
-
export const SEARCH_HARD_TIMEOUT_MS =
|
|
58
|
+
export const SEARCH_HARD_TIMEOUT_MS = DEFAULT_WEB_SEARCH_TIMEOUT_SECONDS * 1_000;
|
|
57
59
|
|
|
58
60
|
/**
|
|
59
61
|
* Compose a caller-supplied {@link AbortSignal} with a hard timeout so an
|