import type { InterruptResult, RuntimeDriverName, RuntimeEvent, RuntimeOptions, RuntimeSessionId, RuntimeStatus, RuntimeSecurityMode, RuntimeThinkingLevel, StartSessionInput } from "@pi-claude-code-agent/runtime"; import type { ClaudeCodeRuntime } from "@pi-claude-code-agent/runtime/internal"; export type BridgeState = "starting" | "connected" | "idle" | "busy" | "interrupted" | "stopped" | "errored" | "disconnected"; export type IntercomMessageKind = "send" | "ask" | "reply"; export type BridgePeerKind = "ad-hoc" | "managed"; export interface BridgePeer { name: string; sessionId: RuntimeSessionId; cwd: string; model?: string; driver?: RuntimeDriverName; state: BridgeState; createdAt: string; updatedAt: string; lastActivityAt: string; kind?: BridgePeerKind; metadata?: Record; /** * Verbatim projection of `RuntimeStatus.raw` — driver-specific init payload * plus any other runtime-level scratch state. The runtime folds system/init * driver messages into `raw.init` rather than emitting them as transcript * events, so this is the only surface where consumers can read capability * fields like `requestedThinkingLevel` / `effectiveThinkingLevel` / * `thinkingLevelSupported`. Opaque by design — drivers control the shape. */ raw?: Record; } export interface InterruptPeerResult { peer: BridgePeer; interrupt: InterruptResult; } export interface LaunchPeerInput { name: string; prompt: string; driver?: RuntimeDriverName; cwd?: string; model?: string; appendSystemPrompt?: string; /** * Add the bridge's intercom peer behavior prompt at launch. Direct bridge * launches default to true because they are normally `/peer` workers. * `PiCaLeashManagedPeerApi` defaults this to false so downstream orchestrators * can use the shared peer lifecycle without inheriting intercom role text. */ includeBridgeSystemPrompt?: boolean; /** @deprecated Use `securityMode`. */ permissionMode?: StartSessionInput["permissionMode"]; /** * Coarse security posture. `safe` (default) keeps the driver's native * sandbox/permission prompts; `yolo` disables them where the driver * supports it. See runtime `RuntimeSecurityMode` for per-driver details. */ securityMode?: RuntimeSecurityMode; tools?: string[]; additionalDirectories?: string[]; env?: Record; waitForIdle?: boolean; kind?: BridgePeerKind; metadata?: Record; /** * Per-call thinking budget for drivers that support it (currently * `pi-coding-agent`). When omitted, the driver's configured * `defaultThinkingLevel` is used. Drivers that don't support per-call * thinking ignore this field. */ thinkingLevel?: RuntimeThinkingLevel; } export interface AttachPeerInput { name: string; sessionId: RuntimeSessionId; kind?: BridgePeerKind; metadata?: Record; } export interface IntercomInboundMessage { kind: IntercomMessageKind; from: string; text: string; replyTo?: string; timeoutMs?: number; model?: string; /** * Driver fields forwarded verbatim to `runtime.send`. The Bridge does not * filter these — drivers ignore fields they don't understand. Of these * four, only `securityMode` is session-sticky at the runtime layer (see * `RuntimeStatus.securityMode`): omitting it on a follow-up re-applies * the value captured at `start()`. `appendSystemPrompt`, `env`, and * `thinkingLevel` are per-send only — omit on a follow-up and the * driver falls back to its own defaults (no implicit re-application). */ appendSystemPrompt?: string; env?: Record; thinkingLevel?: RuntimeThinkingLevel; securityMode?: RuntimeSecurityMode; } export type DeliveryState = "completed" | "delivered_and_running"; export interface AskResult { peer: BridgePeer; reply: string; runState: RuntimeStatus["state"]; events: RuntimeEvent[]; deliveryState: DeliveryState; } export interface WaitForCompletionOptions { /** * Reject if no driver event arrives within this many ms. Every observable * driver event (message, tool, result, state change) refreshes the * staleness window. Omit to use the driver-aware default from * `defaultStaleThresholdMsForDriver`. Pass `Infinity` to disable. */ staleThresholdMs?: number; /** * Wall-clock backstop. Reject if total elapsed time since the call * started exceeds this, regardless of activity. Catches peers that keep * emitting events but never reach a terminal state. Omit for no ceiling. */ hardCeilingMs?: number; signal?: AbortSignal; /** * `waitForCompletion` resolves (does not reject) when a peer ends in * `state: "failed"` — inspect `status.state` and `status.lastError` on * the result. To make those failures hard to miss the bridge emits a * single `console.warn` on each failed resolution, carrying the peer * sessionId and the captured error message. Set this to `true` to * suppress the warning (typical only in tests that *expect* failures). */ silentOnFailure?: boolean; } export type WaitCompletionErrorCode = "WAIT_STALE" | "WAIT_HARD_CEILING"; export declare class WaitCompletionError extends Error { readonly code: WaitCompletionErrorCode; readonly sessionId: RuntimeSessionId; readonly elapsedMs: number; readonly stalenessMs?: number; readonly lastActivityAt?: string; constructor(init: { code: WaitCompletionErrorCode; message: string; sessionId: RuntimeSessionId; elapsedMs: number; stalenessMs?: number; lastActivityAt?: string; }); } export interface BridgeTransportAttachment { type: "file" | "snippet" | "context"; name: string; content: string; language?: string; } export interface BridgeTransportSessionInfo { id: string; name?: string; cwd: string; model: string; pid: number; startedAt: number; lastActivity: number; status?: string; } export interface BridgeTransportIncomingMessage { id: string; timestamp: number; replyTo?: string; expectsReply?: boolean; text: string; attachments?: BridgeTransportAttachment[]; from: BridgeTransportSessionInfo; } export interface BridgeTransportOutgoingMessage { text: string; replyTo?: string; expectsReply?: boolean; attachments?: BridgeTransportAttachment[]; } export interface BridgeTransportStatus { kind: string; boundPeers: number; connectedPeers: number; } export interface BridgeTransport { registerPeer(peer: BridgePeer, onMessage: (message: BridgeTransportIncomingMessage) => Promise | void): Promise; updatePeer(peer: BridgePeer): Promise; unregisterPeer(name: string): Promise; sendFromPeer(peerName: string, to: string, message: BridgeTransportOutgoingMessage): Promise; listSessions?(): Promise; getStatus?(): Promise | BridgeTransportStatus; close?(): Promise; } export interface BridgeOptions { /** * Share an externally-constructed Runtime with the Bridge. Use this when a * sibling consumer (e.g. `ClaudeCodeSubagentBackend`) needs the same * in-process event source and storage owner. The Bridge does NOT expose * this back to its public surface — callers are responsible for not * leaking their own reference. * * If both `runtime` and `runtimeOptions` are set, `runtime` wins and * `runtimeOptions` is ignored. */ runtime?: ClaudeCodeRuntime; /** * Construction options for a Bridge-owned `ClaudeCodeRuntime`. Use this in * the common case where the Bridge is the sole Runtime consumer (most * applications, all tests with a fake driver). Ignored if `runtime` is * also passed. */ runtimeOptions?: RuntimeOptions; storageDir?: string; transport?: BridgeTransport; pollIntervalMs?: number; askTimeoutMs?: number; }