/** * The mirror: a live subagent's session, on a unix socket. * * A herdr pane cannot host an in-process subagent, so the pane hosts a client * of this instead. The client attaches by id and gets the transcript so far, * then pi's own session events as they happen, which is what pi's chat * components are written against. It may speak back: a word for the turn in * flight, or a stop. * * This is a port of the core, beside `ask.ts` and `verify.ts`, not a reporter: * a reporter only reads the stream, and the keyboard reaches the session. It * is still opt-in by the act that opens a pane - a subagent nobody attached to * is a subagent nobody spoke to, and the workflow runs identically. * * One server per process, listening only once something asks where it is, and * gone with the last subagent. Registration itself costs a map entry, so every * subagent registers and a test suite never touches a socket. */ import { type EventBus, type SubagentEvent } from "./events.ts"; import type { SessionEvent, SessionPort } from "./session.ts"; /** What the mirror needs from a live subagent. Registered by `spawn`. */ export type Mirrored = { /** The id a client attaches by, e.g. `scout#1`. */ id: string; /** The definition's name, for the pane's header. */ agent: string; /** The model pi resolved, when it has. */ model?: string; /** Where its tools run - pi's tool renderers want it to shorten paths. */ cwd: string; /** The session itself: its transcript, its events, its steer. */ session: SessionPort; /** Its run's bus: a steer is emitted there, so the record holds it. */ bus: EventBus; /** Its stop switch, the one thing that is safe at any moment. */ stop(): void; }; /** The first line a client sends. */ export type Attach = { /** The id of the subagent to watch. */ attach: string; }; /** What a client may say after attaching. */ export type MirrorIn = { type: "steer"; text: string; } | { type: "abort"; }; /** * What a client receives, one JSON object per line. * * `attached` comes first and names the pi package this process runs, so a * client draws with the same components that produced the events. Then one * `message` per transcript entry, then pi's own events as they are (their * `type` is pi's), interleaved with `status`, `usage`, `steer` and `close` * from the run's stream. */ export type MirrorOut = { type: "attached"; id: string; agent: string; model?: string; cwd: string; pi: string; } | { type: "message"; message: unknown; } | { type: "refused"; reason: string; } | { type: "error"; message: string; } | SessionEvent | SubagentEvent; /** * Why a steer on an idle subagent is turned down rather than queued. * * Measured: pi delivers a steer queued while idle with the next `prompt()` * and the model answers the steer instead of the task, and a follow-up is * answered inside the next `prompt()` after the task, so the workflow reads * the person's exchange back as its own result. Neither is a turn the pane * can be allowed to add to a run. */ export declare const REFUSED_IDLE = "between tasks - it can only be steered while it works"; /** * Where the mirror listens. Starts it if it was not. * * The path is decided by the process, not by a subagent, which is why it is * asked for here and does not ride on the `spawn` event: a reporter opening a * pane needs it once, and the same path serves every subagent alive. */ export declare function mirrorSocket(): string; /** Registers a live subagent. Returns the unregistration, idempotent. */ export declare function registerMirror(mirrored: Mirrored): () => void; //# sourceMappingURL=mirror.d.ts.map