@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.
Files changed (39) hide show
  1. package/README.md +11 -12
  2. package/package.json +49 -26
  3. package/src/cli.ts +7 -7
  4. package/src/index.ts +2 -1
  5. package/src/models.generated.ts +100 -101
  6. package/src/providers/amazon-bedrock.ts +12 -13
  7. package/src/providers/anthropic.ts +67 -37
  8. package/src/providers/cursor.ts +57 -57
  9. package/src/providers/google-gemini-cli-usage.ts +2 -2
  10. package/src/providers/google-gemini-cli.ts +8 -10
  11. package/src/providers/google-shared.ts +12 -13
  12. package/src/providers/google-vertex.ts +7 -7
  13. package/src/providers/google.ts +8 -8
  14. package/src/providers/openai-codex/request-transformer.ts +6 -6
  15. package/src/providers/openai-codex-responses.ts +28 -28
  16. package/src/providers/openai-completions.ts +39 -39
  17. package/src/providers/openai-responses.ts +31 -31
  18. package/src/providers/transform-messages.ts +3 -3
  19. package/src/storage.ts +29 -19
  20. package/src/stream.ts +6 -6
  21. package/src/types.ts +1 -2
  22. package/src/usage/claude.ts +4 -4
  23. package/src/usage/github-copilot.ts +3 -4
  24. package/src/usage/google-antigravity.ts +3 -3
  25. package/src/usage/openai-codex.ts +4 -4
  26. package/src/usage/zai.ts +3 -3
  27. package/src/usage.ts +0 -1
  28. package/src/utils/event-stream.ts +4 -4
  29. package/src/utils/oauth/anthropic.ts +0 -1
  30. package/src/utils/oauth/callback-server.ts +2 -3
  31. package/src/utils/oauth/github-copilot.ts +2 -3
  32. package/src/utils/oauth/google-antigravity.ts +0 -1
  33. package/src/utils/oauth/google-gemini-cli.ts +2 -3
  34. package/src/utils/oauth/index.ts +11 -12
  35. package/src/utils/oauth/openai-codex.ts +0 -1
  36. package/src/utils/overflow.ts +2 -2
  37. package/src/utils/retry.ts +78 -0
  38. package/src/utils/validation.ts +4 -5
  39. 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 "@oh-my-pi/pi-ai/types";
38
- import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
39
- import { parseStreamingJson } from "@oh-my-pi/pi-ai/utils/json-parse";
40
- import { sanitizeSurrogates } from "@oh-my-pi/pi-ai/utils/sanitize-unicode";
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((b) => b.index === contentBlockIndex);
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((b) => b.index === event.contentBlockIndex);
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((c) => {
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((block) => {
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((c) =>
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((c) =>
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((tool) => ({
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 "@oh-my-pi/pi-ai/models";
8
- import { getEnvApiKey, OUTPUT_FALLBACK_BUFFER } from "@oh-my-pi/pi-ai/stream";
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 "@oh-my-pi/pi-ai/types";
25
- import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
26
- import { parseStreamingJson } from "@oh-my-pi/pi-ai/utils/json-parse";
27
- import { formatErrorMessageWithRetryAfter } from "@oh-my-pi/pi-ai/utils/retry-after";
28
- import { sanitizeSurrogates } from "@oh-my-pi/pi-ai/utils/sanitize-unicode";
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((c) => c.type === "image");
91
+ const hasImages = content.some(c => c.type === "image");
93
92
  if (!hasImages) {
94
- return sanitizeSurrogates(content.map((c) => (c as TextContent).text).join("\n"));
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((block) => {
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((b) => b.type === "text");
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((b) => b.index === event.index);
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((b) => b.index === event.index);
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((b) => b.index === event.index);
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((b) => b.index === event.index);
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((b) => b.index === event.index);
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((beta) => beta.trim()).filter((beta) => beta.length > 0);
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((key) => key.toLowerCase()),
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((index) => index >= 0);
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((item) => {
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((b) => b.type !== "image") : blocks;
766
- filteredBlocks = filteredBlocks.filter((b) => {
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
- toolResults.push({
828
- type: "tool_result",
829
- tool_use_id: sanitizeToolCallId(msg.toolCallId),
830
- content: convertContentBlocks(msg.content),
831
- is_error: msg.isError,
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
- toolResults.push({
839
- type: "tool_result",
840
- tool_use_id: sanitizeToolCallId(nextMsg.toolCallId),
841
- content: convertContentBlocks(nextMsg.content),
842
- is_error: nextMsg.isError,
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((msg) => {
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((tool) => {
902
+ return tools.map(tool => {
873
903
  const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema
874
904
 
875
905
  return {
@@ -1,9 +1,10 @@
1
1
  import { createHash } from "node:crypto";
2
- import { appendFile } from "node:fs/promises";
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 { calculateCost } from "@oh-my-pi/pi-ai/models";
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 "@oh-my-pi/pi-ai/types";
26
- import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
27
- import { parseStreamingJson } from "@oh-my-pi/pi-ai/utils/json-parse";
28
- import { formatErrorMessageWithRetryAfter } from "@oh-my-pi/pi-ai/utils/retry-after";
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((entry) => decodeLogData(entry));
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: (b) => {
378
+ setTextBlock: b => {
379
379
  currentTextBlock = b;
380
380
  },
381
- setThinkingBlock: (b) => {
381
+ setThinkingBlock: b => {
382
382
  currentThinkingBlock = b;
383
383
  },
384
- setToolCall: (t) => {
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((error) => {
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", (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
- (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),
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
- (toolResult) => buildReadResultFromToolResult(args.path, toolResult),
814
- (reason) => buildReadRejectedResult(args.path, reason),
815
- (error) => buildReadErrorResult(args.path, error),
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
- (toolResult) => buildLsResultFromToolResult(args.path, toolResult),
827
- (reason) => buildLsRejectedResult(args.path, reason),
828
- (error) => buildLsErrorResult(args.path, error),
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
- (toolResult) => buildGrepResultFromToolResult(args, toolResult),
840
- (reason) => buildGrepErrorResult(reason),
841
- (error) => buildGrepErrorResult(error),
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
- (toolResult) =>
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
- (reason) => buildWriteRejectedResult(args.path, reason),
863
- (error) => buildWriteErrorResult(args.path, error),
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
- (toolResult) => buildDeleteResultFromToolResult(args.path, toolResult),
875
- (reason) => buildDeleteRejectedResult(args.path, reason),
876
- (error) => buildDeleteErrorResult(args.path, error),
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
- (toolResult) => buildShellResultFromToolResult(args, toolResult),
888
- (reason) => buildShellRejectedResult(args.command, args.workingDirectory, reason),
889
- (error) => buildShellFailureResult(args.command, args.workingDirectory, error),
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
- (toolResult) => buildDiagnosticsResultFromToolResult(args.path, toolResult),
948
- (reason) => buildDiagnosticsRejectedResult(args.path, reason),
949
- (error) => buildDiagnosticsErrorResult(args.path, error),
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
- (toolResult) => buildMcpResultFromToolResult(mcpCall, toolResult),
962
- (_reason) => buildMcpToolNotFoundResult(mcpCall),
963
- (error) => buildMcpErrorResult(error),
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((item) => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n");
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((line) => line.trim())
1278
- .filter((line) => line.length > 0 && !line.startsWith("["));
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((line) => line.trimEnd())
1350
- .filter((line) => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches"));
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((line) => {
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((entry) =>
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((todo) => ({
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((item) => {
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((tool) => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name));
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((tool) => {
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((c) => c.text)
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((c) => c.text)
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((entry) =>
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((tool) => tool.name) ?? [];
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((c) => c.text)
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 "@oh-my-pi/pi-ai/usage";
10
- import { refreshGoogleCloudToken } from "@oh-my-pi/pi-ai/utils/oauth/google-gemini-cli";
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;