@oh-my-pi/pi-ai 17.4.0 → 17.4.2
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 +30 -0
- package/LICENSE +23 -0
- package/THIRD-PARTY-NOTICES.txt +22909 -0
- package/dist/types/auth-retry.d.ts +8 -3
- package/dist/types/auth-storage.d.ts +9 -0
- package/dist/types/dialect/qwen-xml.d.ts +6 -0
- package/dist/types/error/auth-classify.d.ts +8 -8
- package/dist/types/error/oauth.d.ts +2 -1
- package/dist/types/providers/cursor/exec-modern.d.ts +1 -1
- package/dist/types/providers/cursor-pi-args.d.ts +21 -0
- package/dist/types/providers/cursor.d.ts +23 -3
- package/dist/types/providers/google-types.d.ts +6 -0
- package/dist/types/providers/mock.d.ts +3 -1
- package/dist/types/providers/openai-shared.d.ts +6 -1
- package/dist/types/registry/oauth/google-antigravity.d.ts +2 -6
- package/dist/types/registry/oauth/index.d.ts +2 -3
- package/dist/types/registry/oauth/types.d.ts +2 -1
- package/dist/types/registry/oauth/xai-oauth.d.ts +2 -2
- package/dist/types/registry/registry.d.ts +1 -1
- package/dist/types/registry/types.d.ts +2 -1
- package/dist/types/registry/xai-oauth.d.ts +1 -1
- package/dist/types/types.d.ts +21 -0
- package/dist/types/utils/stream-markup-healing.d.ts +1 -1
- package/package.json +8 -6
- package/src/auth-retry.ts +41 -3
- package/src/auth-storage.ts +229 -75
- package/src/dialect/qwen-xml.ts +82 -0
- package/src/error/auth-classify.ts +10 -8
- package/src/error/oauth.ts +2 -1
- package/src/providers/amazon-bedrock.ts +12 -19
- package/src/providers/anthropic.ts +29 -35
- package/src/providers/cursor/exec-modern.ts +2 -0
- package/src/providers/cursor-pi-args.ts +38 -2
- package/src/providers/cursor.ts +357 -32
- package/src/providers/google-shared.ts +11 -12
- package/src/providers/google-types.ts +7 -0
- package/src/providers/mock.ts +4 -1
- package/src/providers/openai-codex-responses.ts +34 -8
- package/src/providers/openai-completions.ts +2 -2
- package/src/providers/openai-shared.ts +72 -14
- package/src/registry/oauth/google-antigravity.ts +247 -162
- package/src/registry/oauth/index.ts +3 -3
- package/src/registry/oauth/types.ts +2 -1
- package/src/registry/oauth/xai-oauth.ts +14 -7
- package/src/registry/types.ts +2 -1
- package/src/registry/xai-oauth.ts +2 -2
- package/src/stream.ts +8 -4
- package/src/types.ts +22 -0
- package/src/utils/schema/CONSTRAINTS.md +1 -0
- package/src/utils/schema/fields.ts +20 -4
- package/src/utils/stream-markup-healing.ts +9 -2
|
@@ -2,6 +2,7 @@ import { scheduler } from "node:timers/promises";
|
|
|
2
2
|
import { type } from "@oh-my-pi/omptype";
|
|
3
3
|
import { calculateCost } from "@oh-my-pi/pi-catalog/models";
|
|
4
4
|
import {
|
|
5
|
+
applyCodexResidencyHeader,
|
|
5
6
|
CODEX_BASE_URL,
|
|
6
7
|
CODEX_CLIENT_VERSION,
|
|
7
8
|
getCodexAccountId,
|
|
@@ -495,12 +496,15 @@ interface CodexCompatibilityIdentity {
|
|
|
495
496
|
sessionId: string;
|
|
496
497
|
threadId: string;
|
|
497
498
|
windowId: string;
|
|
498
|
-
|
|
499
|
+
/** Header projection: identity fields only, never the Code Mode snapshot. */
|
|
500
|
+
turnMetadataHeaderJson?: string;
|
|
499
501
|
}
|
|
500
502
|
|
|
501
503
|
interface CodexRequestMetadata extends CodexCompatibilityIdentity {
|
|
502
504
|
turnId: string;
|
|
505
|
+
/** Canonical body projection, including `tool_namespaces_info` when present. */
|
|
503
506
|
turnMetadataJson: string;
|
|
507
|
+
turnMetadataHeaderJson: string;
|
|
504
508
|
clientMetadata: Record<string, string>;
|
|
505
509
|
}
|
|
506
510
|
|
|
@@ -517,10 +521,11 @@ const CODEX_RESERVED_METADATA_KEYS: Record<string, true> = {
|
|
|
517
521
|
[OPENAI_HEADERS.SUBAGENT]: true,
|
|
518
522
|
request_kind: true,
|
|
519
523
|
compaction: true,
|
|
520
|
-
// codex-rs
|
|
521
|
-
//
|
|
522
|
-
//
|
|
524
|
+
// codex-rs retired `code_mode_tool_names` in favor of `tool_namespaces_info`
|
|
525
|
+
// for its Responses Lite Code Mode mapping; both stay reserved so callers
|
|
526
|
+
// cannot smuggle them in as extras.
|
|
523
527
|
code_mode_tool_names: true,
|
|
528
|
+
tool_namespaces_info: true,
|
|
524
529
|
turn_started_at_unix_ms: true,
|
|
525
530
|
forked_from_thread_id: true,
|
|
526
531
|
parent_thread_id: true,
|
|
@@ -598,6 +603,7 @@ function createCodexRequestMetadata(
|
|
|
598
603
|
clientMetadata?: Readonly<Record<string, string>>;
|
|
599
604
|
parentTurnId?: string;
|
|
600
605
|
compaction?: CodexCompactionRequestContext;
|
|
606
|
+
toolNamespacesInfo?: unknown;
|
|
601
607
|
},
|
|
602
608
|
): CodexRequestMetadata {
|
|
603
609
|
if (options.startNewTurn || !session.turnId) {
|
|
@@ -637,7 +643,17 @@ function createCodexRequestMetadata(
|
|
|
637
643
|
turnMetadata.turn_started_at_unix_ms = session.turnStartedAtUnixMs;
|
|
638
644
|
}
|
|
639
645
|
for (const key in extra) turnMetadata[key] = extra[key];
|
|
640
|
-
|
|
646
|
+
// The `x-codex-turn-metadata` header is capped at 100KB by the Codex
|
|
647
|
+
// backend, while `tool_namespaces_info` grows with the session's tool count
|
|
648
|
+
// (409 tools ~ 100KB on its own). The body's `client_metadata` is the
|
|
649
|
+
// canonical envelope, so the snapshot rides there only and the header keeps
|
|
650
|
+
// the fixed-size identity projection.
|
|
651
|
+
const turnMetadataHeaderJson = toAsciiJsonString(turnMetadata);
|
|
652
|
+
let turnMetadataJson = turnMetadataHeaderJson;
|
|
653
|
+
if (options.toolNamespacesInfo !== undefined) {
|
|
654
|
+
turnMetadata.tool_namespaces_info = options.toolNamespacesInfo;
|
|
655
|
+
turnMetadataJson = toAsciiJsonString(turnMetadata);
|
|
656
|
+
}
|
|
641
657
|
const clientMetadata: Record<string, string> = {
|
|
642
658
|
[OPENAI_HEADERS.INSTALLATION_ID]: identity.installationId,
|
|
643
659
|
session_id: identity.sessionId,
|
|
@@ -653,6 +669,7 @@ function createCodexRequestMetadata(
|
|
|
653
669
|
...identity,
|
|
654
670
|
turnId: session.turnId,
|
|
655
671
|
turnMetadataJson,
|
|
672
|
+
turnMetadataHeaderJson,
|
|
656
673
|
clientMetadata,
|
|
657
674
|
};
|
|
658
675
|
}
|
|
@@ -661,8 +678,8 @@ function applyCodexCompatibilityHeaders(headers: Headers, metadata: CodexCompati
|
|
|
661
678
|
headers.set(OPENAI_HEADERS.SCOPED_SESSION_ID, metadata.sessionId);
|
|
662
679
|
headers.set(OPENAI_HEADERS.THREAD_ID, metadata.threadId);
|
|
663
680
|
headers.set(OPENAI_HEADERS.WINDOW_ID, metadata.windowId);
|
|
664
|
-
if (metadata.
|
|
665
|
-
headers.set(OPENAI_HEADERS.TURN_METADATA, metadata.
|
|
681
|
+
if (metadata.turnMetadataHeaderJson) {
|
|
682
|
+
headers.set(OPENAI_HEADERS.TURN_METADATA, metadata.turnMetadataHeaderJson);
|
|
666
683
|
} else {
|
|
667
684
|
headers.delete(OPENAI_HEADERS.TURN_METADATA);
|
|
668
685
|
}
|
|
@@ -1452,6 +1469,7 @@ function createCodexRequestContext(
|
|
|
1452
1469
|
clientMetadata: transformedBody.client_metadata,
|
|
1453
1470
|
parentTurnId: options?.parentTurnId,
|
|
1454
1471
|
compaction,
|
|
1472
|
+
toolNamespacesInfo: options?.toolNamespacesInfo,
|
|
1455
1473
|
});
|
|
1456
1474
|
transformedBody.client_metadata = requestMetadata.clientMetadata;
|
|
1457
1475
|
return {
|
|
@@ -4238,6 +4256,12 @@ function createCodexHeaders(
|
|
|
4238
4256
|
headers.delete("x-api-key");
|
|
4239
4257
|
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
4240
4258
|
if (accountId) headers.set(OPENAI_HEADERS.ACCOUNT_ID, accountId);
|
|
4259
|
+
// Region-pinned enterprise workspaces answer 401 `Workspace is not authorized
|
|
4260
|
+
// in this region.` when the request's egress region does not match them and
|
|
4261
|
+
// the client did not declare the workspace's residency. The access token
|
|
4262
|
+
// already carries it, so this needs no configuration. A caller-supplied
|
|
4263
|
+
// header still wins: a proxy fronting Codex may want a different value.
|
|
4264
|
+
applyCodexResidencyHeader(headers, accessToken);
|
|
4241
4265
|
if (attestation) {
|
|
4242
4266
|
headers.set(OPENAI_HEADERS.ATTESTATION, attestation);
|
|
4243
4267
|
} else {
|
|
@@ -4479,7 +4503,9 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex
|
|
|
4479
4503
|
|
|
4480
4504
|
function normalizeInputMessageContent(
|
|
4481
4505
|
model: Model<"openai-codex-responses">,
|
|
4482
|
-
content:
|
|
4506
|
+
content:
|
|
4507
|
+
| string
|
|
4508
|
+
| Array<{ type: "text"; text: string } | { type: "image"; mimeType: string; data: string; url?: string }>,
|
|
4483
4509
|
): ResponseInputContent[] {
|
|
4484
4510
|
// gpt-5.x codex rejects reserved Harmony control-token spellings in input
|
|
4485
4511
|
// data; escape the transport copy of untrusted user text so ordinary docs or
|
|
@@ -1950,7 +1950,7 @@ export function convertMessages(
|
|
|
1950
1950
|
content.push({
|
|
1951
1951
|
type: "image_url",
|
|
1952
1952
|
image_url: {
|
|
1953
|
-
url: `data:${item.mimeType};base64,${item.data}`,
|
|
1953
|
+
url: item.url ?? `data:${item.mimeType};base64,${item.data}`,
|
|
1954
1954
|
// Chat Completions has no "original"; omit it (provider default).
|
|
1955
1955
|
...(item.detail && item.detail !== "original" ? { detail: item.detail } : {}),
|
|
1956
1956
|
},
|
|
@@ -2257,7 +2257,7 @@ export function convertMessages(
|
|
|
2257
2257
|
imageBlocks.push({
|
|
2258
2258
|
type: "image_url",
|
|
2259
2259
|
image_url: {
|
|
2260
|
-
url: `data:${block.mimeType};base64,${block.data}`,
|
|
2260
|
+
url: block.url ?? `data:${block.mimeType};base64,${block.data}`,
|
|
2261
2261
|
},
|
|
2262
2262
|
});
|
|
2263
2263
|
}
|
|
@@ -1630,6 +1630,18 @@ function clampResponsesImageDetail(
|
|
|
1630
1630
|
return resolved === "original" && !supportsImageDetailOriginal ? "auto" : resolved;
|
|
1631
1631
|
}
|
|
1632
1632
|
|
|
1633
|
+
function convertResponsesInputImage(image: ImageContent, supportsImageDetailOriginal: boolean): ResponseInputImage {
|
|
1634
|
+
const detail = clampResponsesImageDetail(image.detail, supportsImageDetailOriginal);
|
|
1635
|
+
if (image.providerFile?.provider === "openai" && image.providerFile.id) {
|
|
1636
|
+
return { type: "input_image", detail, file_id: image.providerFile.id };
|
|
1637
|
+
}
|
|
1638
|
+
return {
|
|
1639
|
+
type: "input_image",
|
|
1640
|
+
detail,
|
|
1641
|
+
image_url: image.url ?? `data:${image.mimeType};base64,${image.data}`,
|
|
1642
|
+
};
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1633
1645
|
export function convertResponsesInputContent(
|
|
1634
1646
|
content: string | Array<TextContent | ImageContent>,
|
|
1635
1647
|
supportsImages: boolean,
|
|
@@ -1659,11 +1671,7 @@ export function convertResponsesInputContent(
|
|
|
1659
1671
|
} satisfies ResponseInputText);
|
|
1660
1672
|
}
|
|
1661
1673
|
for (const item of imageBlocks) {
|
|
1662
|
-
normalizedContent.push(
|
|
1663
|
-
type: "input_image",
|
|
1664
|
-
detail: clampResponsesImageDetail(item.detail, supportsImageDetailOriginal),
|
|
1665
|
-
image_url: `data:${item.mimeType};base64,${item.data}`,
|
|
1666
|
-
} satisfies ResponseInputImage);
|
|
1674
|
+
normalizedContent.push(convertResponsesInputImage(item, supportsImageDetailOriginal));
|
|
1667
1675
|
}
|
|
1668
1676
|
if (omittedImages) {
|
|
1669
1677
|
normalizedContent.push({
|
|
@@ -2338,11 +2346,7 @@ export function appendResponsesToolResultMessages<TApi extends Api>(
|
|
|
2338
2346
|
];
|
|
2339
2347
|
for (const block of toolResult.content) {
|
|
2340
2348
|
if (block.type === "image") {
|
|
2341
|
-
contentParts.push(
|
|
2342
|
-
type: "input_image",
|
|
2343
|
-
detail: clampResponsesImageDetail(block.detail, supportsImageDetailOriginal),
|
|
2344
|
-
image_url: `data:${block.mimeType};base64,${block.data}`,
|
|
2345
|
-
} satisfies ResponseInputImage);
|
|
2349
|
+
contentParts.push(convertResponsesInputImage(block, supportsImageDetailOriginal));
|
|
2346
2350
|
}
|
|
2347
2351
|
}
|
|
2348
2352
|
const imageMessage = { role: "user", content: contentParts } satisfies ResponseInput[number];
|
|
@@ -2363,6 +2367,17 @@ export function appendResponsesToolResultMessages<TApi extends Api>(
|
|
|
2363
2367
|
*/
|
|
2364
2368
|
type ResponsesToolCallBlock = ToolCall & { [kStreamingPartialJson]: string; [kStreamingLastParseLen]?: number };
|
|
2365
2369
|
|
|
2370
|
+
function ensureReasoningSummaryPart(
|
|
2371
|
+
item: ResponseReasoningItem,
|
|
2372
|
+
summaryIndex: number,
|
|
2373
|
+
): ResponseReasoningItem["summary"][number] {
|
|
2374
|
+
item.summary = item.summary || [];
|
|
2375
|
+
while (item.summary.length <= summaryIndex) {
|
|
2376
|
+
item.summary.push({ type: "summary_text", text: "" });
|
|
2377
|
+
}
|
|
2378
|
+
return item.summary[summaryIndex]!;
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2366
2381
|
export function appendReasoningSummaryPart(
|
|
2367
2382
|
item: ResponseReasoningItem,
|
|
2368
2383
|
part: ResponseReasoningItem["summary"][number],
|
|
@@ -2450,15 +2465,44 @@ export function appendReasoningSummaryTextDelta(
|
|
|
2450
2465
|
stream: AssistantMessageEventStream,
|
|
2451
2466
|
output: AssistantMessage,
|
|
2452
2467
|
contentIndex: number,
|
|
2468
|
+
summaryIndex = 0,
|
|
2453
2469
|
): void {
|
|
2454
|
-
|
|
2455
|
-
const lastPart = item.summary[item.summary.length - 1];
|
|
2456
|
-
if (!lastPart) return;
|
|
2470
|
+
const part = ensureReasoningSummaryPart(item, summaryIndex);
|
|
2457
2471
|
block.thinking += delta;
|
|
2458
|
-
|
|
2472
|
+
part.text += delta;
|
|
2459
2473
|
stream.push({ type: "thinking_delta", contentIndex, delta, partial: output });
|
|
2460
2474
|
}
|
|
2461
2475
|
|
|
2476
|
+
/**
|
|
2477
|
+
* Applies a completed reasoning-summary snapshot, including providers that omit
|
|
2478
|
+
* the preceding summary-part event.
|
|
2479
|
+
*/
|
|
2480
|
+
export function applyReasoningSummaryTextDone(
|
|
2481
|
+
item: ResponseReasoningItem,
|
|
2482
|
+
block: ThinkingContent,
|
|
2483
|
+
text: string,
|
|
2484
|
+
summaryIndex: number,
|
|
2485
|
+
stream: AssistantMessageEventStream,
|
|
2486
|
+
output: AssistantMessage,
|
|
2487
|
+
contentIndex: number,
|
|
2488
|
+
): void {
|
|
2489
|
+
const part = ensureReasoningSummaryPart(item, summaryIndex);
|
|
2490
|
+
const previous = part.text;
|
|
2491
|
+
part.text = text;
|
|
2492
|
+
if (!text || text === previous) return;
|
|
2493
|
+
if (!block.thinking) {
|
|
2494
|
+
block.thinking = text;
|
|
2495
|
+
stream.push({ type: "thinking_delta", contentIndex, delta: text, partial: output });
|
|
2496
|
+
return;
|
|
2497
|
+
}
|
|
2498
|
+
if (text.startsWith(block.thinking)) {
|
|
2499
|
+
const delta = text.slice(block.thinking.length);
|
|
2500
|
+
if (!delta) return;
|
|
2501
|
+
block.thinking += delta;
|
|
2502
|
+
stream.push({ type: "thinking_delta", contentIndex, delta, partial: output });
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
2505
|
+
|
|
2462
2506
|
export function appendReasoningSummaryPartDone(
|
|
2463
2507
|
item: ResponseReasoningItem,
|
|
2464
2508
|
block: ThinkingContent,
|
|
@@ -2994,6 +3038,20 @@ export async function processResponsesStream<TApi extends Api>(
|
|
|
2994
3038
|
stream,
|
|
2995
3039
|
output,
|
|
2996
3040
|
contentIndexOf(entry.block),
|
|
3041
|
+
event.summary_index,
|
|
3042
|
+
);
|
|
3043
|
+
}
|
|
3044
|
+
} else if (event.type === "response.reasoning_summary_text.done") {
|
|
3045
|
+
const entry = lookupOpenItem(event);
|
|
3046
|
+
if (entry?.item.type === "reasoning" && entry.block.type === "thinking") {
|
|
3047
|
+
applyReasoningSummaryTextDone(
|
|
3048
|
+
entry.item,
|
|
3049
|
+
entry.block,
|
|
3050
|
+
event.text,
|
|
3051
|
+
event.summary_index,
|
|
3052
|
+
stream,
|
|
3053
|
+
output,
|
|
3054
|
+
contentIndexOf(entry.block),
|
|
2997
3055
|
);
|
|
2998
3056
|
}
|
|
2999
3057
|
} else if (event.type === "response.reasoning_summary_part.done") {
|