Skip to content

On this page

Each entry below is generated from the plugin API 1.0 schema. A member marked required must be there. A plugin must ignore members, event kinds, error codes and values it does not know, because a later minor version adds them; it never removes one.

The four message shapes

A plugin and the host talk over the plugin's standard input and output: one JSON object per line, at most 65,536 bytes, in UTF-8. Each line is one of these four shapes. The daemon methods at the end of this page use the daemon's own socket instead.

Request

Asks the other side to do something and expects exactly one response or failure with the same id. params is always present and always an object.

idrequired

string

  • matches ^[A-Za-z0-9_.:-]{1,64}$

Chosen by the sender of a request and repeated in its response. Each side numbers its own requests; the two id spaces are separate.

methodrequired

one of "events.subscribe", "events.unsubscribe", "sessions.list", "lanes.list", "seats.list", "storage.get", "storage.set", "storage.delete", "storage.list", "tasks.create", "initialize", "ping", "command.run", "shutdown"

paramsrequired

object

No other members are allowed.

Request, example
{
  "id": "1",
  "method": "events.subscribe",
  "params": {
    "kinds": [
      "turn.end",
      "lane.verdict"
    ]
  }
}
Request, example
{
  "id": "h-1",
  "method": "shutdown",
  "params": {
    "reason": "disabled"
  }
}

Response

The answer to a request that succeeded. result is always an object; its shape is the result of the call the id belongs to.

idrequired

string

  • matches ^[A-Za-z0-9_.:-]{1,64}$

Chosen by the sender of a request and repeated in its response. Each side numbers its own requests; the two id spaces are separate.

resultrequired

object

No other members are allowed.

Response, example
{
  "id": "1",
  "result": {
    "kinds": [
      "turn.end",
      "lane.verdict"
    ]
  }
}

Failure

The answer to a request that was refused or failed.

idrequired

string

  • matches ^[A-Za-z0-9_.:-]{1,64}$

Chosen by the sender of a request and repeated in its response. Each side numbers its own requests; the two id spaces are separate.

errorrequired

object

  • no other members

The error member of a failure, in either direction. code is lowercase words joined by hyphens; message is text for a person and is never parsed. A plugin's message may be any text that fits the line, also longer than 500 characters or with line breaks or other control characters: the failure is valid, and the host cleans the message and cuts it to 500 characters before a client sees it. What the host itself sends is also valid against hostError, whose message is one line of at most 500 characters.

error.coderequired

string

  • at most 64 characters
  • matches ^[a-z]+(?:-[a-z]+)*$
error.messagerequired

string

error.dataoptional

object

No other members are allowed.

Failure, example
{
  "id": "9",
  "error": {
    "code": "method-not-found",
    "message": "Unknown method: storage.clear"
  }
}

Notification

Tells the other side something and expects no answer. It has no id. API 1.0 has two, both from the host.

methodrequired

one of "event", "events.dropped"

paramsrequired

object

No other members are allowed.

  • When method is "event", params is event.
  • When method is "events.dropped", params is events.dropped.
Notification, example
{
  "method": "event",
  "params": {
    "kind": "turn.end",
    "ts": "2026-11-14T09:31:07.412Z",
    "seq": 58,
    "session": "s-4f9a1c2b7d3e",
    "data": {
      "reason": "completed"
    }
  }
}

Calls a plugin makes to the host

events.subscribe

Starts or changes the delivery of events. Without kinds the plugin gets every kind of API 1.0. A second call replaces the first. Delivery starts with the first event the daemon records after the response; there is no replay.

Params

kindsoptional

array; each item is one of "session.start", "session.end", "turn.start", "turn.end", "approval.request", "approval.resolved", "lane.start", "lane.stage", "lane.verdict"

  • 1 to 9 items
  • no item twice

No other members are allowed.

Result

kindsrequired

array; each item is one of "session.start", "session.end", "turn.start", "turn.end", "approval.request", "approval.resolved", "lane.start", "lane.stage", "lane.verdict"

  • 1 to 9 items
  • no item twice

The kinds now delivered, in the order of the kind list.

No other members are allowed.

Errors

Only the errors every call can get.

Example 1

params
{
  "kinds": [
    "turn.end",
    "lane.verdict"
  ]
}
result
{
  "kinds": [
    "turn.end",
    "lane.verdict"
  ]
}

Example 2

params
{}
result
{
  "kinds": [
    "session.start",
    "session.end",
    "turn.start",
    "turn.end",
    "approval.request",
    "approval.resolved",
    "lane.start",
    "lane.stage",
    "lane.verdict"
  ]
}

events.unsubscribe

Stops the delivery of events. Events already written to the plugin's input still arrive.

Params

None: params is an empty object.

Result

kindsrequired

array

  • at most 0 items

No other members are allowed.

Errors

Only the errors every call can get.

Example

params
{}
result
{
  "kinds": []
}

sessions.list

  • A plugin sends it to the host
  • Permission lists.read

One page of the daemon's sessions in creation order, this machine's first, then the sessions it follows on other nodes. A page holds at most limit sessions and never more than fit one line; nextCursor is null on the last page.

Params

cursoroptional

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The nextCursor of the page before.

limitoptional

integer

  • from 1 to 500
  • default 100

No other members are allowed.

Result

sessionsrequired

array; each item is object

  • at most 500 items
sessions[].sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

sessions[].harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

sessions[].providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

sessions[].transportrequired

one of "acp", "claude-pty-hooks", "stream-json", "muse-msp", "omp-rpc"

How the daemon drives the session's agent.

sessions[].staterequired

one of "starting", "idle", "running", "awaiting-approval", "ended"

sessions[].turnrequired

integer

  • at least 0

How many turns the session has started.

sessions[].turnActiverequired

boolean

sessions[].endReasonrequired

one of "completed", "failed", "interrupted", "usage-limited" or null

How the last turn ended; null while a turn runs and before the first one has ended.

sessions[].pendingApprovalsrequired

integer

  • at least 0
sessions[].parentrequired

string or null

  • matches ^[a-zA-Z0-9_-]{1,128}$

The session this one was handed off from, or null.

sessions[].noderequired

string

  • matches ^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$

The machine the session runs on, as the daemon names it, or unknown.

sessions[].worktreerequired

string

  • at most 4,096 characters
  • matches ^(?:/|[A-Za-z]:[\\/]|\\\\)[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

The folder the session works in.

sessions[].createdAtrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

nextCursorrequired

string or null

  • matches ^[a-zA-Z0-9_-]{1,128}$

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example

params
{
  "limit": 2
}
result
{
  "sessions": [
    {
      "session": "s-4f9a1c2b7d3e",
      "harness": "claude",
      "provider": "anthropic",
      "transport": "claude-pty-hooks",
      "state": "running",
      "turn": 1,
      "turnActive": true,
      "endReason": null,
      "pendingApprovals": 0,
      "parent": null,
      "node": "desk",
      "worktree": "/srv/work/shop-wt/fix-login",
      "createdAt": "2026-11-14T09:12:44.031Z"
    },
    {
      "session": "s-7a1b2c3d4e5f",
      "harness": "codex",
      "provider": "openai",
      "transport": "stream-json",
      "state": "ended",
      "turn": 1,
      "turnActive": false,
      "endReason": "completed",
      "pendingApprovals": 0,
      "parent": null,
      "node": "desk",
      "worktree": "/srv/work/shop-wt/fix-login",
      "createdAt": "2026-11-14T09:20:02.118Z"
    }
  ],
  "nextCursor": "s-7a1b2c3d4e5f"
}

lanes.list

  • A plugin sends it to the host
  • Permission lists.read

One page of the lanes the daemon dispatched, newest first, as many as the daemon's verify.list put in its page. A lane the daemon archived after its merge is not listed. The host answers from a copy it reads from the daemon at most once in 5 seconds.

Params

cursoroptional

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z/t-[0-9a-f]{12}$

The nextCursor of the page before.

No other members are allowed.

Result

lanesrequired

array; each item is object

lanes[].lanerequired

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

lanes[].reporequired

string

  • at most 4,096 characters
  • matches ^(?:/|[A-Za-z]:[\\/]|\\\\)[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

An absolute path of this machine: /... on Linux and macOS, a drive or a share on Windows. No control or invisible formatting character (Unicode categories Cc, Cf, Cs, Zl, Zp).

lanes[].baserequired

string

  • 1 to 255 characters
  • matches ^[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp} ~^:?*\[\\]+$

A git branch name, without control or invisible formatting characters.

lanes[].branchrequired

string

  • 1 to 255 characters
  • matches ^[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp} ~^:?*\[\\]+$

A git branch name, without control or invisible formatting characters.

lanes[].worktreerequired

string

  • at most 4,096 characters
  • matches ^(?:/|[A-Za-z]:[\\/]|\\\\)[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

An absolute path of this machine: /... on Linux and macOS, a drive or a share on Windows. No control or invisible formatting character (Unicode categories Cc, Cf, Cs, Zl, Zp).

lanes[].staterequired

one of "building", "under-test", "sent-back", "verified", "merged"

The lane's state as verify.list gives it.

lanes[].stagerequired

one of "building", "idle", "checking", "waiting-verifier", "verifying", "verify-failed", "second-opinion", "pass", "rework", "sent-back", "cap", "merging", "merge-failed", "merged", "closed"

Where a lane is in the daemon's lane loop.

lanes[].builderrequired

object

  • no other members

The seat that builds the lane now (after a rework or a failover, the latest), and its session.

lanes[].builder.sessionrequired

string or null

  • matches ^[a-zA-Z0-9_-]{1,128}$
lanes[].builder.harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

lanes[].builder.providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

lanes[].builder.familyrequired

one of "anthropic", "openai", "google", "xai", "moonshot", "deepseek", "local", "auto"

A provider family of the registry.

lanes[].builder.modelrequired

string or null

  • matches ^[A-Za-z0-9][A-Za-z0-9_.:/\[\]-]{0,127}$

A model name from a seat's approved arguments, or null when the seat names none or the name is not a plain model name.

lanes[].verifierrequired

object

  • no other members

The seat a lane was built or is verified on.

lanes[].verifier.harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

lanes[].verifier.providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

lanes[].verifier.familyrequired

one of "anthropic", "openai", "google", "xai", "moonshot", "deepseek", "local", "auto"

A provider family of the registry.

lanes[].verifier.modelrequired

string or null

  • matches ^[A-Za-z0-9][A-Za-z0-9_.:/\[\]-]{0,127}$

A model name from a seat's approved arguments, or null when the seat names none or the name is not a plain model name.

lanes[].verdictrequired

null or object

The latest verdict, or null before the first.

lanes[].verdict.verdictrequired

one of "pass", "rework"

lanes[].verdict.commitrequired

string

  • matches ^(?:[0-9a-f]{40}|[0-9a-f]{64})$

A full git commit id.

lanes[].headrequired

string or null

  • matches ^(?:[0-9a-f]{40}|[0-9a-f]{64})$

The commit the lane's branch points at now, or null when the branch or the repository is gone.

lanes[].stalerequired

boolean

True when the verdict is on another commit than head.

lanes[].createdAtrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

nextCursorrequired

string or null

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z/t-[0-9a-f]{12}$

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example

params
{}
result
{
  "lanes": [
    {
      "lane": "t-0c1d2e3f4a5b",
      "repo": "/srv/work/shop",
      "base": "main",
      "branch": "omni/fix-login",
      "worktree": "/srv/work/shop-wt/fix-login",
      "state": "verified",
      "stage": "pass",
      "builder": {
        "session": "s-4f9a1c2b7d3e",
        "harness": "claude",
        "provider": "anthropic",
        "family": "anthropic",
        "model": "claude-fable-5-2"
      },
      "verifier": {
        "harness": "codex",
        "provider": "openai",
        "family": "openai",
        "model": "gpt-6-astra"
      },
      "verdict": {
        "verdict": "pass",
        "commit": "9f2c4e6a8b0d1f3a5c7e9b1d3f5a7c9e0b2d4f6a"
      },
      "head": "9f2c4e6a8b0d1f3a5c7e9b1d3f5a7c9e0b2d4f6a",
      "stale": false,
      "createdAt": "2026-11-14T09:12:43.870Z"
    }
  ],
  "nextCursor": null
}

seats.list

  • A plugin sends it to the host
  • Permission lists.read

One page of the seats in the daemon's fleet, in registry order: each installed harness, what it runs on and whether it is ready. The daemon's own seats.list method (cloud seats) is another list and is not exposed.

Params

cursoroptional

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The nextCursor of the page before.

No other members are allowed.

Result

seatsrequired

array; each item is object

seats[].harnessrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A registry id, the form tasks.create takes as builder and verifier.

seats[].providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

seats[].familyrequired

one of "anthropic", "openai", "google", "xai", "moonshot", "deepseek", "local", "auto"

A provider family of the registry.

seats[].statusrequired

one of "ready", "degraded", "missing"

ready: it can take a task. degraded: installed, and something is in the way (an approval, a sign-in, a failed check). missing: not installed.

seats[].signInrequired

one of "signed-in", "signed-out", "unknown"

nextCursorrequired

string or null

  • matches ^[a-zA-Z0-9_-]{1,128}$

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example

params
{}
result
{
  "seats": [
    {
      "harness": "claude",
      "provider": "anthropic",
      "family": "anthropic",
      "status": "ready",
      "signIn": "signed-in"
    },
    {
      "harness": "codex",
      "provider": "openai",
      "family": "openai",
      "status": "degraded",
      "signIn": "signed-in"
    },
    {
      "harness": "grok",
      "provider": "xai",
      "family": "xai",
      "status": "missing",
      "signIn": "unknown"
    }
  ],
  "nextCursor": null
}

storage.get

Reads one value from the plugin's own folder. value is null when the key holds nothing.

Params

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

No other members are allowed.

Result

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

valuerequired

string or null

  • at most 16,384 characters

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example 1

params
{
  "key": "last-run"
}
result
{
  "key": "last-run",
  "value": "2026-11-14T09:30:00.000Z"
}

Example 2

params
{
  "key": "never-set"
}
result
{
  "key": "never-set",
  "value": null
}

storage.set

Writes one value into the plugin's own folder, replacing what the key held. The value is on disk before the response is sent.

Params

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

valuerequired

string

  • at most 16,384 characters

Text of at most 16384 characters. The plugin chooses what it means; the host stores it as it is.

No other members are allowed.

Result

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

bytesrequired

integer

  • from 0 to 65,536

The size of the stored value in bytes of UTF-8.

No other members are allowed.

Errors

Besides the errors every call can get: quota-exceeded, unavailable.

Example

params
{
  "key": "last-run",
  "value": "2026-11-14T09:30:00.000Z"
}
result
{
  "key": "last-run",
  "bytes": 24
}

storage.delete

Removes one key. deleted is false when the key held nothing.

Params

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

No other members are allowed.

Result

keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

deletedrequired

boolean

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example

params
{
  "key": "last-run"
}
result
{
  "key": "last-run",
  "deleted": true
}

storage.list

Every key the plugin's folder holds, sorted by key, with the size of each value and the total.

Params

None: params is an empty object.

Result

keysrequired

array of objects

  • at most 128 items
keys[].keyrequired

string

  • matches ^[a-z0-9][a-z0-9._-]{0,63}$

A name, never a path: 1 to 64 characters, lowercase letters, digits, dots, underscores and hyphens, starting with a letter or a digit.

keys[].bytesrequired

integer

  • from 0 to 65,536
bytesrequired

integer

  • from 0 to 1,048,576

The total of the values in bytes of UTF-8.

No other members are allowed.

Errors

Besides the errors every call can get: unavailable.

Example

params
{}
result
{
  "keys": [
    {
      "key": "last-run",
      "bytes": 24
    },
    {
      "key": "notes",
      "bytes": 310
    }
  ],
  "bytes": 334
}

tasks.create

Asks the operator to start a task: a builder seat works in a new worktree of repo, a verifier seat of another provider family checks the result, and acceptance.command is the check. The host always puts the request in the daemon's approvals inbox, where the risk gate scores it; nothing starts unless the operator allows this very request in the app. The response comes when the operator answered and the daemon dispatched or refused the task, or when the request expired: up to 30 minutes for the answer and up to 15 more for the dispatch.

Params

reporequired

string

  • at most 4,096 characters
  • matches ^(?:/(?!/)|[A-Za-z]:[\\/])[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

The git repository the task works in: a local absolute path (a drive path on Windows, never a share or a device path) without control or invisible formatting characters. It must be a repository or worktree the daemon already lists.

taskrequired

string

  • 1 to 4,000 characters
  • matches ^(?:[\n\t]|[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}])*$
  • does not match ^\s*$

What the builder is asked to do. 1 to 4000 characters, not only spaces. No control character other than line feed and tab, and no character that is invisible or reorders text: none of the Unicode categories Cc (but line feed and tab), Cf, Cs, Zl and Zp. So the operator reads exactly what was asked.

builderrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The harness id of the seat that builds.

verifierrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The harness id of the seat that verifies. The daemon refuses a verifier of the builder's provider family.

acceptancerequired

object

  • no other members

The check that decides whether the task is done. The daemon runs command in its own checkout of the result.

acceptance.namerequired

string

  • 1 to 64 characters
  • matches ^[!-~](?:[ -~]*[!-~])?$
acceptance.commandrequired

string

  • 1 to 900 characters
  • matches ^[ -~]*[!-~][ -~]*$

One line of 1 to 900 printable basic Latin characters (U+0020 to U+007E), not only spaces, so the operator reads exactly what will run.

nameoptional

string

  • matches ^[A-Za-z0-9][A-Za-z0-9 ._-]{0,47}$

A short name for the task; the worktree and the branch are named after it. Without it the daemon uses the first line of task.

baseoptional

string

  • matches ^[A-Za-z0-9][A-Za-z0-9._/-]{0,199}$

The branch the task starts from. Without it the daemon uses the branch of the repository's main worktree.

No other members are allowed.

Result

requestIdrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The inbox request the operator allowed.

lanerequired

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The builder's session.

No other members are allowed.

Errors

Besides the errors every call can get: too-many-pending, approval-denied, approval-expired, task-refused, unavailable.

Example

params
{
  "repo": "/srv/work/shop",
  "task": "Add a test for the login redirect and make it pass.",
  "builder": "claude",
  "verifier": "codex",
  "acceptance": {
    "name": "unit tests",
    "command": "npm test"
  },
  "name": "login redirect test"
}
result
{
  "requestId": "plugin_3b0c5a52-6d0f-4f0e-9d5c-1f2a3b4c5d6e",
  "lane": "t-0c1d2e3f4a5b",
  "session": "s-4f9a1c2b7d3e"
}

Calls the host makes to a plugin

initialize

  • The host sends it to a plugin
  • No permission needed

The first message of every run. The host says which API it speaks and which permissions were approved; the plugin answers with the API version it was built for, which must equal the api of its manifest. Until the plugin has answered, the host refuses its requests with not-initialized.

Params

apirequired

string

  • matches ^[1-9][0-9]{0,3}\.(?:0|[1-9][0-9]{0,3})$

The host's API version.

hostVersionrequired

string

  • at most 64 characters
  • matches ^[0-9A-Za-z][0-9A-Za-z.+-]*$

The daemon's version, for log lines only.

platformrequired

one of "linux", "macos", "windows"

pluginrequired

object

  • no other members
plugin.idrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
plugin.versionrequired

string

  • at most 64 characters
permissionsrequired

array; each item is one of "events.read", "lists.read", "commands.add", "storage.own", "tasks.create"

  • at most 5 items
  • no item twice

The approved permission list, in the order of the permission table.

No other members are allowed.

Result

apirequired

string

  • matches ^[1-9][0-9]{0,3}\.(?:0|[1-9][0-9]{0,3})$

No other members are allowed.

Errors

The schema names none for it.

Example

params
{
  "api": "1.0",
  "hostVersion": "0.3.0",
  "platform": "linux",
  "plugin": {
    "id": "night-shift",
    "version": "1.2.0"
  },
  "permissions": [
    "events.read",
    "storage.own"
  ]
}
result
{
  "api": "1.0"
}

ping

  • The host sends it to a plugin
  • No permission needed

Sent every 30 seconds. Any response or failure within 10 seconds shows the plugin is alive.

Params

None: params is an empty object.

Result

An empty object.

Errors

The schema names none for it.

Example

params
{}
result
{}

command.run

The operator chose one of the plugin's commands in the app. The plugin answers with the text the app shows. input is present exactly when the manifest gives the command input text.

Params

commandrequired

string

  • at most 32 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

A command's id inside its plugin, as the manifest gives it.

inputoptional

string

  • at most 1,024 characters
  • matches ^[^\x00-\x1f\x7f]*$

The one line the operator typed: at most 1024 characters, no control characters.

No other members are allowed.

Result

textrequired

string

  • at most 16,384 characters

Plain text of at most 16384 characters. Before any client sees it the host cleans it: control characters other than line feed and tab are removed, invisible formatting characters are shown as marks, and the result is cut to 16384 bytes.

No other members are allowed.

Errors

The schema names none for it.

Example 1

params
{
  "command": "summary"
}
result
{
  "text": "2 lanes verified since 21:00.\nt-0c1d2e3f4a5b passed on round 1."
}

Example 2

params
{
  "command": "note",
  "input": "check the flaky login test"
}
result
{
  "text": "Saved."
}

shutdown

  • The host sends it to a plugin
  • No permission needed

The host is about to stop the plugin. The plugin answers and exits. The host closes the plugin's input after this message, sends SIGTERM to what is left two seconds later and ends it by force three seconds after that.

Params

reasonrequired

one of "daemon-stop", "disabled", "removed", "updated"

daemon-stop: the daemon is stopping. disabled: the operator switched the plugin off. removed: it is being removed. updated: the operator approved another copy than the one that runs, or the approved copy changed on disk.

No other members are allowed.

Result

An empty object.

Errors

The schema names none for it.

Example

params
{
  "reason": "daemon-stop"
}
result
{}

What the host sends without being asked

event

One event of a kind the plugin subscribed to.

Params

One event, in one of these shapes: session.start, session.end, turn.start, turn.end, approval.request, approval.resolved, lane.start, lane.stage, lane.verdict.

Errors

None: a notification has no answer.

Example

params
{
  "kind": "turn.end",
  "ts": "2026-11-14T09:31:07.412Z",
  "seq": 58,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "reason": "completed"
  }
}

events.dropped

The plugin read too slowly: more than 1 MiB of messages waited for it, and the host left out the next count events. Sent once, as soon as there is room again and before any later event.

Params

countrequired

integer

  • at least 1

No other members are allowed.

Errors

None: a notification has no answer.

Example

params
{
  "count": 12
}

Event kinds

The params of an event notification, one shape per kind. ts is the time the daemon recorded the event; seq is the event's number in its ledger stream, so it rises within one session or within the lane stream and has gaps where the daemon recorded events a plugin does not get. A session event carries session, a lane event carries lane.

session.start

A session was created; its agent is about to start.

kindrequired

"session.start"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

data.providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

data.transportrequired

one of "acp", "claude-pty-hooks", "stream-json", "muse-msp", "omp-rpc"

How the daemon drives the session's agent.

data.flagsrequired

object

  • no other members

What the session can do.

data.flags.interruptrequired

boolean

data.flags.steerrequired

boolean

data.flags.queuerequired

boolean

data.flags.forkrequired

boolean

data.flags.approvalRoutingrequired

boolean

data.flags.mcpPassthroughrequired

boolean

data.flags.usageReportingrequired

boolean

No other members are allowed.

params of the event notification
{
  "kind": "session.start",
  "ts": "2026-11-14T09:12:44.031Z",
  "seq": 1,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "harness": "claude",
    "provider": "anthropic",
    "transport": "claude-pty-hooks",
    "flags": {
      "interrupt": true,
      "steer": false,
      "queue": false,
      "fork": false,
      "approvalRouting": true,
      "mcpPassthrough": true,
      "usageReporting": true
    }
  }
}

session.end

The session's agent is gone. code is its exit status and signal the signal that ended it; each is null when there was none.

kindrequired

"session.end"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.coderequired

integer or null

data.signalrequired

string or null

  • matches ^SIG[A-Z0-9]{1,16}$

No other members are allowed.

params of the event notification
{
  "kind": "session.end",
  "ts": "2026-11-14T09:31:07.590Z",
  "seq": 60,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "code": 0,
    "signal": null
  }
}

turn.start

The agent was given a prompt. turn counts the session's turns from 1; mode is queue for a prompt the agent had queued itself; chars is the length of the prompt, whose text is never sent.

kindrequired

"turn.start"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.turnrequired

integer

  • at least 1
data.moderequired

one of "prompt", "queue"

data.charsrequired

integer

  • at least 0

No other members are allowed.

params of the event notification
{
  "kind": "turn.start",
  "ts": "2026-11-14T09:12:44.040Z",
  "seq": 2,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "turn": 1,
    "mode": "prompt",
    "chars": 412
  }
}

turn.end

The turn ended, for the reason given.

kindrequired

"turn.end"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.reasonrequired

one of "completed", "failed", "interrupted", "usage-limited"

How a turn ended.

No other members are allowed.

params of the event notification
{
  "kind": "turn.end",
  "ts": "2026-11-14T09:31:07.412Z",
  "seq": 58,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "reason": "completed"
  }
}

approval.request

The session's agent asked the operator for permission or asked a question, and waits. Only the session's own requests are delivered (see ownRequestId). action is the kind of thing it wants to do (read, edit, execute, question and so on) or tool when the agent gave no plain word; risk is the session's own rough label. What the request is about (paths, commands, the question) is never sent.

kindrequired

"approval.request"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.requestIdrequired

string

  • at most 128 characters
  • matches ^[a-zA-Z0-9_-]+-a[1-9][0-9]*$

A request the session's own agent made: the session id, then -a and a number, as Sessions.request() numbers them. Requests the daemon writes on a session for another reason (the cited checks of a rework, ids that start with check-) are never delivered.

data.actionrequired

string

  • matches ^[A-Za-z][A-Za-z0-9_.-]{0,63}$
data.riskrequired

one of "low", "medium", "high"

No other members are allowed.

params of the event notification
{
  "kind": "approval.request",
  "ts": "2026-11-14T09:14:02.300Z",
  "seq": 17,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "requestId": "s-4f9a1c2b7d3e-a1",
    "action": "execute",
    "risk": "high"
  }
}

approval.resolved

A request of the session was answered or withdrawn. delivered is false when the request was withdrawn without an answer reaching the agent (its turn ended, the daemon restarted).

kindrequired

"approval.resolved"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

datarequired

object

  • no other members
data.requestIdrequired

string

  • at most 128 characters
  • matches ^[a-zA-Z0-9_-]+-a[1-9][0-9]*$

A request the session's own agent made: the session id, then -a and a number, as Sessions.request() numbers them. Requests the daemon writes on a session for another reason (the cited checks of a rework, ids that start with check-) are never delivered.

data.decisionrequired

one of "allow", "deny", "cancel"

data.deliveredrequired

boolean

No other members are allowed.

params of the event notification
{
  "kind": "approval.resolved",
  "ts": "2026-11-14T09:14:20.914Z",
  "seq": 19,
  "session": "s-4f9a1c2b7d3e",
  "data": {
    "requestId": "s-4f9a1c2b7d3e-a1",
    "decision": "allow",
    "delivered": true
  }
}

lane.start

The daemon dispatched a lane: a builder session started and a verifier seat was named.

kindrequired

"lane.start"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
lanerequired

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

datarequired

object

  • no other members
data.sessionrequired

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

The builder's session.

data.builderrequired

object

  • no other members

The seat a lane was built or is verified on.

data.builder.harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

data.builder.providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

data.builder.familyrequired

one of "anthropic", "openai", "google", "xai", "moonshot", "deepseek", "local", "auto"

A provider family of the registry.

data.builder.modelrequired

string or null

  • matches ^[A-Za-z0-9][A-Za-z0-9_.:/\[\]-]{0,127}$

A model name from a seat's approved arguments, or null when the seat names none or the name is not a plain model name.

data.verifierrequired

object

  • no other members

The seat a lane was built or is verified on.

data.verifier.harnessrequired

string

  • matches ^[A-Za-z0-9_.-]{1,128}$

A seat's harness id, as the registry names it.

data.verifier.providerrequired

string

  • matches ^[a-z][a-z0-9-]{0,63}$

A provider name of the registry, or unknown.

data.verifier.familyrequired

one of "anthropic", "openai", "google", "xai", "moonshot", "deepseek", "local", "auto"

A provider family of the registry.

data.verifier.modelrequired

string or null

  • matches ^[A-Za-z0-9][A-Za-z0-9_.:/\[\]-]{0,127}$

A model name from a seat's approved arguments, or null when the seat names none or the name is not a plain model name.

No other members are allowed.

params of the event notification
{
  "kind": "lane.start",
  "ts": "2026-11-14T09:12:43.870Z",
  "seq": 311,
  "lane": "t-0c1d2e3f4a5b",
  "data": {
    "session": "s-4f9a1c2b7d3e",
    "builder": {
      "harness": "claude",
      "provider": "anthropic",
      "family": "anthropic",
      "model": "claude-fable-5-2"
    },
    "verifier": {
      "harness": "codex",
      "provider": "openai",
      "family": "openai",
      "model": "gpt-6-astra"
    }
  }
}

lane.stage

The lane moved to another stage of the lane loop, or the daemon's reason for the stage it is in changed; so the same stage can arrive again with a later since. since is when the record was made. The daemon's reason text is never sent.

kindrequired

"lane.stage"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
lanerequired

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

datarequired

object

  • no other members
data.stagerequired

one of "building", "idle", "checking", "waiting-verifier", "verifying", "verify-failed", "second-opinion", "pass", "rework", "sent-back", "cap", "merging", "merge-failed", "merged", "closed"

Where a lane is in the daemon's lane loop.

data.sincerequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

No other members are allowed.

params of the event notification
{
  "kind": "lane.stage",
  "ts": "2026-11-14T09:31:09.002Z",
  "seq": 318,
  "lane": "t-0c1d2e3f4a5b",
  "data": {
    "stage": "verifying",
    "since": "2026-11-14T09:31:09.001Z"
  }
}

lane.verdict

The verifier's verdict on a commit of the lane was recorded. round counts the lane's verify rounds from 1; defects is how many defects a rework verdict cites (0 for a pass). The defects themselves are never sent.

kindrequired

"lane.verdict"

tsrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

seqrequired

integer

  • at least 1
lanerequired

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

datarequired

object

  • no other members
data.commitrequired

string

  • matches ^(?:[0-9a-f]{40}|[0-9a-f]{64})$

A full git commit id.

data.verdictrequired

one of "pass", "rework"

data.roundrequired

integer

  • at least 1
data.defectsrequired

integer

  • from 0 to 50

No other members are allowed.

params of the event notification
{
  "kind": "lane.verdict",
  "ts": "2026-11-14T09:44:51.377Z",
  "seq": 326,
  "lane": "t-0c1d2e3f4a5b",
  "data": {
    "commit": "9f2c4e6a8b0d1f3a5c7e9b1d3f5a7c9e0b2d4f6a",
    "verdict": "pass",
    "round": 1,
    "defects": 0
  }
}

Daemon methods that manage plugins

New methods on the daemon's own socket, for the app and for the omniconfluxd command line. They use the daemon's request and reply shapes, not the plugin wire: a failure carries only code and message.

plugin.install

  • The app or the command line sends it to the daemon

Copies the folder at path into the host's store, checks the copy and leaves it waiting for approval. A copy of this id that is approved keeps running until the new copy is approved. When the copy is, file for file, the approved copy or the copy that already waits, nothing changes and unchanged is true.

Params

pathrequired

string

  • at most 4,096 characters
  • matches ^(?:/|[A-Za-z]:[\\/]|\\\\)[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

An absolute path of this machine: /... on Linux and macOS, a drive or a share on Windows. No control or invisible formatting character (Unicode categories Cc, Cf, Cs, Zl, Zp).

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
versionrequired

string

  • at most 64 characters
snapshotHashrequired

string

  • matches ^[0-9a-f]{64}$

The hash of the copy that now waits (or, when unchanged, of the copy that was there).

staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

replacedrequired

boolean

True when a copy that was waiting for approval was replaced by this one.

unchangedrequired

boolean

installedByrequired

one of "operator", "agent-session", "plugin"

Who called plugin.install, as the daemon saw the caller: operator (a process outside everything the daemon started), agent-session (inside a session or terminal holder the daemon started) or plugin (inside a plugin's container). Shown in the approval prompt.

No other members are allowed.

Errors

invalid-params, plugin-invalid, plugin-incompatible, plugin-limit, plugin-remove-pending, ledger-corrupt.

Example

params
{
  "path": "/srv/work/night-shift"
}
result
{
  "plugin": "night-shift",
  "version": "1.2.0",
  "snapshotHash": "3b7e0c1d5a9f2468ace013579bdf02468ace13579bdf02468ace013579bdf0a1",
  "state": "pending-approval",
  "replaced": false,
  "unchanged": false,
  "installedBy": "operator"
}

plugin.list

  • The app or the command line sends it to the daemon

One page of the installed plugins sorted by id, as many as fit one frame.

Params

cursoroptional

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Result

pluginsrequired

array; each item is object

  • at most 20 items
plugins[].pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
plugins[].namerequired

string

  • at most 64 characters

The display name, or the plugin id when no manifest can be shown.

plugins[].versionrequired

string

  • at most 64 characters
plugins[].descriptionrequired

string

  • at most 200 characters
plugins[].publisherrequired

string or null

  • at most 64 characters
plugins[].apirequired

string

  • matches ^[1-9][0-9]{0,3}\.(?:0|[1-9][0-9]{0,3})$
plugins[].staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

plugins[].reasonrequired

one of null, "first-approval", "files-changed", "store-changed", "disabled", "crash-limit", "run-limit", "api-unsupported", "removing", "remove-failed", "exit", "load-refused", "stdout-closed", "invalid-message", "oversized-line", "flood", "refusals", "memory", "processes", "initialize-timeout", "initialize-failed", "api-mismatch", "unresponsive", "spawn-failed"

Why a plugin is not running, or null while it is starting or running. first-approval, files-changed, store-changed: with state pending-approval. disabled: with state disabled. crash-limit, run-limit: with state failed. api-unsupported: with state incompatible. removing (a removal is at work), remove-failed (it did not finish): with state remove-pending. A fault name: with state restarting.

plugins[].enabledrequired

boolean

False once the operator switched the plugin off.

plugins[].approvedrequired

boolean

True when the copy in the store is the one the operator approved, so it can run.

plugins[].waitingrequired

boolean

True when a copy waits for the operator's approval: a first install, an update, files that changed on disk, or an approval that was lost with the host's record.

plugins[].permissionsrequired

array; each item is one of "events.read", "lists.read", "commands.add", "storage.own", "tasks.create"

  • at most 5 items
  • no item twice
plugins[].commandsrequired

integer

  • from 0 to 8

How many commands the manifest declares.

nextCursorrequired

string or null

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Errors

invalid-params.

Example

params
{}
result
{
  "plugins": [
    {
      "plugin": "night-shift",
      "name": "Night Shift",
      "version": "1.2.0",
      "description": "Starts a follow-up task when a lane is verified.",
      "publisher": "Example Works",
      "api": "1.0",
      "state": "pending-approval",
      "reason": "first-approval",
      "enabled": true,
      "approved": false,
      "waiting": true,
      "permissions": [
        "events.read",
        "lists.read",
        "commands.add",
        "storage.own",
        "tasks.create"
      ],
      "commands": 2
    }
  ],
  "nextCursor": null
}

plugin.get

  • The app or the command line sends it to the daemon

Everything the app shows about one plugin: the approved manifest that is in force, the copy that waits for approval with the hash to approve and what differs, the lanes the plugin started, and the tail of its error output.

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

reasonrequired

one of null, "first-approval", "files-changed", "store-changed", "disabled", "crash-limit", "run-limit", "api-unsupported", "removing", "remove-failed", "exit", "load-refused", "stdout-closed", "invalid-message", "oversized-line", "flood", "refusals", "memory", "processes", "initialize-timeout", "initialize-failed", "api-mismatch", "unresponsive", "spawn-failed"

Why a plugin is not running, or null while it is starting or running. first-approval, files-changed, store-changed: with state pending-approval. disabled: with state disabled. crash-limit, run-limit: with state failed. api-unsupported: with state incompatible. removing (a removal is at work), remove-failed (it did not finish): with state remove-pending. A fault name: with state restarting.

enabledrequired

boolean

approvedrequired

boolean

manifestrequired

object or null

The approved manifest, the one in force; null when nothing is approved.

approvalrequired

null or object

The approval in force, or null.

approval.snapshotHashrequired

string

  • matches ^[0-9a-f]{64}$
approval.operatorrequired

string

  • matches ^[a-zA-Z0-9 ._-]{1,64}$
approval.atrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

waitingrequired

null or object

The copy that waits for approval, or null.

waiting.kindrequired

one of "install", "files-changed", "store-changed"

install: a copy was installed and waits beside the approved one (or is the first). files-changed: the approved copy changed on disk. store-changed: the host's approval record failed its check, so nothing counts as approved.

waiting.snapshotHashrequired

string

  • matches ^[0-9a-f]{64}$

The hash of the copy that waits: the value plugin.approve takes.

waiting.manifestrequired

object or null

The manifest of the copy that waits, as parsed; null when it is missing or not valid against manifest.schema.json (such a copy cannot be approved).

waiting.diffrequired

array; each item is object or object

  • 1 to 12 items

What differs between the approved copy and the copy that waits: one entry per manifest member that differs (before is null when there is no approval or the member was absent), then one entry with field files that counts every file that differs, the manifest file included. Never empty. An entry for id can appear only for a copy changed on disk or a record made again from the store's folders; such a copy names an id other than the plugin's and cannot be approved.

waiting.diff[].fieldrequired

Shape 1 of 2

one of "manifestVersion", "api", "id", "name", "version", "description", "publisher", "main", "permissions", "commands"

waiting.diff[].beforerequired

Shape 1 of 2

any value

waiting.diff[].afterrequired

Shape 1 of 2

any value

waiting.diff[].fieldrequired

Shape 2 of 2

"files"

waiting.diff[].addedrequired

Shape 2 of 2

integer

  • at least 0
waiting.diff[].removedrequired

Shape 2 of 2

integer

  • at least 0
waiting.diff[].changedrequired

Shape 2 of 2

integer

  • at least 0
waiting.diff[].pathsrequired

Shape 2 of 2

array of strings

  • at most 20 items
  • each item: at most 200 characters

The first 20 paths that were added, removed or changed, sorted by path.

waiting.installedByrequired

one of "operator", "agent-session", "plugin" or null

Who installed the copy that waits; null for files-changed and store-changed.

waiting.installedAtrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

When the copy was installed, or when the change was found.

filesrequired

object

  • no other members

The regular files of the approved copy.

files.countrequired

integer

  • at least 0
files.bytesrequired

integer

  • at least 0
storagerequired

object

  • no other members
storage.keysrequired

integer

  • from 0 to 128
storage.bytesrequired

integer

  • from 0 to 1,048,576
lanesrequired

array; each item is string

  • at most 50 items
  • each item: matches ^t-[0-9a-f]{12}$

The lanes this plugin's task requests started that are at work (neither merged nor closed), newest first, at most 50, read from the plugin's own record in plugins.json, never from the ledger. A plugin installed again under the same id lists none of the old one's. The removal prompt lists them; a removal with stopTasks stops every lane of the plugin, also beyond these 50.

startingrequired

array of strings

  • at most 50 items
  • no item twice
  • each item: matches ^plugin_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$

The request ids of the plugin's allowed task requests whose dispatch has not ended (at most 3 at a time) or was cut off by a daemon stop, newest first, at most 50. The removal prompt lists them with the lanes; a removal with stopTasks aborts every one.

crashesrequired

integer

  • at least 0

Crashes in the last 10 minutes.

lastExitrequired

null or object

How the plugin's last process ended, or null when it never ran or still runs its first.

lastExit.atrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

lastExit.coderequired

integer or null

lastExit.signalrequired

string or null

lastExit.faultrequired

one of "exit", "load-refused", "stdout-closed", "invalid-message", "oversized-line", "flood", "refusals", "memory", "processes", "initialize-timeout", "initialize-failed", "api-mismatch", "unresponsive", "spawn-failed" or null

null when the host asked the plugin to stop.

stderrTailrequired

string

  • at most 4,096 characters

The plugin's error output, cleaned like every text of a plugin, and of that the last 4096 bytes, starting at a character boundary.

No other members are allowed.

Errors

invalid-params, plugin-not-found.

Example

params
{
  "plugin": "night-shift"
}
result
{
  "plugin": "night-shift",
  "state": "running",
  "reason": null,
  "enabled": true,
  "approved": true,
  "manifest": {
    "manifestVersion": 1,
    "api": "1.0",
    "id": "night-shift",
    "name": "Night Shift",
    "version": "1.2.0",
    "description": "Starts a follow-up task when a lane is verified.",
    "main": "dist/main.mjs",
    "permissions": [
      "events.read",
      "storage.own"
    ]
  },
  "approval": {
    "snapshotHash": "3b7e0c1d5a9f2468ace013579bdf02468ace13579bdf02468ace013579bdf0a1",
    "operator": "sam via app",
    "at": "2026-11-14T08:02:11.000Z"
  },
  "waiting": {
    "kind": "install",
    "snapshotHash": "5d1f0a9c3e7b2468ace013579bdf02468ace13579bdf02468ace013579bdf0c4",
    "manifest": {
      "manifestVersion": 1,
      "api": "1.0",
      "id": "night-shift",
      "name": "Night Shift",
      "version": "1.2.1",
      "description": "Starts a follow-up task when a lane is verified.",
      "main": "dist/main.mjs",
      "permissions": [
        "events.read",
        "lists.read",
        "storage.own",
        "tasks.create"
      ]
    },
    "diff": [
      {
        "field": "version",
        "before": "1.2.0",
        "after": "1.2.1"
      },
      {
        "field": "permissions",
        "before": [
          "events.read",
          "storage.own"
        ],
        "after": [
          "events.read",
          "lists.read",
          "storage.own",
          "tasks.create"
        ]
      },
      {
        "field": "files",
        "added": 0,
        "removed": 0,
        "changed": 2,
        "paths": [
          "dist/main.mjs",
          "omniconflux-plugin.json"
        ]
      }
    ],
    "installedBy": "agent-session",
    "installedAt": "2026-11-14T09:58:40.120Z"
  },
  "files": {
    "count": 14,
    "bytes": 48211
  },
  "storage": {
    "keys": 2,
    "bytes": 334
  },
  "lanes": [
    "t-0c1d2e3f4a5b"
  ],
  "starting": [],
  "crashes": 0,
  "lastExit": null,
  "stderrTail": ""
}

plugin.approve

  • The app or the command line sends it to the daemon

Records the operator's approval of the copy that waits, named by its hash together with the permission list and the command ids the prompt showed, then puts that copy in force and starts the plugin when it is enabled. Only the app can call it: the daemon refuses any caller that is not the operator's app (operator-only).

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
snapshotHashrequired

string

  • matches ^[0-9a-f]{64}$
permissionsrequired

array; each item is one of "events.read", "lists.read", "commands.add", "storage.own", "tasks.create"

  • at most 5 items
  • no item twice

The permission list the prompt showed, in the order of the permission table, whatever order the manifest lists them in. The daemon compares it member by member with the waiting manifest's list put in that order; any difference is plugin-stale-snapshot.

commandsrequired

array; each item is string

  • at most 8 items
  • no item twice
  • each item: at most 32 characters, matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

The ids of the commands the prompt showed, in the manifest's order.

operatorrequired

string

  • matches ^[a-zA-Z0-9 ._-]{1,64}$

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
snapshotHashrequired

string

  • matches ^[0-9a-f]{64}$
approvedrequired

true

staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

No other members are allowed.

Errors

invalid-params, plugin-not-found, plugin-invalid, plugin-incompatible, plugin-stale-snapshot, plugin-remove-pending, operator-only, ledger-corrupt.

Example

params
{
  "plugin": "night-shift",
  "snapshotHash": "5d1f0a9c3e7b2468ace013579bdf02468ace13579bdf02468ace013579bdf0c4",
  "permissions": [
    "events.read",
    "lists.read",
    "storage.own",
    "tasks.create"
  ],
  "commands": [],
  "operator": "sam via app"
}
result
{
  "plugin": "night-shift",
  "snapshotHash": "5d1f0a9c3e7b2468ace013579bdf02468ace13579bdf02468ace013579bdf0c4",
  "approved": true,
  "state": "starting"
}

plugin.discard

  • The app or the command line sends it to the daemon

Deletes the installed copy that waits for approval and leaves the approved copy as it is. For a plugin that was never approved this removes the plugin. Refused for a caller inside anything the daemon started (operator-only).

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
discardedrequired

true

removedrequired

boolean

True when nothing of the plugin is left, because it was never approved.

No other members are allowed.

Errors

invalid-params, plugin-not-found, not-found, plugin-remove-pending, operator-only.

Example

params
{
  "plugin": "night-shift"
}
result
{
  "plugin": "night-shift",
  "discarded": true,
  "removed": false
}

plugin.enable

  • The app or the command line sends it to the daemon

Switches a plugin on. An approved plugin starts; one that had failed is started again with its crash count cleared. Only the app can call it (operator-only).

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
enabledrequired

true

staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

No other members are allowed.

Errors

invalid-params, plugin-not-found, plugin-remove-pending, operator-only, ledger-corrupt.

Example

params
{
  "plugin": "night-shift"
}
result
{
  "plugin": "night-shift",
  "enabled": true,
  "state": "starting"
}

plugin.disable

  • The app or the command line sends it to the daemon

Switches a plugin off: it is stopped and stays stopped across daemon restarts until it is enabled again. Its approval and its data stay.

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
enabledrequired

false

staterequired

one of "pending-approval", "disabled", "starting", "running", "restarting", "failed", "incompatible", "remove-pending"

pending-approval: no approved copy can run. disabled: approved and switched off. starting: started, the first exchange is not finished. running. restarting: it crashed and the host waits to start it again. failed: the host gave up, or there was no room to run it. incompatible: its api version is not one this host runs. remove-pending: a removal is at work or did not finish; the plugin is off, is never started again, and the host finishes the removal by itself.

No other members are allowed.

Errors

invalid-params, plugin-not-found.

Example

params
{
  "plugin": "night-shift"
}
result
{
  "plugin": "night-shift",
  "enabled": false,
  "state": "disabled"
}

plugin.remove

  • The app or the command line sends it to the daemon

Stops the plugin and deletes its copies, its approval, its data and its commands. With stopTasks true, which it is when absent, the tasks its requests started are stopped with it: every lane of its requests that is still at work is closed and its sessions stopped, and every dispatch still in flight, or cut off by a daemon stop, is aborted. They are read from the plugin's record in plugins.json, not from the ledger or a paged list. The reply is sent when its processes are gone and the folder is deleted. Refused for a caller inside anything the daemon started (operator-only). When a step fails, or an abort does not reply in time, the plugin is left remove-pending.

Params

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
stopTasksoptional

boolean

The operator's choice about the tasks the plugin's requests started. True, also when absent: they are stopped with the plugin. False: they go on as ordinary lanes.

No other members are allowed.

Result

pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
removedrequired

true

stoppedrequired

integer

  • from 0 to 10,000

How many sessions the removal stopped.

worktreesrequired

array; each item is string

  • at most 50 items
  • no item twice
  • each item: at most 4,096 characters, matches ^(?:/|[A-Za-z]:[\\/]|\\\\)[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}]*$

The worktrees that aborted dispatches left, those of the newest requests first, as many as fit one frame. They hold no plugin code and are kept for the operator.

No other members are allowed.

Errors

invalid-params, plugin-not-found, operator-only.

Example 1

params
{
  "plugin": "night-shift",
  "stopTasks": true
}
result
{
  "plugin": "night-shift",
  "removed": true,
  "stopped": 1,
  "worktrees": []
}

Example 2

params
{
  "plugin": "night-shift"
}
result
{
  "plugin": "night-shift",
  "removed": true,
  "stopped": 0,
  "worktrees": [
    "/srv/work/shop-wt/login-redirect-test"
  ]
}

plugin.commands

  • The app or the command line sends it to the daemon

One page of the commands of every plugin whose state is starting, running, restarting or failed, sorted by plugin id and then by command id, as many as fit one frame. available is true while the command's plugin is running.

Params

cursoroptional

string

  • at most 97 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

A command as the app and the daemon name it: <plugin id>.<command id>.

No other members are allowed.

Result

commandsrequired

array; each item is object

  • at most 256 items
commands[].commandrequired

string

  • at most 97 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

A command as the app and the daemon name it: <plugin id>.<command id>.

commands[].pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
commands[].pluginNamerequired

string

  • at most 48 characters

The name in the plugin's approved manifest.

commands[].titlerequired

string

  • at most 48 characters
commands[].descriptionrequired

string or null

  • at most 120 characters
commands[].inputrequired

one of "none", "text"

commands[].availablerequired

boolean

nextCursorrequired

string or null

  • at most 97 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

No other members are allowed.

Errors

invalid-params.

Example

params
{}
result
{
  "commands": [
    {
      "command": "night-shift.note",
      "plugin": "night-shift",
      "pluginName": "Night Shift",
      "title": "Add a note",
      "description": null,
      "input": "text",
      "available": true
    },
    {
      "command": "night-shift.summary",
      "plugin": "night-shift",
      "pluginName": "Night Shift",
      "title": "Show tonight's summary",
      "description": "Lists the lanes verified since the last run.",
      "input": "none",
      "available": true
    }
  ],
  "nextCursor": null
}

plugin.command.run

  • The app or the command line sends it to the daemon

Runs one plugin command because the operator chose it in the app, and returns the text to show. Only the app can call it (operator-only). input is required for a command whose manifest says input text and refused for any other.

Params

commandrequired

string

  • at most 97 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

A command as the app and the daemon name it: <plugin id>.<command id>.

inputoptional

string

  • at most 1,024 characters
  • matches ^[^\x00-\x1f\x7f]*$

The one line the operator typed: at most 1024 characters, no control characters.

No other members are allowed.

Result

commandrequired

string

  • at most 97 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$

A command as the app and the daemon name it: <plugin id>.<command id>.

textrequired

string

  • at most 16,384 characters
  • matches ^(?:[\n\t]|[^\p{Cc}\p{Cf}\p{Cs}\p{Zl}\p{Zp}])*$

The plugin's text after the host cleaned it: no control character but line feed and tab, invisible formatting characters shown as marks, at most 16384 bytes.

No other members are allowed.

Errors

invalid-params, command-not-found, plugin-not-running, plugin-timeout, plugin-error, busy, operator-only.

Example

params
{
  "command": "night-shift.summary"
}
result
{
  "command": "night-shift.summary",
  "text": "2 lanes verified since 21:00.\nt-0c1d2e3f4a5b passed on round 1."
}

plugin.request.get

  • The app or the command line sends it to the daemon

The whole of a task request a plugin made, for the app's inbox card: the inbox item itself holds only a summary. Answering stays with approvals.answer.

Params

requestIdrequired

string

  • matches ^plugin_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$

No other members are allowed.

Result

requestIdrequired

string

  • matches ^plugin_[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$
pluginrequired

string

  • 3 to 64 characters
  • matches ^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$
pluginNamerequired

string

  • at most 64 characters

The name in the plugin's approved manifest, or the plugin id when the plugin is no longer installed.

callrequired

"tasks.create"

paramsrequired

tasks.create params

  • no other members
digestrequired

string

  • matches ^[0-9a-f]{64}$

The SHA-256 of the request as the host stored it: the inbox item's scopeIdentity. The host dispatches exactly the stored request.

lastEventrequired

null or object

The kind, and the session or lane, of the last event the host delivered to the plugin before the request; null when it delivered none in this run. The card shows it, so the operator sees what the plugin reacted to.

lastEvent.kindrequired

one of "session.start", "session.end", "turn.start", "turn.end", "approval.request", "approval.resolved", "lane.start", "lane.stage", "lane.verdict"

lastEvent.sessionoptional

string

  • matches ^[a-zA-Z0-9_-]{1,128}$

A session id of the daemon.

lastEvent.laneoptional

string

  • matches ^t-[0-9a-f]{12}$

A lane id: the id of a task the daemon dispatched with a verifier.

staterequired

one of "pending", "answered", "expired", "orphaned"

The inbox item's state.

createdAtrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

expiresAtrequired

string

  • matches ^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{3}Z$

UTC time in the form the daemon writes: YYYY-MM-DDTHH:MM:SS.mmmZ.

No other members are allowed.

Errors

invalid-params, not-found.

Example

params
{
  "requestId": "plugin_3b0c5a52-6d0f-4f0e-9d5c-1f2a3b4c5d6e"
}
result
{
  "requestId": "plugin_3b0c5a52-6d0f-4f0e-9d5c-1f2a3b4c5d6e",
  "plugin": "night-shift",
  "pluginName": "Night Shift",
  "call": "tasks.create",
  "params": {
    "repo": "/srv/work/shop",
    "task": "Add a test for the login redirect and make it pass.",
    "builder": "claude",
    "verifier": "codex",
    "acceptance": {
      "name": "unit tests",
      "command": "npm test"
    },
    "name": "login redirect test"
  },
  "digest": "7c1e5b0f9a3d2468ace013579bdf02468ace13579bdf02468ace013579bdf0e9",
  "lastEvent": {
    "kind": "lane.verdict",
    "lane": "t-0c1d2e3f4a5b"
  },
  "state": "pending",
  "createdAt": "2026-11-14T09:45:00.000Z",
  "expiresAt": "2026-11-14T10:15:00.000Z"
}

Errors

A failure carries an error object: a code, one line of message for a person, and data for the three codes that name one. Decide by the code, never by the message, and treat a code you do not know as a failure of the call.

The error object the host sends

coderequired

one of "invalid-request", "method-not-found", "invalid-params", "permission-denied", "not-initialized", "busy", "rate-limited", "result-too-large", "quota-exceeded", "too-many-pending", "approval-denied", "approval-expired", "task-refused", "unavailable", "shutting-down", "internal-error"

messagerequired

string

  • at most 500 characters
  • matches ^[^\x00-\x1f\x7f]*$

One line the host wrote itself. It never carries a message of a lower layer: no path, no parser's words, no stack trace, no id of another session, no text from an agent.

dataoptional

object

No other members are allowed.

  • When code is "permission-denied", data is required and data is an object with data.permission only.
  • When code is "quota-exceeded", data is required and data is an object with data.limit only.
  • When code is "task-refused", data is required and data is an object with data.reason only.
  • Unless code is one of "permission-denied", "quota-exceeded", "task-refused", data is not allowed.

approval-denied

  • The host sends it to a plugin

The operator did not allow the task.

Named by tasks.create.

approval-expired

  • The host sends it to a plugin

The operator did not answer in 30 minutes.

Named by tasks.create.

busy

  • The host sends it to a plugin
  • A daemon method answers with it

16 requests of the plugin are unanswered.

From a daemon method: For plugin.command.run: 4 commands of the plugin are unanswered.

Any call a plugin makes can get it.

Named by plugin.command.run.

command-not-found

  • A daemon method answers with it

No plugin in the state starting, running, restarting or failed has this command.

Named by plugin.command.run.

internal-error

  • The host sends it to a plugin

Anything else. The message is always: The host could not complete the request.

Any call a plugin makes can get it.

invalid-params

  • The host sends it to a plugin
  • A daemon method answers with it

The params do not fit the call's schema, or name something the daemon does not know, such as a cursor.

From a daemon method: The params do not fit the method's schema, and an unknown member is refused. For plugin.command.run also: input given for a command that takes none, or missing for one that takes text.

Any call a plugin makes can get it.

Named by plugin.install, plugin.list, plugin.get, plugin.approve, plugin.discard, plugin.enable, plugin.disable, plugin.remove, plugin.commands, plugin.command.run, plugin.request.get.

error
{
  "code": "invalid-params",
  "message": "key must be 1 to 64 characters"
}

invalid-request

  • The host sends it to a plugin

The line is a request in name only: a member is missing, of the wrong type or unknown, or its id is in use.

Any call a plugin makes can get it.

ledger-corrupt

  • A daemon method answers with it

The ledger is read-only, so nothing was installed, approved or switched on.

Named by plugin.install, plugin.approve, plugin.enable.

method-not-found

  • The host sends it to a plugin

The host serves no such call.

Any call a plugin makes can get it.

error
{
  "code": "method-not-found",
  "message": "Unknown method: storage.clear"
}

not-found

  • A daemon method answers with it

For plugin.request.get: no such request. For plugin.discard: no installed copy waits.

Named by plugin.discard, plugin.request.get.

not-initialized

  • The host sends it to a plugin

The plugin has not answered initialize yet.

Any call a plugin makes can get it.

operator-only

  • A daemon method answers with it

The caller is not the operator's app. plugin.approve, plugin.enable, plugin.command.run and the answer to a plugin's request take only the app; plugin.remove and plugin.discard take no caller inside anything the daemon started.

Named by plugin.approve, plugin.discard, plugin.enable, plugin.remove, plugin.command.run.

permission-denied

  • The host sends it to a plugin

The approved permission list lacks the call's permission. data.permission names it.

Any call a plugin makes can get it.

data

data.permissionrequired

one of "events.read", "lists.read", "commands.add", "storage.own", "tasks.create"

No other members are allowed.

error
{
  "code": "permission-denied",
  "message": "This plugin was not approved for tasks.create",
  "data": {
    "permission": "tasks.create"
  }
}

plugin-error

  • A daemon method answers with it

The plugin answered with a failure, or with a result that is not valid.

Named by plugin.command.run.

plugin-incompatible

  • A daemon method answers with it

The plugin's api is not one this host runs. The message names both versions.

Named by plugin.install, plugin.approve.

plugin-invalid

  • A daemon method answers with it

The folder or the installed copy fails a folder or manifest check. The message is the problem code of the first failed check, then what failed and where.

Named by plugin.install, plugin.approve.

plugin-limit

  • A daemon method answers with it

20 plugins are installed.

Named by plugin.install.

plugin-not-found

  • A daemon method answers with it

No plugin with this id is installed.

Named by plugin.get, plugin.approve, plugin.discard, plugin.enable, plugin.disable, plugin.remove.

plugin-not-running

  • A daemon method answers with it

The command's plugin is not running, or it crashed before it answered.

Named by plugin.command.run.

plugin-remove-pending

  • A daemon method answers with it

The plugin is being removed, or its removal did not finish.

Named by plugin.install, plugin.approve, plugin.discard, plugin.enable.

plugin-stale-snapshot

  • A daemon method answers with it

No copy waits, or the copy that waits is not the one the call names by hash, permission list and command ids.

Named by plugin.approve.

plugin-timeout

  • A daemon method answers with it

The plugin did not answer command.run in 30 seconds.

Named by plugin.command.run.

quota-exceeded

  • The host sends it to a plugin

A storage limit was reached. data.limit says which: keys or bytes.

Named by storage.set.

data

data.limitrequired

one of "keys", "bytes"

No other members are allowed.

error
{
  "code": "quota-exceeded",
  "message": "The plugin's folder holds 128 keys",
  "data": {
    "limit": "keys"
  }
}

rate-limited

  • The host sends it to a plugin

The plugin's budget of requests is empty (50 a second, 200 at once), or it made more than 10 list calls in the current second, or 20 task requests in 24 hours, or fewer than 3 of the 60 ledger records of the last hour are left for a task request.

Any call a plugin makes can get it.

error
{
  "code": "rate-limited",
  "message": "More than 50 requests a second for too long"
}

result-too-large

  • The host sends it to a plugin

The response would not fit on one line.

Any call a plugin makes can get it.

shutting-down

  • The host sends it to a plugin

The plugin is being stopped.

Any call a plugin makes can get it.

task-refused

  • The host sends it to a plugin

The daemon refused the allowed task, or the platform cannot run its check isolated. data.reason is the daemon's error code, or isolation-missing.

Named by tasks.create.

data

data.reasonrequired

string

  • at most 64 characters
  • matches ^[a-z]+(?:-[a-z]+)*$

The daemon's own error code for the refused task.dispatch, or isolation-missing when the platform cannot run the task's check isolated.

No other members are allowed.

error
{
  "code": "task-refused",
  "message": "The daemon refused the task: registry-pending-approval",
  "data": {
    "reason": "registry-pending-approval"
  }
}

too-many-pending

  • The host sends it to a plugin

3 task requests of the plugin are unanswered.

Named by tasks.create.

unavailable

  • The host sends it to a plugin

The part of the daemon the call needs could not do it now: the storage file, the inbox, or a dispatch that failed without a code.

Named by sessions.list, lanes.list, seats.list, storage.get, storage.set, storage.delete, storage.list, tasks.create.

Product and company names in the values and examples on this page are trademarks of their owners. Naming one describes what the API carries, not endorsement.

Plugins ship inside the app

OmniConflux 0.3.0 is released for Omarchy, macOS on Apple silicon and Windows, in a closed beta with a waitlist. Plugins and the SDK ship inside the app from 0.3.0, and the SDK is not on a package registry.