/** * pi-mock — integration test harness for pi extensions. * * Composes gateway + rpc + sandbox into one object. * Exposes a management HTTP API (/_/) for CLI control. * * createMock() → Mock * - gateway: mock Anthropic API + HTTP/HTTPS proxy * - rpc: JSONL communication with pi * - sandbox: local child process or Docker container */ import { type NetworkRule, type NetworkAction, type ProxyLogEntry } from "./gateway.js"; import { type RpcEvent, type RpcResponse, type UIHandler } from "./rpc.js"; import { type Brain, type ApiRequest, type BrainResponse } from "./anthropic.js"; export interface MockOptions { /** Brain function — you are the model. */ brain: Brain; /** Extension paths to load. */ extensions?: string[]; /** Use Docker sandbox for network isolation. Default: false */ sandbox?: boolean; /** Network rules. Only meaningful with sandbox: true for full isolation. */ network?: { default?: NetworkAction; rules?: NetworkRule[]; }; /** Working directory for pi. */ cwd?: string; /** Path to pi binary (local mode). Default: "pi" */ piBinary?: string; /** Extra pi CLI args. */ piArgs?: string[]; /** Extra env vars. */ env?: Record; /** Gateway port. 0 = random (recommended). Default: 0 */ port?: number; /** Max wait for pi to start (ms). Default: 15000 */ startupTimeoutMs?: number; /** Default timeout for run() (ms). Default: 120000 */ runTimeoutMs?: number; /** Handler for extension UI dialogs. Default: cancel all. */ uiHandler?: UIHandler; /** Docker image name. Default: auto-build "pi-mock-sandbox" */ image?: string; /** Extra Docker volumes. */ volumes?: string[]; } export interface Mock { /** Send a prompt, wait for full agent cycle, return events from this cycle. */ run(message: string, timeoutMs?: number): Promise; /** Send a prompt (fire-and-forget). */ prompt(message: string): Promise; /** Wait for current agent cycle to finish. Returns events since last prompt. */ drain(timeoutMs?: number): Promise; /** Wait for an event matching a predicate. */ waitFor(pred: (e: RpcEvent) => boolean, timeoutMs?: number): Promise; /** * Steer the agent mid-turn — message is delivered after the current tool call completes. * Use this to test extensions like pi-manager that inject guidance during active turns. */ steer(message: string): Promise; /** * Queue a follow-up message — delivered after the agent finishes current work. * Triggers a new agent turn with this message. Use to test multi-turn extension flows. */ followUp(message: string): Promise; /** * Abort the current agent turn. The agent stops what it's doing and emits agent_end. */ abort(): Promise; /** * Send a raw RPC command to pi. Escape hatch for any RPC command pi supports * (get_session_stats, set_auto_retry, set_model, etc.) */ sendRpc(command: Record, timeoutMs?: number): Promise; /** Replace the brain mid-test. */ setBrain(brain: Brain): void; /** Update network rules mid-test. */ setNetworkRules(rules: NetworkRule[], defaultAction?: NetworkAction): void; /** All API requests the brain saw (all providers). */ readonly requests: ApiRequest[]; /** Every proxy request (host, action, timestamp). */ readonly proxyLog: ProxyLogEntry[]; /** Wait for the next brain request. Resolves with the request + index. */ waitForRequest(pred?: (req: ApiRequest, index: number) => boolean, timeoutMs?: number): Promise<{ request: ApiRequest; index: number; }>; /** All RPC events from pi. */ readonly events: RpcEvent[]; /** Pi's stderr output lines. */ readonly stderr: string[]; /** Gateway port. */ readonly port: number; /** Gateway URL (http://127.0.0.1:PORT). */ readonly url: string; /** Management API auth token. Required as x-pi-mock-token header or ?token= param. */ readonly token: string; /** Shut everything down. */ close(): Promise; } export declare function createMock(options: MockOptions): Promise; /** Brain that returns responses in order, then a default. */ export declare function script(...responses: BrainResponse[]): Brain; /** Brain that repeats the same response forever. */ export declare function always(response: BrainResponse): Brain; export interface PendingCall { /** The API request pi sent. */ request: ApiRequest; /** Which call this is (0-indexed). */ index: number; /** Release the brain with this response. */ respond(response: BrainResponse): void; } /** Filter predicate for waitForCall. */ export type CallFilter = (request: ApiRequest, index: number) => boolean; export interface ControllableBrain { /** The brain function — pass this to createMock({ brain: cb.brain }). */ brain: Brain; /** Wait for the next brain call. Blocks until pi makes an API request. */ waitForCall(timeoutMs?: number): Promise; /** * Wait for a brain call matching a filter. Non-matching calls stay * buffered for other waiters — no head-of-line blocking. * * ```typescript * // Filter by model name * const call = await cb.waitForCall({ model: "gpt-4" }, 3000); * * // Filter by predicate * const call = await cb.waitForCall(req => req.model.includes("claude"), 3000); * ``` */ waitForCall(filter: CallFilter | { model?: string; _provider?: string; }, timeoutMs?: number): Promise; /** Snapshot of pending (buffered, unresponded) calls. Useful for debugging. */ pending(): PendingCall[]; } /** * Create a brain where each call blocks until you explicitly respond. * Gives tests full control over timing and interleaving. * * Supports filtered waiting — when multiple clients hit the brain * concurrently, you can wait for a specific one by model name or * custom predicate. Non-matching calls stay buffered for other waiters. * * ```typescript * const cb = createControllableBrain(); * const mock = await createMock({ brain: cb.brain, ... }); * await mock.prompt("do something"); * const call = await cb.waitForCall(); * console.log(call.request.messages); // inspect what pi sent * call.respond(text("hello")); // release the brain * * // Filtered — wait for a specific model * const gptCall = await cb.waitForCall({ model: "gpt-4" }, 3000); * const claudeCall = await cb.waitForCall(req => req.model.includes("claude"), 3000); * ``` */ export declare function createControllableBrain(): ControllableBrain; /** Default brain — always responds with a simple text message. */ export declare function echo(): Brain; //# sourceMappingURL=mock.d.ts.map