import type { CanonicalMessage, CanonicalModelEvent, CanonicalModelRequest } from "../../model/index.js";
import type {
PilotDeckElicitationChannel,
PilotDeckToolAuditRecorder,
PilotDeckFileUpdateNotifier,
PilotDeckToolFileHistorySink,
PilotDeckToolScheduler,
ToolRegistry,
} from "../../tool/index.js";
import type { PlanFileManager } from "../../tool/builtin/planFile.js";
import type { PlanTodoStateManager } from "./PlanTodoState.js";
import type { LifecycleRuntime } from "../../lifecycle/index.js";
import type { AgentContextRuntime } from "../../context/ContextRuntime.js";
import type { RouterRuntime } from "../../router/index.js";
import type { AgentEvent, AgentEventEmitter } from "../protocol/events.js";
* Narrow view of the router that the agent loop actually consumes. Tests can
* inject anything that satisfies this contract; production wiring uses
* `createRouterRuntime`.
*
* `decide` + `execute` are exposed so the agent loop can insert a post-routing
* compaction pass between the routing decision and the model call.
*/
export type AgentRouterRuntime = Pick<RouterRuntime, "stream" | "decide" | "execute"> & {
observeUsage?: RouterRuntime["observeUsage"];
invalidateSticky?: RouterRuntime["invalidateSticky"];
};
* Subagent sidechain transcript hooks (C3 §6.3). The agent loop calls these
* around a forked subagent so:
* - `recordSubagentStarted` writes a `subagent_started` reference into the
* **parent** transcript (truncated directive preview).
* - `recordSubagentCompleted` writes a `subagent_completed` reference into
* the **parent** transcript (truncated summary + usage / duration).
* - `subagentTranscriptResolver(subagentId)` returns a sidechain writer
* that captures the subagent's turn-by-turn entries into a separate
* `<subagentId>.jsonl` file.
*
* All hooks are optional — when missing, the agent loop falls back to the
* legacy "no sidechain" behavior (subagent runs, but no persistence).
*/
export type AgentSubagentTranscriptHooks = {
recordSubagentStarted?(args: {
sessionId: string;
turnId: string;
subagentId: string;
subagentType: string;
prompt: string;
transcriptRelativePath: string;
subagentSessionId?: string;
}): Promise<void>;
recordSubagentCompleted?(args: {
sessionId: string;
turnId: string;
subagentId: string;
subagentType: string;
summary: string;
usage?: {
inputTokens?: number;
outputTokens?: number;
cacheReadTokens?: number;
cacheWriteTokens?: number;
totalTokens?: number;
};
turns: number;
durationMs: number;
errored?: boolean;
}): Promise<void>;
subagentTranscriptResolver?(subagentId: string): {
recordAcceptedInput(sessionId: string, turnId: string, messages: CanonicalMessage[]): Promise<void>;
recordDurableMessage(sessionId: string, turnId: string, message: CanonicalMessage): Promise<void>;
transcriptRelativePath: string;
};
};
export type AgentRuntimeDependencies = {
router: AgentRouterRuntime;
tools: {
scheduler: PilotDeckToolScheduler;
registry: ToolRegistry;
};
context?: AgentContextRuntime;
* Look up a model's context-window size by provider/model id. Used after
* routing to re-evaluate compaction against the target model's window when
* it is smaller than the agent's default model. Returns `undefined` for
* unknown models so the caller can skip re-compaction gracefully.
*/
getModelMaxContextTokens?: (provider: string, model: string) => number | undefined;
now?: () => Date;
uuid?: () => string;
auditRecorder?: PilotDeckToolAuditRecorder;
lifecycle?: LifecycleRuntime;
subagentTranscript?: AgentSubagentTranscriptHooks;
* Elicitation channel — wired into the per-tool `PilotDeckToolRuntimeContext`
* so `ask_user_question` (B1) can drive the gateway. When omitted, the
* tool returns a `mcp_unavailable` error instead of crashing.
*/
elicitation?: PilotDeckElicitationChannel;
* File-history sink — wired into the per-tool runtime context so
* `edit_file` / `write_file` (C4) snapshot the file before mutation.
* `FileHistoryStore` directly satisfies this contract.
*/
fileHistory?: PilotDeckToolFileHistorySink;
* Optional sink for propagating successful file writes to editor / LSP
* integrations. When absent, write_file still succeeds and performs no
* post-write host notifications.
*/
fileUpdateNotifier?: PilotDeckFileUpdateNotifier;
* Plan file manager — resolves the project-local `.pilotdeck/plans`
* directory and reads explicitly submitted plan documents for
* `enter_plan_mode` / `exit_plan_mode`. Absent in headless / test runtimes.
*/
planFileManager?: PlanFileManager;
planTodoManager?: PlanTodoStateManager;
eventEmitter?: AgentEventEmitter;
drainEvents?: () => AgentEvent[];
};
export type AgentLegacyModelRuntime = {
stream(request: CanonicalModelRequest, signal?: AbortSignal): AsyncIterable<CanonicalModelEvent>;
};