* 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.
15 KiB
Orca plugin catalog reference
The orca-catalog.json file that lists installable plugins and their releases.
Add "$schema": "https://raw.githubusercontent.com/stablyai/orca/main/docs/schemas/plugins/orca-catalog.v2.schema.json" to orca-catalog.json for completion and inline errors in your editor.
Since is the first Orca release that reads the field. Older releases ignore fields they do not know.
Fields
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
$schema |
string | no | 1.4.245 | JSON Schema URL for editor completion. Ignored by Orca. |
catalogVersion |
2 |
yes | 1.4.245 | Catalog format. Raised only for a change older Orca cannot ignore; a newer Orca reads this one. |
name |
string | yes | 1.4.245 | Catalog display name. Example: "Orca Plugins" |
owner |
string | yes | 1.4.245 | Account that publishes the catalog. Example: "stablyai" |
plugins |
CatalogEntry[] | yes | 1.4.245 | Listed plugins, at most 2048. Each id should appear once; Orca keeps the first. An entry this Orca cannot read is skipped, never fatal. |
CatalogEntry
One installable plugin.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
id |
string | yes | 1.4.245 | <publisher>.<id>: the publisher is the repository owner, lowercased (Orca installs under this key, whatever the manifest's deprecated publisher says), and the id is the manifest's id. Each half is at most 64 characters, and the reserved names constructor and prototype are refused. Example: "acme.gleam-syntax" |
source |
CatalogGitSource | yes | Where the plugin source lives. | |
ownerId |
integer | no | 1.4.246 | GitHub's numeric id of the repository owner, recorded the first time the plugin was listed. Names can be renamed or re-registered; this id cannot. Informational: Orca does not read it yet, and older Orca ignores it. Example: 1234567 |
description |
string | no | 1.4.245 | Listing text shown in the catalog. |
categories |
string[] | no | 1.4.245 | Lowercase slugs used to filter the catalog, each listed once. At most 16; extras are ignored. |
releases |
CatalogRelease[] | yes | 1.4.245 | Installable releases, newest first. Orca installs the first one whose minimumOrca it meets, so keep older releases listed when you raise engines.orca. At most 32. |
CatalogGitSource
Where the plugin source lives.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
kind |
"git" |
yes | 1.4.245 | Source kind. Only git is supported. |
url |
string | yes | 1.4.245 | HTTPS or SSH URL of the plugin repository. Example: "https://github.com/acme/orca-gleam-syntax.git" |
ref |
string | yes | 1.4.245 | Branch or tag the author publishes from. Each release pins an exact commit. Example: "main" |
path |
string | no | 1.4.245 | Directory inside the repository that holds orca-plugin.json, so one repository can publish many plugins. Use / separators, with no leading /, . or ... Omit when the plugin is the repository root. Example: "plugins/gleam-syntax" |
CatalogRelease
The exact release the catalog vouches for. Orca refuses bytes that differ.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
version |
string | yes | 1.4.245 | The manifest's version for this release. Example: "1.2.0" |
commit |
string | yes | 1.4.245 | Full SHA-1 commit Orca checks out for this release. |
contentHash |
string | yes | 1.4.245 | SHA-256 of the plugin's installed files, as Orca computes it at install. |
execution |
"data" | "declarative" | "sandboxed-ui" | "trusted-worker" |
yes | 1.4.245 | The manifest's execution profile. Example: "data" |
minimumOrca |
string | yes | 1.4.245 | The manifest's engines.orca without >=. Older Orca lists the plugin but will not install it. Example: "1.4.245" |
types |
("interface-language" | "app-theme" | "terminal-theme" | "icon-theme" | "syntax" | "instructions" | "panel" | "editor" | "worker")[] |
no | 1.4.245 | Optional; Orca derives it from contributes and execution. When present, every kind of contribution the release carries, matching its manifest exactly. instructions needs the declarative profile or higher, panel and editor need sandboxed-ui, and worker needs trusted-worker. Example: ["syntax"] |
name |
string | no | 1.4.245 | The manifest's name, so Orca can suggest the plugin before downloading it. Example: "Gleam" |
license |
string | no | 1.4.245 | SPDX license expression. Example: "MIT" |
repository |
string | no | 1.4.245 | Source repository page, https only. Orca links it when it is a github.com or gitlab.com repository of the plugin's publisher. Example: "https://github.com/acme/orca-gleam-syntax" |
sourceUnavailable |
true |
no | 1.4.246 | Set when the release's repository is gone and source serves an archived copy, so Orca links source instead. Example: true |
homepage |
string | no | 1.4.245 | Project homepage, https only. Example: "https://example.com/gleam" |
updatedAt |
string | no | 1.4.245 | When this release was published, as an ISO 8601 date or date-time. Example: "2026-10-20" |
changelog |
string | no | 1.4.245 | What changed in this release, as plain text. At most 2000 characters. Example: "Adds highlighting for use expressions." |
images |
CatalogImages | no | 1.4.245 | Images extracted from the release by catalog CI, which re-encodes them and strips metadata. Same rules as the manifest: icon PNG, WEBP, static, at least 128×128, square, at most 256 KB. Cover PNG, JPEG, WEBP, static, at least 800×500, at most 1024 KB. Screenshots PNG, JPEG, WEBP, static, at least 1280×800, at most 1536 KB. Images must not imitate Orca's trust badges. |
themePreview |
CatalogThemePreview[] | no | 1.4.245 | Up to 4 theme palettes for listing art. Display only; the installed theme files decide the real colors. |
contributes |
CatalogContribution[] | no | 1.4.245 | What the release adds, as the listing shows it. Generate it with the catalog tooling; Orca refuses an install whose manifest says otherwise. |
capabilities |
("workspace:read" | "workspace:write" | "agent:read" | "agent:launch" | "terminal:send" | "terminal:read" | "notifications:show" | "storage:own" | "secrets:own" | "settings:own" | "appearance:write" | "workspace:create" | "automation:read" | "automation:write")[] |
no | 1.4.245 | Every capability the release asks for. Orca refuses an install whose manifest asks for different ones. Example: ["notifications:show"] |
fileTypes |
string[] | no | 1.4.245 | Extensions, without the dot, that this release opens. Orca suggests the plugin when such a file has no viewer. |
editorFileTypes |
string[] | no | 1.4.245 | The fileTypes this release opens in a custom editor. Orca suggests the plugin for such a file even when it already opens it as text. |
CatalogImages
Images extracted from the release by catalog CI, which re-encodes them and strips metadata. Same rules as the manifest: icon PNG, WEBP, static, at least 128×128, square, at most 256 KB. Cover PNG, JPEG, WEBP, static, at least 800×500, at most 1024 KB. Screenshots PNG, JPEG, WEBP, static, at least 1280×800, at most 1536 KB. Images must not imitate Orca's trust badges.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
icon |
CatalogImage | no | An image stored in the catalog repository. | |
cover |
CatalogImage | no | An image stored in the catalog repository. | |
screenshots |
CatalogImage[] | no | 1.4.245 | Screenshots, at most 6. |
CatalogImage
An image stored in the catalog repository.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
path |
string | yes | 1.4.245 | File in the catalog repository, relative to its root. Relative to the plugin root; .. segments and Windows device names such as CON are refused. Example: "assets/acme.gleam/cover.png" |
alt |
string | yes | 1.4.245 | What the image shows, for screen readers. Required. Example: "The Dusk theme on a terminal" |
sha256 |
string | yes | 1.4.245 | SHA-256 of the file's bytes; Orca refuses bytes that differ. |
width |
integer | yes | 1.4.245 | Width in pixels. Example: 1600 |
height |
integer | yes | 1.4.245 | Height in pixels. Example: 1000 |
CatalogThemePreview
Colors to draw a theme card before install.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
label |
string | yes | 1.4.245 | Theme name. Example: "Dusk" |
base |
"light" | "dark" |
yes | 1.4.245 | Light or dark theme. |
background |
string | yes | 1.4.245 | Window background. Example: "#1b1430" |
foreground |
string | yes | 1.4.245 | Main text. Example: "#1b1430" |
accent |
string | yes | 1.4.245 | Accent, such as buttons. Example: "#1b1430" |
surface |
string | yes | 1.4.245 | Cards and sidebars. Example: "#1b1430" |
CatalogContribution
One thing the release adds to Orca.
| Field | Type | Required | Since | Description |
|---|---|---|---|---|
kind |
"panels" | "commands" | "events" | "hooks" | "languagePacks" | "keybindings" | "vmRecipes" | "themes" | "terminalThemes" | "iconThemes" | "fonts" | "syntaxes" | "customEditors" | "newFiles" | "settings" | "statusBarItems" |
yes | 1.4.245 | Contribution point. Example: "themes" |
name |
string | yes | 1.4.245 | What the item is called in Orca. Example: "Dusk" |
detail |
string | no | 1.4.245 | Extra context, such as file extensions. |
Versions and compatibility
- What only lists is tolerant. A catalog never decides what runs (installing re-checks the exact bytes), so Orca keeps whatever it can read: unknown keys are ignored at every level, an entry it cannot read is skipped, an invalid display-only field is dropped, a list over its limit is trimmed, and a repeated
idkeeps the first entry. Validation in CI is where these become errors. - The file name never changes.
catalogVersion(now 2) rises only for a change older clients cannot ignore; an Orca that sees a higher number refuses the catalog with "update Orca to browse this catalog" rather than misreading it. - Releases are a list, newest first. Orca offers the first release whose
minimumOrcait meets, so keep the previous release listed when a new one raisesengines.orca; Orca shows "Needs Orca X" only when none fits.
Publisher names and trust
- Publisher is the repository owner. Like a Go module path, a plugin's publisher is where it lives, never what its manifest says: the official and community catalogs set each entry's
idto<owner>.<manifest id>from the GitHub user or organization that owns the repository (acmeforgithub.com/acme/gleam-syntax), and Orca installs under thatid. A Git URL install uses the URL's owner and a folder useslocal. The manifest'spublisheris deprecated and ignored. Orca does not check the owner for catalogs you add yourself; theiridis what they say. - Listed names are pinned to GitHub ids. The community catalog records the numeric owner and repository ids when a plugin is first listed, pauses new versions after a rename or transfer until Stably confirms it, and refuses a repository that was deleted and recreated under the same name. Each listed version is permanent: a moved tag never replaces it.
- Reserved names install only from Orca's official catalog. The publishers
stablyai,official,anthropic,openai,githuband any startingorca(any case), and ids startingorca-, are refused from any other catalog, a Git URL or a local folder. A catalog that lists one is refused whole.pnpm validate:pluginrejects these publishers exceptstablyai. - Trust comes from the catalog's address. Orca labels a plugin with where it came from: the Official mark for Orca's catalog, otherwise the catalog's name and the owner in its Git URL (
github.com/acme,gitlab.com/acme/tools), never the manifest's text. Development folders showDev · unverified, Git URL installsUnverified. - An install stays with its catalog. Orca records the catalog each plugin was installed from and updates it only from there. When another catalog lists the same id, the plugin page says so, and switching means removing the plugin and installing it again.
One repository can hold many plugins
Give each plugin its own directory and set source.path to it. Orca downloads the pinned commit but installs only that directory, and contentHash covers only its files, so a commit that changes a sibling plugin leaves this plugin's hash, and its users, untouched. Moving a plugin to another directory counts as moving repositories: users review the update before it installs.
{ "id": "acme.gleam-syntax", "source": { "kind": "git", "url": "https://github.com/acme/orca-plugins.git", "ref": "main", "path": "plugins/gleam-syntax" } }
Run pnpm validate:plugin plugins/gleam-syntax in that repository to print the directory's path and its release entry. Listing images stay relative to the catalog repository.
Install links
An install link opens Orca at a plugin's page in Settings → Plugins. It never installs: the user reads the page and presses Install, and the usual review applies. If plugins are off, the page waits behind Turn on plugins.
orca://plugins/<publisher>.<id>?catalog=<official | community | catalog Git URL>
catalogdefaults toofficial.communityis Orca's community catalog. Any other value must be an HTTPS or SSH Git URL of at most 512 characters, read at itsmainbranch, without a password or token. Orca asks Add catalog …? before fetching a catalog the user hasn't added, and that includescommunity.- Reserved publishers (above) are accepted only with the official catalog. A link longer than 2048 characters, with another query parameter, or with an id that isn't
<publisher>.<id>shows a short error and does nothing else. Repeats of the same link within a few seconds are ignored. - Browsers and README viewers often block custom schemes, so link to the directory page instead. It opens the
orca://link and shows install steps when Orca is missing:https://orca.dev/plugins/<publisher>.<id>?catalog=<same value>.
For a README:
[Open in Orca](https://orca.dev/plugins/acme.gleam-syntax?catalog=community)
Clients before format 2
Orca reads orca-catalog.json first and falls back to orca-marketplace.json. That older file is frozen: shipped clients reject any key they do not know, so it carries only name, owner and each plugin's id, source, description and categories, and leaves out plugins with a source.path, which those clients would install from the repository root. Generate it from orca-catalog.json rather than editing two files:
node config/scripts/export-legacy-plugin-catalog.mjs orca-catalog.json orca-marketplace.json