import type { SteeringChannel } from '../runtime/query/steering.js'; import type { TopicStateStore } from '../store/topic/state.js'; import { NamzuError } from '../types/errors/index.js'; import type { TenantId, TopicId } from '../types/ids/index.js'; import type { Message } from '../types/message/index.js'; /** * The object a host holds between turns. * * There was none. A host could not ask whether the agent was running, and * had nowhere to put "when you next run, start with this" — so it either * held a steer until it observed a turn starting, or carried the text itself * and passed it manually on the next `run()` call. * * Two delivery targets, each with a stated lifetime, and no silent third * state. `steer` reaches the turn that is happening; `queueForNextTurn` * reaches the one that has not started. `steer` on an idle handle THROWS * rather than accepting into a queue nothing will read — that refusal is * the design, because the alternative is a host believing it redirected an * agent that was not listening. */ /** * Whether a turn is in flight, as this handle sees it. * * NOT `AgentStatus` — that name was a deprecated alias of the execution * status which never typed an agent. Reusing it here would silently change * what a consumer's `AgentStatus` MEANS rather than failing their build, * which is the worse of the two outcomes. */ export type AgentHandleStatus = 'idle' | 'running'; /** A steer aimed at an agent that is not running. */ export declare class AgentNotRunningError extends NamzuError { constructor(); } export interface AgentHandle { readonly status: AgentHandleStatus; /** * Hand guidance to the turn happening now. * * Reaches the model at the next turn boundary whether or not a tool is * in flight — the channel rides a settled tool result when there is one, * and the loop delivers the remainder otherwise. */ steer(text: string): void; /** Leave a message for the turn that has not started yet. */ queueForNextTurn(message: Message): Promise; } export interface AgentHandleOptions { readonly steering: SteeringChannel; /** Where a queued message is persisted. Absent means it cannot be. */ readonly topicStateStore?: TopicStateStore; readonly topicId: TopicId; readonly tenantId: TenantId; /** Live, not captured — the status has to be true when it is asked. */ readonly isRunning: () => boolean; } /** * A host's handle on one agent. * * `status` is a function call rather than a stored boolean, because a * stored one is only as current as whoever remembered to update it — and * the whole value of this object is answering a question at the moment it * is asked. */ export declare function createAgentHandle(options: AgentHandleOptions): AgentHandle; /** * Add a message to the topic's next-turn queue, under compare-and-set. * * Read-modify-write against the record's revision, so two hosts queueing * for one conversation cannot silently drop each other's message — the * loser gets the store's stale-revision error and can retry with what it * now sees. */ export declare function appendQueuedMessage(store: TopicStateStore, topicId: TopicId, tenantId: TenantId, message: Message): Promise; /** * Take whatever was queued for this topic, leaving the queue empty. * * Cleared as it is read, in the same compare-and-set write. A queue that * was read and cleared separately would re-deliver on a crash between the * two, and "start with this" arriving twice is a different instruction from * the one that was left. */ export declare function drainQueuedMessages(store: TopicStateStore, topicId: TopicId, tenantId: TenantId): Promise; //# sourceMappingURL=handle.d.ts.map