fix(sessions): name relay handoffs deterministically

This commit is contained in:
Federico Jaramillo Martinez
2026-07-03 10:51:45 +02:00
parent d2b5520785
commit 10efb7f221
4 changed files with 61 additions and 2 deletions
@@ -5,6 +5,13 @@ const SESSION_NAME_TIMEOUT_MS = 10_000;
const SESSION_NAME_MAX_INPUT_CHARS = 4_000;
const SESSION_NAME_MAX_LENGTH = 60;
const FALLBACK_SESSION_NAME_MAX_WORDS = 6;
const RELAY_HANDOFF_FIRST_LINE = /^Relay\s+"([^"\n]+)"\s+leg\s+(\d+)\s+begins now\.?\s*(?:\n|$)/;
export function deterministicSessionName(firstMessage: unknown): string | undefined {
if (typeof firstMessage !== "string") return undefined;
return relayHandoffSessionName(firstMessage.trimStart());
}
export async function generateShortSessionName<TApi extends Api>(streamFn: StreamFn, model: Model<TApi>, firstMessage: string): Promise<string | undefined> {
const stream = await streamFn(
@@ -59,6 +66,31 @@ export function cleanSessionName(value: string): string | undefined {
return title === "" ? undefined : title;
}
function relayHandoffSessionName(firstMessage: string): string | undefined {
const match = RELAY_HANDOFF_FIRST_LINE.exec(firstMessage);
if (match === null) return undefined;
const relayName = match[1]?.replace(/\s+/g, " ").trim();
const legNumber = match[2];
if (relayName === undefined || relayName === "" || legNumber === undefined) return undefined;
return cleanSessionName(formatRelaySessionName(relayName, legNumber));
}
function formatRelaySessionName(relayName: string, legNumber: string): string {
const prefix = "Relay ";
const suffix = ` leg ${legNumber}`;
const maxRelayNameLength = Math.max(1, SESSION_NAME_MAX_LENGTH - prefix.length - suffix.length);
const displayedRelayName = truncateRelayName(relayName, maxRelayNameLength);
return `${prefix}${displayedRelayName}${suffix}`;
}
function truncateRelayName(relayName: string, maxLength: number): string {
if (relayName.length <= maxLength) return relayName;
const truncated = relayName.slice(0, maxLength).replace(/[\s._-]+$/g, "").trim();
return truncated === "" ? relayName.slice(0, maxLength).trim() : truncated;
}
function textFromAssistant(message: AssistantMessage): string {
return message.content
.filter((part) => part.type === "text")