chore(vendor): import pi 1.3.0

Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
This commit is contained in:
alexion committed 2026-10-15 17:58:48 -04:00
1 parent 719370c8f7
commit 6081e3e325
1743 files changed
+205459 -131704

No files matched your search

+174 -83
View File
@@ -11,7 +11,8 @@ import {
stream as streamOpenAICodexResponses,
streamSimple as streamSimpleOpenAICodexResponses,
} from "../src/api/openai-codex-responses.ts";
import type { Context, Model } from "../src/types.ts";
import type { Api, Context, Model } from "../src/types.ts";
import { normalizeContext } from "../src/utils/transcript.ts";
const originalAgentDir = process.env.PI_CODING_AGENT_DIR;
@@ -97,7 +98,7 @@ function buildSSEPayload({
}
describe("openai-codex streaming", () => {
it("streams SSE responses into AssistantMessageEventStream", async () => {
it("streams SSE responses and forwards raw provider events", async () => {
const tempDir = mkdtempSync(join(tmpdir(), "pi-codex-stream-"));
process.env.PI_CODING_AGENT_DIR = tempDir;
@@ -186,12 +187,21 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const streamResult = streamOpenAICodexResponses(model, context, { apiKey: token, transport: "sse" });
const providerEvents: unknown[] = [];
const providerEventModels: Model<Api>[] = [];
const streamResult = streamOpenAICodexResponses(model, context, {
apiKey: token,
transport: "sse",
onProviderStreamEvent: (event, eventModel) => {
providerEvents.push(event);
providerEventModels.push(eventModel);
},
});
let sawTextDelta = false;
let sawDone = false;
@@ -207,6 +217,14 @@ describe("openai-codex streaming", () => {
expect(sawTextDelta).toBe(true);
expect(sawDone).toBe(true);
expect(providerEvents.map((event) => (event as { type?: string }).type)).toEqual([
"response.output_item.added",
"response.content_part.added",
"response.output_text.delta",
"response.output_item.done",
"response.completed",
]);
expect(providerEventModels).toEqual([model, model, model, model, model]);
});
// Regression test for https://github.com/earendil-works/pi/issues/9047
@@ -225,10 +243,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const resultStream = streamOpenAICodexResponses(model, context, {
apiKey: token,
transport: "sse",
@@ -284,10 +302,10 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const result = await Promise.race([
streamOpenAICodexResponses(model, context, { apiKey: token, transport: "sse" }).result(),
@@ -345,10 +363,10 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const result = await Promise.race([
streamOpenAICodexResponses(model, context, { apiKey: token, transport: "sse" }).result(),
@@ -401,10 +419,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const result = await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -498,10 +516,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const controller = new AbortController();
const events: string[] = [];
@@ -617,12 +635,16 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const streamResult = streamOpenAICodexResponses(model, context, { apiKey: token, sessionId, transport: "sse" });
const streamResult = streamOpenAICodexResponses(model, context, {
apiKey: token,
sessionId,
transport: "sse",
});
await streamResult.result();
});
@@ -660,10 +682,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -710,10 +732,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -760,10 +782,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -775,6 +797,55 @@ describe("openai-codex streaming", () => {
expect(capturedHeaders?.get("x-client-request-id")).toBe("x".repeat(64));
});
it("lets model and caller headers override originator and User-Agent", async () => {
const token = mockToken();
let capturedHeaders: Headers | undefined;
const encoder = new TextEncoder();
vi.stubGlobal(
"fetch",
vi.fn(async (_input: string | URL, init?: RequestInit) => {
capturedHeaders = init?.headers instanceof Headers ? init.headers : undefined;
return new Response(
new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(encoder.encode(buildSSEPayload({ status: "completed" })));
controller.close();
},
}),
{ status: 200, headers: { "content-type": "text/event-stream" } },
);
}),
);
const model: Model<"openai-codex-responses"> = {
id: "gpt-5.1-codex",
name: "GPT-5.1 Codex",
api: "openai-codex-responses",
provider: "openai-codex",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 400000,
maxTokens: 128000,
headers: { originator: "my-app" },
};
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
});
await streamOpenAICodexResponses(model, context, {
apiKey: token,
transport: "sse",
headers: { "user-agent": "my-app/1.0", Authorization: "Bearer ignored" },
}).result();
expect(capturedHeaders?.get("originator")).toBe("my-app");
expect(capturedHeaders?.get("User-Agent")).toBe("my-app/1.0");
expect(capturedHeaders?.get("Authorization")).toBe(`Bearer ${token}`);
});
it("preserves gpt-5.5 xhigh reasoning effort from simple options", async () => {
const tempDir = mkdtempSync(join(tmpdir(), "pi-codex-stream-"));
process.env.PI_CODING_AGENT_DIR = tempDir;
@@ -822,10 +893,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
await streamSimpleOpenAICodexResponses(model, context, {
apiKey: token,
@@ -873,7 +944,7 @@ describe("openai-codex streaming", () => {
await streamOpenAICodexResponses(
model,
{
normalizeContext({
messages: [
{ role: "user", content: "Do not call ping. Respond with text instead.", timestamp: Date.now() },
],
@@ -884,7 +955,7 @@ describe("openai-codex streaming", () => {
parameters: Type.Object({ value: Type.String() }),
},
],
},
}),
{ apiKey: token, transport: "sse", toolChoice: "required" },
).result();
@@ -928,7 +999,7 @@ describe("openai-codex streaming", () => {
await streamOpenAICodexResponses(
model,
{
normalizeContext({
messages: [{ role: "user", content: "Use a tool", timestamp: Date.now() }],
tools: [
{
@@ -944,7 +1015,7 @@ describe("openai-codex streaming", () => {
constrainedSampling: { type: "json_schema", strict: "prefer" },
},
],
},
}),
{
apiKey: token,
transport: "sse",
@@ -1046,10 +1117,10 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const streamResult = streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -1142,10 +1213,10 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const result = await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -1246,18 +1317,23 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
// No sessionId provided
const streamResult = streamOpenAICodexResponses(model, context, { apiKey: token, transport: "sse" });
const streamResult = streamOpenAICodexResponses(model, context, {
apiKey: token,
transport: "sse",
});
await streamResult.result();
});
it("forwards auto transport from streamSimple options and uses cached websocket context", async () => {
it("forwards auto transport and raw provider events from streamSimple", async () => {
const token = mockToken();
const sentBodies: unknown[] = [];
const providerEvents: unknown[] = [];
const providerEventModels: Model<Api>[] = [];
let capturedWebSocketHeaders: Record<string, string> | undefined;
const fetchMock = vi.fn(async () => new Response("unexpected fetch", { status: 500 }));
@@ -1306,7 +1382,7 @@ describe("openai-codex streaming", () => {
},
},
{
type: "response.completed",
type: "response.done",
response: {
status: "completed",
end_turn: false,
@@ -1349,19 +1425,31 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const result = await streamSimpleOpenAICodexResponses(model, context, {
apiKey: token,
sessionId: "session-auto",
transport: "auto",
onProviderStreamEvent: (event, eventModel) => {
providerEvents.push(event);
providerEventModels.push(eventModel);
},
}).result();
expect(result.endTurn).toBe(false);
expect(sentBodies).toHaveLength(1);
expect(providerEvents.map((event) => (event as { type?: string }).type)).toEqual([
"response.output_item.added",
"response.content_part.added",
"response.output_text.delta",
"response.output_item.done",
"response.done",
]);
expect(providerEventModels).toEqual([model, model, model, model, model]);
expect(capturedWebSocketHeaders?.["session-id"]).toBe("session-auto");
expect(capturedWebSocketHeaders?.session_id).toBeUndefined();
expect(capturedWebSocketHeaders?.["x-client-request-id"]).toBe("session-auto");
@@ -1444,12 +1532,21 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = { systemPrompt: "", messages: [] };
const context = normalizeContext({ systemPrompt: "", messages: [] });
const options = { sessionId: "shared-session", transport: "websocket-cached" as const };
await streamOpenAICodexResponses(model, context, { ...options, apiKey: mockToken("account-a") }).result();
await streamOpenAICodexResponses(model, context, { ...options, apiKey: mockToken("account-b") }).result();
await streamOpenAICodexResponses(model, context, { ...options, apiKey: mockToken("account-a") }).result();
await streamOpenAICodexResponses(model, context, {
...options,
apiKey: mockToken("account-a"),
}).result();
await streamOpenAICodexResponses(model, context, {
...options,
apiKey: mockToken("account-b"),
}).result();
await streamOpenAICodexResponses(model, context, {
...options,
apiKey: mockToken("account-a"),
}).result();
expect(connectedHeaders.map((headers) => headers["chatgpt-account-id"])).toEqual(["account-a", "account-b"]);
expect(connectedHeaders.map((headers) => headers.authorization)).toEqual([
@@ -1535,10 +1632,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const options = {
apiKey: token,
cacheRetention: "none" as const,
@@ -1618,10 +1715,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const resultPromise = streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -1693,13 +1790,9 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const result = await streamOpenAICodexResponses(
model,
{ systemPrompt: "", messages: [] },
{
apiKey: token,
},
).result();
const result = await streamOpenAICodexResponses(model, normalizeContext({ systemPrompt: "", messages: [] }), {
apiKey: token,
}).result();
expect(result.stopReason).toBe("stop");
expect(connections).toBe(2);
@@ -1782,10 +1875,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const resultPromise = streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -1873,10 +1966,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const resultPromise = streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -1968,10 +2061,10 @@ describe("openai-codex streaming", () => {
maxTokens: 128000,
};
const sessionId = "aged-ws-session";
const firstContext: Context = {
const firstContext = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const first = await streamOpenAICodexResponses(model, firstContext, {
apiKey: token,
@@ -1979,10 +2072,9 @@ describe("openai-codex streaming", () => {
transport: "websocket-cached",
}).result();
vi.setSystemTime(new Date(startedAt.getTime() + 56 * 60 * 1000));
const secondContext: Context = {
systemPrompt: "You are a helpful assistant.",
const secondContext = normalizeContext({
messages: [...firstContext.messages, first, { role: "user", content: "Now finish", timestamp: 2 }],
};
});
await streamOpenAICodexResponses(model, secondContext, {
apiKey: token,
@@ -2117,13 +2209,13 @@ describe("openai-codex streaming", () => {
],
};
const first = await streamOpenAICodexResponses(model, firstContext, {
const first = await streamOpenAICodexResponses(model, normalizeContext(firstContext), {
apiKey: token,
sessionId: "session-1",
transport: "websocket-cached",
}).result();
const secondContext: Context = {
const secondContext = normalizeContext({
...firstContext,
messages: [
...firstContext.messages,
@@ -2138,7 +2230,7 @@ describe("openai-codex streaming", () => {
},
{ role: "user", content: "Now finish", timestamp: 3 },
],
};
});
await streamOpenAICodexResponses(model, secondContext, {
apiKey: token,
sessionId: "session-1",
@@ -2333,20 +2425,19 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const firstContext: Context = {
const firstContext = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: 1 }],
};
});
const first = await streamOpenAICodexResponses(model, firstContext, {
apiKey: token,
sessionId,
transport: "websocket-cached",
}).result();
const secondContext: Context = {
systemPrompt: "You are a helpful assistant.",
const secondContext = normalizeContext({
messages: [...firstContext.messages, first, { role: "user", content: "Now finish", timestamp: 2 }],
};
});
const eventTypes: string[] = [];
const secondStream = streamOpenAICodexResponses(model, secondContext, {
apiKey: token,
@@ -2443,10 +2534,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const resultPromise = streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -2485,10 +2576,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const result = await streamOpenAICodexResponses(model, context, {
apiKey: token,
@@ -2546,10 +2637,10 @@ describe("openai-codex streaming", () => {
const largeText = "compress me ".repeat(400);
await streamOpenAICodexResponses(
model,
{
normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: largeText, timestamp: 1 }],
},
}),
{ apiKey: token, transport: "sse" },
).result();
@@ -2564,10 +2655,10 @@ describe("openai-codex streaming", () => {
capturedBody = undefined;
await streamOpenAICodexResponses(
model,
{
normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "hi", timestamp: 1 }],
},
}),
{ apiKey: token, transport: "sse" },
).result();
@@ -2622,10 +2713,10 @@ describe("openai-codex streaming", () => {
contextWindow: 400000,
maxTokens: 128000,
};
const context: Context = {
const context = normalizeContext({
systemPrompt: "You are a helpful assistant.",
messages: [{ role: "user", content: "Say hello", timestamp: Date.now() }],
};
});
const retryTimeoutDelays = () =>
setTimeoutSpy.mock.calls