Skip to content

Commit 1ff461f

Browse files
committed
fix(cli): stabilize oauth session auth epochs
1 parent 8778521 commit 1ff461f

6 files changed

Lines changed: 26 additions & 22 deletions

File tree

src/agents/cli-auth-epoch.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ const defaultCliAuthEpochDeps: CliAuthEpochDeps = {
2323

2424
const cliAuthEpochDeps: CliAuthEpochDeps = { ...defaultCliAuthEpochDeps };
2525

26+
export const CLI_AUTH_EPOCH_VERSION = 2;
27+
2628
export function setCliAuthEpochTestDeps(overrides: Partial<CliAuthEpochDeps>): void {
2729
Object.assign(cliAuthEpochDeps, overrides);
2830
}
@@ -41,24 +43,16 @@ function encodeUnknown(value: unknown): string {
4143

4244
function encodeClaudeCredential(credential: ClaudeCliCredential): string {
4345
if (credential.type === "oauth") {
44-
return JSON.stringify([
45-
"oauth",
46-
credential.provider,
47-
credential.access,
48-
credential.refresh,
49-
credential.expires,
50-
]);
46+
return JSON.stringify(["oauth", credential.provider, credential.refresh]);
5147
}
52-
return JSON.stringify(["token", credential.provider, credential.token, credential.expires]);
48+
return JSON.stringify(["token", credential.provider, credential.token]);
5349
}
5450

5551
function encodeCodexCredential(credential: CodexCliCredential): string {
5652
return JSON.stringify([
5753
credential.type,
5854
credential.provider,
59-
credential.access,
6055
credential.refresh,
61-
credential.expires,
6256
credential.accountId ?? null,
6357
]);
6458
}
@@ -81,20 +75,16 @@ function encodeAuthProfileCredential(credential: AuthProfileCredential): string
8175
credential.provider,
8276
credential.token ?? null,
8377
encodeUnknown(credential.tokenRef),
84-
credential.expires ?? null,
8578
credential.email ?? null,
8679
credential.displayName ?? null,
8780
]);
8881
case "oauth":
8982
return JSON.stringify([
9083
"oauth",
9184
credential.provider,
92-
credential.access,
9385
credential.refresh,
94-
credential.expires,
9586
credential.clientId ?? null,
9687
credential.email ?? null,
97-
credential.displayName ?? null,
9888
credential.enterpriseUrl ?? null,
9989
credential.projectId ?? null,
10090
credential.accountId ?? null,

src/agents/cli-runner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export async function runPreparedCliAgent(
7272
? { authProfileId: context.effectiveAuthProfileId }
7373
: {}),
7474
...(context.authEpoch ? { authEpoch: context.authEpoch } : {}),
75+
authEpochVersion: context.authEpochVersion,
7576
...(context.extraSystemPromptHash
7677
? { extraSystemPromptHash: context.extraSystemPromptHash }
7778
: {}),

src/agents/cli-runner/prepare.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
makeBootstrapWarn as makeBootstrapWarnImpl,
2222
resolveBootstrapContextForRun as resolveBootstrapContextForRunImpl,
2323
} from "../bootstrap-files.js";
24-
import { resolveCliAuthEpoch } from "../cli-auth-epoch.js";
24+
import { CLI_AUTH_EPOCH_VERSION, resolveCliAuthEpoch } from "../cli-auth-epoch.js";
2525
import { resolveCliBackendConfig } from "../cli-backends.js";
2626
import { hashCliSessionText, resolveCliSessionReuse } from "../cli-session.js";
2727
import { resolveHeartbeatPromptForSystemPrompt } from "../heartbeat-system-prompt.js";
@@ -188,15 +188,16 @@ export async function prepareCliRunContext(
188188
modelId,
189189
authProfileId: effectiveAuthProfileId,
190190
});
191+
const skipLocalCredentialEpoch = shouldSkipLocalCliCredentialEpoch({
192+
authEpochMode: backendResolved.authEpochMode,
193+
authProfileId: effectiveAuthProfileId,
194+
authCredential,
195+
preparedExecution,
196+
});
191197
const authEpoch = await resolveCliAuthEpoch({
192198
provider: params.provider,
193199
authProfileId: effectiveAuthProfileId,
194-
skipLocalCredential: shouldSkipLocalCliCredentialEpoch({
195-
authEpochMode: backendResolved.authEpochMode,
196-
authProfileId: effectiveAuthProfileId,
197-
authCredential,
198-
preparedExecution,
199-
}),
200+
skipLocalCredential: skipLocalCredentialEpoch,
200201
});
201202
const preparedBackendEnv =
202203
preparedExecution?.env && Object.keys(preparedExecution.env).length > 0
@@ -232,6 +233,7 @@ export async function prepareCliRunContext(
232233
binding: params.cliSessionBinding,
233234
authProfileId: effectiveAuthProfileId,
234235
authEpoch,
236+
authEpochVersion: CLI_AUTH_EPOCH_VERSION,
235237
extraSystemPromptHash,
236238
mcpConfigHash: preparedBackendFinal.mcpConfigHash,
237239
mcpResumeHash: preparedBackendFinal.mcpResumeHash,
@@ -332,6 +334,7 @@ export async function prepareCliRunContext(
332334
bootstrapPromptWarningLines: bootstrapPromptWarning.lines,
333335
heartbeatPrompt,
334336
authEpoch,
337+
authEpochVersion: CLI_AUTH_EPOCH_VERSION,
335338
extraSystemPromptHash,
336339
};
337340
}

src/agents/cli-runner/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,6 @@ export type PreparedCliRunContext = {
6868
bootstrapPromptWarningLines: string[];
6969
heartbeatPrompt?: string;
7070
authEpoch?: string;
71+
authEpochVersion: number;
7172
extraSystemPromptHash?: string;
7273
};

src/agents/cli-session.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function getCliSessionBinding(
2828
sessionId: bindingSessionId,
2929
authProfileId: normalizeOptionalString(fromBindings?.authProfileId),
3030
authEpoch: normalizeOptionalString(fromBindings?.authEpoch),
31+
authEpochVersion: fromBindings?.authEpochVersion,
3132
extraSystemPromptHash: normalizeOptionalString(fromBindings?.extraSystemPromptHash),
3233
mcpConfigHash: normalizeOptionalString(fromBindings?.mcpConfigHash),
3334
mcpResumeHash: normalizeOptionalString(fromBindings?.mcpResumeHash),
@@ -78,6 +79,9 @@ export function setCliSessionBinding(
7879
...(normalizeOptionalString(binding.authEpoch)
7980
? { authEpoch: normalizeOptionalString(binding.authEpoch) }
8081
: {}),
82+
...(typeof binding.authEpochVersion === "number" && Number.isFinite(binding.authEpochVersion)
83+
? { authEpochVersion: binding.authEpochVersion }
84+
: {}),
8185
...(normalizeOptionalString(binding.extraSystemPromptHash)
8286
? { extraSystemPromptHash: normalizeOptionalString(binding.extraSystemPromptHash) }
8387
: {}),
@@ -122,6 +126,7 @@ export function resolveCliSessionReuse(params: {
122126
binding?: CliSessionBinding;
123127
authProfileId?: string;
124128
authEpoch?: string;
129+
authEpochVersion?: number;
125130
extraSystemPromptHash?: string;
126131
mcpConfigHash?: string;
127132
mcpResumeHash?: string;
@@ -144,7 +149,10 @@ export function resolveCliSessionReuse(params: {
144149
return { invalidatedReason: "auth-profile" };
145150
}
146151
const storedAuthEpoch = normalizeOptionalString(binding?.authEpoch);
147-
if (storedAuthEpoch !== currentAuthEpoch) {
152+
if (
153+
binding?.authEpochVersion === params.authEpochVersion &&
154+
storedAuthEpoch !== currentAuthEpoch
155+
) {
148156
return { invalidatedReason: "auth-epoch" };
149157
}
150158
const storedExtraSystemPromptHash = normalizeOptionalString(binding?.extraSystemPromptHash);

src/config/sessions/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export type CliSessionBinding = {
7272
sessionId: string;
7373
authProfileId?: string;
7474
authEpoch?: string;
75+
authEpochVersion?: number;
7576
extraSystemPromptHash?: string;
7677
mcpConfigHash?: string;
7778
mcpResumeHash?: string;

0 commit comments

Comments
 (0)