@oh-my-pi/pi-coding-agent 12.6.0 → 12.7.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/CHANGELOG.md +7 -0
- package/package.json +7 -7
- package/src/cli/update-cli.ts +14 -9
- package/src/cli/web-search-cli.ts +2 -0
- package/src/commands/web-search.ts +1 -0
- package/src/config/model-resolver.ts +102 -3
- package/src/config/settings-schema.ts +1 -1
- package/src/extensibility/extensions/index.ts +9 -0
- package/src/extensibility/extensions/runner.ts +1 -0
- package/src/extensibility/extensions/types.ts +68 -5
- package/src/extensibility/plugins/git-url.ts +18 -7
- package/src/main.ts +45 -20
- package/src/modes/components/footer.ts +10 -18
- package/src/modes/components/settings-defs.ts +7 -1
- package/src/modes/controllers/extension-ui-controller.ts +22 -0
- package/src/modes/controllers/selector-controller.ts +3 -1
- package/src/modes/interactive-mode.ts +2 -0
- package/src/modes/rpc/rpc-mode.ts +5 -0
- package/src/session/agent-session.ts +84 -14
- package/src/task/render.ts +1 -1
- package/src/web/search/index.ts +15 -5
- package/src/web/search/provider.ts +3 -1
- package/src/web/search/providers/zai.ts +352 -0
- package/src/web/search/types.ts +1 -1
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
ExtensionError,
|
|
10
10
|
ExtensionUIContext,
|
|
11
11
|
ExtensionUIDialogOptions,
|
|
12
|
+
TerminalInputHandler,
|
|
12
13
|
} from "../../extensibility/extensions";
|
|
13
14
|
import { HookEditorComponent } from "../../modes/components/hook-editor";
|
|
14
15
|
import { HookInputComponent } from "../../modes/components/hook-input";
|
|
@@ -20,6 +21,7 @@ import { setTerminalTitle } from "../../utils/title-generator";
|
|
|
20
21
|
export class ExtensionUiController {
|
|
21
22
|
#hookSelectorOverlay: OverlayHandle | undefined;
|
|
22
23
|
#hookInputOverlay: OverlayHandle | undefined;
|
|
24
|
+
#extensionTerminalInputUnsubscribers = new Set<() => void>();
|
|
23
25
|
|
|
24
26
|
readonly #dialogOverlayOptions = {
|
|
25
27
|
anchor: "bottom-center",
|
|
@@ -41,6 +43,7 @@ export class ExtensionUiController {
|
|
|
41
43
|
confirm: (title, message, _dialogOptions) => this.showHookConfirm(title, message),
|
|
42
44
|
input: (title, placeholder, _dialogOptions) => this.showHookInput(title, placeholder),
|
|
43
45
|
notify: (message, type) => this.showHookNotify(message, type),
|
|
46
|
+
onTerminalInput: handler => this.addExtensionTerminalInputListener(handler),
|
|
44
47
|
setStatus: (key, text) => this.setHookStatus(key, text),
|
|
45
48
|
setWorkingMessage: message => this.ctx.setWorkingMessage(message),
|
|
46
49
|
setWidget: (key, content) => this.setHookWidget(key, content),
|
|
@@ -157,6 +160,7 @@ export class ExtensionUiController {
|
|
|
157
160
|
this.ctx.statusContainer.clear();
|
|
158
161
|
|
|
159
162
|
// Create new session
|
|
163
|
+
this.clearExtensionTerminalInputListeners();
|
|
160
164
|
const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
|
|
161
165
|
if (!success) {
|
|
162
166
|
return { cancelled: true };
|
|
@@ -351,6 +355,7 @@ export class ExtensionUiController {
|
|
|
351
355
|
this.ctx.statusContainer.clear();
|
|
352
356
|
|
|
353
357
|
// Create new session
|
|
358
|
+
this.clearExtensionTerminalInputListeners();
|
|
354
359
|
const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
|
|
355
360
|
if (!success) {
|
|
356
361
|
return { cancelled: true };
|
|
@@ -450,6 +455,7 @@ export class ExtensionUiController {
|
|
|
450
455
|
confirm: async (_title: string, _message: string, _dialogOptions) => false,
|
|
451
456
|
input: async (_title: string, _placeholder?: string, _dialogOptions?: unknown) => undefined,
|
|
452
457
|
notify: () => {},
|
|
458
|
+
onTerminalInput: () => () => {},
|
|
453
459
|
setStatus: () => {},
|
|
454
460
|
setWorkingMessage: () => {},
|
|
455
461
|
setWidget: () => {},
|
|
@@ -726,6 +732,22 @@ export class ExtensionUiController {
|
|
|
726
732
|
/**
|
|
727
733
|
* Show an extension error in the UI.
|
|
728
734
|
*/
|
|
735
|
+
addExtensionTerminalInputListener(handler: TerminalInputHandler): () => void {
|
|
736
|
+
const unsubscribe = this.ctx.ui.addInputListener(handler);
|
|
737
|
+
this.#extensionTerminalInputUnsubscribers.add(unsubscribe);
|
|
738
|
+
return () => {
|
|
739
|
+
unsubscribe();
|
|
740
|
+
this.#extensionTerminalInputUnsubscribers.delete(unsubscribe);
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
clearExtensionTerminalInputListeners(): void {
|
|
745
|
+
for (const unsubscribe of this.#extensionTerminalInputUnsubscribers) {
|
|
746
|
+
unsubscribe();
|
|
747
|
+
}
|
|
748
|
+
this.#extensionTerminalInputUnsubscribers.clear();
|
|
749
|
+
}
|
|
750
|
+
|
|
729
751
|
showExtensionError(extensionPath: string, error: string): void {
|
|
730
752
|
const errorText = new Text(theme.fg("error", `Extension "${extensionPath}" error: ${error}`), 1, 0);
|
|
731
753
|
this.ctx.chatContainer.addChild(errorText);
|
|
@@ -279,7 +279,9 @@ export class SelectorController {
|
|
|
279
279
|
|
|
280
280
|
// Provider settings - update runtime preferences
|
|
281
281
|
case "webSearchProvider":
|
|
282
|
-
setPreferredSearchProvider(
|
|
282
|
+
setPreferredSearchProvider(
|
|
283
|
+
value as "auto" | "exa" | "jina" | "zai" | "perplexity" | "anthropic" | "gemini" | "codex",
|
|
284
|
+
);
|
|
283
285
|
break;
|
|
284
286
|
case "imageProvider":
|
|
285
287
|
setPreferredImageProvider(value as "auto" | "gemini" | "openrouter");
|
|
@@ -720,6 +720,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
720
720
|
this.#sttController.dispose();
|
|
721
721
|
this.#sttController = undefined;
|
|
722
722
|
}
|
|
723
|
+
this.#extensionUiController.clearExtensionTerminalInputListeners();
|
|
723
724
|
this.statusLine.dispose();
|
|
724
725
|
if (this.unsubscribe) {
|
|
725
726
|
this.unsubscribe();
|
|
@@ -919,6 +920,7 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
919
920
|
}
|
|
920
921
|
|
|
921
922
|
handleClearCommand(): Promise<void> {
|
|
923
|
+
this.#extensionUiController.clearExtensionTerminalInputListeners();
|
|
922
924
|
return this.#commandController.handleClearCommand();
|
|
923
925
|
}
|
|
924
926
|
|
|
@@ -164,6 +164,11 @@ export async function runRpcMode(session: AgentSession): Promise<never> {
|
|
|
164
164
|
);
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
+
onTerminalInput(): () => void {
|
|
168
|
+
// Raw terminal input not supported in RPC mode
|
|
169
|
+
return () => {};
|
|
170
|
+
}
|
|
171
|
+
|
|
167
172
|
notify(message: string, type?: "info" | "warning" | "error"): void {
|
|
168
173
|
// Fire and forget - no response needed
|
|
169
174
|
this.output({
|
|
@@ -51,10 +51,16 @@ import type {
|
|
|
51
51
|
ExtensionCommandContext,
|
|
52
52
|
ExtensionRunner,
|
|
53
53
|
ExtensionUIContext,
|
|
54
|
+
MessageEndEvent,
|
|
55
|
+
MessageStartEvent,
|
|
56
|
+
MessageUpdateEvent,
|
|
54
57
|
SessionBeforeBranchResult,
|
|
55
58
|
SessionBeforeCompactResult,
|
|
56
59
|
SessionBeforeSwitchResult,
|
|
57
60
|
SessionBeforeTreeResult,
|
|
61
|
+
ToolExecutionEndEvent,
|
|
62
|
+
ToolExecutionStartEvent,
|
|
63
|
+
ToolExecutionUpdateEvent,
|
|
58
64
|
TreePreparation,
|
|
59
65
|
TurnEndEvent,
|
|
60
66
|
TurnStartEvent,
|
|
@@ -102,6 +108,7 @@ import {
|
|
|
102
108
|
pythonExecutionToText,
|
|
103
109
|
} from "./messages";
|
|
104
110
|
import type { BranchSummaryEntry, CompactionEntry, NewSessionOptions, SessionManager } from "./session-manager";
|
|
111
|
+
import { getLatestCompactionEntry } from "./session-manager";
|
|
105
112
|
|
|
106
113
|
/** Session-specific events that extend the core AgentEvent */
|
|
107
114
|
export type AgentSessionEvent =
|
|
@@ -226,6 +233,7 @@ const noOpUIContext: ExtensionUIContext = {
|
|
|
226
233
|
confirm: async (_title, _message, _dialogOptions) => false,
|
|
227
234
|
input: async (_title, _placeholder, _dialogOptions) => undefined,
|
|
228
235
|
notify: () => {},
|
|
236
|
+
onTerminalInput: () => () => {},
|
|
229
237
|
setStatus: () => {},
|
|
230
238
|
setWorkingMessage: () => {},
|
|
231
239
|
setWidget: () => {},
|
|
@@ -874,6 +882,51 @@ export class AgentSession {
|
|
|
874
882
|
};
|
|
875
883
|
await this.#extensionRunner.emit(hookEvent);
|
|
876
884
|
this.#turnIndex++;
|
|
885
|
+
} else if (event.type === "message_start") {
|
|
886
|
+
const extensionEvent: MessageStartEvent = {
|
|
887
|
+
type: "message_start",
|
|
888
|
+
message: event.message,
|
|
889
|
+
};
|
|
890
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
891
|
+
} else if (event.type === "message_update") {
|
|
892
|
+
const extensionEvent: MessageUpdateEvent = {
|
|
893
|
+
type: "message_update",
|
|
894
|
+
message: event.message,
|
|
895
|
+
assistantMessageEvent: event.assistantMessageEvent,
|
|
896
|
+
};
|
|
897
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
898
|
+
} else if (event.type === "message_end") {
|
|
899
|
+
const extensionEvent: MessageEndEvent = {
|
|
900
|
+
type: "message_end",
|
|
901
|
+
message: event.message,
|
|
902
|
+
};
|
|
903
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
904
|
+
} else if (event.type === "tool_execution_start") {
|
|
905
|
+
const extensionEvent: ToolExecutionStartEvent = {
|
|
906
|
+
type: "tool_execution_start",
|
|
907
|
+
toolCallId: event.toolCallId,
|
|
908
|
+
toolName: event.toolName,
|
|
909
|
+
args: event.args,
|
|
910
|
+
};
|
|
911
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
912
|
+
} else if (event.type === "tool_execution_update") {
|
|
913
|
+
const extensionEvent: ToolExecutionUpdateEvent = {
|
|
914
|
+
type: "tool_execution_update",
|
|
915
|
+
toolCallId: event.toolCallId,
|
|
916
|
+
toolName: event.toolName,
|
|
917
|
+
args: event.args,
|
|
918
|
+
partialResult: event.partialResult,
|
|
919
|
+
};
|
|
920
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
921
|
+
} else if (event.type === "tool_execution_end") {
|
|
922
|
+
const extensionEvent: ToolExecutionEndEvent = {
|
|
923
|
+
type: "tool_execution_end",
|
|
924
|
+
toolCallId: event.toolCallId,
|
|
925
|
+
toolName: event.toolName,
|
|
926
|
+
result: event.result,
|
|
927
|
+
isError: event.isError ?? false,
|
|
928
|
+
};
|
|
929
|
+
await this.#extensionRunner.emit(extensionEvent);
|
|
877
930
|
} else if (event.type === "auto_compaction_start") {
|
|
878
931
|
await this.#extensionRunner.emit({ type: "auto_compaction_start", reason: event.reason });
|
|
879
932
|
} else if (event.type === "auto_compaction_end") {
|
|
@@ -2664,9 +2717,9 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
2664
2717
|
// The error shouldn't trigger another compaction since we already compacted.
|
|
2665
2718
|
// Example: opus fails → switch to codex → compact → switch back to opus → opus error
|
|
2666
2719
|
// is still in context but shouldn't trigger compaction again.
|
|
2667
|
-
const compactionEntry = this.sessionManager.getBranch()
|
|
2720
|
+
const compactionEntry = getLatestCompactionEntry(this.sessionManager.getBranch());
|
|
2668
2721
|
const errorIsFromBeforeCompaction =
|
|
2669
|
-
compactionEntry && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
|
|
2722
|
+
compactionEntry !== null && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
|
|
2670
2723
|
|
|
2671
2724
|
// Case 1: Overflow - LLM returned context overflow error
|
|
2672
2725
|
if (sameModel && !errorIsFromBeforeCompaction && isContextOverflow(assistantMessage, contextWindow)) {
|
|
@@ -3959,6 +4012,35 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3959
4012
|
const contextWindow = model.contextWindow ?? 0;
|
|
3960
4013
|
if (contextWindow <= 0) return undefined;
|
|
3961
4014
|
|
|
4015
|
+
// After compaction, the last assistant usage reflects pre-compaction context size.
|
|
4016
|
+
// We can only trust usage from an assistant that responded after the latest compaction.
|
|
4017
|
+
// If no such assistant exists, context token count is unknown until the next LLM response.
|
|
4018
|
+
const branchEntries = this.sessionManager.getBranch();
|
|
4019
|
+
const latestCompaction = getLatestCompactionEntry(branchEntries);
|
|
4020
|
+
|
|
4021
|
+
if (latestCompaction) {
|
|
4022
|
+
// Check if there's a valid assistant usage after the compaction boundary
|
|
4023
|
+
const compactionIndex = branchEntries.lastIndexOf(latestCompaction);
|
|
4024
|
+
let hasPostCompactionUsage = false;
|
|
4025
|
+
for (let i = branchEntries.length - 1; i > compactionIndex; i--) {
|
|
4026
|
+
const entry = branchEntries[i];
|
|
4027
|
+
if (entry.type === "message" && entry.message.role === "assistant") {
|
|
4028
|
+
const assistant = entry.message;
|
|
4029
|
+
if (assistant.stopReason !== "aborted" && assistant.stopReason !== "error") {
|
|
4030
|
+
const contextTokens = calculateContextTokens(assistant.usage);
|
|
4031
|
+
if (contextTokens > 0) {
|
|
4032
|
+
hasPostCompactionUsage = true;
|
|
4033
|
+
}
|
|
4034
|
+
break;
|
|
4035
|
+
}
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
|
|
4039
|
+
if (!hasPostCompactionUsage) {
|
|
4040
|
+
return { tokens: null, contextWindow, percent: null };
|
|
4041
|
+
}
|
|
4042
|
+
}
|
|
4043
|
+
|
|
3962
4044
|
const estimate = this.#estimateContextTokens();
|
|
3963
4045
|
const percent = (estimate.tokens / contextWindow) * 100;
|
|
3964
4046
|
|
|
@@ -3966,9 +4048,6 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3966
4048
|
tokens: estimate.tokens,
|
|
3967
4049
|
contextWindow,
|
|
3968
4050
|
percent,
|
|
3969
|
-
usageTokens: estimate.usageTokens,
|
|
3970
|
-
trailingTokens: estimate.trailingTokens,
|
|
3971
|
-
lastUsageIndex: estimate.lastUsageIndex,
|
|
3972
4051
|
};
|
|
3973
4052
|
}
|
|
3974
4053
|
|
|
@@ -3985,9 +4064,6 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
3985
4064
|
*/
|
|
3986
4065
|
#estimateContextTokens(): {
|
|
3987
4066
|
tokens: number;
|
|
3988
|
-
usageTokens: number;
|
|
3989
|
-
trailingTokens: number;
|
|
3990
|
-
lastUsageIndex: number | null;
|
|
3991
4067
|
} {
|
|
3992
4068
|
const messages = this.messages;
|
|
3993
4069
|
|
|
@@ -4014,9 +4090,6 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
4014
4090
|
}
|
|
4015
4091
|
return {
|
|
4016
4092
|
tokens: estimated,
|
|
4017
|
-
usageTokens: 0,
|
|
4018
|
-
trailingTokens: estimated,
|
|
4019
|
-
lastUsageIndex: null,
|
|
4020
4093
|
};
|
|
4021
4094
|
}
|
|
4022
4095
|
|
|
@@ -4028,9 +4101,6 @@ Be thorough - include exact file paths, function names, error messages, and tech
|
|
|
4028
4101
|
|
|
4029
4102
|
return {
|
|
4030
4103
|
tokens: usageTokens + trailingTokens,
|
|
4031
|
-
usageTokens,
|
|
4032
|
-
trailingTokens,
|
|
4033
|
-
lastUsageIndex,
|
|
4034
4104
|
};
|
|
4035
4105
|
}
|
|
4036
4106
|
|
package/src/task/render.ts
CHANGED
|
@@ -684,7 +684,7 @@ function renderFindings(
|
|
|
684
684
|
const findingContinue = isLastFinding ? " " : `${theme.tree.vertical} `;
|
|
685
685
|
|
|
686
686
|
const { color } = getPriorityInfo(finding.priority);
|
|
687
|
-
const titleText = finding.title
|
|
687
|
+
const titleText = finding.title?.replace(/^\[P\d\]\s*/, "") ?? "Untitled";
|
|
688
688
|
const loc = `${path.basename(finding.file_path)}:${finding.line_start}`;
|
|
689
689
|
|
|
690
690
|
lines.push(
|
package/src/web/search/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified Web Search Tool
|
|
3
3
|
*
|
|
4
|
-
* Single tool supporting Anthropic, Perplexity, Exa, Jina, Gemini, and
|
|
4
|
+
* Single tool supporting Anthropic, Perplexity, Exa, Jina, Gemini, Codex, and Z.AI
|
|
5
5
|
* providers with provider-specific parameters exposed conditionally.
|
|
6
6
|
*
|
|
7
7
|
* When EXA_API_KEY is available, additional specialized tools are exposed:
|
|
@@ -33,7 +33,7 @@ import { SearchProviderError } from "./types";
|
|
|
33
33
|
export const webSearchSchema = Type.Object({
|
|
34
34
|
query: Type.String({ description: "Search query" }),
|
|
35
35
|
provider: Type.Optional(
|
|
36
|
-
StringEnum(["auto", "exa", "jina", "anthropic", "perplexity", "gemini", "codex"], {
|
|
36
|
+
StringEnum(["auto", "exa", "jina", "zai", "anthropic", "perplexity", "gemini", "codex"], {
|
|
37
37
|
description: "Search provider (default: auto)",
|
|
38
38
|
}),
|
|
39
39
|
),
|
|
@@ -47,7 +47,7 @@ export const webSearchSchema = Type.Object({
|
|
|
47
47
|
|
|
48
48
|
export type SearchParams = {
|
|
49
49
|
query: string;
|
|
50
|
-
provider?: "auto" | "exa" | "jina" | "anthropic" | "perplexity" | "gemini" | "codex";
|
|
50
|
+
provider?: "auto" | "exa" | "jina" | "zai" | "anthropic" | "perplexity" | "gemini" | "codex";
|
|
51
51
|
recency?: "day" | "week" | "month" | "year";
|
|
52
52
|
limit?: number;
|
|
53
53
|
/** Maximum output tokens. Defaults to 4096. */
|
|
@@ -56,6 +56,8 @@ export type SearchParams = {
|
|
|
56
56
|
temperature?: number;
|
|
57
57
|
/** Number of search results to retrieve. Defaults to 10. */
|
|
58
58
|
num_search_results?: number;
|
|
59
|
+
/** Disable provider fallback when explicit provider is selected (CLI/debug use). */
|
|
60
|
+
no_fallback?: boolean;
|
|
59
61
|
};
|
|
60
62
|
|
|
61
63
|
function formatProviderList(providers: SearchProvider[]): string {
|
|
@@ -68,6 +70,9 @@ function formatProviderError(error: unknown, provider: SearchProvider): string {
|
|
|
68
70
|
return "Anthropic web search returned 404 (model or endpoint not found).";
|
|
69
71
|
}
|
|
70
72
|
if (error.status === 401 || error.status === 403) {
|
|
73
|
+
if (error.provider === "zai") {
|
|
74
|
+
return error.message;
|
|
75
|
+
}
|
|
71
76
|
return `${getSearchProvider(error.provider).label} authorization failed (${error.status}). Check API key or base URL.`;
|
|
72
77
|
}
|
|
73
78
|
return error.message;
|
|
@@ -165,7 +170,12 @@ async function executeSearch(
|
|
|
165
170
|
_toolCallId: string,
|
|
166
171
|
params: SearchParams,
|
|
167
172
|
): Promise<{ content: Array<{ type: "text"; text: string }>; details: SearchRenderDetails }> {
|
|
168
|
-
const providers =
|
|
173
|
+
const providers =
|
|
174
|
+
params.provider && params.provider !== "auto" && params.no_fallback
|
|
175
|
+
? (await getSearchProvider(params.provider).isAvailable())
|
|
176
|
+
? [getSearchProvider(params.provider)]
|
|
177
|
+
: []
|
|
178
|
+
: await resolveProviderChain(params.provider);
|
|
169
179
|
|
|
170
180
|
if (providers.length === 0) {
|
|
171
181
|
const message = "No web search provider configured.";
|
|
@@ -226,7 +236,7 @@ export async function runSearchQuery(
|
|
|
226
236
|
/**
|
|
227
237
|
* Web search tool implementation.
|
|
228
238
|
*
|
|
229
|
-
* Supports Anthropic, Perplexity, Exa, Jina, Gemini, and
|
|
239
|
+
* Supports Anthropic, Perplexity, Exa, Jina, Gemini, Codex, and Z.AI providers with automatic fallback.
|
|
230
240
|
* Session is accepted for interface consistency but not used.
|
|
231
241
|
*/
|
|
232
242
|
export class SearchTool implements AgentTool<typeof webSearchSchema, SearchRenderDetails> {
|
|
@@ -5,6 +5,7 @@ import { ExaProvider } from "./providers/exa";
|
|
|
5
5
|
import { GeminiProvider } from "./providers/gemini";
|
|
6
6
|
import { JinaProvider } from "./providers/jina";
|
|
7
7
|
import { PerplexityProvider } from "./providers/perplexity";
|
|
8
|
+
import { ZaiProvider } from "./providers/zai";
|
|
8
9
|
import type { SearchProviderId } from "./types";
|
|
9
10
|
|
|
10
11
|
export type { SearchParams } from "./providers/base";
|
|
@@ -14,12 +15,13 @@ const SEARCH_PROVIDERS: Record<SearchProviderId, SearchProvider> = {
|
|
|
14
15
|
exa: new ExaProvider(),
|
|
15
16
|
jina: new JinaProvider(),
|
|
16
17
|
perplexity: new PerplexityProvider(),
|
|
18
|
+
zai: new ZaiProvider(),
|
|
17
19
|
anthropic: new AnthropicProvider(),
|
|
18
20
|
gemini: new GeminiProvider(),
|
|
19
21
|
codex: new CodexProvider(),
|
|
20
22
|
} as const;
|
|
21
23
|
|
|
22
|
-
const SEARCH_PROVIDER_ORDER: SearchProviderId[] = ["exa", "jina", "perplexity", "anthropic", "gemini", "codex"];
|
|
24
|
+
const SEARCH_PROVIDER_ORDER: SearchProviderId[] = ["exa", "jina", "perplexity", "anthropic", "gemini", "codex", "zai"];
|
|
23
25
|
|
|
24
26
|
export function getSearchProvider(provider: SearchProviderId): SearchProvider {
|
|
25
27
|
return SEARCH_PROVIDERS[provider];
|