/** * Main-thread mirror of an agent session homed on a subagent worker. * * Created when the worker's `SessionPublisher` posts `opened` and held by the * session directory until `closed` (or the worker dies). It serves both the * cross-agent {@link AgentSessionHandle} surface and the TUI's * {@link AgentViewSession} surface, without ever blocking on the worker: * * - handle state reads come from the mirrored {@link SessionMirrorState} and * message list the publisher keeps current; * - view reads come from the {@link SessionViewState} and entry list the * publisher pushes while main watches the session (any `subscribe` * listener, e.g. the focused TUI view; the entry list is kept between * watches, so a new watch only catches it up), or are computed on main * from them: the transcript context (entries + leaf), role-model cycles, * temporary model thinking levels and service tiers (main-side settings * and model registry), async job views (the root job manager scoped to the * child's agent id), and generation tok/s (a main-local meter fed by * forwarded events); * - `settings` is rebuilt on main from the main instance the worker's * settings mirror plus the child's own overlay layers, and `modelRegistry` * is the main registry the worker's registry mirrors; * - tool renderers and extension renderers resolve through the nearest * main-thread ancestor session (worker tool copies carry no render hooks); * - synchronous commands (thinking level, queued-message restore, todo * phases, custom entries) apply optimistically to the mirror and are sent * to the home worker, whose next patch reconciles; asynchronous commands * are RPC to the publisher. * * `subscribe` turns the worker's event stream (and view state) on while any * listener is attached; run-state listeners are fed by every transition the * publisher reports. A closed mirror keeps its last state, rejects commands * with an error naming the worker, and treats `dispose` as already done. */ import { type AgentMessage, type AgentTool, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { Effort, ImageContent, Model, ServiceTier, UsageReport } from "@oh-my-pi/pi-ai"; import { type ConfiguredThinkingLevel } from "@oh-my-pi/pi-tui/thinking"; import type { IrcMessage } from "@oh-my-pi/pi-tui/tools/irc"; import type { JobWidgetEntry } from "@oh-my-pi/pi-tui/tools/job-widget"; import type { TodoPhase } from "@oh-my-pi/pi-tui/tools/todo"; import type { AsyncJobManagerApi } from "../async/index.js"; import type { EffectiveExtensionRoots } from "../capability/types.js"; import type { ModelRegistry } from "../config/model-registry.js"; import { Settings } from "../config/settings.js"; import type { ExtensionRunner } from "../extensibility/extensions/index.js"; import type { ContextUsage } from "../extensibility/extensions/types.js"; import type { Skill } from "../extensibility/skills.js"; import type { GoalModeState } from "../goals/state.js"; import type { AgentSessionEvent, AgentSessionEventListener } from "../session/agent-session-events.js"; import { type AgentSessionAdvisorOverview, type AgentViewLoop, type AgentViewRuleSource, type AgentViewSession, type AgentViewSessionManager, type AgentViewState, type ToolExecutionUpdateEvent } from "../session/agent-session-handle.js"; import type { AgentSessionDisposeOptions, AsyncJobInspection, AsyncJobSnapshot, AsyncJobTree, EphemeralTurnOptions, EphemeralTurnResult, Prewalk, PromptOptions, ResolvedRoleModel, RestoredQueuedMessage, RoleModelCycle, RoleModelCycleResult, SessionStats, SteerOptions } from "../session/agent-session-types.js"; import type { IdleMaintenanceOptions } from "../session/idle-maintenance.js"; import type { CustomMessage } from "../session/messages.js"; import type { ServingModel } from "../session/retry-fallback-chains.js"; import { type SessionContext, type TranscriptSessionContextOptions } from "../session/session-context.js"; import type { AgentDefinition } from "../task/types.js"; import type { WorkPoolYieldItem } from "../task/workpool-yield.js"; import { type ApprovalGrantStore } from "../tools/approval-cache.js"; import { TokenRateMeter } from "../utils/token-rate.js"; import type { ThreadId } from "./identity.js"; import { type RemoteObject } from "./rpc.js"; import { type SessionAbortOptions, type SessionEntriesDelta, type SessionHostProtocol, type SessionMessagesDelta, type SessionMirrorState, type SessionRunState, type SessionViewState } from "./session-mirror.js"; /** Main-side proxy of a worker's publisher (remote-object kind `"agent-session-host"`). */ export declare class SessionHostClient { readonly remote: RemoteObject; constructor(remote: RemoteObject); } /** State of a mirror that never received (or no longer has) data from its home. */ export declare function emptyMirrorState(): SessionMirrorState; /** Options for {@link RemoteAgentSession}. */ export interface RemoteAgentSessionInit { readonly key: string; readonly home: ThreadId; readonly agentId: string | undefined; readonly host: SessionHostClient | undefined; readonly state: SessionMirrorState; readonly messages: AgentMessage[]; /** Main registry the worker session's registry mirrors. */ readonly modelRegistry?: ModelRegistry | null; /** Main job manager the worker session registers its async jobs with. */ readonly asyncJobManager?: AsyncJobManagerApi | null; } /** {@link AgentViewSession} for a session homed on a subagent worker; see the module doc. */ export declare class RemoteAgentSession implements AgentViewSession { #private; readonly instanceKey: string; /** Worker the session lives on. */ readonly home: ThreadId; /** Registry id the session reported when it opened. */ readonly agentId: string | undefined; readonly agent: AgentViewLoop; readonly state: AgentViewState; readonly sessionManager: AgentViewSessionManager; readonly tokenRate: TokenRateMeter; constructor(init: RemoteAgentSessionInit); /** A closed mirror for a key whose home no longer has (or never published) the session. */ static gone(key: string, home: ThreadId, reason: string): RemoteAgentSession; /** Whether the home worker closed the session (dispose) or died. */ get closed(): boolean; get isStreaming(): boolean; get runStartedAt(): number | undefined; get ircWakesPending(): boolean; get messages(): AgentMessage[]; get sessionFile(): string | undefined; get model(): Model | undefined; get thinkingLevel(): ThinkingLevel | undefined; get servingModel(): ServingModel | undefined; get effectiveExtensionRoots(): EffectiveExtensionRoots; get hindsightActive(): boolean; /** Main-side equivalent of the child's settings: the mirrored main instance under the child's own overlay layers. */ get settings(): Settings; /** The main registry the worker session's registry mirrors. */ get modelRegistry(): ModelRegistry; /** Extension runner of the nearest main-thread ancestor session, which loaded the extensions the worker re-imported. */ get extensionRunner(): ExtensionRunner | undefined; get sessionId(): string; get ttsrManager(): AgentViewRuleSource | undefined; get hasAbortedToolCallTail(): boolean; get systemPrompt(): string[]; get skills(): readonly Skill[]; get isAutoThinking(): boolean; get isCompacting(): boolean; get compactionSpeculation(): "idle" | "running" | "armed"; get contextUsageRevision(): number; get isGeneratingHandoff(): boolean; get isRetrying(): boolean; get retryAttempt(): number; get isTtsrAbortPending(): boolean; get hasInterruptibleInput(): boolean; getSessionStats(): SessionStats; getAdvisorStatusOverview(): AgentSessionAdvisorOverview; getSessionAgents(): AgentDefinition[]; hasQueuedIrcFrom(from: string): boolean; /** * Only ever called on the caller's own session, which is homed on the * caller's thread; draining another thread's queue from a mirror would * consume records its owner is about to inject. */ drainPendingIrcInboxMessages(_agentId: string, _opts?: { ifAny?: (msg: IrcMessage) => boolean; }): IrcMessage[]; emitIrcRelayObservation(record: CustomMessage): void; activeToolExecutionUpdates(): readonly ToolExecutionUpdateEvent[]; settleInFlightMessagePersistence(): Promise; /** Display transcript built on main from the mirrored entries (already in display form) and leaf. */ buildTranscriptSessionContext(options?: TranscriptSessionContextOptions): SessionContext; getContextUsage(options?: { contextWindow?: number; }): ContextUsage | undefined; autoResolvedThinkingLevel(): Effort | undefined; configuredThinkingLevel(): ConfiguredThinkingLevel | undefined; getAvailableEffortSelectors(): ConfiguredThinkingLevel[]; /** Applied to the mirror at once; the home worker applies it (with clamping) and its next patch reconciles. */ setThinkingLevel(level: ConfiguredThinkingLevel | undefined, persist?: boolean): void; /** Next selector in the mirrored cycle order (off → auto → minimal..max → off), applied like {@link setThinkingLevel}. */ cycleThinkingLevel(): ConfiguredThinkingLevel | undefined; resolveTemporaryModelThinkingLevel(model: Model): ConfiguredThinkingLevel | undefined; setModelTemporary(model: Model, thinkingLevel?: ConfiguredThinkingLevel, options?: { ephemeral?: boolean; }): Promise; getRoleModelCycle(roleOrder: readonly string[]): RoleModelCycle | undefined; cycleRoleModels(roleOrder: readonly string[], direction?: "forward" | "backward"): Promise; applyRoleModel(entry: ResolvedRoleModel): Promise; effectiveServiceTier(model: Model): ServiceTier | undefined; isFastModeActive(): boolean; getAnthropicSlowModeLabel(): string | undefined; getPrewalkState(): Prewalk | undefined; getGoalModeState(): GoalModeState | undefined; getAdvisorCost(): number; isAdvisorUsingSubscription(): boolean; fetchUsageReports(signal?: AbortSignal): Promise; getAsyncJobSnapshot(options?: { recentLimit?: number; }): AsyncJobSnapshot | null; getAsyncJobTree(options?: { recentLimit?: number; }): AsyncJobTree | null; getRunningJobWidgets(): JobWidgetEntry[]; inspectAsyncJob(id: string): AsyncJobInspection | undefined; cancelAsyncJob(id: string): boolean; requestJobCaptions(): void; getTodoPhases(): TodoPhase[]; /** Applied to the mirror at once and sent to the home worker. */ setTodoPhases(phases: TodoPhase[]): void; /** Same two steps as a home session: the optimistic entry and the mirror update reach the worker in order. */ commitTodoPhases(phases: TodoPhase[]): void; /** * Main-thread renderer source for the child's tool `name`: the nearest * main-thread ancestor's tool of that name (worker tool copies drop their * render hooks), and only while the child itself registers the name. */ getToolByName(name: string): AgentTool | undefined; hasBuiltInTool(name: string): boolean; getQueuedMessages(): { steering: readonly string[]; followUp: readonly string[]; liveSteered: number; }; /** * Restore the newest user-authored queued message (steering first) the home * has not claimed for delivery, drop it from the mirror, and have the home * take it out of its queue. Main decides: the home's gate asks main before * delivering any restorable entry, so a message restored here is never * delivered too. */ popLastQueuedMessage(): RestoredQueuedMessage | undefined; /** * Bridge only, synchronous: the home's gate is about to deliver queued * entries `ids`. Returns those already restored here, which the home drops; * either way none of them is restorable any more. */ claimQueued(ids: readonly string[]): string[]; hasSessionApproval(key: string): boolean; /** The child's conversation tier, as mirrored, over the database of its agent directory. */ getApprovalGrants(): ApprovalGrantStore; resume(): Promise; retry(): Promise; abortRetry(): void; abortCompaction(reason?: unknown): void; abortHandoff(): void; runEphemeralTurn(args: EphemeralTurnOptions): Promise; ephemeralMaxTokensPreservesRequest(): boolean; exportToHtml(outputPath?: string, useUserThemes?: boolean): Promise; /** Idle maintenance runs on the home worker; its blocking predicates are answered synchronously from main. */ enableIdleMaintenance(options?: IdleMaintenanceOptions): void; refreshIdleMaintenance(): void; subscribe(listener: AgentSessionEventListener): () => void; subscribeRunState(listener: (state: SessionRunState) => void): () => void; deliverIrcMessage(msg: IrcMessage): Promise<"injected" | "woken">; waitForIrcReplies(): Promise; prompt(text: string, options?: PromptOptions): Promise; steer(text: string, images?: ImageContent[], options?: SteerOptions): Promise; abort(options?: SessionAbortOptions): Promise; setWorkPoolYieldItems(items: readonly WorkPoolYieldItem[]): Promise; /** Dispose on the home worker; a session that is already gone counts as disposed. */ dispose(options?: AgentSessionDisposeOptions): Promise; /** Bridge only: merge a state patch from the publisher. */ applyPatch(patch: Partial): void; /** Bridge only: apply a message-list change in place (the array identity is stable). */ applyMessages(delta: SessionMessagesDelta): void; /** * Bridge only: a watch started; replace the view state with the publisher's * snapshot and catch the entry list (kept across watches) up with `entries`. */ applyView(state: SessionViewState, entries: SessionEntriesDelta | undefined): void; /** Bridge only: merge a view-state patch from the publisher. */ applyViewPatch(patch: Partial): void; /** Bridge only: apply an entry-list change; locally appended entries give way to the worker's. */ applyEntries(delta: SessionEntriesDelta): void; /** Bridge only: dispatch one forwarded event to subscribers. */ applyEvent(event: AgentSessionEvent): void; /** Bridge only: report a run-state transition to run-state subscribers. */ applyRunState(state: SessionRunState): void; /** * Bridge only: the home closed the session or died. The mirror keeps its * last data, reports idle to run-state subscribers that saw it running, * and drops every listener. */ close(reason: string): void; }