89 lines
2.5 KiB
TypeScript
89 lines
2.5 KiB
TypeScript
import type { AgentOrchestrator } from '../backends/native/orchestrator.js';
|
|
import type { Message } from '../models/types.js';
|
|
import { getMessageText } from '../models/media.js';
|
|
import type { MemoryStore } from '../memory/store.js';
|
|
import type { ModelTier } from '../models/router.js';
|
|
|
|
export interface SessionEndSummaryConfig {
|
|
enabled: boolean;
|
|
tier: ModelTier;
|
|
maxMessages: number;
|
|
maxInputChars: number;
|
|
maxTokens: number;
|
|
writeToMemory: boolean;
|
|
memoryNamespace: string;
|
|
}
|
|
|
|
export interface SessionEndSummaryResult {
|
|
summary: string;
|
|
namespace?: string;
|
|
}
|
|
|
|
const SESSION_END_SUMMARY_PROMPT = [
|
|
'Summarize this completed conversation for future continuity.',
|
|
'Focus on durable facts, decisions, preferences, unresolved tasks, and next steps.',
|
|
'Be concise and structured with short bullets.',
|
|
'Do not include private secrets.',
|
|
].join(' ');
|
|
|
|
function sanitizeSessionId(sessionId: string): string {
|
|
return sessionId
|
|
.replace(/:/g, '/')
|
|
.replace(/[^a-zA-Z0-9/_-]+/g, '_')
|
|
.replace(/_+/g, '_');
|
|
}
|
|
|
|
function formatHistory(messages: Message[], maxInputChars: number): string {
|
|
const text = messages
|
|
.map((msg) => `${msg.role}: ${getMessageText(msg)}`)
|
|
.join('\n\n');
|
|
if (text.length <= maxInputChars) {
|
|
return text;
|
|
}
|
|
return text.slice(text.length - maxInputChars);
|
|
}
|
|
|
|
export async function summarizeSessionOnEnd(opts: {
|
|
agent: AgentOrchestrator;
|
|
sessionId: string;
|
|
history: Message[];
|
|
config: SessionEndSummaryConfig;
|
|
memoryStore?: MemoryStore;
|
|
}): Promise<SessionEndSummaryResult | null> {
|
|
if (!opts.config.enabled || opts.history.length === 0) {
|
|
return null;
|
|
}
|
|
|
|
const selected = opts.history.slice(-opts.config.maxMessages);
|
|
const conversation = formatHistory(selected, opts.config.maxInputChars);
|
|
if (!conversation.trim()) {
|
|
return null;
|
|
}
|
|
|
|
const result = await opts.agent.delegate({
|
|
tier: opts.config.tier,
|
|
systemPrompt: SESSION_END_SUMMARY_PROMPT,
|
|
message: conversation,
|
|
maxTokens: opts.config.maxTokens,
|
|
});
|
|
|
|
const summary = result.content.trim();
|
|
if (!summary) {
|
|
return null;
|
|
}
|
|
|
|
let namespace: string | undefined;
|
|
if (opts.memoryStore && opts.config.writeToMemory) {
|
|
namespace = `${opts.config.memoryNamespace}/${sanitizeSessionId(opts.sessionId)}`;
|
|
const block = `## ${new Date().toISOString()}\n\n${summary}\n\n`;
|
|
opts.memoryStore.write(namespace, block, 'append');
|
|
}
|
|
|
|
return { summary, namespace };
|
|
}
|
|
|
|
export const sessionEndSummaryInternals = {
|
|
sanitizeSessionId,
|
|
formatHistory,
|
|
};
|