@bitkyc08/opencodex 2.26.0 → 2.28.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/gui/dist/assets/{index-RL6b1bTV.js → index-D2sP-biU.js} +14 -14
- package/gui/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/adapters/anthropic.ts +60 -1
- package/src/adapters/base.ts +16 -2
- package/src/adapters/command-code.ts +4 -3
- package/src/adapters/cursor/cursor-errors.ts +15 -0
- package/src/adapters/cursor/live-transport.ts +14 -1
- package/src/adapters/google-antigravity-replay.ts +16 -8
- package/src/adapters/google.ts +22 -5
- package/src/adapters/openai-chat.ts +189 -60
- package/src/adapters/openai-responses.ts +37 -0
- package/src/adapters/tool-catalog-nudge.ts +1 -1
- package/src/bridge.ts +11 -5
- package/src/cli/doctor.ts +76 -0
- package/src/cli/help.ts +2 -0
- package/src/cli/index.ts +19 -6
- package/src/cli/models.ts +13 -6
- package/src/codex/account-usability.ts +3 -0
- package/src/codex/app-server-processes.ts +269 -37
- package/src/codex/auth-api.ts +22 -5
- package/src/codex/auth-context.ts +108 -3
- package/src/codex/catalog/aggregation.ts +3 -0
- package/src/codex/catalog/metadata.ts +17 -3
- package/src/codex/catalog/native-models.ts +22 -14
- package/src/codex/catalog/parsing.ts +20 -3
- package/src/codex/catalog/provider-fetch.ts +8 -0
- package/src/codex/catalog/sync.ts +63 -15
- package/src/codex/convergence.ts +61 -13
- package/src/codex/log-guard/path-safety.ts +52 -3
- package/src/codex/model-entitlements.ts +353 -0
- package/src/codex/native-profile-startup.ts +100 -2
- package/src/codex/quota.ts +28 -3
- package/src/codex/routing.ts +14 -8
- package/src/codex/user-identity.ts +21 -1
- package/src/config/provider-name.ts +24 -0
- package/src/config.ts +11 -24
- package/src/generated/compatibility-version.json +110 -70
- package/src/images/loop.ts +11 -4
- package/src/lib/destination-policy.ts +47 -0
- package/src/lib/shadow-call.ts +15 -0
- package/src/lib/state-store-registrations.ts +8 -2
- package/src/oauth/index.ts +33 -5
- package/src/oauth/store.ts +11 -5
- package/src/providers/antigravity-models.ts +70 -5
- package/src/providers/derive.ts +12 -2
- package/src/providers/fastwire.ts +39 -8
- package/src/providers/quota.ts +9 -2
- package/src/providers/registry.ts +120 -6
- package/src/providers/service-tier.ts +50 -15
- package/src/responses/parser.ts +59 -11
- package/src/responses/state.ts +162 -5
- package/src/responses/tool-search-compat.ts +301 -0
- package/src/router.ts +17 -3
- package/src/routing/capability.ts +26 -9
- package/src/routing/compatibility/behavior.ts +44 -6
- package/src/routing/profile.ts +1 -1
- package/src/server/chat-native.ts +11 -2
- package/src/server/index.ts +59 -8
- package/src/server/management/agent-settings-routes.ts +16 -2
- package/src/server/management/shared.ts +3 -1
- package/src/server/request-log.ts +31 -0
- package/src/server/responses/collaboration.ts +34 -9
- package/src/server/responses/compact.ts +54 -7
- package/src/server/responses/core.ts +259 -43
- package/src/server/responses/input-admission.ts +7 -2
- package/src/server/responses/responses-field-backfill.ts +88 -6
- package/src/server/responses/terminal-guard.ts +10 -0
- package/src/server/responses-tool-search-repair.ts +217 -0
- package/src/server/system-env.ts +74 -5
- package/src/service-manager-probe.ts +99 -0
- package/src/service.ts +86 -6
- package/src/tray/windows.ts +25 -5
- package/src/types/accounts.ts +37 -0
- package/src/types/config.ts +818 -0
- package/src/types/provider.ts +521 -0
- package/src/types/request.ts +358 -0
- package/src/types/tools.ts +131 -0
- package/src/types/wire.ts +80 -0
- package/src/types.ts +103 -1883
- package/src/usage/cost.ts +37 -1
- package/src/usage/log.ts +4 -0
- package/src/web-search/loop.ts +11 -4
package/gui/dist/index.html
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
} catch (e) {}
|
|
17
17
|
})();
|
|
18
18
|
</script>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-D2sP-biU.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-DQsMZzI5.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/package.json
CHANGED
|
@@ -594,6 +594,63 @@ function orphanToolResultText(msg: OcxToolResultMessage): string {
|
|
|
594
594
|
return `[tool_result without adjacent tool_use: ${label}]\n${content}`;
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
+
/**
|
|
598
|
+
* AgentRouter answers 400 `content-blocked` when the first user message is not in English
|
|
599
|
+
* (#2074), while the same request in English returns 200. The gateway is inspecting the opening
|
|
600
|
+
* user content, so an Anthropic `system` string cannot reach it — the framing has to sit in the
|
|
601
|
+
* first user turn.
|
|
602
|
+
*/
|
|
603
|
+
const AGENTROUTER_LANGUAGE_PREAMBLE =
|
|
604
|
+
"[Instruction: Process the user request below and respond in the appropriate language.]";
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Exact host match, not a substring.
|
|
608
|
+
*
|
|
609
|
+
* A `hostname.includes("agentrouter")` test also matches `notagentrouter.example` and
|
|
610
|
+
* `agentrouter.org.attacker.example`, which would let an unrelated destination silently
|
|
611
|
+
* receive an injected instruction block. A prompt mutation keyed on a provider's identity
|
|
612
|
+
* must be keyed on that identity exactly.
|
|
613
|
+
*/
|
|
614
|
+
function isAgentRouterEndpoint(baseUrl: string): boolean {
|
|
615
|
+
try {
|
|
616
|
+
const { hostname } = new URL(baseUrl);
|
|
617
|
+
return hostname === "agentrouter.org" || hostname.endsWith(".agentrouter.org");
|
|
618
|
+
} catch {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Prepend the framing as its OWN text block instead of splicing it into the user's string.
|
|
625
|
+
*
|
|
626
|
+
* The distinction matters: rewriting `content` to `${marker}\n\n${original}` edits what the
|
|
627
|
+
* user wrote, and every downstream consumer — logs, retries, an upstream that echoes the turn —
|
|
628
|
+
* then sees a sentence the user never typed as if they had. A separate leading block carries the
|
|
629
|
+
* same signal to the filter while the original text survives byte-for-byte.
|
|
630
|
+
*
|
|
631
|
+
* Only the first user turn is framed, because only the first is what the gateway rejects.
|
|
632
|
+
*/
|
|
633
|
+
function applyAgentRouterLanguageFraming(messages: unknown[]): void {
|
|
634
|
+
const firstUser = messages.find(
|
|
635
|
+
(m): m is { role: string; content: unknown } =>
|
|
636
|
+
typeof m === "object" && m !== null && (m as { role?: unknown }).role === "user",
|
|
637
|
+
);
|
|
638
|
+
if (!firstUser) return;
|
|
639
|
+
const preamble = { type: "text", text: AGENTROUTER_LANGUAGE_PREAMBLE };
|
|
640
|
+
if (typeof firstUser.content === "string") {
|
|
641
|
+
firstUser.content = firstUser.content === ""
|
|
642
|
+
? [preamble]
|
|
643
|
+
: [preamble, { type: "text", text: firstUser.content }];
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
if (!Array.isArray(firstUser.content)) return;
|
|
647
|
+
// Idempotence is keyed on the LEADING block being exactly the marker. A substring test would
|
|
648
|
+
// let a user who quotes the marker later in their own prompt suppress the framing entirely.
|
|
649
|
+
const [head] = firstUser.content as { type?: unknown; text?: unknown }[];
|
|
650
|
+
if (head?.type === "text" && head.text === AGENTROUTER_LANGUAGE_PREAMBLE) return;
|
|
651
|
+
(firstUser.content as unknown[]).unshift(preamble);
|
|
652
|
+
}
|
|
653
|
+
|
|
597
654
|
function messagesToAnthropicFormat(
|
|
598
655
|
parsed: OcxParsedRequest,
|
|
599
656
|
toolNames: { toWire: (name: string) => string },
|
|
@@ -743,7 +800,7 @@ function toolsToAnthropicFormat(parsed: OcxParsedRequest, toolNames: { toWire: (
|
|
|
743
800
|
? new Set(parsed.options.toolChoice.allowedTools)
|
|
744
801
|
: undefined;
|
|
745
802
|
const tools = allowed
|
|
746
|
-
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed))
|
|
803
|
+
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed, parsed.context.tools))
|
|
747
804
|
: parsed.context.tools;
|
|
748
805
|
if (tools.length === 0) return undefined;
|
|
749
806
|
const converted = tools.map(t => ({
|
|
@@ -833,6 +890,8 @@ export function createAnthropicAdapter(provider: OcxProviderConfig, cacheRetenti
|
|
|
833
890
|
}
|
|
834
891
|
|
|
835
892
|
const { system, messages } = messagesToAnthropicFormat(parsed, toolNames);
|
|
893
|
+
// Before image normalization, so the framing block is present for every downstream pass.
|
|
894
|
+
if (isAgentRouterEndpoint(provider.baseUrl)) applyAgentRouterLanguageFraming(messages);
|
|
836
895
|
// Primary image layer: resize/re-encode to fit Anthropic limits without dropping
|
|
837
896
|
// (anthropic-image-normalize.ts); the guard below remains the deterministic backstop.
|
|
838
897
|
// imageTierBias > 0 = upstream-413 tightened retry (030): start every image one tier lower.
|
package/src/adapters/base.ts
CHANGED
|
@@ -39,8 +39,20 @@ export interface ProviderAdapter {
|
|
|
39
39
|
|
|
40
40
|
fetchResponse?(request: AdapterRequest, ctx?: AdapterFetchContext): Promise<Response>;
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Parse one upstream response. `tierMetadata` is the same live observer returned on the
|
|
44
|
+
* corresponding AdapterRequest; adapters that receive a documented tier echo may update it.
|
|
45
|
+
*/
|
|
46
|
+
parseStream(
|
|
47
|
+
response: Response,
|
|
48
|
+
budget: TranslatorBudget,
|
|
49
|
+
tierMetadata?: AdapterTierMetadata,
|
|
50
|
+
): AsyncGenerator<AdapterEvent>;
|
|
51
|
+
parseResponse?(
|
|
52
|
+
response: Response,
|
|
53
|
+
budget: TranslatorBudget,
|
|
54
|
+
tierMetadata?: AdapterTierMetadata,
|
|
55
|
+
): Promise<AdapterEvent[]>;
|
|
44
56
|
runTurn?(
|
|
45
57
|
parsed: OcxParsedRequest,
|
|
46
58
|
incoming: IncomingMeta,
|
|
@@ -58,6 +70,8 @@ export interface AdapterRequest {
|
|
|
58
70
|
body: string;
|
|
59
71
|
/** Custom-tool names actually lowered to upstream function calls while building this request. */
|
|
60
72
|
convertedRoutedCustomToolNames?: ReadonlySet<string>;
|
|
73
|
+
/** Client tool-search names actually lowered to upstream function calls for this request. */
|
|
74
|
+
convertedRoutedToolSearchNames?: ReadonlySet<string>;
|
|
61
75
|
/** Releases observation of a serialized request body after its final fetch attempt settles. */
|
|
62
76
|
releaseBodyObservation?: () => void;
|
|
63
77
|
/** Exact reasoning parameter emitted by the adapter, for request-log diagnostics only. */
|
|
@@ -3,7 +3,7 @@ import { execFile as execFileCallback } from "node:child_process";
|
|
|
3
3
|
import { promisify } from "node:util";
|
|
4
4
|
import { opendir } from "node:fs/promises";
|
|
5
5
|
import type { AdapterEvent, OcxContentPart, OcxMessage, OcxParsedRequest, OcxProviderConfig, OcxTool, OcxUsage } from "../types";
|
|
6
|
-
import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolAllowedByChoice
|
|
6
|
+
import { isAllowedToolChoice, namespacedToolName, resolveToolChoiceWireName, toolAllowedByChoice } from "../types";
|
|
7
7
|
import type { AdapterFetchContext, AdapterRequest, ProviderAdapter } from "./base";
|
|
8
8
|
import type { TranslatorBudget } from "../lib/translator-budget";
|
|
9
9
|
import { readBoundedResponseBody } from "../lib/bounded-body";
|
|
@@ -157,10 +157,11 @@ function visibleTools(parsed: OcxParsedRequest): OcxTool[] {
|
|
|
157
157
|
const tools = parsed.context.tools ?? [];
|
|
158
158
|
if (isAllowedToolChoice(choice)) {
|
|
159
159
|
const allowed = new Set(choice.allowedTools);
|
|
160
|
-
return tools.filter(tool => toolAllowedByChoice(tool, allowed));
|
|
160
|
+
return tools.filter(tool => toolAllowedByChoice(tool, allowed, tools));
|
|
161
161
|
}
|
|
162
162
|
if (choice && typeof choice !== "string") {
|
|
163
|
-
|
|
163
|
+
const selected = resolveToolChoiceWireName(tools, choice.name);
|
|
164
|
+
return tools.filter(tool => namespacedToolName(tool.namespace, tool.name) === selected);
|
|
164
165
|
}
|
|
165
166
|
return tools;
|
|
166
167
|
}
|
|
@@ -85,6 +85,21 @@ export function isCursorBenignCancelError(value: unknown): boolean {
|
|
|
85
85
|
return false;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
/**
|
|
89
|
+
* True when the turn was torn down by an `AbortSignal` rather than by a transport fault.
|
|
90
|
+
*
|
|
91
|
+
* This is deliberately NOT part of `isCursorBenignCancelError`: an abort mid-turn is a real
|
|
92
|
+
* failure and must still surface. It is only meaningful in combination with a terminal frame
|
|
93
|
+
* having already been emitted, where it means "the answer landed and then the connection went
|
|
94
|
+
* away" (#1527).
|
|
95
|
+
*/
|
|
96
|
+
export function isCursorAbortError(value: unknown): boolean {
|
|
97
|
+
const message = errorMessage(value).toLowerCase();
|
|
98
|
+
if (message.includes("cursor request was aborted")) return true;
|
|
99
|
+
const name = (value as { name?: unknown })?.name;
|
|
100
|
+
return typeof name === "string" && name === "AbortError";
|
|
101
|
+
}
|
|
102
|
+
|
|
88
103
|
/**
|
|
89
104
|
* True when Cursor Connect rejected the turn with invalid_argument.
|
|
90
105
|
* Seen after stepCompleted on brittle external-model continuations.
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
type InteractionResponse,
|
|
49
49
|
} from "./gen/agent_pb";
|
|
50
50
|
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
51
|
-
import { classifyCursorError, CursorUnexpectedCancelError, isCursorBenignCancelError, safeCursorErrorMessage } from "./cursor-errors";
|
|
51
|
+
import { classifyCursorError, CursorUnexpectedCancelError, isCursorAbortError, isCursorBenignCancelError, safeCursorErrorMessage } from "./cursor-errors";
|
|
52
52
|
import { mcpArgsFromToolCall } from "./protobuf-events";
|
|
53
53
|
import { OCX_RESPONSES_TOOL_PROVIDER } from "./tool-definitions";
|
|
54
54
|
import {
|
|
@@ -650,6 +650,18 @@ class LiveCursorTransport implements CursorTransport {
|
|
|
650
650
|
// A CANCEL is benign only on the client-tool suspend path (expectedClose); an
|
|
651
651
|
// unexpected server-side NGHTTP2_CANCEL must surface as a real transport error.
|
|
652
652
|
if (this.expectedClose && isCursorBenignCancelError(failure)) return;
|
|
653
|
+
// A teardown error arriving AFTER the turn's terminal frame describes the connection,
|
|
654
|
+
// not the turn: the answer is committed and every queued message has been yielded.
|
|
655
|
+
//
|
|
656
|
+
// Narrow on purpose. A benign cancel after a terminal is already swallowed one layer
|
|
657
|
+
// up (`cursor.ts:183`), so widening this to every post-terminal error would change
|
|
658
|
+
// what the adapter sees for genuine faults. What it does cover is the abort case
|
|
659
|
+
// from #1527: `signal.abort` fires `failAndClear(new Error("Cursor request was
|
|
660
|
+
// aborted"))`, which is NOT benign (`cursor-errors.ts:74`), so an ordinary completed
|
|
661
|
+
// turn that is then torn down still surfaced as `turn-failed` with
|
|
662
|
+
// `expectedClose:false`. Only `cancelCursorRun()` sets `expectedClose`, so a normal
|
|
663
|
+
// completion never qualified for the branch above.
|
|
664
|
+
if (this.emittedTerminal && isCursorAbortError(failure)) return;
|
|
653
665
|
throw attachPartialUsage(classifyTurnFailure(failure), state);
|
|
654
666
|
}
|
|
655
667
|
if (done) break;
|
|
@@ -659,6 +671,7 @@ class LiveCursorTransport implements CursorTransport {
|
|
|
659
671
|
}
|
|
660
672
|
if (failure) {
|
|
661
673
|
if (this.expectedClose && isCursorBenignCancelError(failure)) return;
|
|
674
|
+
if (this.emittedTerminal && isCursorAbortError(failure)) return;
|
|
662
675
|
throw attachPartialUsage(classifyTurnFailure(failure), state);
|
|
663
676
|
}
|
|
664
677
|
}
|
|
@@ -599,12 +599,20 @@ export function antigravityUsesReplayCache(model: string): boolean {
|
|
|
599
599
|
* Observe a parsed CCA chunk's `candidates[0].content.parts` and record thought signatures keyed by
|
|
600
600
|
* the functionCall identity (name + args). Accumulates across the whole session so a sequential
|
|
601
601
|
* multi-step tool loop keeps EVERY prior call's signature, not just the latest part-index slot.
|
|
602
|
-
* A signature on a standalone thought part
|
|
603
|
-
* array
|
|
602
|
+
* A signature on a standalone thought part applies to the functionCall parts that follow it in
|
|
603
|
+
* the same array AND to later arrays of the same turn: streaming splits a thought part and its
|
|
604
|
+
* calls across SSE chunks, so `carriedThoughtSig` threads the still-unpaired signature from the
|
|
605
|
+
* previous chunk and the return value hands the remainder to the next one (#897, #2125). A call's
|
|
606
|
+
* own signature always takes precedence over a carried one.
|
|
604
607
|
* `parts` is the already-unwrapped `response.candidates[0].content.parts`.
|
|
605
608
|
*/
|
|
606
|
-
export function observeAntigravityReplay(
|
|
607
|
-
|
|
609
|
+
export function observeAntigravityReplay(
|
|
610
|
+
model: string,
|
|
611
|
+
sessionId: string,
|
|
612
|
+
parts: unknown[],
|
|
613
|
+
carriedThoughtSig?: string,
|
|
614
|
+
): string | undefined {
|
|
615
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(parts) || parts.length === 0) return carriedThoughtSig;
|
|
608
616
|
ensureReplaySnapshotLoaded();
|
|
609
617
|
const now = Date.now();
|
|
610
618
|
deleteExpiredReplaySessionsThrottled(now);
|
|
@@ -618,7 +626,7 @@ export function observeAntigravityReplay(model: string, sessionId: string, parts
|
|
|
618
626
|
lastActiveAtMs: 0,
|
|
619
627
|
};
|
|
620
628
|
let inserted = false;
|
|
621
|
-
let pendingThoughtSig: string | undefined;
|
|
629
|
+
let pendingThoughtSig: string | undefined = carriedThoughtSig;
|
|
622
630
|
for (const raw of parts) {
|
|
623
631
|
if (!raw || typeof raw !== "object") continue;
|
|
624
632
|
const part = raw as Record<string, unknown>;
|
|
@@ -632,7 +640,6 @@ export function observeAntigravityReplay(model: string, sessionId: string, parts
|
|
|
632
640
|
continue;
|
|
633
641
|
}
|
|
634
642
|
const callSig = sig ?? pendingThoughtSig; // a signature on the call part itself wins
|
|
635
|
-
pendingThoughtSig = undefined;
|
|
636
643
|
if (!callSig) continue;
|
|
637
644
|
const ck = functionCallKey(fc.name, fc.args);
|
|
638
645
|
if (!ck) continue; // only function-call signatures are replayable by identity
|
|
@@ -645,7 +652,7 @@ export function observeAntigravityReplay(model: string, sessionId: string, parts
|
|
|
645
652
|
replayBytes += sizeBytes;
|
|
646
653
|
inserted = true;
|
|
647
654
|
}
|
|
648
|
-
if (!inserted) return;
|
|
655
|
+
if (!inserted) return pendingThoughtSig;
|
|
649
656
|
// Charge the fixed outer key only when the session is actually stored.
|
|
650
657
|
if (!existing) replayBytes += REPLAY_SESSION_KEY_BYTES;
|
|
651
658
|
evictInnerCalls(entry);
|
|
@@ -659,7 +666,7 @@ export function observeAntigravityReplay(model: string, sessionId: string, parts
|
|
|
659
666
|
} else {
|
|
660
667
|
replayBytes -= REPLAY_SESSION_KEY_BYTES;
|
|
661
668
|
}
|
|
662
|
-
return;
|
|
669
|
+
return pendingThoughtSig;
|
|
663
670
|
}
|
|
664
671
|
entry.expiresAtMs = now + REPLAY_TTL_MS;
|
|
665
672
|
entry.lastActiveAtMs = now;
|
|
@@ -668,6 +675,7 @@ export function observeAntigravityReplay(model: string, sessionId: string, parts
|
|
|
668
675
|
evictIfNeeded();
|
|
669
676
|
enforceAppOwnedMemoryBudget();
|
|
670
677
|
markReplayDirty();
|
|
678
|
+
return pendingThoughtSig;
|
|
671
679
|
}
|
|
672
680
|
|
|
673
681
|
/**
|
package/src/adapters/google.ts
CHANGED
|
@@ -262,7 +262,7 @@ function toolsToGeminiFormat(parsed: OcxParsedRequest): unknown[] | undefined {
|
|
|
262
262
|
? new Set(parsed.options.toolChoice.allowedTools)
|
|
263
263
|
: undefined;
|
|
264
264
|
const tools = allowed
|
|
265
|
-
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed))
|
|
265
|
+
? parsed.context.tools.filter(t => toolAllowedByChoice(t, allowed, parsed.context.tools))
|
|
266
266
|
: parsed.context.tools;
|
|
267
267
|
if (tools.length === 0) return undefined;
|
|
268
268
|
return [{
|
|
@@ -342,6 +342,7 @@ interface GoogleResponsePart {
|
|
|
342
342
|
text?: string;
|
|
343
343
|
thought?: boolean;
|
|
344
344
|
thoughtSignature?: string;
|
|
345
|
+
thought_signature?: string;
|
|
345
346
|
functionCall?: { name: string; args: unknown };
|
|
346
347
|
}
|
|
347
348
|
|
|
@@ -352,8 +353,9 @@ interface GoogleResponsePart {
|
|
|
352
353
|
*/
|
|
353
354
|
function googleToolCallMetadataFromPart(
|
|
354
355
|
part: GoogleResponsePart,
|
|
356
|
+
fallbackSignature?: string,
|
|
355
357
|
): { providerMetadata: OcxProviderOpaqueToolCallMetadata } | undefined {
|
|
356
|
-
const signature = part.thoughtSignature;
|
|
358
|
+
const signature = part.thoughtSignature ?? part.thought_signature ?? fallbackSignature;
|
|
357
359
|
if (!isLikelyRealThoughtSignature(signature)) return undefined;
|
|
358
360
|
return { providerMetadata: { google: { thoughtSignature: signature } } };
|
|
359
361
|
}
|
|
@@ -601,6 +603,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
601
603
|
let lastFinishReason: string | undefined;
|
|
602
604
|
let sawAnyFrame = false;
|
|
603
605
|
let sawTerminalSignal = false;
|
|
606
|
+
let pendingStreamThoughtSig: string | undefined;
|
|
604
607
|
|
|
605
608
|
const handleDataLine = async function* (line: string): AsyncGenerator<AdapterEvent, "continue" | "content" | "terminate"> {
|
|
606
609
|
const payload = line.slice(5).trim();
|
|
@@ -697,10 +700,19 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
697
700
|
const replaySession = provider.googleMode === "cloud-code-assist" ? antigravitySession : vertexReplaySession;
|
|
698
701
|
if ((provider.googleMode === "cloud-code-assist" || provider.googleMode === "vertex")
|
|
699
702
|
&& parts && replayModel && replaySession) {
|
|
700
|
-
observeAntigravityReplay(
|
|
703
|
+
pendingStreamThoughtSig = observeAntigravityReplay(
|
|
704
|
+
replayModel,
|
|
705
|
+
replaySession,
|
|
706
|
+
parts as unknown[],
|
|
707
|
+
pendingStreamThoughtSig,
|
|
708
|
+
);
|
|
701
709
|
}
|
|
702
710
|
if (parts) {
|
|
703
711
|
for (const part of parts) {
|
|
712
|
+
const sig = part.thoughtSignature ?? part.thought_signature;
|
|
713
|
+
if (part.thought === true && sig && isLikelyRealThoughtSignature(sig)) {
|
|
714
|
+
pendingStreamThoughtSig = sig;
|
|
715
|
+
}
|
|
704
716
|
const textEvent = googlePartTextEvent(part);
|
|
705
717
|
if (textEvent) {
|
|
706
718
|
emittedContentEvent = true;
|
|
@@ -729,7 +741,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
729
741
|
type: "tool_call_start",
|
|
730
742
|
id,
|
|
731
743
|
name: restoreGoogleToolName(part.functionCall.name),
|
|
732
|
-
...googleToolCallMetadataFromPart(part),
|
|
744
|
+
...googleToolCallMetadataFromPart(part, pendingStreamThoughtSig),
|
|
733
745
|
};
|
|
734
746
|
yield { type: "tool_call_delta", arguments: JSON.stringify(part.functionCall.args ?? {}) };
|
|
735
747
|
yield { type: "tool_call_end" };
|
|
@@ -926,7 +938,12 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
926
938
|
&& replayModel && replaySession) {
|
|
927
939
|
observeAntigravityReplay(replayModel, replaySession, candidates[0].content.parts as unknown[]);
|
|
928
940
|
}
|
|
941
|
+
let pendingThoughtSig: string | undefined;
|
|
929
942
|
for (const part of candidates[0].content.parts) {
|
|
943
|
+
const sig = part.thoughtSignature ?? part.thought_signature;
|
|
944
|
+
if (part.thought === true && sig && isLikelyRealThoughtSignature(sig)) {
|
|
945
|
+
pendingThoughtSig = sig;
|
|
946
|
+
}
|
|
930
947
|
const textEvent = googlePartTextEvent(part);
|
|
931
948
|
if (textEvent) events.push(textEvent);
|
|
932
949
|
const inline = (part as { inlineData?: { mimeType?: string; data?: string } }).inlineData;
|
|
@@ -950,7 +967,7 @@ export function createGoogleAdapter(provider: OcxProviderConfig): ProviderAdapte
|
|
|
950
967
|
type: "tool_call_start",
|
|
951
968
|
id,
|
|
952
969
|
name: restoreGoogleToolName(part.functionCall.name),
|
|
953
|
-
...googleToolCallMetadataFromPart(part),
|
|
970
|
+
...googleToolCallMetadataFromPart(part, pendingThoughtSig),
|
|
954
971
|
});
|
|
955
972
|
events.push({ type: "tool_call_delta", arguments: JSON.stringify(part.functionCall.args ?? {}) });
|
|
956
973
|
events.push({ type: "tool_call_end" });
|