Files
orca/docs/reference/plugin-host-api.md
T
Neil 255b5c22c1 feat(plugins): host API 1.8 — create workspaces, resume agents, automations (#41528)
* feat(plugins): proposed tier for host methods not yet frozen

A method spec may set `proposed: true`: only development plugins may call
it (others get not_permitted), and the minor, frame/worker method lists,
typings and skill references omit it. The reference renders such methods
in a separate Proposed section; dev workers' host.has answers for them.

* feat(plugins): param capabilities, host confirmations, automation provenance

- PluginHostMethodSpec.paramCapabilities: a passed param can require a
  second capability; executePluginHostCall and the mock host refuse it with
  capability_denied before any audit or effect, and the reference shows it.
- confirmPluginAction generalizes the external-open prompt into a host-owned
  confirmation, exposed as PluginHostServices.confirm (desktop only; absent
  in headless serve). It re-checks the plugin's approval after the answer.
- Automations record createdBy { kind: 'plugin', pluginKey, name }, set by
  the runtime from the in-process plugin caller (RpcContext.plugin), never
  from params; the Automations detail shows "Created by <plugin>".

* feat(plugins): workspace:create, automation:read and automation:write capabilities

New closed-enum kinds (older Orcas refuse such manifests with the engines
message), worker-only, with consent copy that names what each allows;
release floor PLUGIN_HOST_API_1_8_SINCE (1.4.259) joins
PLUGIN_SCHEMA_NEWEST_SINCE. agent:launch copy now says it also resumes.

* feat(plugins): host API 1.8 methods: workspace.create, agent.resume, automation.*

- workspace.create (workspace:create; agent also needs agent:launch): a
  worktree in a Git project, as orca worktree create does; with agent, the
  CLI's agent.launch create-worktree path, so the agent starts after setup.
  Setup is skipped or follows the project's settings; never run, an
  approved hash or a startup command.
- agent.resume (agent:launch): terminal.ensureAgentSession by session id;
  started is false when a terminal already ran it. agents.list rows and
  agent.status.changed carry sessionId.
- automation.list/create/update/delete (automation:read/write): create
  forwards only name, prompt, agent, target, schedule, time zone and
  enabled; update/delete of automations another plugin or the person made
  ask the person first and report cancelled or consent-unavailable.

* test(plugins): host API 1.8 coverage; regenerate plugin artifacts

Covers param capabilities (host and mock host), workspace.create payloads
and refusals, agent.resume keys and adoption, sessionId on agents.list,
automation projection, forbidden create fields, confirmation outcomes,
runtime-set provenance and capability access growth. Regenerates the
reference, schemas, typings, declarative runtime and skill references.

* fix(plugins): host API 1.8 review fixes

- Confirmations spend the frame dialog budget, allow one question per
  plugin at a time and pause after a refusal (rate_limited); a worker's
  question shows in the focused window, else the first visible one.
- Changing what an automation with extra agent settings runs (prompt,
  schedule, resuming) asks first even for its creator, showing the prompt.
- Removing, disabling or blocking a plugin pauses its automations and ends
  its ownership (createdBy.endedAt), so neither re-enabling nor a reinstall
  under the key resumes or edits them silently; the remove dialog says so.
- workspace.create and agent.resume record the terminals they return as
  the plugin's own; a launch that fails after its worktree exists names it.
- Setup-decision refusals are told by the runtime's error class (kept as
  the in-process RPC error's cause); generated names see every worktree
  of the project; resume transcripts match the workspace and host.
- Created by shows the plugin id beside its name.

* fix(plugins): end automation ownership only on uninstall, disable or block

- The ownership sweep no longer reads discovery: only an uninstall
  (announced by the removal path), the disabled list, the kill list or
  the plugin-system switch end it, and only once the service is ready,
  so an unreadable plugins folder pauses nothing. An install under the
  same key after a removal keeps what it creates.
- A new time zone on an automation with extra agent settings asks first.
- A failed launch names only a new worktree made from the requested name.
- Settings' plugins-off state and the CLI's plugin system off and plugin
  disable say that plugin-created automations are paused and stay so.

* fix(plugins): address host API 1.8 review threads

- Confirmations close when the worker's call ends, freeing the plugin's
  turn; they name the automation (short id, project, workspace,
  schedule), show any new prompt, and are refused as `changed` when the
  automation was edited while the person was asked.
- automation.create takes hostId (through the project's setup on that
  host) and a creationKey scoped to the plugin; the runtime rechecks the
  plugin's approval right before the write.
- agent.resume records only terminals it started; both it and
  workspace.create recheck approval before recording.
- automation.list never cuts a schedule (null when too long) or fails on
  an id too long to show.
- The Automations detail says when a creator plugin was removed or
  disabled.
2026-11-12 05:28:13 -08:00

33 KiB

Orca plugin host API reference

Host API 1.8 ("pluginApi": 1). Workers call a method with await orca.host.call(name, params); panels and custom editors call the same methods through window.orca.host.call(name, params). Each method lists the surfaces allowed to call it; any other surface gets not_permitted. orca.host.has(name) answers synchronously whether this Orca offers a method on the current surface, and orca.api is { major, minor }. See Plugin authoring for the frame API.

Every call is checked against the capabilities the user approved, and params are validated before Orca runs anything: params are strict, so a field this Orca does not know is refused with invalid_params instead of being dropped. Mutations are recorded in the audit log as plugin:<publisher>.<id>.

Methods whose capability is none need no capability but still need the plugin enabled and approved. shell.openExternal and files.saveAs also need transient user activation (a click or key press in the calling frame, within a few seconds), are rate-limited, and run only in the desktop app; frames also reach them as orca.shell.openExternal(url) and orca.files.saveAs({ suggestedName, mimeType, data }), which resolves with { status: "saved" }, or { status: "cancelled" } when the person cancels the dialog. Saved files carry the OS mark for downloaded files.

Methods

Method Capability Surfaces Mutation Since Summary
workspace.readContext workspace:read worker, panel, editor no 1.0 Id, branch, name and terminal ids of the focused workspace, or null when none is focused.
terminal.sendText terminal:send worker yes 1.0 Types text into one terminal named by id, optionally pressing Enter.
notifications.show notifications:show worker, panel, editor yes 1.0 Shows a desktop notification.
storage.get storage:own worker, panel, editor no 1.0 Reads a JSON value from the plugin's private store; null when absent.
storage.set storage:own worker, panel, editor yes 1.0 Writes a JSON value to the plugin's private store.
storage.delete storage:own worker, panel, editor yes 1.0 Removes a key from the plugin's private store.
storage.keys storage:own worker, panel, editor no 1.0 Lists the keys in the plugin's private store.
secrets.get secrets:own worker, panel, editor no 1.0 Reads a secret from the plugin's encrypted secrets file; null when absent.
secrets.set secrets:own worker, panel, editor yes 1.0 Stores a secret in the plugin's encrypted secrets file.
secrets.delete secrets:own worker, panel, editor yes 1.0 Removes a secret from the plugin's encrypted secrets file.
secrets.protection secrets:own worker, panel, editor no 1.1 How secrets are protected at rest: os-keyring, or unavailable (no OS key store, or Linux basic_text), where secrets.set fails.
settings.get settings:own worker, panel, editor no 1.0 Reads every setting the plugin owns.
settings.set settings:own worker, panel, editor yes 1.0 Writes one setting the plugin owns.
shell.openExternal none panel, editor no 1.1 Opens an http:, https: or mailto: URL in the default browser. Needs a click or key press in the frame; desktop app only. Orca asks the person before an http(s) host the plugin was not allowed; opened is false if they cancel.
files.saveAs none panel, editor yes 1.1 Offers data (a string or ArrayBuffer, up to 64 MiB) in Orca's save dialog; the plugin never learns the path. Needs a click or key press in the frame; desktop app only.
plugin.request none panel, editor no 1.2 Asks the plugin's own worker to run the handler it registered with orca.panels.handle(name), starting the worker if needed; resolves with what the handler returned. unknown_method when no handler has that name.
agents.list agent:read worker, panel, editor no 1.2 The agents in the focused workspace: their terminal, title, agent kind and status. agent.status.changed reports each change after.
events.subscribe none panel, editor no 1.2 Delivers a workspace, agent or editor event to this panel as orca.on(event); needs the event's read capability, when it has one. The frame APIs call it for you.
statusBar.update none worker no 1.2 Changes one of the plugin's contributes.statusBarItems (named by id): fields given replace their current value, visible: false hides it. Desktop app only; lasts until Orca restarts, and is hidden while the plugin is disabled. A plugin with main and status bar items has its worker started when it is enabled and kept running while it stays enabled, so a worker can poll and update its items without an event or a hook.
appearance.read appearance:write worker no 1.4 Reads the current desktop appearance revision for a conditional update.
appearance.apply appearance:write worker yes 1.4 Publishes owned app/editor and terminal colors and selects them in the desktop. Returns publication, not renderer acknowledgement.
workspace.list workspace:read worker no 1.3 Lists every workspace, Git worktrees and folders, on every host Orca reaches; includeStatus (host API 1.7) adds each one's agent status. hostId is the host a row records (local, ssh:…, runtime:…), or null when it records none, as for a project-group folder on this machine.
project.list workspace:read worker no 1.7 Lists your projects (repositories and folders) on every host; id is what workspace.list rows name as projectId. hostId is always named: local, ssh:… or runtime:….
workspace.open workspace:write worker yes 1.3 Shows a workspace in Orca, as clicking it in the sidebar does; a folder workspace needs the desktop app.
workspace.rename workspace:write worker yes 1.5 Renames a workspace in Orca's sidebar, as renaming it by hand does; the name stays until it is renamed again.
terminal.create terminal:send worker yes 1.3 Opens a terminal in a workspace, optionally running a command, in the background.
terminal.read terminal:read worker no 1.3 Reads a terminal's newest output as plain text, at most 2000 lines.
agent.launch agent:launch worker yes 1.3 Starts an agent's CLI in a new terminal of a workspace, in the background, and returns as soon as it starts; a prompt is sent once the agent is ready.
editor.open workspace:write worker yes 1.3 Opens a file of a workspace in an editor tab of Orca's window; desktop app only. workspace.open shows the workspace.
commands.run workspace:write worker yes 1.3 Runs one of Orca's built-in actions in its window, never a plugin's replacement of it; desktop app only.
terminal.list workspace:read worker no 1.7 Lists a workspace's terminals, at most 50; workspace.readContext lists the focused one's.
terminal.focus workspace:write worker yes 1.7 Shows any terminal's workspace and focuses its pane, as clicking it does; desktop app only. focused is false when a newer focus request won or the window did not move to it.
terminal.close terminal:send worker yes 1.7 Closes a terminal of the focused workspace, or one this plugin opened (terminal.create, agent.launch), stopping what runs in it (local or SSH); closing its last pane closes the tab. Any other terminal is refused with action_failed. A plugin keeps its terminals until it is disabled or removed.
workspace.create workspace:create (+agent:launch for agent) worker yes 1.8 Creates a workspace (a new branch and worktree) in a Git project, local or SSH, and returns its id; with agent, starts that agent there once setup has run, in the background unless open; if the agent fails after the workspace exists, the error names its id. Orca's workspace-create and agent hooks run as for any create.
agent.resume agent:launch worker yes 1.8 Resumes an agent's earlier session by its sessionId in a new terminal of a workspace, in the background; started is false when an Orca terminal already runs it. In WSL it uses the distro's default agent account, as agent.launch does.
automation.list automation:read worker no 1.8 Lists the automations this Orca schedules, at most 200, with each one's prompt cut to 4096 characters.
automation.create automation:write worker yes 1.8 Creates an automation that starts agent with prompt on schedule, even when the plugin is not running, with the person's settings for that agent; Orca's Automations page shows it as created by the plugin.
automation.update automation:write worker yes 1.8 Changes an automation's name, prompt, schedule, time zone or whether it is enabled. Orca first asks the person about an automation this plugin did not create (or created before it was removed or disabled); if they decline or cannot be asked, or it changes while they are asked, nothing changes and reason says why. Orca also asks before a new prompt or schedule, or resuming, for any automation whose runs use extra agent settings the person chose.
automation.delete automation:write worker yes 1.8 Deletes an automation. Orca first asks the person about an automation this plugin did not create (or created before it was removed or disabled); if they decline or cannot be asked, or it changes while they are asked, nothing changes and reason says why.
editor.readTypography none worker, panel, editor no 1.6 The font family, size, weight, line height, ligatures, word wrap and minimap Orca's file editor uses now, editor zoom included. editor.typography.changed reports each change after.

workspace.readContext

Id, branch, name and terminal ids of the focused workspace, or null when none is focused.

(params?: {}) => { workspaceId?: string; branch: string; displayName: string; terminals: { id: string; title?: string }[] } | null

terminal.sendText

Types text into one terminal named by id, optionally pressing Enter.

(params: { terminalId: string; text: string; enter?: boolean }) => { accepted: boolean }

notifications.show

Shows a desktop notification.

(params: { title: string; body?: string }) => { delivered: boolean }

storage.get

Reads a JSON value from the plugin's private store; null when absent.

(params: { key: string }) => { value: JSON }

storage.set

Writes a JSON value to the plugin's private store.

(params: { key: string; value: JSON }) => { ok: true }

storage.delete

Removes a key from the plugin's private store.

(params: { key: string }) => { ok: true }

storage.keys

Lists the keys in the plugin's private store.

(params?: {}) => { keys: string[] }

secrets.get

Reads a secret from the plugin's encrypted secrets file; null when absent.

(params: { key: string }) => { value: string | null }

secrets.set

Stores a secret in the plugin's encrypted secrets file.

(params: { key: string; value: string }) => { ok: true }

secrets.delete

Removes a secret from the plugin's encrypted secrets file.

(params: { key: string }) => { ok: true }

secrets.protection

How secrets are protected at rest: os-keyring, or unavailable (no OS key store, or Linux basic_text), where secrets.set fails.

(params?: {}) => { status: "os-keyring" | "unavailable" }

settings.get

Reads every setting the plugin owns.

(params?: {}) => { settings: Record<string, JSON> }

settings.set

Writes one setting the plugin owns.

(params: { key: string; value: JSON }) => { ok: true }

shell.openExternal

Opens an http:, https: or mailto: URL in the default browser. Needs a click or key press in the frame; desktop app only. Orca asks the person before an http(s) host the plugin was not allowed; opened is false if they cancel.

(params: { url: string }) => { opened: boolean; reason?: "cancelled" | "consent-unavailable" }

files.saveAs

Offers data (a string or ArrayBuffer, up to 64 MiB) in Orca's save dialog; the plugin never learns the path. Needs a click or key press in the frame; desktop app only.

(params: { suggestedName?: string; mimeType?: string; data: string | ArrayBuffer }) => { status: "saved" | "cancelled" }

plugin.request

Asks the plugin's own worker to run the handler it registered with orca.panels.handle(name), starting the worker if needed; resolves with what the handler returned. unknown_method when no handler has that name.

(params: { name: string; input?: JSON; timeoutMs?: number }) => { output: JSON }

agents.list

The agents in the focused workspace: their terminal, title, agent kind and status. agent.status.changed reports each change after.

(params?: {}) => { agents: { terminalId: string | null; title: string | null; agent: string | null; state: "working" | "blocked" | "waiting" | "done"; stateStartedAt: number; sessionId?: string | null }[] }

events.subscribe

Delivers a workspace, agent or editor event to this panel as orca.on(event); needs the event's read capability, when it has one. The frame APIs call it for you.

(params: { event: "workspace.created" | "workspace.removed" | "agent.status.changed" | "workspace.changed" | "editor.typography.changed" }) => { ok: true }

statusBar.update

Changes one of the plugin's contributes.statusBarItems (named by id): fields given replace their current value, visible: false hides it. Desktop app only; lasts until Orca restarts, and is hidden while the plugin is disabled. A plugin with main and status bar items has its worker started when it is enabled and kept running while it stays enabled, so a worker can poll and update its items without an event or a hook.

(params: { id: string; text?: string; icon?: "activity" | "bar-chart-3" | "bell" | "blocks" | "book" | "bot" | "bug" | "calendar" | "cloud" | "code" | "database" | "file-text" | "flag" | "folder" | "gauge" | "globe" | "hammer" | "layers" | "lightbulb" | "package" | "plug" | "puzzle" | "rocket" | "star" | "terminal" | "wrench" | "zap" | "add" | "remove" | "delete" | "edit" | "refresh" | "search" | "settings" | "check" | "close" | "info" | "warning" | "error" | "open-external" | "copy" | "download" | "upload" | "play" | "pause" | "stop" | "more" | "expand" | "collapse" | "next" | "filter" | "branch" | "file" | "agent" | "link"; tone?: "neutral" | "accent" | "success" | "warning" | "danger"; tooltip?: string; visible?: boolean }) => { ok: true }

appearance.read

Reads the current desktop appearance revision for a conditional update.

(params?: {}) => { revision: string }

appearance.apply

Publishes owned app/editor and terminal colors and selects them in the desktop. Returns publication, not renderer acknowledgement.

(params: { expectedRevision: string; app: string; terminal: string }) => { revision: string; themeId: string; mode: "dark" | "light"; delivery: "published" }

workspace.list

Lists every workspace, Git worktrees and folders, on every host Orca reaches; includeStatus (host API 1.7) adds each one's agent status. hostId is the host a row records (local, ssh:…, runtime:…), or null when it records none, as for a project-group folder on this machine.

(params?: { includeStatus?: boolean }) => { workspaces: { id: string; kind: "worktree" | "folder"; name: string; project?: string | null; projectId?: string | null; path: string; branch: string | null; hostId: string | null; status?: { state: "working" | "permission" | "done" | "active" | "inactive"; unread: boolean; terminalCount: number; lastActivityAt: number | null } }[]; truncated: boolean; statusTruncated?: boolean }

project.list

Lists your projects (repositories and folders) on every host; id is what workspace.list rows name as projectId. hostId is always named: local, ssh:… or runtime:….

(params?: {}) => { projects: { id: string; name: string; kind: "git" | "folder"; hostId: string | null; path: string }[]; truncated: boolean }

workspace.open

Shows a workspace in Orca, as clicking it in the sidebar does; a folder workspace needs the desktop app.

(params: { workspaceId: string }) => { opened: true }

workspace.rename

Renames a workspace in Orca's sidebar, as renaming it by hand does; the name stays until it is renamed again.

(params: { workspaceId: string; name: string }) => { name: string }

terminal.create

Opens a terminal in a workspace, optionally running a command, in the background.

(params: { workspaceId: string; title?: string; command?: string }) => { terminalId: string }

terminal.read

Reads a terminal's newest output as plain text, at most 2000 lines.

(params: { terminalId: string; maxLines?: number }) => { lines: string[]; truncated: boolean }

agent.launch

Starts an agent's CLI in a new terminal of a workspace, in the background, and returns as soon as it starts; a prompt is sent once the agent is ready.

(params: { workspaceId: string; agent: string; prompt?: string }) => { terminalId: string }

editor.open

Opens a file of a workspace in an editor tab of Orca's window; desktop app only. workspace.open shows the workspace.

(params: { workspaceId: string; path: string }) => { opened: true }

commands.run

Runs one of Orca's built-in actions in its window, never a plugin's replacement of it; desktop app only.

(params: { action: "worktree.history.back" | "worktree.history.forward" | "sidebar.left.toggle" | "sidebar.sleepingWorkspaces.toggle" | "floatingWorkspace.maximize" | "tab.rename" | "workspace.rename" | "workspace.openBoard" | "view.tasks" | "sidebar.right.toggle" | "sidebar.explorer.toggle" | "sidebar.search.toggle" | "sidebar.sourceControl.toggle" | "sidebar.checks.toggle" | "sidebar.ports.toggle" }) => { ran: true }

terminal.list

Lists a workspace's terminals, at most 50; workspace.readContext lists the focused one's.

(params: { workspaceId: string }) => { terminals: { id: string; title?: string }[]; truncated: boolean }

terminal.focus

Shows any terminal's workspace and focuses its pane, as clicking it does; desktop app only. focused is false when a newer focus request won or the window did not move to it.

(params: { terminalId: string }) => { focused: boolean }

terminal.close

Closes a terminal of the focused workspace, or one this plugin opened (terminal.create, agent.launch), stopping what runs in it (local or SSH); closing its last pane closes the tab. Any other terminal is refused with action_failed. A plugin keeps its terminals until it is disabled or removed.

(params: { terminalId: string }) => { closed: true }

workspace.create

Creates a workspace (a new branch and worktree) in a Git project, local or SSH, and returns its id; with agent, starts that agent there once setup has run, in the background unless open; if the agent fails after the workspace exists, the error names its id. Orca's workspace-create and agent hooks run as for any create.

(params: { projectId: string; hostId?: string; name?: string; baseBranch?: string; agent?: string; prompt?: string; setup?: "skip"; open?: boolean }) => { workspaceId: string; terminalId?: string }

agent.resume

Resumes an agent's earlier session by its sessionId in a new terminal of a workspace, in the background; started is false when an Orca terminal already runs it. In WSL it uses the distro's default agent account, as agent.launch does.

(params: { workspaceId: string; agent: "claude" | "codebuddy" | "codex" | "qoder" | "qoder-cn" | "qwen-code" | "gemini" | "antigravity" | "opencode" | "opencode2" | "pi" | "mimo-code" | "droid" | "grok" | "devin" | "omp" | "prime-agent" | "copilot" | "cursor" | "kimi" | "muse" | "zcode" | "dsh" | "jcode" | "kiro" | "hermes"; sessionId: string }) => { terminalId: string; started: boolean }

automation.list

Lists the automations this Orca schedules, at most 200, with each one's prompt cut to 4096 characters.

(params?: {}) => { automations: { id: string; name: string; agent: string; prompt: string; promptTruncated: boolean; projectId: string | null; hostId: string | null; workspaceId: string | null; workspaceMode: "existing" | "new-per-run"; schedule: string | null; timezone: string; enabled: boolean; nextRunAt: number | null; lastRunAt: number | null; createdByThisPlugin: boolean }[]; truncated: boolean }

automation.create

Creates an automation that starts agent with prompt on schedule, even when the plugin is not running, with the person's settings for that agent; Orca's Automations page shows it as created by the plugin.

(params: { name: string; prompt: string; agent: string; projectId?: string; hostId?: string; workspaceId?: string; schedule: string; timezone?: string; enabled?: boolean; creationKey?: string }) => { automationId: string }

automation.update

Changes an automation's name, prompt, schedule, time zone or whether it is enabled. Orca first asks the person about an automation this plugin did not create (or created before it was removed or disabled); if they decline or cannot be asked, or it changes while they are asked, nothing changes and reason says why. Orca also asks before a new prompt or schedule, or resuming, for any automation whose runs use extra agent settings the person chose.

(params: { automationId: string; name?: string; prompt?: string; schedule?: string; timezone?: string; enabled?: boolean }) => { updated: boolean; reason?: "cancelled" | "consent-unavailable" | "changed" }

automation.delete

Deletes an automation. Orca first asks the person about an automation this plugin did not create (or created before it was removed or disabled); if they decline or cannot be asked, or it changes while they are asked, nothing changes and reason says why.

(params: { automationId: string }) => { deleted: boolean; reason?: "cancelled" | "consent-unavailable" | "changed" }

editor.readTypography

The font family, size, weight, line height, ligatures, word wrap and minimap Orca's file editor uses now, editor zoom included. editor.typography.changed reports each change after.

(params?: {}) => { fontFamily: string; cssFontFamily: string; fontSize: number; fontWeight: string; lineHeight: number; fontLigatures: boolean; wordWrap: "on" | "off"; minimap: boolean }

Events

A worker receives the events its manifest lists in contributes.events through orca.events.on(event, handler), which returns { dispose() }. Each needs main, the trusted-worker profile and its domain's capability (workspace:read or agent:read; editor events need none). orca plugin types writes the payloads as PluginEvents in orca/host.

Event Payload Summary
workspace.created { workspaceId: string; workspaceListId?: string; kind: "worktree" | "folder"; path: string; branch: string } A worktree workspace was created; workspaceListId (host API 1.7) is the id workspace.list gives it.
workspace.removed { workspaceId: string; workspaceListId?: string; kind: "worktree" | "folder"; path: string } A worktree workspace was removed; workspaceListId (host API 1.7) is the id workspace.list gave it.
agent.status.changed { workspaceId: string | null; workspaceListId?: string; paneKey: string; terminalId?: string; title?: string; state: "working" | "blocked" | "waiting" | "done"; receivedAt: number; mainAgent?: { state: "working" | "blocked" | "waiting" | "done"; outcome?: "success" | "failure" | "cancellation" | "superseded" | "interruption" | "unconfirmed"; stateStartedAt: number }; sessionId?: string } An agent in a terminal pane changed status; workspaceId is null when Orca cannot tie the pane to a workspace, workspaceListId (host API 1.7) is the id workspace.list gives that workspace, terminalId names its terminal when Orca lists it, and sessionId (host API 1.8) is what agent.resume takes.
workspace.changed { context: { workspaceId?: string; branch: string; displayName: string; terminals: { id: string; title?: string }[] } | null } The focused workspace changed, or its terminals were opened, closed or renamed; context is what workspace.readContext now answers, its workspaceId included. Each new subscriber first gets the current context.
editor.typography.changed { fontFamily: string; cssFontFamily: string; fontSize: number; fontWeight: string; lineHeight: number; fontLigatures: boolean; wordWrap: "on" | "off"; minimap: boolean } What Orca's file editor uses changed (a setting or the editor zoom); the payload is what editor.readTypography now answers. Needs no capability.

Hooks

A worker changes or stops what Orca is about to do with orca.hooks.on(hook, (event, next) => ...), which returns { dispose() }. Each hook must be listed in contributes.hooks (main and the trusted-worker profile; no capability). Return next(event) to go on, with the fields the hook allows changed; return a result such as { deny: reason } to stop; return nothing to pass. Plugins run one after another (installed plugins by key, then development folders), then Orca acts on what is left. Orca starts the workers of plugins that list hooks (or status bar items) as soon as they are enabled and keeps them running until they are disabled. A handler that takes longer than 2 s, a worker that does not start within the worker ready timeout, a throw, a crash or a disabled plugin counts as having passed, and orca plugin logs shows the hook, the plugin and how long it took. A guard that must fail closed sets "onError": "deny" on its contributes.hooks entry; then those failures refuse the action, naming the plugin. Decide before any other awaited call: an awaited host call ahead of your { deny } can fail or time out and let the action through.

Hooks run in the workers of the Orca that handles the request, before it acts: for an SSH or WSL terminal or workspace, that is the desktop, before the work is sent to its host. A paired client reaching a remote Orca server is decided by that server's plugins. orca plugin types writes the events as PluginHooks in orca/host; orca/testing mountWorker(...).hooks.fire(hook, event) returns what the chain decided.

Hook Event Stop result next may change Summary
terminal.send { terminalId: string; text: string; enter: boolean; inputKind: "driving" | "orchestration" | "launch" } { deny: string } text, enter Text Orca, the CLI or a plugin is about to send to a terminal. A person typing (on the desktop, a phone or a paired client) is never hooked, and prompts Orca submits to an agent take agent.prompt.before instead. Writes to one terminal stay in order while a hook decides. Rewrite text or enter with next, or return { deny } to refuse the send.
workspace.create.before { kind: "worktree" | "folder"; projectId: string; name: string; branch?: string; base?: string } { deny: string } name, branch, base A worktree or folder workspace is about to be created. Change name, branch or base with next, or return { deny } to refuse it.
workspace.create.after { workspaceId: string; kind: "worktree" | "folder"; path: string; branch: string } none nothing A worktree or folder workspace was created. Observe only: Orca does not wait for it, and results are ignored.
agent.launch.before { agent: string; workspaceId: string | null; workspaceKind: "worktree" | "folder" | null; terminalId?: string; command: string; env: Record<string, string> } { deny: string } command, env Orca is about to start an agent CLI in a terminal, from any launcher (new tab, workspace create, CLI, automation, orchestration). Change command or add env with next, or return { deny } to refuse the launch. Restores of saved sessions are not hooked.
agent.prompt.before { agent: string | null; terminalId: string; prompt: string; inputKind: "driving" | "orchestration" | "launch" } { deny: string } prompt Orca is about to submit a prompt to an agent in a terminal: a launch prompt, an orchestration dispatch, an issue or task prompt, or orca terminal send to an agent. A prompt a person submits is never hooked, and writes to one terminal stay in order while it decides. Rewrite prompt with next, or return { deny } to refuse it.
agent.tool.before { agent: string; workspaceId: string | null; terminalId?: string; tool: string; input: JSON; callId?: string } { deny: string } input An agent in an Orca terminal is about to run a tool. Claude Code waits for the answer: return { deny } to refuse the call (the agent sees the reason) or change input with next; the person is still asked as usual. Other agents only report the call, so their results are ignored. Agents on SSH or WSL hosts are not hooked yet.
agent.tool.after { agent: string; workspaceId: string | null; terminalId?: string; tool: string; input: JSON; callId?: string; output?: JSON } none nothing An agent in an Orca terminal finished a tool call (successfully or not). Observe only: Orca does not wait for it.
editor.save.before { workspaceId: string | null; path: string; languageId?: string; text: string } { deny: string } text The person is about to save a text file from an Orca editor tab (an explicit save, not autosave). Change text with next (format on save) or return { deny } to refuse the save, which shows your reason. Edits made while the hook ran stay unsaved.

Errors

A failed call rejects with an Error carrying code, message, retryable and, for rate_limited, retryAfterMs. Treat a code not listed here as action_failed:

  • invalid_request: the call itself is malformed.
  • invalid_params: params do not match the method, including a field this Orca does not know; the message names it.
  • unknown_method: the method does not exist in this Orca.
  • capability_denied: the manifest does not declare the capability.
  • consent_required: the plugin is disabled or the user has not approved this version.
  • not_permitted: the method or document is not available here, e.g. a worker-only method from a panel.
  • rate_limited: calls arrive faster than the budget; retryAfterMs says when to try again.
  • unavailable: the service behind the method is not running yet.
  • action_failed: the method ran and failed.
  • cancelled: the caller aborted the call through its signal.
  • timeout: Orca gave up waiting.
  • conflict: the call was made against a stale version, e.g. an editor edit after an outside change.
  • too_large: the message is larger than Orca accepts.

Stability and deprecation

Plugins are in Alpha: the manifest v2 format and catalog format are stable contracts; items listed as experimental in Plugin authoring may change.

  • Host API 1 is frozen. Within pluginApi 1, methods, params and results only grow: a new method or a new optional param arrives with a higher Since minor, and nothing listed here is removed, renamed or narrowed.
  • Deprecation. A method to be retired is marked deprecated in this table and keeps working for the rest of the major.
  • New majors. A breaking change ships as a new pluginApi major beside the old one. Orca refuses a manifest whose pluginApi names a major it does not implement, with "update Orca", instead of loading it against the wrong API.
  • Detect, do not assume. Check orca.host.has(name) or orca.api.minor before calling a method newer than the oldest Orca you support; a call that slips through fails with unknown_method.