feat: agent session management — metrics, channels, dispose (P2-006) (#78)

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #78.
This commit is contained in:
2026-03-13 03:35:59 +00:00
committed by jason.woltje
parent f3a7eadcea
commit 7f6dc43a2d
6 changed files with 116 additions and 3 deletions

View File

@@ -12,6 +12,7 @@ import { CoordService } from '../coord/coord.service.js';
import { ProviderService } from './provider.service.js';
import { createBrainTools } from './tools/brain-tools.js';
import { createCoordTools } from './tools/coord-tools.js';
import type { SessionInfoDto } from './session.dto.js';
export interface AgentSessionOptions {
provider?: string;
@@ -25,6 +26,9 @@ export interface AgentSession {
piSession: PiAgentSession;
listeners: Set<(event: AgentSessionEvent) => void>;
unsubscribe: () => void;
createdAt: number;
promptCount: number;
channels: Set<string>;
}
@Injectable()
@@ -107,6 +111,9 @@ export class AgentService implements OnModuleDestroy {
piSession,
listeners,
unsubscribe,
createdAt: Date.now(),
promptCount: 0,
channels: new Set(),
};
this.sessions.set(sessionId, session);
@@ -143,11 +150,53 @@ export class AgentService implements OnModuleDestroy {
return this.sessions.get(sessionId);
}
listSessions(): SessionInfoDto[] {
const now = Date.now();
return Array.from(this.sessions.values()).map((s) => ({
id: s.id,
provider: s.provider,
modelId: s.modelId,
createdAt: new Date(s.createdAt).toISOString(),
promptCount: s.promptCount,
channels: Array.from(s.channels),
durationMs: now - s.createdAt,
}));
}
getSessionInfo(sessionId: string): SessionInfoDto | undefined {
const s = this.sessions.get(sessionId);
if (!s) return undefined;
return {
id: s.id,
provider: s.provider,
modelId: s.modelId,
createdAt: new Date(s.createdAt).toISOString(),
promptCount: s.promptCount,
channels: Array.from(s.channels),
durationMs: Date.now() - s.createdAt,
};
}
addChannel(sessionId: string, channel: string): void {
const session = this.sessions.get(sessionId);
if (session) {
session.channels.add(channel);
}
}
removeChannel(sessionId: string, channel: string): void {
const session = this.sessions.get(sessionId);
if (session) {
session.channels.delete(channel);
}
}
async prompt(sessionId: string, message: string): Promise<void> {
const session = this.sessions.get(sessionId);
if (!session) {
throw new Error(`No agent session found: ${sessionId}`);
}
session.promptCount += 1;
try {
await session.piSession.prompt(message);
} catch (err) {
@@ -177,7 +226,13 @@ export class AgentService implements OnModuleDestroy {
} catch (err) {
this.logger.error(`Failed to unsubscribe session ${sessionId}`, String(err));
}
try {
session.piSession.dispose();
} catch (err) {
this.logger.error(`Failed to dispose piSession for ${sessionId}`, String(err));
}
session.listeners.clear();
session.channels.clear();
this.sessions.delete(sessionId);
}