refactor(agent-dispatcher): split into 4 focused modules (SOLID Phase 2)
This commit is contained in:
parent
54b1c60810
commit
450756f540
7 changed files with 326 additions and 252 deletions
117
v2/src/lib/utils/session-persistence.ts
Normal file
117
v2/src/lib/utils/session-persistence.ts
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
// Session persistence — maps session IDs to projects/providers and persists state to SQLite
|
||||
// Extracted from agent-dispatcher.ts (SRP: persistence concern)
|
||||
|
||||
import type { ProviderId } from '../providers/types';
|
||||
import { getAgentSession } from '../stores/agents.svelte';
|
||||
import {
|
||||
saveProjectAgentState,
|
||||
saveAgentMessages,
|
||||
saveSessionMetric,
|
||||
type AgentMessageRecord,
|
||||
} from '../adapters/groups-bridge';
|
||||
|
||||
// Map sessionId -> projectId for persistence routing
|
||||
const sessionProjectMap = new Map<string, string>();
|
||||
|
||||
// Map sessionId -> provider for message adapter routing
|
||||
const sessionProviderMap = new Map<string, ProviderId>();
|
||||
|
||||
// Map sessionId -> start timestamp for metrics
|
||||
const sessionStartTimes = new Map<string, number>();
|
||||
|
||||
// In-flight persistence counter — prevents teardown from racing with async saves
|
||||
let pendingPersistCount = 0;
|
||||
|
||||
export function registerSessionProject(sessionId: string, projectId: string, provider: ProviderId = 'claude'): void {
|
||||
sessionProjectMap.set(sessionId, projectId);
|
||||
sessionProviderMap.set(sessionId, provider);
|
||||
}
|
||||
|
||||
export function getSessionProjectId(sessionId: string): string | undefined {
|
||||
return sessionProjectMap.get(sessionId);
|
||||
}
|
||||
|
||||
export function getSessionProvider(sessionId: string): ProviderId {
|
||||
return sessionProviderMap.get(sessionId) ?? 'claude';
|
||||
}
|
||||
|
||||
export function recordSessionStart(sessionId: string): void {
|
||||
sessionStartTimes.set(sessionId, Date.now());
|
||||
}
|
||||
|
||||
/** Wait until all in-flight persistence operations complete */
|
||||
export async function waitForPendingPersistence(): Promise<void> {
|
||||
while (pendingPersistCount > 0) {
|
||||
await new Promise(r => setTimeout(r, 10));
|
||||
}
|
||||
}
|
||||
|
||||
/** Persist session state + messages to SQLite for the project that owns this session */
|
||||
export async function persistSessionForProject(sessionId: string): Promise<void> {
|
||||
const projectId = sessionProjectMap.get(sessionId);
|
||||
if (!projectId) return; // Not a project-scoped session
|
||||
|
||||
const session = getAgentSession(sessionId);
|
||||
if (!session) return;
|
||||
|
||||
pendingPersistCount++;
|
||||
try {
|
||||
// Save agent state
|
||||
await saveProjectAgentState({
|
||||
project_id: projectId,
|
||||
last_session_id: sessionId,
|
||||
sdk_session_id: session.sdkSessionId ?? null,
|
||||
status: session.status,
|
||||
cost_usd: session.costUsd,
|
||||
input_tokens: session.inputTokens,
|
||||
output_tokens: session.outputTokens,
|
||||
last_prompt: session.prompt,
|
||||
updated_at: Math.floor(Date.now() / 1000),
|
||||
});
|
||||
|
||||
// Save messages (use seconds to match session.rs convention)
|
||||
const nowSecs = Math.floor(Date.now() / 1000);
|
||||
const records: AgentMessageRecord[] = session.messages.map((m, i) => ({
|
||||
id: i,
|
||||
session_id: sessionId,
|
||||
project_id: projectId,
|
||||
sdk_session_id: session.sdkSessionId ?? null,
|
||||
message_type: m.type,
|
||||
content: JSON.stringify(m.content),
|
||||
parent_id: m.parentId ?? null,
|
||||
created_at: nowSecs,
|
||||
}));
|
||||
|
||||
if (records.length > 0) {
|
||||
await saveAgentMessages(sessionId, projectId, session.sdkSessionId, records);
|
||||
}
|
||||
|
||||
// Persist session metric for historical tracking
|
||||
const toolCallCount = session.messages.filter(m => m.type === 'tool_call').length;
|
||||
const startTime = sessionStartTimes.get(sessionId) ?? Math.floor(Date.now() / 1000);
|
||||
await saveSessionMetric({
|
||||
project_id: projectId,
|
||||
session_id: sessionId,
|
||||
start_time: Math.floor(startTime / 1000),
|
||||
end_time: nowSecs,
|
||||
peak_tokens: session.inputTokens + session.outputTokens,
|
||||
turn_count: session.numTurns,
|
||||
tool_call_count: toolCallCount,
|
||||
cost_usd: session.costUsd,
|
||||
model: session.model ?? null,
|
||||
status: session.status,
|
||||
error_message: session.error ?? null,
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn('Failed to persist agent session:', e);
|
||||
} finally {
|
||||
pendingPersistCount--;
|
||||
}
|
||||
}
|
||||
|
||||
/** Clear all session maps — called on dispatcher shutdown */
|
||||
export function clearSessionMaps(): void {
|
||||
sessionProjectMap.clear();
|
||||
sessionProviderMap.clear();
|
||||
sessionStartTimes.clear();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue