From 58d8ae96bc0f5dce64cd5d9a8c145b04ecfe41bc Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 18 May 2026 02:46:32 +0100 Subject: [PATCH] fix: preserve sqlite refactor runtime fallbacks --- src/agents/auth-profiles/order.test.ts | 37 ++++++++++++++++ src/agents/auth-profiles/order.ts | 24 +++++------ src/agents/auth-profiles/persisted.ts | 6 ++- src/agents/auth-profiles/source-check.test.ts | 20 +++++++++ src/config/sessions/delivery-info.test.ts | 43 ++++++++++++++++++- src/config/sessions/delivery-info.ts | 9 +++- 6 files changed, 122 insertions(+), 17 deletions(-) diff --git a/src/agents/auth-profiles/order.test.ts b/src/agents/auth-profiles/order.test.ts index 04f8f7269bd..1d552e448b7 100644 --- a/src/agents/auth-profiles/order.test.ts +++ b/src/agents/auth-profiles/order.test.ts @@ -411,6 +411,43 @@ describe("resolveAuthProfileOrder", () => { expect(order).toEqual(["openai-codex:legacy"]); }); + it("keeps configured Codex auth order ahead of stored OpenAI alias order", async () => { + const store: AuthProfileStore = { + version: 1, + profiles: { + "openai:platform": { + type: "api_key", + provider: "openai", + key: "sk-platform", + }, + "openai-codex:work": { + type: "oauth", + provider: "openai-codex", + access: "work-access", + refresh: "work-refresh", + expires: Date.now() + 60_000, + }, + }, + order: { + openai: ["openai:platform"], + }, + }; + + const order = resolveAuthProfileOrder({ + cfg: { + auth: { + order: { + "openai-codex": ["openai-codex:work"], + }, + }, + }, + store, + provider: "openai-codex", + }); + + expect(order).toEqual(["openai-codex:work"]); + }); + it("marks profile success with one canonical last-good and usage update", async () => { const agentDir = await mkdtemp(path.join(os.tmpdir(), "openclaw-auth-profile-success-")); try { diff --git a/src/agents/auth-profiles/order.ts b/src/agents/auth-profiles/order.ts index 43a6f142f74..543efb635b6 100644 --- a/src/agents/auth-profiles/order.ts +++ b/src/agents/auth-profiles/order.ts @@ -233,19 +233,19 @@ export function resolveAuthProfileOrder(params: { providerAuthKey === OPENAI_CODEX_PROVIDER_ID || providerKey === OPENAI_CODEX_PROVIDER_ID ? OPENAI_PROVIDER_ID : undefined; - const storedOrder = - resolveAuthOrder(store.order, providerAuthKey) ?? - resolveAuthOrder(store.order, providerKey) ?? - (openAIOrderAliasProvider - ? resolveAuthOrder(store.order, openAIOrderAliasProvider) - : undefined); - const configuredOrder = + const directStoredOrder = + resolveAuthOrder(store.order, providerAuthKey) ?? resolveAuthOrder(store.order, providerKey); + const directConfiguredOrder = resolveAuthOrder(cfg?.auth?.order, providerAuthKey) ?? - resolveAuthOrder(cfg?.auth?.order, providerKey) ?? - (openAIOrderAliasProvider - ? resolveAuthOrder(cfg?.auth?.order, openAIOrderAliasProvider) - : undefined); - const explicitOrder = storedOrder ?? configuredOrder; + resolveAuthOrder(cfg?.auth?.order, providerKey); + const aliasStoredOrder = openAIOrderAliasProvider + ? resolveAuthOrder(store.order, openAIOrderAliasProvider) + : undefined; + const aliasConfiguredOrder = openAIOrderAliasProvider + ? resolveAuthOrder(cfg?.auth?.order, openAIOrderAliasProvider) + : undefined; + const explicitOrder = + directStoredOrder ?? directConfiguredOrder ?? aliasStoredOrder ?? aliasConfiguredOrder; const explicitProfiles = cfg?.auth?.profiles ? Object.entries(cfg.auth.profiles) .filter(([profileId, profile]) => diff --git a/src/agents/auth-profiles/persisted.ts b/src/agents/auth-profiles/persisted.ts index 52964a15e6f..0fb1a35a1c2 100644 --- a/src/agents/auth-profiles/persisted.ts +++ b/src/agents/auth-profiles/persisted.ts @@ -1046,6 +1046,8 @@ export function hasPersistedAuthProfileSecretsStore( agentDir?: string, options: OpenClawStateDatabaseOptions = {}, ): boolean { - return readAuthProfileStorePayloadResult(authProfileStoreKey(agentDir, options.env), options) - .exists; + return readAuthProfileStorePayloadResultReadOnly( + authProfileStoreKey(agentDir, options.env), + options, + ).exists; } diff --git a/src/agents/auth-profiles/source-check.test.ts b/src/agents/auth-profiles/source-check.test.ts index 5b3e92072c8..8e222b5868c 100644 --- a/src/agents/auth-profiles/source-check.test.ts +++ b/src/agents/auth-profiles/source-check.test.ts @@ -69,4 +69,24 @@ describe("hasAnyAuthProfileStoreSource", () => { }); }); }); + + it("does not create SQLite state while probing an empty auth source", () => { + withTempAgentDir("openclaw-auth-source-empty-", (agentDir) => { + const previousStateDir = process.env.OPENCLAW_STATE_DIR; + try { + const stateDir = path.join(agentDir, "state"); + process.env.OPENCLAW_STATE_DIR = stateDir; + const sqlitePath = path.join(stateDir, "state", "openclaw.sqlite"); + + expect(hasAnyAuthProfileStoreSource(agentDir)).toBe(false); + expect(fs.existsSync(sqlitePath)).toBe(false); + } finally { + if (previousStateDir === undefined) { + delete process.env.OPENCLAW_STATE_DIR; + } else { + process.env.OPENCLAW_STATE_DIR = previousStateDir; + } + } + }); + }); }); diff --git a/src/config/sessions/delivery-info.test.ts b/src/config/sessions/delivery-info.test.ts index 94579dc1e8d..16da1a47e24 100644 --- a/src/config/sessions/delivery-info.test.ts +++ b/src/config/sessions/delivery-info.test.ts @@ -1,5 +1,5 @@ import { randomUUID } from "node:crypto"; -import { afterEach, describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it, vi } from "vitest"; import { executeSqliteQuerySync, getNodeSqliteKysely } from "../../infra/kysely-sync.js"; import type { DB as OpenClawAgentKyselyDatabase } from "../../state/openclaw-agent-db.generated.js"; import { @@ -15,6 +15,11 @@ import type { SessionEntry } from "./types.js"; type DeliveryInfoTestDatabase = Pick; const ORIGINAL_STATE_DIR = process.env.OPENCLAW_STATE_DIR; +const runtimeConfigMock = vi.hoisted(() => vi.fn(() => ({}) as OpenClawConfig)); + +vi.mock("../io.js", () => ({ + getRuntimeConfig: runtimeConfigMock, +})); function setStateDir(): NodeJS.ProcessEnv { const stateDir = `${process.env.TMPDIR ?? "/tmp"}/openclaw-delivery-info-${randomUUID()}`; @@ -57,6 +62,8 @@ function corruptStoredEntryJson(params: { afterEach(() => { closeOpenClawAgentDatabasesForTest(); closeOpenClawStateDatabaseForTest(); + runtimeConfigMock.mockReset(); + runtimeConfigMock.mockReturnValue({} as OpenClawConfig); if (ORIGINAL_STATE_DIR === undefined) { delete process.env.OPENCLAW_STATE_DIR; } else { @@ -171,6 +178,40 @@ describe("extractDeliveryInfo", () => { }); }); + it("uses runtime config to search registered agent databases when cfg is omitted", () => { + const env = setStateDir(); + const cfg = { + agents: { + list: [{ id: "main", default: true }, { id: "ops" }], + }, + } as OpenClawConfig; + runtimeConfigMock.mockReturnValue(cfg); + const sessionKey = "agent:ops:matrix:channel:!ops:example.org"; + const registeredPath = `${env.OPENCLAW_STATE_DIR}/registered/ops.sqlite`; + upsertSessionEntry({ + agentId: "ops", + env, + path: registeredPath, + sessionKey, + entry: buildEntry({ + channel: "matrix", + to: "!ops:example.org", + accountId: "work", + threadId: "$thread", + }), + }); + + expect(extractDeliveryInfo(sessionKey)).toEqual({ + deliveryContext: { + channel: "matrix", + to: "!ops:example.org", + accountId: "work", + threadId: "$thread", + }, + threadId: "$thread", + }); + }); + it("returns empty delivery info when the session row is missing", () => { setStateDir(); diff --git a/src/config/sessions/delivery-info.ts b/src/config/sessions/delivery-info.ts index 3abf42849ee..555db3bc8bf 100644 --- a/src/config/sessions/delivery-info.ts +++ b/src/config/sessions/delivery-info.ts @@ -5,6 +5,7 @@ import { } from "../../routing/session-key.js"; import { deliveryContextFromSession } from "../../utils/delivery-context.shared.js"; import type { DeliveryContext } from "../../utils/delivery-context.types.js"; +import { getRuntimeConfig } from "../io.js"; import type { OpenClawConfig } from "../types.openclaw.js"; import { normalizeSessionRowKey } from "./store-entry.js"; import { getSessionEntry } from "./store.js"; @@ -99,9 +100,13 @@ export function extractDeliveryInfo( const { baseSessionKey, threadId } = parseSessionThreadInfo(sessionKey); const lookupKey = baseSessionKey ?? sessionKey; try { + const lookupOptions = { + ...options, + cfg: options.cfg ?? getRuntimeConfig(), + }; const entry = - readDeliverySessionEntry(lookupKey, options) ?? - (lookupKey === sessionKey ? undefined : readDeliverySessionEntry(sessionKey, options)); + readDeliverySessionEntry(lookupKey, lookupOptions) ?? + (lookupKey === sessionKey ? undefined : readDeliverySessionEntry(sessionKey, lookupOptions)); const deliveryContext = toExtractedDeliveryContext(entry); return { deliveryContext,