@gajae-code/ai 0.18.5 → 0.18.7
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 +73 -0
- package/dist/types/auth-storage.d.ts +8 -1
- package/dist/types/providers/cursor.d.ts +6 -3
- package/dist/types/providers/kiro-api-key.d.ts +1 -0
- package/dist/types/providers/openai-codex-responses.d.ts +3 -0
- package/dist/types/types.d.ts +16 -1
- package/dist/types/utils/fallback-transport.d.ts +6 -0
- package/dist/types/utils/idle-iterator.d.ts +2 -0
- package/dist/types/utils/oauth/glm-zcode.d.ts +5 -0
- package/dist/types/utils/oauth/index.d.ts +4 -0
- package/package.json +3 -3
- package/src/auth-storage.ts +115 -39
- package/src/model-thinking.ts +8 -0
- package/src/providers/aws-eventstream.ts +2 -1
- package/src/providers/cursor.d.ts +6 -3
- package/src/providers/cursor.ts +73 -64
- package/src/providers/google-gemini-cli.ts +49 -3
- package/src/providers/kiro-api-key.d.ts +1 -0
- package/src/providers/kiro-api-key.ts +1 -1
- package/src/providers/kiro-codewhisperer.ts +164 -68
- package/src/providers/openai-codex-responses.d.ts +3 -0
- package/src/providers/openai-codex-responses.ts +589 -41
- package/src/providers/openai-opencodex-responses.ts +6 -1
- package/src/providers/openai-responses.ts +1 -1
- package/src/providers/register-builtins.ts +53 -5
- package/src/stream.ts +20 -0
- package/src/types.d.ts +16 -1
- package/src/types.ts +19 -0
- package/src/utils/fallback-transport.d.ts +6 -0
- package/src/utils/fallback-transport.ts +6 -0
- package/src/utils/idle-iterator.d.ts +2 -0
- package/src/utils/idle-iterator.ts +13 -3
- package/src/utils/oauth/glm-zcode.d.ts +5 -0
- package/src/utils/oauth/glm-zcode.ts +8 -1
- package/src/utils/oauth/index.ts +11 -1
- package/src/utils/validation.ts +5 -0
|
@@ -2,8 +2,10 @@ import http2 from "node:http2";
|
|
|
2
2
|
import { type JsonValue } from "@bufbuild/protobuf";
|
|
3
3
|
import type { AssistantMessage, CursorExecHandlerResult, CursorExecHandlers, CursorToolResultHandler, Message, Model, StreamFunction, StreamOptions, Tool, ToolCall, ToolResultMessage, Usage } from "../types";
|
|
4
4
|
import { kProviderResolvedToolCall } from "../utils/block-symbols";
|
|
5
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
5
6
|
import { CURSOR_CLIENT_VERSION } from "./cursor/client-version";
|
|
6
7
|
import type { CursorRule, RequestedModel_ModelParameterbytes } from "./cursor/gen/agent_pb";
|
|
8
|
+
import { type AgentServerMessage } from "./cursor/gen/agent_pb";
|
|
7
9
|
export declare const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
8
10
|
export { CURSOR_CLIENT_VERSION };
|
|
9
11
|
/** Drop all cached state + blob bytes for a conversation (F15 bound + session-teardown hook). */
|
|
@@ -46,9 +48,8 @@ export declare function mapH2TransportError(error: unknown, baseUrl: string): un
|
|
|
46
48
|
export declare const streamCursor: StreamFunction<"cursor-agent">;
|
|
47
49
|
type ToolCallState = ToolCall & {
|
|
48
50
|
index: number;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
[kProviderResolvedToolCall]?: true;
|
|
51
|
+
kind: "todo_write" | "native" | "cursor-exec";
|
|
52
|
+
[kProviderResolvedToolCall]: true;
|
|
52
53
|
};
|
|
53
54
|
interface UsageState {
|
|
54
55
|
sawTokenDelta: boolean;
|
|
@@ -56,6 +57,8 @@ interface UsageState {
|
|
|
56
57
|
checkpointOutputTokens: number;
|
|
57
58
|
hasConversationCheckpoint: boolean;
|
|
58
59
|
}
|
|
60
|
+
/** Offline test seam for the same server-message boundary used by streamCursor. */
|
|
61
|
+
export declare function createCursorServerMessageHandlerForTest(output: AssistantMessage, stream: AssistantMessageEventStream, write: (frame: Uint8Array, done: () => void) => boolean, options?: CursorOptions): (message: AgentServerMessage) => Promise<void>;
|
|
59
62
|
export declare function storeCursorBlobForTest(blobStore: Map<string, Uint8Array>, blobId: Uint8Array, blobData: Uint8Array, limits: {
|
|
60
63
|
maxBytes: number;
|
|
61
64
|
}): boolean;
|
package/src/providers/cursor.ts
CHANGED
|
@@ -32,7 +32,6 @@ import { kProviderResolvedToolCall } from "../utils/block-symbols";
|
|
|
32
32
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
33
33
|
import { transportFailureFacts } from "../utils/fallback-transport";
|
|
34
34
|
import { FirstEventTimeoutError, getStreamFirstEventTimeoutMs, getStreamIdleTimeoutMs } from "../utils/idle-iterator";
|
|
35
|
-
import { captureUnicodeEscapeEvidence, parseStreamingJson } from "../utils/json-parse";
|
|
36
35
|
import { connectProxiedSocket, getProxyForUrl } from "../utils/proxy";
|
|
37
36
|
import { formatErrorMessageWithRetryAfter } from "../utils/retry-after";
|
|
38
37
|
import { flattenToolRootCombinators, toolWireSchema } from "../utils/schema";
|
|
@@ -2288,10 +2287,8 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
2288
2287
|
}
|
|
2289
2288
|
if (state.currentToolCall) {
|
|
2290
2289
|
const idx = output.content.indexOf(state.currentToolCall);
|
|
2291
|
-
state.currentToolCall
|
|
2292
|
-
|
|
2293
|
-
delete (state.currentToolCall as any).partialJson;
|
|
2294
|
-
delete (state.currentToolCall as any).index;
|
|
2290
|
+
delete (state.currentToolCall as Partial<ToolCallState>).index;
|
|
2291
|
+
delete (state.currentToolCall as Partial<ToolCallState>).kind;
|
|
2295
2292
|
stream.push({
|
|
2296
2293
|
type: "toolcall_end",
|
|
2297
2294
|
contentIndex: idx,
|
|
@@ -2428,9 +2425,8 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
2428
2425
|
|
|
2429
2426
|
type ToolCallState = ToolCall & {
|
|
2430
2427
|
index: number;
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
[kProviderResolvedToolCall]?: true;
|
|
2428
|
+
kind: "todo_write" | "native" | "cursor-exec";
|
|
2429
|
+
[kProviderResolvedToolCall]: true;
|
|
2434
2430
|
};
|
|
2435
2431
|
|
|
2436
2432
|
interface BlockState {
|
|
@@ -2493,6 +2489,57 @@ async function handleServerMessage(
|
|
|
2493
2489
|
}
|
|
2494
2490
|
}
|
|
2495
2491
|
|
|
2492
|
+
/** Offline test seam for the same server-message boundary used by streamCursor. */
|
|
2493
|
+
export function createCursorServerMessageHandlerForTest(
|
|
2494
|
+
output: AssistantMessage,
|
|
2495
|
+
stream: AssistantMessageEventStream,
|
|
2496
|
+
write: (frame: Uint8Array, done: () => void) => boolean,
|
|
2497
|
+
options: CursorOptions = {},
|
|
2498
|
+
): (message: AgentServerMessage) => Promise<void> {
|
|
2499
|
+
const state: BlockState = {
|
|
2500
|
+
currentTextBlock: null,
|
|
2501
|
+
currentThinkingBlock: null,
|
|
2502
|
+
currentToolCall: null,
|
|
2503
|
+
firstTokenTime: undefined,
|
|
2504
|
+
setTextBlock(block) {
|
|
2505
|
+
this.currentTextBlock = block;
|
|
2506
|
+
},
|
|
2507
|
+
setThinkingBlock(block) {
|
|
2508
|
+
this.currentThinkingBlock = block;
|
|
2509
|
+
},
|
|
2510
|
+
setToolCall(block) {
|
|
2511
|
+
this.currentToolCall = block;
|
|
2512
|
+
},
|
|
2513
|
+
setFirstTokenTime() {
|
|
2514
|
+
this.firstTokenTime ??= Date.now();
|
|
2515
|
+
},
|
|
2516
|
+
};
|
|
2517
|
+
const usageState: UsageState = {
|
|
2518
|
+
sawTokenDelta: false,
|
|
2519
|
+
conversationUsedTokens: 0,
|
|
2520
|
+
checkpointOutputTokens: 0,
|
|
2521
|
+
hasConversationCheckpoint: false,
|
|
2522
|
+
};
|
|
2523
|
+
const writer = { write, isActive: () => true } as unknown as CursorRequestWriter;
|
|
2524
|
+
const blobs = new Map<string, Uint8Array>();
|
|
2525
|
+
return message =>
|
|
2526
|
+
handleServerMessage(
|
|
2527
|
+
message,
|
|
2528
|
+
output,
|
|
2529
|
+
stream,
|
|
2530
|
+
state,
|
|
2531
|
+
blobs,
|
|
2532
|
+
writer,
|
|
2533
|
+
options.execHandlers,
|
|
2534
|
+
options.onToolResult,
|
|
2535
|
+
usageState,
|
|
2536
|
+
[],
|
|
2537
|
+
undefined,
|
|
2538
|
+
[],
|
|
2539
|
+
options.signal,
|
|
2540
|
+
);
|
|
2541
|
+
}
|
|
2542
|
+
|
|
2496
2543
|
function handleKvServerMessage(
|
|
2497
2544
|
kvMsg: KvServerMessage,
|
|
2498
2545
|
blobStore: Map<string, Uint8Array>,
|
|
@@ -3164,6 +3211,7 @@ async function handleExecServerMessage(
|
|
|
3164
3211
|
case "mcpArgs": {
|
|
3165
3212
|
const args = execMsg.message.value;
|
|
3166
3213
|
const mcpCall = decodeMcpCall(args);
|
|
3214
|
+
synthesizeCursorExecToolCall(output, stream, mcpCall.toolCallId, mcpCall.toolName, mcpCall.args);
|
|
3167
3215
|
const { execResult } = await resolveExecHandler(
|
|
3168
3216
|
mcpCall,
|
|
3169
3217
|
execHandlers?.mcp?.bind(execHandlers),
|
|
@@ -4063,17 +4111,6 @@ function decodeMcpArgValue(value: Uint8Array): unknown {
|
|
|
4063
4111
|
return parseToolArgsJson(text);
|
|
4064
4112
|
}
|
|
4065
4113
|
|
|
4066
|
-
function decodeMcpArgsMap(args?: Record<string, Uint8Array>): Record<string, unknown> | undefined {
|
|
4067
|
-
if (!args) {
|
|
4068
|
-
return undefined;
|
|
4069
|
-
}
|
|
4070
|
-
const decoded: Record<string, unknown> = {};
|
|
4071
|
-
for (const [key, value] of Object.entries(args)) {
|
|
4072
|
-
decoded[key] = decodeMcpArgValue(value);
|
|
4073
|
-
}
|
|
4074
|
-
return decoded;
|
|
4075
|
-
}
|
|
4076
|
-
|
|
4077
4114
|
function decodeMcpCall(args: {
|
|
4078
4115
|
name: string;
|
|
4079
4116
|
args: Record<string, Uint8Array>;
|
|
@@ -4089,7 +4126,7 @@ function decodeMcpCall(args: {
|
|
|
4089
4126
|
name: args.name,
|
|
4090
4127
|
providerIdentifier: args.providerIdentifier,
|
|
4091
4128
|
toolName: args.toolName || args.name,
|
|
4092
|
-
toolCallId: args.toolCallId,
|
|
4129
|
+
toolCallId: args.toolCallId || crypto.randomUUID(),
|
|
4093
4130
|
args: decodedArgs,
|
|
4094
4131
|
rawArgs: args.args ?? {},
|
|
4095
4132
|
};
|
|
@@ -4336,7 +4373,7 @@ const CURSOR_EXEC_OWNED_TOOL_CASES = new Set([
|
|
|
4336
4373
|
]);
|
|
4337
4374
|
|
|
4338
4375
|
function isExecOwnedToolCall(toolCall: any): boolean {
|
|
4339
|
-
return CURSOR_EXEC_OWNED_TOOL_CASES.has(toolCall?.tool?.case);
|
|
4376
|
+
return !!selectMcpToolCall(toolCall) || CURSOR_EXEC_OWNED_TOOL_CASES.has(toolCall?.tool?.case);
|
|
4340
4377
|
}
|
|
4341
4378
|
|
|
4342
4379
|
export function buildNativeToolCallBlock(
|
|
@@ -4358,6 +4395,7 @@ export function buildNativeToolCallBlock(
|
|
|
4358
4395
|
: { raw: convertedArgs },
|
|
4359
4396
|
index,
|
|
4360
4397
|
kind: "native",
|
|
4398
|
+
[kProviderResolvedToolCall]: true,
|
|
4361
4399
|
};
|
|
4362
4400
|
}
|
|
4363
4401
|
for (const [key, payload] of Object.entries(toolCall)) {
|
|
@@ -4380,6 +4418,7 @@ export function buildNativeToolCallBlock(
|
|
|
4380
4418
|
arguments: safeArguments,
|
|
4381
4419
|
index,
|
|
4382
4420
|
kind: "native",
|
|
4421
|
+
[kProviderResolvedToolCall]: true,
|
|
4383
4422
|
};
|
|
4384
4423
|
}
|
|
4385
4424
|
return null;
|
|
@@ -4445,6 +4484,7 @@ function synthesizeCursorExecToolCall(
|
|
|
4445
4484
|
name: string,
|
|
4446
4485
|
args: Record<string, unknown>,
|
|
4447
4486
|
): void {
|
|
4487
|
+
if (output.content.some(block => block.type === "toolCall" && block.id === toolCallId)) return;
|
|
4448
4488
|
const block: ToolCallState = {
|
|
4449
4489
|
type: "toolCall",
|
|
4450
4490
|
id: toolCallId,
|
|
@@ -4518,33 +4558,19 @@ function processInteractionUpdate(
|
|
|
4518
4558
|
state.setThinkingBlock(null);
|
|
4519
4559
|
}
|
|
4520
4560
|
} else if (updateCase === "toolCallStarted" && isExecOwnedToolCall(update.message.value.toolCall)) {
|
|
4521
|
-
//
|
|
4522
|
-
//
|
|
4561
|
+
// Exec owns MCP and Pi tools. Suppress interaction announcements and
|
|
4562
|
+
// synthesize canonical, provider-resolved blocks before executing them.
|
|
4523
4563
|
log("exec", "streamedToolCallOwnedByExec", { case: update.message.value.toolCall?.tool?.case });
|
|
4524
4564
|
} else if (updateCase === "toolCallStarted") {
|
|
4525
4565
|
const toolCall = update.message.value.toolCall;
|
|
4526
4566
|
if (toolCall) {
|
|
4527
|
-
const
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
const block: ToolCallState = {
|
|
4531
|
-
type: "toolCall",
|
|
4532
|
-
id: args.toolCallId || crypto.randomUUID(),
|
|
4533
|
-
name: args.name || args.toolName || "",
|
|
4534
|
-
arguments: {},
|
|
4535
|
-
index: output.content.length,
|
|
4536
|
-
partialJson: "",
|
|
4537
|
-
kind: "mcp",
|
|
4538
|
-
};
|
|
4539
|
-
output.content.push(block);
|
|
4540
|
-
state.setToolCall(block);
|
|
4541
|
-
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
4542
|
-
return;
|
|
4543
|
-
}
|
|
4567
|
+
const callId = update.message.value.callId || crypto.randomUUID();
|
|
4568
|
+
// Repeated announcements describe the same provider-owned execution.
|
|
4569
|
+
if (output.content.some(block => block.type === "toolCall" && block.id === callId)) return;
|
|
4544
4570
|
|
|
4545
4571
|
const todoArgs = buildTodoWriteArgs(toolCall);
|
|
4546
4572
|
if (todoArgs) {
|
|
4547
|
-
|
|
4573
|
+
// Cursor owns its todo state; this snapshot is for display, not local { ops } dispatch.
|
|
4548
4574
|
const block: ToolCallState = {
|
|
4549
4575
|
type: "toolCall",
|
|
4550
4576
|
id: callId,
|
|
@@ -4552,6 +4578,7 @@ function processInteractionUpdate(
|
|
|
4552
4578
|
arguments: todoArgs,
|
|
4553
4579
|
index: output.content.length,
|
|
4554
4580
|
kind: "todo_write",
|
|
4581
|
+
[kProviderResolvedToolCall]: true,
|
|
4555
4582
|
};
|
|
4556
4583
|
output.content.push(block);
|
|
4557
4584
|
state.setToolCall(block);
|
|
@@ -4562,44 +4589,26 @@ function processInteractionUpdate(
|
|
|
4562
4589
|
// Fallback: cursor-native tool variants (shell/glob/grep/…) we don't model
|
|
4563
4590
|
// explicitly. Render them so the call and its result are visible instead of
|
|
4564
4591
|
// vanishing.
|
|
4565
|
-
const nativeBlock = buildNativeToolCallBlock(
|
|
4566
|
-
toolCall,
|
|
4567
|
-
update.message.value.callId || crypto.randomUUID(),
|
|
4568
|
-
output.content.length,
|
|
4569
|
-
);
|
|
4592
|
+
const nativeBlock = buildNativeToolCallBlock(toolCall, callId, output.content.length);
|
|
4570
4593
|
if (nativeBlock) {
|
|
4571
4594
|
output.content.push(nativeBlock);
|
|
4572
4595
|
state.setToolCall(nativeBlock);
|
|
4573
4596
|
stream.push({ type: "toolcall_start", contentIndex: output.content.length - 1, partial: output });
|
|
4574
4597
|
}
|
|
4575
4598
|
}
|
|
4576
|
-
} else if (updateCase === "toolCallDelta" || updateCase === "partialToolCall") {
|
|
4577
|
-
if (state.currentToolCall?.kind === "mcp") {
|
|
4578
|
-
const delta = update.message.value.argsTextDelta || "";
|
|
4579
|
-
state.currentToolCall.partialJson = `${state.currentToolCall.partialJson ?? ""}${delta}`;
|
|
4580
|
-
state.currentToolCall.arguments = parseStreamingJson(state.currentToolCall.partialJson ?? "");
|
|
4581
|
-
const idx = output.content.indexOf(state.currentToolCall);
|
|
4582
|
-
stream.push({ type: "toolcall_delta", contentIndex: idx, delta, partial: output });
|
|
4583
|
-
}
|
|
4584
4599
|
} else if (updateCase === "toolCallCompleted") {
|
|
4600
|
+
if (isExecOwnedToolCall(update.message.value.toolCall)) return;
|
|
4585
4601
|
if (state.currentToolCall) {
|
|
4586
4602
|
const toolCall = update.message.value.toolCall;
|
|
4587
|
-
if (state.currentToolCall.kind === "
|
|
4588
|
-
captureUnicodeEscapeEvidence(state.currentToolCall, state.currentToolCall.partialJson ?? "");
|
|
4589
|
-
const decodedArgs = decodeMcpArgsMap(selectMcpToolCall(toolCall)?.args?.args);
|
|
4590
|
-
if (decodedArgs) {
|
|
4591
|
-
state.currentToolCall.arguments = decodedArgs;
|
|
4592
|
-
}
|
|
4593
|
-
} else if (state.currentToolCall.kind === "todo_write" && toolCall) {
|
|
4603
|
+
if (state.currentToolCall.kind === "todo_write" && toolCall) {
|
|
4594
4604
|
const todoArgs = buildTodoWriteArgs(toolCall);
|
|
4595
4605
|
if (todoArgs) {
|
|
4596
4606
|
state.currentToolCall.arguments = todoArgs;
|
|
4597
4607
|
}
|
|
4598
4608
|
}
|
|
4599
4609
|
const idx = output.content.indexOf(state.currentToolCall);
|
|
4600
|
-
delete (state.currentToolCall as
|
|
4601
|
-
delete (state.currentToolCall as
|
|
4602
|
-
delete (state.currentToolCall as any).kind;
|
|
4610
|
+
delete (state.currentToolCall as Partial<ToolCallState>).index;
|
|
4611
|
+
delete (state.currentToolCall as Partial<ToolCallState>).kind;
|
|
4603
4612
|
stream.push({ type: "toolcall_end", contentIndex: idx, toolCall: state.currentToolCall, partial: output });
|
|
4604
4613
|
state.setToolCall(null);
|
|
4605
4614
|
}
|
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
Api,
|
|
17
17
|
AssistantMessage,
|
|
18
18
|
Context,
|
|
19
|
+
GoogleGeminiCliDiagnostics,
|
|
19
20
|
Model,
|
|
20
21
|
StreamFunction,
|
|
21
22
|
StreamOptions,
|
|
@@ -27,6 +28,7 @@ import { normalizeSystemPrompts } from "../utils";
|
|
|
27
28
|
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
28
29
|
import { transportFailureFacts } from "../utils/fallback-transport";
|
|
29
30
|
import { appendRawHttpRequestDumpFor400, type RawHttpRequestDump, withHttpStatus } from "../utils/http-inspector";
|
|
31
|
+
import { FirstEventTimeoutError } from "../utils/idle-iterator";
|
|
30
32
|
import { resolveRetryBudget } from "../utils/retry-budget";
|
|
31
33
|
// Refresh is the sole responsibility of AuthStorage (broker-aware, single-flighted);
|
|
32
34
|
// the stream provider trusts the access token threaded through `options.apiKey`.
|
|
@@ -325,6 +327,29 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
325
327
|
timestamp: Date.now(),
|
|
326
328
|
};
|
|
327
329
|
let rawRequestDump: RawHttpRequestDump | undefined;
|
|
330
|
+
const googleGeminiCliDiagnostics: GoogleGeminiCliDiagnostics = {
|
|
331
|
+
chunkCounts: { content: 0, thinking: 0, functionCall: 0, usageOnly: 0, other: 0 },
|
|
332
|
+
};
|
|
333
|
+
(
|
|
334
|
+
stream as AssistantMessageEventStream & { googleGeminiCliDiagnostics?: GoogleGeminiCliDiagnostics }
|
|
335
|
+
).googleGeminiCliDiagnostics = googleGeminiCliDiagnostics;
|
|
336
|
+
const recordTimeoutDiagnostics = (): void => {
|
|
337
|
+
const reason = options?.signal?.reason;
|
|
338
|
+
if (!(reason instanceof FirstEventTimeoutError)) return;
|
|
339
|
+
googleGeminiCliDiagnostics.firstEventTimeoutMs = reason.firstEventTimeoutMs;
|
|
340
|
+
googleGeminiCliDiagnostics.firstEventTimeoutSource = reason.firstEventTimeoutSource;
|
|
341
|
+
(
|
|
342
|
+
reason as FirstEventTimeoutError & {
|
|
343
|
+
googleGeminiCliDiagnostics?: GoogleGeminiCliDiagnostics;
|
|
344
|
+
}
|
|
345
|
+
).googleGeminiCliDiagnostics = googleGeminiCliDiagnostics;
|
|
346
|
+
};
|
|
347
|
+
options?.signal?.addEventListener("abort", recordTimeoutDiagnostics, { once: true });
|
|
348
|
+
const recordResponse = (): void => {
|
|
349
|
+
if (googleGeminiCliDiagnostics.responseAtMs === undefined) {
|
|
350
|
+
googleGeminiCliDiagnostics.responseAtMs = Date.now() - startTime;
|
|
351
|
+
}
|
|
352
|
+
};
|
|
328
353
|
|
|
329
354
|
try {
|
|
330
355
|
const apiKeyRaw = options?.apiKey;
|
|
@@ -405,6 +430,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
405
430
|
fetch: options?.fetch,
|
|
406
431
|
},
|
|
407
432
|
);
|
|
433
|
+
recordResponse();
|
|
408
434
|
if (!response.ok && sentForcedToolChoice) {
|
|
409
435
|
const errorText = await response.text();
|
|
410
436
|
const error = createGeminiCliHttpError(response, errorText);
|
|
@@ -447,6 +473,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
447
473
|
fetch: options?.fetch,
|
|
448
474
|
},
|
|
449
475
|
);
|
|
476
|
+
recordResponse();
|
|
450
477
|
} else {
|
|
451
478
|
throw error;
|
|
452
479
|
}
|
|
@@ -496,20 +523,29 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
496
523
|
for await (const chunk of readSseJson<CloudCodeAssistResponseChunk>(
|
|
497
524
|
activeResponse.body!,
|
|
498
525
|
options?.signal,
|
|
499
|
-
event =>
|
|
526
|
+
event => {
|
|
527
|
+
if (googleGeminiCliDiagnostics.firstRawSseAtMs === undefined) {
|
|
528
|
+
googleGeminiCliDiagnostics.firstRawSseAtMs = Date.now() - startTime;
|
|
529
|
+
}
|
|
500
530
|
options?.onSseEvent?.(
|
|
501
531
|
{ event: event.event, data: event.data, raw: [...event.raw] },
|
|
502
532
|
model,
|
|
503
533
|
options?.attemptScope,
|
|
504
|
-
)
|
|
534
|
+
);
|
|
535
|
+
},
|
|
505
536
|
)) {
|
|
506
537
|
const responseData = chunk.response;
|
|
507
|
-
if (!responseData)
|
|
538
|
+
if (!responseData) {
|
|
539
|
+
googleGeminiCliDiagnostics.chunkCounts.other += 1;
|
|
540
|
+
continue;
|
|
541
|
+
}
|
|
508
542
|
|
|
509
543
|
const candidate = responseData.candidates?.[0];
|
|
544
|
+
let chunkKind: keyof GoogleGeminiCliDiagnostics["chunkCounts"] = "other";
|
|
510
545
|
if (candidate?.content?.parts) {
|
|
511
546
|
for (const part of candidate.content.parts) {
|
|
512
547
|
if (part.text !== undefined) {
|
|
548
|
+
chunkKind = isThinkingPart(part) ? "thinking" : "content";
|
|
513
549
|
hasContent = true;
|
|
514
550
|
const isThinking = isThinkingPart(part);
|
|
515
551
|
if (
|
|
@@ -550,6 +586,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
550
586
|
}
|
|
551
587
|
|
|
552
588
|
if (part.functionCall) {
|
|
589
|
+
chunkKind = "functionCall";
|
|
553
590
|
hasContent = true;
|
|
554
591
|
if (currentBlock) {
|
|
555
592
|
pushBlockEndEvent(currentBlock, blockIndex(), output, stream);
|
|
@@ -618,6 +655,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
618
655
|
}
|
|
619
656
|
|
|
620
657
|
if (responseData.usageMetadata) {
|
|
658
|
+
if (chunkKind === "other") chunkKind = "usageOnly";
|
|
621
659
|
// promptTokenCount includes cachedContentTokenCount, so subtract to get fresh input
|
|
622
660
|
const promptTokens = responseData.usageMetadata.promptTokenCount || 0;
|
|
623
661
|
const cacheReadTokens = responseData.usageMetadata.cachedContentTokenCount || 0;
|
|
@@ -639,6 +677,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
639
677
|
};
|
|
640
678
|
calculateCost(model, output.usage);
|
|
641
679
|
}
|
|
680
|
+
googleGeminiCliDiagnostics.chunkCounts[chunkKind] += 1;
|
|
642
681
|
}
|
|
643
682
|
|
|
644
683
|
if (currentBlock) {
|
|
@@ -676,6 +715,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
676
715
|
body: requestBodyJson,
|
|
677
716
|
signal: options?.signal,
|
|
678
717
|
});
|
|
718
|
+
recordResponse();
|
|
679
719
|
|
|
680
720
|
if (!currentResponse.ok) {
|
|
681
721
|
const retryErrorText = await currentResponse.text();
|
|
@@ -711,6 +751,11 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
711
751
|
stream.push({ type: "done", reason: output.stopReason, message: output });
|
|
712
752
|
stream.end();
|
|
713
753
|
} catch (error) {
|
|
754
|
+
const timeoutError = options?.signal?.reason;
|
|
755
|
+
if (timeoutError instanceof FirstEventTimeoutError) {
|
|
756
|
+
googleGeminiCliDiagnostics.firstEventTimeoutMs = timeoutError.firstEventTimeoutMs;
|
|
757
|
+
googleGeminiCliDiagnostics.firstEventTimeoutSource = timeoutError.firstEventTimeoutSource;
|
|
758
|
+
}
|
|
714
759
|
for (const block of output.content) {
|
|
715
760
|
if ("index" in block) {
|
|
716
761
|
delete (block as { index?: number }).index;
|
|
@@ -719,6 +764,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
719
764
|
output.stopReason = options?.signal?.aborted ? "aborted" : "error";
|
|
720
765
|
output.errorStatus = extractHttpStatusFromError(error);
|
|
721
766
|
output.transportFailure = transportFailureFacts(error);
|
|
767
|
+
output.googleGeminiCliDiagnostics = googleGeminiCliDiagnostics;
|
|
722
768
|
output.errorMessage = await appendRawHttpRequestDumpFor400(
|
|
723
769
|
error instanceof Error ? error.message : JSON.stringify(error),
|
|
724
770
|
error,
|
|
@@ -5,6 +5,7 @@ export declare function kiroApiRegion(options?: {
|
|
|
5
5
|
}): string;
|
|
6
6
|
export declare function kiroApiBaseUrl(region: string): string;
|
|
7
7
|
export declare function toKiroModelId(modelId: string): string;
|
|
8
|
+
export declare function sanitizeKiroError(value: unknown, secret?: string): string;
|
|
8
9
|
export declare function kiroApiStaticModels(): Model<"kiro-codewhisperer-stream">[];
|
|
9
10
|
/** Discover models this API key can use. Returns null when the key is missing. */
|
|
10
11
|
export declare function fetchKiroApiModels(apiKey: string, region?: string): Promise<Model<"kiro-codewhisperer-stream">[]>;
|
|
@@ -118,7 +118,7 @@ function kiroApiHeaders(apiKey: string, target: string): Record<string, string>
|
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
function sanitizeKiroError(value: unknown, secret?: string): string {
|
|
121
|
+
export function sanitizeKiroError(value: unknown, secret?: string): string {
|
|
122
122
|
let message = value instanceof Error ? value.message : String(value);
|
|
123
123
|
if (secret) message = message.split(secret).join("[redacted]");
|
|
124
124
|
message = message.replace(/bearer\s+[^\s,;]+/gi, "Bearer [redacted]");
|