@oh-my-pi/pi-coding-agent 15.3.2 → 15.4.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 +104 -0
- package/dist/types/cli/file-processor.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +45 -3
- package/dist/types/config/settings.d.ts +1 -1
- package/dist/types/debug/raw-sse.d.ts +2 -0
- package/dist/types/edit/file-read-cache.d.ts +15 -4
- package/dist/types/edit/index.d.ts +3 -8
- package/dist/types/edit/renderer.d.ts +1 -2
- package/dist/types/eval/__tests__/shared-executors.test.d.ts +1 -0
- package/dist/types/eval/js/shared/local-module-loader.d.ts +16 -0
- package/dist/types/eval/js/shared/rewrite-imports.d.ts +4 -0
- package/dist/types/eval/js/shared/runtime.d.ts +14 -8
- package/dist/types/eval/py/executor.d.ts +1 -2
- package/dist/types/eval/py/kernel.d.ts +6 -0
- package/dist/types/eval/py/tool-bridge.d.ts +1 -5
- package/dist/types/eval/session-id.d.ts +3 -0
- package/dist/types/extensibility/extensions/types.d.ts +1 -3
- package/dist/types/hashline/anchors.d.ts +15 -9
- package/dist/types/hashline/constants.d.ts +0 -2
- package/dist/types/hashline/diff.d.ts +1 -2
- package/dist/types/hashline/executor.d.ts +52 -0
- package/dist/types/hashline/hash.d.ts +44 -93
- package/dist/types/hashline/index.d.ts +2 -1
- package/dist/types/hashline/input.d.ts +2 -9
- package/dist/types/hashline/recovery.d.ts +3 -9
- package/dist/types/hashline/tokenizer.d.ts +91 -0
- package/dist/types/hashline/types.d.ts +5 -7
- package/dist/types/modes/components/extensions/types.d.ts +0 -4
- package/dist/types/modes/types.d.ts +1 -0
- package/dist/types/modes/utils/ui-helpers.d.ts +1 -0
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +11 -15
- package/dist/types/session/agent-storage.d.ts +11 -10
- package/dist/types/slash-commands/acp-builtins.d.ts +3 -3
- package/dist/types/slash-commands/types.d.ts +0 -5
- package/dist/types/task/executor.d.ts +2 -0
- package/dist/types/tool-discovery/tool-index.d.ts +0 -50
- package/dist/types/tools/index.d.ts +2 -8
- package/dist/types/tools/match-line-format.d.ts +4 -4
- package/dist/types/tools/output-schema-validator.d.ts +64 -0
- package/dist/types/tools/review.d.ts +13 -0
- package/dist/types/tools/search-tool-bm25.d.ts +1 -1
- package/dist/types/tools/search.d.ts +4 -3
- package/dist/types/utils/edit-mode.d.ts +1 -1
- package/dist/types/web/kagi.d.ts +4 -2
- package/dist/types/web/parallel.d.ts +4 -3
- package/dist/types/web/scrapers/types.d.ts +2 -1
- package/dist/types/web/search/index.d.ts +12 -4
- package/dist/types/web/search/provider.d.ts +2 -1
- package/dist/types/web/search/providers/anthropic.d.ts +9 -4
- package/dist/types/web/search/providers/base.d.ts +34 -2
- package/dist/types/web/search/providers/brave.d.ts +8 -1
- package/dist/types/web/search/providers/codex.d.ts +13 -9
- package/dist/types/web/search/providers/exa.d.ts +10 -1
- package/dist/types/web/search/providers/gemini.d.ts +20 -23
- package/dist/types/web/search/providers/jina.d.ts +2 -1
- package/dist/types/web/search/providers/kagi.d.ts +4 -1
- package/dist/types/web/search/providers/kimi.d.ts +10 -1
- package/dist/types/web/search/providers/parallel.d.ts +3 -2
- package/dist/types/web/search/providers/perplexity.d.ts +5 -2
- package/dist/types/web/search/providers/searxng.d.ts +2 -1
- package/dist/types/web/search/providers/synthetic.d.ts +5 -8
- package/dist/types/web/search/providers/tavily.d.ts +11 -4
- package/dist/types/web/search/providers/utils.d.ts +8 -6
- package/dist/types/web/search/providers/zai.d.ts +12 -3
- package/package.json +7 -7
- package/src/cli/file-processor.ts +12 -2
- package/src/cli.ts +0 -8
- package/src/commands/commit.ts +8 -8
- package/src/config/prompt-templates.ts +6 -6
- package/src/config/settings-schema.ts +47 -3
- package/src/config/settings.ts +5 -5
- package/src/debug/raw-sse.ts +68 -3
- package/src/edit/file-read-cache.ts +68 -25
- package/src/edit/index.ts +6 -37
- package/src/edit/renderer.ts +9 -47
- package/src/edit/streaming.ts +43 -56
- package/src/eval/__tests__/shared-executors.test.ts +520 -0
- package/src/eval/js/context-manager.ts +64 -53
- package/src/eval/js/shared/local-module-loader.ts +265 -0
- package/src/eval/js/shared/prelude.txt +4 -0
- package/src/eval/js/shared/rewrite-imports.ts +85 -0
- package/src/eval/js/shared/runtime.ts +129 -86
- package/src/eval/js/worker-core.ts +23 -38
- package/src/eval/py/executor.ts +155 -84
- package/src/eval/py/kernel.ts +10 -1
- package/src/eval/py/prelude.py +22 -24
- package/src/eval/py/runner.py +203 -85
- package/src/eval/py/tool-bridge.ts +17 -10
- package/src/eval/session-id.ts +8 -0
- package/src/exec/bash-executor.ts +27 -16
- package/src/extensibility/extensions/runner.ts +0 -1
- package/src/extensibility/extensions/types.ts +1 -3
- package/src/hashline/anchors.ts +56 -65
- package/src/hashline/apply.ts +29 -31
- package/src/hashline/constants.ts +0 -3
- package/src/hashline/diff-preview.ts +4 -5
- package/src/hashline/diff.ts +30 -4
- package/src/hashline/execute.ts +91 -26
- package/src/hashline/executor.ts +239 -0
- package/src/hashline/grammar.lark +12 -10
- package/src/hashline/hash.ts +69 -114
- package/src/hashline/index.ts +2 -1
- package/src/hashline/input.ts +48 -41
- package/src/hashline/prefixes.ts +21 -11
- package/src/hashline/recovery.ts +63 -71
- package/src/hashline/stream.ts +2 -2
- package/src/hashline/tokenizer.ts +467 -0
- package/src/hashline/types.ts +6 -8
- package/src/internal-urls/docs-index.generated.ts +7 -7
- package/src/modes/components/extensions/types.ts +0 -5
- package/src/modes/components/session-observer-overlay.ts +11 -2
- package/src/modes/components/tree-selector.ts +10 -2
- package/src/modes/controllers/command-controller.ts +1 -3
- package/src/modes/controllers/extension-ui-controller.ts +10 -11
- package/src/modes/controllers/selector-controller.ts +5 -5
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +4 -0
- package/src/prompts/agents/explore.md +1 -1
- package/src/prompts/tools/ast-edit.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/eval.md +1 -1
- package/src/prompts/tools/hashline.md +73 -94
- package/src/prompts/tools/read.md +4 -4
- package/src/prompts/tools/search.md +3 -3
- package/src/sdk.ts +17 -23
- package/src/session/agent-session.ts +59 -66
- package/src/session/agent-storage.ts +13 -14
- package/src/slash-commands/acp-builtins.ts +3 -3
- package/src/slash-commands/types.ts +0 -6
- package/src/task/executor.ts +26 -57
- package/src/task/index.ts +8 -4
- package/src/tool-discovery/tool-index.ts +0 -134
- package/src/tools/ast-edit.ts +36 -13
- package/src/tools/ast-grep.ts +45 -4
- package/src/tools/browser/tab-worker.ts +3 -2
- package/src/tools/eval.ts +2 -1
- package/src/tools/fetch.ts +23 -14
- package/src/tools/index.ts +2 -8
- package/src/tools/irc.ts +59 -5
- package/src/tools/match-line-format.ts +5 -7
- package/src/tools/output-schema-validator.ts +132 -0
- package/src/tools/read.ts +142 -31
- package/src/tools/review.ts +23 -0
- package/src/tools/search-tool-bm25.ts +3 -30
- package/src/tools/search.ts +48 -16
- package/src/tools/write.ts +3 -3
- package/src/tools/yield.ts +32 -41
- package/src/utils/edit-mode.ts +1 -2
- package/src/utils/file-mentions.ts +2 -2
- package/src/web/kagi.ts +15 -6
- package/src/web/parallel.ts +9 -6
- package/src/web/scrapers/types.ts +7 -1
- package/src/web/scrapers/youtube.ts +13 -7
- package/src/web/search/index.ts +37 -11
- package/src/web/search/provider.ts +5 -3
- package/src/web/search/providers/anthropic.ts +30 -21
- package/src/web/search/providers/base.ts +35 -2
- package/src/web/search/providers/brave.ts +4 -4
- package/src/web/search/providers/codex.ts +118 -89
- package/src/web/search/providers/exa.ts +3 -2
- package/src/web/search/providers/gemini.ts +58 -155
- package/src/web/search/providers/jina.ts +4 -4
- package/src/web/search/providers/kagi.ts +17 -11
- package/src/web/search/providers/kimi.ts +29 -13
- package/src/web/search/providers/parallel.ts +171 -23
- package/src/web/search/providers/perplexity.ts +38 -37
- package/src/web/search/providers/searxng.ts +3 -1
- package/src/web/search/providers/synthetic.ts +16 -19
- package/src/web/search/providers/tavily.ts +23 -18
- package/src/web/search/providers/utils.ts +11 -17
- package/src/web/search/providers/zai.ts +16 -8
- package/dist/types/hashline/parser.d.ts +0 -7
- package/dist/types/mcp/discoverable-tool-metadata.d.ts +0 -7
- package/dist/types/tools/vim.d.ts +0 -58
- package/dist/types/vim/buffer.d.ts +0 -41
- package/dist/types/vim/commands.d.ts +0 -6
- package/dist/types/vim/engine.d.ts +0 -47
- package/dist/types/vim/parser.d.ts +0 -3
- package/dist/types/vim/render.d.ts +0 -25
- package/dist/types/vim/types.d.ts +0 -182
- package/src/hashline/parser.ts +0 -246
- package/src/mcp/discoverable-tool-metadata.ts +0 -24
- package/src/prompts/tools/vim.md +0 -98
- package/src/tools/vim.ts +0 -949
- package/src/vim/buffer.ts +0 -309
- package/src/vim/commands.ts +0 -382
- package/src/vim/engine.ts +0 -2409
- package/src/vim/parser.ts +0 -134
- package/src/vim/render.ts +0 -252
- package/src/vim/types.ts +0 -197
|
@@ -2,15 +2,20 @@
|
|
|
2
2
|
* Google Gemini Web Search Provider
|
|
3
3
|
*
|
|
4
4
|
* Uses Gemini's Google Search grounding via Cloud Code Assist API.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Auth is resolved through `AuthStorage.getOAuthAccess(...)` for both
|
|
6
|
+
* `google-gemini-cli` (stable prod) and `google-antigravity` (daily sandbox)
|
|
7
|
+
* — the broker is the sole refresh authority, so this module never opens a
|
|
8
|
+
* sibling SQLite store and never POSTs the broker sentinel to a Google token
|
|
9
|
+
* endpoint.
|
|
7
10
|
*/
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
import {
|
|
12
|
+
ANTIGRAVITY_SYSTEM_INSTRUCTION,
|
|
13
|
+
type AuthStorage,
|
|
14
|
+
getAntigravityUserAgent,
|
|
15
|
+
getGeminiCliHeaders,
|
|
16
|
+
} from "@oh-my-pi/pi-ai";
|
|
17
|
+
import { fetchWithRetry } from "@oh-my-pi/pi-utils";
|
|
12
18
|
|
|
13
|
-
import { AgentStorage } from "../../../session/agent-storage";
|
|
14
19
|
import type { SearchCitation, SearchResponse, SearchSource } from "../../../web/search/types";
|
|
15
20
|
import { SearchProviderError } from "../../../web/search/types";
|
|
16
21
|
import type { SearchParams } from "./base";
|
|
@@ -26,6 +31,9 @@ const MAX_RETRIES = 3;
|
|
|
26
31
|
const BASE_DELAY_MS = 1000;
|
|
27
32
|
const RATE_LIMIT_BUDGET_MS = 5 * 60 * 1000;
|
|
28
33
|
|
|
34
|
+
const GEMINI_PROVIDERS = ["google-gemini-cli", "google-antigravity"] as const;
|
|
35
|
+
type GeminiProviderId = (typeof GEMINI_PROVIDERS)[number];
|
|
36
|
+
|
|
29
37
|
interface GeminiToolParams {
|
|
30
38
|
google_search?: Record<string, unknown>;
|
|
31
39
|
code_execution?: Record<string, unknown>;
|
|
@@ -41,6 +49,8 @@ export interface GeminiSearchParams extends GeminiToolParams {
|
|
|
41
49
|
/** Sampling temperature (0–1). Lower = more focused/factual. */
|
|
42
50
|
temperature?: number;
|
|
43
51
|
signal?: AbortSignal;
|
|
52
|
+
authStorage: AuthStorage;
|
|
53
|
+
sessionId?: string;
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
export function buildGeminiRequestTools(params: GeminiToolParams): Array<Record<string, Record<string, unknown>>> {
|
|
@@ -54,131 +64,40 @@ export function buildGeminiRequestTools(params: GeminiToolParams): Array<Record<
|
|
|
54
64
|
return tools;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
/**
|
|
58
|
-
interface GeminiOAuthCredential {
|
|
59
|
-
type: "oauth";
|
|
60
|
-
access: string;
|
|
61
|
-
refresh?: string;
|
|
62
|
-
expires: number;
|
|
63
|
-
projectId?: string;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/** Auth info for Gemini API requests */
|
|
67
|
+
/** Resolved auth for a Gemini API request. */
|
|
67
68
|
interface GeminiAuth {
|
|
68
69
|
accessToken: string;
|
|
69
|
-
refreshToken?: string;
|
|
70
70
|
projectId: string;
|
|
71
71
|
isAntigravity: boolean;
|
|
72
|
-
storage: AgentStorage;
|
|
73
|
-
credentialId: number;
|
|
74
|
-
credential: GeminiOAuthCredential;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
async function refreshGeminiAuth(auth: GeminiAuth): Promise<boolean> {
|
|
78
|
-
if (!auth.refreshToken) return false;
|
|
79
|
-
try {
|
|
80
|
-
const refreshed = auth.isAntigravity
|
|
81
|
-
? await refreshAntigravityToken(auth.refreshToken, auth.projectId)
|
|
82
|
-
: await refreshGoogleCloudToken(auth.refreshToken, auth.projectId);
|
|
83
|
-
auth.accessToken = refreshed.access;
|
|
84
|
-
auth.refreshToken = refreshed.refresh ?? auth.refreshToken;
|
|
85
|
-
auth.storage.updateAuthCredential(auth.credentialId, {
|
|
86
|
-
...auth.credential,
|
|
87
|
-
access: auth.accessToken,
|
|
88
|
-
refresh: auth.refreshToken,
|
|
89
|
-
expires: refreshed.expires,
|
|
90
|
-
});
|
|
91
|
-
auth.credential.access = auth.accessToken;
|
|
92
|
-
auth.credential.refresh = auth.refreshToken;
|
|
93
|
-
auth.credential.expires = refreshed.expires;
|
|
94
|
-
return true;
|
|
95
|
-
} catch {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
72
|
}
|
|
99
73
|
|
|
100
74
|
/**
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* @
|
|
75
|
+
* Walks the configured Gemini OAuth providers in deterministic order and
|
|
76
|
+
* returns the first one that yields a usable access token + projectId via
|
|
77
|
+
* {@link AuthStorage.getOAuthAccess}. AuthStorage handles refresh + broker
|
|
78
|
+
* routing internally; this helper never touches refresh tokens directly.
|
|
104
79
|
*/
|
|
105
|
-
export async function findGeminiAuth(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
for (const record of records) {
|
|
119
|
-
const credential = record.credential;
|
|
120
|
-
if (credential.type !== "oauth") continue;
|
|
121
|
-
|
|
122
|
-
const oauthCred = credential as GeminiOAuthCredential;
|
|
123
|
-
if (!oauthCred.access) continue;
|
|
124
|
-
|
|
125
|
-
// Get projectId from credential
|
|
126
|
-
const projectId = oauthCred.projectId;
|
|
127
|
-
if (!projectId) continue;
|
|
128
|
-
|
|
129
|
-
// Check if token is expired (or about to expire)
|
|
130
|
-
if (oauthCred.expires <= now + expiryBuffer) {
|
|
131
|
-
// Try to refresh if we have a refresh token
|
|
132
|
-
if (oauthCred.refresh) {
|
|
133
|
-
try {
|
|
134
|
-
const refreshed =
|
|
135
|
-
provider === "google-antigravity"
|
|
136
|
-
? await refreshAntigravityToken(oauthCred.refresh, projectId)
|
|
137
|
-
: await refreshGoogleCloudToken(oauthCred.refresh, projectId);
|
|
138
|
-
// Update the credential in storage
|
|
139
|
-
const updated = {
|
|
140
|
-
...oauthCred,
|
|
141
|
-
access: refreshed.access,
|
|
142
|
-
refresh: refreshed.refresh ?? oauthCred.refresh,
|
|
143
|
-
expires: refreshed.expires,
|
|
144
|
-
};
|
|
145
|
-
storage.updateAuthCredential(record.id, updated);
|
|
146
|
-
return {
|
|
147
|
-
accessToken: refreshed.access,
|
|
148
|
-
refreshToken: refreshed.refresh ?? oauthCred.refresh,
|
|
149
|
-
projectId,
|
|
150
|
-
isAntigravity: provider === "google-antigravity",
|
|
151
|
-
storage,
|
|
152
|
-
credentialId: record.id,
|
|
153
|
-
credential: updated,
|
|
154
|
-
};
|
|
155
|
-
} catch {
|
|
156
|
-
// Refresh failed, skip this credential
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
// No refresh token or refresh failed
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return {
|
|
165
|
-
accessToken: oauthCred.access,
|
|
166
|
-
refreshToken: oauthCred.refresh,
|
|
167
|
-
projectId,
|
|
168
|
-
isAntigravity: provider === "google-antigravity",
|
|
169
|
-
storage,
|
|
170
|
-
credentialId: record.id,
|
|
171
|
-
credential: oauthCred,
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
} catch {
|
|
176
|
-
return null;
|
|
80
|
+
export async function findGeminiAuth(
|
|
81
|
+
authStorage: AuthStorage,
|
|
82
|
+
sessionId: string | undefined,
|
|
83
|
+
signal: AbortSignal | undefined,
|
|
84
|
+
): Promise<GeminiAuth | null> {
|
|
85
|
+
for (const provider of GEMINI_PROVIDERS) {
|
|
86
|
+
const access = await authStorage.getOAuthAccess(provider, sessionId, { signal });
|
|
87
|
+
if (!access?.accessToken || !access.projectId) continue;
|
|
88
|
+
return {
|
|
89
|
+
accessToken: access.accessToken,
|
|
90
|
+
projectId: access.projectId,
|
|
91
|
+
isAntigravity: provider === "google-antigravity",
|
|
92
|
+
};
|
|
177
93
|
}
|
|
178
|
-
|
|
179
94
|
return null;
|
|
180
95
|
}
|
|
181
96
|
|
|
97
|
+
function hasGeminiOAuth(authStorage: AuthStorage): boolean {
|
|
98
|
+
return GEMINI_PROVIDERS.some((provider: GeminiProviderId) => authStorage.hasOAuth(provider));
|
|
99
|
+
}
|
|
100
|
+
|
|
182
101
|
/** Cloud Code Assist API response types */
|
|
183
102
|
interface GeminiGroundingChunk {
|
|
184
103
|
web?: {
|
|
@@ -224,20 +143,20 @@ interface CloudCodeResponseChunk {
|
|
|
224
143
|
|
|
225
144
|
/**
|
|
226
145
|
* Calls the Cloud Code Assist API with Google Search grounding enabled.
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
*
|
|
146
|
+
*
|
|
147
|
+
* If a request returns a refreshable auth failure (401/403/auth-flavoured 400),
|
|
148
|
+
* we ask AuthStorage to invalidate + refresh the credential and retry once.
|
|
149
|
+
* Provider-direct refresh helpers are intentionally not used: AuthStorage owns
|
|
150
|
+
* the single-flight refresh and broker round-trip.
|
|
232
151
|
*/
|
|
233
152
|
async function callGeminiSearch(
|
|
234
153
|
auth: GeminiAuth,
|
|
235
154
|
query: string,
|
|
236
|
-
systemPrompt
|
|
237
|
-
maxOutputTokens
|
|
238
|
-
temperature
|
|
239
|
-
toolParams: GeminiToolParams
|
|
240
|
-
signal
|
|
155
|
+
systemPrompt: string | undefined,
|
|
156
|
+
maxOutputTokens: number | undefined,
|
|
157
|
+
temperature: number | undefined,
|
|
158
|
+
toolParams: GeminiToolParams,
|
|
159
|
+
signal: AbortSignal | undefined,
|
|
241
160
|
): Promise<{
|
|
242
161
|
answer: string;
|
|
243
162
|
sources: SearchSource[];
|
|
@@ -316,29 +235,13 @@ async function callGeminiSearch(
|
|
|
316
235
|
const urlFor = (attempt: number) =>
|
|
317
236
|
`${endpoints[Math.min(attempt, endpoints.length - 1)]}/v1internal:streamGenerateContent?alt=sse`;
|
|
318
237
|
|
|
319
|
-
|
|
238
|
+
const response = await fetchWithRetry(urlFor, {
|
|
320
239
|
...buildInit(),
|
|
321
240
|
maxAttempts: MAX_RETRIES + 1,
|
|
322
241
|
defaultDelayMs: attempt => BASE_DELAY_MS * 2 ** attempt,
|
|
323
242
|
maxDelayMs: RATE_LIMIT_BUDGET_MS,
|
|
324
243
|
});
|
|
325
244
|
|
|
326
|
-
if (!response.ok) {
|
|
327
|
-
const errorText = await response.clone().text();
|
|
328
|
-
const canRefreshAuth =
|
|
329
|
-
response.status === 401 ||
|
|
330
|
-
response.status === 403 ||
|
|
331
|
-
(response.status === 400 && /api key not valid|invalid credentials|invalid authentication/i.test(errorText));
|
|
332
|
-
if (canRefreshAuth && (await refreshGeminiAuth(auth))) {
|
|
333
|
-
response = await fetchWithRetry(urlFor, {
|
|
334
|
-
...buildInit(),
|
|
335
|
-
maxAttempts: MAX_RETRIES + 1,
|
|
336
|
-
defaultDelayMs: attempt => BASE_DELAY_MS * 2 ** attempt,
|
|
337
|
-
maxDelayMs: RATE_LIMIT_BUDGET_MS,
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
245
|
if (!response.ok) {
|
|
343
246
|
const errorText = await response.text();
|
|
344
247
|
const classified = classifyProviderHttpError("gemini", response.status, errorText);
|
|
@@ -482,13 +385,9 @@ async function callGeminiSearch(
|
|
|
482
385
|
|
|
483
386
|
/**
|
|
484
387
|
* Executes a web search using Google Gemini with Google Search grounding.
|
|
485
|
-
* Requires OAuth credentials stored in agent.db for provider "google-gemini-cli" or "google-antigravity".
|
|
486
|
-
* @param params - Search parameters including query and optional settings
|
|
487
|
-
* @returns Search response with synthesized answer, sources, and citations
|
|
488
|
-
* @throws {Error} If no Gemini OAuth credentials are configured
|
|
489
388
|
*/
|
|
490
389
|
export async function searchGemini(params: GeminiSearchParams): Promise<SearchResponse> {
|
|
491
|
-
const auth = await findGeminiAuth();
|
|
390
|
+
const auth = await findGeminiAuth(params.authStorage, params.sessionId, params.signal);
|
|
492
391
|
if (!auth) {
|
|
493
392
|
throw new Error(
|
|
494
393
|
"No Gemini OAuth credentials found. Login with 'omp /login google-gemini-cli' or 'omp /login google-antigravity' to enable Gemini web search.",
|
|
@@ -511,7 +410,6 @@ export async function searchGemini(params: GeminiSearchParams): Promise<SearchRe
|
|
|
511
410
|
|
|
512
411
|
let sources = result.sources;
|
|
513
412
|
|
|
514
|
-
// Apply num_results limit if specified
|
|
515
413
|
if (params.num_results && sources.length > params.num_results) {
|
|
516
414
|
sources = sources.slice(0, params.num_results);
|
|
517
415
|
}
|
|
@@ -532,8 +430,11 @@ export class GeminiProvider extends SearchProvider {
|
|
|
532
430
|
readonly id = "gemini";
|
|
533
431
|
readonly label = "Gemini";
|
|
534
432
|
|
|
535
|
-
isAvailable() {
|
|
536
|
-
|
|
433
|
+
isAvailable(authStorage: AuthStorage): boolean {
|
|
434
|
+
// Cheap, in-memory check — avoids driving the refresh pipeline during
|
|
435
|
+
// the provider-chain probe. `searchGemini` calls `getOAuthAccess` which
|
|
436
|
+
// will refresh lazily on the actual request.
|
|
437
|
+
return hasGeminiOAuth(authStorage);
|
|
537
438
|
}
|
|
538
439
|
|
|
539
440
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
@@ -547,6 +448,8 @@ export class GeminiProvider extends SearchProvider {
|
|
|
547
448
|
code_execution: params.codeExecution,
|
|
548
449
|
url_context: params.urlContext,
|
|
549
450
|
signal: params.signal,
|
|
451
|
+
authStorage: params.authStorage,
|
|
452
|
+
sessionId: params.sessionId,
|
|
550
453
|
});
|
|
551
454
|
}
|
|
552
455
|
}
|
|
@@ -5,12 +5,12 @@
|
|
|
5
5
|
* cleaned content.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
8
|
+
import { type AuthStorage, getEnvApiKey } from "@oh-my-pi/pi-ai";
|
|
9
9
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
10
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
11
|
import type { SearchParams } from "./base";
|
|
12
12
|
import { SearchProvider } from "./base";
|
|
13
|
-
import { classifyProviderHttpError,
|
|
13
|
+
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
14
14
|
|
|
15
15
|
const JINA_SEARCH_URL = "https://s.jina.ai";
|
|
16
16
|
|
|
@@ -87,8 +87,8 @@ export class JinaProvider extends SearchProvider {
|
|
|
87
87
|
readonly id = "jina";
|
|
88
88
|
readonly label = "Jina";
|
|
89
89
|
|
|
90
|
-
isAvailable() {
|
|
91
|
-
return
|
|
90
|
+
isAvailable(_authStorage: AuthStorage): boolean {
|
|
91
|
+
return !!findApiKey();
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Thin wrapper that adapts shared Kagi API utilities to SearchResponse shape.
|
|
5
5
|
*/
|
|
6
|
+
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
6
7
|
import type { SearchResponse } from "../../../web/search/types";
|
|
7
8
|
import { SearchProviderError } from "../../../web/search/types";
|
|
8
|
-
import {
|
|
9
|
+
import { KagiApiError, searchWithKagi } from "../../kagi";
|
|
9
10
|
import { clampNumResults } from "../utils";
|
|
10
11
|
import type { SearchParams } from "./base";
|
|
11
12
|
import { SearchProvider } from "./base";
|
|
@@ -19,14 +20,21 @@ export async function searchKagi(params: {
|
|
|
19
20
|
query: string;
|
|
20
21
|
num_results?: number;
|
|
21
22
|
signal?: AbortSignal;
|
|
23
|
+
authStorage: AuthStorage;
|
|
24
|
+
sessionId?: string;
|
|
22
25
|
}): Promise<SearchResponse> {
|
|
23
26
|
const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
|
|
24
27
|
|
|
25
28
|
try {
|
|
26
|
-
const result = await searchWithKagi(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const result = await searchWithKagi(
|
|
30
|
+
params.query,
|
|
31
|
+
{
|
|
32
|
+
limit: numResults,
|
|
33
|
+
sessionId: params.sessionId,
|
|
34
|
+
signal: params.signal,
|
|
35
|
+
},
|
|
36
|
+
params.authStorage,
|
|
37
|
+
);
|
|
30
38
|
|
|
31
39
|
return {
|
|
32
40
|
provider: "kagi",
|
|
@@ -51,12 +59,8 @@ export class KagiProvider extends SearchProvider {
|
|
|
51
59
|
readonly id = "kagi";
|
|
52
60
|
readonly label = "Kagi";
|
|
53
61
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return !!(await findKagiApiKey());
|
|
57
|
-
} catch {
|
|
58
|
-
return false;
|
|
59
|
-
}
|
|
62
|
+
isAvailable(authStorage: AuthStorage): boolean {
|
|
63
|
+
return authStorage.hasAuth("kagi");
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
@@ -64,6 +68,8 @@ export class KagiProvider extends SearchProvider {
|
|
|
64
68
|
query: params.query,
|
|
65
69
|
num_results: params.numSearchResults ?? params.limit,
|
|
66
70
|
signal: params.signal,
|
|
71
|
+
authStorage: params.authStorage,
|
|
72
|
+
sessionId: params.sessionId,
|
|
67
73
|
});
|
|
68
74
|
}
|
|
69
75
|
}
|
|
@@ -4,14 +4,15 @@
|
|
|
4
4
|
* Uses Moonshot Kimi Code search API to retrieve web results.
|
|
5
5
|
* Endpoint: POST https://api.kimi.com/coding/v1/search
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import type { AuthStorage } from "@oh-my-pi/pi-ai";
|
|
8
8
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
9
|
+
|
|
9
10
|
import type { SearchResponse, SearchSource } from "../../../web/search/types";
|
|
10
11
|
import { SearchProviderError } from "../../../web/search/types";
|
|
11
12
|
import { clampNumResults, dateToAgeSeconds } from "../utils";
|
|
12
13
|
import type { SearchParams } from "./base";
|
|
13
14
|
import { SearchProvider } from "./base";
|
|
14
|
-
import { classifyProviderHttpError,
|
|
15
|
+
import { classifyProviderHttpError, withHardTimeout } from "./utils";
|
|
15
16
|
|
|
16
17
|
const KIMI_SEARCH_URL = "https://api.kimi.com/coding/v1/search";
|
|
17
18
|
|
|
@@ -24,6 +25,8 @@ export interface KimiSearchParams {
|
|
|
24
25
|
num_results?: number;
|
|
25
26
|
include_content?: boolean;
|
|
26
27
|
signal?: AbortSignal;
|
|
28
|
+
authStorage: AuthStorage;
|
|
29
|
+
sessionId?: string;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
interface KimiSearchResult {
|
|
@@ -51,14 +54,20 @@ function resolveBaseUrl(): string {
|
|
|
51
54
|
return asTrimmed($env.MOONSHOT_SEARCH_BASE_URL) ?? asTrimmed($env.KIMI_SEARCH_BASE_URL) ?? KIMI_SEARCH_URL;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
/** Find Kimi search credentials from environment or
|
|
55
|
-
async function findApiKey(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
/** Find Kimi search credentials from environment or AuthStorage. */
|
|
58
|
+
async function findApiKey(
|
|
59
|
+
authStorage: AuthStorage,
|
|
60
|
+
sessionId: string | undefined,
|
|
61
|
+
signal: AbortSignal | undefined,
|
|
62
|
+
): Promise<string | null> {
|
|
63
|
+
const envKey = asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ?? asTrimmed($env.KIMI_SEARCH_API_KEY);
|
|
64
|
+
if (envKey) return envKey;
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
(await authStorage.getApiKey("moonshot", sessionId, { signal })) ??
|
|
68
|
+
(await authStorage.getApiKey("kimi-code", sessionId, { signal })) ??
|
|
69
|
+
null
|
|
70
|
+
);
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
async function callKimiSearch(
|
|
@@ -99,7 +108,7 @@ async function callKimiSearch(
|
|
|
99
108
|
|
|
100
109
|
/** Execute Kimi web search. */
|
|
101
110
|
export async function searchKimi(params: KimiSearchParams): Promise<SearchResponse> {
|
|
102
|
-
const apiKey = await findApiKey();
|
|
111
|
+
const apiKey = await findApiKey(params.authStorage, params.sessionId, params.signal);
|
|
103
112
|
if (!apiKey) {
|
|
104
113
|
throw new Error(
|
|
105
114
|
"Kimi search credentials not found. Set MOONSHOT_SEARCH_API_KEY, KIMI_SEARCH_API_KEY, MOONSHOT_API_KEY, or login with 'omp /login moonshot'.",
|
|
@@ -141,8 +150,13 @@ export class KimiProvider extends SearchProvider {
|
|
|
141
150
|
readonly id = "kimi";
|
|
142
151
|
readonly label = "Kimi";
|
|
143
152
|
|
|
144
|
-
isAvailable():
|
|
145
|
-
return
|
|
153
|
+
isAvailable(authStorage: AuthStorage): boolean {
|
|
154
|
+
return (
|
|
155
|
+
!!asTrimmed($env.MOONSHOT_SEARCH_API_KEY) ||
|
|
156
|
+
!!asTrimmed($env.KIMI_SEARCH_API_KEY) ||
|
|
157
|
+
authStorage.hasAuth("moonshot") ||
|
|
158
|
+
authStorage.hasAuth("kimi-code")
|
|
159
|
+
);
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
search(params: SearchParams): Promise<SearchResponse> {
|
|
@@ -150,6 +164,8 @@ export class KimiProvider extends SearchProvider {
|
|
|
150
164
|
query: params.query,
|
|
151
165
|
num_results: params.numSearchResults ?? params.limit,
|
|
152
166
|
signal: params.signal,
|
|
167
|
+
authStorage: params.authStorage,
|
|
168
|
+
sessionId: params.sessionId,
|
|
153
169
|
});
|
|
154
170
|
}
|
|
155
171
|
}
|