octomuxdocs

Extending

Plugin API

A plugin exports apply(ctx), as a named export or hung off a default export. The loader checks both.

Minimal plugin

// index.mjs
export async function apply(ctx) {
  ctx.workflows.register({
    kind: 'hello',           // becomes "<row-id>:hello"
    displayName: 'Hello',
    execution: 'session',
    surfaces: ['artifact'],
    trigger: { kind: 'manual' },
    run: async (runCtx) => {
      ctx.logger.info({ repoPath: runCtx.repoPath }, 'hello');
    },
  });
}

PluginContext

FieldWhat it is
idYour manifest row id, unqualified.
loggerdebug / info / warn / error, each taking an object and an optional message.
settingsAsync scoped settings: get() and update(patch).
kvPer-plugin key-value store: get / set / del / list(prefix).
workflows integrations harnessesThe three registrars. These are the only way a plugin reaches core.

Workflows

A workflow takes a kind, displayName, surfaces (session, artifact or feed), an optional config JSON Schema that drives the schedules form, an optional output schema that drives the run detail view, a trigger, and a run(ctx).

You do not write a frontend. Describe your config in JSON Schema and octomux renders the form; describe the output shape and you get a detail view. Plugins ship no browser JavaScript.

Integrations

An integration declares kind, displayName, configSchema, the events it reacts to, a validate(config) and a handler(envelope, config).

Events available: task_created, workflow_status_changed, summary_updated, note_added, ref_added, ref_removed, runtime_state_changed.

Outbound only. octomux tells the external system that something happened. There is no inbound poller seam, so a plugin cannot watch an external system for changes.

Harnesses

The hardest registrar. Requires newSessionId, buildLaunchCommand, buildResumeCommand, buildContinueCommand, installHooks, uninstallHooks, resolveFlags and validateSettings.

Two things to know before starting: command builders return a shell command string, not an argv array, so you own escaping and injection-safety; and several members on the Harness type are reserved but not yet called. Read server/harnesses/claude-code.ts and cursor.ts first — they are the reference implementations.

The five seams

A seam exists where two people would reasonably want different things. There are five.

SeamDecidesStatus
workflowsWhat starts work, and what the work isshipped
harnessesWhich agent does itshipped
computeWhich machine it runs oncoming
surfacesWhere you see it and answer itcoming
integrationsWhere the result landsshipped

What is deliberately not a seam

Every one of these was considered and cut. A seam you do not need is a versioned interface you have to keep working forever.

Not a seamWhy not
SkillsSkills are markdown files. A plugin ships them by shipping files — there is nothing to swap and no behaviour to override.
MCP serversMCP is already the plugin protocol for agent tools. Wrapping it in a second one adds a layer and buys nothing.
ModelsYour harness already picks the model. A parallel model registry would fight it and lose.
HistoryOne append-only log, same shape for everyone. Plugins read it; nobody needs a different one.
ArtifactsFiles a run produced. A plugin needs to write one, which is a method on ctx, not a registry.
CatalogReading what is installed is a query, not an implementation choice.
IsolationA git worktree per run is the guarantee, not a preference. Where that worktree lives is the compute seam.
StorageSQLite on your machine. If a team ever needs Postgres it is a connection string in settings, not a plugin.

Coming

The following are designed but not released. They are tracked publicly and the landing page describes them as though shipped — that gap is a known issue.

SeamWhat it will doStatus
ctx.http.route()Routes as data, replacing the Express router handout — the change that makes unload possiblecoming
Lifecyclemount / unmount / dispose and hot reload without a restartcoming
ctx.factsA shared task-scoped log so plugins can read each other’s outputcoming
ctx.uiDeclarative panels and actions, still with no browser JavaScriptcoming
ctx.policyIntercept an intent and deny or rewrite it — spend caps, model routingcoming
ctx.attentionAsk a human and await the answer, routed to whatever surfaces are installedcoming

Full reference for what ships today: api-reference.md.