Skip to content
3 changes: 2 additions & 1 deletion apps/server/src/provider/CodexChatGptAuth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,8 @@ it.effect(
yield* h.phase("succeeded");
assert.strictEqual(Option.getOrThrow(yield* h.auth.read).subject, "user-test");
assert.strictEqual(h.exchanges[0]?.get("client_id"), "oaiapp_test");
yield* h.seedExpired;
// 75 s left on the one-hour token: inside the renewal margin Codex's 60 s token cache needs.
yield* TestClock.adjust("3525 seconds");
const records = yield* Effect.all([h.auth.access, h.auth.access], {
concurrency: "unbounded",
});
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/provider/CodexChatGptAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import type { ProviderAuthFlowContext } from "@t3tools/provider-core/server/prov
import { HttpClient, HttpClientRequest } from "effect/http";
import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
import { withChatGptSessionLock } from "./CodexChatGptSessionLock.ts";
import { TOKEN_RENEWAL_MARGIN_MS } from "./CodexManagedTokenProtocol.ts";
import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import * as ServerSettings from "../serverSettings.ts";
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
Expand Down Expand Up @@ -823,7 +824,7 @@ export const makeCodexChatGptAuth = Effect.fn("makeCodexChatGptAuth")(function*
"Token sharing is disabled. Sign in with ChatGPT and enable token sharing.",
);
const now = yield* Clock.currentTimeMillis;
if (record.expiresAt - now > 60_000) return record;
if (record.expiresAt - now > TOKEN_RENEWAL_MARGIN_MS) return record;
if (record.earliestRefreshAt !== null && record.earliestRefreshAt > now) {
if (record.expiresAt > now) return record;
return yield* failure(
Expand Down
5 changes: 4 additions & 1 deletion apps/server/src/provider/CodexManagedRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as ServerSecretStore from "../auth/ServerSecretStore.ts";
import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
import * as CodexInstallation from "./CodexInstallation.ts";
import { makeCodexManagedRuntime } from "./CodexManagedRuntime.ts";
import { TOKEN_RENEWAL_MARGIN_MS } from "./CodexManagedTokenProtocol.ts";
import * as ProviderCredentialStore from "./ProviderCredentialStore.ts";
import { codexAppServerArgs } from "./codexLaunchArgs.ts";

Expand Down Expand Up @@ -147,7 +148,9 @@ it.live.each(
assert.include(provider, 'base_url="https://api.openai.com/v1"');
assert.include(provider, 'model_catalog_url="https://api.openai.com/v1/models"');
assert.notInclude(provider, "env_key");
assert.include(provider, "refresh_interval_ms=0");
const refreshIntervalMs = Number(/refresh_interval_ms=(\d+)/u.exec(provider)?.[1]);
// regression: #33701 0 makes Codex reuse the launch token forever for catalog requests.
assert.isTrue(refreshIntervalMs > 0 && refreshIntervalMs < TOKEN_RENEWAL_MARGIN_MS);
// regression: #16132 Codex fetches each token through this helper, so a running
// session picks up renewals instead of keeping the token it launched with.
if (source === "managed" && account === "primary") {
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/provider/CodexManagedRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as ServerEnvironment from "../environment/ServerEnvironment.ts";
import { resolveSelfInvocation, selfInvocationArgs } from "@t3tools/shared/nodeRuntime";
import * as CodexInstallation from "./CodexInstallation.ts";
import { makeCodexChatGptAuth } from "./CodexChatGptAuth.ts";
import { COMMAND_TIMEOUT_MS } from "./CodexManagedTokenProtocol.ts";
import { COMMAND_TIMEOUT_MS, TOKEN_REFRESH_INTERVAL_MS } from "./CodexManagedTokenProtocol.ts";
import { materializeCodexShadowHome } from "./Drivers/CodexHomeLayout.ts";

export interface CodexEffectiveRuntime {
Expand All @@ -23,7 +23,7 @@ const managedCodexLaunchArgs = (command: string, args: ReadonlyArray<string>) =>
[
'model_provider="openai_token_sharing"',
// Codex persists this provider ID in rollouts and needs it unchanged to resume.
`model_providers.openai_token_sharing={name="OpenAI Token Sharing",base_url="https://api.openai.com/v1",model_catalog_url="https://api.openai.com/v1/models",wire_api="responses",requires_openai_auth=false,supports_websockets=false,auth={command=${JSON.stringify(command)},args=${JSON.stringify(args)},timeout_ms=${COMMAND_TIMEOUT_MS},refresh_interval_ms=0}}`,
`model_providers.openai_token_sharing={name="OpenAI Token Sharing",base_url="https://api.openai.com/v1",model_catalog_url="https://api.openai.com/v1/models",wire_api="responses",requires_openai_auth=false,supports_websockets=false,auth={command=${JSON.stringify(command)},args=${JSON.stringify(args)},timeout_ms=${COMMAND_TIMEOUT_MS},refresh_interval_ms=${TOKEN_REFRESH_INTERVAL_MS}}}`,
"features.api_key_model_discovery=true",
"features.multi_agent=false",
"features.multi_agent_v2=false",
Expand Down
3 changes: 3 additions & 0 deletions apps/server/src/provider/CodexManagedTokenProtocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export const RESPONSE_BYTES = 64 * 1024;
export const SOCKET_IDLE_MS = 5_000;
// Existing lock retry (40s), OAuth (20s), plus startup/store headroom (20s).
export const COMMAND_TIMEOUT_MS = 80_000;
// Codex reuses a helper token this long; the broker renews earlier, leaving room for helper latency.
export const TOKEN_REFRESH_INTERVAL_MS = 60_000;
export const TOKEN_RENEWAL_MARGIN_MS = TOKEN_REFRESH_INTERVAL_MS + 30_000;
class ProtocolError extends Schema.TaggedError<ProtocolError>()("ManagedTokenProtocolError", {}) {
override get message(): string {
return "The managed token frame was invalid or incomplete.";
Expand Down
Loading