| 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<AgentId, AgentTranscript>(); |
| readonly #descriptors = new Map<AgentId, AgentDescriptor>(); |
| readonly #rosterListeners = new Set<RosterListener>(); |
|
|
| 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); |
| } |
| } |
|
|