/** * Cooperative cross-extension tool execution protocol (Plan 003). * * pi 0.83.0 has no public `executeTool` on `ExtensionAPI`, so pi-codemode * cannot invoke another extension's tool handlers directly. Instead, * extensions that OPT IN expose their tools to eval cells over pi's shared * extension event bus (`pi.events`). The protocol is: * * - The client (pi-codemode) probes once per session on `codemode/tools/v1/probe`. * - Opt-in servers answer on `codemode/tools/v1/offer` with their `serverId` * and the tool names they own. First offer per name wins for the session. * - A call is a request frame on `codemode/tools/v1/request/`; the * server executes with the OWNER's validation and context and answers on * `codemode/tools/v1/response/`. * - Cancellation is best-effort on `codemode/tools/v1/cancel/`. * * The protocol is inert without cooperating peers: an unbound tool keeps the * byte-identical refusal from ToolExecutionShim. Builtin and SDK tools never * subscribe, so they are never probed into execution. The event bus is * in-process and unauthenticated, but extensions already form that trust * domain (they can register tools, intercept `tool_call`, and rewrite * results), so binding integrity is a sanity check, not authentication. */ export const PEER_PROTOCOL_VERSION = 1; export const PEER_CHANNEL_PROBE = "codemode/tools/v1/probe"; export const PEER_CHANNEL_OFFER = "codemode/tools/v1/offer"; const PEER_CHANNEL_REQUEST_PREFIX = "codemode/tools/v1/request/"; const PEER_CHANNEL_RESPONSE_PREFIX = "codemode/tools/v1/response/"; const PEER_CHANNEL_CANCEL_PREFIX = "codemode/tools/v1/cancel/"; export function peerRequestChannel(serverId: string): string { return `${PEER_CHANNEL_REQUEST_PREFIX}${serverId}`; } export function peerResponseChannel(requestId: string): string { return `${PEER_CHANNEL_RESPONSE_PREFIX}${requestId}`; } export function peerCancelChannel(serverId: string): string { return `${PEER_CHANNEL_CANCEL_PREFIX}${serverId}`; } export interface ProbeFrame { readonly v: 1; } export interface OfferFrame { readonly serverId: string; readonly toolNames: readonly string[]; readonly v: 1; } export interface RequestFrame { readonly params: unknown; readonly requestId: string; readonly toolName: string; readonly v: 1; } export interface ResponseOkFrame { readonly ok: true; readonly requestId: string; readonly v: 1; readonly value: unknown; } export interface ResponseErrorFrame { readonly error: { readonly message: string }; readonly ok: false; readonly requestId: string; readonly v: 1; } export type ResponseFrame = ResponseOkFrame | ResponseErrorFrame; export interface CancelFrame { readonly requestId: string; readonly v: 1; } function isRecord(value: unknown): value is Record { return typeof value === "object" && value !== null && !Array.isArray(value); } function isStringArray(value: unknown): value is readonly string[] { return ( Array.isArray(value) && value.every((entry) => typeof entry === "string" && entry.length > 0) ); } export function isProbeFrame(value: unknown): value is ProbeFrame { return isRecord(value) && value.v === PEER_PROTOCOL_VERSION; } export function isOfferFrame(value: unknown): value is OfferFrame { return ( isRecord(value) && value.v === PEER_PROTOCOL_VERSION && typeof value.serverId === "string" && value.serverId.length > 0 && isStringArray(value.toolNames) ); } export function isRequestFrame(value: unknown): value is RequestFrame { return ( isRecord(value) && value.v === PEER_PROTOCOL_VERSION && typeof value.requestId === "string" && value.requestId.length > 0 && typeof value.toolName === "string" && value.toolName.length > 0 && "params" in value ); } export function isResponseFrame(value: unknown): value is ResponseFrame { if ( !isRecord(value) || value.v !== PEER_PROTOCOL_VERSION || typeof value.requestId !== "string" || value.requestId.length === 0 ) { return false; } if (value.ok === true) { return "value" in value; } if (value.ok === false) { return ( isRecord(value.error) && typeof value.error.message === "string" && value.error.message.length > 0 ); } return false; } export function isCancelFrame(value: unknown): value is CancelFrame { return ( isRecord(value) && value.v === PEER_PROTOCOL_VERSION && typeof value.requestId === "string" && value.requestId.length > 0 ); }