@yansigit/opencodex 2.31.1 → 2.31.3
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/gui/dist/assets/{index-DJDp_XER.js → index-Cxt5fZMP.js} +14 -14
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/base.ts +6 -0
- package/src/adapters/command-code-project-context.ts +377 -0
- package/src/adapters/command-code.ts +5 -1
- package/src/adapters/cursor/cursor-errors.ts +12 -0
- package/src/adapters/cursor/live-transport.ts +21 -0
- package/src/adapters/cursor/native-exec-bridge.ts +141 -0
- package/src/adapters/cursor/thread-continuity.ts +71 -0
- package/src/adapters/cursor.ts +90 -28
- package/src/adapters/google-http.ts +12 -2
- package/src/adapters/google-wire-compiler.ts +91 -2
- package/src/adapters/google.ts +83 -15
- package/src/config.ts +2 -0
- package/src/generated/compatibility-version.json +57 -25
- package/src/lab/subject/behavior-fingerprint.ts +1 -1
- package/src/oauth/anthropic-routing.ts +129 -219
- package/src/oauth/antigravity-routing.ts +165 -0
- package/src/oauth/cursor-routing.ts +252 -0
- package/src/oauth/index.ts +3 -0
- package/src/providers/cursor-pool.ts +3 -3
- package/src/routing/account-pool/affinity.ts +125 -0
- package/src/routing/account-pool/cooldown.ts +145 -0
- package/src/routing/account-pool/index.ts +45 -0
- package/src/routing/account-pool/resolve.ts +356 -0
- package/src/routing/account-pool/types.ts +32 -0
- package/src/routing/compatibility/behavior.ts +3 -0
- package/src/server/management/oauth-account-routes.ts +47 -12
- package/src/server/responses/core.ts +400 -72
- package/src/types/config.ts +8 -0
- package/src/types/provider.ts +7 -0
- package/src/types/request.ts +13 -3
- package/src/usage/log.ts +4 -0
- package/src/web-search/gemini-executor.ts +35 -13
- package/src/web-search/index.ts +85 -1
package/src/types/request.ts
CHANGED
|
@@ -94,6 +94,12 @@ export interface OcxParsedRequest {
|
|
|
94
94
|
* executes searches via the gpt-5.4-mini sidecar (see src/web-search). Absent when not requested.
|
|
95
95
|
*/
|
|
96
96
|
_webSearch?: Record<string, unknown>;
|
|
97
|
+
/**
|
|
98
|
+
* Antigravity Gemini in-turn CCA grounding: google_search and optional url_context ride the main
|
|
99
|
+
* routed fetch instead of the web-search sidecar loop. Set by core.ts when resolveCcaInTurnGrounding
|
|
100
|
+
* matches; consumed by the Google adapter at buildRequest/parseStream time.
|
|
101
|
+
*/
|
|
102
|
+
_ccaInTurnGrounding?: { search: boolean; urlContext: boolean };
|
|
97
103
|
/** Hosted image_generation tool config stashed for the image bridge sidecar (see src/images). */
|
|
98
104
|
_imageGeneration?: { toolNames: Set<string>; originalTool?: Record<string, unknown> };
|
|
99
105
|
/**
|
|
@@ -245,9 +251,13 @@ export interface OcxRequestOptions {
|
|
|
245
251
|
/**
|
|
246
252
|
* Responses `text.format` (json_schema / json_object), preserved for adapters whose
|
|
247
253
|
* upstream wire has an equivalent. The openai-chat adapter re-nests it as chat
|
|
248
|
-
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts
|
|
249
|
-
*
|
|
250
|
-
*
|
|
254
|
+
* `response_format`, the exact inverse of responseFormatToText in src/chat/inbound.ts; the
|
|
255
|
+
* Google adapter lowers supported requests to Gemini JSON mode (`responseMimeType` /
|
|
256
|
+
* `responseSchema`) but skips requests with tools, Claude models, or image-capable models.
|
|
257
|
+
* The `openai-chat` adapter can omit it for models in `noStructuredOutputModels`; Kiro
|
|
258
|
+
* rejects structured output via `_structuredOutput`; and Cursor has no structured-output
|
|
259
|
+
* wire field and rejects the request before transport.
|
|
260
|
+
* Native passthrough does not consume this option and forwards `_rawBody.text` verbatim.
|
|
251
261
|
*/
|
|
252
262
|
textFormat?: {
|
|
253
263
|
type: "json_schema" | "json_object";
|
package/src/usage/log.ts
CHANGED
|
@@ -28,6 +28,8 @@ export type AttemptRecoveryKind =
|
|
|
28
28
|
| "key-429"
|
|
29
29
|
| "rate-limit-429"
|
|
30
30
|
| "anthropic-oauth-429"
|
|
31
|
+
| "cursor-oauth-auth"
|
|
32
|
+
| "cursor-oauth-429"
|
|
31
33
|
| "image-413"
|
|
32
34
|
| "opaque-blob-rejection"
|
|
33
35
|
| "empty-completion";
|
|
@@ -218,6 +220,8 @@ const ATTEMPT_RECOVERY_KINDS = new Set<AttemptRecoveryKind>([
|
|
|
218
220
|
"key-429",
|
|
219
221
|
"rate-limit-429",
|
|
220
222
|
"anthropic-oauth-429",
|
|
223
|
+
"cursor-oauth-auth",
|
|
224
|
+
"cursor-oauth-429",
|
|
221
225
|
"image-413",
|
|
222
226
|
"opaque-blob-rejection",
|
|
223
227
|
"empty-completion",
|
|
@@ -19,6 +19,7 @@ import { ANTIGRAVITY_REQUEST_UA } from "../adapters/google-antigravity-wire";
|
|
|
19
19
|
import { resolveAntigravityEffortWireModel } from "../providers/antigravity-models";
|
|
20
20
|
import { getProviderRegistryEntry } from "../providers/registry";
|
|
21
21
|
import { MAX_SIDECAR_RESPONSE_BYTES, type WebSearchSource } from "./parse";
|
|
22
|
+
import { appendSafeWebSearchSource } from "./sources";
|
|
22
23
|
import { BASE_INSTRUCTION, IMAGE_INSTRUCTION, type SidecarOutcome, type SidecarSettings } from "./executor";
|
|
23
24
|
|
|
24
25
|
const CCA_FALLBACK_BASE = "https://daily-cloudcode-pa.googleapis.com";
|
|
@@ -117,25 +118,46 @@ export async function runGeminiWebSearch(
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
/** Map a CCA generateContent payload (possibly wrapped in {response}) to text + grounding sources. */
|
|
121
|
+
export function extractCcaGroundingSources(groundingMetadata: unknown): WebSearchSource[] {
|
|
122
|
+
const sources: WebSearchSource[] = [];
|
|
123
|
+
const gm = isRec(groundingMetadata) ? groundingMetadata : undefined;
|
|
124
|
+
if (!gm || !Array.isArray(gm.groundingChunks)) return sources;
|
|
125
|
+
for (const chunk of gm.groundingChunks) {
|
|
126
|
+
const web = isRec(chunk) && isRec(chunk.web) ? chunk.web : undefined;
|
|
127
|
+
if (!web) continue;
|
|
128
|
+
// Route through the shared safe-source validator: enforces http(s) scheme,
|
|
129
|
+
// control-character filtering, dedup, and per-message count/byte budgets.
|
|
130
|
+
appendSafeWebSearchSource(sources, { url: web.uri, title: web.title });
|
|
131
|
+
}
|
|
132
|
+
return sources;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Skip search-suggestion HTML widgets Google may attach alongside grounded answers. */
|
|
136
|
+
export function isCcaSearchSuggestionHtml(text: string): boolean {
|
|
137
|
+
const trimmed = text.trim();
|
|
138
|
+
if (!trimmed.startsWith("<")) return false;
|
|
139
|
+
return /<style[\s>]/i.test(trimmed) || /search[_-]?suggest/i.test(trimmed) || /grounding-widget/i.test(trimmed);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function formatCcaGroundingSourcesAppendix(sources: readonly WebSearchSource[]): string {
|
|
143
|
+
if (sources.length === 0) return "";
|
|
144
|
+
const lines = ["", "Sources:"];
|
|
145
|
+
sources.slice(0, 8).forEach((s, i) => {
|
|
146
|
+
lines.push(`[${i + 1}] ${s.title ? `${s.title} — ` : ""}${s.url}`);
|
|
147
|
+
});
|
|
148
|
+
return lines.join("\n");
|
|
149
|
+
}
|
|
150
|
+
|
|
120
151
|
export function mapCcaGroundedResponse(payload: unknown): SidecarOutcome {
|
|
121
152
|
const root = isRec(payload) && isRec(payload.response) ? payload.response : payload;
|
|
122
153
|
if (!isRec(root)) return { text: "", sources: [], error: "gemini sidecar returned a non-JSON or empty body" };
|
|
123
154
|
const candidate = Array.isArray(root.candidates) && isRec(root.candidates[0]) ? root.candidates[0] : undefined;
|
|
124
155
|
if (!candidate) return { text: "", sources: [], error: "gemini sidecar returned no candidates" };
|
|
125
156
|
const parts = isRec(candidate.content) && Array.isArray(candidate.content.parts) ? candidate.content.parts : [];
|
|
126
|
-
const text = parts
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
if (gm && Array.isArray(gm.groundingChunks)) {
|
|
131
|
-
for (const chunk of gm.groundingChunks) {
|
|
132
|
-
const web = isRec(chunk) && isRec(chunk.web) ? chunk.web : undefined;
|
|
133
|
-
const uri = web && typeof web.uri === "string" ? web.uri : undefined;
|
|
134
|
-
if (!uri || seen.has(uri)) continue;
|
|
135
|
-
seen.add(uri);
|
|
136
|
-
sources.push({ url: uri, ...(typeof web?.title === "string" && web.title.length > 0 ? { title: web.title } : {}) });
|
|
137
|
-
}
|
|
138
|
-
}
|
|
157
|
+
const text = parts
|
|
158
|
+
.map(p => (isRec(p) && typeof p.text === "string" && !isCcaSearchSuggestionHtml(p.text) ? p.text : ""))
|
|
159
|
+
.join("");
|
|
160
|
+
const sources = extractCcaGroundingSources(candidate.groundingMetadata);
|
|
139
161
|
if (text.length === 0) return { text: "", sources, error: "gemini sidecar returned no text" };
|
|
140
162
|
return { text, sources };
|
|
141
163
|
}
|
package/src/web-search/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OcxConfig, OcxParsedRequest, OcxProviderConfig } from "../types";
|
|
1
|
+
import type { OcxConfig, OcxContentPart, OcxParsedRequest, OcxProviderConfig } from "../types";
|
|
2
2
|
import { modelInList, toolChoiceToolPredicate } from "../types";
|
|
3
3
|
import { isModelTextOnly } from "../vision";
|
|
4
4
|
import type { SidecarSettings } from "./executor";
|
|
@@ -166,6 +166,86 @@ export function resolveSidecarBackend(
|
|
|
166
166
|
return "openai";
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
export interface CcaInTurnGrounding {
|
|
170
|
+
search: boolean;
|
|
171
|
+
urlContext: boolean;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const HTTP_URL_RE = /https?:\/\/[^\s<>"')\]]+/i;
|
|
175
|
+
const EXPLICIT_SIDECAR_BACKENDS = new Set<WebSearchBackendId>(["openai", "anthropic", "xai", "exa"]);
|
|
176
|
+
|
|
177
|
+
function isGemini3ModelId(modelId: string): boolean {
|
|
178
|
+
return /gemini-3/i.test(modelId);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** Whether a planned media bridge will actually inject tools on this turn. */
|
|
182
|
+
export function mediaBridgeWillRun(
|
|
183
|
+
hasMediaPlan: boolean,
|
|
184
|
+
hasWebSearchPlan: boolean,
|
|
185
|
+
adapterRunsTurn: boolean,
|
|
186
|
+
isStreaming = true,
|
|
187
|
+
): boolean {
|
|
188
|
+
return hasMediaPlan && isStreaming && (!hasWebSearchPlan || adapterRunsTurn);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function messageTextContainsHttpUrl(content: string | OcxContentPart[]): boolean {
|
|
192
|
+
if (typeof content === "string") return HTTP_URL_RE.test(content);
|
|
193
|
+
for (const part of content) {
|
|
194
|
+
if (part.type === "image") {
|
|
195
|
+
const url = part.imageUrl;
|
|
196
|
+
if (url && !url.startsWith("data:") && /^https?:/i.test(url)) return true;
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (part.type === "text" && HTTP_URL_RE.test(part.text ?? "")) return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** True when request text carries http(s) URLs that become remote-url placeholders upstream. */
|
|
205
|
+
export function requestNeedsCcaUrlContext(parsed: OcxParsedRequest): boolean {
|
|
206
|
+
for (const msg of parsed.context.messages) {
|
|
207
|
+
if (msg.role === "user" || msg.role === "developer") {
|
|
208
|
+
if (messageTextContainsHttpUrl(msg.content)) return true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (msg.role === "toolResult" && typeof msg.content === "string" && HTTP_URL_RE.test(msg.content)) return true;
|
|
212
|
+
if (msg.role === "assistant") {
|
|
213
|
+
for (const part of msg.content) {
|
|
214
|
+
if (part.type === "text" && HTTP_URL_RE.test(part.text ?? "")) return true;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Antigravity Gemini in-turn google_search / url_context on the main routed fetch. Returns undefined
|
|
223
|
+
* when the request should use the web-search sidecar (Claude-on-CCA, explicit sidecar backend, or
|
|
224
|
+
* Gemini <3 with Codex function tools, including media bridge tools that will be injected later).
|
|
225
|
+
*/
|
|
226
|
+
export function resolveCcaInTurnGrounding(
|
|
227
|
+
config: OcxConfig,
|
|
228
|
+
parsed: OcxParsedRequest,
|
|
229
|
+
isPassthrough: boolean,
|
|
230
|
+
provider: OcxProviderConfig,
|
|
231
|
+
modelId: string,
|
|
232
|
+
hasMediaBridge = false,
|
|
233
|
+
): CcaInTurnGrounding | undefined {
|
|
234
|
+
if (!parsed._webSearch || isPassthrough) return undefined;
|
|
235
|
+
if (provider.googleMode !== "cloud-code-assist") return undefined;
|
|
236
|
+
if (/claude/i.test(modelId)) return undefined;
|
|
237
|
+
if (!toolChoiceToolPredicate(parsed.options.toolChoice)(buildWebSearchTool())) return undefined;
|
|
238
|
+
const cfg = config.webSearchSidecar ?? {};
|
|
239
|
+
if (cfg.enabled === false) return undefined;
|
|
240
|
+
if (cfg.backend !== undefined && EXPLICIT_SIDECAR_BACKENDS.has(cfg.backend)) return undefined;
|
|
241
|
+
const hasCodexTools = (parsed.context.tools?.length ?? 0) > 0 || hasMediaBridge;
|
|
242
|
+
if (hasCodexTools && !isGemini3ModelId(modelId)) return undefined;
|
|
243
|
+
return {
|
|
244
|
+
search: true,
|
|
245
|
+
urlContext: requestNeedsCcaUrlContext(parsed),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
169
249
|
export interface SidecarPlan {
|
|
170
250
|
/** Which executor runs the search. Anthropic does not require a forward provider. */
|
|
171
251
|
backend: WebSearchBackendId;
|
|
@@ -215,7 +295,11 @@ export function planWebSearch(
|
|
|
215
295
|
provider: OcxProviderConfig,
|
|
216
296
|
modelId: string,
|
|
217
297
|
openAiSidecar?: ResolvedOpenAiForwardSidecar,
|
|
298
|
+
// Core passes the potential media plan here so sidecar precedence can be resolved
|
|
299
|
+
// before deciding whether the media bridge actually runs.
|
|
300
|
+
hasMediaBridge = false,
|
|
218
301
|
): SidecarPlan | undefined {
|
|
302
|
+
if (resolveCcaInTurnGrounding(config, parsed, isPassthrough, provider, modelId, hasMediaBridge)) return undefined;
|
|
219
303
|
if (!parsed._webSearch || isPassthrough) return undefined;
|
|
220
304
|
if (!toolChoiceToolPredicate(parsed.options.toolChoice)(buildWebSearchTool())) return undefined;
|
|
221
305
|
const cfg = config.webSearchSidecar ?? {};
|