mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-27 17:55:58 +00:00
fix: preserve source agent for sqlite session rows
This commit is contained in:
@@ -16,6 +16,7 @@ function mergeSessionEntryIntoCombined(params: {
|
||||
cfg: OpenClawConfig;
|
||||
combined: Record<string, SessionEntry>;
|
||||
sourceDatabasePathBySessionKey: Record<string, string>;
|
||||
sourceAgentIdBySessionKey: Record<string, string>;
|
||||
sourceDatabasePath: string;
|
||||
entry: SessionEntry;
|
||||
agentId: string;
|
||||
@@ -53,6 +54,7 @@ function mergeSessionEntryIntoCombined(params: {
|
||||
};
|
||||
}
|
||||
params.sourceDatabasePathBySessionKey[canonicalKey] = params.sourceDatabasePath;
|
||||
params.sourceAgentIdBySessionKey[canonicalKey] = agentId;
|
||||
}
|
||||
|
||||
export function loadCombinedSessionEntriesForGateway(
|
||||
@@ -62,6 +64,7 @@ export function loadCombinedSessionEntriesForGateway(
|
||||
databasePath: string;
|
||||
entries: Record<string, SessionEntry>;
|
||||
sourceDatabasePathBySessionKey?: Record<string, string>;
|
||||
sourceAgentIdBySessionKey?: Record<string, string>;
|
||||
} {
|
||||
const requestedAgentId =
|
||||
typeof opts.agentId === "string" && opts.agentId.trim()
|
||||
@@ -76,6 +79,7 @@ export function loadCombinedSessionEntriesForGateway(
|
||||
: resolveAllAgentSessionDatabaseTargetsSync(cfg);
|
||||
const combined: Record<string, SessionEntry> = {};
|
||||
const sourceDatabasePathBySessionKey: Record<string, string> = {};
|
||||
const sourceAgentIdBySessionKey: Record<string, string> = {};
|
||||
for (const target of targets) {
|
||||
const agentId = target.agentId;
|
||||
for (const { sessionKey: key, entry } of listSessionEntries({
|
||||
@@ -91,6 +95,7 @@ export function loadCombinedSessionEntriesForGateway(
|
||||
cfg,
|
||||
combined,
|
||||
sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey,
|
||||
sourceDatabasePath: target.databasePath,
|
||||
entry,
|
||||
agentId,
|
||||
@@ -105,5 +110,10 @@ export function loadCombinedSessionEntriesForGateway(
|
||||
: targets.length === 1
|
||||
? targets[0].databasePath
|
||||
: "(multiple)";
|
||||
return { databasePath, entries: combined, sourceDatabasePathBySessionKey };
|
||||
return {
|
||||
databasePath,
|
||||
entries: combined,
|
||||
sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -901,6 +901,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
||||
databasePath,
|
||||
entries: store,
|
||||
sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey,
|
||||
} = measureDiagnosticsTimelineSpanSync(
|
||||
"gateway.sessions.list.store_load",
|
||||
() => {
|
||||
@@ -938,6 +939,7 @@ export const sessionsHandlers: GatewayRequestHandlers = {
|
||||
cfg,
|
||||
databasePath,
|
||||
sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey,
|
||||
store,
|
||||
modelCatalog,
|
||||
opts: p,
|
||||
|
||||
@@ -8,10 +8,14 @@ import type { OpenClawConfig } from "../config/config.js";
|
||||
import { getSessionEntry, upsertSessionEntry, type SessionEntry } from "../config/sessions.js";
|
||||
import { replaceSqliteSessionTranscriptEvents } from "../config/sessions/transcript-store.sqlite.js";
|
||||
import { registerAgentRunContext, resetAgentRunContextForTest } from "../infra/agent-events.js";
|
||||
import { openOpenClawAgentDatabase } from "../state/openclaw-agent-db.js";
|
||||
import {
|
||||
listOpenClawRegisteredAgentDatabases,
|
||||
openOpenClawAgentDatabase,
|
||||
} from "../state/openclaw-agent-db.js";
|
||||
import { withStateDirEnv } from "../test-helpers/state-dir-env.js";
|
||||
import {
|
||||
listSessionsFromStore,
|
||||
listSessionsFromStoreAsync,
|
||||
loadCombinedSessionEntriesForGateway,
|
||||
resolveGatewayModelSupportsImages,
|
||||
} from "./session-utils.js";
|
||||
@@ -1219,6 +1223,7 @@ describe("loadCombinedSessionEntriesForGateway includes SQLite-registered agents
|
||||
cfg,
|
||||
databasePath: combined.databasePath,
|
||||
sourceDatabasePathBySessionKey: combined.sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey: combined.sourceAgentIdBySessionKey,
|
||||
store: combined.entries,
|
||||
opts: { agentId: "work", includeDerivedTitles: true, includeLastMessage: true },
|
||||
});
|
||||
@@ -1233,6 +1238,67 @@ describe("loadCombinedSessionEntriesForGateway includes SQLite-registered agents
|
||||
});
|
||||
});
|
||||
|
||||
test("global rows from registered agent databases preserve their source agent", async () => {
|
||||
await withStateDirEnv("openclaw-acp-registered-global-owner-", async ({ tempRoot }) => {
|
||||
const databasePath = path.join(tempRoot, "relocated", "work.sqlite");
|
||||
openOpenClawAgentDatabase({ agentId: "work", path: databasePath });
|
||||
upsertSessionEntry({
|
||||
agentId: "work",
|
||||
path: databasePath,
|
||||
sessionKey: "global",
|
||||
entry: {
|
||||
sessionId: "s-work-global",
|
||||
updatedAt: 400,
|
||||
modelProvider: "openai",
|
||||
model: "gpt-5.4",
|
||||
},
|
||||
});
|
||||
replaceSqliteSessionTranscriptEvents({
|
||||
agentId: "work",
|
||||
path: databasePath,
|
||||
sessionId: "s-work-global",
|
||||
events: [
|
||||
{ type: "session", version: 1, id: "s-work-global" },
|
||||
{ message: { role: "user", content: "work global title" } },
|
||||
{ message: { role: "assistant", content: "work global last" } },
|
||||
],
|
||||
});
|
||||
const cfg = {
|
||||
session: {
|
||||
mainKey: "main",
|
||||
},
|
||||
agents: {
|
||||
list: [{ id: "main", default: true }],
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
|
||||
const combined = loadCombinedSessionEntriesForGateway(cfg, { agentId: "work" });
|
||||
expect(combined.entries.global?.sessionId).toBe("s-work-global");
|
||||
expect(combined.sourceAgentIdBySessionKey?.global).toBe("work");
|
||||
const result = await listSessionsFromStoreAsync({
|
||||
cfg,
|
||||
databasePath: combined.databasePath,
|
||||
sourceDatabasePathBySessionKey: combined.sourceDatabasePathBySessionKey,
|
||||
sourceAgentIdBySessionKey: combined.sourceAgentIdBySessionKey,
|
||||
store: combined.entries,
|
||||
opts: { includeGlobal: true, includeDerivedTitles: true, includeLastMessage: true },
|
||||
});
|
||||
|
||||
expect(result.sessions[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
key: "global",
|
||||
derivedTitle: "work global title",
|
||||
lastMessagePreview: "work global last",
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
listOpenClawRegisteredAgentDatabases()
|
||||
.filter((row) => row.path === databasePath)
|
||||
.map((row) => row.agentId),
|
||||
).toEqual(["work"]);
|
||||
});
|
||||
});
|
||||
|
||||
test("configured-only loads preserve registered database paths", async () => {
|
||||
await withStateDirEnv("openclaw-acp-configured-registered-path-", async ({ tempRoot }) => {
|
||||
const databasePath = path.join(tempRoot, "relocated", "work.sqlite");
|
||||
|
||||
@@ -670,6 +670,7 @@ function resolveChildSessionKeys(
|
||||
|
||||
function resolveTranscriptUsageFallback(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentId?: string;
|
||||
databasePath?: string;
|
||||
key: string;
|
||||
entry?: SessionEntry;
|
||||
@@ -690,9 +691,9 @@ function resolveTranscriptUsageFallback(params: {
|
||||
return null;
|
||||
}
|
||||
const parsed = parseAgentSessionKey(params.key);
|
||||
const agentId = parsed?.agentId
|
||||
? normalizeAgentId(parsed.agentId)
|
||||
: resolveDefaultAgentId(params.cfg);
|
||||
const agentId = normalizeAgentId(
|
||||
params.agentId ?? parsed?.agentId ?? resolveDefaultAgentId(params.cfg),
|
||||
);
|
||||
const snapshot = readRecentSessionUsageFromTranscript(
|
||||
{
|
||||
agentId,
|
||||
@@ -1305,6 +1306,7 @@ export function resolveSessionDisplayModelIdentityRef(params: {
|
||||
|
||||
export function buildGatewaySessionRow(params: {
|
||||
cfg: OpenClawConfig;
|
||||
agentId?: string;
|
||||
databasePath?: string;
|
||||
store: Record<string, SessionEntry>;
|
||||
key: string;
|
||||
@@ -1347,7 +1349,9 @@ export function buildGatewaySessionRow(params: {
|
||||
deliveryContext: entry?.deliveryContext,
|
||||
});
|
||||
const parsedAgent = parseAgentSessionKey(key);
|
||||
const sessionAgentId = normalizeAgentId(parsedAgent?.agentId ?? resolveDefaultAgentId(cfg));
|
||||
const sessionAgentId = normalizeAgentId(
|
||||
params.agentId ?? parsedAgent?.agentId ?? resolveDefaultAgentId(cfg),
|
||||
);
|
||||
const rowContext = params.rowContext;
|
||||
const subagentRun = rowContext
|
||||
? rowContext.subagentRuns.getDisplaySubagentRun(key)
|
||||
@@ -1433,6 +1437,7 @@ export function buildGatewaySessionRow(params: {
|
||||
(needsTranscriptTotalTokens || needsTranscriptContextTokens || needsTranscriptEstimatedCostUsd)
|
||||
? resolveTranscriptUsageFallback({
|
||||
cfg,
|
||||
agentId: sessionAgentId,
|
||||
databasePath: params.databasePath,
|
||||
key,
|
||||
entry,
|
||||
@@ -1957,10 +1962,24 @@ function resolveSessionRowSourceDatabasePath(params: {
|
||||
return databasePath && databasePath !== "(multiple)" ? databasePath : undefined;
|
||||
}
|
||||
|
||||
function resolveSessionRowSourceAgentId(params: {
|
||||
cfg: OpenClawConfig;
|
||||
sourceAgentIdBySessionKey?: Record<string, string>;
|
||||
key: string;
|
||||
}): string {
|
||||
const parsed = parseAgentSessionKey(params.key);
|
||||
return normalizeAgentId(
|
||||
params.sourceAgentIdBySessionKey?.[params.key] ??
|
||||
parsed?.agentId ??
|
||||
resolveDefaultAgentId(params.cfg),
|
||||
);
|
||||
}
|
||||
|
||||
export function listSessionsFromStore(params: {
|
||||
cfg: OpenClawConfig;
|
||||
databasePath?: string;
|
||||
sourceDatabasePathBySessionKey?: Record<string, string>;
|
||||
sourceAgentIdBySessionKey?: Record<string, string>;
|
||||
store: Record<string, SessionEntry>;
|
||||
modelCatalog?: ModelCatalogEntry[];
|
||||
opts: import("./protocol/index.js").SessionsListParams;
|
||||
@@ -1998,8 +2017,14 @@ export function listSessionsFromStore(params: {
|
||||
sourceDatabasePathBySessionKey: params.sourceDatabasePathBySessionKey,
|
||||
key,
|
||||
});
|
||||
const rowAgentId = resolveSessionRowSourceAgentId({
|
||||
cfg,
|
||||
sourceAgentIdBySessionKey: params.sourceAgentIdBySessionKey,
|
||||
key,
|
||||
});
|
||||
return buildGatewaySessionRow({
|
||||
cfg,
|
||||
agentId: rowAgentId,
|
||||
databasePath: rowDatabasePath,
|
||||
store,
|
||||
key,
|
||||
@@ -2044,6 +2069,7 @@ export async function listSessionsFromStoreAsync(params: {
|
||||
cfg: OpenClawConfig;
|
||||
databasePath?: string;
|
||||
sourceDatabasePathBySessionKey?: Record<string, string>;
|
||||
sourceAgentIdBySessionKey?: Record<string, string>;
|
||||
store: Record<string, SessionEntry>;
|
||||
modelCatalog?: ModelCatalogEntry[];
|
||||
opts: import("./protocol/index.js").SessionsListParams;
|
||||
@@ -2083,8 +2109,14 @@ export async function listSessionsFromStoreAsync(params: {
|
||||
sourceDatabasePathBySessionKey: params.sourceDatabasePathBySessionKey,
|
||||
key,
|
||||
});
|
||||
const rowAgentId = resolveSessionRowSourceAgentId({
|
||||
cfg,
|
||||
sourceAgentIdBySessionKey: params.sourceAgentIdBySessionKey,
|
||||
key,
|
||||
});
|
||||
const row = buildGatewaySessionRow({
|
||||
cfg,
|
||||
agentId: rowAgentId,
|
||||
databasePath: rowDatabasePath,
|
||||
store,
|
||||
key,
|
||||
@@ -2104,12 +2136,8 @@ export async function listSessionsFromStoreAsync(params: {
|
||||
includeTranscriptFields &&
|
||||
(includeDerivedTitles || includeLastMessage)
|
||||
) {
|
||||
const parsed = parseAgentSessionKey(key);
|
||||
const sessionAgentId = parsed?.agentId
|
||||
? normalizeAgentId(parsed.agentId)
|
||||
: resolveDefaultAgentId(cfg);
|
||||
const fields = await readSessionTitleFieldsFromTranscriptAsync({
|
||||
agentId: sessionAgentId,
|
||||
agentId: rowAgentId,
|
||||
...(rowDatabasePath ? { path: rowDatabasePath } : {}),
|
||||
sessionId: entry.sessionId,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user