/** * Per-agent message inbox: queued and steering FIFOs. Purely an in-memory * mechanism of the loop driver — the public surface is `Agent.send()` and * `Agent.steer()`. * * @module dsh-agent-loop/inbox */ import type { ContentBlock, MessageSource } from '@deepseek-ai/dsh-llm' /** One message waiting in an agent's inbox. */ export interface InboxMessage { content: ContentBlock[] source: MessageSource } /** * Per-agent inbox: a queued FIFO (drained at turn start) and a steering FIFO * (drained between steps of a running turn). Purely an in-memory mechanism of * the loop — the public surface is `Agent.send()` / `Agent.steer()`. */ export class Inbox { private queuedMessages: InboxMessage[] = [] private steeringMessages: InboxMessage[] = [] private wakeup: (() => void) | undefined /** Resolves when a queued message arrives (used by the idle loop). */ get hasQueued(): boolean { return this.queuedMessages.length > 0 } get hasSteering(): boolean { return this.steeringMessages.length > 0 } enqueue(message: InboxMessage): void { this.queuedMessages.push(message) this.wakeup?.() } steer(message: InboxMessage): void { this.steeringMessages.push(message) } /** Drain all queued messages (turn start). */ drainQueued(): InboxMessage[] { return this.queuedMessages.splice(0) } /** Drain all steering messages (between steps). */ drainSteering(): InboxMessage[] { return this.steeringMessages.splice(0) } /** * Discard all pending messages (queued + steering) without delivering them — * used by `cancel()`, which drops un-started work rather than draining it into * a turn. Unlike `drainQueued`/`drainSteering`, the messages are thrown away. */ clear(): void { this.queuedMessages.length = 0 this.steeringMessages.length = 0 } /** Wait until a queued message arrives or `cancel` resolves. */ waitForQueued(cancel: Promise): Promise { if (this.hasQueued) return Promise.resolve() const { promise, resolve } = Promise.withResolvers() this.wakeup = resolve void cancel.then(resolve) return promise.finally(() => { if (this.wakeup === resolve) this.wakeup = undefined }) } }