import type { AgentId } from '../model/ids'; import { AgentTranscript, type Disposable } from './agentTranscript'; export interface AgentDescriptor { readonly agentId: AgentId; readonly type?: 'main' | 'sub' | 'independent'; readonly parentAgentId?: AgentId; readonly label?: string; readonly createdAt?: string; readonly disposedAt?: string; } export type RosterListener = (agents: readonly AgentDescriptor[]) => void; export class TranscriptStore { readonly #agents = new Map(); readonly #descriptors = new Map(); readonly #rosterListeners = new Set(); constructor(readonly sessionId: string) { } ensureAgent(agentId: AgentId, descriptor?: AgentDescriptor): AgentTranscript { let transcript = this.#agents.get(agentId); if (!transcript) { transcript = new AgentTranscript(agentId); this.#agents.set(agentId, transcript); } if (descriptor && this.#descriptors.get(agentId) !== descriptor) { this.#descriptors.set(agentId, descriptor); this.#emitRoster(); } return transcript; } getAgent(agentId: AgentId): AgentTranscript | undefined { return this.#agents.get(agentId); } removeAgent(agentId: AgentId): boolean { const removed = this.#agents.delete(agentId); if (this.#descriptors.delete(agentId) || removed) this.#emitRoster(); return removed; } describeAgent(descriptor: AgentDescriptor): void { if (this.#descriptors.get(descriptor.agentId) !== descriptor) { this.#descriptors.set(descriptor.agentId, descriptor); this.#emitRoster(); } } markDisposed(agentId: AgentId, disposedAt: string): void { const descriptor = this.#descriptors.get(agentId); if (descriptor === undefined || descriptor.disposedAt !== undefined) return; this.describeAgent({ ...descriptor, disposedAt }); } agents(): readonly AgentDescriptor[] { return [...this.#descriptors.values()]; } onRosterChange(listener: RosterListener): Disposable { this.#rosterListeners.add(listener); return { dispose: () => void this.#rosterListeners.delete(listener) }; } #emitRoster(): void { const agents = this.agents(); for (const listener of this.#rosterListeners) listener(agents); } }