feat: add heuristic session title derivation for session picker
Enable meaningful session titles via priority-based derivation: 1. displayName (user-set) 2. subject (group name) 3. First user message (truncated to 60 chars) 4. sessionId prefix + date fallback Opt-in via includeDerivedTitles param to avoid perf impact on regular listing. Reads only first 10 lines of transcript files. Closes #1161
This commit is contained in:
committed by
Peter Steinberger
parent
4fda10c508
commit
83d5e30027
@@ -6,6 +6,7 @@ import type { SessionEntry } from "../config/sessions.js";
|
||||
import {
|
||||
capArrayByJsonBytes,
|
||||
classifySessionKey,
|
||||
deriveSessionTitle,
|
||||
parseGroupKey,
|
||||
resolveGatewaySessionStoreTarget,
|
||||
resolveSessionStoreKey,
|
||||
@@ -91,3 +92,98 @@ describe("gateway session utils", () => {
|
||||
expect(target.storePath).toBe(path.resolve(storeTemplate.replace("{agentId}", "ops")));
|
||||
});
|
||||
});
|
||||
|
||||
describe("deriveSessionTitle", () => {
|
||||
test("returns undefined for undefined entry", () => {
|
||||
expect(deriveSessionTitle(undefined)).toBeUndefined();
|
||||
});
|
||||
|
||||
test("prefers displayName when set", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
displayName: "My Custom Session",
|
||||
subject: "Group Chat",
|
||||
} as SessionEntry;
|
||||
expect(deriveSessionTitle(entry)).toBe("My Custom Session");
|
||||
});
|
||||
|
||||
test("falls back to subject when displayName is missing", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
subject: "Dev Team Chat",
|
||||
} as SessionEntry;
|
||||
expect(deriveSessionTitle(entry)).toBe("Dev Team Chat");
|
||||
});
|
||||
|
||||
test("uses first user message when displayName and subject missing", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
} as SessionEntry;
|
||||
expect(deriveSessionTitle(entry, "Hello, how are you?")).toBe("Hello, how are you?");
|
||||
});
|
||||
|
||||
test("truncates long first user message to 60 chars with ellipsis", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
} as SessionEntry;
|
||||
const longMsg =
|
||||
"This is a very long message that exceeds sixty characters and should be truncated appropriately";
|
||||
const result = deriveSessionTitle(entry, longMsg);
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.length).toBeLessThanOrEqual(60);
|
||||
expect(result!.endsWith("…")).toBe(true);
|
||||
});
|
||||
|
||||
test("truncates at word boundary when possible", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
} as SessionEntry;
|
||||
const longMsg = "This message has many words and should be truncated at a word boundary nicely";
|
||||
const result = deriveSessionTitle(entry, longMsg);
|
||||
expect(result).toBeDefined();
|
||||
expect(result!.endsWith("…")).toBe(true);
|
||||
expect(result!.includes(" ")).toBe(false);
|
||||
});
|
||||
|
||||
test("falls back to sessionId prefix with date", () => {
|
||||
const entry = {
|
||||
sessionId: "abcd1234-5678-90ef-ghij-klmnopqrstuv",
|
||||
updatedAt: new Date("2024-03-15T10:30:00Z").getTime(),
|
||||
} as SessionEntry;
|
||||
const result = deriveSessionTitle(entry);
|
||||
expect(result).toBe("abcd1234 (2024-03-15)");
|
||||
});
|
||||
|
||||
test("falls back to sessionId prefix without date when updatedAt missing", () => {
|
||||
const entry = {
|
||||
sessionId: "abcd1234-5678-90ef-ghij-klmnopqrstuv",
|
||||
updatedAt: 0,
|
||||
} as SessionEntry;
|
||||
const result = deriveSessionTitle(entry);
|
||||
expect(result).toBe("abcd1234");
|
||||
});
|
||||
|
||||
test("trims whitespace from displayName", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
displayName: " Padded Name ",
|
||||
} as SessionEntry;
|
||||
expect(deriveSessionTitle(entry)).toBe("Padded Name");
|
||||
});
|
||||
|
||||
test("ignores empty displayName and falls through", () => {
|
||||
const entry = {
|
||||
sessionId: "abc123",
|
||||
updatedAt: Date.now(),
|
||||
displayName: " ",
|
||||
subject: "Actual Subject",
|
||||
} as SessionEntry;
|
||||
expect(deriveSessionTitle(entry)).toBe("Actual Subject");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user