fix(gateway): stabilize session creator facets (#116624)

This commit is contained in:
Peter Steinberger
2026-07-30 19:36:33 -07:00
committed by GitHub
parent 9ccc8fcc27
commit 2f860a7736
2 changed files with 96 additions and 8 deletions
+81 -1
View File
@@ -15,10 +15,90 @@ const getUserProfileListItem = vi.hoisted(() =>
);
vi.mock("../state/user-profiles.js", () => ({ getUserProfileListItem }));
vi.mock("./session-utils-row.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./session-utils-row.js")>();
return {
...actual,
projectSessionActor: (
actor: Parameters<typeof actual.projectSessionActor>[0],
identities: Parameters<typeof actual.projectSessionActor>[1],
) => {
if (actor?.id === "shared-id") {
return actor.type === "human"
? { type: actor.type, id: actor.id, label: "Alpha" }
: { type: actor.type, id: actor.id, label: "Zulu", avatarUrl: "/avatar" };
}
if (actor?.id === "unicode-id") {
return {
type: actor.type,
id: actor.id,
label: actor.type === "human" ? "é" : "e\u0301",
};
}
return actual.projectSessionActor(actor, identities);
},
};
});
import { listSessionsFromStore, listSessionsFromStoreAsync } from "./session-utils.js";
afterEach(() => vi.restoreAllMocks());
afterEach(() => {
vi.restoreAllMocks();
getUserProfileListItem.mockClear();
});
it("keeps creator labels and avatars stable across actor order", () => {
const actorOrders = [
["human", "agent"],
["agent", "human"],
] as const;
for (const actorOrder of actorOrders) {
const store = Object.fromEntries(
actorOrder.map((type, index) => [
`agent:main:${index}`,
{
createdActor: { type, id: "shared-id" },
sessionId: `session-${index}`,
updatedAt: 2 - index,
} satisfies SessionEntry,
]),
);
const result = listSessionsFromStore({
cfg: {} as OpenClawConfig,
storePath: "/tmp/openclaw-session-creator-order",
store,
opts: { archived: "all" },
});
expect(result.creators).toEqual([{ id: "shared-id", label: "Alpha", avatarUrl: "/avatar" }]);
}
});
it("breaks locale-equivalent creator label ties deterministically", () => {
for (const actorOrder of [
["human", "agent"],
["agent", "human"],
] as const) {
const store = Object.fromEntries(
actorOrder.map((type, index) => [
`agent:main:unicode-${index}`,
{
createdActor: { type, id: "unicode-id" },
sessionId: `unicode-session-${index}`,
updatedAt: 2 - index,
} satisfies SessionEntry,
]),
);
const result = listSessionsFromStore({
cfg: {} as OpenClawConfig,
storePath: "/tmp/openclaw-session-creator-unicode-order",
store,
opts: { archived: "all" },
});
expect(result.creators).toEqual([{ id: "unicode-id", label: "e\u0301" }]);
}
});
it("returns the complete deterministic creator facet independently of pagination", () => {
const store: Record<string, SessionEntry> = {
+15 -7
View File
@@ -82,6 +82,16 @@ type SessionEntrySelection = {
hasMore: boolean;
};
function preferredCreatorIdentityValue(
current: string | undefined,
candidate: string | undefined,
): string | undefined {
if (!current || !candidate) {
return current ?? candidate;
}
return candidate < current ? candidate : current;
}
function addSessionCreatorIdentity(
creators: Map<string, { id: string; label?: string; avatarUrl?: string }>,
entry: SessionEntry,
@@ -95,15 +105,13 @@ function addSessionCreatorIdentity(
const label = normalizeOptionalString(actor?.label);
const avatarUrl = normalizeOptionalString(actor?.avatarUrl);
const existing = creators.get(id);
if (
!existing ||
(label && (!existing.label || label.localeCompare(existing.label) < 0)) ||
(avatarUrl && !existing.avatarUrl)
) {
const preferredLabel = preferredCreatorIdentityValue(existing?.label, label);
const preferredAvatarUrl = preferredCreatorIdentityValue(existing?.avatarUrl, avatarUrl);
if (!existing || preferredLabel !== existing.label || preferredAvatarUrl !== existing.avatarUrl) {
creators.set(id, {
id,
...(label ? { label } : existing?.label ? { label: existing.label } : {}),
...(avatarUrl ? { avatarUrl } : existing?.avatarUrl ? { avatarUrl: existing.avatarUrl } : {}),
...(preferredLabel ? { label: preferredLabel } : {}),
...(preferredAvatarUrl ? { avatarUrl: preferredAvatarUrl } : {}),
});
}
}