forked from farhoodlabs/paperclip
docs: update plugin authoring guide for managed resources (#6261)
## Thinking Path
<!--
Required. Trace your reasoning from the top of the project down to this
specific change. Start with what Paperclip is, then narrow through the
subsystem, the problem, and why this PR exists. Use blockquote style.
Aim for 5-8 steps. See CONTRIBUTING.md for full examples.
-->
> - Paperclip orchestrates AI agents for zero-human companies.
> - The plugin system is how optional capabilities extend the control
plane without adding hidden core behavior.
> - Plugin authors need accurate guidance for the current managed
capabilities model.
> - The existing docs under-described managed skills and the
routine-first pattern for durable plugin automation.
> - Content-oriented plugins such as LLM Wiki should model recurring
work with visible managed agents, projects, routines, and skills.
> - This pull request aligns the authoring guide, local development
guide, and longer plugin spec with that model.
> - The benefit is clearer plugin guidance that preserves Paperclip
visibility, budgets, pause controls, and audit trails.
## What Changed
<!-- Bullet list of concrete changes. One bullet per logical unit. -->
- Documented plugin-managed skills alongside managed agents, projects,
and routines.
- Added guidance for content-oriented plugins to use managed projects,
agents, skills, and routines instead of private daemon-like state.
- Updated the manifest/spec examples and capability lists for current
plugin-managed surfaces.
- Clarified when to use managed routines instead of plugin runtime jobs
for board-visible recurring work.
- Added a short local plugin development note pointing authors toward
routine-first automation.
- Addressed Greptile docs feedback by marking top-level `launchers` as
legacy and removing a redundant `slug` from the managed skill example.
## Verification
<!--
How can a reviewer confirm this works? Include test commands, manual
steps, or both. For UI changes, include before/after screenshots.
-->
- `git diff --check public-gh/master...HEAD`
- Reviewed `ROADMAP.md`; this is docs alignment for the completed plugin
system milestone and does not add roadmap-level core feature work.
- Greptile Review: success on the latest head; `3 files reviewed, 0
comments added` after follow-up fixes.
- GitHub PR checks are green on the latest head, including Build,
Typecheck + Release Registry, General tests, serialized server suites,
e2e, Canary Dry Run, policy, Snyk, and aggregate `verify`.
## Risks
<!--
What could go wrong? Mention migration safety, breaking changes,
behavioral shifts, or "Low risk" if genuinely minor.
-->
- Low risk: documentation-only changes.
- Main risk is documentation drift if the plugin API changes again
before these docs are reviewed.
> 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`.
## Model Used
<!--
Required. Specify which AI model was used to produce or assist with
this change. Be as descriptive as possible - include:
• Provider and model name (e.g., Claude, GPT, Gemini, Codex)
• Exact model ID or version (e.g., claude-opus-4-6,
gpt-4-turbo-2024-04-09)
• Context window size if relevant (e.g., 1M context)
• Reasoning/thinking mode if applicable (e.g., extended thinking,
chain-of-thought)
• Any other relevant capability details (e.g., tool use, code execution)
If no AI model was used, write "None — human-authored".
-->
- OpenAI Codex, GPT-5 coding agent with shell and GitHub connector tool
use.
## 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
- [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>
This commit is contained in:
@@ -2,7 +2,12 @@
|
||||
|
||||
This is the short happy-path guide for developing a Paperclip plugin from a folder on your machine. You will scaffold a plugin, run it in watch mode, install it into a running Paperclip instance from an absolute local path, and edit code with the plugin worker reloading after each rebuild.
|
||||
|
||||
For the full alpha surface — manifest fields, capabilities, managed agents/projects/routines, UI slots, scoped API routes — see [`PLUGIN_AUTHORING_GUIDE.md`](./PLUGIN_AUTHORING_GUIDE.md).
|
||||
For the full alpha surface — manifest fields, capabilities, managed agents/projects/routines/skills, UI slots, scoped API routes — see [`PLUGIN_AUTHORING_GUIDE.md`](./PLUGIN_AUTHORING_GUIDE.md).
|
||||
|
||||
If your plugin has background-like recurring work, model it as managed resources:
|
||||
declare managed routines plus managed agents/projects/skills, then reconcile those
|
||||
resources in worker actions. This gives operators visible work items, budgets,
|
||||
pause controls, and consistent audits instead of hidden daemon behavior.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
@@ -126,7 +131,8 @@ When you are done iterating locally, publish the package and reinstall the npm-p
|
||||
|
||||
- **Restart cleanly:** `paperclipai plugin disable <key>` pauses the plugin without removing it. `paperclipai plugin enable <key>` brings it back. `paperclipai plugin uninstall <key>` removes the install record; add `--force` to also purge plugin state and settings.
|
||||
- **Browse examples:** `paperclipai plugin examples` lists the bundled example plugins that ship with the repo, each with a ready-to-run `paperclipai plugin install <path>` line.
|
||||
- **Go deeper:** [`PLUGIN_AUTHORING_GUIDE.md`](./PLUGIN_AUTHORING_GUIDE.md) covers worker capabilities, managed agents/projects/routines, plugin database namespaces, scoped API routes, and the shared UI components in `@paperclipai/plugin-sdk/ui`. [`PLUGIN_SPEC.md`](./PLUGIN_SPEC.md) is the longer-form specification, including future ideas that are not yet implemented.
|
||||
- **Go deeper:** [`PLUGIN_AUTHORING_GUIDE.md`](./PLUGIN_AUTHORING_GUIDE.md) covers worker capabilities, managed agents/projects/routines/skills, plugin database namespaces, scoped API routes, and the shared UI components in `@paperclipai/plugin-sdk/ui`. [`PLUGIN_SPEC.md`](./PLUGIN_SPEC.md) is the longer-form specification, including future ideas that are not yet implemented.
|
||||
- **Routine-first automation:** If your plugin should produce periodic issue work, prefer managed routines and `ctx.routines.managed` reconciliation over custom process loops or unobserved cron code.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ It is intentionally narrower than [PLUGIN_SPEC.md](./PLUGIN_SPEC.md). The spec i
|
||||
- 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-managed surfaces are first-class records (agents, projects, routines, and
|
||||
skills) rather than private plugin-only state.
|
||||
- Plugin-owned JSON API routes must be declared in the manifest and are mounted
|
||||
only under `/api/plugins/:pluginId/api/*`.
|
||||
- The host provides a small shared React component kit through
|
||||
@@ -74,6 +76,7 @@ Worker:
|
||||
- issues, comments, namespaced `plugin:<pluginKey>` origins, blocker relations, checkout assertions, assignment wakeups, and orchestration summaries
|
||||
- agents, plugin-managed agents, and agent sessions
|
||||
- plugin-managed routines
|
||||
- plugin-managed skills
|
||||
- goals
|
||||
- data/actions
|
||||
- streams
|
||||
@@ -134,11 +137,16 @@ paths; they always remain under `/api/plugins/:pluginId/api/*`.
|
||||
|
||||
Plugins that provide durable Paperclip business objects should declare them in
|
||||
the manifest and let the host create or relink the actual records per company.
|
||||
Do this for plugin-owned agents, plugin-owned projects, and recurring automation.
|
||||
Do this for plugin-owned agents, projects, routines, and skills.
|
||||
Do not hide long-lived work behind private plugin state when it should be visible
|
||||
to the board, scoped to a company, audited, budgeted, and assigned like normal
|
||||
Paperclip work.
|
||||
|
||||
Content-oriented plugins, such as LLM Wiki-style ingestion or durable knowledge
|
||||
systems, should use the same pattern: managed projects for operation issues,
|
||||
managed agents plus managed skills for LLM work, and managed routines for
|
||||
ingest, lint, refresh, or maintenance runs.
|
||||
|
||||
Use these surfaces:
|
||||
|
||||
- Managed agents: declare top-level `agents[]` and require
|
||||
@@ -155,10 +163,14 @@ Use these surfaces:
|
||||
jobs that should create visible Paperclip issues. Prefer managed routines over
|
||||
plugin `jobs[]` for recurring business work; plugin jobs are for plugin
|
||||
runtime maintenance that does not need a board-visible task trail.
|
||||
- Managed skills: declare top-level `skills[]` and require `skills.managed`.
|
||||
Use this for reusable plugin capabilities that should be surfaced to operators and
|
||||
synced into Paperclip managed agents.
|
||||
|
||||
Managed resources are resolved by stable plugin keys, not hardcoded database
|
||||
ids. In a worker action or data handler, call `ctx.agents.managed.reconcile()`,
|
||||
`ctx.projects.managed.reconcile()`, and `ctx.routines.managed.reconcile()` for
|
||||
`ctx.projects.managed.reconcile()`, `ctx.routines.managed.reconcile()`, and
|
||||
`ctx.skills.managed.reconcile()` for
|
||||
the current `companyId`. `reconcile()` creates the missing resource, relinks a
|
||||
recoverable binding, or returns the existing resource. `reset()` reapplies the
|
||||
manifest defaults when the operator wants to restore the plugin's suggested
|
||||
@@ -185,6 +197,7 @@ const manifest: PaperclipPluginManifestV1 = {
|
||||
"agents.managed",
|
||||
"projects.managed",
|
||||
"routines.managed",
|
||||
"skills.managed",
|
||||
"instance.settings.register",
|
||||
],
|
||||
entrypoints: {
|
||||
@@ -231,6 +244,13 @@ const manifest: PaperclipPluginManifestV1 = {
|
||||
],
|
||||
},
|
||||
],
|
||||
skills: [
|
||||
{
|
||||
skillKey: "weekly-brief-skills",
|
||||
displayName: "Weekly Briefer",
|
||||
description: "Reusable skill for the managed research workflow.",
|
||||
},
|
||||
],
|
||||
ui: {
|
||||
slots: [
|
||||
{
|
||||
@@ -261,8 +281,9 @@ export default definePlugin({
|
||||
const project = await ctx.projects.managed.reconcile("research", companyId);
|
||||
const agent = await ctx.agents.managed.reconcile("researcher", companyId);
|
||||
const routine = await ctx.routines.managed.reconcile("weekly-brief", companyId);
|
||||
const skill = await ctx.skills.managed.reconcile("weekly-brief-skills", companyId);
|
||||
|
||||
return { project, agent, routine };
|
||||
return { project, agent, routine, skill };
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -270,14 +291,18 @@ export default definePlugin({
|
||||
|
||||
Authoring rules:
|
||||
|
||||
- Keep keys stable once published. Renaming `agentKey`, `projectKey`, or
|
||||
`routineKey` creates a new managed resource from the host's point of view.
|
||||
- Keep keys stable once published. Renaming `agentKey`, `projectKey`,
|
||||
`routineKey`, or `skillKey` creates a new managed resource from the host's
|
||||
point of view.
|
||||
- Use managed agents for plugin-provided labor. Use `ctx.agents.invoke()` or
|
||||
`ctx.agents.sessions` only after you have a real agent id, either selected by
|
||||
the operator or resolved from `ctx.agents.managed`.
|
||||
- Use managed routines for recurring or externally triggered work that should
|
||||
produce tasks. Schedule, webhook, and API triggers are visible routine
|
||||
triggers, and each run has the normal Paperclip issue/audit trail.
|
||||
- Use managed skills for reusable operator-visible capabilities that are shared
|
||||
by managed agents. Reconcile skill declarations by `skillKey` and keep the
|
||||
declared skill markdown and files in sync with agent behavior.
|
||||
- Use managed projects to keep plugin-generated work organized and to give
|
||||
project-scoped plugin UI a stable home. For filesystem access inside a
|
||||
project, still resolve project workspaces through `ctx.projects`.
|
||||
@@ -300,6 +325,7 @@ Mount surfaces currently wired in the host include:
|
||||
- `settingsPage`
|
||||
- `dashboardWidget`
|
||||
- `sidebar`
|
||||
- `routeSidebar`
|
||||
- `sidebarPanel`
|
||||
- `detailTab`
|
||||
- `taskDetailView`
|
||||
@@ -317,6 +343,10 @@ Paperclip-native control. The host owns the implementation, so plugins inherit
|
||||
the board's current styling, ordering, recent selections, and dark-mode behavior
|
||||
without importing `ui/src` internals.
|
||||
|
||||
Prefer shared components for common Paperclip UX patterns to reduce drift and
|
||||
deprecation risk, especially for task/assignment flows and routine or sidebar-like
|
||||
plugin screens.
|
||||
|
||||
Currently exposed components include:
|
||||
|
||||
- `MarkdownBlock` and `MarkdownEditor` for rendered and editable markdown.
|
||||
|
||||
@@ -319,7 +319,10 @@ export interface PaperclipPluginManifestV1 {
|
||||
version: string;
|
||||
displayName: string;
|
||||
description: string;
|
||||
author: string;
|
||||
categories: Array<"connector" | "workspace" | "automation" | "ui">;
|
||||
minimumHostVersion?: string;
|
||||
/** @deprecated Use `minimumHostVersion` instead. Retained for backwards compatibility. */
|
||||
minimumPaperclipVersion?: string;
|
||||
capabilities: string[];
|
||||
entrypoints: {
|
||||
@@ -335,9 +338,33 @@ export interface PaperclipPluginManifestV1 {
|
||||
description: string;
|
||||
parametersSchema: JsonSchema;
|
||||
}>;
|
||||
database?: PluginDatabaseDeclaration;
|
||||
apiRoutes?: PluginApiRouteDeclaration[];
|
||||
environmentDrivers?: PluginEnvironmentDriverDeclaration[];
|
||||
agents?: PluginManagedAgentDeclaration[];
|
||||
projects?: PluginManagedProjectDeclaration[];
|
||||
routines?: PluginManagedRoutineDeclaration[];
|
||||
skills?: PluginManagedSkillDeclaration[];
|
||||
localFolders?: PluginLocalFolderDeclaration[];
|
||||
/** Legacy top-level launcher declarations. Prefer `ui.launchers` for new manifests. */
|
||||
launchers?: PluginLauncherDeclaration[];
|
||||
ui?: {
|
||||
launchers?: PluginLauncherDeclaration[];
|
||||
slots: Array<{
|
||||
type: "page" | "detailTab" | "dashboardWidget" | "sidebar" | "settingsPage";
|
||||
type: "page"
|
||||
| "detailTab"
|
||||
| "taskDetailView"
|
||||
| "dashboardWidget"
|
||||
| "sidebar"
|
||||
| "routeSidebar"
|
||||
| "sidebarPanel"
|
||||
| "projectSidebarItem"
|
||||
| "globalToolbarButton"
|
||||
| "toolbarButton"
|
||||
| "contextMenuItem"
|
||||
| "commentAnnotation"
|
||||
| "commentContextMenuItem"
|
||||
| "settingsPage";
|
||||
id: string;
|
||||
displayName: string;
|
||||
/** Which export name in the UI bundle provides this component */
|
||||
@@ -354,10 +381,17 @@ Rules:
|
||||
- `id` must be globally unique
|
||||
- `id` should normally equal the npm package name
|
||||
- `apiVersion` must match the host-supported plugin API version
|
||||
- `minimumHostVersion` is preferred, with `minimumPaperclipVersion` retained for
|
||||
backwards compatibility
|
||||
- `capabilities` must be static and install-time visible
|
||||
- config schema must be JSON Schema compatible
|
||||
- `entrypoints.ui` points to the directory containing the built UI bundle
|
||||
- `ui.slots` declares which extension slots the plugin fills, so the host knows what to mount without loading the bundle eagerly; each slot references an `exportName` from the UI bundle
|
||||
- declare managed declarations with the matching `*.managed` capability:
|
||||
- `agents` → `agents.managed`
|
||||
- `projects` → `projects.managed`
|
||||
- `routines` → `routines.managed`
|
||||
- `skills` → `skills.managed`
|
||||
|
||||
## 11. Agent Tools
|
||||
|
||||
@@ -631,6 +665,22 @@ Plugins that need filesystem, git, terminal, or process operations handle those
|
||||
|
||||
Trusted orchestration plugins can create and update Paperclip issues through `ctx.issues` instead of importing server internals. The public issue contract includes parent/project/goal links, board or agent assignees, blocker IDs, labels, billing code, request depth, execution workspace inheritance, and plugin origin metadata.
|
||||
|
||||
Plugins that perform durable work should declare managed Paperclip resources rather than using private plugin state:
|
||||
|
||||
- `agents` + `ctx.agents.managed.*` for named, invokable operators (`agents.managed` required)
|
||||
- `projects` + `ctx.projects.managed.*` for stable, scoped issue/workspace ownership (`projects.managed` required)
|
||||
- `routines` + `ctx.routines.managed.*` for schedule/webhook/manual execution with issue trails (`routines.managed` required)
|
||||
- `skills` + `ctx.skills.managed.*` for reusable agent capabilities (`skills.managed` required)
|
||||
|
||||
The LLM Wiki plugin is the current reference for this pattern: it declares managed
|
||||
agents, projects, routines, and skills in manifest, reconciles them per company,
|
||||
and uses managed routines for periodic wiki maintenance and ingest operations.
|
||||
Content-oriented plugins should follow the same model instead of running
|
||||
unmanaged background loops: make the LLM-facing worker an operator-visible
|
||||
managed agent, attach reusable prompt/tool guidance as managed skills, keep
|
||||
operation issues in a managed project, and drive recurring work through managed
|
||||
routines.
|
||||
|
||||
Origin rules:
|
||||
|
||||
- Built-in core issues keep built-in origins such as `manual` and `routine_execution`.
|
||||
@@ -746,20 +796,38 @@ The host enforces capabilities in the SDK layer and refuses calls outside the gr
|
||||
- `activity.read`
|
||||
- `costs.read`
|
||||
- `issues.orchestration.read`
|
||||
- `database.namespace.read`
|
||||
|
||||
### Data Write
|
||||
|
||||
- `issues.create`
|
||||
- `issues.update`
|
||||
- `issue.comments.create`
|
||||
- `issue.interactions.create`
|
||||
- `issue.documents.write`
|
||||
- `issue.relations.write`
|
||||
- `issues.checkout`
|
||||
- `issues.wakeup`
|
||||
- `assets.write`
|
||||
- `assets.read`
|
||||
- `activity.log.write`
|
||||
- `metrics.write`
|
||||
- `telemetry.track`
|
||||
- `assets.read`
|
||||
- `assets.write`
|
||||
- `database.namespace.migrate`
|
||||
- `database.namespace.write`
|
||||
- `goals.create`
|
||||
- `goals.update`
|
||||
- `projects.managed`
|
||||
- `routines.managed`
|
||||
- `skills.managed`
|
||||
- `agents.managed`
|
||||
- `agents.pause`
|
||||
- `agents.resume`
|
||||
- `agents.invoke`
|
||||
- `agent.sessions.create`
|
||||
- `agent.sessions.list`
|
||||
- `agent.sessions.send`
|
||||
- `agent.sessions.close`
|
||||
|
||||
### Plugin State
|
||||
|
||||
@@ -772,8 +840,10 @@ The host enforces capabilities in the SDK layer and refuses calls outside the gr
|
||||
- `events.emit`
|
||||
- `jobs.schedule`
|
||||
- `webhooks.receive`
|
||||
- `local.folders`
|
||||
- `http.outbound`
|
||||
- `secrets.read-ref`
|
||||
- `environment.drivers.register`
|
||||
|
||||
### Agent Tools
|
||||
|
||||
@@ -786,6 +856,7 @@ The host enforces capabilities in the SDK layer and refuses calls outside the gr
|
||||
- `ui.page.register`
|
||||
- `ui.detailTab.register`
|
||||
- `ui.dashboardWidget.register`
|
||||
- `ui.commentAnnotation.register`
|
||||
- `ui.action.register`
|
||||
|
||||
## 15.2 Forbidden Capabilities
|
||||
@@ -894,6 +965,7 @@ Job rules:
|
||||
3. The host prevents overlapping execution of the same plugin/job combination unless explicitly allowed later.
|
||||
4. Every job run is recorded in Postgres.
|
||||
5. Failed jobs are retryable.
|
||||
6. For recurring business workflows that should create visible Paperclip work, prefer managed routines and managed resources over jobs. Jobs remain useful for private plugin-runtime maintenance tasks.
|
||||
|
||||
## 18. Webhooks
|
||||
|
||||
|
||||
Reference in New Issue
Block a user