@oh-my-pi/pi-ai 8.0.20 → 8.2.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/README.md +11 -12
- package/package.json +49 -26
- package/src/cli.ts +7 -7
- package/src/index.ts +2 -1
- package/src/models.generated.ts +100 -101
- package/src/providers/amazon-bedrock.ts +12 -13
- package/src/providers/anthropic.ts +67 -37
- package/src/providers/cursor.ts +57 -57
- package/src/providers/google-gemini-cli-usage.ts +2 -2
- package/src/providers/google-gemini-cli.ts +8 -10
- package/src/providers/google-shared.ts +12 -13
- package/src/providers/google-vertex.ts +7 -7
- package/src/providers/google.ts +8 -8
- package/src/providers/openai-codex/request-transformer.ts +6 -6
- package/src/providers/openai-codex-responses.ts +28 -28
- package/src/providers/openai-completions.ts +39 -39
- package/src/providers/openai-responses.ts +31 -31
- package/src/providers/transform-messages.ts +3 -3
- package/src/storage.ts +29 -19
- package/src/stream.ts +6 -6
- package/src/types.ts +1 -2
- package/src/usage/claude.ts +4 -4
- package/src/usage/github-copilot.ts +3 -4
- package/src/usage/google-antigravity.ts +3 -3
- package/src/usage/openai-codex.ts +4 -4
- package/src/usage/zai.ts +3 -3
- package/src/usage.ts +0 -1
- package/src/utils/event-stream.ts +4 -4
- package/src/utils/oauth/anthropic.ts +0 -1
- package/src/utils/oauth/callback-server.ts +2 -3
- package/src/utils/oauth/github-copilot.ts +2 -3
- package/src/utils/oauth/google-antigravity.ts +0 -1
- package/src/utils/oauth/google-gemini-cli.ts +2 -3
- package/src/utils/oauth/index.ts +11 -12
- package/src/utils/oauth/openai-codex.ts +0 -1
- package/src/utils/overflow.ts +2 -2
- package/src/utils/retry.ts +78 -0
- package/src/utils/validation.ts +4 -5
- package/tsconfig.json +0 -42
|
@@ -17,8 +17,7 @@ import {
|
|
|
17
17
|
type ToolConfiguration,
|
|
18
18
|
ToolResultStatus,
|
|
19
19
|
} from "@aws-sdk/client-bedrock-runtime";
|
|
20
|
-
|
|
21
|
-
import { calculateCost } from "@oh-my-pi/pi-ai/models";
|
|
20
|
+
import { calculateCost } from "../models";
|
|
22
21
|
import type {
|
|
23
22
|
Api,
|
|
24
23
|
AssistantMessage,
|
|
@@ -34,10 +33,10 @@ import type {
|
|
|
34
33
|
Tool,
|
|
35
34
|
ToolCall,
|
|
36
35
|
ToolResultMessage,
|
|
37
|
-
} from "
|
|
38
|
-
import { AssistantMessageEventStream } from "
|
|
39
|
-
import { parseStreamingJson } from "
|
|
40
|
-
import { sanitizeSurrogates } from "
|
|
36
|
+
} from "../types";
|
|
37
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
38
|
+
import { parseStreamingJson } from "../utils/json-parse";
|
|
39
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
41
40
|
import { transformMessages } from "./transform-messages";
|
|
42
41
|
|
|
43
42
|
export interface BedrockOptions extends StreamOptions {
|
|
@@ -200,7 +199,7 @@ function handleContentBlockDelta(
|
|
|
200
199
|
): void {
|
|
201
200
|
const contentBlockIndex = event.contentBlockIndex!;
|
|
202
201
|
const delta = event.delta;
|
|
203
|
-
let index = blocks.findIndex(
|
|
202
|
+
let index = blocks.findIndex(b => b.index === contentBlockIndex);
|
|
204
203
|
let block = blocks[index];
|
|
205
204
|
|
|
206
205
|
if (delta?.text !== undefined) {
|
|
@@ -271,7 +270,7 @@ function handleContentBlockStop(
|
|
|
271
270
|
output: AssistantMessage,
|
|
272
271
|
stream: AssistantMessageEventStream,
|
|
273
272
|
): void {
|
|
274
|
-
const index = blocks.findIndex(
|
|
273
|
+
const index = blocks.findIndex(b => b.index === event.contentBlockIndex);
|
|
275
274
|
const block = blocks[index];
|
|
276
275
|
if (!block) return;
|
|
277
276
|
delete (block as Block).index;
|
|
@@ -351,7 +350,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
351
350
|
});
|
|
352
351
|
} else {
|
|
353
352
|
const contentBlocks = m.content
|
|
354
|
-
.map(
|
|
353
|
+
.map(c => {
|
|
355
354
|
switch (c.type) {
|
|
356
355
|
case "text":
|
|
357
356
|
return { text: sanitizeSurrogates(c.text) };
|
|
@@ -361,7 +360,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
361
360
|
throw new Error("Unknown user content type");
|
|
362
361
|
}
|
|
363
362
|
})
|
|
364
|
-
.filter(
|
|
363
|
+
.filter(block => {
|
|
365
364
|
// Filter out empty text blocks
|
|
366
365
|
if ("text" in block && block.text) {
|
|
367
366
|
return block.text.trim().length > 0;
|
|
@@ -442,7 +441,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
442
441
|
toolResults.push({
|
|
443
442
|
toolResult: {
|
|
444
443
|
toolUseId: sanitizeToolCallId(m.toolCallId),
|
|
445
|
-
content: m.content.map(
|
|
444
|
+
content: m.content.map(c =>
|
|
446
445
|
c.type === "image"
|
|
447
446
|
? { image: createImageBlock(c.mimeType, c.data) }
|
|
448
447
|
: { text: sanitizeSurrogates(c.text) },
|
|
@@ -458,7 +457,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream
|
|
|
458
457
|
toolResults.push({
|
|
459
458
|
toolResult: {
|
|
460
459
|
toolUseId: sanitizeToolCallId(nextMsg.toolCallId),
|
|
461
|
-
content: nextMsg.content.map(
|
|
460
|
+
content: nextMsg.content.map(c =>
|
|
462
461
|
c.type === "image"
|
|
463
462
|
? { image: createImageBlock(c.mimeType, c.data) }
|
|
464
463
|
: { text: sanitizeSurrogates(c.text) },
|
|
@@ -500,7 +499,7 @@ function convertToolConfig(
|
|
|
500
499
|
): ToolConfiguration | undefined {
|
|
501
500
|
if (!tools?.length || toolChoice === "none") return undefined;
|
|
502
501
|
|
|
503
|
-
const bedrockTools: BedrockTool[] = tools.map(
|
|
502
|
+
const bedrockTools: BedrockTool[] = tools.map(tool => ({
|
|
504
503
|
toolSpec: {
|
|
505
504
|
name: tool.name,
|
|
506
505
|
description: tool.description,
|
|
@@ -4,8 +4,8 @@ import type {
|
|
|
4
4
|
MessageCreateParamsStreaming,
|
|
5
5
|
MessageParam,
|
|
6
6
|
} from "@anthropic-ai/sdk/resources/messages";
|
|
7
|
-
import { calculateCost } from "
|
|
8
|
-
import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "
|
|
7
|
+
import { calculateCost } from "../models";
|
|
8
|
+
import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "../stream";
|
|
9
9
|
import type {
|
|
10
10
|
Api,
|
|
11
11
|
AssistantMessage,
|
|
@@ -21,12 +21,11 @@ import type {
|
|
|
21
21
|
Tool,
|
|
22
22
|
ToolCall,
|
|
23
23
|
ToolResultMessage,
|
|
24
|
-
} from "
|
|
25
|
-
import { AssistantMessageEventStream } from "
|
|
26
|
-
import { parseStreamingJson } from "
|
|
27
|
-
import { formatErrorMessageWithRetryAfter } from "
|
|
28
|
-
import { sanitizeSurrogates } from "
|
|
29
|
-
|
|
24
|
+
} from "../types";
|
|
25
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
26
|
+
import { parseStreamingJson } from "../utils/json-parse";
|
|
27
|
+
import { formatErrorMessageWithRetryAfter } from "../utils/retry-after";
|
|
28
|
+
import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
30
29
|
import { transformMessages } from "./transform-messages";
|
|
31
30
|
|
|
32
31
|
// Stealth mode: Mimic Claude Code headers and tool prefixing.
|
|
@@ -89,13 +88,13 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]):
|
|
|
89
88
|
}
|
|
90
89
|
> {
|
|
91
90
|
// If only text blocks, return as concatenated string for simplicity
|
|
92
|
-
const hasImages = content.some(
|
|
91
|
+
const hasImages = content.some(c => c.type === "image");
|
|
93
92
|
if (!hasImages) {
|
|
94
|
-
return sanitizeSurrogates(content.map(
|
|
93
|
+
return sanitizeSurrogates(content.map(c => (c as TextContent).text).join("\n"));
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
// If we have images, convert to content block array
|
|
98
|
-
const blocks = content.map(
|
|
97
|
+
const blocks = content.map(block => {
|
|
99
98
|
if (block.type === "text") {
|
|
100
99
|
return {
|
|
101
100
|
type: "text" as const,
|
|
@@ -113,7 +112,7 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]):
|
|
|
113
112
|
});
|
|
114
113
|
|
|
115
114
|
// If only images (no text), add placeholder text block
|
|
116
|
-
const hasText = blocks.some(
|
|
115
|
+
const hasText = blocks.some(b => b.type === "text");
|
|
117
116
|
if (!hasText) {
|
|
118
117
|
blocks.unshift({
|
|
119
118
|
type: "text" as const,
|
|
@@ -218,7 +217,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
218
217
|
}
|
|
219
218
|
} else if (event.type === "content_block_delta") {
|
|
220
219
|
if (event.delta.type === "text_delta") {
|
|
221
|
-
const index = blocks.findIndex(
|
|
220
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
222
221
|
const block = blocks[index];
|
|
223
222
|
if (block && block.type === "text") {
|
|
224
223
|
block.text += event.delta.text;
|
|
@@ -230,7 +229,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
230
229
|
});
|
|
231
230
|
}
|
|
232
231
|
} else if (event.delta.type === "thinking_delta") {
|
|
233
|
-
const index = blocks.findIndex(
|
|
232
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
234
233
|
const block = blocks[index];
|
|
235
234
|
if (block && block.type === "thinking") {
|
|
236
235
|
block.thinking += event.delta.thinking;
|
|
@@ -242,7 +241,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
242
241
|
});
|
|
243
242
|
}
|
|
244
243
|
} else if (event.delta.type === "input_json_delta") {
|
|
245
|
-
const index = blocks.findIndex(
|
|
244
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
246
245
|
const block = blocks[index];
|
|
247
246
|
if (block && block.type === "toolCall") {
|
|
248
247
|
block.partialJson += event.delta.partial_json;
|
|
@@ -255,7 +254,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
255
254
|
});
|
|
256
255
|
}
|
|
257
256
|
} else if (event.delta.type === "signature_delta") {
|
|
258
|
-
const index = blocks.findIndex(
|
|
257
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
259
258
|
const block = blocks[index];
|
|
260
259
|
if (block && block.type === "thinking") {
|
|
261
260
|
block.thinkingSignature = block.thinkingSignature || "";
|
|
@@ -263,7 +262,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
263
262
|
}
|
|
264
263
|
}
|
|
265
264
|
} else if (event.type === "content_block_stop") {
|
|
266
|
-
const index = blocks.findIndex(
|
|
265
|
+
const index = blocks.findIndex(b => b.index === event.index);
|
|
267
266
|
const block = blocks[index];
|
|
268
267
|
if (block) {
|
|
269
268
|
delete (block as any).index;
|
|
@@ -360,7 +359,7 @@ function isAnthropicBaseUrl(baseUrl?: string): boolean {
|
|
|
360
359
|
export function normalizeExtraBetas(betas?: string[] | string): string[] {
|
|
361
360
|
if (!betas) return [];
|
|
362
361
|
const raw = Array.isArray(betas) ? betas : betas.split(",");
|
|
363
|
-
return raw.map(
|
|
362
|
+
return raw.map(beta => beta.trim()).filter(beta => beta.length > 0);
|
|
364
363
|
}
|
|
365
364
|
|
|
366
365
|
// Build deduplicated beta header string
|
|
@@ -406,7 +405,7 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record<s
|
|
|
406
405
|
"X-App",
|
|
407
406
|
"Authorization",
|
|
408
407
|
"X-Api-Key",
|
|
409
|
-
].map(
|
|
408
|
+
].map(key => key.toLowerCase()),
|
|
410
409
|
);
|
|
411
410
|
const modelHeaders = Object.fromEntries(
|
|
412
411
|
Object.entries(options.modelHeaders ?? {}).filter(([key]) => !enforcedHeaderKeys.has(key.toLowerCase())),
|
|
@@ -687,7 +686,7 @@ function applyPromptCaching(params: MessageCreateParamsStreaming): void {
|
|
|
687
686
|
// 3. Cache penultimate user message for conversation history caching
|
|
688
687
|
const userIndexes = params.messages
|
|
689
688
|
.map((message, index) => (message.role === "user" ? index : -1))
|
|
690
|
-
.filter(
|
|
689
|
+
.filter(index => index >= 0);
|
|
691
690
|
|
|
692
691
|
if (userIndexes.length >= 2) {
|
|
693
692
|
const penultimateUserIndex = userIndexes[userIndexes.length - 2];
|
|
@@ -727,6 +726,8 @@ function convertMessages(
|
|
|
727
726
|
isOAuthToken: boolean,
|
|
728
727
|
): MessageParam[] {
|
|
729
728
|
const params: MessageParam[] = [];
|
|
729
|
+
// Track tool call IDs from skipped assistant messages to also skip their results
|
|
730
|
+
let skippedToolCallIds: string[] | null = null;
|
|
730
731
|
|
|
731
732
|
// Transform messages for cross-provider compatibility
|
|
732
733
|
const transformedMessages = transformMessages(messages, model);
|
|
@@ -746,7 +747,7 @@ function convertMessages(
|
|
|
746
747
|
});
|
|
747
748
|
}
|
|
748
749
|
} else if (Array.isArray(msg.content)) {
|
|
749
|
-
const blocks: Array<ContentBlockParam & CacheControlBlock> = msg.content.map(
|
|
750
|
+
const blocks: Array<ContentBlockParam & CacheControlBlock> = msg.content.map(item => {
|
|
750
751
|
if (item.type === "text") {
|
|
751
752
|
return {
|
|
752
753
|
type: "text",
|
|
@@ -762,8 +763,8 @@ function convertMessages(
|
|
|
762
763
|
},
|
|
763
764
|
};
|
|
764
765
|
});
|
|
765
|
-
let filteredBlocks = !model?.input.includes("image") ? blocks.filter(
|
|
766
|
-
filteredBlocks = filteredBlocks.filter(
|
|
766
|
+
let filteredBlocks = !model?.input.includes("image") ? blocks.filter(b => b.type !== "image") : blocks;
|
|
767
|
+
filteredBlocks = filteredBlocks.filter(b => {
|
|
767
768
|
if (b.type === "text") {
|
|
768
769
|
return b.text.trim().length > 0;
|
|
769
770
|
}
|
|
@@ -779,6 +780,31 @@ function convertMessages(
|
|
|
779
780
|
// Skip messages with undefined/null content
|
|
780
781
|
if (!msg.content || !Array.isArray(msg.content)) continue;
|
|
781
782
|
|
|
783
|
+
// When interleaved thinking is enabled, Anthropic requires the last assistant
|
|
784
|
+
// message to start with a thinking block. If the first content block is a thinking
|
|
785
|
+
// block with a missing/invalid signature (e.g., from aborted stream), we must skip
|
|
786
|
+
// the entire message to avoid API rejection. Checking the first non-empty block.
|
|
787
|
+
const firstContentBlock = msg.content.find(
|
|
788
|
+
b =>
|
|
789
|
+
(b.type === "text" && b.text.trim().length > 0) ||
|
|
790
|
+
(b.type === "thinking" && b.thinking.trim().length > 0) ||
|
|
791
|
+
b.type === "toolCall",
|
|
792
|
+
);
|
|
793
|
+
if (
|
|
794
|
+
firstContentBlock?.type === "thinking" &&
|
|
795
|
+
(!firstContentBlock.thinkingSignature || firstContentBlock.thinkingSignature.trim().length === 0)
|
|
796
|
+
) {
|
|
797
|
+
// Skip this assistant message - it has corrupt thinking that would break the API.
|
|
798
|
+
// Also track any tool calls in this message so we can skip their results.
|
|
799
|
+
for (const block of msg.content) {
|
|
800
|
+
if (block.type === "toolCall") {
|
|
801
|
+
skippedToolCallIds ??= [];
|
|
802
|
+
skippedToolCallIds.push(block.id);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
|
|
782
808
|
const blocks: Array<ContentBlockParam & CacheControlBlock> = [];
|
|
783
809
|
|
|
784
810
|
for (const block of msg.content) {
|
|
@@ -824,23 +850,27 @@ function convertMessages(
|
|
|
824
850
|
const toolResults: Array<ContentBlockParam & CacheControlBlock> = [];
|
|
825
851
|
|
|
826
852
|
// Add the current tool result
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
853
|
+
if (!skippedToolCallIds?.includes(msg.toolCallId)) {
|
|
854
|
+
toolResults.push({
|
|
855
|
+
type: "tool_result",
|
|
856
|
+
tool_use_id: sanitizeToolCallId(msg.toolCallId),
|
|
857
|
+
content: convertContentBlocks(msg.content),
|
|
858
|
+
is_error: msg.isError,
|
|
859
|
+
});
|
|
860
|
+
}
|
|
833
861
|
|
|
834
862
|
// Look ahead for consecutive toolResult messages
|
|
835
863
|
let j = i + 1;
|
|
836
864
|
while (j < transformedMessages.length && transformedMessages[j].role === "toolResult") {
|
|
837
865
|
const nextMsg = transformedMessages[j] as ToolResultMessage; // We know it's a toolResult
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
866
|
+
if (!skippedToolCallIds?.includes(nextMsg.toolCallId)) {
|
|
867
|
+
toolResults.push({
|
|
868
|
+
type: "tool_result",
|
|
869
|
+
tool_use_id: sanitizeToolCallId(nextMsg.toolCallId),
|
|
870
|
+
content: convertContentBlocks(nextMsg.content),
|
|
871
|
+
is_error: nextMsg.isError,
|
|
872
|
+
});
|
|
873
|
+
}
|
|
844
874
|
j++;
|
|
845
875
|
}
|
|
846
876
|
|
|
@@ -858,7 +888,7 @@ function convertMessages(
|
|
|
858
888
|
}
|
|
859
889
|
|
|
860
890
|
// Final validation: filter out any messages with invalid content
|
|
861
|
-
return params.filter(
|
|
891
|
+
return params.filter(msg => {
|
|
862
892
|
if (!msg.content) return false;
|
|
863
893
|
if (typeof msg.content === "string") return msg.content.length > 0;
|
|
864
894
|
if (Array.isArray(msg.content)) return msg.content.length > 0;
|
|
@@ -869,7 +899,7 @@ function convertMessages(
|
|
|
869
899
|
function convertTools(tools: Tool[], isOAuthToken: boolean): Anthropic.Messages.Tool[] {
|
|
870
900
|
if (!tools) return [];
|
|
871
901
|
|
|
872
|
-
return tools.map(
|
|
902
|
+
return tools.map(tool => {
|
|
873
903
|
const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema
|
|
874
904
|
|
|
875
905
|
return {
|
package/src/providers/cursor.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
-
import
|
|
2
|
+
import * as fs from "node:fs/promises";
|
|
3
3
|
import http2 from "node:http2";
|
|
4
4
|
import { create, fromBinary, fromJson, type JsonValue, toBinary, toJson } from "@bufbuild/protobuf";
|
|
5
5
|
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
6
|
-
import
|
|
6
|
+
import JSON5 from "json5";
|
|
7
|
+
import { calculateCost } from "../models";
|
|
7
8
|
import type {
|
|
8
9
|
Api,
|
|
9
10
|
AssistantMessage,
|
|
@@ -22,11 +23,10 @@ import type {
|
|
|
22
23
|
Tool,
|
|
23
24
|
ToolCall,
|
|
24
25
|
ToolResultMessage,
|
|
25
|
-
} from "
|
|
26
|
-
import { AssistantMessageEventStream } from "
|
|
27
|
-
import { parseStreamingJson } from "
|
|
28
|
-
import { formatErrorMessageWithRetryAfter } from "
|
|
29
|
-
import JSON5 from "json5";
|
|
26
|
+
} from "../types";
|
|
27
|
+
import { AssistantMessageEventStream } from "../utils/event-stream";
|
|
28
|
+
import { parseStreamingJson } from "../utils/json-parse";
|
|
29
|
+
import { formatErrorMessageWithRetryAfter } from "../utils/retry-after";
|
|
30
30
|
import type { McpToolDefinition } from "./cursor/gen/agent_pb";
|
|
31
31
|
import {
|
|
32
32
|
AgentClientMessageSchema,
|
|
@@ -142,7 +142,7 @@ async function appendCursorDebugLog(entry: CursorLogEntry): Promise<void> {
|
|
|
142
142
|
const logPath = process.env.DEBUG_CURSOR_LOG;
|
|
143
143
|
if (!logPath) return;
|
|
144
144
|
try {
|
|
145
|
-
await appendFile(logPath, `${JSON.stringify(entry, debugReplacer)}\n`);
|
|
145
|
+
await fs.appendFile(logPath, `${JSON.stringify(entry, debugReplacer)}\n`);
|
|
146
146
|
} catch {
|
|
147
147
|
// Ignore debug log failures
|
|
148
148
|
}
|
|
@@ -245,7 +245,7 @@ function decodeLogData(value: unknown): unknown {
|
|
|
245
245
|
return value;
|
|
246
246
|
}
|
|
247
247
|
if (Array.isArray(value)) {
|
|
248
|
-
return value.map(
|
|
248
|
+
return value.map(entry => decodeLogData(entry));
|
|
249
249
|
}
|
|
250
250
|
const record = value as Record<string, unknown>;
|
|
251
251
|
const typeName = record.$typeName;
|
|
@@ -375,13 +375,13 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
375
375
|
get firstTokenTime() {
|
|
376
376
|
return firstTokenTime;
|
|
377
377
|
},
|
|
378
|
-
setTextBlock:
|
|
378
|
+
setTextBlock: b => {
|
|
379
379
|
currentTextBlock = b;
|
|
380
380
|
},
|
|
381
|
-
setThinkingBlock:
|
|
381
|
+
setThinkingBlock: b => {
|
|
382
382
|
currentThinkingBlock = b;
|
|
383
383
|
},
|
|
384
|
-
setToolCall:
|
|
384
|
+
setToolCall: t => {
|
|
385
385
|
currentToolCall = t;
|
|
386
386
|
},
|
|
387
387
|
setFirstTokenTime: () => {
|
|
@@ -427,7 +427,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
427
427
|
usageState,
|
|
428
428
|
requestContextTools,
|
|
429
429
|
onConversationCheckpoint,
|
|
430
|
-
).catch(
|
|
430
|
+
).catch(error => {
|
|
431
431
|
log("error", "handleServerMessage", { error: String(error) });
|
|
432
432
|
});
|
|
433
433
|
} catch (e) {
|
|
@@ -452,7 +452,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
452
452
|
heartbeatTimer = setInterval(sendHeartbeat, 5000);
|
|
453
453
|
|
|
454
454
|
await new Promise<void>((resolve, reject) => {
|
|
455
|
-
h2Request!.on("trailers",
|
|
455
|
+
h2Request!.on("trailers", trailers => {
|
|
456
456
|
const status = trailers["grpc-status"];
|
|
457
457
|
const msg = trailers["grpc-message"];
|
|
458
458
|
if (status && status !== "0") {
|
|
@@ -662,9 +662,9 @@ async function handleShellStreamArgs(
|
|
|
662
662
|
args as any,
|
|
663
663
|
execHandlers?.shell,
|
|
664
664
|
onToolResult,
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
665
|
+
toolResult => buildShellResultFromToolResult(args as any, toolResult),
|
|
666
|
+
reason => buildShellRejectedResult((args as any).command, (args as any).workingDirectory, reason),
|
|
667
|
+
error => buildShellFailureResult((args as any).command, (args as any).workingDirectory, error),
|
|
668
668
|
);
|
|
669
669
|
|
|
670
670
|
sendShellStreamEvent(h2Request, execMsg, { case: "start", value: create(ShellStreamStartSchema, {}) });
|
|
@@ -810,9 +810,9 @@ async function handleExecServerMessage(
|
|
|
810
810
|
args,
|
|
811
811
|
execHandlers?.read,
|
|
812
812
|
onToolResult,
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
813
|
+
toolResult => buildReadResultFromToolResult(args.path, toolResult),
|
|
814
|
+
reason => buildReadRejectedResult(args.path, reason),
|
|
815
|
+
error => buildReadErrorResult(args.path, error),
|
|
816
816
|
);
|
|
817
817
|
sendExecClientMessage(h2Request, execMsg, "readResult", execResult);
|
|
818
818
|
return;
|
|
@@ -823,9 +823,9 @@ async function handleExecServerMessage(
|
|
|
823
823
|
args,
|
|
824
824
|
execHandlers?.ls,
|
|
825
825
|
onToolResult,
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
826
|
+
toolResult => buildLsResultFromToolResult(args.path, toolResult),
|
|
827
|
+
reason => buildLsRejectedResult(args.path, reason),
|
|
828
|
+
error => buildLsErrorResult(args.path, error),
|
|
829
829
|
);
|
|
830
830
|
sendExecClientMessage(h2Request, execMsg, "lsResult", execResult);
|
|
831
831
|
return;
|
|
@@ -836,9 +836,9 @@ async function handleExecServerMessage(
|
|
|
836
836
|
args,
|
|
837
837
|
execHandlers?.grep,
|
|
838
838
|
onToolResult,
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
839
|
+
toolResult => buildGrepResultFromToolResult(args, toolResult),
|
|
840
|
+
reason => buildGrepErrorResult(reason),
|
|
841
|
+
error => buildGrepErrorResult(error),
|
|
842
842
|
);
|
|
843
843
|
sendExecClientMessage(h2Request, execMsg, "grepResult", execResult);
|
|
844
844
|
return;
|
|
@@ -849,7 +849,7 @@ async function handleExecServerMessage(
|
|
|
849
849
|
args,
|
|
850
850
|
execHandlers?.write,
|
|
851
851
|
onToolResult,
|
|
852
|
-
|
|
852
|
+
toolResult =>
|
|
853
853
|
buildWriteResultFromToolResult(
|
|
854
854
|
{
|
|
855
855
|
path: args.path,
|
|
@@ -859,8 +859,8 @@ async function handleExecServerMessage(
|
|
|
859
859
|
},
|
|
860
860
|
toolResult,
|
|
861
861
|
),
|
|
862
|
-
|
|
863
|
-
|
|
862
|
+
reason => buildWriteRejectedResult(args.path, reason),
|
|
863
|
+
error => buildWriteErrorResult(args.path, error),
|
|
864
864
|
);
|
|
865
865
|
sendExecClientMessage(h2Request, execMsg, "writeResult", execResult);
|
|
866
866
|
return;
|
|
@@ -871,9 +871,9 @@ async function handleExecServerMessage(
|
|
|
871
871
|
args,
|
|
872
872
|
execHandlers?.delete,
|
|
873
873
|
onToolResult,
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
874
|
+
toolResult => buildDeleteResultFromToolResult(args.path, toolResult),
|
|
875
|
+
reason => buildDeleteRejectedResult(args.path, reason),
|
|
876
|
+
error => buildDeleteErrorResult(args.path, error),
|
|
877
877
|
);
|
|
878
878
|
sendExecClientMessage(h2Request, execMsg, "deleteResult", execResult);
|
|
879
879
|
return;
|
|
@@ -884,9 +884,9 @@ async function handleExecServerMessage(
|
|
|
884
884
|
args,
|
|
885
885
|
execHandlers?.shell,
|
|
886
886
|
onToolResult,
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
887
|
+
toolResult => buildShellResultFromToolResult(args, toolResult),
|
|
888
|
+
reason => buildShellRejectedResult(args.command, args.workingDirectory, reason),
|
|
889
|
+
error => buildShellFailureResult(args.command, args.workingDirectory, error),
|
|
890
890
|
);
|
|
891
891
|
sendExecClientMessage(h2Request, execMsg, "shellResult", execResult);
|
|
892
892
|
return;
|
|
@@ -944,9 +944,9 @@ async function handleExecServerMessage(
|
|
|
944
944
|
args,
|
|
945
945
|
execHandlers?.diagnostics,
|
|
946
946
|
onToolResult,
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
947
|
+
toolResult => buildDiagnosticsResultFromToolResult(args.path, toolResult),
|
|
948
|
+
reason => buildDiagnosticsRejectedResult(args.path, reason),
|
|
949
|
+
error => buildDiagnosticsErrorResult(args.path, error),
|
|
950
950
|
);
|
|
951
951
|
sendExecClientMessage(h2Request, execMsg, "diagnosticsResult", execResult);
|
|
952
952
|
return;
|
|
@@ -958,9 +958,9 @@ async function handleExecServerMessage(
|
|
|
958
958
|
mcpCall,
|
|
959
959
|
execHandlers?.mcp,
|
|
960
960
|
onToolResult,
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
961
|
+
toolResult => buildMcpResultFromToolResult(mcpCall, toolResult),
|
|
962
|
+
_reason => buildMcpToolNotFoundResult(mcpCall),
|
|
963
|
+
error => buildMcpErrorResult(error),
|
|
964
964
|
);
|
|
965
965
|
sendExecClientMessage(h2Request, execMsg, "mcpResult", execResult);
|
|
966
966
|
return;
|
|
@@ -1075,7 +1075,7 @@ async function applyToolResultHandler(
|
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
1077
|
function toolResultToText(toolResult: ToolResultMessage): string {
|
|
1078
|
-
return toolResult.content.map(
|
|
1078
|
+
return toolResult.content.map(item => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n");
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
1081
|
function toolResultWasTruncated(toolResult: ToolResultMessage): boolean {
|
|
@@ -1274,8 +1274,8 @@ function buildLsResultFromToolResult(path: string, toolResult: ToolResultMessage
|
|
|
1274
1274
|
const rootPath = path || ".";
|
|
1275
1275
|
const entries = text
|
|
1276
1276
|
.split("\n")
|
|
1277
|
-
.map(
|
|
1278
|
-
.filter(
|
|
1277
|
+
.map(line => line.trim())
|
|
1278
|
+
.filter(line => line.length > 0 && !line.startsWith("["));
|
|
1279
1279
|
const childrenDirs: LsDirectoryTreeNode[] = [];
|
|
1280
1280
|
const childrenFiles: LsDirectoryTreeNode_File[] = [];
|
|
1281
1281
|
|
|
@@ -1346,8 +1346,8 @@ function buildGrepResultFromToolResult(
|
|
|
1346
1346
|
const clientTruncated = toolResultDetailBoolean(toolResult, "truncated");
|
|
1347
1347
|
const lines = text
|
|
1348
1348
|
.split("\n")
|
|
1349
|
-
.map(
|
|
1350
|
-
.filter(
|
|
1349
|
+
.map(line => line.trimEnd())
|
|
1350
|
+
.filter(line => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches"));
|
|
1351
1351
|
|
|
1352
1352
|
const workspaceKey = args.path || ".";
|
|
1353
1353
|
let unionResult: GrepUnionResult;
|
|
@@ -1367,7 +1367,7 @@ function buildGrepResultFromToolResult(
|
|
|
1367
1367
|
});
|
|
1368
1368
|
} else if (outputMode === "count") {
|
|
1369
1369
|
const counts = lines
|
|
1370
|
-
.map(
|
|
1370
|
+
.map(line => {
|
|
1371
1371
|
const separatorIndex = line.lastIndexOf(":");
|
|
1372
1372
|
if (separatorIndex === -1) {
|
|
1373
1373
|
return null;
|
|
@@ -1417,7 +1417,7 @@ function buildGrepResultFromToolResult(
|
|
|
1417
1417
|
const matches = Array.from(matchMap.entries()).map(([file, matches]) =>
|
|
1418
1418
|
create(GrepFileMatchSchema, {
|
|
1419
1419
|
file,
|
|
1420
|
-
matches: matches.map(
|
|
1420
|
+
matches: matches.map(entry =>
|
|
1421
1421
|
create(GrepContentMatchSchema, {
|
|
1422
1422
|
lineNumber: entry.line,
|
|
1423
1423
|
content: entry.content,
|
|
@@ -1586,7 +1586,7 @@ function buildTodoWriteArgs(toolCall: CursorUpdateTodosToolCall): {
|
|
|
1586
1586
|
const todos = toolCall.updateTodosToolCall?.args?.todos;
|
|
1587
1587
|
if (!todos) return null;
|
|
1588
1588
|
return {
|
|
1589
|
-
todos: todos.map(
|
|
1589
|
+
todos: todos.map(todo => ({
|
|
1590
1590
|
id: typeof todo.id === "string" && todo.id.length > 0 ? todo.id : undefined,
|
|
1591
1591
|
content: typeof todo.content === "string" ? todo.content : "",
|
|
1592
1592
|
activeForm: typeof todo.content === "string" ? todo.content : "",
|
|
@@ -1599,7 +1599,7 @@ function buildMcpResultFromToolResult(_mcpCall: CursorMcpCall, toolResult: ToolR
|
|
|
1599
1599
|
if (toolResult.isError) {
|
|
1600
1600
|
return buildMcpErrorResult(toolResultToText(toolResult) || "MCP tool failed");
|
|
1601
1601
|
}
|
|
1602
|
-
const content = toolResult.content.map(
|
|
1602
|
+
const content = toolResult.content.map(item => {
|
|
1603
1603
|
if (item.type === "image") {
|
|
1604
1604
|
return create(McpToolResultContentItemSchema, {
|
|
1605
1605
|
content: {
|
|
@@ -1810,12 +1810,12 @@ function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[]
|
|
|
1810
1810
|
return [];
|
|
1811
1811
|
}
|
|
1812
1812
|
|
|
1813
|
-
const advertisedTools = tools.filter(
|
|
1813
|
+
const advertisedTools = tools.filter(tool => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name));
|
|
1814
1814
|
if (advertisedTools.length === 0) {
|
|
1815
1815
|
return [];
|
|
1816
1816
|
}
|
|
1817
1817
|
|
|
1818
|
-
return advertisedTools.map(
|
|
1818
|
+
return advertisedTools.map(tool => {
|
|
1819
1819
|
const jsonSchema = tool.parameters as Record<string, unknown> | undefined;
|
|
1820
1820
|
const schemaValue: JsonValue =
|
|
1821
1821
|
jsonSchema && typeof jsonSchema === "object"
|
|
@@ -1841,7 +1841,7 @@ function extractUserMessageText(msg: Message): string {
|
|
|
1841
1841
|
if (typeof content === "string") return content.trim();
|
|
1842
1842
|
const text = content
|
|
1843
1843
|
.filter((c): c is TextContent => c.type === "text")
|
|
1844
|
-
.map(
|
|
1844
|
+
.map(c => c.text)
|
|
1845
1845
|
.join("\n");
|
|
1846
1846
|
return text.trim();
|
|
1847
1847
|
}
|
|
@@ -1854,7 +1854,7 @@ function extractAssistantMessageText(msg: Message): string {
|
|
|
1854
1854
|
if (!Array.isArray(msg.content)) return "";
|
|
1855
1855
|
return msg.content
|
|
1856
1856
|
.filter((c): c is TextContent => c.type === "text")
|
|
1857
|
-
.map(
|
|
1857
|
+
.map(c => c.text)
|
|
1858
1858
|
.join("\n");
|
|
1859
1859
|
}
|
|
1860
1860
|
|
|
@@ -2007,7 +2007,7 @@ function buildGrpcRequest(
|
|
|
2007
2007
|
// Build conversation turns from prior messages (excluding the last user message)
|
|
2008
2008
|
const turns = buildConversationTurns(context.messages);
|
|
2009
2009
|
|
|
2010
|
-
const hasMatchingPrompt = state.conversationState?.rootPromptMessagesJson?.some(
|
|
2010
|
+
const hasMatchingPrompt = state.conversationState?.rootPromptMessagesJson?.some(entry =>
|
|
2011
2011
|
Buffer.from(entry).equals(systemPromptId),
|
|
2012
2012
|
);
|
|
2013
2013
|
|
|
@@ -2064,7 +2064,7 @@ function buildGrpcRequest(
|
|
|
2064
2064
|
|
|
2065
2065
|
const requestBytes = toBinary(AgentClientMessageSchema, clientMessage);
|
|
2066
2066
|
|
|
2067
|
-
const toolNames = context.tools?.map(
|
|
2067
|
+
const toolNames = context.tools?.map(tool => tool.name) ?? [];
|
|
2068
2068
|
const detail =
|
|
2069
2069
|
process.env.DEBUG_CURSOR === "2"
|
|
2070
2070
|
? ` ${JSON.stringify(clientMessage.message.value, debugReplacer, 2)?.slice(0, 2000)}`
|
|
@@ -2082,6 +2082,6 @@ function buildGrpcRequest(
|
|
|
2082
2082
|
function extractText(content: (TextContent | ImageContent)[]): string {
|
|
2083
2083
|
return content
|
|
2084
2084
|
.filter((c): c is TextContent => c.type === "text")
|
|
2085
|
-
.map(
|
|
2085
|
+
.map(c => c.text)
|
|
2086
2086
|
.join("\n");
|
|
2087
2087
|
}
|
|
@@ -6,8 +6,8 @@ import type {
|
|
|
6
6
|
UsageProvider,
|
|
7
7
|
UsageReport,
|
|
8
8
|
UsageWindow,
|
|
9
|
-
} from "
|
|
10
|
-
import { refreshGoogleCloudToken } from "
|
|
9
|
+
} from "../usage";
|
|
10
|
+
import { refreshGoogleCloudToken } from "../utils/oauth/google-gemini-cli";
|
|
11
11
|
|
|
12
12
|
const DEFAULT_ENDPOINT = "https://cloudcode-pa.googleapis.com";
|
|
13
13
|
const CACHE_TTL_MS = 60_000;
|