@oh-my-pi/pi-ai 3.20.0 → 3.34.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 +69 -12
- package/package.json +3 -10
- package/src/cli.ts +89 -89
- package/src/index.ts +2 -2
- package/src/models.generated.ts +871 -151
- package/src/models.ts +11 -17
- package/src/providers/anthropic.ts +92 -28
- package/src/providers/google-gemini-cli.ts +268 -133
- package/src/providers/google-shared.ts +48 -5
- package/src/providers/google-vertex.ts +13 -3
- package/src/providers/google.ts +13 -3
- package/src/providers/openai-codex/index.ts +7 -0
- package/src/providers/openai-codex/prompts/codex.ts +26 -59
- package/src/providers/openai-codex/prompts/pi-codex-bridge.ts +38 -31
- package/src/providers/openai-codex/prompts/system-prompt.ts +26 -0
- package/src/providers/openai-codex/request-transformer.ts +38 -203
- package/src/providers/openai-codex-responses.ts +91 -24
- package/src/providers/openai-completions.ts +33 -26
- package/src/providers/openai-responses.ts +1 -1
- package/src/providers/transorm-messages.ts +4 -3
- package/src/stream.ts +34 -25
- package/src/types.ts +21 -4
- package/src/utils/oauth/github-copilot.ts +38 -3
- package/src/utils/oauth/google-antigravity.ts +146 -55
- package/src/utils/oauth/google-gemini-cli.ts +146 -55
- package/src/utils/oauth/index.ts +5 -5
- package/src/utils/oauth/openai-codex.ts +129 -54
- package/src/utils/overflow.ts +1 -1
- package/src/bun-imports.d.ts +0 -14
package/src/models.ts
CHANGED
|
@@ -12,34 +12,28 @@ for (const [provider, models] of Object.entries(MODELS)) {
|
|
|
12
12
|
modelRegistry.set(provider, providerModels);
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
type ProviderModels = typeof MODELS;
|
|
16
|
-
type ProviderWithModels = keyof ProviderModels;
|
|
17
|
-
|
|
18
15
|
type ModelApi<
|
|
19
|
-
TProvider extends
|
|
20
|
-
TModelId extends keyof
|
|
21
|
-
> =
|
|
16
|
+
TProvider extends KnownProvider,
|
|
17
|
+
TModelId extends keyof (typeof MODELS)[TProvider],
|
|
18
|
+
> = (typeof MODELS)[TProvider][TModelId] extends { api: infer TApi } ? (TApi extends Api ? TApi : never) : never;
|
|
22
19
|
|
|
23
|
-
export function getModel<TProvider extends
|
|
20
|
+
export function getModel<TProvider extends KnownProvider, TModelId extends keyof (typeof MODELS)[TProvider]>(
|
|
24
21
|
provider: TProvider,
|
|
25
22
|
modelId: TModelId,
|
|
26
|
-
): Model<ModelApi<TProvider, TModelId
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return modelRegistry.get(provider)?.get(modelId as string) as Model<Api> | undefined;
|
|
23
|
+
): Model<ModelApi<TProvider, TModelId>> {
|
|
24
|
+
const providerModels = modelRegistry.get(provider);
|
|
25
|
+
return providerModels?.get(modelId as string) as Model<ModelApi<TProvider, TModelId>>;
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
export function getProviders(): KnownProvider[] {
|
|
33
29
|
return Array.from(modelRegistry.keys()) as KnownProvider[];
|
|
34
30
|
}
|
|
35
31
|
|
|
36
|
-
export function getModels<TProvider extends
|
|
32
|
+
export function getModels<TProvider extends KnownProvider>(
|
|
37
33
|
provider: TProvider,
|
|
38
|
-
): Model<ModelApi<TProvider, keyof
|
|
39
|
-
export function getModels(provider: KnownProvider): Model<Api>[];
|
|
40
|
-
export function getModels(provider: KnownProvider): Model<Api>[] {
|
|
34
|
+
): Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[] {
|
|
41
35
|
const models = modelRegistry.get(provider);
|
|
42
|
-
return models ? (Array.from(models.values()) as Model<
|
|
36
|
+
return models ? (Array.from(models.values()) as Model<ModelApi<TProvider, keyof (typeof MODELS)[TProvider]>>[]) : [];
|
|
43
37
|
}
|
|
44
38
|
|
|
45
39
|
export function calculateCost<TApi extends Api>(model: Model<TApi>, usage: Usage): Usage["cost"] {
|
|
@@ -56,7 +50,7 @@ const XHIGH_MODELS = new Set(["gpt-5.1-codex-max", "gpt-5.2", "gpt-5.2-codex"]);
|
|
|
56
50
|
|
|
57
51
|
/**
|
|
58
52
|
* Check if a model supports xhigh thinking level.
|
|
59
|
-
* Currently only certain OpenAI models support this.
|
|
53
|
+
* Currently only certain OpenAI Codex models support this.
|
|
60
54
|
*/
|
|
61
55
|
export function supportsXhigh<TApi extends Api>(model: Model<TApi>): boolean {
|
|
62
56
|
return XHIGH_MODELS.has(model.id);
|
|
@@ -3,7 +3,7 @@ import type {
|
|
|
3
3
|
ContentBlockParam,
|
|
4
4
|
MessageCreateParamsStreaming,
|
|
5
5
|
MessageParam,
|
|
6
|
-
} from "@anthropic-ai/sdk/resources/messages
|
|
6
|
+
} from "@anthropic-ai/sdk/resources/messages";
|
|
7
7
|
import { calculateCost } from "../models";
|
|
8
8
|
import { getEnvApiKey } from "../stream";
|
|
9
9
|
import type {
|
|
@@ -28,6 +28,16 @@ import { sanitizeSurrogates } from "../utils/sanitize-unicode";
|
|
|
28
28
|
|
|
29
29
|
import { transformMessages } from "./transorm-messages";
|
|
30
30
|
|
|
31
|
+
// Stealth mode: Mimic Claude Code's tool naming exactly
|
|
32
|
+
const claudeCodeVersion = "2.1.2";
|
|
33
|
+
|
|
34
|
+
// Prefix all tool names to avoid collisions with Claude Code's built-in tools
|
|
35
|
+
const toolNamePrefix = "cli_";
|
|
36
|
+
|
|
37
|
+
const toClaudeCodeName = (name: string) => toolNamePrefix + name;
|
|
38
|
+
const fromClaudeCodeName = (name: string) =>
|
|
39
|
+
name.startsWith(toolNamePrefix) ? name.slice(toolNamePrefix.length) : name;
|
|
40
|
+
|
|
31
41
|
/**
|
|
32
42
|
* Convert content blocks to Anthropic API format
|
|
33
43
|
*/
|
|
@@ -157,7 +167,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
157
167
|
const block: Block = {
|
|
158
168
|
type: "toolCall",
|
|
159
169
|
id: event.content_block.id,
|
|
160
|
-
name: event.content_block.name,
|
|
170
|
+
name: isOAuthToken ? fromClaudeCodeName(event.content_block.name) : event.content_block.name,
|
|
161
171
|
arguments: event.content_block.input as Record<string, any>,
|
|
162
172
|
partialJson: "",
|
|
163
173
|
index: event.index,
|
|
@@ -278,23 +288,68 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
278
288
|
return stream;
|
|
279
289
|
};
|
|
280
290
|
|
|
291
|
+
function isOAuthToken(apiKey: string): boolean {
|
|
292
|
+
return apiKey.includes("sk-ant-oat");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Build deduplicated beta header string
|
|
296
|
+
function buildBetaHeader(baseBetas: string[], extraBetas: string[]): string {
|
|
297
|
+
const seen = new Set<string>();
|
|
298
|
+
const result: string[] = [];
|
|
299
|
+
for (const beta of [...baseBetas, ...extraBetas]) {
|
|
300
|
+
const trimmed = beta.trim();
|
|
301
|
+
if (trimmed && !seen.has(trimmed)) {
|
|
302
|
+
seen.add(trimmed);
|
|
303
|
+
result.push(trimmed);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return result.join(",");
|
|
307
|
+
}
|
|
308
|
+
|
|
281
309
|
function createClient(
|
|
282
310
|
model: Model<"anthropic-messages">,
|
|
283
311
|
apiKey: string,
|
|
284
312
|
interleavedThinking: boolean,
|
|
285
313
|
): { client: Anthropic; isOAuthToken: boolean } {
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
314
|
+
const oauthToken = isOAuthToken(apiKey);
|
|
315
|
+
|
|
316
|
+
// Base betas required for Claude Code compatibility
|
|
317
|
+
const baseBetas = oauthToken
|
|
318
|
+
? [
|
|
319
|
+
"claude-code-20250219",
|
|
320
|
+
"oauth-2025-04-20",
|
|
321
|
+
"interleaved-thinking-2025-05-14",
|
|
322
|
+
"fine-grained-tool-streaming-2025-05-14",
|
|
323
|
+
]
|
|
324
|
+
: ["fine-grained-tool-streaming-2025-05-14"];
|
|
325
|
+
|
|
326
|
+
// Add interleaved thinking if requested (and not already in base)
|
|
327
|
+
const extraBetas: string[] = [];
|
|
328
|
+
if (interleavedThinking && !oauthToken) {
|
|
329
|
+
extraBetas.push("interleaved-thinking-2025-05-14");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Include any betas from model headers
|
|
333
|
+
const modelBeta = model.headers?.["anthropic-beta"];
|
|
334
|
+
if (modelBeta) {
|
|
335
|
+
extraBetas.push(...modelBeta.split(","));
|
|
289
336
|
}
|
|
290
337
|
|
|
291
|
-
|
|
338
|
+
const betaHeader = buildBetaHeader(baseBetas, extraBetas);
|
|
339
|
+
|
|
340
|
+
if (oauthToken) {
|
|
341
|
+
// Stealth mode: Mimic Claude Code's headers exactly
|
|
292
342
|
const defaultHeaders = {
|
|
293
343
|
accept: "application/json",
|
|
294
344
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
295
|
-
"anthropic-beta":
|
|
345
|
+
"anthropic-beta": betaHeader,
|
|
346
|
+
"user-agent": `claude-cli/${claudeCodeVersion} (external, cli)`,
|
|
347
|
+
"x-app": "cli",
|
|
296
348
|
...(model.headers || {}),
|
|
297
349
|
};
|
|
350
|
+
// Don't duplicate anthropic-beta from model.headers
|
|
351
|
+
delete (defaultHeaders as Record<string, string>)["anthropic-beta"];
|
|
352
|
+
(defaultHeaders as Record<string, string>)["anthropic-beta"] = betaHeader;
|
|
298
353
|
|
|
299
354
|
const client = new Anthropic({
|
|
300
355
|
apiKey: null,
|
|
@@ -305,23 +360,25 @@ function createClient(
|
|
|
305
360
|
});
|
|
306
361
|
|
|
307
362
|
return { client, isOAuthToken: true };
|
|
308
|
-
}
|
|
309
|
-
const defaultHeaders = {
|
|
310
|
-
accept: "application/json",
|
|
311
|
-
"anthropic-dangerous-direct-browser-access": "true",
|
|
312
|
-
"anthropic-beta": betaFeatures.join(","),
|
|
313
|
-
...(model.headers || {}),
|
|
314
|
-
};
|
|
363
|
+
}
|
|
315
364
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
365
|
+
const defaultHeaders = {
|
|
366
|
+
accept: "application/json",
|
|
367
|
+
"anthropic-dangerous-direct-browser-access": "true",
|
|
368
|
+
"anthropic-beta": betaHeader,
|
|
369
|
+
...(model.headers || {}),
|
|
370
|
+
};
|
|
371
|
+
// Ensure our beta header takes precedence
|
|
372
|
+
(defaultHeaders as Record<string, string>)["anthropic-beta"] = betaHeader;
|
|
373
|
+
|
|
374
|
+
const client = new Anthropic({
|
|
375
|
+
apiKey,
|
|
376
|
+
baseURL: model.baseUrl,
|
|
377
|
+
dangerouslyAllowBrowser: true,
|
|
378
|
+
defaultHeaders,
|
|
379
|
+
});
|
|
322
380
|
|
|
323
|
-
|
|
324
|
-
}
|
|
381
|
+
return { client, isOAuthToken: false };
|
|
325
382
|
}
|
|
326
383
|
|
|
327
384
|
function buildParams(
|
|
@@ -332,7 +389,7 @@ function buildParams(
|
|
|
332
389
|
): MessageCreateParamsStreaming {
|
|
333
390
|
const params: MessageCreateParamsStreaming = {
|
|
334
391
|
model: model.id,
|
|
335
|
-
messages: convertMessages(context.messages, model),
|
|
392
|
+
messages: convertMessages(context.messages, model, isOAuthToken),
|
|
336
393
|
max_tokens: options?.maxTokens || (model.maxTokens / 3) | 0,
|
|
337
394
|
stream: true,
|
|
338
395
|
};
|
|
@@ -375,7 +432,7 @@ function buildParams(
|
|
|
375
432
|
}
|
|
376
433
|
|
|
377
434
|
if (context.tools) {
|
|
378
|
-
params.tools = convertTools(context.tools);
|
|
435
|
+
params.tools = convertTools(context.tools, isOAuthToken);
|
|
379
436
|
}
|
|
380
437
|
|
|
381
438
|
if (options?.thinkingEnabled && model.reasoning) {
|
|
@@ -388,6 +445,9 @@ function buildParams(
|
|
|
388
445
|
if (options?.toolChoice) {
|
|
389
446
|
if (typeof options.toolChoice === "string") {
|
|
390
447
|
params.tool_choice = { type: options.toolChoice };
|
|
448
|
+
} else if (isOAuthToken && options.toolChoice.name) {
|
|
449
|
+
// Prefix tool name in tool_choice for OAuth mode
|
|
450
|
+
params.tool_choice = { ...options.toolChoice, name: toClaudeCodeName(options.toolChoice.name) };
|
|
391
451
|
} else {
|
|
392
452
|
params.tool_choice = options.toolChoice;
|
|
393
453
|
}
|
|
@@ -402,7 +462,11 @@ function sanitizeToolCallId(id: string): string {
|
|
|
402
462
|
return id.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
403
463
|
}
|
|
404
464
|
|
|
405
|
-
function convertMessages(
|
|
465
|
+
function convertMessages(
|
|
466
|
+
messages: Message[],
|
|
467
|
+
model: Model<"anthropic-messages">,
|
|
468
|
+
isOAuthToken: boolean,
|
|
469
|
+
): MessageParam[] {
|
|
406
470
|
const params: MessageParam[] = [];
|
|
407
471
|
|
|
408
472
|
// Transform messages for cross-provider compatibility
|
|
@@ -481,7 +545,7 @@ function convertMessages(messages: Message[], model: Model<"anthropic-messages">
|
|
|
481
545
|
blocks.push({
|
|
482
546
|
type: "tool_use",
|
|
483
547
|
id: sanitizeToolCallId(block.id),
|
|
484
|
-
name: block.name,
|
|
548
|
+
name: isOAuthToken ? toClaudeCodeName(block.name) : block.name,
|
|
485
549
|
input: block.arguments,
|
|
486
550
|
});
|
|
487
551
|
}
|
|
@@ -547,14 +611,14 @@ function convertMessages(messages: Message[], model: Model<"anthropic-messages">
|
|
|
547
611
|
return params;
|
|
548
612
|
}
|
|
549
613
|
|
|
550
|
-
function convertTools(tools: Tool[]): Anthropic.Messages.Tool[] {
|
|
614
|
+
function convertTools(tools: Tool[], isOAuthToken: boolean): Anthropic.Messages.Tool[] {
|
|
551
615
|
if (!tools) return [];
|
|
552
616
|
|
|
553
617
|
return tools.map((tool) => {
|
|
554
618
|
const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema
|
|
555
619
|
|
|
556
620
|
return {
|
|
557
|
-
name: tool.name,
|
|
621
|
+
name: isOAuthToken ? toClaudeCodeName(tool.name) : tool.name,
|
|
558
622
|
description: tool.description,
|
|
559
623
|
input_schema: {
|
|
560
624
|
type: "object" as const,
|