/** * Client-side capability round-trip helpers. * * When the LLM invokes a `side: 'client'` capability, the runner does NOT * execute the handler in-process. Instead it: * * 1. Generates a `callId`. * 2. Emits a {@link CapabilityInvokedEvent} over the transport. * 3. Registers a pending promise keyed by `callId`. * 4. Awaits a {@link CapabilityResultCommand} from the platform; resolves * with `result` or rejects with `result.error`. * * `fireAndForget` short-circuits step 3 — the handler resolves with `{}` * immediately after emitting the invoke event. The bridge-level dispatch * also short-circuits before calling `hooks.invoke()`, so this is a * defence-in-depth guard. * * The helpers below are extracted for unit testing; the actual wiring * lives in `serve.ts` where the transport / pending map are owned. * * @category Capabilities * @since 2.0.0 */ import type { AgentEvent, Capability, CapabilityResultCommand } from "@skaile/workspaces/types"; /** * Registry of in-flight capability calls. The serve loop owns the map; this * type is exported so tests can pass a real `Map` instance and assert the * pending state across the round-trip. * @docLink packages/runner/capabilities#registry-surface */ export type PendingCallEntry = { resolve: (value: unknown) => void; reject: (err: Error) => void; timer: ReturnType; }; /** * Default timeout (60s) for waiting on a capability result from the platform. * Mirrors the value used in `serve.ts`. * * @docLink packages/runner/capabilities#registry-surface */ export declare const DEFAULT_CAPABILITY_CALL_TIMEOUT_MS = 60000; /** * Resolve the round-trip timeout for a wire-format client capability. * * Precedence: * 1. An explicit per-capability `callTimeoutMs` — set for capabilities that * legitimately block far longer than a normal tool call (e.g. * `platform.ask_session`, which suspends the caller's turn until a peer * session answers). * 2. `approvalTimeoutMs` when the capability is `requiresApproval` — human * approvals routinely take minutes. * 3. `DEFAULT_CAPABILITY_CALL_TIMEOUT_MS` (60s) otherwise. * * Extracted as a pure function so the precedence is unit-testable without * spinning up `startAgentServer`. * * @docLink packages/runner/capabilities#registry-surface */ export declare function resolveCapabilityCallTimeoutMs(cap: Pick, approvalTimeoutMs: number): number; /** * Build the runner-side handler for a wire-format client capability. The * returned function emits `capability_invoked` and waits for a matching * `capability_result` command via the supplied pending-calls map. * * Decoupled from `serve.ts` so it can be exercised in isolation without * spinning up `startAgentServer`. * @docLink packages/runner/capabilities#registry-surface */ export declare function buildClientCapabilityHandler(args: { wire: Capability; pending: Map; emit: (event: AgentEvent) => void; timeoutMs?: number; /** Override the callId factory for deterministic tests. */ generateCallId?: () => string; }): (input: unknown) => Promise; /** * Handle a platform→runner `capability_invoked` command: invoke the named * capability through the registry and emit a single `capability_result` * reply. The inbound mirror of {@link buildClientCapabilityHandler} (the * runner→platform direction). A thrown error — unknown capability or Zod * validation failure — rides back as `{ error }` so the gateway rejects the * caller; a handler's own structured `{ ok: false }` result rides back * verbatim (the gateway resolves it). Decoupled from `startAgentServer` for * unit testability — the serve loop calls this with `invoke = registry.invoke` * and `emit = sendEvent`. */ export declare function dispatchRunnerCapabilityInvocation(cmd: { callId: string; name: string; input: unknown; }, deps: { invoke: (name: string, input: unknown) => Promise; emit: (event: { type: "capability_result"; callId: string; result: unknown; }) => void; }): Promise; /** * Resolve / reject a pending capability call based on a `capability_result` * command from the platform. Returns `true` when the callId matched a * pending entry, `false` otherwise (for telemetry / debug logging). * @docLink packages/runner/capabilities#registry-surface */ export declare function resolveCapabilityResult(pending: Map, cmd: CapabilityResultCommand): boolean; /** * Reject a pending capability call when the platform sends a * `capability_approve` with `decision: 'rejected'`. Used by capabilities * declared `requiresApproval: true` — the platform shows a confirm UI on * `capability_invoked`; on user denial it forwards the rejection back to the * runner via this command instead of dispatching the handler. * * Returns `true` when the callId matched a pending entry, `false` otherwise * (e.g. the call was already resolved via `capability_result`). * * Approvals (`decision: 'approved'`) are no-ops at this level — the platform * still drives dispatch and replies via `capability_result`. This function * only handles the rejection short-circuit. * * @param pending - Map of in-flight capability calls keyed by `callId`. * @param callId - ID of the call being denied. * @param feedback - Optional user-facing feedback message passed to the rejection error. * @returns `true` when the call was found and rejected; `false` when already resolved. * @category Capabilities * @since 2.1.0 * @docLink packages/runner/capabilities#reject-capability-on-approval-deny */ export declare function rejectCapabilityOnApprovalDeny(pending: Map, callId: string, feedback?: string): boolean; //# sourceMappingURL=capability-roundtrip.d.ts.map