feat(codex): add fast mode #4

Merged
eleith merged 2 commits from feat/codex-fast into main 2026-11-01 17:45:58 +00:00
17 changed files with 2756 additions and 46 deletions

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@eleith-pi/codex": minor
---
Rename context-codex to codex while preserving context controls and branch state. Add Codex account usage reporting and explicit branch-local, per-model fast controls using Pi's existing login, conservative catalog eligibility, and higher-consumption confirmation for priority requests on supported direct legacy Codex selections.
+74
View File
@@ -0,0 +1,74 @@
import { expect, it } from "vitest";
import { codexFastEligibility } from "./capabilities.ts";
const modelId = "future-codex-model";
it.each(["priority", "fast"])(
"recognizes the advertised %s service tier without guessing model names",
(id) => {
const catalog = { models: [{ slug: modelId, service_tiers: [{ id }] }] };
expect(codexFastEligibility(catalog, modelId)).toBe("supported");
},
);
it("supports the legacy fast advertisement even alongside an empty newer field", () => {
const catalog = {
models: [{ slug: modelId, service_tiers: [], additional_speed_tiers: ["fast"] }],
};
expect(codexFastEligibility(catalog, modelId)).toBe("supported");
});
it("accepts legacy-only catalogs", () => {
const catalog = { models: [{ slug: modelId, additional_speed_tiers: ["fast"] }] };
expect(codexFastEligibility(catalog, modelId)).toBe("supported");
});
it.each([
{ service_tiers: [] },
{ service_tiers: [{ id: "default" }, { id: "ultrafast" }] },
{ additional_speed_tiers: [] },
])("distinguishes a valid list without fast mode from unknown metadata: %j", (metadata) => {
expect(codexFastEligibility({ models: [{ slug: modelId, ...metadata }] }, modelId)).toBe(
"unsupported",
);
});
it.each([
{},
{ default_service_tier: "priority" },
{ service_tiers: ["priority"] },
{ service_tiers: [{ id: "priority" }, null] },
{ service_tiers: [], additional_speed_tiers: ["fast", null] },
{ service_tiers: [{ id: "priority" }], additional_speed_tiers: ["fast", null] },
{ service_tiers: [null], additional_speed_tiers: ["fast"] },
])("does not infer eligibility from missing or malformed fields: %j", (metadata) => {
expect(codexFastEligibility({ models: [{ slug: modelId, ...metadata }] }, modelId)).toBe(
"unknown",
);
});
it("does not apply another model's capability to the selected model", () => {
const catalog = {
models: [
{ slug: "other-model", service_tiers: [{ id: "priority" }] },
{ slug: modelId, service_tiers: [] },
],
};
expect(codexFastEligibility(catalog, modelId)).toBe("unsupported");
expect(codexFastEligibility(catalog, "missing-model")).toBe("unknown");
});
it("treats duplicate model entries as ambiguous", () => {
const entry = { slug: modelId, service_tiers: [{ id: "priority" }] };
expect(codexFastEligibility({ models: [entry, entry] }, modelId)).toBe("unknown");
});
it.each([
null,
[],
{},
{ models: {} },
{ models: [{ id: modelId, service_tiers: [{ id: "priority" }] }] },
])("keeps unrecognized catalog schemas unknown: %j", (catalog) => {
expect(codexFastEligibility(catalog, modelId)).toBe("unknown");
});
+55
View File
@@ -0,0 +1,55 @@
type CatalogModel = Record<string, unknown>;
export type FastEligibility = "supported" | "unsupported" | "unknown";
export function codexFastEligibility(catalog: unknown, modelId: string): FastEligibility {
const model = findCatalogModel(catalog, modelId);
if (!model) return "unknown";
const serviceTierSupport = readServiceTierSupport(model.service_tiers);
const legacyTierSupport = readLegacyTierSupport(model.additional_speed_tiers);
const invalidServiceTiers = "service_tiers" in model && serviceTierSupport === undefined;
const invalidLegacyTiers = "additional_speed_tiers" in model && legacyTierSupport === undefined;
if (invalidServiceTiers || invalidLegacyTiers) return "unknown";
if (serviceTierSupport === true || legacyTierSupport === true) return "supported";
if (serviceTierSupport === false || legacyTierSupport === false) return "unsupported";
return "unknown";
}
function findCatalogModel(catalog: unknown, modelId: string): CatalogModel | undefined {
if (!modelId) return undefined;
const models = readObject(catalog)?.models;
if (!Array.isArray(models)) return undefined;
let matched: CatalogModel | undefined;
for (const value of models) {
const model = readObject(value);
if (model?.slug !== modelId) continue;
if (matched) return undefined;
matched = model;
}
return matched;
}
function readServiceTierSupport(value: unknown): boolean | undefined {
if (!Array.isArray(value)) return undefined;
let supported = false;
for (const item of value) {
const tier = readObject(item);
if (typeof tier?.id !== "string" || !tier.id.trim()) return undefined;
if (tier.id === "priority" || tier.id === "fast") supported = true;
}
return supported;
}
function readLegacyTierSupport(value: unknown): boolean | undefined {
if (!Array.isArray(value)) return undefined;
if (!value.every((tier) => typeof tier === "string" && tier.trim().length > 0)) return undefined;
return value.includes("fast");
}
function readObject(value: unknown): CatalogModel | undefined {
return value !== null && typeof value === "object" && !Array.isArray(value)
? (value as CatalogModel)
: undefined;
}
+245 -32
View File
@@ -1,6 +1,12 @@
import { createHash } from "node:crypto";
import type { Api, AuthResult, Model } from "@earendil-works/pi-ai";
import { afterEach, expect, it, vi } from "vitest";
import { fetchCodexUsage, type CodexAccountRegistry } from "./client.ts";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
fetchCodexModelCatalog,
fetchCodexUsage,
readCodexAuthScope,
type CodexAccountRegistry,
} from "./client.ts";
const BASE_URL = "https://chatgpt.com/backend-api";
const model: Model<Api> = {
@@ -66,6 +72,142 @@ it("uses Pi's existing Codex auth for the fixed account endpoint", async () => {
});
});
it("uses the same Pi credentials for the fixed versioned model catalog", async () => {
const f = fixture();
const catalog = { models: [{ slug: "future-model", service_tiers: [{ id: "priority" }] }] };
f.fetcher.mockResolvedValue(json(catalog));
const result = await fetchCodexModelCatalog(f.registry, undefined, f.fetcher);
expect(result.data).toEqual(catalog);
expect(f.registry.getProviderAuth).toHaveBeenCalledExactlyOnceWith("openai-codex");
expect(f.fetcher).toHaveBeenCalledExactlyOnceWith(
`${BASE_URL}/codex/models?client_version=0.162.0`,
expect.objectContaining({ method: "GET", redirect: "error" }),
);
const headers = new Headers(f.fetcher.mock.calls[0][1]?.headers);
expect(headers.get("authorization")).toBe(`Bearer ${f.auth.auth.apiKey}`);
expect(headers.get("chatgpt-account-id")).toBe("test-account");
expect(result.authScope).toBe(
createHash("sha256")
.update(JSON.stringify([headers.get("authorization"), headers.get("chatgpt-account-id")]))
.digest("hex"),
);
expect(Object.keys(result).sort()).toEqual(["authScope", "data"]);
expect(JSON.stringify(result)).not.toContain(f.auth.auth.apiKey);
expect(JSON.stringify(result)).not.toContain("test-account");
});
it("reads a stable auth scope without fetching and ignores unused provider headers", async () => {
const f = fixture();
const fetchSpy = vi.spyOn(globalThis, "fetch").mockRejectedValue(new Error("must not fetch"));
try {
const scope = await readCodexAuthScope(f.registry);
f.auth.auth.headers = {
"ChatGPT-Account-Id": "unused-account",
Authorization: "unused-token",
"X-Unrelated-Secret": "unused-secret",
};
expect(await readCodexAuthScope(f.registry)).toBe(scope);
expect(scope).toMatch(/^[a-f0-9]{64}$/);
expect(scope).not.toContain(f.auth.auth.apiKey);
expect(scope).not.toContain("test-account");
expect(f.registry.getProviderAuth).toHaveBeenCalledTimes(2);
expect(fetchSpy).not.toHaveBeenCalled();
const catalog = await fetchCodexModelCatalog(f.registry, undefined, f.fetcher);
expect(catalog.authScope).toBe(scope);
} finally {
fetchSpy.mockRestore();
}
});
it.each(["token refresh", "account change"] as const)(
"changes the scope on %s and binds the catalog to its own resolved credentials",
async (change) => {
const f = fixture();
const previous = await readCodexAuthScope(f.registry);
f.auth.auth.apiKey =
change === "token refresh" ? `${token()}-refreshed` : token("another-account");
const catalog = await fetchCodexModelCatalog(f.registry, undefined, f.fetcher);
expect(catalog.authScope).not.toBe(previous);
expect(await readCodexAuthScope(f.registry)).toBe(catalog.authScope);
},
);
describe("auth-only validation without HTTP", () => {
const unexpectedFetch = vi.fn<typeof fetch>(async () => {
throw new Error("must not fetch");
});
beforeEach(() => {
unexpectedFetch.mockClear();
vi.stubGlobal("fetch", unexpectedFetch);
});
afterEach(() => {
vi.unstubAllGlobals();
expect(unexpectedFetch).not.toHaveBeenCalled();
});
const unavailableAuth = {
code: "auth",
message: "Codex account access is unavailable. Check your Pi Codex login.",
};
const unsupportedProvider = {
code: "unsupported",
message: "Account requests require the official Codex provider.",
};
it("rejects an overridden provider endpoint", async () => {
const account = fixture();
account.provider.baseUrl = "https://gateway.invalid";
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unsupportedProvider);
});
it("rejects a catalog model at a different endpoint", async () => {
const account = fixture();
account.provider.getModels = () => [{ ...model, baseUrl: "https://gateway.invalid" }];
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unsupportedProvider);
});
it("rejects an endpoint override in resolved credentials", async () => {
const account = fixture();
account.auth.auth.baseUrl = "https://gateway.invalid";
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject({
code: "unsupported",
message: "Account requests do not support endpoint overrides.",
});
});
it("requires an OAuth availability snapshot", async () => {
const account = fixture();
account.registry.isUsingOAuth.mockReturnValue(false);
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unavailableAuth);
});
it("rejects a non-OAuth resolution even when the snapshot says OAuth", async () => {
const account = fixture();
account.auth.source = "models.json";
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unavailableAuth);
});
it("requires the token's account-routing claim", async () => {
const account = fixture();
account.auth.auth.apiKey = "e30.e30.private-token";
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unavailableAuth);
});
it("does not repeat private Pi refresh errors", async () => {
const account = fixture();
account.registry.getProviderAuth.mockRejectedValue(new Error("private refresh details"));
await expect(readCodexAuthScope(account.registry)).rejects.toMatchObject(unavailableAuth);
});
});
it("does not forward catalog credentials through an endpoint override", async () => {
const f = fixture();
f.auth.auth.baseUrl = "https://gateway.invalid";
await expect(fetchCodexModelCatalog(f.registry, undefined, f.fetcher)).rejects.toMatchObject({
code: "unsupported",
});
expect(f.fetcher).not.toHaveBeenCalled();
});
it("derives the account from the token like Pi's legacy Codex transport", async () => {
const f = fixture();
f.auth.auth.headers = { "ChatGPT-Account-Id": "selected-account" };
@@ -207,23 +349,29 @@ it("does not follow or display a redirected endpoint", async () => {
expect(f.fetcher).toHaveBeenCalledOnce();
});
it("cancels a chunked response before retaining more than the byte limit", async () => {
const f = fixture();
const cancel = vi.fn();
const body = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new Uint8Array(1024 * 1024));
controller.enqueue(new Uint8Array(1));
},
cancel,
});
f.fetcher.mockResolvedValue(new Response(body));
await expect(fetchCodexUsage(f.registry, undefined, f.fetcher)).rejects.toMatchObject({
code: "response",
message: "Codex account response was too large.",
});
expect(cancel).toHaveBeenCalledOnce();
});
it.each([
["usage", fetchCodexUsage],
["catalog", fetchCodexModelCatalog],
] as const)(
"bounds chunked %s responses before retaining more than the byte limit",
async (_resource, fetchData) => {
const f = fixture();
const cancel = vi.fn();
const body = new ReadableStream<Uint8Array>({
start(controller) {
controller.enqueue(new Uint8Array(1024 * 1024));
controller.enqueue(new Uint8Array(1));
},
cancel,
});
f.fetcher.mockResolvedValue(new Response(body));
await expect(fetchData(f.registry, undefined, f.fetcher)).rejects.toMatchObject({
code: "response",
message: "Codex account response was too large.",
});
expect(cancel).toHaveBeenCalledOnce();
},
);
it("reports invalid JSON without repeating returned content", async () => {
const f = fixture();
@@ -234,6 +382,65 @@ it("reports invalid JSON without repeating returned content", async () => {
});
});
it.each(["usage", "catalog", "auth"] as const)(
"cancels during the public %s wrapper handoff",
async (resource) => {
const f = fixture();
const controller = new AbortController();
const abort = AbortController.prototype.abort;
// The private flow aborts its deadline in finally, immediately before handing off.
const cleanup = vi.spyOn(AbortController.prototype, "abort").mockImplementation(function (
this: AbortController,
reason?: unknown,
) {
abort.call(this, reason);
if (this !== controller) abort.call(controller);
});
try {
let request: Promise<unknown>;
if (resource === "auth") request = readCodexAuthScope(f.registry, controller.signal);
else if (resource === "usage")
request = fetchCodexUsage(f.registry, controller.signal, f.fetcher);
else request = fetchCodexModelCatalog(f.registry, controller.signal, f.fetcher);
await expect(request).rejects.toMatchObject({ code: "cancelled" });
} finally {
cleanup.mockRestore();
}
},
);
it("does not resolve auth for an already-cancelled scope read", async () => {
const f = fixture();
const controller = new AbortController();
controller.abort();
await expect(readCodexAuthScope(f.registry, controller.signal)).rejects.toMatchObject({
code: "cancelled",
});
expect(f.registry.getProviderAuth).not.toHaveBeenCalled();
});
it("ignores auth resolving after a scope read is cancelled", async () => {
const f = fixture();
const auth = deferred<AuthResult>();
f.registry.getProviderAuth.mockReturnValue(auth.promise);
const controller = new AbortController();
const request = readCodexAuthScope(f.registry, controller.signal);
controller.abort();
await expect(request).rejects.toMatchObject({ code: "cancelled" });
auth.resolve(f.auth);
await auth.promise;
});
it("bounds auth-only resolution by the same ten-second deadline", async () => {
vi.useFakeTimers();
const f = fixture();
f.registry.getProviderAuth.mockReturnValue(new Promise(() => {}));
const result = expect(readCodexAuthScope(f.registry)).rejects.toMatchObject({ code: "timeout" });
await vi.advanceTimersByTimeAsync(10_000);
await result;
expect(vi.getTimerCount()).toBe(0);
});
it("cancels during handoff of an already-buffered response", async () => {
const f = fixture();
const started = deferred<void>();
@@ -321,16 +528,22 @@ it("bounds the wait for response headers", async () => {
expect(vi.getTimerCount()).toBe(0);
});
it("keeps the deadline active while consuming the response body", async () => {
vi.useFakeTimers();
const f = fixture();
const cancel = vi.fn();
f.fetcher.mockResolvedValue(new Response(new ReadableStream<Uint8Array>({ cancel })));
const result = expect(fetchCodexUsage(f.registry, undefined, f.fetcher)).rejects.toMatchObject({
code: "timeout",
});
await vi.advanceTimersByTimeAsync(10_000);
await result;
expect(cancel).toHaveBeenCalledOnce();
expect(vi.getTimerCount()).toBe(0);
});
it.each([
["usage", fetchCodexUsage],
["catalog", fetchCodexModelCatalog],
] as const)(
"keeps the deadline active while consuming the %s body",
async (_resource, fetchData) => {
vi.useFakeTimers();
const f = fixture();
const cancel = vi.fn();
f.fetcher.mockResolvedValue(new Response(new ReadableStream<Uint8Array>({ cancel })));
const result = expect(fetchData(f.registry, undefined, f.fetcher)).rejects.toMatchObject({
code: "timeout",
});
await vi.advanceTimersByTimeAsync(10_000);
await result;
expect(cancel).toHaveBeenCalledOnce();
expect(vi.getTimerCount()).toBe(0);
},
);
+56 -7
View File
@@ -1,9 +1,15 @@
import { createHash } from "node:crypto";
import type { AuthResult, Provider } from "@earendil-works/pi-ai";
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
const PROVIDER = "openai-codex";
const BASE_URL = "https://chatgpt.com/backend-api";
const USAGE_URL = `${BASE_URL}/wham/usage`;
const ACCOUNT_URLS = {
usage: `${BASE_URL}/wham/usage`,
catalog: `${BASE_URL}/codex/models?client_version=0.162.0`,
} as const;
type NetworkResource = keyof typeof ACCOUNT_URLS;
type AccountResource = NetworkResource | "auth";
const TIMEOUT_MS = 10_000;
const MAX_RESPONSE_BYTES = 1024 * 1024;
@@ -14,6 +20,13 @@ export type CodexAccountRegistry = Pick<
getProvider(id: string): Pick<Provider, "baseUrl" | "getModels"> | undefined;
};
interface AccountData {
data: unknown;
authScope: string;
}
export type CodexModelCatalog = AccountData;
type Failure = "auth" | "unsupported" | "cancelled" | "timeout" | "network" | "response" | "http";
export class CodexAccountError extends Error {
@@ -33,6 +46,36 @@ export async function fetchCodexUsage(
signal?: AbortSignal,
fetcher: typeof fetch = fetch,
): Promise<unknown> {
const result = await readAccountData("usage", registry, signal, fetcher);
if (signal?.aborted) throw new CodexAccountError("cancelled", "Codex account request cancelled.");
return result.data;
}
export async function fetchCodexModelCatalog(
registry: CodexAccountRegistry,
signal?: AbortSignal,
fetcher: typeof fetch = fetch,
): Promise<CodexModelCatalog> {
const result = await readAccountData("catalog", registry, signal, fetcher);
if (signal?.aborted) throw new CodexAccountError("cancelled", "Codex account request cancelled.");
return result;
}
export async function readCodexAuthScope(
registry: CodexAccountRegistry,
signal?: AbortSignal,
): Promise<string> {
const result = await readAccountData("auth", registry, signal);
if (signal?.aborted) throw new CodexAccountError("cancelled", "Codex account request cancelled.");
return result.authScope;
}
async function readAccountData(
resource: AccountResource,
registry: CodexAccountRegistry,
signal: AbortSignal | undefined,
fetcher: typeof fetch = fetch,
): Promise<AccountData> {
const deadline = new AbortController();
const timer = setTimeout(() => deadline.abort(), TIMEOUT_MS);
const requestSignal = signal ? AbortSignal.any([signal, deadline.signal]) : deadline.signal;
@@ -40,9 +83,14 @@ export async function fetchCodexUsage(
requestSignal.throwIfAborted();
const headers = await resolveAccountHeaders(registry, requestSignal);
requestSignal.throwIfAborted();
const data = await requestUsage(headers, requestSignal, fetcher);
// Hash only the normalized credentials we actually send, never persist raw headers.
const authScope = createHash("sha256")
.update(JSON.stringify([headers.get("authorization"), headers.get("chatgpt-account-id")]))
.digest("hex");
if (resource === "auth") return { data: undefined, authScope };
const data = await requestAccountJson(resource, headers, requestSignal, fetcher);
requestSignal.throwIfAborted();
return data;
return { data, authScope };
} catch (error) {
if (signal?.aborted)
throw new CodexAccountError("cancelled", "Codex account request cancelled.");
@@ -73,7 +121,7 @@ async function resolveAccountHeaders(
if (!officialBase(provider?.baseUrl) || !model) {
throw new CodexAccountError(
"unsupported",
"Account usage requires the official Codex provider.",
"Account requests require the official Codex provider.",
);
}
if (!registry.isUsingOAuth(model)) throw authError();
@@ -94,7 +142,7 @@ async function resolveAccountHeaders(
) {
throw new CodexAccountError(
"unsupported",
"Account usage does not support endpoint overrides.",
"Account requests do not support endpoint overrides.",
);
}
const accessToken = resolvedAuth.auth.apiKey;
@@ -145,14 +193,15 @@ function authError(): CodexAccountError {
);
}
async function requestUsage(
async function requestAccountJson(
resource: NetworkResource,
headers: Headers,
requestSignal: AbortSignal,
fetcher: typeof fetch,
): Promise<unknown> {
let receivedResponse: Response | undefined;
try {
const request = fetcher(USAGE_URL, {
const request = fetcher(ACCOUNT_URLS[resource], {
method: "GET",
headers,
redirect: "error",
+423
View File
@@ -0,0 +1,423 @@
import { rmSync } from "node:fs";
import { zstdDecompressSync } from "node:zlib";
import { afterAll, afterEach, beforeEach, expect, it, vi } from "vitest";
import {
InMemoryModelsStore,
type Api,
type CredentialStore,
type Model,
type OAuthCredential,
} from "@earendil-works/pi-ai";
import {
createAgentSession,
DefaultResourceLoader,
ModelRuntime,
SessionManager,
SettingsManager,
type ExtensionUIContext,
} from "@earendil-works/pi-coding-agent";
import codex from "./index.ts";
const directory = await vi.hoisted(async () => {
const { mkdtempSync } = await import("node:fs");
const { tmpdir } = await import("node:os");
const { join } = await import("node:path");
const directory = mkdtempSync(join(tmpdir(), "codex-fast-sdk-"));
vi.stubEnv("PI_CODING_AGENT_DIR", directory);
vi.stubEnv("PI_OFFLINE", "1");
return directory;
});
const CATALOG_URL = "https://chatgpt.com/backend-api/codex/models?client_version=0.162.0";
const RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
const SOCKET_URL = "wss://chatgpt.com/backend-api/codex/responses";
const FAST_ENTRY = "eleith-codex-fast";
type Transport = "sse" | "websocket";
beforeEach(blockUnexpectedNetwork);
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
afterAll(() => {
vi.unstubAllEnvs();
rmSync(directory, { recursive: true, force: true });
});
it.each(["sse", "websocket"] as const)(
"SDK fast on reaches the native Codex %s wire, independently of the observed tier",
async (transport) => {
const network = installResponseTransports(transport);
const { runtime, model, accessToken, credentialWrites, resolveAuth, modelTurns } =
await createOfflineModelRuntime(transport, network.fetch);
const { session, manager, confirm, notify, errors, closeSession } =
await openRealExtensionSession(runtime, model);
try {
const branch = structuredClone(manager.getBranch());
await session.prompt("/eleith:codex fast");
await session.prompt("/eleith:codex fast status");
expect(network.fetch).not.toHaveBeenCalled();
expect(manager.getBranch()).toEqual(branch);
expect(modelTurns).not.toHaveBeenCalled();
await session.prompt("/eleith:codex fast on");
expect(confirm).toHaveBeenCalledOnce();
expect(network.catalog).toHaveBeenCalledOnce();
expect(network.catalog.mock.calls[0][0]).toBe(CATALOG_URL);
const headers = new Headers(network.catalog.mock.calls[0][1]?.headers);
expect(headers.get("authorization")).toBe(`Bearer ${accessToken}`);
expect(headers.get("chatgpt-account-id")).toBe("fast-sdk-account");
expect(fastChoices(manager)).toEqual([
{ provider: "openai-codex", modelId: model.id, mode: "on" },
]);
expect(modelTurns).not.toHaveBeenCalled();
resolveAuth.mockClear();
await session.prompt("Reply with ok.");
// Ordinary input checks the cached catalog's auth scope through Pi resolution only.
expect(resolveAuth).toHaveBeenCalledWith("openai-codex");
expect(network.catalog).toHaveBeenCalledOnce();
expect(modelTurns).toHaveBeenCalledOnce();
expect(network.payloads).toEqual([
expect.objectContaining({ model: model.id, service_tier: "priority" }),
]);
expect(session.getLastAssistantText()).toBe("ok");
expect(network.sse).toHaveBeenCalledTimes(transport === "sse" ? 1 : 0);
expect(network.sockets).toHaveLength(transport === "websocket" ? 1 : 0);
if (transport === "websocket") {
expect(network.payloads[0]).toHaveProperty("type", "response.create");
expect(network.sockets[0].readyState).toBe(3);
}
const requests = network.fetch.mock.calls.length;
resolveAuth.mockClear();
const afterTurn = structuredClone(manager.getBranch());
for (const command of ["/eleith:codex fast status", "/eleith:codex fast"]) {
await session.prompt(command);
expect(notify).toHaveBeenLastCalledWith(expect.stringContaining("Preference: on"), "info");
expect(notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last requested tier: priority"),
"info",
);
expect(notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last observed tier: default"),
"info",
);
}
expect(network.fetch).toHaveBeenCalledTimes(requests);
expect(resolveAuth).not.toHaveBeenCalled();
expect(manager.getBranch()).toEqual(afterTurn);
expect(modelTurns).toHaveBeenCalledOnce();
expect(credentialWrites).not.toHaveBeenCalled();
expect(errors).toEqual([]);
} finally {
await closeSession();
}
},
);
it("SDK replays the active fast branch alongside context controls, then off and reset change the wire", async () => {
const network = installResponseTransports("sse");
const { runtime, model, credentialWrites, modelTurns } = await createOfflineModelRuntime(
"sse",
network.fetch,
);
const manager = SessionManager.inMemory(directory);
const saved = { provider: "openai-codex", modelId: model.id, mode: "on" };
const onLeaf = manager.appendCustomEntry(FAST_ENTRY, saved);
// Leave a conflicting choice on an inactive sibling: replay must use getBranch(), not all entries.
manager.appendCustomEntry(FAST_ENTRY, { ...saved, mode: "off" });
manager.branch(onLeaf);
const { session, confirm, notify, errors, closeSession } = await openRealExtensionSession(
runtime,
model,
manager,
);
try {
expect(network.fetch).not.toHaveBeenCalled();
const branch = structuredClone(manager.getBranch());
await session.prompt("/eleith:codex fast");
await session.prompt("/eleith:codex fast status");
expect(notify).toHaveBeenLastCalledWith(expect.stringContaining("Preference: on"), "info");
expect(network.fetch).not.toHaveBeenCalled();
expect(manager.getBranch()).toEqual(branch);
const outputLimit = session.model?.maxTokens;
const thinking = session.thinkingLevel;
await session.prompt("/eleith:codex extend");
const extendedWindow = session.model?.contextWindow;
expect(extendedWindow).toBeGreaterThanOrEqual(model.contextWindow);
expect(session.model?.maxTokens).toBe(outputLimit);
expect(session.thinkingLevel).toBe(thinking);
expect(network.fetch).not.toHaveBeenCalled();
// No explicit fast command fetched metadata in this resumed session.
await session.prompt("Reply with ok on the resumed branch.");
expect(network.catalog).toHaveBeenCalledOnce();
expect(network.payloads[0]).toMatchObject({ model: model.id, service_tier: "priority" });
expect(fastChoices(manager)).toEqual([saved]);
expect(confirm).not.toHaveBeenCalled();
await session.prompt("/eleith:codex fast off");
expect(fastChoices(manager)).toEqual([saved, { ...saved, mode: "off" }]);
await session.prompt("Reply with ok without priority.");
expect(network.payloads[1]).toMatchObject({ model: model.id, service_tier: "default" });
await session.prompt("/eleith:codex fast reset");
expect(fastChoices(manager)).toEqual([
saved,
{ ...saved, mode: "off" },
{ ...saved, mode: "inherit" },
]);
await session.prompt("Reply with ok using the provider default.");
expect(network.payloads[2]).toHaveProperty("model", model.id);
expect(network.payloads[2]).not.toHaveProperty("service_tier");
expect(network.payloads).toHaveLength(3);
expect(session.getLastAssistantText()).toBe("ok");
expect(modelTurns).toHaveBeenCalledTimes(3);
expect(network.catalog).toHaveBeenCalledOnce();
expect(session.model?.contextWindow).toBe(extendedWindow);
expect(session.model?.maxTokens).toBe(outputLimit);
expect(session.thinkingLevel).toBe(thinking);
expect(credentialWrites).not.toHaveBeenCalled();
expect(errors).toEqual([]);
} finally {
await closeSession();
}
});
// Network policy: globals start closed; the supplied fetch accepts only these two fixed URLs.
function blockUnexpectedNetwork() {
vi.stubGlobal(
"fetch",
vi.fn(async () => {
throw new Error("Unexpected network request");
}),
);
vi.stubGlobal(
"WebSocket",
class {
constructor() {
throw new Error("Unexpected WebSocket request");
}
},
);
}
function installResponseTransports(transport: Transport) {
const payloads: Record<string, unknown>[] = [];
const http = createHttpResponses(transport, payloads);
vi.stubGlobal("fetch", http.fetch);
const sockets = transport === "websocket" ? installSocketResponses(payloads) : [];
return { ...http, payloads, sockets };
}
function createHttpResponses(transport: Transport, payloads: Record<string, unknown>[]) {
const catalog = vi.fn<typeof fetch>(async () =>
// The client wraps this internally as { data, authScope }; the network JSON does not.
Response.json({ models: [{ slug: "gpt-6-sol", service_tiers: [{ id: "priority" }] }] }),
);
const sse = vi.fn<typeof fetch>(async (_url, init) => {
if (transport !== "sse") throw new Error("Unexpected SSE fallback");
payloads.push(readHttpPayload(init));
const body = responseProtocolData()
.map((event) => `data: ${JSON.stringify(event)}\n\n`)
.join("");
return new Response(body, { headers: { "Content-Type": "text/event-stream" } });
});
const fetcher = vi.fn<typeof fetch>(async (url, init) => {
if (String(url) === CATALOG_URL) return catalog(url, init);
if (String(url) === RESPONSES_URL) return sse(url, init);
throw new Error("Unexpected network URL");
});
return { fetch: fetcher, catalog, sse };
}
function installSocketResponses(payloads: Record<string, unknown>[]) {
const sockets: FakeWebSocket[] = [];
class FakeWebSocket extends EventTarget {
readyState = 0;
constructor(url: string | URL) {
super();
if (String(url) !== SOCKET_URL) throw new Error("Unexpected WebSocket URL");
sockets.push(this);
queueMicrotask(() => {
if (this.readyState !== 0) return;
this.readyState = 1;
this.dispatchEvent(new Event("open"));
});
}
send(data: string) {
if (this.readyState !== 1) throw new Error("WebSocket is not open");
payloads.push(JSON.parse(data));
queueMicrotask(() => {
for (const frame of responseProtocolData()) {
this.dispatchEvent(new MessageEvent("message", { data: JSON.stringify(frame) }));
}
});
}
close() {
if (this.readyState === 3) return;
this.readyState = 3;
this.dispatchEvent(new Event("close"));
}
}
vi.stubGlobal("WebSocket", FakeWebSocket);
return sockets;
}
// Offline ModelRuntime: real built-in provider and OAuth resolution, never persistence or refresh I/O.
async function createOfflineModelRuntime(transport: Transport, fetcher: typeof fetch) {
const claim = { "https://api.openai.com/auth": { chatgpt_account_id: "fast-sdk-account" } };
const accessToken = `e30.${Buffer.from(JSON.stringify(claim)).toString("base64url")}.fake-signature`;
const { credentials, credentialWrites } = createReadOnlyCredentials(accessToken);
const runtime = await ModelRuntime.create({
credentials,
modelsPath: null,
modelsStore: new InMemoryModelsStore(),
allowModelNetwork: false,
refreshOnCreate: false,
});
const refresh = await runtime.refresh({ providers: ["openai-codex"], allowNetwork: false });
expect(refresh.errors.size).toBe(0);
expect(runtime.isUsingOAuth("openai-codex")).toBe(true);
const model = runtime.getModel("openai-codex", "gpt-6-sol");
if (!model) throw new Error("Built-in Codex model missing");
const resolveAuth = vi.spyOn(runtime, "getAuth");
const nativeStream = runtime.streamSimple.bind(runtime);
// Only select the transport at this public seam; retain native serialization and extension hooks.
const modelTurns = vi
.spyOn(runtime, "streamSimple")
.mockImplementation((selected, context, options) =>
nativeStream(selected, context, {
...options,
transport,
cacheRetention: "none", // No socket cache or idle-expiry timer survives a turn.
fetch: fetcher,
}),
);
return { runtime, model, accessToken, credentialWrites, resolveAuth, modelTurns };
}
function createReadOnlyCredentials(accessToken: string) {
const freshOAuth: OAuthCredential = {
type: "oauth",
access: accessToken,
refresh: "must-not-refresh",
expires: Date.now() + 3_600_000,
};
const credentialWrites = vi.fn<CredentialStore["modify"]>(async () => {
throw new Error("Credential writes forbidden");
});
const credentials: CredentialStore = {
read: async (id) => (id === "openai-codex" ? freshOAuth : undefined),
list: async () => [{ providerId: "openai-codex", type: "oauth" }],
modify: credentialWrites,
delete: async () => {
throw new Error("Credential deletion forbidden");
},
};
return { credentials, credentialWrites };
}
// Real extension session: production commands, branch persistence, and the public agent bridge.
async function openRealExtensionSession(
runtime: ModelRuntime,
model: Model<Api>,
manager = SessionManager.inMemory(directory),
) {
const settingsManager = SettingsManager.inMemory({
packages: [],
enableInstallTelemetry: false,
enableAnalytics: false,
compaction: { enabled: false },
retry: { enabled: false },
cacheWarming: "off",
});
const resourceLoader = new DefaultResourceLoader({
cwd: directory,
agentDir: directory,
settingsManager,
noExtensions: true,
noSkills: true,
noPromptTemplates: true,
noThemes: true,
noContextFiles: true,
extensionFactories: [codex],
});
await resourceLoader.reload();
expect(resourceLoader.getExtensions().errors).toEqual([]);
const { session } = await createAgentSession({
cwd: directory,
agentDir: directory,
resourceLoader,
settingsManager,
modelRuntime: runtime,
model,
thinkingLevel: "off",
sessionManager: manager,
noTools: "all",
});
const confirm = vi.fn(async () => true);
const notify = vi.fn();
const errors: unknown[] = [];
await session.bindExtensions({
mode: "tui",
uiContext: { notify, confirm } as unknown as ExtensionUIContext,
onError: (error) => errors.push(error),
});
return {
session,
manager,
confirm,
notify,
errors,
async closeSession() {
await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
session.dispose();
},
};
}
function fastChoices(manager: SessionManager) {
return manager
.getBranch()
.flatMap((entry) =>
entry.type === "custom" && entry.customType === FAST_ENTRY ? [entry.data] : [],
);
}
// Response protocol data: deliberately report default, not the requested priority.
function responseProtocolData() {
const message = {
type: "message",
id: "answer",
role: "assistant",
status: "completed",
content: [{ type: "output_text", text: "ok", annotations: [] }],
};
return [
{ type: "response.created", response: { id: "response" } },
{ type: "response.output_item.added", output_index: 0, item: { ...message, content: [] } },
{ type: "response.output_text.delta", output_index: 0, delta: "ok" },
{ type: "response.output_item.done", output_index: 0, item: message },
{
type: "response.completed",
response: { id: "response", status: "completed", service_tier: "default", output: [message] },
},
];
}
function readHttpPayload(init: RequestInit | undefined): Record<string, unknown> {
if (typeof init?.body === "string") return JSON.parse(init.body);
if (
init?.body instanceof Uint8Array &&
new Headers(init.headers).get("content-encoding") === "zstd"
) {
return JSON.parse(zstdDecompressSync(init.body).toString("utf8"));
}
throw new Error("Unexpected Codex request body");
}
+128
View File
@@ -0,0 +1,128 @@
import type { SessionEntry } from "@earendil-works/pi-coding-agent";
import { describe, expect, it } from "vitest";
import { FAST_ENTRY, readFastPreferences, type FastChoice } from "./fast-state.ts";
import { CONTEXT_ENTRY, readPreferences } from "./state.ts";
function preference(
data: unknown,
id = "preference",
parentId: string | null = null,
customType = FAST_ENTRY,
): Extract<SessionEntry, { type: "custom" }> {
return { type: "custom", id, parentId, timestamp: "2026-01-01T00:00:00Z", customType, data };
}
const enabled: FastChoice = { provider: "openai-codex", modelId: "gpt-6-sol", mode: "on" };
describe("active branch fast preferences", () => {
it("uses a dedicated transcript identifier and returns an empty map for an empty branch", () => {
expect(FAST_ENTRY).toBe("eleith-codex-fast");
expect(readFastPreferences([])).toEqual(new Map());
});
it("keeps choices independent by provider and model", () => {
const branch: readonly SessionEntry[] = [
preference(enabled),
preference({ ...enabled, modelId: "gpt-6-astra", mode: "off" }),
preference({ ...enabled, provider: "other", mode: "inherit" }),
];
expect(readFastPreferences(branch)).toEqual(
new Map([
["openai-codex/gpt-6-sol", "on"],
["openai-codex/gpt-6-astra", "off"],
["other/gpt-6-sol", "inherit"],
]),
);
});
it.each([
undefined,
null,
"on",
1,
true,
[],
Object.assign([], enabled),
{},
{ modelId: "gpt-6-sol", mode: "on" },
{ ...enabled, provider: 1 },
{ ...enabled, provider: "" },
{ provider: "openai-codex", mode: "on" },
{ ...enabled, modelId: null },
{ ...enabled, modelId: "" },
{ provider: "openai-codex", modelId: "gpt-6-sol" },
{ ...enabled, mode: true },
{ ...enabled, mode: "fast" },
{ ...enabled, mode: "ON" },
{ ...enabled, mode: null },
{ provider: "openai-codex", modelId: "gpt-6-sol", extended: true },
])("ignores malformed data: %j", (data) => {
expect(readFastPreferences([preference(data)])).toEqual(new Map());
});
it("requires a custom entry with the fast customType", () => {
expect(
readFastPreferences([
preference(enabled, "unrelated", null, "another-extension"),
{ type: "custom_message", customType: FAST_ENTRY, data: enabled },
{ type: "custom", data: enabled },
]),
).toEqual(new Map());
});
it("uses the latest valid entry in branch order, not timestamp order", () => {
const first = preference(enabled, "first");
const second = preference({ ...enabled, mode: "off" }, "second", first.id);
second.timestamp = "2025-01-01T00:00:00Z";
const malformed = preference({ ...enabled, mode: "invalid" }, "bad", second.id);
expect(readFastPreferences([first, second, malformed])).toEqual(
new Map([["openai-codex/gpt-6-sol", "off"]]),
);
expect(readFastPreferences([first, second, preference(enabled, "third", second.id)])).toEqual(
new Map([["openai-codex/gpt-6-sol", "on"]]),
);
});
it("preserves inherit as a tombstone until a later valid choice replaces it", () => {
const branch = [preference(enabled), preference({ ...enabled, mode: "inherit" })];
const preferences = readFastPreferences(branch);
expect(preferences.has("openai-codex/gpt-6-sol")).toBe(true);
expect(preferences.get("openai-codex/gpt-6-sol")).toBe("inherit");
expect(readFastPreferences([...branch, preference({ ...enabled, mode: "off" })])).toEqual(
new Map([["openai-codex/gpt-6-sol", "off"]]),
);
});
it("reads only the supplied branch without retaining abandoned choices", () => {
const shared = preference(enabled, "shared");
const abandoned: SessionEntry[] = [
shared,
preference({ ...enabled, mode: "inherit" }, "abandoned", shared.id),
preference({ ...enabled, modelId: "gpt-6-astra" }, "abandoned-model", "abandoned"),
];
const active: readonly SessionEntry[] = [
shared,
{ type: "session_info", id: "active", parentId: shared.id, timestamp: shared.timestamp },
];
expect(readFastPreferences(abandoned)).toEqual(
new Map([
["openai-codex/gpt-6-sol", "inherit"],
["openai-codex/gpt-6-astra", "on"],
]),
);
expect(readFastPreferences(active)).toEqual(new Map([["openai-codex/gpt-6-sol", "on"]]));
expect(readFastPreferences([])).toEqual(new Map());
});
it("keeps fast and context schemas independent even when payload fields overlap", () => {
const context = { provider: enabled.provider, modelId: enabled.modelId, extended: false };
const branch = [
preference({ ...enabled, extended: true }),
preference({ ...context, mode: "off" }, "context", null, CONTEXT_ENTRY),
preference(context, "wrong-fast-schema"),
preference(enabled, "wrong-context-schema", null, CONTEXT_ENTRY),
];
expect(readFastPreferences(branch)).toEqual(new Map([["openai-codex/gpt-6-sol", "on"]]));
expect(readPreferences(branch)).toEqual(new Map([["openai-codex/gpt-6-sol", false]]));
});
});
+36
View File
@@ -0,0 +1,36 @@
export const FAST_ENTRY = "eleith-codex-fast";
export type FastPreference = "on" | "off" | "inherit";
export interface FastChoice {
readonly provider: string;
readonly modelId: string;
readonly mode: FastPreference;
}
interface Entry {
readonly type: string;
readonly customType?: string;
readonly data?: unknown;
}
// Replay only the supplied active branch, retaining explicit inherit tombstones.
export function readFastPreferences(branch: readonly Entry[]): Map<string, FastPreference> {
const preferences = new Map<string, FastPreference>();
for (const entry of branch) {
if (entry.type !== "custom" || entry.customType !== FAST_ENTRY) continue;
if (!isFastChoice(entry.data)) continue;
preferences.set(`${entry.data.provider}/${entry.data.modelId}`, entry.data.mode);
}
return preferences;
}
function isFastChoice(value: unknown): value is FastChoice {
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
if (!("provider" in value) || typeof value.provider !== "string" || !value.provider.length)
return false;
if (!("modelId" in value) || typeof value.modelId !== "string" || !value.modelId.length)
return false;
if (!("mode" in value)) return false;
return value.mode === "on" || value.mode === "off" || value.mode === "inherit";
}
+42
View File
@@ -0,0 +1,42 @@
import { expect, it } from "vitest";
import { formatFastStatus, type FastStatus } from "./fast-status.ts";
const suspended: FastStatus = {
preference: "on",
eligibility: "unknown",
requestPolicy: "suspended",
lastRequested: undefined,
lastObserved: undefined,
conflict: false,
};
it("labels unknown delivery and explains selected scope, allowance consumption and cost-estimate limits", () => {
const text = formatFastStatus(suspended);
expect(text.split("\n")).toEqual([
"Codex fast mode (selected model)",
"Preference: on",
"Eligibility: unknown",
"Next request policy: suspended",
"Last requested tier: unknown",
"Last observed tier: unknown",
"Scope: direct legacy Codex; may include retries, compaction and cache warming. Selected scope is not physical-request identity.",
"Priority uses more allowance. Pi's cost estimate may miss priority pricing when response tier metadata is absent or normalized.",
"Observed tier is model-scoped, not a receipt for the last requested tier.",
]);
});
it("keeps requested and observed tiers distinct and adds the conflict note only when needed", () => {
const status: FastStatus = {
...suspended,
eligibility: "supported",
requestPolicy: "priority",
lastRequested: "priority",
lastObserved: "default",
};
const text = formatFastStatus(status);
expect(text).toContain("Last requested tier: priority\nLast observed tier: default");
expect(text).not.toContain("Tier conflict:");
expect(formatFastStatus({ ...status, conflict: true })).toContain(
"Tier conflict: an existing non-owned tier was left untouched.",
);
});
+33
View File
@@ -0,0 +1,33 @@
import type { FastEligibility } from "./capabilities.ts";
import type { FastPreference } from "./fast-state.ts";
export type RequestedTier = "priority" | "default";
export type ObservedTier = RequestedTier | "auto" | "flex" | "fast";
export type FastRequestPolicy = RequestedTier | "untouched" | "suspended" | "outside scope";
export interface FastStatus {
preference: FastPreference;
eligibility: FastEligibility;
requestPolicy: FastRequestPolicy;
lastRequested: RequestedTier | undefined;
lastObserved: ObservedTier | undefined;
conflict: boolean;
}
export function formatFastStatus(status: FastStatus): string {
const rows = [
"Codex fast mode (selected model)",
`Preference: ${status.preference}`,
`Eligibility: ${status.eligibility}`,
`Next request policy: ${status.requestPolicy}`,
`Last requested tier: ${status.lastRequested ?? "unknown"}`,
`Last observed tier: ${status.lastObserved ?? "unknown"}`,
];
if (status.conflict) rows.push("Tier conflict: an existing non-owned tier was left untouched.");
rows.push(
"Scope: direct legacy Codex; may include retries, compaction and cache warming. Selected scope is not physical-request identity.",
"Priority uses more allowance. Pi's cost estimate may miss priority pricing when response tier metadata is absent or normalized.",
"Observed tier is model-scoped, not a receipt for the last requested tier.",
);
return rows.join("\n");
}
+818
View File
@@ -0,0 +1,818 @@
import type { Api, Model } from "@earendil-works/pi-ai";
import type {
ExtensionAPI,
ProviderStreamEvent,
SessionEntry,
} from "@earendil-works/pi-coding-agent";
import { describe, expect, it, vi } from "vitest";
import {
CodexAccountError,
type CodexModelCatalog,
type fetchCodexModelCatalog,
type readCodexAuthScope,
} from "./client.ts";
import { FastController, type FastHost } from "./fast.ts";
import { FAST_ENTRY, type FastPreference } from "./fast-state.ts";
import { CONTEXT_ENTRY } from "./state.ts";
const model: Model<Api> = {
id: "gpt-6-sol",
name: "Sol",
provider: "openai-codex",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 272_000,
maxTokens: 128_000,
};
const otherModel: Model<Api> = { ...model, id: "gpt-6-astra", name: "Astra" };
const catalog: CodexModelCatalog = {
authScope: "scope-a",
data: { models: [{ slug: model.id, service_tiers: [{ id: "default" }, { id: "priority" }] }] },
};
describe("commands and ownership", () => {
it.each(["", " STATUS "])(
"reports %j without confirmation, auth IO, catalog IO or writes",
async (args) => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
await fast.controller.handle(args, fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenCalledExactlyOnceWith(
expect.stringContaining(
"Preference: on\nEligibility: unknown\nNext request policy: suspended",
),
"info",
);
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.ctx.modelRegistry.getProviderAuth).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
},
);
it("does not discover or persist anything when the cost confirmation is declined", async () => {
const fast = createFastControllerFixture();
fast.ctx.ui.confirm.mockResolvedValue(false);
await fast.controller.handle("on", fast.ctx);
expect(fast.ctx.ui.confirm).toHaveBeenCalledExactlyOnceWith(
"Enable Codex fast mode?",
expect.stringContaining("use more of your Codex allowance"),
{ signal: expect.any(AbortSignal) },
);
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
});
it("requires UI to enable, but permits a noninteractive explicit off", async () => {
const fast = createFastControllerFixture();
fast.ctx.hasUI = false;
await fast.controller.handle("on", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("interactive confirmation"),
"warning",
);
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
await fast.controller.handle("off", fast.ctx);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toEqual({
model: model.id,
service_tier: "default",
});
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
});
it("rejects invalid arguments and mutations during a run without prompting or IO", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("on extra", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
"Usage: /eleith:codex fast [on|off|toggle|status|reset]",
"warning",
);
fast.ctx.isIdle.mockReturnValue(false);
for (const action of ["on", "off", "toggle", "reset"])
await fast.controller.handle(action, fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Wait for the current run"),
"warning",
);
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it("reports a missing selection rather than confirming or persisting a choice", async () => {
const fast = createFastControllerFixture();
fast.ctx.model = undefined;
await fast.controller.handle("on", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith("No model selected.", "warning");
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it("persists on only after cost confirmation and matching catalog discovery complete", async () => {
const fast = createFastControllerFixture();
const confirmation = deferred<boolean>();
const response = deferred<CodexModelCatalog>();
const started = deferred<void>();
fast.ctx.ui.confirm.mockReturnValue(confirmation.promise);
fast.fetchCatalog.mockImplementationOnce(() => {
started.resolve();
return response.promise;
});
const enabling = fast.controller.handle(" ON ", fast.ctx);
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
// A second mutating command must not compete with the confirmation.
await fast.controller.handle("off", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Wait for the current run"),
"warning",
);
confirmation.resolve(true);
await started.promise;
expect(fast.readAuthScope).toHaveBeenCalledExactlyOnceWith(
fast.ctx.modelRegistry,
expect.any(AbortSignal),
);
expect(fast.fetchCatalog).toHaveBeenCalledExactlyOnceWith(
fast.ctx.modelRegistry,
expect.any(AbortSignal),
);
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
response.resolve(catalog);
await enabling;
expect(fast.appendEntry).toHaveBeenCalledExactlyOnceWith(FAST_ENTRY, {
provider: model.provider,
modelId: model.id,
mode: "on",
});
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toEqual({
model: model.id,
service_tier: "priority",
});
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Next request policy: priority"),
"info",
);
});
it.each([
{ models: [{ slug: otherModel.id, service_tiers: [{ id: "priority" }] }] },
{ models: [{ slug: model.id, service_tiers: [{ id: "default" }] }] },
])(
"does not enable without priority support for the selected catalog model: %j",
async (data) => {
const fast = createFastControllerFixture();
fast.fetchCatalog.mockResolvedValue({ ...catalog, data });
await fast.controller.handle("on", fast.ctx);
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Preference unchanged"),
"warning",
);
},
);
it("copies a frozen payload for explicit default, preserves fields, then resets with an inherit tombstone", async () => {
const fast = createFastControllerFixture();
const input = Object.freeze([{ role: "user", content: "hello" }]);
const payload = Object.freeze({
model: model.id,
input,
stream: true,
metadata: { trace: "keep" },
max_output_tokens: 42,
});
await fast.controller.handle("off", fast.ctx);
const transformed = fast.controller.transform(payload, fast.ctx);
expect(transformed).toEqual({ ...payload, service_tier: "default" });
expect(transformed).not.toBe(payload);
expect(transformed?.input).toBe(input);
expect(transformed?.metadata).toBe(payload.metadata);
expect(payload).not.toHaveProperty("service_tier");
expect(
fast.controller.transform(Object.freeze({ ...payload, service_tier: "default" }), fast.ctx),
).toBeUndefined();
await fast.controller.handle("off", fast.ctx);
expect(fast.appendEntry).toHaveBeenCalledOnce();
await fast.controller.handle("reset", fast.ctx);
expect(fast.appendEntry).toHaveBeenLastCalledWith(FAST_ENTRY, {
provider: model.provider,
modelId: model.id,
mode: "inherit",
});
expect(fast.controller.transform(payload, fast.ctx)).toBeUndefined();
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining(
"Preference: inherit\nEligibility: unknown\nNext request policy: untouched",
),
"info",
);
await fast.controller.handle("reset", fast.ctx);
expect(fast.appendEntry).toHaveBeenCalledTimes(2);
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
});
it("can remove its saved choice after the selection leaves direct scope", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("off", fast.ctx);
fast.ctx.modelRegistry.isUsingOAuth.mockReturnValue(false);
await fast.controller.handle("reset", fast.ctx);
expect(fast.appendEntry).toHaveBeenLastCalledWith(FAST_ENTRY, {
provider: model.provider,
modelId: model.id,
mode: "inherit",
});
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Next request policy: outside scope"),
"info",
);
});
it.each(["on", "off"])(
"preserves an existing non-owned flex tier when %s is selected",
async (mode) => {
const fast = createFastControllerFixture();
const tier = mode === "on" ? "priority" : "default";
await fast.controller.handle(mode, fast.ctx);
const payload = Object.freeze({ model: model.id, service_tier: "flex", input: [] });
expect(fast.controller.transform(payload, fast.ctx)).toBeUndefined();
expect(payload.service_tier).toBe("flex");
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Tier conflict: an existing non-owned tier was left untouched."),
"info",
);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last requested tier: unknown"),
"info",
);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining(`Next request policy: ${tier}`),
"info",
);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toEqual({
model: model.id,
service_tier: tier,
});
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.not.stringContaining("Tier conflict:"),
"info",
);
},
);
it("toggle confirms only transitions to on and leaves off intact if the next confirmation is declined", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("toggle", fast.ctx);
expect(fast.ctx.ui.confirm).toHaveBeenCalledOnce();
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
await fast.controller.handle("toggle", fast.ctx);
expect(fast.ctx.ui.confirm).toHaveBeenCalledOnce();
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("default");
fast.ctx.ui.confirm.mockResolvedValue(false);
await fast.controller.handle("toggle", fast.ctx);
expect(fast.ctx.ui.confirm).toHaveBeenCalledTimes(2);
expect(fast.appendEntry.mock.calls.map((call) => call[1])).toEqual([
{ provider: model.provider, modelId: model.id, mode: "on" },
{ provider: model.provider, modelId: model.id, mode: "off" },
]);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("default");
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
});
});
describe("branch replay and preparation", () => {
it("replays only the current branch, separates model choices, and ignores context choices", async () => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([
choice("off"),
choice("on"),
choice("off", otherModel),
{
...choice("off"),
customType: CONTEXT_ENTRY,
data: { provider: model.provider, modelId: model.id, extended: true, mode: "off" },
},
]);
fast.controller.restore(fast.ctx);
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
await fast.controller.prepare(fast.ctx);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
fast.ctx.model = otherModel;
expect(fast.controller.transform({ model: otherModel.id }, fast.ctx)?.service_tier).toBe(
"default",
);
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on"), choice("inherit")]);
fast.controller.restore(fast.ctx);
expect(fast.controller.transform({ model: otherModel.id }, fast.ctx)).toBeUndefined();
fast.ctx.model = model;
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
await fast.controller.prepare(fast.ctx);
expect(fast.readAuthScope).toHaveBeenCalledOnce();
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
});
it("lets fresh on expire at the TTL without hook IO or erasing the suspended preference", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("on", fast.ctx);
fast.clock.now += 5 * 60_000 - 1;
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
fast.clock.now += 1;
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "default" } }),
fast.ctx,
);
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining(
"Preference: on\nEligibility: unknown\nNext request policy: suspended",
),
"info",
);
expect(fast.readAuthScope).toHaveBeenCalledOnce();
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.appendEntry).toHaveBeenCalledOnce();
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledTimes(2);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.appendEntry).toHaveBeenCalledOnce();
expect(fast.ctx.ui.confirm).toHaveBeenCalledOnce();
});
it("prepares only idle saved-on input, checking auth even when the catalog cache is fresh", async () => {
const fast = createFastControllerFixture();
for (const branch of [[], [choice("off")], [choice("inherit")]]) {
fast.ctx.sessionManager.getBranch.mockReturnValue(branch);
fast.controller.restore(fast.ctx);
await fast.controller.prepare(fast.ctx);
}
expect(fast.readAuthScope).not.toHaveBeenCalled();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
fast.ctx.isIdle.mockReturnValue(false);
await fast.controller.prepare(fast.ctx);
expect(fast.readAuthScope).not.toHaveBeenCalled();
fast.ctx.isIdle.mockReturnValue(true);
await fast.controller.prepare(fast.ctx);
await fast.controller.prepare(fast.ctx);
expect(fast.readAuthScope).toHaveBeenCalledTimes(2);
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.ctx.ui.confirm).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it("invalidates a fresh catalog on auth scope change and recatalogs before resuming", async () => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
await fast.controller.prepare(fast.ctx);
fast.readAuthScope.mockResolvedValue("scope-b");
fast.fetchCatalog.mockResolvedValue({
authScope: "scope-b",
data: { models: [{ slug: model.id, service_tiers: [{ id: "default" }] }] },
});
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledTimes(2);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining(
"Preference: on\nEligibility: unsupported\nNext request policy: suspended",
),
"info",
);
fast.readAuthScope.mockResolvedValue("scope-c");
fast.fetchCatalog.mockResolvedValue({ ...catalog, authScope: "scope-c" });
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledTimes(3);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it("rejects a catalog fetched under a different auth scope, preserving the preference for a retry", async () => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
fast.fetchCatalog.mockResolvedValueOnce({ ...catalog, authScope: "scope-b" });
await fast.controller.prepare(fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("authentication changed during discovery"),
"warning",
);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledTimes(2);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it("honors 429 backoff without timers or hook retries and resumes only on later input", async () => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
fast.fetchCatalog.mockRejectedValueOnce(
new CodexAccountError("http", "Codex account service is rate limited.", 60_000),
);
await fast.controller.prepare(fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("rate limited. Preference unchanged."),
"warning",
);
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Try again later"),
"warning",
);
fast.clock.now += 60_000;
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "priority" } }),
fast.ctx,
);
await fast.controller.handle("status", fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.readAuthScope).toHaveBeenCalledTimes(2);
await fast.controller.prepare(fast.ctx);
expect(fast.fetchCatalog).toHaveBeenCalledTimes(2);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.appendEntry).not.toHaveBeenCalled();
});
it.each(["auth", "catalog"])(
"does not display unexpected %s errors or clear a saved-on preference",
async (source) => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
await fast.controller.prepare(fast.ctx);
const error = new Error("private failure details must not appear");
if (source === "auth") fast.readAuthScope.mockRejectedValueOnce(error);
else {
fast.clock.now += 5 * 60_000;
fast.fetchCatalog.mockRejectedValueOnce(error);
}
await fast.controller.prepare(fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
"Fast mode suspended: Could not prepare Codex fast mode. Preference unchanged.",
"warning",
);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Preference: on"),
"info",
);
expect(fast.appendEntry).not.toHaveBeenCalled();
},
);
});
describe("asynchronous ownership", () => {
it.each(["model", "session", "leaf"])(
"discards confirmation after the %s changes",
async (changed) => {
const fast = createFastControllerFixture();
const confirmation = deferred<boolean>();
fast.ctx.ui.confirm.mockReturnValue(confirmation.promise);
const enabling = fast.controller.handle("on", fast.ctx);
if (changed === "model") fast.ctx.model = otherModel;
if (changed === "session") fast.ctx.sessionManager.getSessionId.mockReturnValue("session-b");
if (changed === "leaf") fast.ctx.sessionManager.getLeafId.mockReturnValue("leaf-b");
confirmation.resolve(true);
await enabling;
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.ctx.ui.notify).not.toHaveBeenCalled();
},
);
it.each(["model", "session", "leaf"])(
"discards catalog discovery after the %s changes",
async (changed) => {
const fast = createFastControllerFixture();
const response = deferred<CodexModelCatalog>();
const started = deferred<void>();
fast.fetchCatalog.mockImplementationOnce(() => {
started.resolve();
return response.promise;
});
const enabling = fast.controller.handle("on", fast.ctx);
await started.promise;
if (changed === "model") fast.ctx.model = otherModel;
if (changed === "session") fast.ctx.sessionManager.getSessionId.mockReturnValue("session-b");
if (changed === "leaf") fast.ctx.sessionManager.getLeafId.mockReturnValue("leaf-b");
response.resolve(catalog);
await enabling;
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.ctx.ui.notify).not.toHaveBeenCalled();
expect(fast.controller.transform({ model: fast.ctx.model?.id }, fast.ctx)).toBeUndefined();
},
);
it("aborts invalidated discovery without letting a late result clear a newer operation", async () => {
const fast = createFastControllerFixture();
const oldCatalog = deferred<CodexModelCatalog>();
const oldStarted = deferred<void>();
fast.fetchCatalog.mockImplementationOnce(() => {
oldStarted.resolve();
return oldCatalog.promise;
});
const oldEnable = fast.controller.handle("on", fast.ctx);
await oldStarted.promise;
const oldSignal = fast.fetchCatalog.mock.calls[0][1]!;
fast.controller.invalidate();
expect(oldSignal.aborted).toBe(true);
const confirmation = deferred<boolean>();
fast.ctx.ui.confirm.mockReturnValueOnce(confirmation.promise);
const newEnable = fast.controller.handle("on", fast.ctx);
oldCatalog.resolve(catalog);
await oldEnable;
expect(fast.appendEntry).not.toHaveBeenCalled();
await fast.controller.handle("off", fast.ctx);
expect(fast.appendEntry).not.toHaveBeenCalled();
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Wait for the current run"),
"warning",
);
confirmation.resolve(true);
await newEnable;
expect(fast.appendEntry).toHaveBeenCalledExactlyOnceWith(FAST_ENTRY, {
provider: model.provider,
modelId: model.id,
mode: "on",
});
});
it.each(["resolve", "reject"])(
"shutdown ignores late confirmation %s before touching expired host getters",
async (outcome) => {
const fast = createFastControllerFixture();
const confirmation = deferred<boolean>();
fast.ctx.ui.confirm.mockReturnValue(confirmation.promise);
const enabling = fast.controller.handle("on", fast.ctx);
const signal = fast.ctx.ui.confirm.mock.calls[0][2]!.signal!;
const notify = fast.ctx.ui.notify;
fast.controller.dispose();
fast.controller.dispose();
for (const key of ["model", "modelRegistry", "isIdle", "sessionManager", "ui", "hasUI"]) {
Object.defineProperty(fast.ctx, key, {
get: () => {
throw new Error(`Expired ${key}`);
},
});
}
if (outcome === "resolve") confirmation.resolve(true);
else confirmation.reject(new Error("private late failure"));
await enabling;
expect(signal.aborted).toBe(true);
expect(notify).not.toHaveBeenCalled();
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
await fast.controller.handle("status", fast.ctx);
await fast.controller.prepare(fast.ctx);
fast.controller.restore(fast.ctx);
expect(fast.controller.transform({ model: model.id }, fast.ctx)).toBeUndefined();
fast.controller.observe(stream({ type: "response.completed" }), fast.ctx);
},
);
it.each(["resolve", "reject"])(
"shutdown ignores late catalog %s without accessing the expired host",
async (outcome) => {
const fast = createFastControllerFixture();
const response = deferred<CodexModelCatalog>();
const started = deferred<void>();
fast.fetchCatalog.mockImplementationOnce(() => {
started.resolve();
return response.promise;
});
const enabling = fast.controller.handle("on", fast.ctx);
await started.promise;
const signal = fast.fetchCatalog.mock.calls[0][1]!;
const notify = fast.ctx.ui.notify;
fast.controller.dispose();
for (const key of ["model", "modelRegistry", "isIdle", "sessionManager", "ui"]) {
Object.defineProperty(fast.ctx, key, {
get: () => {
throw new Error(`Expired ${key}`);
},
});
}
if (outcome === "resolve") response.resolve(catalog);
else
response.reject(
new CodexAccountError("http", "Codex account service is rate limited.", 60_000),
);
await enabling;
expect(signal.aborted).toBe(true);
expect(notify).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
},
);
it("shutdown stops a saved-on auth check before any late catalog IO or host access", async () => {
const fast = createFastControllerFixture();
fast.ctx.sessionManager.getBranch.mockReturnValue([choice("on")]);
fast.controller.restore(fast.ctx);
const scope = deferred<string>();
fast.readAuthScope.mockReturnValueOnce(scope.promise);
const preparing = fast.controller.prepare(fast.ctx);
const signal = fast.readAuthScope.mock.calls[0][1]!;
const notify = fast.ctx.ui.notify;
fast.controller.dispose();
for (const key of ["model", "modelRegistry", "isIdle", "sessionManager", "ui"]) {
Object.defineProperty(fast.ctx, key, {
get: () => {
throw new Error(`Expired ${key}`);
},
});
}
scope.resolve("scope-a");
await preparing;
expect(signal.aborted).toBe(true);
expect(fast.fetchCatalog).not.toHaveBeenCalled();
expect(notify).not.toHaveBeenCalled();
expect(fast.appendEntry).not.toHaveBeenCalled();
});
});
describe("provider stream observations", () => {
it("reports an observed default independently of requested priority, without changing the next preference", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("on", fast.ctx);
const payload = Object.freeze({ model: model.id, input: [], service_tier: "priority" });
expect(fast.controller.transform(payload, fast.ctx)).toBeUndefined();
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "default" } }),
fast.ctx,
);
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining(
"Next request policy: priority\nLast requested tier: priority\nLast observed tier: default",
),
"info",
);
expect(fast.controller.transform({ model: model.id }, fast.ctx)?.service_tier).toBe("priority");
expect(fast.fetchCatalog).toHaveBeenCalledOnce();
expect(fast.readAuthScope).toHaveBeenCalledOnce();
});
it.each([
{ type: "response.done", response: {} },
{ type: "response.incomplete", response: { service_tier: "unrecognized-tier" } },
])("reports missing or unrecognized terminal tier metadata as unknown: %j", async (data) => {
const fast = createFastControllerFixture();
await fast.controller.handle("off", fast.ctx);
fast.controller.transform({ model: model.id }, fast.ctx);
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "default" } }),
fast.ctx,
);
fast.controller.observe(stream(data), fast.ctx);
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last requested tier: default\nLast observed tier: unknown"),
"info",
);
expect(fast.readAuthScope).not.toHaveBeenCalled();
expect(fast.fetchCatalog).not.toHaveBeenCalled();
});
it("ignores wrong-model and nonterminal events, and does not show delivery from another session", async () => {
const fast = createFastControllerFixture();
await fast.controller.handle("off", fast.ctx);
fast.controller.transform({ model: model.id }, fast.ctx);
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "default" } }),
fast.ctx,
);
fast.controller.observe(
{
...stream({ type: "response.completed", response: { service_tier: "priority" } }),
model: otherModel.id,
},
fast.ctx,
);
fast.controller.observe(
stream({ type: "response.created", response: { service_tier: "priority" } }),
fast.ctx,
);
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last observed tier: default"),
"info",
);
fast.ctx.sessionManager.getSessionId.mockReturnValue("session-b");
fast.controller.observe(
stream({ type: "response.completed", response: { service_tier: "priority" } }),
fast.ctx,
);
await fast.controller.handle("status", fast.ctx);
expect(fast.ctx.ui.notify).toHaveBeenLastCalledWith(
expect.stringContaining("Last requested tier: unknown\nLast observed tier: unknown"),
"info",
);
});
});
function choice(mode: FastPreference, selected = model): Extract<SessionEntry, { type: "custom" }> {
return {
type: "custom",
id: `${selected.id}-${mode}`,
parentId: null,
timestamp: "2026-01-01T00:00:00Z",
customType: FAST_ENTRY,
data: { provider: selected.provider, modelId: selected.id, mode },
};
}
function deferred<T>() {
let resolve!: (value: T) => void;
let reject!: (error: unknown) => void;
const promise = new Promise<T>((done, fail) => {
resolve = done;
reject = fail;
});
return { promise, resolve, reject };
}
function createFastControllerFixture() {
const clock = { now: 1_000 };
const registry = {
getProvider: vi.fn<FastHost["modelRegistry"]["getProvider"]>(() => ({
baseUrl: model.baseUrl,
getModels: () => [model, otherModel],
})),
isUsingOAuth: vi.fn<FastHost["modelRegistry"]["isUsingOAuth"]>(() => true),
getRegisteredProviderConfig: vi.fn<FastHost["modelRegistry"]["getRegisteredProviderConfig"]>(
() => undefined,
),
getRegisteredNativeProvider: vi.fn<FastHost["modelRegistry"]["getRegisteredNativeProvider"]>(
() => undefined,
),
getProviderAuth: vi.fn<FastHost["modelRegistry"]["getProviderAuth"]>(async () => undefined),
};
const ctx = {
model: model as FastHost["model"],
modelRegistry: registry,
hasUI: true as FastHost["hasUI"],
isIdle: vi.fn<FastHost["isIdle"]>(() => true),
ui: {
notify: vi.fn<FastHost["ui"]["notify"]>(),
confirm: vi.fn<FastHost["ui"]["confirm"]>(async () => true),
},
sessionManager: {
getBranch: vi.fn<FastHost["sessionManager"]["getBranch"]>(() => []),
getSessionId: vi.fn(() => "session-a"),
getLeafId: vi.fn<FastHost["sessionManager"]["getLeafId"]>(() => "leaf-a"),
},
} satisfies FastHost;
const appendEntry = vi.fn<ExtensionAPI["appendEntry"]>();
const fetchCatalog = vi.fn<typeof fetchCodexModelCatalog>(async () => catalog);
const readAuthScope = vi.fn<typeof readCodexAuthScope>(async () => "scope-a");
const controller = new FastController(
{ appendEntry },
"eleith:codex",
fetchCatalog,
readAuthScope,
() => clock.now,
);
return { controller, ctx, appendEntry, fetchCatalog, readAuthScope, clock };
}
function stream(data: unknown): ProviderStreamEvent {
return {
type: "provider_stream_event",
provider: model.provider,
api: model.api,
model: model.id,
data,
};
}
+432
View File
@@ -0,0 +1,432 @@
import type {
ExtensionAPI,
ExtensionContext,
ProviderStreamEvent,
} from "@earendil-works/pi-coding-agent";
import {
CodexAccountError,
fetchCodexModelCatalog,
readCodexAuthScope,
type CodexAccountRegistry,
} from "./client.ts";
import { codexFastEligibility, type FastEligibility } from "./capabilities.ts";
import {
FAST_ENTRY,
readFastPreferences,
type FastChoice,
type FastPreference,
} from "./fast-state.ts";
import {
formatFastStatus,
type FastRequestPolicy,
type ObservedTier,
type RequestedTier,
} from "./fast-status.ts";
import { modelKey } from "./profiles.ts";
import {
isDirectCodexSelection,
matchesDirectCodexSelection,
type DirectCodexHost,
} from "./request-scope.ts";
const CAPABILITY_TTL_MS = 5 * 60_000;
export interface FastHost extends DirectCodexHost {
readonly modelRegistry: CodexAccountRegistry &
Pick<
ExtensionContext["modelRegistry"],
"getRegisteredProviderConfig" | "getRegisteredNativeProvider"
>;
readonly hasUI: boolean;
readonly isIdle: ExtensionContext["isIdle"];
readonly ui: Pick<ExtensionContext["ui"], "notify" | "confirm">;
readonly sessionManager: Pick<
ExtensionContext["sessionManager"],
"getBranch" | "getSessionId" | "getLeafId"
>;
}
interface EligibilityPreparation {
abortController: AbortController;
sessionId: string;
leafId: string | null;
modelKey: string;
api: string;
baseUrl: string;
}
interface CachedEligibility {
modelKey: string;
authScope: string;
eligibility: FastEligibility;
expiresAt: number;
}
interface ModelTierActivity {
modelKey: string;
sessionId: string;
requested: RequestedTier | undefined;
observed: ObservedTier | undefined;
conflict: boolean;
}
export class FastController {
private preferences = new Map<string, FastPreference>();
private cachedEligibility: CachedEligibility | undefined;
private pendingPreparation: EligibilityPreparation | undefined;
private modelTierActivity: ModelTierActivity | undefined;
// Honor endpoint Retry-After across model/branch changes; account switching is not supported.
private retryAt = 0;
private disposed = false;
constructor(
private readonly pi: Pick<ExtensionAPI, "appendEntry">,
private readonly commandName: string,
private readonly fetchCatalog: typeof fetchCodexModelCatalog = fetchCodexModelCatalog,
private readonly readAuthScope: typeof readCodexAuthScope = readCodexAuthScope,
private readonly now: () => number = Date.now,
) {}
restore(ctx: FastHost): void {
if (this.disposed) return;
this.invalidate();
this.preferences = readFastPreferences(ctx.sessionManager.getBranch());
}
async handle(args: string, ctx: FastHost): Promise<void> {
if (this.disposed) return;
const action = args.trim().toLowerCase();
if (!action || action === "status") {
this.report(ctx);
return;
}
if (!["on", "off", "toggle", "reset"].includes(action)) {
ctx.ui.notify(`Usage: /${this.commandName} fast [on|off|toggle|status|reset]`, "warning");
return;
}
if (this.pendingPreparation || !ctx.isIdle()) {
ctx.ui.notify("Wait for the current run or fast-mode change to finish.", "warning");
return;
}
const model = ctx.model;
if (!model) {
ctx.ui.notify("No model selected.", "warning");
return;
}
let mode: FastPreference;
if (action === "reset") mode = "inherit";
else if (action === "off") mode = "off";
else if (action === "toggle" && this.preferenceFor(modelKey(model)) === "on") mode = "off";
else mode = "on";
// Reset only removes our choice; it is safe even after the selection leaves supported scope.
if (mode === "inherit") {
this.rememberChoice(ctx, mode);
this.report(ctx);
return;
}
if (!isDirectCodexSelection(ctx)) {
ctx.ui.notify(
"Fast controls require a direct official legacy Codex model with Pi OAuth and no provider override.",
"warning",
);
return;
}
if (mode === "off") {
this.rememberChoice(ctx, mode);
this.report(ctx);
return;
}
if (!ctx.hasUI) {
ctx.ui.notify(
"Enabling fast mode requires interactive confirmation of higher allowance consumption.",
"warning",
);
return;
}
await this.enable(ctx);
}
// Only ordinary idle input prepares saved on choices. Never fetch from request/stream hooks.
async prepare(ctx: FastHost): Promise<void> {
if (this.disposed || this.pendingPreparation || !ctx.isIdle()) return;
const model = ctx.model;
if (!model || this.preferenceFor(modelKey(model)) !== "on" || !isDirectCodexSelection(ctx))
return;
const preparation = this.startPreparation(ctx);
try {
await this.refreshEligibility(ctx, preparation, false);
} catch (error) {
this.reportPreparationFailure(error, ctx, preparation);
} finally {
if (this.pendingPreparation === preparation) this.pendingPreparation = undefined;
}
}
transform(payload: unknown, ctx: FastHost): Record<string, unknown> | undefined {
if (this.disposed) return;
if (!matchesDirectCodexSelection(payload, ctx) || !ctx.model) {
this.modelTierActivity = undefined;
return;
}
const activity: ModelTierActivity = {
modelKey: modelKey(ctx.model),
sessionId: ctx.sessionManager.getSessionId(),
requested: undefined,
observed: undefined,
conflict: false,
};
this.modelTierActivity = activity;
const policy = this.requestPolicy(ctx);
if (policy !== "priority" && policy !== "default") return;
if (payload.service_tier !== undefined && payload.service_tier !== policy) {
activity.conflict = true;
return;
}
activity.requested = policy;
if (payload.service_tier === policy) return;
return { ...payload, service_tier: policy };
}
observe(event: ProviderStreamEvent, ctx: FastHost): void {
if (this.disposed) return;
const activity = this.modelTierActivity;
if (!activity || !isDirectCodexSelection(ctx) || !ctx.model) return;
if (
event.provider !== ctx.model.provider ||
event.api !== ctx.model.api ||
event.model !== ctx.model.id
)
return;
if (
activity.modelKey !== modelKey(ctx.model) ||
activity.sessionId !== ctx.sessionManager.getSessionId()
)
return;
const data = readObject(event.data);
if (!data || typeof data.type !== "string") return;
if (!["response.completed", "response.done", "response.incomplete"].includes(data.type)) return;
const tier = readObject(data.response)?.service_tier;
activity.observed = readObservedTier(tier);
}
invalidate(): void {
this.pendingPreparation?.abortController.abort();
this.pendingPreparation = undefined;
this.cachedEligibility = undefined;
this.modelTierActivity = undefined;
}
dispose(): void {
if (this.disposed) return;
this.disposed = true;
this.invalidate();
this.preferences.clear();
}
private async enable(ctx: FastHost): Promise<void> {
const preparation = this.startPreparation(ctx);
try {
const confirmed = await ctx.ui.confirm(
"Enable Codex fast mode?",
"Priority requests use more of your Codex allowance. This branch/model choice can also affect retries, compaction and cache warming. Speed and priority processing are not guaranteed.",
{ signal: preparation.abortController.signal },
);
if (!this.canApplyPreparation(ctx, preparation) || !confirmed) return;
const eligibility = await this.refreshEligibility(ctx, preparation, true);
if (!this.canApplyPreparation(ctx, preparation) || eligibility !== "supported") return;
this.rememberChoice(ctx, "on");
this.pendingPreparation = undefined;
this.report(ctx);
} catch (error) {
this.reportPreparationFailure(error, ctx, preparation);
} finally {
if (this.pendingPreparation === preparation) this.pendingPreparation = undefined;
}
}
private async refreshEligibility(
ctx: FastHost,
preparation: EligibilityPreparation,
force: boolean,
): Promise<FastEligibility | undefined> {
const signal = preparation.abortController.signal;
const authScope = await this.readAuthScope(ctx.modelRegistry, signal);
if (!this.canApplyPreparation(ctx, preparation)) return;
const cached = this.cachedEligibility;
if (
!force &&
cached?.modelKey === preparation.modelKey &&
cached.authScope === authScope &&
cached.expiresAt > this.now()
)
return cached.eligibility;
this.cachedEligibility = undefined;
this.modelTierActivity = undefined;
if (this.now() < this.retryAt) {
ctx.ui.notify(
"Fast mode suspended: the catalog service is rate limited. Try again later.",
"warning",
);
return;
}
const catalog = await this.fetchCatalog(ctx.modelRegistry, signal);
if (!this.canApplyPreparation(ctx, preparation)) return;
if (catalog.authScope !== authScope) {
ctx.ui.notify(
"Fast mode suspended: authentication changed during discovery. Try fast on again.",
"warning",
);
return;
}
const model = ctx.model;
if (!model) return;
const eligibility = codexFastEligibility(catalog.data, model.id);
this.cachedEligibility = {
modelKey: preparation.modelKey,
authScope,
eligibility,
expiresAt: this.now() + CAPABILITY_TTL_MS,
};
if (eligibility !== "supported") {
ctx.ui.notify(
`Fast mode suspended: model eligibility is ${eligibility}. Preference unchanged.`,
"warning",
);
}
return eligibility;
}
private startPreparation(ctx: FastHost): EligibilityPreparation {
const model = ctx.model;
if (!model) throw new Error("No model selected");
const preparation: EligibilityPreparation = {
abortController: new AbortController(),
sessionId: ctx.sessionManager.getSessionId(),
leafId: ctx.sessionManager.getLeafId(),
modelKey: modelKey(model),
api: model.api,
baseUrl: model.baseUrl,
};
this.pendingPreparation = preparation;
return preparation;
}
private canApplyPreparation(ctx: FastHost, preparation: EligibilityPreparation): boolean {
// Shutdown/cancellation can expire every host getter; check ownership before touching ctx.
if (
this.disposed ||
this.pendingPreparation !== preparation ||
preparation.abortController.signal.aborted
)
return false;
const model = ctx.model;
return (
!!model &&
modelKey(model) === preparation.modelKey &&
model.api === preparation.api &&
model.baseUrl === preparation.baseUrl &&
ctx.isIdle() &&
ctx.sessionManager.getSessionId() === preparation.sessionId &&
ctx.sessionManager.getLeafId() === preparation.leafId &&
isDirectCodexSelection(ctx)
);
}
private reportPreparationFailure(
error: unknown,
ctx: FastHost,
preparation: EligibilityPreparation,
): void {
if (!this.canApplyPreparation(ctx, preparation)) return;
this.cachedEligibility = undefined;
this.modelTierActivity = undefined;
if (error instanceof CodexAccountError && error.retryAfterMs !== undefined) {
this.retryAt = this.now() + error.retryAfterMs;
}
let message = "Could not prepare Codex fast mode.";
if (error instanceof CodexAccountError) message = error.message;
ctx.ui.notify(`Fast mode suspended: ${message} Preference unchanged.`, "warning");
}
private preferenceFor(selectedModelKey: string): FastPreference {
return this.preferences.get(selectedModelKey) ?? "inherit";
}
private rememberChoice(ctx: FastHost, mode: FastPreference): void {
const model = ctx.model;
if (!model) return;
const selectedModelKey = modelKey(model);
if (this.preferences.get(selectedModelKey) !== mode) {
const choice: FastChoice = { provider: model.provider, modelId: model.id, mode };
this.pi.appendEntry(FAST_ENTRY, choice);
this.preferences.set(selectedModelKey, mode);
}
this.modelTierActivity = undefined;
}
private currentEligibility(selectedModelKey: string): FastEligibility {
const cached = this.cachedEligibility;
if (!cached || cached.modelKey !== selectedModelKey || cached.expiresAt <= this.now())
return "unknown";
return cached.eligibility;
}
private requestPolicy(ctx: FastHost): FastRequestPolicy {
if (!ctx.model || !isDirectCodexSelection(ctx)) return "outside scope";
const selectedModelKey = modelKey(ctx.model);
const preference = this.preferenceFor(selectedModelKey);
if (preference === "inherit") return "untouched";
if (preference === "off") return "default";
if (this.pendingPreparation || this.currentEligibility(selectedModelKey) !== "supported")
return "suspended";
return "priority";
}
private report(ctx: FastHost): void {
const model = ctx.model;
if (!model) {
ctx.ui.notify("No model selected.", "info");
return;
}
const selectedModelKey = modelKey(model);
const direct = isDirectCodexSelection(ctx);
let activity = this.modelTierActivity;
if (
!activity ||
!direct ||
activity.modelKey !== selectedModelKey ||
activity.sessionId !== ctx.sessionManager.getSessionId()
)
activity = undefined;
let eligibility: FastEligibility = "unknown";
if (direct) eligibility = this.currentEligibility(selectedModelKey);
const status = {
preference: this.preferenceFor(selectedModelKey),
eligibility,
requestPolicy: this.requestPolicy(ctx),
lastRequested: activity?.requested,
lastObserved: activity?.observed,
conflict: activity?.conflict ?? false,
};
ctx.ui.notify(formatFastStatus(status), "info");
}
}
function readObject(value: unknown): Record<string, unknown> | undefined {
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
return value as Record<string, unknown>;
}
function readObservedTier(value: unknown): ObservedTier | undefined {
if (
value === "priority" ||
value === "default" ||
value === "auto" ||
value === "flex" ||
value === "fast"
)
return value;
return undefined;
}
+31 -2
View File
@@ -3,11 +3,13 @@ import { join } from "node:path";
import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent";
import { afterAll, expect, it, vi } from "vitest";
import codex from "./index.ts";
import { fetchCodexUsage } from "./client.ts";
import { fetchCodexModelCatalog, fetchCodexUsage, readCodexAuthScope } from "./client.ts";
vi.mock("./client.ts", async (importOriginal) => ({
...(await importOriginal<typeof import("./client.ts")>()),
fetchCodexUsage: vi.fn(),
fetchCodexModelCatalog: vi.fn(),
readCodexAuthScope: vi.fn(),
}));
const directory = await vi.hoisted(async () => {
@@ -28,6 +30,8 @@ afterAll(() => {
async function load(prefix = "eleith") {
vi.mocked(fetchCodexUsage).mockReset();
vi.mocked(fetchCodexModelCatalog).mockReset();
vi.mocked(readCodexAuthScope).mockReset();
writeFileSync(configPath, JSON.stringify({ commandPrefix: prefix }));
type Command = Parameters<ExtensionAPI["registerCommand"]>[1];
type Listener = (
@@ -91,6 +95,7 @@ it("registers only the configured native command once after binding", async () =
{ value: "extend", label: "extend" },
{ value: "restore", label: "restore" },
{ value: "usage", label: "usage" },
{ value: "fast", label: "fast" },
]);
});
@@ -102,7 +107,7 @@ it("handles no argument as status rather than an invalid default action", async
expect(context.pi.setModel).not.toHaveBeenCalled();
await context.command().handler("extend restore", context.ctx);
expect(context.notify).toHaveBeenLastCalledWith(
"Usage: /eleith:codex [extend|restore|usage]",
"Usage: /eleith:codex [extend|restore|usage|fast]",
"warning",
);
});
@@ -121,6 +126,30 @@ it("wires restore/reconcile hooks without provider calls for an unsupported mode
expect(context.pi.appendEntry).not.toHaveBeenCalled();
expect(context.pi.on.mock.calls.map(([name]) => name)).not.toContain("before_agent_start");
expect(fetchCodexUsage).not.toHaveBeenCalled();
expect(fetchCodexModelCatalog).not.toHaveBeenCalled();
expect(readCodexAuthScope).not.toHaveBeenCalled();
});
it("routes fast subcommands without allowing trailing arguments on other actions", async () => {
const context = await load("personal");
await context.emit("session_start");
await context.command().handler("fast status", context.ctx);
expect(context.notify).toHaveBeenLastCalledWith("No model selected.", "info");
await context.command().handler("fast off extra", context.ctx);
expect(context.notify).toHaveBeenLastCalledWith(
"Usage: /personal:codex fast [on|off|toggle|status|reset]",
"warning",
);
await context.command().handler("restore extra", context.ctx);
expect(context.notify).toHaveBeenLastCalledWith(
"Usage: /personal:codex [extend|restore|usage|fast]",
"warning",
);
expect(fetchCodexModelCatalog).not.toHaveBeenCalled();
expect(readCodexAuthScope).not.toHaveBeenCalled();
expect(fetchCodexUsage).not.toHaveBeenCalled();
expect(context.pi.appendEntry).not.toHaveBeenCalled();
expect(context.pi.setModel).not.toHaveBeenCalled();
});
it("routes usage through the configured command without requiring an idle Codex selection", async () => {
+20 -5
View File
@@ -3,38 +3,53 @@ import { registerCommand } from "@eleith/pi-internal/commands";
import { getSettings } from "@eleith/pi-internal/config";
import { ContextController } from "./controller.ts";
import { UsageController } from "./usage-controller.ts";
import { FastController } from "./fast.ts";
export default async function codex(pi: ExtensionAPI): Promise<void> {
const settings = await getSettings();
const commandName = `${settings.commandPrefix}:codex`;
const controller = new ContextController(pi, commandName);
const usage = new UsageController();
const fast = new FastController(pi, commandName);
registerCommand(pi, {
name: commandName,
description: "Codex context controls and account usage",
actions: ["extend", "restore", "usage"],
description: "Codex context, account usage and fast controls",
actions: ["extend", "restore", "usage", "fast"],
argumentActions: ["fast"],
defaultAction: "",
handler(action, ctx) {
handler(action, ctx, argument) {
if (action === "usage") return usage.report(ctx);
if (action === "fast") return fast.handle(argument ?? "", ctx);
return controller.handle(action, ctx);
},
});
pi.on("session_start", (_event, ctx) => controller.restore(ctx));
pi.on("session_start", (_event, ctx) => {
fast.restore(ctx);
return controller.restore(ctx);
});
pi.on("session_tree", (_event, ctx) => {
usage.cancel();
fast.restore(ctx);
return controller.restore(ctx);
});
pi.on("model_select", (_event, ctx) => {
usage.cancel();
fast.invalidate();
return controller.modelSelected(ctx);
});
pi.on("session_shutdown", (_event, ctx) => {
usage.dispose();
fast.dispose();
controller.shutdown(ctx);
});
pi.on("input", async (event, ctx) => {
if (event.streamingBehavior === undefined && ctx.isIdle()) await controller.reconcile(ctx);
if (event.streamingBehavior === undefined && ctx.isIdle()) {
await controller.reconcile(ctx);
await fast.prepare(ctx);
}
});
pi.on("before_provider_request", (event, ctx) => fast.transform(event.payload, ctx));
pi.on("provider_stream_event", (event, ctx) => fast.observe(event, ctx));
}
@@ -0,0 +1,237 @@
import { rmSync } from "node:fs";
import { zstdDecompressSync } from "node:zlib";
import { afterAll, afterEach, beforeEach, expect, it, vi } from "vitest";
import {
InMemoryModelsStore,
type Api,
type CredentialStore,
type Model,
type OAuthCredential,
} from "@earendil-works/pi-ai";
import {
createAgentSession,
DefaultResourceLoader,
ModelRuntime,
SessionManager,
SettingsManager,
} from "@earendil-works/pi-coding-agent";
import { matchesDirectCodexSelection } from "./request-scope.ts";
const directory = await vi.hoisted(async () => {
const { mkdtempSync } = await import("node:fs");
const { tmpdir } = await import("node:os");
const { join } = await import("node:path");
const directory = mkdtempSync(join(tmpdir(), "codex-request-scope-sdk-"));
vi.stubEnv("PI_CODING_AGENT_DIR", directory);
vi.stubEnv("PI_OFFLINE", "1");
return directory;
});
beforeEach(blockUnexpectedNetwork);
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
afterAll(() => {
vi.unstubAllEnvs();
rmSync(directory, { recursive: true, force: true });
});
it("SDK direct-selection payload transforms reach the native Codex SSE request", async () => {
const httpRequest = stubCodexResponse();
const { runtime, model } = await createOfflineCodexRuntime(httpRequest);
const { session, decisions, errors, closeSession } = await openProbeSession(runtime, model);
try {
await session.prompt("Reply with ok.");
expect(decisions).toEqual([true]);
expect(httpRequest).toHaveBeenCalledOnce();
const [, init] = httpRequest.mock.calls[0];
expect(readHttpPayload(init)).toMatchObject({ model: model.id, service_tier: "priority" });
expect(session.getLastAssistantText()).toBe("ok");
expect(errors).toEqual([]);
} finally {
await closeSession();
}
});
it("SDK virtual selections remain outside scope even when their dispatch is native Codex", async () => {
const httpRequest = stubCodexResponse();
const { runtime, model } = await createOfflineCodexRuntime(httpRequest);
runtime.registerVirtualModel({
provider: "openai-codex",
id: "auto",
name: "Test route",
route: () => ({ model, thinkingLevel: "off" }),
});
const selected = runtime.getModel("openai-codex", "auto");
if (!selected) throw new Error("Virtual test model missing");
const { session, decisions, errors, closeSession } = await openProbeSession(runtime, selected);
try {
await session.prompt("Reply with ok.");
expect(decisions).toEqual([false]);
expect(session.model?.api).toBe("pi-virtual");
const [, init] = httpRequest.mock.calls[0];
const payload = readHttpPayload(init);
expect(payload.model).toBe(model.id);
expect(payload).not.toHaveProperty("service_tier");
expect(session.getLastAssistantText()).toBe("ok");
expect(errors).toEqual([]);
} finally {
await closeSession();
}
});
async function createOfflineCodexRuntime(httpRequest: typeof fetch) {
const payload = { "https://api.openai.com/auth": { chatgpt_account_id: "scope-test-account" } };
const access = `e30.${Buffer.from(JSON.stringify(payload)).toString("base64")}.fake-signature`;
const credential: OAuthCredential = {
type: "oauth",
access,
refresh: "must-not-refresh",
expires: Date.now() + 3_600_000,
};
const credentials: CredentialStore = {
read: async (id) => {
if (id !== "openai-codex") return undefined;
return credential;
},
list: async () => [{ providerId: "openai-codex", type: "oauth" }],
modify: async () => {
throw new Error("Credential writes forbidden");
},
delete: async () => {
throw new Error("Credential deletion forbidden");
},
};
const runtime = await ModelRuntime.create({
credentials,
modelsPath: null,
modelsStore: new InMemoryModelsStore(),
allowModelNetwork: false,
refreshOnCreate: false,
});
const refresh = await runtime.refresh({ providers: ["openai-codex"], allowNetwork: false });
expect(refresh.errors.size).toBe(0);
const model = runtime.getModel("openai-codex", "gpt-6-sol");
if (!model) throw new Error("Built-in test model missing");
const nativeStream = runtime.streamSimple.bind(runtime);
// Force this transport through a public test seam; keep the real provider and extension bridge.
vi.spyOn(runtime, "streamSimple").mockImplementation((selected, context, options) =>
nativeStream(selected, context, {
...options,
transport: "sse",
cacheRetention: "none",
fetch: httpRequest,
}),
);
return { runtime, model };
}
async function openProbeSession(runtime: ModelRuntime, model: Model<Api>) {
const decisions: boolean[] = [];
const settingsManager = SettingsManager.inMemory({
packages: [],
enableInstallTelemetry: false,
enableAnalytics: false,
compaction: { enabled: false },
retry: { enabled: false },
cacheWarming: "off",
});
const resourceLoader = new DefaultResourceLoader({
cwd: directory,
agentDir: directory,
settingsManager,
noExtensions: true,
noSkills: true,
noPromptTemplates: true,
noThemes: true,
noContextFiles: true,
extensionFactories: [
(pi) => {
pi.on("before_provider_request", (event, ctx) => {
const payload = event.payload;
const matches = matchesDirectCodexSelection(payload, ctx);
decisions.push(matches);
// Test-only transform; production registration belongs to the fast-command changeset.
if (matches) return { ...payload, service_tier: "priority" };
});
},
],
});
await resourceLoader.reload();
const { session } = await createAgentSession({
cwd: directory,
agentDir: directory,
resourceLoader,
settingsManager,
modelRuntime: runtime,
model,
thinkingLevel: "off",
sessionManager: SessionManager.inMemory(directory),
noTools: "all",
});
const errors: unknown[] = [];
await session.bindExtensions({ mode: "print", onError: (error) => errors.push(error) });
return {
session,
decisions,
errors,
async closeSession() {
await session.extensionRunner.emit({ type: "session_shutdown", reason: "quit" });
session.dispose();
},
};
}
function stubCodexResponse() {
const message = {
type: "message",
id: "answer",
role: "assistant",
status: "completed",
content: [{ type: "output_text", text: "ok", annotations: [] }],
};
const events = [
{ type: "response.created", response: { id: "response" } },
{ type: "response.output_item.added", output_index: 0, item: { ...message, content: [] } },
{ type: "response.output_text.delta", output_index: 0, delta: "ok" },
{ type: "response.output_item.done", output_index: 0, item: message },
{
type: "response.completed",
response: { id: "response", status: "completed", output: [message] },
},
];
const body = events.map((event) => `data: ${JSON.stringify(event)}\n\n`).join("");
return vi.fn<typeof fetch>(async (url) => {
expect(String(url)).toBe("https://chatgpt.com/backend-api/codex/responses");
return new Response(body, { headers: { "Content-Type": "text/event-stream" } });
});
}
function blockUnexpectedNetwork() {
vi.stubGlobal(
"fetch",
vi.fn(async () => {
throw new Error("Unexpected network request");
}),
);
vi.stubGlobal(
"WebSocket",
class {
constructor() {
throw new Error("Unexpected WebSocket request");
}
},
);
}
function readHttpPayload(init: RequestInit | undefined): Record<string, unknown> {
if (typeof init?.body === "string") return JSON.parse(init.body);
if (
init?.body instanceof Uint8Array &&
new Headers(init.headers).get("content-encoding") === "zstd"
) {
return JSON.parse(zstdDecompressSync(init.body).toString("utf8"));
}
throw new Error("Unexpected Codex request body");
}
@@ -0,0 +1,79 @@
import type { Api, Model } from "@earendil-works/pi-ai";
import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
import { expect, it, vi } from "vitest";
import { matchesDirectCodexSelection, type DirectCodexHost } from "./request-scope.ts";
const model: Model<Api> = {
id: "gpt-6-sol",
name: "Sol",
provider: "openai-codex",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
reasoning: true,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 272_000,
maxTokens: 128_000,
};
type Registry = DirectCodexHost["modelRegistry"];
function fixture() {
const registry = {
getProvider: vi.fn<Registry["getProvider"]>(() => ({ baseUrl: model.baseUrl })),
isUsingOAuth: vi.fn<Registry["isUsingOAuth"]>(() => true),
getRegisteredProviderConfig: vi.fn<Registry["getRegisteredProviderConfig"]>(() => undefined),
getRegisteredNativeProvider: vi.fn<Registry["getRegisteredNativeProvider"]>(() => undefined),
};
return { model, modelRegistry: registry };
}
it("matches a direct selection without modifying the payload or taking ownership of an existing tier", () => {
const payload = Object.freeze({ model: model.id, service_tier: "flex", input: [] });
expect(matchesDirectCodexSelection(payload, fixture())).toBe(true);
expect(payload).toEqual({ model: model.id, service_tier: "flex", input: [] });
});
it.each([
undefined,
{ ...model, provider: "openai" },
{ ...model, api: "pi-virtual" },
{ ...model, baseUrl: "https://gateway.invalid" },
])("rejects unsupported selections: %j", (selected) => {
expect(matchesDirectCodexSelection({ model: model.id }, { ...fixture(), model: selected })).toBe(
false,
);
});
it.each([null, [], {}, { model: "different-model" }, { model: 42 }])(
"requires an object payload naming the selected model: %j",
(payload) => {
expect(matchesDirectCodexSelection(payload, fixture())).toBe(false);
},
);
it("rejects an effective endpoint override even if the selected model still looks official", () => {
const host = fixture();
host.modelRegistry.getProvider.mockReturnValue({ baseUrl: "https://gateway.invalid" });
expect(matchesDirectCodexSelection({ model: model.id }, host)).toBe(false);
});
it("excludes registered provider overlays", () => {
const host = fixture();
host.modelRegistry.getRegisteredProviderConfig.mockReturnValue({
headers: { "X-Example": "overlay" },
});
expect(matchesDirectCodexSelection({ model: model.id }, host)).toBe(false);
});
it("excludes registered native replacements, including a re-registered stock provider", () => {
const host = fixture();
host.modelRegistry.getRegisteredNativeProvider.mockReturnValue(openaiCodexProvider());
expect(matchesDirectCodexSelection({ model: model.id }, host)).toBe(false);
});
it("does not match a selection whose auth snapshot is not OAuth", () => {
const host = fixture();
host.modelRegistry.isUsingOAuth.mockReturnValue(false);
expect(matchesDirectCodexSelection({ model: model.id }, host)).toBe(false);
});
+42
View File
@@ -0,0 +1,42 @@
import type { Api, Model, Provider } from "@earendil-works/pi-ai";
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
const PROVIDER = "openai-codex";
const BASE_URL = "https://chatgpt.com/backend-api";
type RequestRegistry = Pick<
ExtensionContext["modelRegistry"],
"isUsingOAuth" | "getRegisteredProviderConfig" | "getRegisteredNativeProvider"
> & {
getProvider(id: string): Pick<Provider, "baseUrl"> | undefined;
};
export interface DirectCodexHost {
readonly model: Model<Api> | undefined;
readonly modelRegistry: RequestRegistry;
}
// Pi exposes the selection, not physical request identity; this only checks direct-selection scope.
export function matchesDirectCodexSelection(
payload: unknown,
ctx: DirectCodexHost,
): payload is Record<string, unknown> {
if (!isDirectCodexSelection(ctx)) return false;
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return false;
return "model" in payload && payload.model === ctx.model?.id;
}
export function isDirectCodexSelection(ctx: DirectCodexHost): boolean {
const model = ctx.model;
if (!model || model.provider !== PROVIDER || model.api !== "openai-codex-responses") return false;
if (!officialBase(model.baseUrl)) return false;
const registry = ctx.modelRegistry;
if (!officialBase(registry.getProvider(PROVIDER)?.baseUrl)) return false;
if (registry.getRegisteredProviderConfig(PROVIDER) !== undefined) return false;
if (registry.getRegisteredNativeProvider(PROVIDER) !== undefined) return false;
return registry.isUsingOAuth(model);
}
function officialBase(url: string | undefined): boolean {
return url?.replace(/\/+$/, "") === BASE_URL;
}