fix(telegram): preserve codex login profile identity

This commit is contained in:
Eva
2026-07-12 04:05:44 +07:00
committed by Vincent Koc
parent ecd7d6c787
commit 030fa62b79
3 changed files with 73 additions and 9 deletions
@@ -55,6 +55,7 @@ const persistentBindingMocks = vi.hoisted(() => ({
const sessionMocks = vi.hoisted(() => ({
getSessionEntry: vi.fn(),
loadSessionStore: vi.fn(),
patchSessionEntry: vi.fn(),
recordSessionMetaFromInbound: vi.fn(),
resolveStorePath: vi.fn(),
}));
@@ -175,6 +176,7 @@ vi.mock("openclaw/plugin-sdk/session-store-runtime", async () => {
...actual,
getSessionEntry: sessionMocks.getSessionEntry,
loadSessionStore: sessionMocks.loadSessionStore,
patchSessionEntry: sessionMocks.patchSessionEntry,
resolveStorePath: sessionMocks.resolveStorePath,
};
});
@@ -640,6 +642,7 @@ function resetSessionMetaMocks() {
({ storePath, sessionKey }: { storePath: string; sessionKey: string }) =>
sessionMocks.loadSessionStore(storePath)[sessionKey],
);
sessionMocks.patchSessionEntry.mockClear().mockResolvedValue(null);
sessionMocks.recordSessionMetaFromInbound.mockClear().mockResolvedValue(undefined);
sessionMocks.resolveStorePath.mockClear().mockReturnValue("/tmp/openclaw-sessions.json");
pluginRuntimeMocks.executePluginCommand.mockClear().mockResolvedValue({ text: "ok" });
@@ -1617,7 +1620,7 @@ describe("registerTelegramNativeCommands — session metadata", () => {
);
});
it("passes the target session auth profile to Telegram /login codex", async () => {
it("moves the target session to the profile returned by Telegram /login codex", async () => {
sessionMocks.loadSessionStore.mockReturnValue({
"agent:main:main": {
authProfileOverride: "openai:owner@example.com",
@@ -1633,7 +1636,9 @@ describe("registerTelegramNativeCommands — session metadata", () => {
return {
providerId: "openai",
methodId: "device-code",
profiles: [{ profileId: "openai:owner@example.com", provider: "openai", mode: "oauth" }],
profiles: [
{ profileId: "openai:new-owner@example.com", provider: "openai", mode: "oauth" },
],
};
});
@@ -1653,9 +1658,33 @@ describe("registerTelegramNativeCommands — session metadata", () => {
provider: "openai",
method: "device-code",
agent: "main",
profileId: "openai:owner@example.com",
}),
);
expect(
(runModelsAuthLoginFlow.mock.calls[0]?.[0] as { profileId?: string } | undefined)?.profileId,
).toBeUndefined();
expect(sessionMocks.patchSessionEntry).toHaveBeenCalledWith({
agentId: "main",
sessionKey: "agent:main:main",
storePath: "/tmp/openclaw-sessions.json",
fallbackEntry: {
authProfileOverride: "openai:owner@example.com",
sessionId: "sess-main",
updatedAt: 1,
},
preserveActivity: true,
update: expect.any(Function),
});
const patchUpdate = (
sessionMocks.patchSessionEntry.mock.calls[0]?.[0] as {
update?: () => Record<string, unknown>;
}
)?.update?.();
expect(patchUpdate).toEqual({
authProfileOverride: "openai:new-owner@example.com",
authProfileOverrideSource: "user",
authProfileOverrideCompactionCount: undefined,
});
});
it("passes session identity to plugin commands when the entry has no file", async () => {
+33 -3
View File
@@ -43,6 +43,7 @@ import type { RuntimeEnv } from "openclaw/plugin-sdk/runtime-env";
import {
formatSqliteSessionFileMarker,
getSessionEntry,
patchSessionEntry,
resolveStorePath,
type SessionEntry,
} from "openclaw/plugin-sdk/session-store-runtime";
@@ -1338,21 +1339,50 @@ export const registerTelegramNativeCommands = ({
agentId: route.agentId,
sessionKey: targetSessionKey,
});
const profileId = codexChannelLoginRuntime.resolveProviderScopedProfileId(
const previousProfileId = codexChannelLoginRuntime.resolveProviderScopedProfileId(
targetSessionEntry?.authProfileOverride,
loginProvider,
);
await codexChannelLoginRuntime.runDeviceLoginFlow({
const loginResult = await codexChannelLoginRuntime.runDeviceLoginFlow({
runLoginFlow: loginFlow,
provider: loginProvider,
agentId: route.agentId,
...(profileId ? { profileId } : {}),
config: runtimeCfg,
runtime,
sendMessage: sendLoginMessage,
unsupportedPromptMessage:
"Telegram /login supports only fixed Codex device-code auth.",
});
const nextProfileId = loginResult.profiles.find(
(profile) => profile.provider === loginProvider,
)?.profileId;
if (targetSessionEntry && nextProfileId && nextProfileId !== previousProfileId) {
try {
const storePath = resolveStorePath(runtimeCfg.session?.store, {
agentId: route.agentId,
});
await patchSessionEntry({
agentId: route.agentId,
sessionKey: targetSessionKey,
storePath,
fallbackEntry: targetSessionEntry,
preserveActivity: true,
update: () => ({
authProfileOverride: nextProfileId,
authProfileOverrideSource: "user",
authProfileOverrideCompactionCount: undefined,
}),
});
} catch (error) {
runtime.error?.(
danger(
`telegram /login codex completed but failed to update session auth profile: ${String(
error,
)}`,
),
);
}
}
await sendLoginMessage("Codex login complete. Try your request again now.");
} catch {
runtime.error?.(danger("telegram /login codex failed"));
@@ -10,10 +10,15 @@ export type {
ModelsAuthLoginFlowOptions,
ModelsAuthLoginFlowResult,
} from "../commands/models/auth.js";
import type { ModelsAuthLoginFlowOptions } from "../commands/models/auth.js";
import type {
ModelsAuthLoginFlowOptions,
ModelsAuthLoginFlowResult,
} from "../commands/models/auth.js";
type ProviderAuthLoginFlowRuntime = typeof import("../commands/models/auth.js");
type RunModelsAuthLoginFlow = (opts: ModelsAuthLoginFlowOptions) => Promise<unknown>;
type RunModelsAuthLoginFlow = (
opts: ModelsAuthLoginFlowOptions,
) => Promise<ModelsAuthLoginFlowResult>;
const CODEX_LOGIN_PROVIDER = "openai";
const CODEX_LOGIN_METHOD = "device-code";
@@ -134,7 +139,7 @@ async function runCodexDeviceLoginFlow(params: {
sendMessage: (message: string) => Promise<void>;
unsupportedPromptMessage: string;
runLoginFlow?: RunModelsAuthLoginFlow;
}): Promise<unknown> {
}): Promise<ModelsAuthLoginFlowResult> {
return await (params.runLoginFlow ?? runModelsAuthLoginFlow)({
provider: params.provider,
method: CODEX_LOGIN_METHOD,