/** * OutputMirror — the per-binding egress pipeline (spec §7.2): consumes raw * AgentEvents from a SessionPort subscription and mirrors them into a chat * conversation through a ChannelAdapter, using the vendored streaming modules * (throttled serialized edits, markdown auto-close, frozen-boundary chunking). */ import type { AgentEvent } from "@skaile/workspaces/types"; import type { ChannelAdapter, ChannelLogger } from "../adapter.js"; import type { Binding, MessageRef, ReplyTarget } from "../types.js"; /** Options for {@link OutputMirror}. */ export type OutputMirrorOptions = { adapter: ChannelAdapter; binding: Binding; target: ReplyTarget; /** Egress dedupe key prefix — engine passes `${sessionId}`. */ dedupeScope: string; /** Called with every MessageRef this mirror posts (engine records ReplyRef↔MessageRef + loop guard). */ onPosted?: (ref: MessageRef, turnMessageId: string) => void; /** Turn-origin oracle for mirrorMode "chat-initiated" (engine-owned state). */ isChatInitiatedTurn?: () => boolean; logger: ChannelLogger; }; /** * The egress pipeline for one (binding, conversation) pair. * * `text` events accumulate into a per-turn buffer that streams to the platform * as one growing message (post-then-update), auto-closing unbalanced markdown * mid-stream and chunking at `capabilities.maxMessageLength` with frozen * boundaries. `question` / `error` / mirrored `user_message`s post as their * own messages. Every egress post carries an idempotency key * `${dedupeScope}:${messageId}:${chunkIndex}` and is retried once on failure * with the SAME key (spec §8 at-least-once discipline). * * Invariant: `handleEvent` is synchronous — all platform I/O runs on internal * queues; `dispose()` flushes them. */ export declare class OutputMirror { private readonly opts; private turn; /** Serializes side-posts and turn finalization; awaited by dispose(). */ private tail; private lastTypingAt; private disposed; constructor(opts: OutputMirrorOptions); /** Wire as the SessionPort.subscribe sink. Synchronous fan-in; work is internal. */ handleEvent(e: AgentEvent): void; /** Flush pending state and stop. */ dispose(): Promise; private onText; /** Detach the current turn, drain it on the tail queue, and start fresh. */ private finishTurn; private newTurn; /** Post a standalone message (question / error / mirrored user text). */ private postAside; /** One retry with the SAME dedupe key — the adapter's idempotency absorbs duplicates. */ private postWithRetry; private onTyping; /** Mirror-mode + visibility gating for user_message events (spec §7.2.4, §5). */ private onUserMessage; /** Append work to the tail queue; failures log rather than poison the chain. */ private enqueue; } //# sourceMappingURL=output-mirror.d.ts.map