On this page
RPC Extension UI
Extensions can request user interaction through ctx.ui. In RPC mode, supported calls become a request/response subprotocol alongside normal RPC commands and session events.
There are two categories of extension UI methods:
- Dialog methods (
select,confirm,input,editor): emit anextension_ui_requeston stdout and block until the client sends back anextension_ui_responseon stdin with the matchingid. - Fire-and-forget methods (
notify,setStatus,setWidget,setTitle,set_editor_text): emit anextension_ui_requeston stdout but do not expect a response. The client can display the information or ignore it.
If a dialog method includes a timeout field, the agent-side will auto-resolve with a default value when the timeout expires. The client does not need to track timeouts.
Limitations
CopiedSome ExtensionUIContext methods are not supported or degraded in RPC mode because they require direct terminal UI access:
custom()returnsundefined.onTerminalInput()returns a no-op unsubscribe function.setWorkingMessage(),setWorkingVisible(),setWorkingIndicator(),setHiddenThinkingLabel(),setFooter(),setHeader(),addAutocompleteProvider(),setEditorComponent(), andsetToolsExpanded()are no-ops.getEditorText()returns""andgetEditorComponent()returnsundefined.getToolsExpanded()returnsfalse.pasteToEditor()delegates tosetEditorText()without terminal paste handling.getAllThemes()returns[], andgetTheme()returnsundefined.setTheme()returns{ success: false, error: "Theme switching not supported in RPC mode" }.
Note: ctx.mode is "rpc" and ctx.hasUI is true in RPC mode because the dialog and fire-and-forget methods are functional via the extension UI sub-protocol. Use ctx.mode === "tui" to guard TUI-specific features like custom() that require a real terminal.
Requests from Pi
CopiedAll requests have type: "extension_ui_request", a unique id, and a method field.
select
CopiedPrompt the user to choose from a list. Dialog methods with a timeout field include the timeout in milliseconds; the agent auto-resolves with undefined if the client doesn't respond in time.
{
"type": "extension_ui_request",
"id": "uuid-1",
"method": "select",
"title": "Allow dangerous command?",
"options": ["Allow", "Block"],
"timeout": 10000
}
Expected response: extension_ui_response with value (the selected option string) or cancelled: true.
confirm
CopiedPrompt the user for yes/no confirmation.
{
"type": "extension_ui_request",
"id": "uuid-2",
"method": "confirm",
"title": "Clear session?",
"message": "All messages will be lost.",
"timeout": 5000
}
Expected response: extension_ui_response with confirmed: true/false or cancelled: true.
input
CopiedPrompt the user for free-form text.
{
"type": "extension_ui_request",
"id": "uuid-3",
"method": "input",
"title": "Enter a value",
"placeholder": "type something..."
}
Expected response: extension_ui_response with value (the entered text) or cancelled: true.
editor
CopiedOpen a multi-line text editor with optional prefilled content.
{
"type": "extension_ui_request",
"id": "uuid-4",
"method": "editor",
"title": "Edit some text",
"prefill": "Line 1\nLine 2\nLine 3"
}
Expected response: extension_ui_response with value (the edited text) or cancelled: true.
notify
CopiedDisplay a notification. Fire-and-forget, no response expected.
{
"type": "extension_ui_request",
"id": "uuid-5",
"method": "notify",
"message": "Command blocked by user",
"notifyType": "warning"
}
The notifyType field is "info", "warning", or "error". Defaults to "info" if omitted.
setStatus
CopiedSet or clear a status entry in the footer/status bar. Fire-and-forget.
{
"type": "extension_ui_request",
"id": "uuid-6",
"method": "setStatus",
"statusKey": "my-ext",
"statusText": "Turn 3 running..."
}
Send statusText: undefined (or omit it) to clear the status entry for that key.
setWidget
CopiedSet or clear a widget (block of text lines) displayed above or below the editor. Fire-and-forget.
{
"type": "extension_ui_request",
"id": "uuid-7",
"method": "setWidget",
"widgetKey": "my-ext",
"widgetLines": ["--- My Widget ---", "Line 1", "Line 2"],
"widgetPlacement": "aboveEditor"
}
Send widgetLines: undefined (or omit it) to clear the widget. The widgetPlacement field is "aboveEditor" (default) or "belowEditor". Only string arrays are supported in RPC mode; component factories are ignored.
setTitle
CopiedSet the terminal window/tab title. Fire-and-forget.
{
"type": "extension_ui_request",
"id": "uuid-8",
"method": "setTitle",
"title": "pi - my project"
}
set_editor_text
CopiedSet the text in the input editor. Fire-and-forget.
{
"type": "extension_ui_request",
"id": "uuid-9",
"method": "set_editor_text",
"text": "prefilled text for the user"
}
Responses to Pi
CopiedResponses are sent for dialog methods only (select, confirm, input, editor). The id must match the request.
Value response (select, input, editor)
Copied{"type": "extension_ui_response", "id": "uuid-1", "value": "Allow"}
Confirmation response (confirm)
Copied{"type": "extension_ui_response", "id": "uuid-2", "confirmed": true}
Cancellation response (any dialog)
CopiedDismiss any dialog method. The extension receives undefined (for select/input/editor) or false (for confirm).
{"type": "extension_ui_response", "id": "uuid-3", "cancelled": true}
Example
CopiedSee the checked RPC extension UI client and its demo extension.
The exported request and response unions are defined in rpc-types.ts. See Extensions for mode-independent extension guidance.