Files
paperclip/doc/plugins/PLUGIN_AUTHORING_GUIDE.md
Dotta 9c6f551595 [codex] Add plugin orchestration host APIs (#4114)
## Thinking Path

> - Paperclip orchestrates AI agents for zero-human companies.
> - The plugin system is the extension path for optional capabilities
that should not require core product changes for every integration.
> - Plugins need scoped host APIs for issue orchestration, documents,
wakeups, summaries, activity attribution, and isolated database state.
> - Without those host APIs, richer plugins either cannot coordinate
Paperclip work safely or need privileged core-side special cases.
> - This pull request adds the plugin orchestration host surface, scoped
route dispatch, a database namespace layer, and a smoke plugin that
exercises the contract.
> - The benefit is a broader plugin API that remains company-scoped,
auditable, and covered by tests.

## What Changed

- Added plugin orchestration host APIs for issue creation, document
access, wakeups, summaries, plugin-origin activity, and scoped API route
dispatch.
- Added plugin database namespace tables, schema exports, migration
checks, and idempotent replay coverage under migration
`0059_plugin_database_namespaces`.
- Added shared plugin route/API types and validators used by server and
SDK boundaries.
- Expanded plugin SDK types, protocol helpers, worker RPC host behavior,
and testing utilities for orchestration flows.
- Added the `plugin-orchestration-smoke-example` package to exercise
scoped routes, restricted database namespaces, issue orchestration,
documents, wakeups, summaries, and UI status surfaces.
- Kept the new orchestration smoke fixture out of the root pnpm
workspace importer so this PR preserves the repository policy of not
committing `pnpm-lock.yaml`.
- Updated plugin docs and database docs for the new orchestration and
database namespace surfaces.
- Rebased the branch onto `public-gh/master`, resolved conflicts, and
removed `pnpm-lock.yaml` from the final PR diff.

## Verification

- `pnpm install --frozen-lockfile`
- `pnpm --filter @paperclipai/db typecheck`
- `pnpm exec vitest run packages/db/src/client.test.ts`
- `pnpm exec vitest run server/src/__tests__/plugin-database.test.ts
server/src/__tests__/plugin-orchestration-apis.test.ts
server/src/__tests__/plugin-routes-authz.test.ts
server/src/__tests__/plugin-scoped-api-routes.test.ts
server/src/__tests__/plugin-sdk-orchestration-contract.test.ts`
- From `packages/plugins/examples/plugin-orchestration-smoke-example`:
`pnpm exec vitest run --config ./vitest.config.ts`
- `pnpm --dir
packages/plugins/examples/plugin-orchestration-smoke-example run
typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- PR CI on latest head `293fc67c`: `policy`, `verify`, `e2e`, and
`security/snyk` all passed.

## Risks

- Medium risk: this expands plugin host authority, so route auth,
company scoping, and plugin-origin activity attribution need careful
review.
- Medium risk: database namespace migration behavior must remain
idempotent for environments that may have seen earlier branch versions.
- Medium risk: the orchestration smoke fixture is intentionally excluded
from the root workspace importer to avoid a `pnpm-lock.yaml` PR diff;
direct fixture verification remains listed above.
- Low operational risk from the PR setup itself: the branch is rebased
onto current `master`, the migration is ordered after upstream
`0057`/`0058`, and `pnpm-lock.yaml` is not in the final diff.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

Roadmap checked: this work aligns with the completed Plugin system
milestone and extends the plugin surface rather than duplicating an
unrelated planned core feature.

## Model Used

- OpenAI Codex, GPT-5-based coding agent in a tool-enabled CLI
environment. Exact hosted model build and context-window size are not
exposed by the runtime; reasoning/tool use were enabled for repository
inspection, editing, testing, git operations, and PR creation.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots (N/A: no core UI screen change; example plugin UI contract
is covered by tests)
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-04-20 08:52:51 -05:00

6.1 KiB

Plugin Authoring Guide

This guide describes the current, implemented way to create a Paperclip plugin in this repo.

It is intentionally narrower than PLUGIN_SPEC.md. The spec includes future ideas; this guide only covers the alpha surface that exists now.

Current reality

  • Treat plugin workers and plugin UI as trusted code.
  • Plugin UI runs as same-origin JavaScript inside the main Paperclip app.
  • Worker-side host APIs are capability-gated.
  • Plugin UI is not sandboxed by manifest capabilities.
  • Plugin database migrations are restricted to a host-derived plugin namespace.
  • Plugin-owned JSON API routes must be declared in the manifest and are mounted only under /api/plugins/:pluginId/api/*.
  • There is no host-provided shared React component kit for plugins yet.
  • ctx.assets is not supported in the current runtime.

Scaffold a plugin

Use the scaffold package:

pnpm --filter @paperclipai/create-paperclip-plugin build
node packages/plugins/create-paperclip-plugin/dist/index.js @yourscope/plugin-name --output ./packages/plugins/examples

For a plugin that lives outside the Paperclip repo:

pnpm --filter @paperclipai/create-paperclip-plugin build
node packages/plugins/create-paperclip-plugin/dist/index.js @yourscope/plugin-name \
  --output /absolute/path/to/plugin-repos \
  --sdk-path /absolute/path/to/paperclip/packages/plugins/sdk

That creates a package with:

  • src/manifest.ts
  • src/worker.ts
  • src/ui/index.tsx
  • tests/plugin.spec.ts
  • esbuild.config.mjs
  • rollup.config.mjs

Inside this monorepo, the scaffold uses workspace:* for @paperclipai/plugin-sdk.

Outside this monorepo, the scaffold snapshots @paperclipai/plugin-sdk from the local Paperclip checkout into a .paperclip-sdk/ tarball so you can build and test a plugin without publishing anything to npm first.

From the generated plugin folder:

pnpm install
pnpm typecheck
pnpm test
pnpm build

For local development, install it into Paperclip from an absolute local path through the plugin manager or API. The server supports local filesystem installs and watches local-path plugins for file changes so worker restarts happen automatically after rebuilds.

Example:

curl -X POST http://127.0.0.1:3100/api/plugins/install \
  -H "Content-Type: application/json" \
  -d '{"packageName":"/absolute/path/to/your-plugin","isLocalPath":true}'

Supported alpha surface

Worker:

  • config
  • events
  • jobs
  • launchers
  • http
  • secrets
  • activity
  • state
  • database namespace via ctx.db
  • scoped JSON API routes declared with apiRoutes
  • entities
  • projects and project workspaces
  • companies
  • issues, comments, namespaced plugin:<pluginKey> origins, blocker relations, checkout assertions, assignment wakeups, and orchestration summaries
  • agents and agent sessions
  • goals
  • data/actions
  • streams
  • tools
  • metrics
  • logger

Plugin database declarations

First-party or otherwise trusted orchestration plugins can declare:

database: {
  migrationsDir: "migrations",
  coreReadTables: ["issues"],
}

Required capabilities are database.namespace.migrate and database.namespace.read; add database.namespace.write for runtime mutations. The host derives ctx.db.namespace, runs SQL files in filename order before the worker starts, records checksums in plugin_migrations, and rejects changed already-applied migrations.

Migration SQL may create or alter objects only inside ctx.db.namespace. It may reference whitelisted public core tables for foreign keys or read-only views, but may not mutate/alter/drop/truncate public tables, create extensions, triggers, untrusted languages, or runtime multi-statement SQL. Runtime ctx.db.query() is restricted to SELECT; runtime ctx.db.execute() is restricted to namespace-local INSERT, UPDATE, and DELETE.

Scoped plugin API routes

Plugins can expose JSON-only routes under their own namespace:

apiRoutes: [
  {
    routeKey: "initialize",
    method: "POST",
    path: "/issues/:issueId/smoke",
    auth: "board-or-agent",
    capability: "api.routes.register",
    checkoutPolicy: "required-for-agent-in-progress",
    companyResolution: { from: "issue", param: "issueId" },
  },
]

The host resolves the plugin, checks that it is ready, enforces api.routes.register, matches the declared method/path, resolves company access, and applies checkout policy before dispatching to the worker's onApiRequest handler. The worker receives sanitized headers, route params, query, parsed JSON body, actor context, and company id. Do not use plugin routes to claim core paths; they always remain under /api/plugins/:pluginId/api/*.

UI:

  • usePluginData
  • usePluginAction
  • usePluginStream
  • usePluginToast
  • useHostContext
  • typed slot props from @paperclipai/plugin-sdk/ui

Mount surfaces currently wired in the host include:

  • page
  • settingsPage
  • dashboardWidget
  • sidebar
  • sidebarPanel
  • detailTab
  • taskDetailView
  • projectSidebarItem
  • globalToolbarButton
  • toolbarButton
  • contextMenuItem
  • commentAnnotation
  • commentContextMenuItem

Company routes

Plugins may declare a page slot with routePath to own a company route like:

/:companyPrefix/<routePath>

Rules:

  • routePath must be a single lowercase slug
  • it cannot collide with reserved host routes
  • it cannot duplicate another installed plugin page route

Publishing guidance

  • Use npm packages as the deployment artifact.
  • Treat repo-local example installs as a development workflow only.
  • Prefer keeping plugin UI self-contained inside the package.
  • Do not rely on host design-system components or undocumented app internals.
  • GitHub repository installs are not a first-class workflow today. For local development, use a checked-out local path. For production, publish to npm or a private npm-compatible registry.

Verification before handoff

At minimum:

pnpm --filter <your-plugin-package> typecheck
pnpm --filter <your-plugin-package> test
pnpm --filter <your-plugin-package> build

If you changed host integration too, also run:

pnpm -r typecheck
pnpm test:run
pnpm build