PGAP
PGAP (Plexi Generic App Protocol) is the communication layer between app processes and the Plexi host. It runs as newline-delimited JSON over a child process’s stdin/stdout.
How It Works
When you run a Plexi app, the host spawns it as a child process and establishes a bidirectional message channel over stdio. The app sends draw commands describing what to render; the host sends events when the user interacts with the pane.
Host ──render request──► App
Host ◄──draw commands─── App
Host ──input event──────► App
The host owns the render loop. Each frame tick, the host asks each app pane for a fresh set of draw commands and composites them into the UI.
Every message is a JSON object with a type discriminant field. The schema
version is 3 — see sdk/protocol/pgap.schema.json for the full
machine-readable definition.
Capabilities
Every app declares its capabilities in manifest.toml. The host enforces
these at runtime:
[app.capabilities]
capabilities = ["secrets.get", "net.http"]
Capabilities gate PGAP host APIs; they are not a process sandbox and do not restrict what a native Python process can do outside PGAP. The Cap column in the tables below shows which capability is required. Requests without a cap are available to all apps.
Future CPython-in-WASM compatibility work may add a sandboxed Python app route. That is deferred and is not the current SDK v3 execution path.
The Python SDK
The recommended way to write a Plexi app is with the Python SDK. It wraps
PGAP into idiomatic Python: define module-level init, update, and view
functions, then return effects and component trees. See the
SDK reference for the full API.
App → Host Requests
App-to-host requests — go to route_command.
Messages the app sends to request host services.
capability_request
Request a runtime capability prompt. Host shows modal; responds with CapabilityDecision.
| Field | Type | Required |
|---|---|---|
capability | string | yes |
request_id | string | yes |
secret_get
Request a workspace-scoped secret. Scoped to Init.workspace_root automatically.
| Field | Type | Required |
|---|---|---|
key | string | yes |
file_read
Read a file through the native ProcessApp host. Requires fs.read and the resolved path must stay inside the app’s w…
Capability: fs.read
| Field | Type | Required |
|---|---|---|
path | string | yes |
file_list
List a directory through the native ProcessApp host. Requires fs.read and the resolved path must stay inside the ap…
Capability: fs.read
| Field | Type | Required |
|---|---|---|
extensions | string[] | no |
path | string | yes |
save_app_state
Save app state. Host writes to workspace or global JSON file.
| Field | Type | Required |
|---|---|---|
payload | any | yes |
run_get
Request to start a run. Host surfaces in Run palette (Cmd+R).
| Field | Type | Required |
|---|---|---|
intent | string | yes |
payload | any | yes |
run_complete
Signal that a run the app owns has finished.
| Field | Type | Required |
|---|---|---|
result | any | yes |
run_id | string | yes |
notify
Post a notification. All three action_types must dispatch correctly (no TODO).
| Field | Type | Required |
|---|---|---|
actions | NotificationAction[] | no |
body | string | yes |
image_inline | variant | no |
image_pipe_id | string? | no |
input_prompt | string? | no |
kind | NotifyKind | no |
level | string | yes |
notify_id | string? | no |
on_dismiss | string? | no |
options | NotifyOption[] | no |
priority | integer | yes |
required | boolean | no |
response_file | string? | no |
scope | variant | no |
timeout_secs | integer? | no |
title | string | yes |
set_agent_state
Report agent state for a pane. Called by hook scripts via plexi agent report.
| Field | Type | Required |
|---|---|---|
agent | string | yes |
detail | string? | no |
pane_id | integer | yes |
session_id | string? | no |
state | AgentState | yes |
set_pip_status
Report an app’s own pip status (red/yellow/green) for its activity dot. Fire-and-forget; set by the app process via `…
| Field | Type | Required |
|---|---|---|
pane_id | integer | no |
status | PipStatus | yes |
get_agent_states
Get all tracked pane agent states. Writes JSON array to response_file.
| Field | Type | Required |
|---|---|---|
response_file | string | yes |
pipe_open
Open a typed pipe. mode: “json” | “binary” direction: “in” | “out” | “duplex”
| Field | Type | Required |
|---|---|---|
direction | string | yes |
mode | string | yes |
pipe_id | string | yes |
pipe_open_directed
Open a directed JSON pipe to a specific target pane (#286).
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
target_pane_id | integer | yes |
pipe_send
Send a JSON-mode pipe message (not for binary pipes).
| Field | Type | Required |
|---|---|---|
payload | any | yes |
pipe_id | string | yes |
status_summary
Update the status text shown in the parent pane chrome.
| Field | Type | Required |
|---|---|---|
text | string | yes |
spawn_app
Request the host to spawn a new app pane. Requires spawn.app capability. layout: “split_h” (new pane right), “spl…
Capability: spawn.app
| Field | Type | Required |
|---|---|---|
args | string[] | no |
layout | string? | no |
type_id | string | yes |
spawn_pane
Unified pane spawn primitive (#592). Supersedes SpawnApp for new apps. Requires panes.spawn capability. layout: o…
Capability: panes.spawn
| Field | Type | Required |
|---|---|---|
args | string[] | no |
cwd | string? | no |
ephemeral | boolean | no |
from_pane_id | integer? | no |
layout | string? | no |
name | string? | no |
no_focus | boolean | no |
path | string? | no |
pipe_id | string? | no |
request_id | string? | no |
response_file | string? | no |
target_context | integer? | no |
type_id | string | yes |
workspace_root | string? | no |
set_pane_title
Set the title displayed on a terminal pane’s tab. Sent by plexi pane set-title over PLEXI_SOCKET.
| Field | Type | Required |
|---|---|---|
name | string | yes |
pane_id | integer | yes |
list_panes
List all open panes. Host writes a JSON array to response_file. Sent by plexi pane list.
| Field | Type | Required |
|---|---|---|
context_id | integer? | no |
response_file | string | yes |
list_contexts
List all open contexts. Host writes a JSON array to response_file. Sent by plexi context list.
| Field | Type | Required |
|---|---|---|
response_file | string | yes |
get_pane_info
Query info for a specific pane by ID. Host writes JSON object to response_file. Sent by plexi pane info.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string | yes |
get_previous_pane_info
Query info for the previously focused pane. Host walks pane_focus_history from the end, finds the Nth live entry, a…
| Field | Type | Required |
|---|---|---|
response_file | string | yes |
steps | integer | no |
list_permissions
List permission state across apps (stint 0017). Gated on permissions.manage when arriving over PGAP. Host writes a …
Capability: permissions.manage
| Field | Type | Required |
|---|---|---|
response_file | string | yes |
set_permission
Set the stored permission state for an (app, workspace, capability) triple (stint 0017). Gated on `permissions.manage…
Capability: permissions.manage
| Field | Type | Required |
|---|---|---|
app_id | string | yes |
capability | string | yes |
response_file | string | yes |
state | string | yes |
workspace | string? | no |
slot_write
Write bytes to a named host-managed pane file slot.
| Field | Type | Required |
|---|---|---|
append | boolean | yes |
content | integer[] | yes |
pane_id | integer | yes |
replace | boolean | yes |
response_file | string | yes |
slot_name | string | yes |
slot_read
Read raw bytes from a named host-managed pane file slot.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string | yes |
slot_name | string | yes |
slot_list
List named host-managed pane file slots.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string | yes |
slot_delete
Delete a named host-managed pane file slot.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string | yes |
slot_name | string | yes |
workspace_clean_slots
Remove slot files for pane ids that are no longer live in any window.
| Field | Type | Required |
|---|---|---|
dry_run | boolean | yes |
response_file | string | yes |
focus_pane
Move UI focus to a pane by PaneId. Sent by plexi pane focus. Fire-and-forget.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
close_pane
Close a pane by PaneId. Sent by plexi pane close. Fire-and-forget.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
send_to_pane
Write text to a running pane’s PTY stdin. Sent by plexi pane send. \n in text (literal backslash-n) is interprete…
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string? | no |
text | string | yes |
key_pane
Deliver a synthetic key event to any pane. Sent by plexi pane key. For terminal panes, the key is translated to PTY…
| Field | Type | Required |
|---|---|---|
key | string | yes |
pane_id | integer | yes |
response_file | string? | no |
capture_pane
Read the last N lines from a terminal pane’s PTY scrollback buffer. Sent by plexi pane capture. Host writes a JSON …
| Field | Type | Required |
|---|---|---|
from_cursor | integer? | no |
full_output | boolean | no |
lines | integer | yes |
pane_id | integer | yes |
response_file | string | yes |
get_pane_state
Query the last-rendered UI state of a pane. Sent by plexi pane state. For app panes: host writes a JSON object with…
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
response_file | string | yes |
send_app_action
Dispatch a semantic action to an app pane. Sent by plexi app action <pane_id> <action> [args...]. Host delivers `Pl…
| Field | Type | Required |
|---|---|---|
action | string | yes |
args | string[] | no |
pane_id | integer | yes |
response_file | string? | no |
create_context
Create a new context. Sent by plexi context new over PLEXI_SOCKET.
| Field | Type | Required |
|---|---|---|
anchor_pane | integer? | no |
focus | boolean | no |
name | string? | no |
parent_name | string? | no |
portal_direction | string? | no |
response_file | string? | no |
root | string? | no |
windows | string[] | no |
focus_context
Focus existing context by root, or create one. Sent by plexi context open.
| Field | Type | Required |
|---|---|---|
root | string | yes |
set_context_root
Set/update the root of a context. Sent by plexi context set-root. context_id targets the caller’s context (PLEXI_…
| Field | Type | Required |
|---|---|---|
context_id | integer? | no |
root | string | yes |
set_context_description
Set/update the description of a context. Sent by plexi context describe. context_id targets the caller’s context …
| Field | Type | Required |
|---|---|---|
context_id | integer? | no |
description | string | yes |
zoom_into_context
Zoom into a sub-context. Pushes depth stack. Sent by plexi context zoom.
| Field | Type | Required |
|---|---|---|
context_id | integer | yes |
zoom_out_of_context
Zoom out of a sub-context. Pops depth stack. Sent by plexi context zoom-out.
No additional fields.
push_pane_to_subcontext
Push a pane into a new sub-context. Sent by plexi context push. pane_id targets the caller’s pane (PLEXI_PANE_ID)…
| Field | Type | Required |
|---|---|---|
name | string? | no |
pane_id | integer? | no |
query_context_state
Query the rolled-up ContextState for a context (#1518). The requesting app must be in an ancestor (or the same) con…
| Field | Type | Required |
|---|---|---|
context_id | integer | yes |
http_request
Host-brokered HTTP request. Requires net.http capability. Host replies with `PlexiEvent::HttpResponse { request_id,…
Capability: net.http
| Field | Type | Required |
|---|---|---|
body | string? | no |
headers | object | no |
method | string | no |
request_id | string | yes |
url | string | yes |
open_url
Open an HTTP(S) URL in the user’s default browser.
| Field | Type | Required |
|---|---|---|
url | string | yes |
ai_query
v3.3 brokered AI call. Requires ai.query capability.
Capability: ai.query
| Field | Type | Required |
|---|---|---|
messages | AiMessage[] | yes |
model_tier | ModelTier | yes |
request_id | string | yes |
system | string | yes |
tools | AiTool[] | yes |
expose_tools
v3.7 tool protocol (#398). App declares its callable tools to the host. The host registers these in the global tool r…
| Field | Type | Required |
|---|---|---|
tools | AiTool[] | yes |
tool_result
v3.7 tool protocol (#399). App returns the result of a PlexiEvent::ToolCall invocation. call_id must match the `c…
| Field | Type | Required |
|---|---|---|
call_id | string | yes |
error | string? | no |
output_json | string? | no |
mcp_tool_result
App returns the result of a PlexiEvent::McpToolCall invocation. call_id must match the call_id from the `McpToo…
| Field | Type | Required |
|---|---|---|
call_id | string | yes |
error | string? | no |
result | any | no |
audio_play
Host-owned audio playback via rodio.
| Field | Type | Required |
|---|---|---|
pipe_id | string? | no |
source | string? | no |
state | string | yes |
volume | number | no |
audio_capture
Host-owned audio capture: mic PCM delivered on a binary pipe. device_id selects which input device (from `ListAudio…
| Field | Type | Required |
|---|---|---|
buffer_size | integer | yes |
device_id | string? | no |
pipe_id | string | yes |
sample_rate | integer | yes |
list_audio_devices
Request enumeration of audio devices (#277). Host responds with `PlexiEvent::AudioDevicesListed { request_id, inputs,…
| Field | Type | Required |
|---|---|---|
request_id | string | yes |
list_midi_devices
Request enumeration of MIDI ports (#320). Host responds with `PlexiEvent::MidiDevicesListed { request_id, inputs, out…
| Field | Type | Required |
|---|---|---|
request_id | string | yes |
open_midi_input
Open a MIDI input port and forward every incoming message as a binary pipe frame on pipe_id. Each frame is a single…
Capability: midi.in
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
port_id | string | yes |
close_midi_input
Close the MIDI input previously opened on port_id. The host disconnects from the port and closes the associated bin…
| Field | Type | Required |
|---|---|---|
port_id | string | yes |
send_midi
Send one MIDI 1.0 byte stream to port_id. Fire-and-forget — the host only emits PlexiEvent::MidiSendError if the …
Capability: midi.out
| Field | Type | Required |
|---|---|---|
bytes | integer[] | yes |
port_id | string | yes |
open_video
Open a video decoder (#345). The host responds with `PlexiEvent::VideoOpenAck { request_id, handle_id, width, height,…
Capability: video.playback
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
request_id | string | yes |
source | string | yes |
set_video_state
Drive playback state for a previously-opened video handle (#345). handle_id is the value returned in VideoOpenAck…
| Field | Type | Required |
|---|---|---|
handle_id | integer | yes |
state | VideoState | yes |
close_video
Close a previously-opened video handle (#345). Tears down the decoder thread and the associated binary pipe drains. N…
| Field | Type | Required |
|---|---|---|
handle_id | integer | yes |
cd_request
Request the host to cd all terminals in the same pane group to cwd. Terminals receive cd <cwd>\n written to their…
| Field | Type | Required |
|---|---|---|
cwd | string | yes |
set_timer
Request a one-shot timer. Requires timer capability. Host fires PlexiEvent::Timer { timer_id } after after_ms m…
Capability: timer
| Field | Type | Required |
|---|---|---|
after_ms | integer | yes |
timer_id | string | yes |
cancel_timer
Cancel a pending timer. No-op if the timer has already fired or doesn’t exist.
| Field | Type | Required |
|---|---|---|
timer_id | string | yes |
load_image
Async image fetch brokered through the host. Requires net.http capability. Host fetches src, caches under `handle…
Capability: net.http
| Field | Type | Required |
|---|---|---|
handle | string | yes |
src | string | yes |
request_linked_terminal
Ask the host to open a fresh terminal pane next to this Canvas app and link it. Host responds with `PlexiEvent::Linke…
| Field | Type | Required |
|---|---|---|
cwd | string? | no |
label | string? | no |
request_id | string | yes |
run_in_linked_terminal
Execute command in a linked terminal pane.
| Field | Type | Required |
|---|---|---|
command | string | yes |
echo | boolean | yes |
terminal_pane_id | integer | yes |
insert_path_token
Insert path into the linked terminal at the cursor position.
| Field | Type | Required |
|---|---|---|
mode | PathTokenMode | yes |
path | string | yes |
terminal_pane_id | integer | yes |
request_command_preview
Ask the host to compute the command that would run for a given command string in the linked terminal. Doesn’t execu…
| Field | Type | Required |
|---|---|---|
command | string | yes |
request_id | string | yes |
terminal_pane_id | integer | yes |
open_artifact
Open a workspace artifact (file or directory) via the host.
| Field | Type | Required |
|---|---|---|
mode | ArtifactOpenMode | yes |
path | string | yes |
push_nav
App signals it has pushed a navigation level. The host appends the entry to its per-pane nav stack. While the stack h…
| Field | Type | Required |
|---|---|---|
title | string | yes |
view_id | string | yes |
pop_nav
App signals it has popped a navigation level. The host removes the top entry from the per-pane nav stack (saturating …
No additional fields.
set_mouse_tracking
Enable or disable PlexiEvent::MouseMove delivery for this pane.
| Field | Type | Required |
|---|---|---|
enabled | boolean | yes |
stream_process
Spawn command via sh -c and stream its output back to the app.
| Field | Type | Required |
|---|---|---|
channel | StreamChannel | yes |
command | string | yes |
correlation_id | string | yes |
terminal_pane_id | integer | yes |
cancel_process
Cancel an in-flight StreamProcess. The host sends SIGTERM to the child, waits up to 1s, then SIGKILL. A `PlexiEvent…
| Field | Type | Required |
|---|---|---|
correlation_id | string | yes |
open_file_picker
Show a native macOS file picker dialog. Requires fs.pick capability.
Capability: fs.pick
| Field | Type | Required |
|---|---|---|
filter | string[] | yes |
multiple | boolean | yes |
request_id | string | yes |
declare_event_streams
Declare the named event streams this app may emit on. Event names are app-defined but MUST be declared (with a JSON-S…
| Field | Type | Required |
|---|---|---|
streams | EventStreamDecl[] | yes |
emit_event
Emit a semantic app event into the host timeline.
| Field | Type | Required |
|---|---|---|
actor | AppEventActor | yes |
actor_id | string? | no |
caused_by | string? | no |
changed_resources | string[] | no |
event | string | yes |
payload | any | no |
resource_id | string | yes |
resource_scope | string? | no |
revision_after | string | yes |
revision_before | string? | no |
rollback_token | string? | no |
state_ref | string? | no |
suggested_trigger | variant | no |
summary | string | yes |
rollback_verify_result
App’s answer to PlexiEvent::RollbackVerify: the current revision of the queried resource. The host compares it agai…
| Field | Type | Required |
|---|---|---|
checkpoint_id | string | yes |
current_revision | string | yes |
subscribe_app_events
Subscribe this pane to another app’s declared event streams. Gated through the unified broker (`TargetType::AppEventS…
| Field | Type | Required |
|---|---|---|
app_id | string | yes |
event_names | string[] | no |
payload_mode | PayloadMode | yes |
request_id | string | yes |
resource_id | string? | no |
trigger_mode | TriggerMode | yes |
unsubscribe_app_events
Remove a subscription previously created by SubscribeAppEvents. Only the subscriber that owns it may remove it. Fir…
| Field | Type | Required |
|---|---|---|
subscription_id | string | yes |
list_undo_checkpoints
List undo checkpoints from the host undo timeline, newest first. app_id filters to one app; None = this app’s own…
| Field | Type | Required |
|---|---|---|
app_id | string? | no |
request_id | string | yes |
request_rollback
Request rollback of an undo checkpoint. Gated through the unified broker (TargetType::UndoCheckpoint). On allow, th…
| Field | Type | Required |
|---|---|---|
checkpoint_id | string | yes |
wake
No-op wake. Nudges the (zero-frame-idle) UI thread to run a frame so queued work — spawn-queue files, pane-IPC channe…
No additional fields.
Host → App Events
Messages the host sends to the app.
init
Sent exactly once on startup. App must reply with DrawCommand::Ready.
| Field | Type | Required |
|---|---|---|
app_id | string | yes |
args | string[] | no |
capabilities | string[] | yes |
compact_threshold | number | no |
feature_flags | string[] | yes |
height | number | no |
protocol | string | yes |
regular_threshold | number | no |
state | any | no |
theme | object | no |
width | number | no |
workspace_root | string | yes |
render
Request a new frame. App replies with DrawCommands terminated by FrameDone.
| Field | Type | Required |
|---|---|---|
canvas_height | number | no |
canvas_width | number | no |
frame_id | integer | yes |
rect | Rect | yes |
resize
Surface was resized. App should re-layout and request a new frame.
| Field | Type | Required |
|---|---|---|
height | number | yes |
width | number | yes |
key
User input event.
| Field | Type | Required |
|---|---|---|
key | string | yes |
modifiers | Modifiers | yes |
pressed | boolean | yes |
click
Mouse click at logical coordinates within the app surface.
| Field | Type | Required |
|---|---|---|
button | MouseButton | yes |
region | string? | no |
x | number | yes |
y | number | yes |
mouse_down
Pointer button pressed (fires on the frame the button goes down).
| Field | Type | Required |
|---|---|---|
button | MouseButton | yes |
modifiers | Modifiers | yes |
x | number | yes |
y | number | yes |
mouse_up
Pointer button released (fires on the frame the button goes up).
| Field | Type | Required |
|---|---|---|
button | MouseButton | yes |
modifiers | Modifiers | yes |
x | number | yes |
y | number | yes |
mouse_move
Pointer moved over the app surface. Only fires when the app has opted in via `DrawCommand::SetMouseTracking { enabled…
| Field | Type | Required |
|---|---|---|
buttons | MouseButton[] | yes |
modifiers | Modifiers | yes |
x | number | yes |
y | number | yes |
command
User submitted a command via the command bar.
| Field | Type | Required |
|---|---|---|
text | string | yes |
action
Semantic action dispatched by plexi app action <pane_id> <action> [args...]. Apps receive this in on_event and di…
| Field | Type | Required |
|---|---|---|
action | string | yes |
args | string[] | no |
capability_decision
Response to a runtime CapabilityRequest.
| Field | Type | Required |
|---|---|---|
capability | string | yes |
granted | boolean | yes |
request_id | string | yes |
secret_value
Secret broker response. value is None when denied.
| Field | Type | Required |
|---|---|---|
key | string | yes |
value | string? | no |
file_read_result
Native ProcessApp file read result.
| Field | Type | Required |
|---|---|---|
content | array? | no |
error | string? | no |
file_list_result
Native ProcessApp directory listing result.
| Field | Type | Required |
|---|---|---|
entries | array? | no |
error | string? | no |
run_update
Run lifecycle update from the host.
| Field | Type | Required |
|---|---|---|
payload | any | yes |
run_id | string | yes |
status | string | yes |
pipe_message
Typed pipe message (JSON mode only; binary mode travels on the side channel).
| Field | Type | Required |
|---|---|---|
payload | any | yes |
pipe_id | string | yes |
path_changed
Pane group CWD broadcast. Apps in the same group receive this when any member’s CWD changes.
| Field | Type | Required |
|---|---|---|
cwd | string | yes |
suspend
App is being backgrounded (host window losing focus, app no longer visible).
No additional fields.
resume
App is being foregrounded again.
No additional fields.
shutdown
App is being closed. Process must exit within a short timeout.
No additional fields.
theme
Host theme changed (config hot-reload or macOS system appearance toggle). App should update its color state; the next…
| Field | Type | Required |
|---|---|---|
colors | object | yes |
app_spawned
Confirmation that a SpawnApp request succeeded.
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
type_id | string | yes |
pane_spawned
Confirmation that a SpawnPane request succeeded (#592).
| Field | Type | Required |
|---|---|---|
pane_id | integer | yes |
request_id | string? | no |
pane_spawn_error
SpawnPane could not be fulfilled (#592). reason is a human-readable error.
| Field | Type | Required |
|---|---|---|
reason | string | yes |
request_id | string? | no |
context_state_response
Response to AppRequest::QueryContextState (#1518).
| Field | Type | Required |
|---|---|---|
state | ContextState | yes |
pipe_opened
Binary pipe opened — app connects to socket_path as a unix socket client.
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
socket_path | string | yes |
pipe_overrun
Binary pipe backpressure — host dropped dropped_frames frames from the ring.
| Field | Type | Required |
|---|---|---|
dropped_frames | integer | yes |
pipe_id | string | yes |
inject_state
Drop a JSON payload into the app’s on_inject hook. Sent at startup with persisted app state (workspace if available…
| Field | Type | Required |
|---|---|---|
payload | any | yes |
render_seed
DEPRECATED: superseded by the state field on Init. Kept for backwards compatibility with older SDK versions. The he…
| Field | Type | Required |
|---|---|---|
payload | any | yes |
http_response
Host broker response to a DrawCommand::HttpRequest. error is present when the request failed; body may still ca…
| Field | Type | Required |
|---|---|---|
body | string | yes |
error | string? | no |
request_id | string | yes |
status | integer | yes |
notify_action
Sent when the user responds to a notification that included a notify_id.
| Field | Type | Required |
|---|---|---|
action_label | string | yes |
notify_id | string | yes |
value | string? | no |
timer
Fired when a SetTimer timer expires.
| Field | Type | Required |
|---|---|---|
timer_id | string | yes |
image_loaded
Fired when a load_image request completes (success or failure). status is “ok” or “error”. message carries the …
| Field | Type | Required |
|---|---|---|
handle | string | yes |
message | string? | no |
status | string | yes |
text_measured
Response to a DrawCommand::MeasureText request. width and height are in logical pixels at the requested font size.
| Field | Type | Required |
|---|---|---|
height | number | yes |
request_id | string | yes |
width | number | yes |
text_wrapped_measured
Response to a ControlCommand::MeasureTextWrapped request. height is the pixel height of the text when wrapped at …
| Field | Type | Required |
|---|---|---|
height | number | yes |
request_id | string | yes |
paste
Clipboard paste forwarded into the focused app pane.
| Field | Type | Required |
|---|---|---|
text | string | yes |
ai_response
Response to a DrawCommand::AiQuery. Either content is Some (success) or error is Some (failure) — the two a…
| Field | Type | Required |
|---|---|---|
content | string? | no |
error | string? | no |
request_id | string | yes |
tokens_in | integer | yes |
tokens_out | integer | yes |
ai_stream_chunk
Incremental token chunk from a streaming ai_query response. Sent live while the turn runs, before the final `AiRespon…
| Field | Type | Required |
|---|---|---|
delta | string | yes |
done | boolean | no |
reasoning | string? | no |
request_id | string | yes |
tool_call
Host-to-app tool invocation (#399). The broker calls a tool exposed via DrawCommand::ExposeTools by sending this ev…
| Field | Type | Required |
|---|---|---|
call_id | string | yes |
caller_id | string | yes |
input_json | string | yes |
name | string | yes |
mcp_tool_call
External MCP client called a tool declared in [app.mcp]. The app must reply with `DrawCommand::Host(AppRequest::Mcp…
| Field | Type | Required |
|---|---|---|
arguments | any | yes |
call_id | string | yes |
tool_name | string | yes |
audio_devices_listed
Response to a DrawCommand::ListAudioDevices request (#277). Both vectors are always present — empty when enumeratio…
| Field | Type | Required |
|---|---|---|
error | string? | no |
inputs | AudioDeviceWire[] | yes |
outputs | AudioDeviceWire[] | yes |
request_id | string | yes |
audio_capture_started
Sent when a DrawCommand::AudioCapture successfully opened the device and started delivering PCM frames on pipe_id…
| Field | Type | Required |
|---|---|---|
buffer_size | integer | yes |
channels | integer | yes |
device_name | string | yes |
pipe_id | string | yes |
sample_rate | integer | yes |
audio_capture_error
Sent when a DrawCommand::AudioCapture could not be honoured — permission denied, bad device id, no devices, cpal fa…
| Field | Type | Required |
|---|---|---|
error | string | yes |
pipe_id | string | yes |
midi_devices_listed
Response to a DrawCommand::ListMidiDevices request (#320). Both vectors are always present — empty when CoreMIDI fi…
| Field | Type | Required |
|---|---|---|
error | string? | no |
inputs | MidiPortWire[] | yes |
outputs | MidiPortWire[] | yes |
request_id | string | yes |
midi_input_opened
Sent when a DrawCommand::OpenMidiInput successfully opened the port and started forwarding incoming MIDI byte strea…
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
port_id | string | yes |
port_name | string | yes |
midi_input_error
Sent when DrawCommand::OpenMidiInput could not be honoured — permission denied, port not found, CoreMIDI failure.
| Field | Type | Required |
|---|---|---|
error | string | yes |
pipe_id | string | yes |
midi_send_error
Sent when DrawCommand::SendMidi could not be honoured. Successful sends produce no event (fire-and-forget); only fa…
| Field | Type | Required |
|---|---|---|
error | string | yes |
port_id | string | yes |
video_open_ack
Sent when a DrawCommand::OpenVideo succeeded (#345). The host has allocated the binary pipe (look for the preceding…
| Field | Type | Required |
|---|---|---|
duration_ms | integer | yes |
fps | number | yes |
handle_id | integer | yes |
height | integer | yes |
request_id | string | yes |
width | integer | yes |
video_open_error
Sent when DrawCommand::OpenVideo could not be honoured (#345) — capability denied, source not found, decoder error,…
| Field | Type | Required |
|---|---|---|
error | string | yes |
request_id | string | yes |
linked_terminal_ready
Response to DrawCommand::RequestLinkedTerminal (#78). Carries the pane id of the freshly-opened terminal so subsequ…
| Field | Type | Required |
|---|---|---|
request_id | string | yes |
terminal_pane_id | integer | yes |
command_preview
Response to DrawCommand::RequestCommandPreview (#78). Returns the command verbatim plus the linked terminal’s curre…
| Field | Type | Required |
|---|---|---|
command | string | yes |
request_id | string | yes |
would_run_in_cwd | string | yes |
nav_back
Emitted by host to app when Escape is pressed and the app’s nav stack depth is > 0. The app handles this by popping i…
| Field | Type | Required |
|---|---|---|
view_id | string | yes |
file_picked
Response to DrawCommand::OpenFilePicker. At least one file was selected. paths contains the absolute paths chosen…
| Field | Type | Required |
|---|---|---|
paths | string[] | yes |
request_id | string | yes |
file_pick_cancelled
Response to DrawCommand::OpenFilePicker when the user cancelled the dialog without selecting a file, or the app lac…
| Field | Type | Required |
|---|---|---|
request_id | string | yes |
stream_chunk
Chunk of stdout/stderr bytes from an active DrawCommand::StreamProcess child. bytes is a raw byte array (values 0…
| Field | Type | Required |
|---|---|---|
bytes | integer[] | yes |
channel | StreamChannel | yes |
correlation_id | string | yes |
stream_end
Terminal event for a DrawCommand::StreamProcess child. Sent when the child exits, on CancelProcess, or on capabil…
| Field | Type | Required |
|---|---|---|
correlation_id | string | yes |
exit_code | integer | yes |
scroll_offset
Emitted by the host when the scroll offset for a BeginScroll region changes (mouse wheel, drag). The app should re-…
| Field | Type | Required |
|---|---|---|
id | string | yes |
offset_y | number | yes |
scroll
Emitted when the mouse wheel moves over an app pane that has no host-managed scroll region or list_view under the cur…
| Field | Type | Required |
|---|---|---|
delta_y | number | yes |
list_select
Emitted when j/k/up/down changes the list selection. id matches the list_view id field; index is the new select…
| Field | Type | Required |
|---|---|---|
id | string | yes |
index | integer | yes |
list_activate
Emitted when Enter is pressed on the selected item. id matches the list_view id field; index is the activated i…
| Field | Type | Required |
|---|---|---|
id | string | yes |
index | integer | yes |
component_event
Fired when a user interacts with a node that has Interactive wrapping or when a Button/Input node is activated.
| Field | Type | Required |
|---|---|---|
event_type | string | yes |
node_id | string | yes |
payload | any | no |
rollback_verify
Host asks the app whether resource_id is still at expected_revision before rolling back a checkpoint. The app mus…
| Field | Type | Required |
|---|---|---|
checkpoint_id | string | yes |
expected_revision | string | yes |
resource_id | string | yes |
rollback_apply
Host instructs the app to roll resource_id back using the rollback_token the app supplied when it emitted the rev…
| Field | Type | Required |
|---|---|---|
checkpoint_id | string | yes |
resource_id | string | yes |
rollback_token | string | yes |
app_events_subscribed
Response to AppRequest::SubscribeAppEvents. Exactly one of subscription_id / error is set.
| Field | Type | Required |
|---|---|---|
error | string? | no |
request_id | string | yes |
subscription_id | string? | no |
app_event
One subscribed app event, delivered to the subscriber pane. Content beyond the event identity is shaped by the subscr…
| Field | Type | Required |
|---|---|---|
app_id | string | yes |
created_at | string | yes |
event | string | yes |
event_id | integer | yes |
payload | any | no |
resource_id | string | yes |
state_ref | string? | no |
subscription_id | string | yes |
summary | string? | no |
trigger_mode | TriggerMode | yes |
undo_checkpoints
Response to AppRequest::ListUndoCheckpoints: undo checkpoints, newest first, serialized with the spec’s checkpoint …
| Field | Type | Required |
|---|---|---|
checkpoints | any[] | yes |
request_id | string | yes |
Render Commands
Render primitives — go to pending_frame → drawn to screen.
Draw primitives the app emits each frame.
push_clip
Push a clip rect onto the host’s clip stack.
| Field | Type | Required |
|---|---|---|
h | number | yes |
w | number | yes |
x | number | yes |
y | number | yes |
pop_clip
Pop the most recently pushed clip rect from the stack.
No additional fields.
rect
Fill a rectangle.
| Field | Type | Required |
|---|---|---|
fill | string | yes |
glow_color | string? | no |
glow_radius | number | no |
gradient | variant | no |
h | number | yes |
hit_region | string? | no |
radius | number | no |
stroke | string? | no |
stroke_width | number | no |
w | number | yes |
x | number | yes |
y | number | yes |
text
Draw text at a position.
| Field | Type | Required |
|---|---|---|
align | TextAlign | no |
bold | boolean | no |
color | string | yes |
elide | boolean | yes |
hit_region | string? | no |
max_lines | integer? | no |
max_width | number? | no |
monospace | boolean | no |
selectable | boolean | yes |
size | number | yes |
text | string | yes |
x | number | yes |
y | number | yes |
line
Draw a line segment.
| Field | Type | Required |
|---|---|---|
color | string | yes |
width | number | no |
x1 | number | yes |
x2 | number | yes |
y1 | number | yes |
y2 | number | yes |
circle
Draw a filled circle. Alpha is supported via 8-digit hex fill (#rrggbbaa).
| Field | Type | Required |
|---|---|---|
cx | number | yes |
cy | number | yes |
fill | string | yes |
glow_color | string? | no |
glow_radius | number | no |
r | number | yes |
stroke | string? | no |
stroke_width | number | no |
arc
Draw a filled arc / pie slice. start_angle and end_angle are in radians, measured clockwise from the right (east)…
| Field | Type | Required |
|---|---|---|
cx | number | yes |
cy | number | yes |
end_angle | number | yes |
fill | string | yes |
r | number | yes |
start_angle | number | yes |
arc_ring
Draw a stroked arc ring (hollow donut arc). Distinct from Arc (filled pie).
| Field | Type | Required |
|---|---|---|
color | string | yes |
cx | number | yes |
cy | number | yes |
end_angle | number | yes |
r | number | yes |
start_angle | number | yes |
stroke_width | number | no |
list
High-level scrollable list — host handles layout and scrolling.
| Field | Type | Required |
|---|---|---|
h | number | no |
item_height | number | no |
items | ListItem[] | yes |
selected | integer | yes |
w | number | no |
x | number | no |
y | number | no |
list_view
Host-native scrollable list with j/k navigation, typed row slots, and built-in loading/error/empty states. Host emits…
| Field | Type | Required |
|---|---|---|
error | string? | no |
h | number | no |
id | string | yes |
items | ListViewItem[] | yes |
loading | boolean | no |
selected | integer | no |
w | number | no |
x | number | no |
y | number | no |
badge
Host-rendered pill badge. The host measures the label with real font metrics, sizes the pill (text_w + padding), and …
| Field | Type | Required |
|---|---|---|
fg | string | yes |
fill | string | yes |
font_size | number | yes |
label | string | yes |
radius | number | yes |
x | number | yes |
y | number | yes |
key_chip
Host-rendered keycap chip. The host measures the label with real monospace font metrics, sizes the chip, and centres …
| Field | Type | Required |
|---|---|---|
font_size | number | yes |
label | string | yes |
x | number | yes |
y | number | yes |
key_chip_row
A horizontal row of keycap chips. The host flows them left-to-right with a fixed 2px gap between chips, sizes each ch…
| Field | Type | Required |
|---|---|---|
description | string? | no |
font_size | number | yes |
keys | string[] | yes |
x | number | yes |
y | number | yes |
shortcuts
A multi-group shortcut row. The host owns all layout — chip widths from real font metrics, flow horizontally with con…
| Field | Type | Required |
|---|---|---|
align | ShortcutsAlign | no |
font_size | number | yes |
max_width | number | yes |
pairs | ShortcutPair[] | yes |
x | number | yes |
y | number | yes |
text_row
Multiple text segments rendered horizontally with host-measured layout. The host measures each segment with real font…
| Field | Type | Required |
|---|---|---|
align | TextAlign | no |
gap | number | yes |
items | TextRowItem[] | yes |
x | number | yes |
y | number | yes |
markdown
Render markdown text using the host’s egui_commonmark renderer.
| Field | Type | Required |
|---|---|---|
base_size | number | yes |
color | string | yes |
text | string | yes |
w | number | yes |
x | number | yes |
y | number | yes |
image
Draw an image from a workspace-scoped path or data URL.
| Field | Type | Required |
|---|---|---|
fit | string | no |
h | number | yes |
src | string | yes |
w | number | yes |
x | number | yes |
y | number | yes |
avatar
Circular clipped image. src accepts a load_image handle (UUID) or local path. cx, cy are the circle centre in p…
| Field | Type | Required |
|---|---|---|
cx | number | yes |
cy | number | yes |
radius | number | yes |
src | string | yes |
skeleton
Animated shimmer placeholder rect for loading states. The host drives a subtle pulsing animation at ~20fps using requ…
| Field | Type | Required |
|---|---|---|
h | number | yes |
radius | number | no |
w | number | yes |
x | number | yes |
y | number | yes |
audio_meter
Render an amplitude meter reading from a binary pipe.
| Field | Type | Required |
|---|---|---|
pipe_id | string | yes |
rect | Rect | yes |
begin_scroll
Begin a host-managed vertical scroll region.
| Field | Type | Required |
|---|---|---|
content_height | number | yes |
h | number | yes |
id | string | yes |
w | number | yes |
x | number | yes |
y | number | yes |
end_scroll
Close the most recently opened scroll region.
No additional fields.
layout
Declarative flex layout tree. The host resolves all positions using taffy (flexbox) and real egui font metrics before…
| Field | Type | Required |
|---|---|---|
children | LayoutChild[] | yes |
direction | LayoutDirection | yes |
gap | number | no |
x | number | yes |
y | number | yes |
responsive
Responsive layout — host picks the matching tier based on available rect aspect ratio.
| Field | Type | Required |
|---|---|---|
tiers | ResponsiveTier[] | yes |
x | number | yes |
y | number | yes |
component_tree
Component tree — host renders via render_component_tree(). Added in PGAP v3.5. Host logs a warning and drops if it do…
| Field | Type | Required |
|---|---|---|
root | UiNode | yes |
Control Commands
Inline-handled commands (processed directly in ui() or background_tick()).
Inline commands processed outside the render loop.
ready
SDK ready handshake. Sent once by the app after receiving Init. Host captures sdk and features_used; the message is o…
| Field | Type | Required |
|---|---|---|
features_used | string[] | no |
protocol_version | string | no |
sdk | string | no |
frame_done
End of frame. Host renders everything queued since last FrameDone.
| Field | Type | Required |
|---|---|---|
frame_id | integer | yes |
log
Forward a log message into Plexi’s logger (tagged with app_id).
| Field | Type | Required |
|---|---|---|
level | string | yes |
message | string | yes |
fatal_error
Terminal SDK failure with traceback text. Sent before the app exits nonzero so the host never has to infer Python hoo…
| Field | Type | Required |
|---|---|---|
message | string | yes |
traceback | string | yes |
schedule_render
Ask the host to trigger a new Render event after after_ms milliseconds. Intended for game loops and animations — em…
| Field | Type | Required |
|---|---|---|
after_ms | integer | yes |
set_scheduler_mode
Declare app render cadence so the host owns recurring animation ticks. mode="idle" disables recurring renders, `mod…
| Field | Type | Required |
|---|---|---|
fps | integer? | no |
mode | string | yes |
copy_to_clipboard
Write text to the OS clipboard.
| Field | Type | Required |
|---|---|---|
text | string | yes |
measure_text
Request a one-shot text measurement. The host measures text at font_size with the proportional font and replies i…
| Field | Type | Required |
|---|---|---|
font_size | number | yes |
monospace | boolean | no |
request_id | string | yes |
text | string | yes |
measure_text_wrapped
Measure the height of text wrapped at max_width using real host font metrics. If max_lines is Some(n), clamps t…
| Field | Type | Required |
|---|---|---|
font_size | number | yes |
max_lines | integer? | no |
max_width | number | yes |
request_id | string | yes |
text | string | yes |
set_min_size
Override the manifest-declared minimum size at runtime. Stored by the host and used as the live effective minimum fro…
| Field | Type | Required |
|---|---|---|
height | number | yes |
width | number | yes |
close_self
Request the host to close this app’s pane gracefully. The host closes the pane on the next frame via the wants_close …
No additional fields.
set_title
Set the display name (tab title) of this app’s own pane. Emitted by the SDK v3 runtime when the app calls `emit.set_t…
| Field | Type | Required |
|---|---|---|
title | string | yes |