@oh-my-pi/pi-coding-agent 15.8.3 → 15.9.1
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 +113 -31
- package/dist/types/capability/skill.d.ts +7 -0
- package/dist/types/cli/update-cli.d.ts +15 -1
- package/dist/types/config/append-only-context-mode.d.ts +8 -0
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +15 -0
- package/dist/types/config/settings-schema.d.ts +38 -24
- package/dist/types/debug/protocol-probe.d.ts +38 -0
- package/dist/types/debug/terminal-info.d.ts +34 -0
- package/dist/types/exa/mcp-client.d.ts +2 -1
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/custom-tools/types.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/mcp/json-rpc.d.ts +6 -1
- package/dist/types/mcp/tool-bridge.d.ts +4 -0
- package/dist/types/mcp/transports/stdio.d.ts +16 -7
- package/dist/types/mnemopi/state.d.ts +2 -2
- package/dist/types/modes/components/agent-dashboard.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +3 -2
- package/dist/types/modes/components/extensions/extension-dashboard.d.ts +1 -0
- package/dist/types/modes/components/hook-selector.d.ts +11 -0
- package/dist/types/modes/components/plugin-settings.d.ts +40 -8
- package/dist/types/modes/components/session-selector.d.ts +8 -3
- package/dist/types/modes/components/settings-selector.d.ts +1 -1
- package/dist/types/modes/components/todo-reminder.d.ts +1 -1
- package/dist/types/modes/components/transcript-container.d.ts +36 -0
- package/dist/types/modes/interactive-mode.d.ts +2 -1
- package/dist/types/modes/rpc/rpc-types.d.ts +1 -1
- package/dist/types/modes/theme/theme.d.ts +20 -1
- package/dist/types/modes/utils/keybinding-matchers.d.ts +4 -0
- package/dist/types/plan-mode/plan-handoff.d.ts +20 -0
- package/dist/types/session/agent-session.d.ts +5 -2
- package/dist/types/session/history-storage.d.ts +3 -4
- package/dist/types/session/indexed-session-storage.d.ts +59 -0
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/redis-session-storage.d.ts +12 -85
- package/dist/types/session/session-manager.d.ts +21 -0
- package/dist/types/session/session-storage.d.ts +5 -7
- package/dist/types/session/sql-session-storage.d.ts +16 -85
- package/dist/types/slash-commands/types.d.ts +17 -4
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/telemetry-export.d.ts +19 -0
- package/dist/types/tiny/compiled-runtime.d.ts +35 -0
- package/dist/types/tiny/text.d.ts +17 -0
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/index.d.ts +4 -2
- package/dist/types/tools/path-utils.d.ts +1 -1
- package/dist/types/tools/search.d.ts +2 -2
- package/dist/types/tools/{todo-write.d.ts → todo.d.ts} +18 -18
- package/dist/types/utils/session-color.d.ts +7 -2
- package/dist/types/web/search/index.d.ts +1 -1
- package/dist/types/web/search/provider.d.ts +2 -2
- package/dist/types/web/search/providers/base.d.ts +14 -0
- package/dist/types/web/search/providers/exa.d.ts +9 -0
- package/dist/types/web/search/providers/perplexity.d.ts +2 -2
- package/dist/types/web/search/types.d.ts +66 -1
- package/package.json +15 -9
- package/scripts/build-binary.ts +12 -0
- package/src/capability/skill.ts +7 -0
- package/src/cli/args.ts +1 -1
- package/src/cli/session-picker.ts +1 -0
- package/src/cli/update-cli.ts +54 -2
- package/src/commands/completions.ts +1 -1
- package/src/config/append-only-context-mode.ts +37 -0
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +24 -57
- package/src/debug/index.ts +67 -1
- package/src/debug/protocol-probe.ts +267 -0
- package/src/debug/terminal-info.ts +127 -0
- package/src/exa/mcp-client.ts +11 -5
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +3 -3
- package/src/extensibility/custom-tools/types.ts +1 -1
- package/src/extensibility/extensions/types.ts +11 -0
- package/src/extensibility/shared-events.ts +1 -1
- package/src/extensibility/skills.ts +3 -3
- package/src/index.ts +1 -0
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/main.ts +16 -2
- package/src/mcp/json-rpc.ts +8 -0
- package/src/mcp/render.ts +3 -0
- package/src/mcp/tool-bridge.ts +10 -2
- package/src/mcp/transports/http.ts +33 -16
- package/src/mcp/transports/stdio.ts +37 -12
- package/src/mnemopi/state.ts +4 -4
- package/src/modes/acp/acp-agent.ts +168 -3
- package/src/modes/acp/acp-event-mapper.ts +7 -7
- package/src/modes/components/agent-dashboard.ts +103 -31
- package/src/modes/components/assistant-message.ts +3 -2
- package/src/modes/components/extensions/extension-dashboard.ts +56 -10
- package/src/modes/components/history-search.ts +128 -14
- package/src/modes/components/hook-selector.ts +149 -14
- package/src/modes/components/plugin-settings.ts +270 -36
- package/src/modes/components/session-selector.ts +81 -19
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +2 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/tips.txt +6 -2
- package/src/modes/components/todo-reminder.ts +1 -1
- package/src/modes/components/tool-execution.ts +9 -4
- package/src/modes/components/transcript-container.ts +109 -0
- package/src/modes/controllers/command-controller.ts +4 -3
- package/src/modes/controllers/event-controller.ts +12 -7
- package/src/modes/controllers/extension-ui-controller.ts +3 -0
- package/src/modes/controllers/input-controller.ts +10 -5
- package/src/modes/controllers/selector-controller.ts +30 -19
- package/src/modes/controllers/todo-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +56 -9
- package/src/modes/print-mode.ts +5 -0
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/theme/theme.ts +48 -8
- package/src/modes/utils/keybinding-matchers.ts +10 -0
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/plan-mode/plan-handoff.ts +37 -0
- package/src/priority.json +4 -0
- package/src/prompts/goals/goal-continuation.md +1 -1
- package/src/prompts/steering/user-interjection.md +10 -0
- package/src/prompts/system/agent-creation-architect.md +1 -26
- package/src/prompts/system/eager-todo.md +3 -3
- package/src/prompts/system/orchestrate-notice.md +5 -5
- package/src/prompts/system/plan-mode-approved.md +14 -17
- package/src/prompts/system/plan-mode-reference.md +3 -6
- package/src/prompts/system/subagent-system-prompt.md +11 -0
- package/src/prompts/system/system-prompt.md +143 -145
- package/src/prompts/system/title-system.md +3 -2
- package/src/prompts/system/workflow-notice.md +1 -1
- package/src/prompts/tools/browser.md +33 -30
- package/src/prompts/tools/render-mermaid.md +2 -2
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +3 -1
- package/src/prompts/tools/{todo-write.md → todo.md} +5 -5
- package/src/sdk.ts +6 -21
- package/src/session/agent-session.ts +44 -21
- package/src/session/history-storage.ts +11 -18
- package/src/session/indexed-session-storage.ts +430 -0
- package/src/session/messages.ts +80 -0
- package/src/session/redis-session-storage.ts +66 -377
- package/src/session/session-manager.ts +109 -23
- package/src/session/session-storage.ts +148 -68
- package/src/session/sql-session-storage.ts +131 -382
- package/src/slash-commands/helpers/todo.ts +2 -2
- package/src/slash-commands/types.ts +27 -10
- package/src/task/executor.ts +9 -1
- package/src/task/index.ts +51 -1
- package/src/telemetry-export.ts +126 -0
- package/src/tiny/compiled-runtime.ts +179 -0
- package/src/tiny/text.ts +112 -1
- package/src/tiny/worker.ts +24 -2
- package/src/tools/ask.ts +133 -87
- package/src/tools/fetch.ts +17 -4
- package/src/tools/find.ts +2 -2
- package/src/tools/index.ts +6 -4
- package/src/tools/memory-recall.ts +1 -1
- package/src/tools/memory-reflect.ts +1 -1
- package/src/tools/path-utils.ts +16 -7
- package/src/tools/renderers.ts +2 -2
- package/src/tools/search.ts +6 -3
- package/src/tools/ssh.ts +26 -10
- package/src/tools/{todo-write.ts → todo.ts} +32 -35
- package/src/tools/write.ts +14 -2
- package/src/tui/status-line.ts +15 -4
- package/src/utils/session-color.ts +39 -14
- package/src/utils/title-generator.ts +9 -2
- package/src/web/search/index.ts +4 -2
- package/src/web/search/provider.ts +19 -35
- package/src/web/search/providers/base.ts +17 -0
- package/src/web/search/providers/exa.ts +111 -7
- package/src/web/search/providers/perplexity.ts +8 -4
- package/src/web/search/types.ts +74 -32
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
10
10
|
import { settings } from "../../../config/settings";
|
|
11
|
+
import { callExaTool, findApiKey, isSearchResponse } from "../../../exa/mcp-client";
|
|
11
12
|
|
|
12
13
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
13
14
|
import { SearchProviderError } from "../../../web/search/types";
|
|
@@ -56,6 +57,71 @@ interface ExaSearchResponse {
|
|
|
56
57
|
costDollars?: { total: number };
|
|
57
58
|
searchTime?: number;
|
|
58
59
|
}
|
|
60
|
+
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
61
|
+
if (typeof value !== "object" || value === null) return null;
|
|
62
|
+
return value as Record<string, unknown>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function parseOptionalField(section: string, label: string): string | null | undefined {
|
|
66
|
+
const regex = new RegExp(`(?:^|\\n)${label}:\\s*([^\\n]*)`);
|
|
67
|
+
const match = section.match(regex);
|
|
68
|
+
if (!match) return undefined;
|
|
69
|
+
const value = match[1].trim();
|
|
70
|
+
return value.length > 0 ? value : null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function parseTextField(section: string): string | null | undefined {
|
|
74
|
+
const match = section.match(/(?:^|\n)Text:\s*([\s\S]*)$/);
|
|
75
|
+
if (!match) return undefined;
|
|
76
|
+
const value = match[1].trim();
|
|
77
|
+
return value.length > 0 ? value : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function parseExaMcpTextPayload(payload: unknown): ExaSearchResponse | null {
|
|
81
|
+
const root = asRecord(payload);
|
|
82
|
+
if (!root) return null;
|
|
83
|
+
|
|
84
|
+
const content = root.content;
|
|
85
|
+
if (!Array.isArray(content)) return null;
|
|
86
|
+
|
|
87
|
+
const textBlocks = content
|
|
88
|
+
.map(item => {
|
|
89
|
+
const part = asRecord(item);
|
|
90
|
+
const text = typeof part?.text === "string" ? part.text : "";
|
|
91
|
+
return text.replace(/\r\n?/g, "\n").trim();
|
|
92
|
+
})
|
|
93
|
+
.filter(text => text.length > 0);
|
|
94
|
+
|
|
95
|
+
if (textBlocks.length === 0) return null;
|
|
96
|
+
|
|
97
|
+
const sections = textBlocks
|
|
98
|
+
.join("\n\n")
|
|
99
|
+
.split(/\n{2,}(?=Title:\s*[^\n]*(?:\n(?:URL|Author|Published Date|Text):))/)
|
|
100
|
+
.map(section => section.trim())
|
|
101
|
+
.filter(section => section.startsWith("Title:"));
|
|
102
|
+
|
|
103
|
+
const results: ExaSearchResult[] = [];
|
|
104
|
+
for (const section of sections) {
|
|
105
|
+
const title = parseOptionalField(section, "Title");
|
|
106
|
+
const url = parseOptionalField(section, "URL");
|
|
107
|
+
const author = parseOptionalField(section, "Author");
|
|
108
|
+
const publishedDate = parseOptionalField(section, "Published Date");
|
|
109
|
+
const text = parseTextField(section);
|
|
110
|
+
|
|
111
|
+
if (!title && !url && !text) continue;
|
|
112
|
+
|
|
113
|
+
results.push({
|
|
114
|
+
title: title ?? undefined,
|
|
115
|
+
url: url ?? undefined,
|
|
116
|
+
author: author ?? undefined,
|
|
117
|
+
publishedDate: publishedDate ?? undefined,
|
|
118
|
+
text: text ?? undefined,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (results.length === 0) return null;
|
|
123
|
+
return { results };
|
|
124
|
+
}
|
|
59
125
|
|
|
60
126
|
export function normalizeSearchType(type: ExaSearchParamType | undefined): ExaSearchType {
|
|
61
127
|
if (!type) return "auto";
|
|
@@ -133,6 +199,32 @@ async function callExaSearch(apiKey: string, params: ExaSearchParams): Promise<E
|
|
|
133
199
|
|
|
134
200
|
return response.json() as Promise<ExaSearchResponse>;
|
|
135
201
|
}
|
|
202
|
+
function buildExaMcpArgs(params: ExaSearchParams): Record<string, unknown> {
|
|
203
|
+
const args: Record<string, unknown> = { query: params.query };
|
|
204
|
+
if (params.num_results !== undefined) args.num_results = params.num_results;
|
|
205
|
+
if (params.type !== undefined) args.type = params.type;
|
|
206
|
+
if (params.include_domains !== undefined) args.include_domains = params.include_domains;
|
|
207
|
+
if (params.exclude_domains !== undefined) args.exclude_domains = params.exclude_domains;
|
|
208
|
+
if (params.start_published_date !== undefined) args.start_published_date = params.start_published_date;
|
|
209
|
+
if (params.end_published_date !== undefined) args.end_published_date = params.end_published_date;
|
|
210
|
+
return args;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function callExaMcpSearch(params: ExaSearchParams): Promise<ExaSearchResponse> {
|
|
214
|
+
const response = await callExaTool("web_search_exa", buildExaMcpArgs(params), findApiKey(), {
|
|
215
|
+
signal: withHardTimeout(params.signal),
|
|
216
|
+
});
|
|
217
|
+
if (isSearchResponse(response)) {
|
|
218
|
+
return response as ExaSearchResponse;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const parsed = parseExaMcpTextPayload(response);
|
|
222
|
+
if (parsed) {
|
|
223
|
+
return parsed;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
throw new Error("Exa MCP search returned unexpected response shape.");
|
|
227
|
+
}
|
|
136
228
|
|
|
137
229
|
/** Execute Exa web search */
|
|
138
230
|
export async function searchExa(params: ExaSearchParams): Promise<SearchResponse> {
|
|
@@ -140,11 +232,7 @@ export async function searchExa(params: ExaSearchParams): Promise<SearchResponse
|
|
|
140
232
|
? await params.authStorage.getApiKey("exa", params.sessionId, { signal: params.signal })
|
|
141
233
|
: undefined;
|
|
142
234
|
const apiKey = storedKey ?? getEnvApiKey("exa");
|
|
143
|
-
|
|
144
|
-
throw new Error("Exa credentials not found. Set EXA_API_KEY or login with 'omp /login exa'.");
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const response = await callExaSearch(apiKey, params);
|
|
235
|
+
const response = apiKey ? await callExaSearch(apiKey, params) : await callExaMcpSearch(params);
|
|
148
236
|
|
|
149
237
|
// Convert to unified SearchResponse
|
|
150
238
|
const sources: SearchSource[] = [];
|
|
@@ -183,14 +271,30 @@ export class ExaProvider extends SearchProvider {
|
|
|
183
271
|
readonly label = "Exa";
|
|
184
272
|
|
|
185
273
|
isAvailable(authStorage: AuthStorage): boolean {
|
|
274
|
+
if (!this.#settingsAllowSearch()) return false;
|
|
275
|
+
return !!getEnvApiKey("exa") || authStorage.hasAuth("exa");
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Exa ships an unauthenticated public MCP fallback, so an explicit
|
|
280
|
+
* selection (programmatic or via `providers.webSearch: exa`) routes
|
|
281
|
+
* through MCP even when no credential is configured. The auto chain
|
|
282
|
+
* still uses {@link isAvailable} so an unrelated configured provider
|
|
283
|
+
* keeps priority over the public fallback.
|
|
284
|
+
*/
|
|
285
|
+
isExplicitlyAvailable(_authStorage: AuthStorage): boolean {
|
|
286
|
+
return this.#settingsAllowSearch();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
#settingsAllowSearch(): boolean {
|
|
186
290
|
try {
|
|
187
291
|
if (settings.get("exa.enabled") === false || settings.get("exa.enableSearch") === false) {
|
|
188
292
|
return false;
|
|
189
293
|
}
|
|
190
294
|
} catch {
|
|
191
|
-
// Settings may be unavailable before CLI initialization;
|
|
295
|
+
// Settings may be unavailable before CLI initialization; assume not disabled.
|
|
192
296
|
}
|
|
193
|
-
return
|
|
297
|
+
return true;
|
|
194
298
|
}
|
|
195
299
|
|
|
196
300
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
@@ -28,7 +28,7 @@ const PERPLEXITY_OAUTH_ASK_URL = "https://www.perplexity.ai/rest/sse/perplexity_
|
|
|
28
28
|
|
|
29
29
|
const DEFAULT_MAX_TOKENS = 8192;
|
|
30
30
|
const DEFAULT_TEMPERATURE = 0.2;
|
|
31
|
-
const DEFAULT_NUM_SEARCH_RESULTS =
|
|
31
|
+
const DEFAULT_NUM_SEARCH_RESULTS = 20;
|
|
32
32
|
const OAUTH_EXPIRY_BUFFER_MS = 5 * 60 * 1000;
|
|
33
33
|
const OAUTH_API_VERSION = "2.18";
|
|
34
34
|
const OAUTH_USER_AGENT = "Perplexity/641 CFNetwork/1568 Darwin/25.2.0";
|
|
@@ -155,11 +155,11 @@ export interface PerplexitySearchParams {
|
|
|
155
155
|
system_prompt?: string;
|
|
156
156
|
search_recency_filter?: "hour" | "day" | "week" | "month" | "year";
|
|
157
157
|
num_results?: number;
|
|
158
|
-
/** Maximum output tokens. Defaults to
|
|
158
|
+
/** Maximum output tokens. Defaults to 8192. */
|
|
159
159
|
max_tokens?: number;
|
|
160
160
|
/** Sampling temperature (0–1). Lower = more focused/factual. Defaults to 0.2. */
|
|
161
161
|
temperature?: number;
|
|
162
|
-
/** Number of search results to retrieve. Defaults to
|
|
162
|
+
/** Number of search results to retrieve. Defaults to 20. */
|
|
163
163
|
num_search_results?: number;
|
|
164
164
|
authStorage: AuthStorage;
|
|
165
165
|
sessionId?: string;
|
|
@@ -470,11 +470,14 @@ function parseResponse(response: PerplexityResponse): SearchResponse {
|
|
|
470
470
|
}
|
|
471
471
|
}
|
|
472
472
|
|
|
473
|
+
const relatedQuestions = (response.related_questions ?? []).filter(q => q.trim().length > 0);
|
|
474
|
+
|
|
473
475
|
return {
|
|
474
476
|
provider: "perplexity",
|
|
475
477
|
answer: answer || undefined,
|
|
476
478
|
sources,
|
|
477
479
|
citations: citations.length > 0 ? citations : undefined,
|
|
480
|
+
relatedQuestions: relatedQuestions.length > 0 ? relatedQuestions : undefined,
|
|
478
481
|
usage: response.usage
|
|
479
482
|
? {
|
|
480
483
|
inputTokens: response.usage.prompt_tokens,
|
|
@@ -532,11 +535,12 @@ export async function searchPerplexity(params: PerplexitySearchParams): Promise<
|
|
|
532
535
|
num_search_results: params.num_search_results ?? DEFAULT_NUM_SEARCH_RESULTS,
|
|
533
536
|
web_search_options: {
|
|
534
537
|
search_type: "pro",
|
|
535
|
-
search_context_size: "
|
|
538
|
+
search_context_size: "high",
|
|
536
539
|
},
|
|
537
540
|
enable_search_classifier: true,
|
|
538
541
|
reasoning_effort: "medium",
|
|
539
542
|
language_preference: "en",
|
|
543
|
+
return_related_questions: true,
|
|
540
544
|
};
|
|
541
545
|
|
|
542
546
|
if (params.search_recency_filter) {
|
package/src/web/search/types.ts
CHANGED
|
@@ -4,44 +4,85 @@
|
|
|
4
4
|
* Unified types for web search responses across supported providers.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
export const SEARCH_PROVIDER_ORDER = [
|
|
8
|
+
"tavily",
|
|
9
|
+
"perplexity",
|
|
10
|
+
"brave",
|
|
11
|
+
"jina",
|
|
12
|
+
"kimi",
|
|
13
|
+
"anthropic",
|
|
14
|
+
"gemini",
|
|
15
|
+
"codex",
|
|
16
|
+
"zai",
|
|
17
|
+
"exa",
|
|
18
|
+
"parallel",
|
|
19
|
+
"kagi",
|
|
20
|
+
"synthetic",
|
|
21
|
+
"searxng",
|
|
22
|
+
] as const;
|
|
23
|
+
|
|
7
24
|
/** Supported web search providers */
|
|
8
|
-
export type SearchProviderId =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
25
|
+
export type SearchProviderId = (typeof SEARCH_PROVIDER_ORDER)[number];
|
|
26
|
+
|
|
27
|
+
export const SEARCH_PROVIDER_PREFERENCES = ["auto", ...SEARCH_PROVIDER_ORDER] as const;
|
|
28
|
+
|
|
29
|
+
export const SEARCH_PROVIDER_OPTIONS = [
|
|
30
|
+
{
|
|
31
|
+
value: "auto",
|
|
32
|
+
label: "Auto",
|
|
33
|
+
description: "Automatically uses the first configured web-search provider",
|
|
34
|
+
},
|
|
35
|
+
{ value: "tavily", label: "Tavily", description: "Requires TAVILY_API_KEY" },
|
|
36
|
+
{ value: "perplexity", label: "Perplexity", description: "Requires PERPLEXITY_COOKIES or PERPLEXITY_API_KEY" },
|
|
37
|
+
{ value: "brave", label: "Brave", description: "Requires BRAVE_API_KEY" },
|
|
38
|
+
{ value: "jina", label: "Jina", description: "Requires JINA_API_KEY" },
|
|
39
|
+
{ value: "kimi", label: "Kimi", description: "Requires MOONSHOT_SEARCH_API_KEY or MOONSHOT_API_KEY" },
|
|
40
|
+
{
|
|
41
|
+
value: "anthropic",
|
|
42
|
+
label: "Anthropic",
|
|
43
|
+
description: "Claude's native web_search tool (uses Anthropic OAuth or ANTHROPIC_API_KEY)",
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
value: "gemini",
|
|
47
|
+
label: "Gemini",
|
|
48
|
+
description: "Google Search grounding via Gemini (uses google-gemini-cli or google-antigravity OAuth)",
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
value: "codex",
|
|
52
|
+
label: "OpenAI",
|
|
53
|
+
description: "OpenAI's native web_search (uses ChatGPT OAuth via /login openai-codex)",
|
|
54
|
+
},
|
|
55
|
+
{ value: "zai", label: "Z.AI", description: "Calls Z.AI webSearchPrime MCP" },
|
|
56
|
+
{ value: "exa", label: "Exa", description: "Uses Exa API when EXA_API_KEY is set; falls back to Exa MCP" },
|
|
57
|
+
{ value: "parallel", label: "Parallel", description: "Requires PARALLEL_API_KEY" },
|
|
58
|
+
{ value: "kagi", label: "Kagi", description: "Requires KAGI_API_KEY and Kagi Search API beta access" },
|
|
59
|
+
{ value: "synthetic", label: "Synthetic", description: "Requires SYNTHETIC_API_KEY" },
|
|
60
|
+
{ value: "searxng", label: "SearXNG", description: "Requires SEARXNG_ENDPOINT or searxng.endpoint" },
|
|
61
|
+
] as const;
|
|
62
|
+
|
|
63
|
+
export const SEARCH_PROVIDER_LABELS: Record<SearchProviderId, string> = {
|
|
64
|
+
tavily: "Tavily",
|
|
65
|
+
perplexity: "Perplexity",
|
|
66
|
+
brave: "Brave",
|
|
67
|
+
jina: "Jina",
|
|
68
|
+
kimi: "Kimi",
|
|
69
|
+
anthropic: "Anthropic",
|
|
70
|
+
gemini: "Gemini",
|
|
71
|
+
codex: "OpenAI",
|
|
72
|
+
zai: "Z.AI",
|
|
73
|
+
exa: "Exa",
|
|
74
|
+
parallel: "Parallel",
|
|
75
|
+
kagi: "Kagi",
|
|
76
|
+
synthetic: "Synthetic",
|
|
77
|
+
searxng: "SearXNG",
|
|
78
|
+
};
|
|
23
79
|
|
|
24
80
|
export function isSearchProviderId(value: string): value is SearchProviderId {
|
|
25
|
-
return
|
|
26
|
-
"exa",
|
|
27
|
-
"brave",
|
|
28
|
-
"jina",
|
|
29
|
-
"kimi",
|
|
30
|
-
"zai",
|
|
31
|
-
"anthropic",
|
|
32
|
-
"perplexity",
|
|
33
|
-
"gemini",
|
|
34
|
-
"codex",
|
|
35
|
-
"tavily",
|
|
36
|
-
"parallel",
|
|
37
|
-
"kagi",
|
|
38
|
-
"synthetic",
|
|
39
|
-
"searxng",
|
|
40
|
-
].includes(value);
|
|
81
|
+
return SEARCH_PROVIDER_ORDER.includes(value as SearchProviderId);
|
|
41
82
|
}
|
|
42
83
|
|
|
43
84
|
export function isSearchProviderPreference(value: string): value is SearchProviderId | "auto" {
|
|
44
|
-
return value
|
|
85
|
+
return SEARCH_PROVIDER_PREFERENCES.includes(value as SearchProviderId | "auto");
|
|
45
86
|
}
|
|
46
87
|
|
|
47
88
|
/** Source returned by search (all providers) */
|
|
@@ -431,6 +472,7 @@ export interface PerplexityResponse {
|
|
|
431
472
|
choices: PerplexityChoice[];
|
|
432
473
|
citations?: string[] | null;
|
|
433
474
|
search_results?: PerplexitySearchResult[] | null;
|
|
475
|
+
related_questions?: string[] | null;
|
|
434
476
|
type?: PerplexityCompletionResponseType | null;
|
|
435
477
|
status?: PerplexityCompletionResponseStatus | null;
|
|
436
478
|
}
|