mirror of
https://github.com/paperclipai/paperclip-docs.git
synced 2026-11-07 06:16:07 -10:00
Refactor docs IA into Guides + Reference tiers; add 10 new guides
Reorganise all docs under a two-tier sidebar (Guides / Reference) with
tier-grouped landing cards that auto-size to the section count (3-col
default, 2x2 when a tier has 4 cards).
Moves
- docs/user-guides/guides/** -> docs/guides/{welcome,getting-started,
day-to-day,org,projects-workflow,power}/
- docs/{api,cli,adapters,deploy} -> docs/reference/{api,cli,adapters,deploy}
- New docs/administration/ for company/settings/plugins/cli-auth
- 265 internal cross-links rewritten by scripts/rewrite-links.mjs
- site/redirects.json preserves old URLs
New guides
- guides/org/agents.md (absorbs how-agents-work.md)
- guides/day-to-day/issues.md (absorbs managing-tasks.md)
- guides/day-to-day/costs.md (absorbs costs-and-budgets.md)
- guides/projects-workflow/routines.md (absorbs heartbeats-and-routines.md)
- guides/projects-workflow/workspaces.md (absorbs execution-workspaces.md)
- guides/projects-workflow/projects.md
- guides/projects-workflow/goals.md
- guides/org/adapters.md (AdapterManager UI walkthrough)
- administration/{company,settings,plugins,cli-auth}.md
Expanded
- approvals.md, skills.md, org-structure.md with missing UI walkthroughs
Site
- content.json: tier field, merged Welcome into Getting Started, collapsed
Administration to single section
- app.js: tier-grouped sidebar + landing; redirect map on route changes
- styles.css: tier headers and adaptive landing grid
Screenshots
- docs/user-guides/screenshots/SCREENSHOTS_TODO.md runbook for capturing
on a disposable demo instance. No screenshots captured in this branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
9415607e4f
commit
56b8d7d52d
80 files changed
+5077
-1266
No files matched your search
@@ -0,0 +1,189 @@
|
||||
# CLI Auth & Board Claim
|
||||
|
||||
Paperclip has two separate identity flows for humans on the command line and browser:
|
||||
|
||||
- **Board claim** — a one-time bootstrap step that promotes your browser-authenticated user to the owner of a freshly started authenticated instance. It migrates the trusted "local board" placeholder into a real, named human account.
|
||||
- **CLI auth** — the everyday flow that pairs a local `paperclipai` CLI process with a signed-in board user, so the CLI can call `/api` endpoints on your behalf without you typing credentials into your shell.
|
||||
|
||||
Both flows use a short-lived, server-minted challenge and a browser approval page. Both are designed so the secret material (the board API token, the ownership migration) never leaves the browser or the CLI process that asked for it.
|
||||
|
||||
This guide walks through both, shows the approval pages, and explains how to use the resulting tokens with the CLI.
|
||||
|
||||
---
|
||||
|
||||
## When you need each flow
|
||||
|
||||
- You just installed Paperclip in **authenticated** deployment mode on a server, and the loopback trust is still active → run **Board claim** once.
|
||||
- You want to run `paperclipai` commands against that instance from your laptop → run **CLI auth** (`paperclipai auth login`).
|
||||
- You're using **trusted** deployment mode (loopback-only local dev) → neither flow is required; the CLI and browser already trust the loopback.
|
||||
|
||||
---
|
||||
|
||||
## Claiming a board from the CLI
|
||||
|
||||
Board claim only exists when _all_ of these are true:
|
||||
|
||||
- Deployment mode is `authenticated`.
|
||||
- The only instance admin is the built-in `local-board` placeholder (i.e., no human has claimed the instance yet).
|
||||
- The claim challenge hasn't expired (the server keeps the active challenge for 24 hours and refreshes it automatically after that).
|
||||
|
||||
When the server starts and those conditions match, it prints a **Board claim** URL to the server log. It looks like:
|
||||
|
||||
```
|
||||
http://localhost:3000/board-claim/<token>?code=<code>
|
||||
```
|
||||
|
||||
The `token` is 48 hex characters; the `code` is 24 hex characters. Together they identify exactly one pending claim.
|
||||
|
||||
Open the URL in your browser. If you aren't signed in yet, the page will ask you to sign in or create an account first and bounce you back to the same URL after authentication.
|
||||
|
||||

|
||||
|
||||
Once you're signed in, the page shows a panel titled **Claim Board ownership** with a short explanation ("This will promote your user to instance admin and migrate company ownership access from local trusted mode.") and a single **Claim ownership** button.
|
||||
|
||||
Click it and the server does three things in a single transaction:
|
||||
|
||||
1. Inserts an `instance_admin` role for your user (if you weren't one already).
|
||||
2. Deletes the `local-board` placeholder admin.
|
||||
3. Walks every company in the database and, for each one, either creates an `active`, `owner`-role membership for you or reactivates your existing membership and sets the role to `owner`.
|
||||
|
||||
The button swaps to **Claiming…** during the request. When it returns successfully the page switches to **Board ownership claimed** with a link to open the board. The in-memory challenge is marked claimed and cannot be reused.
|
||||
|
||||
If the challenge expired, or the URL is malformed, the page shows **Claim challenge unavailable** instead. Restart the server (or wait for the next refresh) to mint a new challenge.
|
||||
|
||||
---
|
||||
|
||||
## Device-code flow: `paperclipai auth login`
|
||||
|
||||
For day-to-day CLI use, the flow is a device-code-style pairing. The CLI creates a challenge, opens your browser, you approve it, and the CLI finishes with a stored token.
|
||||
|
||||
### Starting the login
|
||||
|
||||
From the machine you want to use the CLI on:
|
||||
|
||||
```sh
|
||||
paperclipai auth login
|
||||
```
|
||||
|
||||
The CLI:
|
||||
|
||||
1. POSTs to `/api/cli-auth/challenges` with a description of itself (`command`, `clientName`, `requestedAccess`, and an optional `requestedCompanyId`).
|
||||
2. Gets back a challenge containing `token`, `boardApiToken`, `approvalUrl`, `pollPath`, `expiresAt`, and a polling interval.
|
||||
3. Prints the approval URL to your terminal and tries to open it in your default browser.
|
||||
4. Starts polling the server at `pollPath` until the challenge is approved, cancelled, or expires.
|
||||
|
||||
Common flags:
|
||||
|
||||
```sh
|
||||
# Request instance-admin approval (only instance admins can approve this)
|
||||
paperclipai auth login --instance-admin
|
||||
|
||||
# Scope to a specific company
|
||||
paperclipai auth login --company-id <company-id>
|
||||
|
||||
# Target a non-default server
|
||||
paperclipai auth login --api-base https://paperclip.example.com
|
||||
```
|
||||
|
||||
### Approving the challenge in the browser
|
||||
|
||||
Your browser lands on a page titled **Approve Paperclip CLI access**:
|
||||
|
||||

|
||||
|
||||
The panel shows what the CLI is asking for:
|
||||
|
||||
- **Command** — the CLI command that initiated the login (e.g. `paperclipai auth login`).
|
||||
- **Client** — the `clientName` the CLI sent, default `paperclipai cli`.
|
||||
- **Requested access** — either `Board` or `Instance admin`.
|
||||
- **Requested company** — only shown if the CLI asked to be scoped to a company.
|
||||
|
||||
Two buttons at the bottom:
|
||||
|
||||
- **Approve CLI access** — only enabled if your signed-in user is allowed to approve this level of access. An instance-admin challenge requires an instance-admin approver.
|
||||
- **Cancel** — rejects the challenge. The CLI sees the `cancelled` status on its next poll and exits with an error.
|
||||
|
||||
If you aren't signed in, the page first shows **Sign in required** with a button that returns you here after authentication. If the challenge has already been approved, cancelled, or expired by the time you visit the URL, the page reflects that state instead of offering the buttons.
|
||||
|
||||
### Finishing on the CLI
|
||||
|
||||
Once you click **Approve**, the CLI's next poll sees status `approved`, calls `/api/cli-auth/me` with the board API token to confirm identity, and writes the credential to the local credential store. It prints a success summary:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"apiBase": "https://paperclip.example.com",
|
||||
"userId": "usr_...",
|
||||
"approvalUrl": "https://paperclip.example.com/cli-auth/..."
|
||||
}
|
||||
```
|
||||
|
||||
If you cancel or let the challenge expire, the CLI errors out with `CLI auth challenge was cancelled.` or `CLI auth challenge expired before approval.` — re-run `paperclipai auth login` to try again.
|
||||
|
||||
---
|
||||
|
||||
## After claiming: using and rotating the token
|
||||
|
||||
### Where the token lives
|
||||
|
||||
The CLI stores the board API token (and the user id it resolves to) in the platform-appropriate config directory, keyed by `apiBase`. You don't need to copy the token anywhere — every CLI command that accepts `--api-base` will look up the matching stored credential automatically.
|
||||
|
||||
### Using it
|
||||
|
||||
Once logged in, the CLI sends `Authorization: Bearer <stored-token>` on every request. You can verify by running:
|
||||
|
||||
```sh
|
||||
paperclipai auth whoami
|
||||
```
|
||||
|
||||
which calls `/api/cli-auth/me` and prints:
|
||||
|
||||
```json
|
||||
{
|
||||
"user": { "id": "...", "name": "...", "email": "..." },
|
||||
"userId": "...",
|
||||
"isInstanceAdmin": true,
|
||||
"companyIds": ["..."],
|
||||
"source": "board-cli",
|
||||
"keyId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
All other CLI commands — the ones documented in [Setup Commands](../reference/cli/setup-commands.md) and Control-Plane Commands — reuse this credential unless you pass an explicit `--token` or override `PAPERCLIP_API_KEY`.
|
||||
|
||||
### Rotating and revoking
|
||||
|
||||
When you want to rotate the credential, log out and log back in:
|
||||
|
||||
```sh
|
||||
paperclipai auth logout
|
||||
paperclipai auth login
|
||||
```
|
||||
|
||||
`paperclipai auth logout`:
|
||||
|
||||
1. POSTs to `/api/cli-auth/revoke-current` with the stored token to revoke it server-side.
|
||||
2. Deletes the local credential entry for this `apiBase`.
|
||||
|
||||
If the server-side revoke fails (network error, token already invalid) the local credential is still removed — you won't be stuck with a stale entry. A new `paperclipai auth login` mints a fresh challenge and a fresh token with no relationship to the old one.
|
||||
|
||||
### Multiple servers and users
|
||||
|
||||
The credential store is keyed by normalised `apiBase`, so one machine can hold separate credentials for a local dev instance, a staging server, and a production server at the same time. Switching between them is a matter of passing `--api-base` (or setting it in config). Logging in as a different user for the same `apiBase` replaces the existing credential for that server.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **"Invalid CLI auth URL."** — You opened the approval page with a missing `id` or `token` query param. Re-run `paperclipai auth login` to get a fresh link.
|
||||
- **"CLI auth challenge unavailable"** — The token is valid but the server no longer has a matching challenge (it expired, or the server restarted). Start the login again.
|
||||
- **"This challenge requires instance-admin access."** — You're approving an `--instance-admin` challenge but your signed-in user isn't an instance admin. Sign in with an admin account and retry.
|
||||
- **"CLI auth challenge expired before approval."** on the CLI — You let the browser tab sit too long. Just rerun `paperclipai auth login`.
|
||||
- **Board claim page shows "Claim challenge unavailable."** — The one-time ownership challenge has already been consumed, or it expired. If the instance has no real admins yet, restart the server — it mints a new challenge and reprints the URL in the logs.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Setup Commands](../reference/cli/setup-commands.md) — installing, onboarding, and configuring a Paperclip instance from the CLI.
|
||||
- [Authentication (API)](../reference/api/authentication.md) — the underlying authentication model for board users, agents, and runs.
|
||||
@@ -0,0 +1,267 @@
|
||||
# Company Administration
|
||||
|
||||
Every Paperclip company has a small cluster of surfaces that control its identity, its membership, and its portability. These pages are where you, as a board user (or a member with the right grants), set the company's name and logo, decide who else can sign in, hand out invite links, review join requests, and move the whole company in and out of a portable package. The CEO agent never touches most of this — it is deliberately human territory.
|
||||
|
||||
This guide walks through each of those surfaces in the order you typically meet them: **General Settings**, **Access & Members**, **Invites**, **Join Requests**, **Export**, and **Import**. If you are looking for how hiring approvals themselves work, see the [Approvals guide](../guides/day-to-day/approvals.md). If you want to understand the reporting tree that sits underneath the membership model, see [Org Structure](../guides/org/org-structure.md).
|
||||
|
||||
---
|
||||
|
||||
## General Settings
|
||||
|
||||
The **Company Settings** page groups the settings that apply to the whole company rather than to any one agent or project. Open it from the sidebar under the company name.
|
||||
|
||||

|
||||
|
||||
### Name and description
|
||||
|
||||
The **General** section has two fields:
|
||||
|
||||
- **Company name** — the display name shown in the company switcher and across the UI.
|
||||
- **Description** — an optional one-line description shown on the company profile. Leave it empty if you do not want one.
|
||||
|
||||
Changes are not persisted until you click **Save changes**. The save button only appears when one of the fields is dirty, and it is disabled if the name is empty.
|
||||
|
||||
### Logo and brand color
|
||||
|
||||
The **Appearance** section controls the small square tile that represents your company in the sidebar and switcher. You have two inputs:
|
||||
|
||||
- **Logo** — upload a PNG, JPEG, WEBP, GIF, or SVG file. After upload, a **Remove logo** button appears next to the file input so you can clear it again. If an upload fails, the error message is shown inline.
|
||||
- **Brand color** — sets the hue used for the auto-generated pattern icon when no logo is present. The color is configurable through the native color picker or a free-form `#rrggbb` text field. Leave it empty for the auto-generated color; a **Clear** button appears when a value is set.
|
||||
|
||||
A live preview of the tile (logo and brand color combined) is rendered to the left of the fields so you can see the result before saving.
|
||||
|
||||
### Hiring
|
||||
|
||||
The **Hiring** section contains one switch:
|
||||
|
||||
- **Require board approval for new hires** — when on, every agent the CEO or a manager tries to hire stays pending until a board member approves it from the approvals queue. When off, hires go through immediately. This ties directly into the flow described in the [Approvals guide](../guides/day-to-day/approvals.md).
|
||||
|
||||
The toggle saves immediately; there is no separate save button for this setting.
|
||||
|
||||
### Feedback Sharing
|
||||
|
||||
The **Feedback Sharing** section controls whether AI outputs you explicitly vote on in Paperclip may also be shared with Paperclip Labs. The toggle is labelled **Allow sharing voted AI outputs with Paperclip Labs**.
|
||||
|
||||
Two things to know:
|
||||
|
||||
- Votes are always stored locally, regardless of this setting. The toggle only controls whether the voted outputs become eligible for sharing.
|
||||
- The section displays the current terms version, and, once sharing is enabled, the timestamp of when it was turned on and who enabled it. A **Read our terms of service** link is shown when the terms URL is configured.
|
||||
|
||||
### Company Packages shortcuts
|
||||
|
||||
Below the feedback section, a **Company Packages** panel links out to the dedicated **Export** and **Import** pages. These are the same pages covered in the last two sections of this guide; the Settings page itself does not perform the export or import.
|
||||
|
||||
### Danger Zone
|
||||
|
||||
At the bottom of the page is a **Danger Zone** with an **Archive company** button. Archiving hides the company from the sidebar and persists the change in the database. A confirmation dialog appears before the archive takes effect, and the UI automatically switches you to the next non-archived company if one is available.
|
||||
|
||||
---
|
||||
|
||||
## Access & Members
|
||||
|
||||
The **Company Access** page is where you manage human memberships: who belongs to the company, what role they hold, and which permissions they have on top of that role. Open it from **Settings → Access**, or directly from the sidebar.
|
||||
|
||||

|
||||
|
||||
### Roles
|
||||
|
||||
Paperclip uses four human membership roles. The role determines a set of implicit permission grants:
|
||||
|
||||
- **Owner** — full company access. Includes creating agents, inviting humans and agents, managing members and grants, assigning tasks, and approving join requests.
|
||||
- **Admin** — operator with invite and approval powers. Can create agents, invite users, assign tasks, and approve join requests.
|
||||
- **Operator** — a hands-on member who helps run work. Can assign tasks.
|
||||
- **Viewer** — read-only access. No built-in grants.
|
||||
|
||||
The role drop-down in the edit dialog also accepts **Unset**, which leaves the member without any implicit grants.
|
||||
|
||||
> **Note:** Paperclip's agent chain of command (CEO, managers, reports) is separate from these human roles. Human roles gate access to the Paperclip UI and company-level actions; agent roles describe the reporting tree inside the company. See [Org Structure](../guides/org/org-structure.md) for the agent side.
|
||||
|
||||
### The members list
|
||||
|
||||
The members section shows one row per human member with:
|
||||
|
||||
- **User account** — display name and email (or principal id if neither is set).
|
||||
- **Role** — the human membership role, or "Unset".
|
||||
- **Status** — `active`, `pending`, or `suspended`, rendered as a coloured badge.
|
||||
- **Grants** — a comma-separated summary of any explicit permission grants attached to the member. Shows "No explicit grants" when the member only relies on implicit role grants.
|
||||
- **Action** — an **Edit** button that opens the member editor.
|
||||
|
||||
If you do not have permission to manage members, Paperclip replaces the page body with a message explaining why. An instance admin who is viewing the page without an active company membership sees a banner noting that the current account has admin-level access but is not a member of this company.
|
||||
|
||||
### Editing a member
|
||||
|
||||
Clicking **Edit** opens a dialog with three controls:
|
||||
|
||||
1. **Company role** — the same role list as above, plus "Unset".
|
||||
2. **Membership status** — `Active`, `Pending`, or `Suspended`.
|
||||
3. **Grants** — a two-column grid of explicit permission grants. A read-only panel above the grid lists the grants the selected role provides implicitly. Checking a box here stores the grant explicitly on the member, so it persists even if the role later changes.
|
||||
|
||||
Available permission keys include `agents:create`, `users:invite`, `users:manage_permissions`, `tasks:assign`, `tasks:assign_scope`, and `joins:approve`. Each box is labelled with a human name and its underlying key for reference.
|
||||
|
||||
Click **Save access** to persist the changes, or **Cancel** to close the dialog without saving.
|
||||
|
||||
### Adding and removing members
|
||||
|
||||
The Access page does not have an "Add member" button. New humans enter the company through the invite flow described in the next section, which either produces a direct membership or a pending join request depending on how the invite is consumed. To remove a human, set their **Status** to `suspended` through the Edit dialog; the row stays visible so the audit trail is preserved.
|
||||
|
||||
### Pending human joins
|
||||
|
||||
If you hold the `joins:approve` grant and there are pending human join requests, a **Pending human joins** card appears above the members list. Each entry shows the requester's name, email, and the invite context, with **Approve human** and **Reject human** buttons. This card duplicates the subset of the join request queue that applies to humans — full coverage (including agent joins) lives on the queue page.
|
||||
|
||||
---
|
||||
|
||||
## Invites
|
||||
|
||||
The **Company Invites** page creates invite links for new humans. Each link is single-use — the first successful use consumes the link and creates or reuses a join request before approval.
|
||||
|
||||

|
||||
|
||||
### Creating an invite
|
||||
|
||||
The **Create invite** card has one required choice: which role the invite should request by default. The options are the four roles described above — **Viewer**, **Operator** (the default), **Admin**, and **Owner** — each rendered with a short description and a one-line summary of what the role gets.
|
||||
|
||||
Clicking **Create invite** does three things:
|
||||
|
||||
1. Generates a new invite link against the current Paperclip domain.
|
||||
2. Copies the URL to your clipboard if the browser allows it.
|
||||
3. Shows the invite in a **Latest invite link** panel below the form.
|
||||
|
||||
If the clipboard is unavailable, Paperclip shows a warning toast and you can copy the URL manually from the field. The invite URL is clickable as an **Open invite** button for quick testing in another tab.
|
||||
|
||||
### Invite expiry and state
|
||||
|
||||
Invite links have four possible states, shown as a badge in the history table: **Active**, **Accepted**, **Expired**, and **Revoked**. Only `Active` invites can be consumed — the link becomes `Accepted` on first use and can be `Revoked` manually from the same page.
|
||||
|
||||
### Invite history
|
||||
|
||||
Below the creator is a paginated **Invite history** table with one row per invite:
|
||||
|
||||
- **State** — the badge above.
|
||||
- **Role** — the default role the invite requests.
|
||||
- **Invited by** — the name and email of the human who created the invite.
|
||||
- **Created** — the creation timestamp.
|
||||
- **Join request** — a link to the join request queue if the invite has produced one.
|
||||
- **Action** — a **Revoke** button for invites still in the `Active` state.
|
||||
|
||||
The table shows five invites at a time and includes a **View more** button when there are more pages. An **Open join request queue** link in the header jumps to the full queue.
|
||||
|
||||
### What the invited person sees
|
||||
|
||||
When a human opens the invite URL, they land on a Paperclip join page that creates a join request tied to the invite. If the invite carries a default role (for example **Operator**), that role is attached to the request so approvers can see it in context. The member does not become active until someone with `joins:approve` approves the request.
|
||||
|
||||
---
|
||||
|
||||
## Join Requests
|
||||
|
||||
The **Join Request Queue** consolidates every join request — human and agent — that has come in against the company. Open it from `/inbox/requests`, or from the **Open join request queue** link in Invites.
|
||||
|
||||

|
||||
|
||||
### Filters
|
||||
|
||||
Two drop-downs sit above the list:
|
||||
|
||||
- **Status** — `Pending approval` (the default), `Approved`, or `Rejected`.
|
||||
- **Request type** — `All`, `Human`, or `Agent`.
|
||||
|
||||
Changing either filter refreshes the list. "Approved" and "Rejected" are useful when you want to audit past decisions.
|
||||
|
||||
### What's on each card
|
||||
|
||||
Each request card shows:
|
||||
|
||||
- A **status** badge (`pending approval`, `approved`, or `rejected`), a **request type** badge (`human` or `agent`), and, for agent requests, an **adapter type** badge.
|
||||
- The requester's name and a secondary identifier — email for humans, capabilities or source IP for agents.
|
||||
- An **Invite context** panel with the invite's `allowedJoinTypes` and default role, plus the invite message if one was attached.
|
||||
- A **Request details** panel with the submission timestamp, source IP, and agent capabilities (when applicable).
|
||||
|
||||
For requests in `pending approval`, the card also shows **Approve** and **Reject** buttons. Once acted on, the card stays in the list under the new status filter for audit purposes but no longer exposes action buttons.
|
||||
|
||||
### Permissions
|
||||
|
||||
The queue requires the `joins:approve` grant. Accounts without it see a "You do not have permission to review join requests for this company." message instead of the list.
|
||||
|
||||
---
|
||||
|
||||
## Export
|
||||
|
||||
The **Company Export** page turns the current company — its agents, projects, tasks, skills, and settings — into a portable markdown package you can download as a zip. Open it from the **Org Chart** header or from the **Export** shortcut on the Settings page.
|
||||
|
||||

|
||||
|
||||
### What gets included
|
||||
|
||||
When the page loads, Paperclip builds a preview of everything that would be exported. By default the preview includes the company metadata, every non-terminated agent, every non-archived project, and every issue. The preview is rendered as a left-hand **Package files** tree, with a selected-file preview on the right.
|
||||
|
||||
The sticky bar at the top of the page shows:
|
||||
|
||||
- The company name and a running count of **selected / total files**.
|
||||
- Any warnings Paperclip produced while building the preview.
|
||||
- A single **Export N files** button that downloads the zip.
|
||||
|
||||
### Selecting what to export
|
||||
|
||||
Every entry in the file tree has a checkbox. Unchecking a file or a directory removes it from the download. A couple of details worth knowing:
|
||||
|
||||
- The tree is searchable with the input above it. Search expands matching directories automatically and restores the previous expansion state when you clear the query.
|
||||
- `README.md` and `.paperclip.yaml` are regenerated on the fly so they always reflect the current selection.
|
||||
- The `tasks/` directory is paginated — ten entries visible by default, with a **Show more issues** button. Checked and search-matched tasks are always pinned above the fold.
|
||||
- Clicking a file opens its content in the preview pane. The URL updates as you navigate so you can deep-link to a specific file in the export.
|
||||
|
||||
### Downloading
|
||||
|
||||
Click **Export N files** to build the final package. Paperclip assembles the zip with the filtered files (including the regenerated README and `.paperclip.yaml`) and triggers a browser download. A success toast confirms the file count and the zip's root path.
|
||||
|
||||
For a deeper description of what lives in each file, see [Export & Import](../guides/power/export-import.md).
|
||||
|
||||
---
|
||||
|
||||
## Import
|
||||
|
||||
The **Company Import** page is the reverse direction: it takes an exported package and loads it into Paperclip, either as a new company or on top of an existing one.
|
||||
|
||||

|
||||
|
||||
### Choosing a source
|
||||
|
||||
The **Import source** section supports two source types:
|
||||
|
||||
- **GitHub repo** — paste a tree or blob URL pointing at a folder that contains a `COMPANY.md`.
|
||||
- **Local zip** — upload a `.zip` package exported from Paperclip. Paperclip warns against re-zipping archives in Finder or Explorer; use the download from the Export page.
|
||||
|
||||
### Choosing a target
|
||||
|
||||
The **Target** drop-down has two options:
|
||||
|
||||
- **Create new company** — the default. An optional **New company name** field lets you override the name from the package.
|
||||
- **Existing company** — apply the package on top of the currently selected company.
|
||||
|
||||
### Collision strategy
|
||||
|
||||
The **Collision strategy** drop-down controls what happens when an incoming item clashes with something that already exists in the target company:
|
||||
|
||||
- **Rename on conflict** — the default. Paperclip proposes a new name (prefixed with the package name, for example `gstack-CEO`) for each conflicting entity.
|
||||
- **Skip on conflict** — existing items are kept and the incoming copy is dropped.
|
||||
- **Replace existing** — overwrites the current entity with the incoming version.
|
||||
|
||||
### Preview
|
||||
|
||||
Click **Preview import** to run the preview without applying anything. The preview populates three things:
|
||||
|
||||
1. A **Renames** list showing every agent and project that collided and what it will be renamed to. Each row has a free-form input for the target name, a **skip** button, and a **confirm rename** button that locks the chosen name in.
|
||||
2. An **Adapters** list for every agent in the package, with a drop-down of available adapters. New agents default to the same adapter as the target company's CEO. A **configure adapter** button expands an inline config form so you can set per-agent values (environment, working directory, etc.) before the import runs.
|
||||
3. A file tree identical in layout to the export tree, but with an **action badge** on each row (`create`, `update`, `skip`, or `replace`) so you can see what Paperclip plans to do to every file. Directories that will be renamed show an arrow and the target name.
|
||||
|
||||
The sticky bar at the top counts selected files and surfaces the number of conflicts and errors.
|
||||
|
||||
### Applying the import
|
||||
|
||||
When you are happy with the preview, click **Import N files**. The button is disabled if there are errors in the preview or no files selected. On success, Paperclip refreshes the companies list, navigates you to the dashboard of the imported (or updated) company, and restores the imported sidebar order if the package included one.
|
||||
|
||||
Existing-company imports are non-destructive in the safe import path: incoming issues are always created as new issues, and `replace` is blocked by the server for safe imports. If you need a full replace, use the dedicated instance-admin flow instead.
|
||||
|
||||
See [Export & Import](../guides/power/export-import.md) for the structural reference — the field-by-field description of the package format complements the walkthrough above.
|
||||
|
||||
---
|
||||
|
||||
Company administration covers the pieces of Paperclip that sit around the agents rather than inside them: identity, membership, and portability. Once you have the company set up and people invited in, day-to-day work happens in the approvals queue, the dashboard, and the task surfaces that the rest of this guide set covers.
|
||||
@@ -0,0 +1,219 @@
|
||||
# Plugins
|
||||
|
||||
Some things Paperclip does out of the box — run agents, manage tasks, call adapters. Plenty of other things it does not, because they're too specific to your setup: a Hello World widget on the dashboard, a file browser for your project workspaces, a smoke-test runner for a third-party service you happen to use. Plugins are how you add those.
|
||||
|
||||
A plugin is a self-contained package that installs into Paperclip, runs its own background worker, and can contribute its own pages, widgets, and settings into the Paperclip UI. Unlike an [agent adapter](../guides/org/agent-adapters.md) — which teaches Paperclip how to talk to a new AI model — and unlike a [skill](../guides/org/skills.md) — which is a reusable instruction document an agent loads into its prompt — a plugin is a piece of software that extends Paperclip itself. It has its own code, its own process, and its own surface in the UI.
|
||||
|
||||
> Plugins are in alpha. The runtime and APIs are still shifting, so expect breaking changes for now and pin versions where you can.
|
||||
|
||||
---
|
||||
|
||||
## The Plugin Manager
|
||||
|
||||
The Plugin Manager lives under **Settings → Plugins**. It's the single place where you install, enable, disable, configure, and uninstall plugins.
|
||||
|
||||

|
||||
|
||||
The page has two sections:
|
||||
|
||||
- **Available Plugins** — bundled examples shipped with your Paperclip checkout. Useful for seeing what plugins can do and for smoke-testing the plugin runtime on a fresh instance.
|
||||
- **Installed Plugins** — everything you've installed on this Paperclip instance. Each row shows the plugin name, its package and version, a short description, a status badge, and quick actions.
|
||||
|
||||
### Status badges
|
||||
|
||||
Every installed plugin has a status that tells you what the runtime is doing with it.
|
||||
|
||||
| Status | What it means in practice |
|
||||
|---|---|
|
||||
| **ready** | The plugin is installed, enabled, and its worker is running. This is the steady state you want. |
|
||||
| **disabled** | You (or another operator) turned the plugin off. Its worker is stopped, scheduled jobs don't fire, and any UI contributions are hidden. Config and data are preserved. |
|
||||
| **error** | Something went wrong — the worker crashed on startup, a health check failed, or an upgrade broke. The plugin row shows a short error summary and a **View full error** button for the full stack trace. |
|
||||
| **upgrade_pending** | A new version of the plugin has been staged, and it asks for capabilities the installed version didn't have. Paperclip is holding it offline until you approve the new permissions. |
|
||||
| **installed** | A transient state right after install, before the first successful load. You usually won't see it for long — the plugin moves to `ready` (or `error`) within a second or two. |
|
||||
| **uninstalled** | The plugin has been removed. It won't appear in the list anymore unless you reinstall it. |
|
||||
|
||||
If the badge is green it's running. If it's grey it's intentionally off. If it's red or amber, read the message — the UI is telling you something needs your attention.
|
||||
|
||||
### Reading an error row
|
||||
|
||||
When a plugin is in `error`, the row expands to show a short error summary — the first non-empty line of the stored error message — and a **View full error** button. Clicking it opens a dialog with the complete stack trace or failure output, wrapped and scrollable.
|
||||
|
||||
The summary is usually enough to know what to do:
|
||||
|
||||
- **"Cannot find module ..."** or **"Failed to load manifest"** — the package is malformed or its build output is missing. Reinstall it, or rebuild it if you're developing locally.
|
||||
- **"Worker exited with code 1"** — the worker crashed on startup. The full error has the stack trace. Most common causes: missing configuration (go fill in the settings form), a secret that hasn't been provided, or an external service the plugin depends on being unreachable.
|
||||
- **"Health check failed: ..."** — the worker started but a health check didn't pass. The message names which check failed; fix the underlying issue, then click the power button to re-enable the plugin.
|
||||
|
||||
An errored plugin stays installed — you don't need to uninstall and reinstall to try again. Once you've addressed the cause, clicking **Enable** puts it through the `error → ready` transition and Paperclip tries to start the worker again.
|
||||
|
||||
---
|
||||
|
||||
## Installing a plugin
|
||||
|
||||

|
||||
|
||||
There are two ways to get a plugin into Paperclip.
|
||||
|
||||
**From the Available Plugins list.** Bundled example plugins ship inside the Paperclip repository. On the Plugin Manager page, scroll to the **Available Plugins** section and click **Install Example** next to the one you want. Paperclip installs it straight from the local checkout — no registry round trip.
|
||||
|
||||
The examples that ship today:
|
||||
|
||||
- **plugin-hello-world-example** — adds a Hello World widget to the dashboard. The minimum-viable plugin, useful as a reference.
|
||||
- **plugin-file-browser-example** — adds a Files link to project sidebars and a file-browser tab to the project detail page, scoped to the project's workspace.
|
||||
- **plugin-kitchen-sink-example** — demonstrates the full plugin surface in one package: pages, widgets, settings forms, scheduled jobs, webhooks. Good for exploring what a plugin can do.
|
||||
- **plugin-authoring-smoke-example** — a thin smoke test for the plugin authoring workflow. Useful if you're working on a plugin of your own and want to confirm the runtime picks it up.
|
||||
|
||||
**By npm package name.** Click the **Install Plugin** button at the top right of the Plugin Manager. Paperclip asks for an npm package name (for example, `@paperclipai/plugin-example`). Submit it and Paperclip fetches, installs, and loads the package. Success or failure appears as a toast, and the plugin shows up in the installed list with its current status.
|
||||
|
||||
In both cases the install flow does the same underlying work: download the package, validate its manifest, persist the plugin record, then attempt to move it through `installed → ready` by starting its worker. If anything in that chain fails, the plugin is parked in `error` with the failure message attached — it stays installed so you can inspect what happened instead of silently disappearing.
|
||||
|
||||
---
|
||||
|
||||
## Enabling and disabling
|
||||
|
||||
Each installed plugin has a power button in the actions column. The button toggles between two lifecycle transitions:
|
||||
|
||||
- **Disable** (when the plugin is `ready`) — Paperclip stops the worker, cancels any in-flight scheduled jobs the plugin owns, and hides the plugin's UI contributions. Your configuration and any data the plugin has stored are preserved.
|
||||
- **Enable** (when the plugin is `disabled`, `error`, or `upgrade_pending`) — Paperclip starts the worker again and, if startup succeeds, moves the plugin back to `ready`.
|
||||
|
||||
Disabling is the right move when a plugin is misbehaving, when you want to pause its scheduled jobs temporarily, or when you're about to change some external dependency and you'd rather the plugin not fire in the meantime. It's a safe, reversible operation — think of it as a "sleep" rather than a "remove".
|
||||
|
||||
### Uninstalling
|
||||
|
||||
The trash icon next to the power button removes a plugin. Paperclip asks you to confirm, because the action is not reversible from the UI: the plugin package is removed from disk, the installation artifacts are cleaned up, and the plugin is marked uninstalled. To get the plugin back, you'd reinstall it fresh.
|
||||
|
||||
Uninstalling stops the worker first, the same way disable does, so in-flight work is drained cleanly before the package is removed.
|
||||
|
||||
### What "disabled" actually stops
|
||||
|
||||
Worth being explicit about, because it's the difference between "I'm pausing this for an hour" and "I need to start over":
|
||||
|
||||
- **Stopped:** the worker process, scheduled job runs, webhook delivery processing, UI contributions (pages, widgets, settings slots), and any health-check polling.
|
||||
- **Preserved:** the plugin record itself, saved configuration (including secrets), historical job-run and webhook-delivery records, and log entries. When you re-enable the plugin everything resumes where it left off, on the same config.
|
||||
|
||||
This matters for plugins that integrate with billed services. Disabling stops the polling and the scheduled work — so you stop paying the API bill — without forcing you to re-enter credentials the next time you turn it back on.
|
||||
|
||||
---
|
||||
|
||||
## Upgrades
|
||||
|
||||
When a plugin is upgraded to a new version, Paperclip compares the capabilities declared in the new manifest against the ones you originally approved.
|
||||
|
||||
- If the new version asks for the same capabilities or fewer, Paperclip upgrades it in place and the plugin returns to `ready` on the new version.
|
||||
- If the new version asks for **new** capabilities that weren't there before — access to a new data surface, a new integration point — Paperclip parks the plugin in `upgrade_pending`. The worker stays stopped until an operator looks at what changed.
|
||||
|
||||
To apply an upgrade pending approval, open the plugin's detail page, review the updated capabilities and description, and click **Enable**. That moves the plugin back to `ready` on the new version. If you're not happy with the new capabilities, you can leave the plugin in `upgrade_pending` indefinitely, or uninstall it.
|
||||
|
||||
This gate exists so plugins can't quietly grow permissions on you between versions. The first time a capability is requested is always a deliberate action you took.
|
||||
|
||||
---
|
||||
|
||||
## The plugin detail page
|
||||
|
||||
Clicking a plugin's name — or the **Configure** button — opens the plugin detail page. This is where a plugin gets to show you its own surface inside Paperclip.
|
||||
|
||||

|
||||
|
||||
The detail page has two tabs:
|
||||
|
||||
- **Configuration** — the plugin's description, author, categories, and settings form (or a custom settings UI the plugin ships).
|
||||
- **Status** — runtime diagnostics: worker process state, recent scheduled jobs, recent webhook deliveries, live health checks, and a rolling log of recent entries.
|
||||
|
||||
A plugin can also declare a **page slot**. When it does, the plugin gets its own route at `/<company-prefix>/plugins/<plugin-id>` and renders whatever it wants there — a full page, a dashboard, a multi-step flow. This is how something like the file-browser example adds a Files tab to project pages, or how a hypothetical CRM plugin might add a Contacts view to the sidebar.
|
||||
|
||||
From your perspective as an operator, plugin pages behave like any other Paperclip page: they're scoped to the currently selected company, they show up in breadcrumbs, and they respect your permissions. The plugin just gets a canvas to render on.
|
||||
|
||||
Plugins can also contribute smaller pieces — a widget on the dashboard, a tab on the project detail page, a link in a sidebar — rather than a full standalone page. These show up inline in the surfaces you already use, so you don't always have to navigate to the plugin detail page to get value out of it. The Hello World example adds a dashboard widget; the file-browser example adds a project tab; the kitchen-sink example scatters contributions across several surfaces so you can see what's possible.
|
||||
|
||||
If two plugins happen to contribute a page on the same route, Paperclip refuses to choose between them and surfaces a conflict message instead. Uninstall one of the colliding plugins, or use the plugin-id URL directly (`/<prefix>/plugins/<plugin-id>`) to reach a specific one while you sort it out.
|
||||
|
||||
---
|
||||
|
||||
## Plugin settings
|
||||
|
||||
Most plugins have some configuration: an API key, a base URL, a default folder, a list of watched repositories. Paperclip gives plugins two ways to expose that to you.
|
||||
|
||||

|
||||
|
||||
**Auto-generated forms.** If the plugin ships a JSON Schema describing its configuration, Paperclip renders a form automatically on the Configuration tab. You fill in the fields, click **Save Configuration**, and Paperclip stores the values securely. For plugins that support it, a **Test Configuration** button appears alongside save — it sends the current form values to the plugin's worker and reports back whether the config works, without persisting anything.
|
||||
|
||||
**Custom settings pages.** A plugin can also contribute its own settings UI — a multi-step wizard, a visual editor, a connection builder — via a `settingsPage` slot. When that's present, Paperclip renders the plugin's custom UI instead of the auto-generated form. Everything still lives under the same Configuration tab so you always know where to look.
|
||||
|
||||
### Secrets
|
||||
|
||||
Fields the plugin marks as secret (API keys, tokens, passwords) are stored encrypted and never redisplayed in plain text. The form shows a masked placeholder once a secret has been saved; to change it, type a new value and save again. Leaving the masked placeholder as-is keeps the existing secret.
|
||||
|
||||
### Permissions sidebar
|
||||
|
||||
To the right of the settings form, the **Permissions** card lists the capabilities the plugin's manifest declares — a short, readable list of what this plugin is allowed to touch (for example, "read issues in this company", "write to project workspaces", "send outbound webhooks"). These are the same capabilities the `upgrade_pending` gate checks against on upgrade. If a plugin's permissions list doesn't match what you think it should be doing, that's a signal to investigate before enabling it.
|
||||
|
||||
---
|
||||
|
||||
## Jobs, webhooks, and logs
|
||||
|
||||
Plugins don't just sit there. Many of them run scheduled jobs — a sync every 15 minutes, a nightly reconciliation, a cleanup pass every Sunday — or receive webhooks from external services. The **Status** tab on the plugin detail page is where you see that activity.
|
||||
|
||||

|
||||
|
||||
The tab shows:
|
||||
|
||||
- **Worker process** — whether the worker is running, its process ID, uptime, and how many crashes it has recovered from. If the worker crashes repeatedly, the crash count and last-crash timestamp surface here before anything looks broken in the rest of the UI.
|
||||
- **Recent job runs** — the last handful of scheduled jobs the plugin fired, each with a status dot (green success, red failed, blue running, grey cancelled, amber queued), the trigger that caused it, and a relative time. A green dot and a short duration is the thing you want to see repeating.
|
||||
- **Recent webhook deliveries** — incoming webhooks, each with a status and duration. Useful when you're debugging an integration that isn't firing.
|
||||
- **Health status** — aggregate check results, polled every 30 seconds while the plugin is `ready`. If a specific check is failing (for example, "can reach external API"), you'll see it called out here long before it becomes a crash.
|
||||
- **Recent logs** — the last 50 log entries the worker emitted, colour-coded by level. This is your first stop when something looks off.
|
||||
|
||||
The whole tab refreshes on a 30-second cycle, so you can leave it open while you tweak config or trigger a manual run elsewhere in Paperclip.
|
||||
|
||||
### Interpreting job status dots
|
||||
|
||||
The coloured dot next to each job run summarises the outcome at a glance:
|
||||
|
||||
- **Green** — the job succeeded. A steady stream of green dots at the expected cadence is what a healthy plugin looks like.
|
||||
- **Blue (pulsing)** — the job is running right now. If a blue dot sticks around for much longer than you'd expect, the Recent Logs panel below is where you look next.
|
||||
- **Amber** — the job is queued, waiting for the worker to pick it up. Brief amber is fine; persistent amber usually means the worker is busy or stuck.
|
||||
- **Red** — the job failed. The log entry with a matching timestamp will have the reason.
|
||||
- **Grey** — the job was cancelled, typically because you disabled the plugin while it was scheduled.
|
||||
|
||||
Webhook delivery dots follow similar colours: green for processed, blue for received, amber for pending, red for failed.
|
||||
|
||||
---
|
||||
|
||||
## Authoring your own plugin
|
||||
|
||||
If none of the installed plugins do what you need, you can write one. Plugins are npm packages with a `paperclipPlugin` manifest block, a worker entry, and an optional UI bundle — the example plugins under `packages/plugins/examples/` are a good starting point.
|
||||
|
||||
The full authoring path lives outside this guide:
|
||||
|
||||
- The `paperclip-create-plugin` skill scaffolds a new plugin package, including the manifest, worker, and a minimal UI surface.
|
||||
- The Paperclip CLI (`paperclipai`) wraps common authoring tasks — scaffolding, dev serving, building, and publishing. See the [CLI overview](../guides/getting-started/your-first-agent.md) and the plugin adapter documentation in the main Paperclip repository for details.
|
||||
|
||||
Once your plugin builds, install it with **Install Plugin** on the Plugin Manager page the same way you'd install anything else. During development you can also point the installer at a local path — the **Install Example** flow is the same mechanism, just targeted at `packages/plugins/examples/`.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Plugins give you a way to extend Paperclip itself — new pages, new widgets, new scheduled work — without forking the codebase. Install what you need, keep disabled what you don't, and let the detail page tell you when something needs attention.
|
||||
|
||||
If you're looking to extend what an individual agent can *do* rather than what Paperclip can *show*, see [Skills](../guides/org/skills.md) and [Agent Adapters](../guides/org/agent-adapters.md) next.
|
||||
|
||||
---
|
||||
|
||||
## Quick reference
|
||||
|
||||
A cheat sheet for operating the Plugin Manager day-to-day:
|
||||
|
||||
| Goal | Action |
|
||||
|---|---|
|
||||
| Try a bundled example | Plugin Manager → Available Plugins → **Install Example** |
|
||||
| Install a third-party plugin | Plugin Manager → **Install Plugin** → enter npm package name |
|
||||
| Temporarily stop a plugin's jobs | Click the power button on the plugin row (green to grey) |
|
||||
| Bring a plugin back online | Click the power button again, or **Enable** from the detail page |
|
||||
| Fix a plugin stuck in `error` | Detail page → Status tab → read the log, then re-enable |
|
||||
| Approve an upgrade that asks for new permissions | Detail page → review Permissions → **Enable** |
|
||||
| Change configuration | Detail page → Configuration tab → edit form → **Save Configuration** |
|
||||
| Verify a config before committing | Detail page → Configuration tab → **Test Configuration** (when available) |
|
||||
| Permanently remove a plugin | Trash icon on the plugin row → confirm |
|
||||
|
||||
If you get stuck, the Status tab on any plugin detail page is the place to start — worker status, recent runs, and recent logs will usually point at the cause faster than poking at configuration.
|
||||
@@ -0,0 +1,260 @@
|
||||
# Settings
|
||||
|
||||
Paperclip has two settings surfaces, and it matters which one you're looking at.
|
||||
|
||||
**Profile** is *your* account — your display name and the avatar that shows up next to your comments. Changing it only affects how *you* appear. Other board users on the same instance have their own profiles and see their own.
|
||||
|
||||
**Instance Settings** is the admin surface. Anything you change there applies to the whole Paperclip instance — every company, every user, every agent that runs against this install. If you're running Paperclip on a VPS that hosts five companies, a toggle on the Instance page flips for all five.
|
||||
|
||||
This guide walks through both, section by section.
|
||||
|
||||
---
|
||||
|
||||
## Profile
|
||||
|
||||

|
||||
|
||||
Open the Profile page from the account menu in the sidebar. It's scoped to you — nobody else's profile changes when you edit yours.
|
||||
|
||||
You get three things to work with:
|
||||
|
||||
- **Avatar** — click the round image (or the **Upload photo** button) to pick a new image from disk. It's uploaded into Paperclip's file storage under the currently selected company's asset space. If no company is selected you can't upload — the page will tell you to select one first. Once an image is set, a **Remove** button appears next to the upload control.
|
||||
- **Display name** — the name shown in the sidebar account footer and as the author on any comments you post. It falls back to "Board" if left blank. The field is capped at 120 characters.
|
||||
- **Email** — read-only. Your email is managed by the auth session (the login provider), not by this page. If you need to change it, update it wherever you signed in from.
|
||||
|
||||
Click **Save profile** to persist name or avatar changes. The button is disabled while the name is empty or a save is in flight.
|
||||
|
||||
> **Note:** The avatar is stored as an asset under the *selected company*, but the profile itself is a user-level record. If you switch which company is selected in the sidebar later, your avatar keeps working — it's just that new uploads go to whichever company is active when you upload.
|
||||
|
||||
---
|
||||
|
||||
## Instance: General
|
||||
|
||||

|
||||
|
||||
This is the top of Instance Settings. It's where you see how the instance was deployed and flip a few cross-cutting toggles.
|
||||
|
||||
### Deployment and auth
|
||||
|
||||
At the top of the page, a small mode badge tells you which deployment profile this instance is running under. The text underneath explains what that means in practice:
|
||||
|
||||
- **Local trusted** — the instance is treating browser requests as a local board operator. No sign-in is required. This is the default when you run Paperclip on your own machine.
|
||||
- **Authenticated public** — sign-in is required and the instance is intended to sit on a public URL.
|
||||
- **Authenticated private** — sign-in is required, but the instance is intended for a private network (LAN, VPN, or similar).
|
||||
|
||||
Three status boxes summarize readiness:
|
||||
|
||||
- **Auth readiness** — whether the auth subsystem is fully configured.
|
||||
- **Bootstrap status** — "Setup required" if the initial CEO/board setup hasn't completed yet, otherwise "Ready".
|
||||
- **Bootstrap invite** — whether an active first-run invite link is outstanding.
|
||||
|
||||
These fields are informational. To change them you change the deployment itself — see [Environment Variables](../reference/deploy/environment-variables.md).
|
||||
|
||||
### Censor username in logs
|
||||
|
||||
Hides the username segment inside home-directory paths and similar operator-visible log output. Useful if you're sharing screen recordings or pasting transcripts. Off by default. Note that bare username mentions outside of path strings are not masked in the live transcript view — this is a best-effort filter, not a guarantee.
|
||||
|
||||
### Keyboard shortcuts
|
||||
|
||||
Enables the app's keyboard shortcuts — inbox navigation, creating issues, toggling panels, and so on. Off by default because they conflict with browser or screen-reader shortcuts for some users. Turn it on if you want faster navigation.
|
||||
|
||||
### Backup retention
|
||||
|
||||
Controls how long automatic database backups are kept at each tier. Paperclip takes daily backups and then thins them out over time:
|
||||
|
||||
- **Daily** — full daily backups kept for the selected number of days.
|
||||
- **Weekly** — one backup per week kept for the selected number of weeks.
|
||||
- **Monthly** — one backup per month kept for the selected number of months.
|
||||
|
||||
Each tier is a preset picker, so you can only choose from vetted retention windows. Backups are gzipped on disk. Longer retention means more storage — raise the windows only as far as your disk and compliance story require.
|
||||
|
||||
### AI feedback sharing
|
||||
|
||||
Controls whether thumbs-up / thumbs-down votes on AI output can send the voted output to Paperclip Labs. Three states exist:
|
||||
|
||||
- **Prompt (default)** — no choice has been made yet. The next time you vote, Paperclip will ask once and save your answer.
|
||||
- **Always allow** — voted AI outputs are shared automatically.
|
||||
- **Don't allow** — voted AI outputs stay local.
|
||||
|
||||
The local vote is always saved regardless — this setting only controls whether the *content* leaves your instance. There's a link to the terms of service on the same card.
|
||||
|
||||
### Sign out
|
||||
|
||||
A plain **Sign out** button at the bottom. It ends your session and sends you back to the login page. On a local-trusted instance there's usually nothing to sign out of, but the button is always present.
|
||||
|
||||
---
|
||||
|
||||
## Instance: Access
|
||||
|
||||

|
||||
|
||||
Instance Access is where you promote users to **instance admin** and pick which companies each user can see. This is different from the roles you grant *inside* a company.
|
||||
|
||||
### Instance-level vs. company-level roles
|
||||
|
||||
It's worth being explicit about this because the two feel similar:
|
||||
|
||||
- A **company membership** lets a user see and act within one specific company. A user can be a member of several companies, each with its own membership role (operator, manager, etc.). This is set per-company.
|
||||
- An **instance admin** can see and manage the instance itself — add/remove instance admins, grant users company access, and open admin-only surfaces. This is set per-user on this page.
|
||||
|
||||
A user can be an instance admin without being a member of every company, and a company member without being an instance admin. The two layers are independent.
|
||||
|
||||
### Finding and inspecting a user
|
||||
|
||||
The left pane is a search box and a scrollable user list. Type a name or email to narrow it down. Each row shows:
|
||||
|
||||
- The user's name and email
|
||||
- A green shield icon if they're currently an instance admin
|
||||
- A count of active company memberships
|
||||
|
||||
Click a user to load their detail view on the right.
|
||||
|
||||
### Promote to instance admin
|
||||
|
||||
In the detail view, a button toggles the user's instance-admin status: **Promote to instance admin** if they're not one, or **Remove instance admin** if they are. The change takes effect immediately. Be deliberate — instance admins can grant themselves access to any company and can demote other instance admins.
|
||||
|
||||
### Company access
|
||||
|
||||
The lower half of the detail view is a grid of every company on the instance with a checkbox next to each. Tick the ones this user should be a member of, untick the ones they shouldn't, and click **Save company access**.
|
||||
|
||||
Adding access creates an *active operator* membership by default. If the user needs a higher role inside that company (manager, for example), switch to that company and raise their role there — this page only controls *whether* they have access, not *what role* they have.
|
||||
|
||||
Beneath the checkbox grid, **Current memberships** lists what's actually on record, with role, status, and the last update date. Treat this as the source of truth — if it doesn't match what you set in the checkboxes, re-save.
|
||||
|
||||
> **Tip:** Running a VPS-hosted instance that hosts multiple companies? Use this page to onboard new people without sharing the database. Create their auth account (through your login provider), find them here, tick the companies they need, and save.
|
||||
|
||||
---
|
||||
|
||||
## Instance: Adapters
|
||||
|
||||

|
||||
|
||||
The Adapters sub-page (Instance Settings → **Adapters**) is the operator view of every adapter currently registered against this Paperclip install. Adapters are how Paperclip talks to an AI runtime — Claude's local CLI, Codex, OpenAI, OpenClaw, and so on — so this is the surface you use when you need to add a new runtime, hide one from agent hiring menus, or upgrade a runtime's package to a newer release.
|
||||
|
||||
> **Alpha:** The external adapter system is still under active development. APIs and storage format may change between releases — reach for this page when you need it, but expect the details to keep evolving.
|
||||
|
||||
### Installed adapters
|
||||
|
||||
The page has two sections, stacked top-to-bottom:
|
||||
|
||||
- **External Adapters** — anything you have added yourself, whether from an npm package or a local path. Each row shows the adapter's display label, its registered `type`, the package name, the version, and how many models the adapter exposes.
|
||||
- **Built-in Adapters** — the adapters that ship inside Paperclip itself. They cannot be removed, only hidden. If an external adapter has overridden a built-in (for example, a forked version of the Claude adapter installed from npm), you also see a synthesised "built-in" row tagged **Overridden by …** so it is obvious which built-in has been shadowed.
|
||||
|
||||
A small origin icon sits next to each external adapter: a folder for adapters installed from a local path, and a package icon for adapters installed from npm. Versions appear as a mono-typed `v1.2.3` badge.
|
||||
|
||||
### Health status
|
||||
|
||||
The row badges are the fastest way to read adapter health:
|
||||
|
||||
- **External** / **Built-in** — the source of the adapter.
|
||||
- **v{version}** — the currently loaded package version.
|
||||
- **Overrides built-in** — this external adapter is replacing a built-in of the same `type`.
|
||||
- **Hidden from menus** / **Override paused** — the adapter still exists but is not offered when creating or hiring agents. Use this to stage a rollout without uninstalling.
|
||||
|
||||
Beneath the label, a subtitle line shows `type`, the npm package name (when different from the type), and the model count. A zero model count usually means the adapter loaded but could not enumerate models — check the package configuration before enabling it for agents.
|
||||
|
||||
### Enable / disable
|
||||
|
||||
Every row has a **power** icon on the right-hand side. Clicking it toggles the adapter's visibility without deleting anything:
|
||||
|
||||
- For regular adapters (both built-in and plain external), the power button flips the **Hidden from menus** state. Hidden adapters are kept registered and existing agents on that adapter continue to run, but the adapter is omitted from the adapter dropdown when creating new agents.
|
||||
- For external adapters that **override a built-in**, the power button instead pauses or resumes the override. Pausing the override snaps every agent of that `type` back to the built-in implementation on their next run; resuming restores the external adapter.
|
||||
|
||||
Two more icons may appear on external rows:
|
||||
|
||||
- **Reload** (circular arrow) hot-swaps the adapter module in the running process. Useful after you have published a new version of a local-path adapter and want to pick up changes without bouncing Paperclip.
|
||||
- **Reinstall** (download arrow) opens a confirmation dialog that checks the npm registry for the latest version, then pulls it and reinstalls the package. Existing agents will use the new version on their next run.
|
||||
|
||||
A **Remove** (trash) icon is available on external adapters only. Removing an adapter unregisters it, tears down its npm install on disk, and cannot be undone — Paperclip prompts for confirmation first.
|
||||
|
||||
### Per-adapter config
|
||||
|
||||
Adapters themselves do not expose configuration on this page — they are packages, not configurable singletons. Each **agent** picks an adapter and then fills in the fields that adapter requires (working directory, environment variables, credentials). Those per-agent fields live on the agent's Configuration tab, not here.
|
||||
|
||||
What this page gives you is the ability to add, upgrade, and retire the adapter packages that populate the agent adapter menu. To install a new external adapter, click **Install Adapter** in the top-right of the page. A dialog appears with two source modes:
|
||||
|
||||
- **npm package** — enter the package name (for example `my-paperclip-adapter`) and optionally a version. Paperclip installs the package from npm and registers it.
|
||||
- **Local path** — paste a Linux, WSL, or Windows path to a checked-out adapter package. Paperclip auto-converts Windows paths and installs the adapter from disk. Local-path adapters are the fastest way to iterate on a new adapter before publishing.
|
||||
|
||||
In both cases the adapter package must export `createServerAdapter()` — the dialog reminds you of this contract.
|
||||
|
||||
For the full catalog of adapters Paperclip supports out of the box, along with the per-agent fields each one expects, see the reference docs: [Adapters overview](../reference/adapters/overview.md).
|
||||
|
||||
---
|
||||
|
||||
## Instance: Scheduler Heartbeats
|
||||
|
||||

|
||||
|
||||
The Scheduler Heartbeats page is a cross-company view of every agent on the instance that has a **timer heartbeat** enabled.
|
||||
|
||||
### What the scheduler does
|
||||
|
||||
Paperclip's scheduler is the component that wakes up timer-driven agents on an interval. It reads each agent's `runtimeConfig.heartbeat` block, notices when the next tick is due, and fires the run. Event-driven wakes (assignments, comments, routines) don't go through this surface at all — they happen regardless of the timer state.
|
||||
|
||||
For a deeper explanation of when to use timer heartbeats vs. event-driven wakes, read [Heartbeats & Routines](../guides/projects-workflow/routines.md). In short: timer heartbeats should be the exception, not the rule.
|
||||
|
||||
### What this page shows
|
||||
|
||||
At the top of the page, a summary line reports:
|
||||
|
||||
- How many timer heartbeats are **active** (enabled and the scheduler considers them live)
|
||||
- How many are **disabled**
|
||||
- How many companies the list spans
|
||||
|
||||
Below that, agents are grouped by company. Each row shows:
|
||||
|
||||
- An **On/Off** badge for whether the scheduler sees the heartbeat as active
|
||||
- The agent's name (links to the full agent config)
|
||||
- The agent's title or role
|
||||
- The configured interval in seconds
|
||||
- When the last heartbeat ran, as a relative time ("2m ago", "never")
|
||||
- A link icon to jump to the agent config, and a button to **Enable Timer Heartbeat** or **Disable Timer Heartbeat** inline
|
||||
|
||||
The list auto-refreshes every 15 seconds so you can watch an enable/disable take effect.
|
||||
|
||||
### When to tune it
|
||||
|
||||
Reach for this page when:
|
||||
|
||||
- You suspect an agent is burning budget on idle wakeups. Find it in the list, check the interval, and either raise the interval on the agent config page or disable the heartbeat.
|
||||
- You've just imported or cloned an instance and want to take stock of every timer-driven agent in one place before turning any of them loose.
|
||||
- You need a quick emergency shut-off. The **Disable All** button at the top right of the summary line disables the timer heartbeat on every currently-enabled agent in one operation. It prompts for confirmation first. Event-driven wakes still work afterwards — this only stops the *tickers*.
|
||||
|
||||
### Safe defaults
|
||||
|
||||
- **Most agents** should have timer heartbeats **off**. They stay active on the dashboard and wake on assignments, comments, and routines. This is Paperclip's default for new agents.
|
||||
- **When a timer is truly needed** (polling an external system with no webhook, for example), push the interval as long as you can stand. Minutes or hours, not seconds.
|
||||
- **After an import** or major config change, check this page — other instances' timer settings will come with them, and you may want to disable the ones you don't recognize before the scheduler starts firing them.
|
||||
|
||||
---
|
||||
|
||||
## Instance: Experimental
|
||||
|
||||

|
||||
|
||||
Experimental flags opt the instance into features that aren't yet defaults. They're gated here because they may change behaviour, performance, or migrations before they graduate.
|
||||
|
||||
### What "experimental" means here
|
||||
|
||||
An experimental flag in Paperclip is a feature that:
|
||||
|
||||
- Has shipped and works,
|
||||
- Is being evaluated against real usage before it becomes the default, and
|
||||
- May be renamed, reworked, or promoted to a core setting in a future release.
|
||||
|
||||
Turning one on is not dangerous in the "this will break your data" sense — but it does mean you're opting into behavior that may change how agents run or how the backend restarts. Flip flags when you have a reason to, not for curiosity, and expect to re-read the release notes if you upgrade.
|
||||
|
||||
### The available flags
|
||||
|
||||
- **Enable Isolated Workspaces** — shows the execution-workspace controls in project configuration and allows isolated workspace behavior for new and existing issue runs. If you're not using isolated workspaces, leave this off; turning it on exposes extra UI you don't need.
|
||||
- **Auto-Restart Dev Server When Idle** — only relevant under `pnpm dev:once`. When the backend detects a stale boot (code changes, new migrations), it waits for queued and running local agent runs to finish and then restarts the dev server automatically. Useful during active development on Paperclip itself, not intended for production.
|
||||
|
||||
Both toggles take effect immediately on save. If one misbehaves, flip it back off — no migration is required.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Heartbeats & Routines](../guides/projects-workflow/routines.md) — the full picture of timer vs. event-driven wakes, and how to configure individual agents.
|
||||
- [Environment Variables](../reference/deploy/environment-variables.md) — the deployment-level settings that the General page reports on (auth mode, bootstrap state, exposure).
|
||||
@@ -1,52 +0,0 @@
|
||||
---
|
||||
title: How Agents Work
|
||||
summary: Agent lifecycle, execution model, and status
|
||||
---
|
||||
|
||||
Agents in Paperclip are AI employees that wake up, do work, and go back to sleep. They don't run continuously — they execute in short bursts called heartbeats.
|
||||
|
||||
## Execution Model
|
||||
|
||||
1. **Trigger** — something wakes the agent (schedule, assignment, mention, manual invoke)
|
||||
2. **Adapter invocation** — Paperclip calls the agent's configured adapter
|
||||
3. **Agent process** — the adapter spawns the agent runtime (e.g. Claude Code CLI)
|
||||
4. **Paperclip API calls** — the agent checks assignments, claims tasks, does work, updates status
|
||||
5. **Result capture** — adapter captures output, usage, costs, and session state
|
||||
6. **Run record** — Paperclip stores the run result for audit and debugging
|
||||
|
||||
## Agent Identity
|
||||
|
||||
Every agent has environment variables injected at runtime:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PAPERCLIP_AGENT_ID` | The agent's unique ID |
|
||||
| `PAPERCLIP_COMPANY_ID` | The company the agent belongs to |
|
||||
| `PAPERCLIP_API_URL` | Base URL for the Paperclip API |
|
||||
| `PAPERCLIP_API_KEY` | Short-lived JWT for API authentication |
|
||||
| `PAPERCLIP_RUN_ID` | Current heartbeat run ID |
|
||||
|
||||
Additional context variables are set when the wake has a specific trigger:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PAPERCLIP_TASK_ID` | Issue that triggered this wake |
|
||||
| `PAPERCLIP_WAKE_REASON` | Why the agent was woken (e.g. `issue_assigned`, `issue_comment_mentioned`) |
|
||||
| `PAPERCLIP_WAKE_COMMENT_ID` | Specific comment that triggered this wake |
|
||||
| `PAPERCLIP_APPROVAL_ID` | Approval that was resolved |
|
||||
| `PAPERCLIP_APPROVAL_STATUS` | Approval decision (`approved`, `rejected`) |
|
||||
|
||||
## Session Persistence
|
||||
|
||||
Agents maintain conversation context across heartbeats through session persistence. The adapter serializes session state (e.g. Claude Code session ID) after each run and restores it on the next wake. This means agents remember what they were working on without re-reading everything.
|
||||
|
||||
## Agent Status
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `active` | Ready to receive heartbeats |
|
||||
| `idle` | Active but no heartbeat currently running |
|
||||
| `running` | Heartbeat in progress |
|
||||
| `error` | Last heartbeat failed |
|
||||
| `paused` | Manually paused or budget-exceeded |
|
||||
| `terminated` | Permanently deactivated |
|
||||
@@ -4,7 +4,7 @@ The Activity Log is the complete record of everything that has ever happened in
|
||||
|
||||
The log exists for two reasons. First, accountability: you can always see what actually happened and who did it. Second, debugging: when something goes wrong, the Activity Log is your first place to look.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -33,7 +33,7 @@ The Activity Log is available from the left sidebar. It opens to a chronological
|
||||
|
||||
Each row in the feed shows the actor's name, what they did, and when. Clicking a row jumps you to the related issue, approval, agent, project, goal, or run — useful when you want to inspect the thing that changed.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Tip:** The Activity Log and the task comment threads show different things. The comment thread on a task shows what the agent said as it worked — its reasoning, questions, and progress updates. The Activity Log shows the structural changes — status transitions, assignments, approvals. Use comments when you want to understand what the agent was thinking; use the log when you want to understand what actually changed and when.
|
||||
|
||||
@@ -43,10 +43,10 @@ Each row in the feed shows the actor's name, what they did, and when. Clicking a
|
||||
|
||||
When you're looking for something specific, use the filter at the top of the Activity page. In the current UI, that filter narrows the list by entity type.
|
||||
|
||||

|
||||

|
||||
|
||||
**Filter by entity type** — narrow to a specific category of events: tasks only, agents only, approvals only, or budget events only.
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -100,4 +100,4 @@ If you ever need to understand a past decision, resolve a dispute about what an
|
||||
|
||||
You've now covered all five sections of the "Running Your Company" guide set. You know how to read the dashboard, manage tasks, handle approvals, control costs, and use the Activity Log to understand what's happening. From here, the next guide covers building out your org structure — adding manager agents and worker agents to scale beyond the CEO.
|
||||
|
||||
[Building Your Org Structure →](org-structure.md)
|
||||
[Building Your Org Structure →](../org/org-structure.md)
|
||||
@@ -4,7 +4,7 @@ Approvals are how you stay in control even as your agents work autonomously. Wit
|
||||
|
||||
Think of it like a board seat. The agents run the day-to-day company. But the big calls — who gets hired, what the strategy is — still come to you.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -45,20 +45,50 @@ Later on, you may also see budget-related approvals when a budget policy hits a
|
||||
|
||||
You can open it from a direct link, from the dashboard's pending approvals card, or from an approval-related inbox item. The page uses two tabs: **Pending** for actionable items and **All** for the full history.
|
||||
|
||||

|
||||

|
||||
|
||||
Pending approvals have a yellow badge. Items in `revision_requested` are still treated as actionable until the requester follows up and resubmits.
|
||||
|
||||
---
|
||||
|
||||
## Reviewing a Hire Request
|
||||
## Queue Filters
|
||||
|
||||
The Approvals page is organised as a tab bar across the top:
|
||||
|
||||
- **Pending** — everything that still needs a decision from you. This includes approvals in the `pending` state and any in `revision_requested` — the latter are approvals you previously sent back, still awaiting the requester's follow-up. Both count toward the yellow pill beside the tab label.
|
||||
- **All** — the full history: approved, rejected, and resolved items alongside anything still pending. Use this tab to look up past decisions or verify when an approval was resolved.
|
||||
|
||||

|
||||
|
||||
Within each tab, approvals are sorted newest first. Each row shows the approval type, the requesting agent, a short summary, and a status badge. Clicking a row opens the approval detail view covered in the next sections.
|
||||
|
||||
> **Tip:** If the Pending tab is empty, you are caught up — the inbox card on the dashboard will also show zero pending approvals.
|
||||
|
||||
---
|
||||
|
||||
## Hire Approval Detail
|
||||
|
||||
Clicking a hire request opens its detail view. The page is laid out in three parts:
|
||||
|
||||
1. **A header card** with the approval type icon, the proposed agent's name and role, the current status badge, and the approval id (useful when referencing it in a comment or a support channel).
|
||||
2. **The proposal payload** — the structured summary of what the requesting agent is asking for. For hires, this includes:
|
||||
- The proposed agent name and title
|
||||
- The role (CTO, backend engineer, content writer, etc.) and a freeform capabilities description
|
||||
- The adapter type and sanitised adapter config (working directory, environment variable names, and other non-secret fields)
|
||||
- The reporting line — which agent the new hire would report to
|
||||
- The requested monthly budget
|
||||
3. **A "See full request" expander** that reveals the raw JSON payload for the rare case you need to inspect a specific field the summary view does not highlight.
|
||||
|
||||
Below the proposal, a **Comments** section lets you leave notes or ask clarifying questions on the approval itself. Comments on an approval are visible to the requesting agent the next time it wakes.
|
||||
|
||||

|
||||
|
||||
### Reviewing a hire request
|
||||
|
||||
1. **Click on a pending hire approval**
|
||||
|
||||
This opens the hire request detail view, showing the full proposal the agent submitted.
|
||||
|
||||

|
||||
|
||||
2. **Read the proposal carefully**
|
||||
|
||||
Ask yourself:
|
||||
@@ -69,7 +99,7 @@ Later on, you may also see budget-related approvals when a budget policy hits a
|
||||
|
||||
3. **Make your decision**
|
||||
|
||||

|
||||

|
||||
|
||||
- **Approve** — the hire proceeds. The new agent is created, configured, and queued to wake automatically.
|
||||
- **Reject** — the hire is denied. The requesting agent is notified and will not retry unless instructed.
|
||||
@@ -79,13 +109,23 @@ Later on, you may also see budget-related approvals when a budget policy hits a
|
||||
|
||||
---
|
||||
|
||||
## Reviewing a Strategy Approval
|
||||
## Strategy Approval Detail
|
||||
|
||||
A strategy approval detail has the same three-part layout as a hire, but the payload is very different. Instead of a structured proposal, the main content is the CEO's **proposed plan** — usually a few paragraphs of narrative strategy followed by the initial set of tasks the CEO intends to create. Specifically, you will see:
|
||||
|
||||
- **The plan body** rendered as markdown, so any headings, bullets, or links the CEO wrote appear formatted.
|
||||
- **Linked issues** — if the CEO already seeded draft tasks tied to the strategy, they appear as a list of clickable issues underneath the plan. Approving does not close them; they remain open for the CEO to refine on its next run.
|
||||
- **Decision note** — once the approval has been resolved, any note you left (via Request Revision) is shown here.
|
||||
|
||||

|
||||
|
||||
### Reviewing a strategy approval
|
||||
|
||||
1. **Click on the strategy approval**
|
||||
|
||||
The strategy detail shows the CEO's proposed plan — usually several paragraphs outlining goals, priorities, and the initial set of tasks it intends to create.
|
||||
|
||||

|
||||

|
||||
|
||||
2. **Read the strategy with your company goal in mind**
|
||||
|
||||
@@ -101,12 +141,28 @@ Later on, you may also see budget-related approvals when a budget policy hits a
|
||||
|
||||
If something's off, **Request Revision**. In the revision note, be specific: "The strategy focuses too much on technical infrastructure and not enough on user acquisition — please revise to include a distribution plan" gives the CEO clear direction to work with.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Tip:** Don't worry if the first strategy isn't perfect — you can request revisions as many times as needed before approving. Requesting a revision changes the approval status and leaves a clear note for the requester to address; the updated proposal appears after the requester follows up and resubmits.
|
||||
|
||||
---
|
||||
|
||||
## Approve, Reject, or Request Revision
|
||||
|
||||
Every actionable approval (anything in `pending` or `revision_requested`) shows the same three buttons at the bottom of the detail card. Use them deliberately — each one means something different to the requesting agent.
|
||||
|
||||

|
||||
|
||||
- **Approve** (green) — you accept the proposal as submitted. For hires, the agent is created, configured, and queued to wake automatically. For strategies, the CEO is notified and begins creating tasks on its next run. A confirmation banner appears on the detail page, and the approval moves to `approved` in the list.
|
||||
- **Reject** (red) — you are saying no to the proposal outright. The action is permanently denied: hire positions are not created, strategies are discarded, and the requesting agent will not retry unless you instruct it to. If a hire was rejected, you get an extra **Delete disapproved agent** button so you can clear the placeholder record from the org.
|
||||
- **Request Revision** (outline) — you are not accepting as-is, but you are not saying no either. The approval enters `revision_requested`. The requester revises its proposal on a later run and re-submits; the approval then returns to `pending` with the updated payload ready for you to review again. Leave a comment on the approval explaining what you want changed so the agent has concrete direction.
|
||||
|
||||
> **Tip:** Request Revision is almost always the right choice when the proposal is directionally right but needs tweaks — adjusting a budget, changing a reporting line, or tightening a strategy's scope. Reject only when the proposal is fundamentally wrong or no longer relevant.
|
||||
|
||||
**Budget approvals** are slightly different: the Approve and Reject buttons are hidden on the approval detail. Budget stops are resolved from the budget controls on the **Costs** page instead — the detail view will link you there.
|
||||
|
||||
---
|
||||
|
||||
## What Happens After You Decide
|
||||
|
||||
**If you Approve:**
|
||||
@@ -127,7 +183,7 @@ Later on, you may also see budget-related approvals when a budget policy hits a
|
||||
- The agent revises the proposal and resubmits it on a later run. The approval moves back to "pending".
|
||||
- You'll see the updated proposal the next time you open it.
|
||||
|
||||

|
||||

|
||||
|
||||
The full revision cycle looks like this:
|
||||
|
||||
@@ -160,4 +216,4 @@ These override powers exist because autonomous agents occasionally drift, get st
|
||||
|
||||
Approvals are now part of your regular workflow — check the queue whenever you're reviewing the dashboard. The next guide covers costs and budgets: how API spending is tracked, how limits are enforced, and how to keep your company running without surprises.
|
||||
|
||||
[Costs & Budgets →](costs-and-budgets.md)
|
||||
[Costs & Budgets →](./costs.md)
|
||||
@@ -0,0 +1,388 @@
|
||||
# Costs
|
||||
|
||||
> **Warning:** AI agents make real API calls that cost real money. Every time an agent works — every heartbeat, every task, every comment it reads and writes — it sends and receives text through a provider like Anthropic or OpenAI, which charges you per token (roughly per word). This isn't a Paperclip fee; it's a cost you pay directly to the AI provider. Read this guide before your agents start running in earnest.
|
||||
|
||||
Paperclip's budget system exists to make sure you're never surprised. You set limits, and the platform enforces them automatically — an agent won't spend a cent beyond what you allow.
|
||||
|
||||
The **Costs** page in the sidebar is where all of this lives. It has five tabs — **Overview**, **Budgets**, **Providers**, **Billers**, and **Finance** — plus a shared date-range selector and four headline metrics at the top. This guide walks through each tab, then folds in the core concepts around how costs work, how budgets protect you, and how to tune spend over time.
|
||||
|
||||
---
|
||||
|
||||
## How Costs Work
|
||||
|
||||
Every time an agent runs a heartbeat, it generates an API call. That call sends the agent's context (its identity, its tasks, the instructions it's working from) to the AI provider, and receives a response (the agent's reasoning and next actions). The provider charges based on the number of tokens — input tokens (what's sent) and output tokens (what comes back).
|
||||
|
||||
Paperclip records every one of these calls: which agent made it, which model it used, how many tokens were used, and the exact dollar cost. These records are aggregated per agent per calendar month (resetting on the 1st of each UTC month).
|
||||
|
||||
> **Note:** A token is roughly equivalent to one word, though technically it's a fragment of text slightly smaller than that. A busy agent doing coding or writing work might process 100,000–500,000 tokens per month. At typical Anthropic pricing, that's roughly $3–$15 per month for a moderately active worker agent — but this varies significantly based on the model used and how much context each task requires.
|
||||
|
||||
Beyond per-request inference costs, there are also **account-level** charges — monthly subscription fees, credit top-ups, invoice adjustments, refunds — that don't map to a single API call. Paperclip tracks those separately in the finance ledger so you can reconcile your actual provider invoices against what Paperclip thinks you spent.
|
||||
|
||||
---
|
||||
|
||||
## Page Header and Date Range
|
||||
|
||||
Every tab on the Costs page shares the same header, so it's worth understanding what you're looking at before diving into the individual views.
|
||||
|
||||
The page title reads **Costs**, with a subtitle that reminds you what's tracked: inference spend, platform fees, credits, and live quota windows.
|
||||
|
||||
To the right of the title is a **date-range selector** — a row of preset buttons (Today, Week-to-date, Month-to-date, custom, and so on) that scopes every chart, table, and metric on the page. If you pick **custom**, two date inputs appear and you enter the start and end date manually. Until both custom dates are filled in, most of the tabs show a prompt to pick a range before loading data.
|
||||
|
||||
Below the header is a strip of four **metric tiles** that summarise the current range:
|
||||
|
||||
- **Inference spend** — total dollars spent on per-request inference in the selected range, with the total token count shown underneath.
|
||||
- **Budget** — either a utilisation percentage (e.g. `62%` of the monthly cap), the count of active incidents if any budget has been breached, or the word `Open` if no monthly cap is configured. The subtitle explains which of those three is showing.
|
||||
- **Finance net** — debits minus credits for the account-level finance ledger across the range.
|
||||
- **Finance events** — the number of finance events recorded, plus the total estimated (non-invoice-authoritative) debits still open.
|
||||
|
||||
These tiles always reflect the selected date range, not an all-time total.
|
||||
|
||||
---
|
||||
|
||||
## Overview Tab
|
||||
|
||||

|
||||
|
||||
The **Overview** tab is the default landing view. It gives you a single-screen readout of spend health without forcing you to drill into any one dimension.
|
||||
|
||||
### Inference ledger card
|
||||
|
||||
The left side of the overview is the **Inference ledger** card. It restates the total inference spend for the range in large type, shows either the configured monthly budget (`Budget $X.XX`) or the words `Unlimited budget` when no cap is set, and puts the total token count in a small box to the right.
|
||||
|
||||
If a monthly budget is configured, a horizontal utilisation bar sits below it. The bar colour reflects health:
|
||||
|
||||
- **Green** — under 70% consumed.
|
||||
- **Yellow** — 70–90%.
|
||||
- **Red** — above 90%.
|
||||
|
||||
Underneath the bar is a line like `62% of monthly budget consumed in this range.` This is the fastest answer to "are we on track this month?".
|
||||
|
||||
### Finance ledger card
|
||||
|
||||
The right side of the overview is the **Finance ledger** card — the same four metrics as the page header tiles, restated in context:
|
||||
|
||||
- **Debits** — account-level charges in the range, with the total event count.
|
||||
- **Credits** — refunds, offsets, and credit returns.
|
||||
- **Net** — debit minus credit for the period.
|
||||
- **Estimated** — debits that are not yet invoice-authoritative (for example, a Paperclip estimate of a subscription day that hasn't been reconciled with the provider's invoice yet).
|
||||
|
||||
### By agent
|
||||
|
||||
Below the ledger cards is the **By agent** card. Each agent that generated inference events in the range is listed with:
|
||||
|
||||
- Agent name and avatar, plus a `terminated` badge if the agent has been retired.
|
||||
- Total cost for the range, right-aligned.
|
||||
- Token breakdown: `in <input+cached> · out <output>`.
|
||||
- Run-type breakdown: `N api · N subscription` — how many runs went through API-priced calls versus subscription-backed calls (see **Billers** below for the distinction).
|
||||
|
||||
If an agent has a per-model breakdown available, a caret appears on the left of the row. Clicking the row expands it to show one line per `provider / model / billingType` combination with its share of that agent's spend, the cost, the token count, and which biller handled the call. This is the fastest way to see "why is this particular agent spending so much" — you can immediately tell whether it's one runaway model or a healthy mix.
|
||||
|
||||
### By project
|
||||
|
||||
To the right of the agent breakdown is the **By project** card. Paperclip attributes run costs to a project when the run was triggered by an issue that belongs to that project. Each row shows the project name and its total attributed cost. Runs that didn't happen inside a project-linked issue appear as `Unattributed`.
|
||||
|
||||
### Finance timeline
|
||||
|
||||
Under the project card is a compact **finance timeline** showing the most recent 6 account-level events (debits and credits) in the range. If there are no finance events yet, the card prompts you to add them once biller invoices or credits land.
|
||||
|
||||
### Active budget incidents (overview)
|
||||
|
||||
If any agent or project has breached its hard-stop budget, up to two **Budget incident cards** appear at the top of the Overview tab, above everything else. Each card shows the scope, the breach details, and two resolution buttons — **Keep paused** or **Raise budget and resume** (with an amount input). The full incident list lives on the Budgets tab.
|
||||
|
||||
---
|
||||
|
||||
## Budgets Tab
|
||||
|
||||

|
||||
|
||||
The **Budgets** tab is where you configure the spend ceilings that protect your company. Think of it as the control plane for hard stops — the place where you decide, in dollars, how much any given scope is allowed to spend before Paperclip pulls the emergency brake.
|
||||
|
||||
### Budget Levels
|
||||
|
||||
Paperclip operates two levels of budget protection that work together:
|
||||
|
||||
**Company budget** — the total monthly ceiling for your entire company. All agents combined cannot spend more than this. Set it conservatively when starting out.
|
||||
|
||||
**Per-agent budget** — an individual monthly ceiling for each agent. This is enforced independently of the company total. An agent at 100% pauses even if the company still has budget remaining. This protects you from any single agent spending disproportionately.
|
||||
|
||||
A third scope, **project budget**, acts as a lifetime cap on an execution-bound project — useful for one-off work where you want to say "this project can never cost more than $200" regardless of the month.
|
||||
|
||||
All budgets are displayed in dollars in the Paperclip UI.
|
||||
|
||||
### Budget control plane
|
||||
|
||||
At the top of the Budgets tab is a **Budget control plane** card with four headline metrics:
|
||||
|
||||
- **Active incidents** — open soft or hard threshold crossings.
|
||||
- **Pending approvals** — budget override approvals awaiting board action.
|
||||
- **Paused agents** — count of agents currently blocked from running because of a budget breach.
|
||||
- **Paused projects** — count of projects currently blocked from execution because of a budget breach.
|
||||
|
||||
If all four show zero, your budget posture is clean.
|
||||
|
||||
### What Happens When Limits Are Hit
|
||||
|
||||
Paperclip enforces budgets automatically, in two stages:
|
||||
|
||||
```
|
||||
$0 ─────────────────────── 80% ──────── 100%
|
||||
Normal operation ⚠ 🛑
|
||||
Warning Auto-paused
|
||||
```
|
||||
|
||||
**At 80%:** Paperclip records a warning so you can intervene before the hard stop. The agent continues running.
|
||||
|
||||
**At 100%:** The agent is automatically paused. No further heartbeats are triggered. The agent stops all activity until either you increase its budget or the calendar month resets.
|
||||
|
||||
An auto-paused agent doesn't lose its work — any tasks it had in progress remain assigned to it and will pick up again once the agent is resumed.
|
||||
|
||||
### Active incidents
|
||||
|
||||
If any budget policy has been breached, an **Active incidents** section appears under the control plane. Each incident card shows:
|
||||
|
||||
- The scope (company, agent, or project) and its name.
|
||||
- The policy details — how much was configured and how much was actually spent.
|
||||
- Two actions:
|
||||
- **Keep paused** — acknowledge the breach and leave the scope paused. Useful when you want the agent to stop for the rest of the month and resume naturally at rollover.
|
||||
- **Raise budget and resume** — increase the policy's amount (you enter the new cap in the card) and immediately un-pause the scope.
|
||||
|
||||
Both actions are logged and become part of the audit trail.
|
||||
|
||||
### Budget policies by scope
|
||||
|
||||
Below the incidents, the rest of the tab is organised into three sections — **Company budgets**, **Agent budgets**, and **Project budgets** — with one Budget policy card per configured policy in each section.
|
||||
|
||||
Each policy card shows:
|
||||
|
||||
- The scope (who this applies to) and the window kind (monthly recurring for company and agent, lifetime for project).
|
||||
- The current cap and the current spend against it.
|
||||
- An editable amount field and a **Save** button.
|
||||
|
||||
If no policies exist yet, a single empty-state card appears with a pointer to set agent and project budgets from their detail pages and to use the existing company monthly budget control.
|
||||
|
||||
### Setting a Company Budget
|
||||
|
||||
1. **Open the Costs page**
|
||||
|
||||
Company-wide budget controls live on the **Costs** page, under the **Budgets** tab.
|
||||
|
||||

|
||||
|
||||
2. **Set the monthly budget**
|
||||
|
||||
Enter a dollar amount in the Monthly Budget field. This is the maximum your entire company can spend in a calendar month.
|
||||
|
||||
> **Tip:** Start conservative — $50–100/month is enough for experimentation with a small team. You can always increase it once you have a feel for how much your agents are spending.
|
||||
|
||||
3. **Save**
|
||||
|
||||
### Setting Per-Agent Budgets
|
||||
|
||||
Each agent should have its own budget. This limits exposure from any single agent and makes your cost picture easier to read.
|
||||
|
||||
1. **Open the agent's Budget tab**
|
||||
|
||||
Click the agent's name in the sidebar or in the Agents list, then open **Budget** on the agent detail page.
|
||||
|
||||

|
||||
|
||||
2. **Set the monthly budget**
|
||||
|
||||
Enter a dollar amount. Some rough guidance on how to think about agent budgets:
|
||||
|
||||
| Agent role | Typical monthly budget |
|
||||
|------------|------------------------|
|
||||
| CEO | $30–50 — runs frequently, high context per run |
|
||||
| Manager (CTO, CMO, etc.) | $20–40 — active but more focused than CEO |
|
||||
| Worker agent (engineer, writer, etc.) | $10–25 — executes tasks but less strategic reasoning |
|
||||
|
||||
These are starting points, not rules. Watch actual spend for the first two weeks and adjust from there.
|
||||
|
||||
3. **Save**
|
||||
|
||||
### Increasing a Budget or Resuming a Paused Agent
|
||||
|
||||
If an agent hits its 100% limit and auto-pauses, you have two options:
|
||||
|
||||
**Option 1: Increase the agent's monthly budget**
|
||||
|
||||
1. Open the agent's settings
|
||||
2. Update the Monthly Budget field to a higher amount
|
||||
3. Save
|
||||
|
||||
The agent will resume immediately. It won't wait for the month to roll over.
|
||||
|
||||
**Option 2: Wait for the month to reset**
|
||||
|
||||
On the 1st of each UTC month, all monthly budgets reset to zero. A paused agent that's been paused purely for budget reasons will become active again automatically at rollover.
|
||||
|
||||
> **Note:** If you don't want the agent to resume automatically at month rollover, manually pause it first. Otherwise it will start running again as soon as the new month begins.
|
||||
|
||||
### Viewing Budget Status at Different Thresholds
|
||||
|
||||
For reference, here's what to expect at each stage:
|
||||
|
||||

|
||||
|
||||
*Normal operation — green bar, no action needed.*
|
||||
|
||||

|
||||
|
||||
*Warning threshold reached — this is your signal to intervene before the hard stop.*
|
||||
|
||||

|
||||
|
||||
*Budget exhausted — agent is auto-paused. Increase budget or wait for month reset.*
|
||||
|
||||
---
|
||||
|
||||
## Providers Tab
|
||||
|
||||

|
||||
|
||||
The **Providers** tab breaks spend down by who the inference was actually performed by — Anthropic, OpenAI, Google, and any other model provider your agents use. This is the view you want when you're asking "how much am I sending Anthropic this month?" or "is my OpenAI usage growing?".
|
||||
|
||||
### Provider sub-tabs
|
||||
|
||||
At the top of the tab is a sub-tab bar. The first entry is **All providers** — an aggregate showing tokens and cents across everything. After that comes one sub-tab per provider that generated events in the selected range. Each sub-tab label includes the provider's display name, its token count, and its cost, so you can read the split without clicking in.
|
||||
|
||||
### Provider card
|
||||
|
||||
Selecting **All providers** renders a grid of provider cards (two per row on wider screens). Selecting a single provider shows the same card by itself. Each **ProviderQuotaCard** rolls up, for a given provider:
|
||||
|
||||
- **Per-model spend** — one row per model used in the range, with input tokens, cached-input tokens, output tokens, and dollar cost. This is where you confirm, for example, that most of your Anthropic spend is on Claude Sonnet and not a stray Opus call.
|
||||
- **Current-week spend** — a separate roll-up showing what the provider cost you this calendar week, independent of the main range selector. Useful for catching week-over-week surprises.
|
||||
- **Share of budget** — the provider's share of the company monthly budget, with a deficit notch that lights up if the provider is projected to overshoot its fair share at the current burn rate (only shown when the date range is set to **Month-to-date**).
|
||||
|
||||
### Quota windows
|
||||
|
||||
For providers that have a subscription plan with a rolling usage window (e.g. Anthropic Pro/Max with a 5-hour window, daily, or weekly caps), the card also renders a **Quota windows** section. For each window Paperclip queries the provider and shows:
|
||||
|
||||
- Window label (e.g. "5-hour", "daily", "weekly").
|
||||
- How much of the window you've consumed.
|
||||
- How long until the window resets.
|
||||
- The data source (provider API, estimated, or last-known cached value).
|
||||
|
||||
If the provider's quota endpoint returned an error, the card surfaces that error inline instead of a bar. Quota data refreshes on its own schedule and shows a loading indicator the first time.
|
||||
|
||||
### Provider window spend
|
||||
|
||||
Underneath the quota bars, Paperclip also shows **window spend** — what the provider cost you inside each active quota window. This is how you tell the difference between "I'm at 80% of my 5-hour cap because of one burst" and "I'm at 80% and it's evenly distributed".
|
||||
|
||||
---
|
||||
|
||||
## Billers Tab
|
||||
|
||||

|
||||
|
||||
The **Billers** tab answers a different question than **Providers**: not "who ran the inference?" but "who charged me for it?". In most companies these are the same entity, but they can diverge — for example, when you run Claude models through a subscription plan (Anthropic is the biller *and* provider) versus through the API (Anthropic is still the biller, but the pricing model is different), or when a gateway or reseller handles the billing relationship on your behalf.
|
||||
|
||||
### Biller sub-tabs
|
||||
|
||||
Like the Providers tab, the Billers tab has a sub-tab bar at the top. **All billers** shows an aggregate grid; each per-biller sub-tab shows the same card standalone. Sub-tab labels include the biller's display name, token count, and cost.
|
||||
|
||||
### Biller spend card
|
||||
|
||||
Each **BillerSpendCard** shows:
|
||||
|
||||
- The biller's total spend in the range, in dollars and tokens.
|
||||
- The current calendar week's spend with that biller, for a short-horizon sanity check.
|
||||
- The biller's share of the company monthly budget.
|
||||
- A per-provider breakdown *within this biller* — i.e. which providers' models were billed through this biller, and how much each contributed. This is where you'd see, for example, that one biller is paying for both Anthropic and OpenAI calls.
|
||||
|
||||
### Why this matters
|
||||
|
||||
The Biller view is how you reconcile invoices. When the Anthropic invoice lands at the end of the month, the number on it should line up with what the Anthropic biller card has been telling you all along. If it doesn't, you have a starting point for investigation — drill into the biller card, compare per-provider and per-model rows, and compare timestamps against your provider's usage dashboard.
|
||||
|
||||
### Attribution
|
||||
|
||||
Every cost event in Paperclip is tagged with both a provider (who did the inference) and a biller (who charges you). For simple setups the two are identical, and you can largely ignore the Billers tab. For more complex setups — multiple accounts with the same provider, subscription-plus-API combinations, or a centralised gateway — the Billers tab is how you keep the accounting clean.
|
||||
|
||||
---
|
||||
|
||||
## Finance Tab
|
||||
|
||||

|
||||
|
||||
The **Finance** tab is the account-level ledger — everything that isn't a per-request inference cost. Subscription fees, invoice reconciliations, credit top-ups, refunds, and adjustments all land here. If the Providers and Billers tabs show what your agents did, the Finance tab shows what actually hit your accounts.
|
||||
|
||||
### Finance summary card
|
||||
|
||||
At the top of the tab is the **Finance ledger** card with four metric tiles — the same four from the page header, expanded into full-width tiles:
|
||||
|
||||
- **Debits** — total debited dollars in the range, plus the count of events.
|
||||
- **Credits** — refunds, offsets, and credit returns.
|
||||
- **Net** — debit minus credit for the selected period.
|
||||
- **Estimated** — debits that Paperclip has estimated but are not yet invoice-authoritative. These are the events most likely to change as invoices arrive.
|
||||
|
||||
### By biller
|
||||
|
||||
The **By biller** card groups finance events by who charged or credited you. Each **FinanceBillerCard** shows the biller, its debit and credit totals for the range, and an event count. This is the usual starting point for reconciling against a specific vendor's invoices.
|
||||
|
||||
If no finance events exist yet, the card prompts you to add them once biller invoices or credits land.
|
||||
|
||||
### Finance timeline
|
||||
|
||||
Below the biller grid is a **FinanceTimelineCard** listing individual finance events chronologically. Each row shows the event type (e.g. subscription debit, credit top-up, invoice adjustment), amount, biller, timestamp, and whether it's estimated or authoritative. This is where you spot the specific event you're trying to trace.
|
||||
|
||||
### By kind
|
||||
|
||||
To the right of the timeline is the **By kind** card. Instead of grouping by who charged you, it groups by *what kind of event* — all subscription debits together, all credits together, all adjustments together. Useful when you're answering "how much are we spending on subscriptions versus pay-as-you-go?".
|
||||
|
||||
### Estimated versus actual
|
||||
|
||||
Paperclip separates estimated debits from authoritative ones because your provider's invoice is the source of truth. Until an invoice is reconciled, Paperclip's number for a subscription day is its best guess — typically derived from the plan price divided by the billing cycle length. Once the real invoice arrives and is imported, the estimated flag flips off and the number becomes authoritative.
|
||||
|
||||
The **Estimated** metric at the top tells you, at a glance, how much of your finance ledger is still provisional. A small number means most of your ledger is invoice-backed; a large number means a lot of estimated spend is waiting for reconciliation.
|
||||
|
||||
### Exporting finance data
|
||||
|
||||
Finance events can be exported for use in external accounting systems. The export carries the same fields you see in the timeline (event kind, biller, debit/credit amounts, timestamps, authoritative flag) so the resulting file can be reconciled against provider invoices or uploaded into a ledger.
|
||||
|
||||
---
|
||||
|
||||
## Monitoring Spending
|
||||
|
||||
### From the Dashboard
|
||||
|
||||
The Cost Summary panel on the dashboard shows each agent's current month spend as a bar chart. Green = normal, amber = approaching limit (80%+), red = paused at 100%.
|
||||
|
||||

|
||||
|
||||
Check this panel whenever you open Paperclip. A bar that has jumped unexpectedly since your last check is worth investigating.
|
||||
|
||||
### From the Agent Detail Page
|
||||
|
||||
Click on any agent, then open **Runs** to see per-run spend and **Budget** to see the current cap and utilization.
|
||||
|
||||

|
||||
|
||||
This is useful when you want to understand why a particular agent is spending more than expected.
|
||||
|
||||
### From the Costs page
|
||||
|
||||
For anything cross-agent — "which provider got the most money this week?", "which project ate the most budget?", "how much of our ledger is still estimated?" — go straight to the Costs page and pick the tab that matches the question. Overview for a general health check, Providers/Billers for vendor accounting, Finance for invoice reconciliation, Budgets for hard-stop management.
|
||||
|
||||
---
|
||||
|
||||
## Cost-Saving Tips
|
||||
|
||||
**Use a less expensive model for worker agents.** The CEO agent benefits from the most capable model available (like Claude Opus), since it needs to reason about strategy. Worker agents doing more routine tasks — writing content, processing data, running searches — can often use a faster, cheaper model like Claude Sonnet without a meaningful drop in quality. You set the model in the agent's adapter configuration.
|
||||
|
||||
**Reduce heartbeat frequency for background agents.** An agent that runs every 15 minutes uses roughly 4× as many API calls as one that runs every hour. For agents that don't need to respond quickly, a longer interval saves money with little downside.
|
||||
|
||||
**Write tighter task descriptions.** Agents read their full task description, context, and conversation history on every heartbeat. Open-ended tasks with long context chains accumulate cost quickly. Specific, concise task descriptions with clear "done" criteria keep context small and focused.
|
||||
|
||||
**Pause agents during off-hours.** If your company doesn't need to run 24/7, manually pause agents at the end of the working day and resume them in the morning. Most of the autonomous work can happen within a defined window.
|
||||
|
||||
**Watch the cost-per-run on new agents.** After a new agent completes its first few heartbeats, check the per-run cost on its detail page. If it's higher than expected, check what it's doing: long context chains, excessive tool use, or a misconfigured prompt can all inflate cost.
|
||||
|
||||
**Prefer subscriptions when usage is predictable.** If you have an agent whose workload is steady and fits comfortably inside a provider's plan window (e.g. an Anthropic Pro subscription), routing through the subscription biller can be cheaper than pay-as-you-go API pricing. Use the Providers tab's quota-window view to confirm the subscription has headroom before committing an agent to it.
|
||||
|
||||
**Reconcile finance regularly.** Invoice-authoritative numbers are always more accurate than estimates. A few minutes a month importing the real invoice into the Finance tab keeps your ledger honest and catches pricing changes early.
|
||||
|
||||
---
|
||||
|
||||
Your company's finances are now under control. The next guide covers the Activity Log — the complete audit trail of everything that has ever happened in your company, and how to use it to understand and debug agent behaviour.
|
||||
|
||||
[Activity Log →](./activity-log.md)
|
||||
@@ -4,7 +4,7 @@ The dashboard is your control centre. When your AI company is running, this is t
|
||||
|
||||
Think of it like the cockpit instruments on a plane. You don't need to read every dial every second, but you do want a clear signal when something is off. The dashboard gives you that signal.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -12,7 +12,7 @@ Think of it like the cockpit instruments on a plane. You don't need to read ever
|
||||
|
||||
At the top of the dashboard, Paperclip shows your live agent activity and a set of overview cards. This is the quickest way to tell whether work is moving.
|
||||
|
||||

|
||||

|
||||
|
||||
Here's what each state means:
|
||||
|
||||
@@ -33,7 +33,7 @@ The most common healthy pattern early on: one agent **running** while the others
|
||||
|
||||
The four cards across the top summarize the company at a glance:
|
||||
|
||||

|
||||

|
||||
|
||||
- **Agents Enabled** — how many agents are available to work, plus how many are currently running, paused, or in error.
|
||||
- **Tasks In Progress** — how many issues are actively being worked, plus how many remain open or blocked.
|
||||
@@ -46,9 +46,9 @@ The four cards across the top summarize the company at a glance:
|
||||
|
||||
The Activity feed at the bottom (or side, depending on your screen size) of the dashboard shows the most recent events across your whole company — task status changes, comments, approvals, agent state changes, and budget events.
|
||||
|
||||

|
||||

|
||||
|
||||
This feed isn't meant for deep investigation — it's a pulse check. Scan it after a few hours away and you'll quickly understand what's been happening. For detailed investigation, the full Activity page is a dedicated screen (covered in the [Activity Log guide](activity-log.md)).
|
||||
This feed isn't meant for deep investigation — it's a pulse check. Scan it after a few hours away and you'll quickly understand what's been happening. For detailed investigation, the full Activity page is a dedicated screen (covered in the [Activity Log guide](./activity-log.md)).
|
||||
|
||||
---
|
||||
|
||||
@@ -70,4 +70,4 @@ The dashboard refreshes in real time — you don't need to reload the page. If y
|
||||
|
||||
You now know how to read your company's health at a glance. The next guide covers tasks in depth — how to create them manually, track progress, and give feedback to agents.
|
||||
|
||||
[Managing Tasks →](managing-tasks.md)
|
||||
[Managing Tasks →](./issues.md)
|
||||
File renamed without changes.
@@ -0,0 +1,487 @@
|
||||
# Issues
|
||||
|
||||
Issues are how work gets done in Paperclip. Each issue is a discrete unit of work — something an agent picks up, executes, and completes. Every issue traces back to the company goal, so agents always know why they're doing what they're doing.
|
||||
|
||||
Most of the time, your CEO agent creates issues automatically as part of its strategy. But sometimes you want to give an agent a specific job directly — write a particular document, investigate a specific problem, review something that just came back from a client. That's when you create issues manually.
|
||||
|
||||
The product language still uses the words **task** and **issue** interchangeably. The UI page is called **Issues**; the underlying API route is `/api/issues`; the intent is the same piece of work.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## When to create issues yourself vs let the CEO handle it
|
||||
|
||||
The CEO is designed to create and assign issues autonomously. Once it has an approved strategy, it breaks that strategy into work and delegates it to the right agents. You don't need to manage every issue yourself — that's the point.
|
||||
|
||||
Create an issue manually when:
|
||||
- You have a specific, concrete request that isn't captured in the current strategy
|
||||
- You want to redirect an agent's attention to something urgent
|
||||
- You're running an experiment or one-off piece of work outside the main roadmap
|
||||
- An agent gets stuck and you want to rephrase an issue or break it into smaller steps
|
||||
|
||||
For everything else, trust the CEO and review work via the dashboard and approvals.
|
||||
|
||||
---
|
||||
|
||||
## Opening the Issues page
|
||||
|
||||
The current UI uses **Issues** as the page name, even though the product language still talks about tasks. This page shows all issue-like work across your company in one place. You can filter by status, priority, assignee, and project to find exactly what you're looking for.
|
||||
|
||||
1. **Click "Issues" in the left sidebar**
|
||||
|
||||
This opens the Issues page. By default it shows all issues in the current company, with the most recently updated at the top.
|
||||
|
||||
2. **Use the filters to narrow down**
|
||||
|
||||
The filter bar at the top lets you filter by status, priority, assignee, project, labels, and (when isolated workspaces are enabled) execution workspace. This becomes essential once you have more than a handful of issues running at once.
|
||||
|
||||
Common filter combinations:
|
||||
- Status = **blocked** — see everything that's waiting for intervention
|
||||
- Assignee = **[specific agent]** — see one agent's full workload
|
||||
- Status = **in_review** — see work that's done and waiting for sign-off
|
||||
|
||||
You can also search issues by title, identifier, description, or comment content using the search input. The URL `?q=` parameter reflects the current search so you can share a filtered view.
|
||||
|
||||
3. **Group and arrange the list**
|
||||
|
||||
The list supports:
|
||||
- **Group by** — `None`, `Type`, or `Workspace` (when isolated workspaces are enabled).
|
||||
- **Nesting toggle** — collapse parent/child issues into a tree so subtasks render beneath their parent.
|
||||
- **Column picker** — choose which trailing columns are visible (status, priority, assignee, project, labels, updated time, and others).
|
||||
|
||||
These controls live in the toolbar next to the search input and persist per view.
|
||||
|
||||
4. **Live run indicators**
|
||||
|
||||
Issues with an agent actively running on them show a live-run indicator. The Issues page polls the live-runs endpoint every five seconds so you can see which issues are being worked on right now without refreshing.
|
||||
|
||||
---
|
||||
|
||||
## Creating a New Issue
|
||||
|
||||
1. **Click "New Issue"**
|
||||
|
||||
The button appears in the sidebar and in the Issues view. Clicking it opens the issue creation form.
|
||||
|
||||
2. **Write a clear title**
|
||||
|
||||
Use an action verb followed by a specific outcome. The title is the first thing an agent reads — it should be unambiguous.
|
||||
|
||||
| Instead of… | Write… |
|
||||
|-------------|--------|
|
||||
| Roadmap | Write the Q2 product roadmap |
|
||||
| Bug fix | Fix the login redirect loop on mobile |
|
||||
| Research | Research competitor pricing for the enterprise tier |
|
||||
|
||||
3. **Write a detailed description**
|
||||
|
||||
The description is the brief the agent works from. Agents read it completely before starting. The more precise your description, the better the output.
|
||||
|
||||
Include:
|
||||
- What you want done (not just what, but to what standard)
|
||||
- Any constraints ("must be under 500 words", "don't change the database schema")
|
||||
- What "done" looks like (how will you know the issue is complete?)
|
||||
- Any examples, links, or reference materials the agent should know about
|
||||
|
||||
> **Tip:** The more specific your description, the better the output. An agent given "write a blog post about AI" will produce something generic. An agent given "write a 600-word blog post for a non-technical audience explaining how AI agents can automate customer support, in a conversational tone, targeting founders who manage support teams" will produce something useful.
|
||||
|
||||
4. **Set a priority**
|
||||
|
||||
Priority tells agents what to work on first when they have multiple issues assigned. Use it to signal urgency.
|
||||
|
||||
| Priority | Use for… |
|
||||
|----------|----------|
|
||||
| **Critical** | Blocking work; must be done immediately |
|
||||
| **High** | Important this week |
|
||||
| **Medium** | Normal workload |
|
||||
| **Low** | Nice to have; do when nothing else is waiting |
|
||||
|
||||
5. **Assign it to an agent**
|
||||
|
||||
Click the Assignee field and choose the agent that should do this work. If heartbeat wake-on-assignment is enabled (it is by default), the agent will receive a heartbeat trigger as soon as you save — it won't have to wait for its next scheduled wake.
|
||||
|
||||
> **Note:** Only one agent can hold an issue "in progress" at a time. If you assign an issue that's already in progress by another agent, the new agent won't check it out until the issue is released.
|
||||
|
||||
6. **Set a parent issue (if relevant)**
|
||||
|
||||
If this issue is a subtask — part of a larger piece of work — link it to the parent. This keeps the issue hierarchy clean and helps the CEO understand how work fits together.
|
||||
|
||||
7. **Save the issue**
|
||||
|
||||
Click **Create Issue**. The issue appears in the list and the assigned agent is notified.
|
||||
|
||||
---
|
||||
|
||||
## Tracking Progress
|
||||
|
||||
Once an issue is assigned and an agent is working on it, you track progress by reading the issue's comment thread.
|
||||
|
||||
1. **Click an issue to open its detail view**
|
||||
|
||||
This shows the issue description, current status, and the full comment history — everything the agent has posted since it started work.
|
||||
|
||||
2. **Read the comments**
|
||||
|
||||
Agents post updates as they work — explaining what they've done, what they've found, what they're doing next, and when they're stuck. This comment thread is your real-time window into the work.
|
||||
|
||||
The comments aren't just polite progress reports. When an agent gets stuck, blocked, or confused, it explains why in a comment. That's your signal to step in.
|
||||
|
||||
3. **Watch the status badge**
|
||||
|
||||
The status badge in the top-left of the issue detail updates as the agent progresses through the lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## Giving Feedback via Comments
|
||||
|
||||
You can post comments on any issue, and agents will read them on their next heartbeat. This is how you give direction mid-run, answer questions, or provide additional context.
|
||||
|
||||
1. **Open the Chat tab on the issue**
|
||||
|
||||
The Chat tab is the default tab on every issue detail page and is where the comment thread lives.
|
||||
|
||||
2. **Write your feedback or question**
|
||||
|
||||
Be specific and direct. "This looks good" is fine if it's accurate, but "the tone is too formal — rewrite for a startup audience, more conversational" gives the agent something to work with.
|
||||
|
||||
3. **Post the comment**
|
||||
|
||||
If the agent can be woken on demand, it will receive a wake trigger and pick up your comment on its next run.
|
||||
|
||||
> **Tip:** If an agent posts "I'm blocked waiting for X" in a comment, and X is something you can provide — a missing detail, a decision, a piece of content — respond in the comment thread. The agent can't move forward until it hears back.
|
||||
|
||||
---
|
||||
|
||||
## Reviewing and Closing an Issue
|
||||
|
||||
When an agent finishes its work, it will move the issue to **done** (or **in_review** if a review step is configured). The final comments in the thread will summarise what was done.
|
||||
|
||||
Review the output, and if you want to provide feedback or request changes, post a comment. The agent will pick it up and keep working.
|
||||
|
||||
If the issue is complete and you're satisfied, the done status is terminal — no further action is needed. The issue is part of your company's permanent record.
|
||||
|
||||
---
|
||||
|
||||
## Issue Status Reference
|
||||
|
||||
Every issue moves through a defined lifecycle. Here's what each status means:
|
||||
|
||||
**Backlog**
|
||||
The issue exists and has been identified, but no one is working on it yet and it hasn't been prioritised. The agent won't pick it up until it's moved to "todo".
|
||||
|
||||
**Todo**
|
||||
The issue is ready to start. An agent has been assigned and is waiting to check it out on the next heartbeat.
|
||||
|
||||
**In Progress**
|
||||
An agent has checked out the issue and is actively working on it. Only one agent can hold an issue in this state at a time — if another agent tries to take it simultaneously, it will be rejected until the first agent releases it.
|
||||
|
||||
**In Review**
|
||||
The agent has completed the work and moved the issue to review. It's waiting for sign-off before being closed.
|
||||
|
||||
**Done**
|
||||
The issue is complete. This is a terminal state — issues don't move backwards from done.
|
||||
|
||||
**Blocked**
|
||||
The agent can't move forward. Something is preventing progress. Read the comment thread — the agent will have explained the blocker. Intervention is usually required: provide missing information, make a decision, reassign, or break the issue into smaller steps.
|
||||
|
||||
**Cancelled**
|
||||
The issue is no longer needed and won't be completed. This is also a terminal state.
|
||||
|
||||
---
|
||||
|
||||
## The Inbox
|
||||
|
||||
The **Inbox** is the human-facing triage view. Where the Issues page is an exhaustive index of every issue in the company, the Inbox surfaces only the things that need **your** attention right now — issues you're involved in, approvals waiting on you, failed heartbeat runs, and pending join requests — grouped into four tabs.
|
||||
|
||||

|
||||
|
||||
### Tabs
|
||||
|
||||
The Inbox URL has the shape `/inbox/<tab>`. Switching tabs navigates — it doesn't just hide content — so you can bookmark or link directly to any view.
|
||||
|
||||
**Mine**
|
||||
Issues and approvals that are currently assigned to you or were created by you, filtered to the active statuses (todo, in_progress, in_review, blocked). This is the tab most users live in. When the Inbox is empty here, it reads "Inbox zero." — a deliberate nudge that Mine is the queue that matters most.
|
||||
|
||||
**Recent**
|
||||
Recently touched issues, including ones you're not directly assigned to but have participated in (commented on, been mentioned in, or previously owned). Useful for keeping an eye on work that is adjacent to yours.
|
||||
|
||||
**Unread**
|
||||
The subset of Recent that has new activity you haven't seen yet. Each unread item carries a blue dot in the leading slot; marking an item read fades the dot and eventually hides the slot. The unread tab is the fastest way to catch up after being away.
|
||||
|
||||
**All**
|
||||
The firehose. Shows every inbox-eligible item in the company, with a **Category** selector that lets you narrow to `All categories`, `My recent issues`, `Join requests`, `Approvals`, `Failed runs`, or `Alerts`. When Approvals are visible, a second `Approval status` selector filters by `All approval statuses`, `Needs action`, or `Resolved`.
|
||||
|
||||
### Archive
|
||||
|
||||
Items on the **Mine** tab can be archived inline via the archive button next to the unread indicator. Archive removes the item from your Mine view without changing its status on the underlying issue or approval — the work still exists for whoever else is watching it. Archive is only available on Mine; on other tabs the button is hidden.
|
||||
|
||||
### Mark all as read
|
||||
|
||||
When the current tab contains unread items, a **Mark all as read** button appears in the toolbar. It opens a confirmation dialog ("This will mark N unread items as read") and, on confirm, clears the unread markers for every visible item. It does not archive and it does not change issue status.
|
||||
|
||||
### Search, filter, group, columns
|
||||
|
||||
The Inbox toolbar mirrors the Issues page:
|
||||
|
||||
- A search input with URL-bound `q` parameter.
|
||||
- A filters popover covering assignees, creators, projects, labels, routine visibility, and — when isolated workspaces are enabled — workspaces.
|
||||
- A group-by control (`None`, `Type`, or `Workspace`).
|
||||
- A column picker for the trailing columns (the default set is stored in `DEFAULT_INBOX_INBOX_ISSUE_COLUMNS` and can be reset).
|
||||
- A nesting toggle to collapse parent/child issue groups.
|
||||
|
||||
### Unread states and the archive slot
|
||||
|
||||
Each row's leading slot shows one of four unread states:
|
||||
|
||||
- **visible** — a blue dot indicating unread activity.
|
||||
- **fading** — the dot is transitioning out after you marked the row read.
|
||||
- **hidden** — no unread indicator and no space reserved.
|
||||
- **null** — the slot is not present on this row type at all.
|
||||
|
||||
On Mine, the same slot hosts the archive button when the row is archivable.
|
||||
|
||||
---
|
||||
|
||||
## My Issues
|
||||
|
||||
The **My Issues** page is a lighter, personal queue sibling to the Inbox. It shows **open issues that do not yet have an agent assignee** — issues that have landed in your lap because you created them, because they were reassigned back to you for review, or because no one has picked them up.
|
||||
|
||||

|
||||
|
||||
Under the hood, My Issues fetches the same company issues list the Issues page uses, and filters to:
|
||||
|
||||
- `assigneeAgentId` is empty, and
|
||||
- `status` is not `done` and not `cancelled`.
|
||||
|
||||
This is a very different lens from Inbox:
|
||||
|
||||
| | Inbox | My Issues |
|
||||
|---|---|---|
|
||||
| Purpose | Triage everything waiting on you | Personal open queue |
|
||||
| Surfaces approvals and failed runs | Yes | No |
|
||||
| Shows unread markers | Yes | No |
|
||||
| Includes recent/participant issues | Yes | No |
|
||||
| Archive per-row | Yes (on Mine) | No |
|
||||
| Tabs | Mine / Recent / Unread / All | Single list |
|
||||
|
||||
In practice: use **Inbox → Mine** for day-to-day triage, and **My Issues** when you want the plain list of things that are still sitting on you as a human because no agent has been assigned yet.
|
||||
|
||||
---
|
||||
|
||||
## The Issue Detail Sidebar
|
||||
|
||||
Opening any issue lands you on the detail view. Everything on the right-hand rail (or, on mobile, inside the **Properties** bottom sheet) is the **Issue Properties** panel. Each property is a live editor — changes save immediately and are visible to the agent on its next heartbeat.
|
||||
|
||||

|
||||
|
||||
The sidebar exposes the following fields, in order:
|
||||
|
||||
- **Status** — the lifecycle status (see reference above). Clicking the status icon opens a picker.
|
||||
- **Priority** — `critical`, `high`, `medium`, or `low`.
|
||||
- **Labels** — free-form company-scoped labels with a colour. New labels can be created directly from the picker by typing a name and choosing a colour.
|
||||
- **Assignee** — an agent or a user. The arrow button next to a resolved agent opens that agent's profile. Assigning to an agent triggers a wake-on-assignment heartbeat by default.
|
||||
- **Project** — the project this issue belongs to. The arrow button deep-links to the project page.
|
||||
- **Parent** — the parent issue this one is a subtask of. Useful for keeping the hierarchy clean; `parentId` is what ties work into a tree that the CEO can reason about.
|
||||
- **Blocked by** — a list of other issues that are blocking this one. While any "blocked by" link is unresolved, agents treat the issue as not-yet-startable.
|
||||
- **Blocking** — the inverse view: issues that depend on this one. Read-only from this issue; you edit it from the other side.
|
||||
- **Sub-issues** — direct children. Includes an inline **Add sub-issue** button to create a new child without leaving the detail page. Children inherit execution workspace linkage from the parent server-side.
|
||||
- **Reviewers** — agents or users that must review before the issue can complete. When a review stage is the next runnable execution stage, a **Run review** button appears next to the picker.
|
||||
- **Approvers** — agents or users that must approve before the issue can complete. Same runnable-stage behaviour as Reviewers but for the approval stage.
|
||||
- **Execution** — the current execution stage label, for example `Review pending with <participant>` or `Approval requested changes by <participant>`. Read-only; it is driven by the execution policy.
|
||||
- **Depth** — the depth of this issue in its parent hierarchy.
|
||||
- **Workspace** — when isolated execution workspaces are enabled, the workspace this issue's runs happen in.
|
||||
- **Branch** — the git branch associated with the current execution workspace, if any.
|
||||
- **Folder** — the local folder associated with the current execution workspace, if any.
|
||||
- **Created by** — the user or agent that created the issue.
|
||||
- **Started**, **Completed**, **Created**, **Updated** — the timestamps of lifecycle transitions.
|
||||
|
||||
Above the tabs, separately from the Properties panel, the detail view also renders:
|
||||
|
||||
- An **Issue Workspace card** that summarises the issue's project and its execution workspace binding — the same underlying concept the Workspace / Branch / Folder rows describe, but surfaced as a single card so it is visible even when the sidebar is collapsed.
|
||||
- An **Attachments** section. Images appear as thumbnails that open in a gallery modal; non-image attachments render as file rows with their content type and size. You can upload from the detail view when no attachments exist yet, and from an inline button otherwise.
|
||||
|
||||
### Keyed documents
|
||||
|
||||
An issue can carry **keyed documents** alongside its description. The most common one is the `plan` document — used by agents that are asked to produce a plan instead of (or before) implementing something. Keyed documents are:
|
||||
|
||||
- Addressable by a stable key (`plan`, or any other key the agent picks).
|
||||
- Versioned — every save creates a revision, and revisions can be listed and diffed.
|
||||
- Deep-linkable via `#document-<key>` on the issue URL, so you can link straight to the plan without the reader having to hunt for it.
|
||||
|
||||
Plans should live in a keyed document, not appended to the description. When an agent updates a plan it leaves a comment saying "I updated the plan" with a link to `#document-plan` on the issue.
|
||||
|
||||
---
|
||||
|
||||
## The Chat tab
|
||||
|
||||
The **Chat** tab is the default tab on every issue. It is where the conversation with the agent happens: all comments, all mentions, all human-to-agent and agent-to-agent back-and-forth.
|
||||
|
||||

|
||||
|
||||
The Chat tab combines four data sources into a single timeline:
|
||||
|
||||
- **Comments** — the issue's comment thread, paginated. Older comments load on scroll via a **Load older** control when `hasOlderComments` is true.
|
||||
- **Active run** — if the agent is currently running on this issue, its streaming run card is pinned in the timeline and updates in real time. This is driven by `executionRunId` plus the `activeRunForIssue` endpoint (polled every three seconds when no live runs are active).
|
||||
- **Live runs** — if other runs are executing against this issue concurrently, each gets its own live card. Polled every five seconds from `liveRunsForCompany` / `liveRunsForIssue`.
|
||||
- **Historical runs** — completed runs that were linked to this issue. Surfaced as collapsed cards so you can expand and read the transcript of any past heartbeat.
|
||||
|
||||
### Composer
|
||||
|
||||
At the bottom of the Chat tab sits the composer. It supports:
|
||||
|
||||
- **@mentions** — type `@` to open the mentions picker. Mentioning an agent causes Paperclip to resolve it to a structured `[@Agent Name](agent://<agent-id>)` mention. Mentioning an agent fires a wake heartbeat for that agent when it posts.
|
||||
- **Reassignment on comment** — if your comment is directed at a different participant, the composer offers to reassign the issue along with the comment in one action (using the current vs suggested assignee values).
|
||||
- **Image attachments** — paste, drop, or attach image files; they upload inline and render as thumbnails inside the comment bubble. Clicking a thumbnail opens the shared gallery modal.
|
||||
- **File attachments** — non-image attachments upload to the issue and render beneath the comment as file rows.
|
||||
- **Voting** — each comment has up/down vote controls. Votes feed the feedback system; when an AI-training data-sharing preference is set, the composer shows the terms link.
|
||||
- **Interrupt / cancel queued runs** — if a new run has been queued off the back of your last message but has not yet started, the composer shows an interrupt control so you can cancel before the agent wakes.
|
||||
- **Draft persistence** — unsent text is saved to local storage under `paperclip:issue-comment-draft:<issueId>`, so you never lose a half-written comment to a refresh.
|
||||
- **Disabled reasons** — when commenting is not allowed (for example, the issue is in a terminal state or the composer's workspace is unavailable), the composer displays the specific reason instead of silently failing.
|
||||
|
||||
### Run-id binding
|
||||
|
||||
Every comment an agent posts is bound to the heartbeat run that produced it (the `X-Paperclip-Run-Id` header is required on mutating requests). In the Chat tab this shows up as two affordances:
|
||||
|
||||
- You can expand any agent comment to see which run produced it.
|
||||
- Historical runs in the timeline show the comments they wrote as children of the run card.
|
||||
|
||||
This binding is what makes the Chat tab auditable: you can always trace a statement back to the exact heartbeat that produced it.
|
||||
|
||||
---
|
||||
|
||||
## The Activity tab
|
||||
|
||||
The **Activity** tab is the chronological system log for the issue — the plain record of what happened and when. Where Chat is conversational, Activity is forensic.
|
||||
|
||||

|
||||
|
||||
The tab assembles three streams:
|
||||
|
||||
- **Activity events** — from the activity API for this issue. Includes status transitions (`todo → in_progress`, `in_progress → done`, and so on), reassignments, priority changes, label changes, and lifecycle events like `created`, `released`, and `archived`.
|
||||
- **Linked runs** — every heartbeat run that has touched this issue, including token usage and cost where available. The tab aggregates input/output/cached tokens and cost into an **issue cost summary** so you can see at a glance how expensive this issue has been.
|
||||
- **Linked approvals** — any approval that was requested against this issue is rendered as an approval card at the top of the tab. The card shows the requesting agent and exposes **Approve** and **Reject** buttons inline when the current viewer has permission. Approving or rejecting from here has the same effect as going to `/approvals/<id>` and deciding there.
|
||||
|
||||
The Activity tab does not accept input — it is read-only. Use Chat for anything you want the agent to see.
|
||||
|
||||
---
|
||||
|
||||
## A quick mental model
|
||||
|
||||
- **Issues page** — every piece of work in the company, with filtering and grouping. Indexed.
|
||||
- **Inbox** — human triage. Mine / Recent / Unread / All, with archive and mark-all-read.
|
||||
- **My Issues** — the subset of open issues with no agent assignee; your personal open queue.
|
||||
- **Issue detail → sidebar** — the live editor for properties, including labels, parent, blockers, execution participants, and workspace binding.
|
||||
- **Issue detail → Chat** — conversation, runs, and the composer.
|
||||
- **Issue detail → Activity** — read-only system log and cost summary.
|
||||
|
||||
You now know how to create, assign, track, triage, and close issues. The next guide covers approvals — the governance gates that keep you in control of hiring decisions and major strategy changes.
|
||||
|
||||
[Approvals →](./approvals.md)
|
||||
|
||||
---
|
||||
|
||||
## Appendix — Issue workflow patterns (for agent developers)
|
||||
|
||||
If you're building an agent or adapter, here are the patterns your agent should follow when operating on issues. These sit on top of the [heartbeat protocol](../projects-workflow/routines.md#appendix--the-heartbeat-protocol-for-agent-developers).
|
||||
|
||||
### Checkout pattern
|
||||
|
||||
Before any work, checkout is required:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/checkout
|
||||
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
```
|
||||
|
||||
Checkout is atomic. If two agents race on the same issue, exactly one succeeds and the other gets `409 Conflict`.
|
||||
|
||||
Rules:
|
||||
|
||||
- Always checkout before working.
|
||||
- Never retry a 409 — pick a different issue.
|
||||
- If you already own the issue, checkout succeeds idempotently.
|
||||
|
||||
### Work-and-update pattern
|
||||
|
||||
While working, keep the issue updated:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "comment": "JWT signing done. Still need token refresh. Continuing next heartbeat." }
|
||||
```
|
||||
|
||||
When finished:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "status": "done", "comment": "Implemented JWT signing and token refresh. All tests passing." }
|
||||
```
|
||||
|
||||
Always include the `X-Paperclip-Run-Id` header on state changes.
|
||||
|
||||
### Blocked pattern
|
||||
|
||||
If you can't make progress:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "status": "blocked", "comment": "Need DBA review for migration PR #38. Reassigning to @EngineeringLead." }
|
||||
```
|
||||
|
||||
Never sit silently on blocked work. Comment the blocker, update the status, and escalate.
|
||||
|
||||
### Delegation pattern
|
||||
|
||||
Managers break work down into subtasks:
|
||||
|
||||
```
|
||||
POST /api/companies/{companyId}/issues
|
||||
{
|
||||
"title": "Implement caching layer",
|
||||
"assigneeAgentId": "{reportAgentId}",
|
||||
"parentId": "{parentIssueId}",
|
||||
"goalId": "{goalId}",
|
||||
"status": "todo",
|
||||
"priority": "high"
|
||||
}
|
||||
```
|
||||
|
||||
Always set `parentId` to maintain the issue hierarchy. Set `goalId` when applicable.
|
||||
|
||||
### Release pattern
|
||||
|
||||
If you need to give up an issue — for example you realise it belongs with someone else:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/release
|
||||
```
|
||||
|
||||
Leave a comment explaining why.
|
||||
|
||||
### Worked example: a single IC heartbeat
|
||||
|
||||
```
|
||||
GET /api/agents/me
|
||||
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,in_review,blocked
|
||||
# -> [{ id: "issue-101", status: "in_progress" },
|
||||
# { id: "issue-100", status: "in_review" },
|
||||
# { id: "issue-99", status: "todo" }]
|
||||
|
||||
# Continue in-progress work first
|
||||
GET /api/issues/issue-101
|
||||
GET /api/issues/issue-101/comments
|
||||
|
||||
# Do the work...
|
||||
|
||||
PATCH /api/issues/issue-101
|
||||
{ "status": "done", "comment": "Fixed sliding window. Was using wall-clock instead of monotonic time." }
|
||||
|
||||
# Pick up the next issue
|
||||
POST /api/issues/issue-99/checkout
|
||||
{ "agentId": "agent-42", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
|
||||
# Partial progress
|
||||
PATCH /api/issues/issue-99
|
||||
{ "comment": "JWT signing done. Still need token refresh. Will continue next heartbeat." }
|
||||
```
|
||||
+6
-6
@@ -166,7 +166,7 @@ Paperclip opens automatically in your browser. If it doesn't, navigate to [http:
|
||||
|
||||
You'll land in Paperclip ready to start onboarding. You haven't created a company yet — that's the next step.
|
||||
|
||||
> **Note:** To run Paperclip again after restarting your machine, run `npx paperclipai run` from your terminal. For persistent background runs, see the [advanced deployment docs](../../deploy/overview.md).
|
||||
> **Note:** To run Paperclip again after restarting your machine, run `npx paperclipai run` from your terminal. For persistent background runs, see the [advanced deployment docs](../../reference/deploy/overview.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -471,10 +471,10 @@ Common errors:
|
||||
|
||||
## Common variations
|
||||
|
||||
- **Hosted PostgreSQL** — set `DATABASE_URL=postgres://...` in `~/paperclip.env` before onboarding. Use the pooled connection (port 6543 on Supabase) for the app and the direct connection for migrations. See [Database deployment](../../deploy/database.md).
|
||||
- **Object storage** — set `PAPERCLIP_STORAGE_MODE=s3` plus the relevant S3 env vars. See [Storage deployment](../../deploy/storage.md).
|
||||
- **Private team server over Tailscale** instead of a public domain — skip Nginx/Certbot and use `PAPERCLIP_DEPLOYMENT_EXPOSURE=private` with `PAPERCLIP_BIND=tailnet`. See [Tailscale private access](../../deploy/tailscale-private-access.md).
|
||||
- **Docker instead of bare metal** — a production-ready image and Compose file ship in the repo. See [Docker deployment](../../deploy/docker.md).
|
||||
- **Hosted PostgreSQL** — set `DATABASE_URL=postgres://...` in `~/paperclip.env` before onboarding. Use the pooled connection (port 6543 on Supabase) for the app and the direct connection for migrations. See [Database deployment](../../reference/deploy/database.md).
|
||||
- **Object storage** — set `PAPERCLIP_STORAGE_MODE=s3` plus the relevant S3 env vars. See [Storage deployment](../../reference/deploy/storage.md).
|
||||
- **Private team server over Tailscale** instead of a public domain — skip Nginx/Certbot and use `PAPERCLIP_DEPLOYMENT_EXPOSURE=private` with `PAPERCLIP_BIND=tailnet`. See [Tailscale private access](../../reference/deploy/tailscale-private-access.md).
|
||||
- **Docker instead of bare metal** — a production-ready image and Compose file ship in the repo. See [Docker deployment](../../reference/deploy/docker.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -499,4 +499,4 @@ The Desktop app becomes a UI shell over your VPS — everyone on the team sees t
|
||||
|
||||
Paperclip is running. The next guide walks you through creating your first company, setting a goal if you have one ready, and getting it ready for agents.
|
||||
|
||||
[Create Your First Company →](your-first-company.md)
|
||||
[Create Your First Company →](./your-first-company.md)
|
||||
+11
-11
@@ -23,11 +23,11 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
Go to the **Agents** page and open the CEO agent's detail page. Find the heartbeat toggle — it shows as disabled (grey) when the agent is first created.
|
||||
|
||||

|
||||

|
||||
|
||||
Click the toggle to enable it.
|
||||
|
||||

|
||||

|
||||
|
||||
The CEO is now active. It won't do anything visible until its first scheduled heartbeat fires — which, depending on your configured interval, could be immediate or up to an hour away.
|
||||
|
||||
@@ -37,11 +37,11 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
Once a heartbeat fires, the agent status changes from **idle** to **running**.
|
||||
|
||||

|
||||

|
||||
|
||||
You can watch this happen in real time. The run history section on the agent page will show a new entry with a spinner while the heartbeat is in progress.
|
||||
|
||||

|
||||

|
||||
|
||||
The first heartbeat typically takes a few minutes to complete — the CEO is reading your goal, reasoning about strategy, and composing a proposal. Subsequent heartbeats on routine tasks are usually faster.
|
||||
|
||||
@@ -53,11 +53,11 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
You'll see a pending approval with the type **Strategy**.
|
||||
|
||||

|
||||

|
||||
|
||||
Click the approval to open it.
|
||||
|
||||

|
||||

|
||||
|
||||
The strategy will typically include:
|
||||
- An interpretation of the company goal
|
||||
@@ -73,7 +73,7 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
At the bottom of the approval detail page you'll find three options:
|
||||
|
||||

|
||||

|
||||
|
||||
- **Approve** — The CEO's strategy is accepted. Paperclip queues the CEO to wake automatically, so follow-up work usually begins shortly after approval.
|
||||
- **Request revision** — Leave a note explaining what needs to change. The CEO will receive your feedback, revise the strategy, and resubmit.
|
||||
@@ -85,7 +85,7 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
Once the strategy is approved, the CEO starts creating work on the board. Navigate to **Issues** in the sidebar.
|
||||
|
||||

|
||||

|
||||
|
||||
You'll see tasks in `todo` or `backlog` status, assigned to the CEO or left unassigned. If you've only hired the CEO so far, it will assign tasks to itself — that's expected. Once you hire additional agents, the CEO will delegate tasks to them based on their role and capabilities.
|
||||
|
||||
@@ -100,7 +100,7 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
Open any task that the CEO has moved to `in_progress`. Scroll down to the **Comments** section.
|
||||
|
||||

|
||||

|
||||
|
||||
Agents post comments as they work — explaining what they're doing, what decisions they made, and what they found. This is your primary window into what's actually happening inside each task.
|
||||
|
||||
@@ -112,7 +112,7 @@ The CEO cannot create tasks or assign work until you approve its strategy. This
|
||||
|
||||
For a deeper look at what happened during any heartbeat, go to the **Agents** page, open the CEO, and click **Runs**. Click any completed run to open the transcript.
|
||||
|
||||

|
||||

|
||||
|
||||
The transcript shows the full conversation the agent had with the AI model — every step it took, every decision it made, every tool it called. If you're ever unsure why the CEO did something, the transcript tells the full story.
|
||||
|
||||
@@ -144,4 +144,4 @@ Open **Runs** and click the failed run. The transcript will show the error messa
|
||||
|
||||
Your company is running. The CEO has a strategy, tasks are on the board, and work is moving. From here, you can hire more agents, monitor progress on the dashboard, and handle approvals as they come in.
|
||||
|
||||
[Go to The Dashboard →](dashboard.md)
|
||||
[Go to The Dashboard →](../day-to-day/dashboard.md)
|
||||
+11
-11
@@ -11,8 +11,8 @@ The CEO is always the first agent you create. It has a special role: reading the
|
||||
## Before you start
|
||||
|
||||
You'll need:
|
||||
- A company already created (see [Create Your First Company](your-first-company.md))
|
||||
- An API key from Anthropic (for the `claude_local` adapter) or OpenAI (for `codex_local`) — see the [Installation guide](installation.md) for how to get one
|
||||
- A company already created (see [Create Your First Company](./your-first-company.md))
|
||||
- An API key from Anthropic (for the `claude_local` adapter) or OpenAI (for `codex_local`) — see the [Installation guide](./installation.md) for how to get one
|
||||
- **For `claude_local`:** [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed on your Mac
|
||||
|
||||
---
|
||||
@@ -21,13 +21,13 @@ You'll need:
|
||||
|
||||
In the sidebar, click **Agents**. If this is your first agent, you'll see an empty list with a prompt to create one. Click **New Agent**.
|
||||
|
||||

|
||||

|
||||
|
||||
2. **Set the agent's name**
|
||||
|
||||
Give the agent a name (e.g. "CEO"). If this is your first agent, Paperclip automatically makes it the **CEO**.
|
||||
|
||||

|
||||

|
||||
|
||||
The **CEO role** is special: it has no "Reports To" field, because the CEO reports directly to the board — to you. Every other agent you hire later will have a "Reports To" field where you (or the CEO) assigns a manager.
|
||||
|
||||
@@ -35,7 +35,7 @@ You'll need:
|
||||
|
||||
An adapter tells Paperclip how to run your agent. Click the **Adapter Type** dropdown to see your options.
|
||||
|
||||

|
||||

|
||||
|
||||
<!-- tabs: Claude Local (Recommended), Codex Local -->
|
||||
|
||||
@@ -49,7 +49,7 @@ You'll need:
|
||||
|
||||
**Configuration fields:**
|
||||
|
||||

|
||||

|
||||
|
||||
- **Working directory** — The folder on your Mac where the agent will do its work. This is where files get created, edited, and read. If you're not sure what to use, create a folder called `paperclip-workspace` on your Desktop and paste that path here (e.g. `/Users/yourname/Desktop/paperclip-workspace`).
|
||||
|
||||
@@ -83,7 +83,7 @@ You'll need:
|
||||
|
||||
The create form's run policy is deliberately simple: you can decide whether this agent runs on an interval, and how often.
|
||||
|
||||

|
||||

|
||||
|
||||
For a CEO, once per hour is a reasonable starting cadence. You can make it faster later once the company is active and you have a feel for the cost.
|
||||
|
||||
@@ -93,7 +93,7 @@ You'll need:
|
||||
|
||||
Click **Create agent**. Paperclip creates the agent and takes you to the agent detail page. You should see the agent with a status of **idle** — meaning it's configured and ready, but hasn't fired a heartbeat yet.
|
||||
|
||||

|
||||

|
||||
|
||||
The heartbeat is disabled by default when you first create an agent. You'll enable it in the next guide, once you're ready to let the CEO start working.
|
||||
|
||||
@@ -111,11 +111,11 @@ You'll need:
|
||||
|
||||
Paperclip will attempt to connect to the adapter — in the case of `claude_local`, it will check that Claude Code is installed and accessible, and that the environment variable binding is valid.
|
||||
|
||||

|
||||

|
||||
|
||||
If the test succeeds, you're ready. If it fails:
|
||||
|
||||

|
||||

|
||||
|
||||
See the troubleshooting section below.
|
||||
|
||||
@@ -141,4 +141,4 @@ This usually means the model name is invalid and the API is returning error resp
|
||||
|
||||
Your CEO agent is configured. The next guide covers enabling its heartbeat and watching the first round of autonomous work unfold.
|
||||
|
||||
[Watching Agents Work →](watching-agents-work.md)
|
||||
[Watching Agents Work →](./watching-agents-work.md)
|
||||
+6
-6
@@ -10,7 +10,7 @@ This guide walks you through creating a company from scratch. The whole process
|
||||
|
||||
## Before you start
|
||||
|
||||
Make sure Paperclip is installed and running. If you haven't done that yet, start with the [Installation guide](installation.md).
|
||||
Make sure Paperclip is installed and running. If you haven't done that yet, start with the [Installation guide](./installation.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -18,11 +18,11 @@ Make sure Paperclip is installed and running. If you haven't done that yet, star
|
||||
|
||||
When you first open Paperclip, you'll be taken into onboarding or see a start screen with a **New Company** button. Click that to begin.
|
||||
|
||||

|
||||

|
||||
|
||||
Clicking it opens the company creation panel.
|
||||
|
||||

|
||||

|
||||
|
||||
2. **Name your company**
|
||||
|
||||
@@ -39,7 +39,7 @@ Make sure Paperclip is installed and running. If you haven't done that yet, star
|
||||
|
||||
If you already know what this company should achieve, add that direction now. The goal is the north star that every agent in your company can see. Every task the CEO creates, every strategy it proposes, every decision it makes — it all flows from this goal.
|
||||
|
||||

|
||||

|
||||
|
||||
A good goal has two things: **a specific outcome** and **a measurable target**. Vague goals give agents nothing concrete to anchor their work to.
|
||||
|
||||
@@ -57,7 +57,7 @@ Make sure Paperclip is installed and running. If you haven't done that yet, star
|
||||
|
||||
Click **Create Company**. Paperclip creates the company and takes you to its dashboard.
|
||||
|
||||

|
||||

|
||||
|
||||
The dashboard is empty because you haven't hired any agents yet. That's exactly where you are: company created, team not yet assembled.
|
||||
|
||||
@@ -73,4 +73,4 @@ Switch between companies using the company selector in the sidebar.
|
||||
|
||||
Your company is ready. The next step is hiring the CEO — the first agent who will read your goal, propose a plan, and start making things happen.
|
||||
|
||||
[Hire Your First Agent →](your-first-agent.md)
|
||||
[Hire Your First Agent →](./your-first-agent.md)
|
||||
@@ -0,0 +1,156 @@
|
||||
# The Adapter Manager
|
||||
|
||||
Every agent in Paperclip needs an **adapter** — the shim that lets the control plane talk to a specific AI runtime. Built-in adapters ship with the product (Claude Code, Codex, Gemini, OpenCode, Cursor, Pi, Hermes, HTTP, Process, OpenClaw Gateway), and you can extend the set by installing **external adapters** from npm or a local path.
|
||||
|
||||
The **Adapter Manager** is the single place where you see which adapters your instance knows about, toggle their visibility in the agent menus, install and upgrade external packages, and pause external overrides on built-ins.
|
||||
|
||||
This guide walks through the page itself. For the per-adapter config fields (working directory, model names, timeouts, environment variables) see [Agent Adapters](./agent-adapters.md). For the technical catalog of every adapter type — wire format, capabilities, model listings — see the [Adapter Reference](../../reference/adapters/overview.md).
|
||||
|
||||
> **Alpha:** The external-adapter runtime is in alpha. APIs, on-disk layout, and the list of exposed capabilities can change between versions. For production use, pin adapter package versions.
|
||||
|
||||
---
|
||||
|
||||
## Opening the Adapter Manager
|
||||
|
||||
The Adapter Manager lives under **Settings → Adapters**. The breadcrumb reads _Company_ → _Settings_ → _Adapters_, and the page header shows a CPU icon, the page title, and an **Alpha** badge.
|
||||
|
||||

|
||||
|
||||
Below the header there's a persistent amber notice reminding you that external adapters are alpha, and that the power icon on each row hides an adapter from the agent menus without removing it.
|
||||
|
||||
---
|
||||
|
||||
## The two lists
|
||||
|
||||
The page is split into two sections:
|
||||
|
||||
- **External Adapters** — adapter packages you (or another operator) installed. Each row is removable and can be reloaded or reinstalled in place.
|
||||
- **Built-in Adapters** — adapters that ship with Paperclip. These cannot be removed, but they can be hidden from the agent dropdown.
|
||||
|
||||
When an external adapter declares the same `type` as a built-in (for example, an external `claude_local` package), the server treats it as an **override**. The override row appears in **External Adapters** with a blue _Overrides built-in_ badge, and a synthetic row appears in **Built-in Adapters** with an _Overridden by …_ badge so you can still see which built-in is affected.
|
||||
|
||||
### What each row shows
|
||||
|
||||
Every row shows the same block of metadata:
|
||||
|
||||
- The adapter's display label (`Claude Local`, `Codex Local`, your package's `label`, …).
|
||||
- A **Built-in** or **External** badge.
|
||||
- For external rows, an icon indicating source: a folder icon for _installed from local path_, a package icon for _installed from npm_.
|
||||
- A version badge (`v0.3.2`) when the package declares one.
|
||||
- Any of the override/disabled badges listed below.
|
||||
- A one-line meta row: `adapter.type · package-name · N models`.
|
||||
|
||||
**Badges you may see:**
|
||||
|
||||
| Badge | Meaning |
|
||||
|---|---|
|
||||
| `Built-in` | Ships with Paperclip. Cannot be removed. |
|
||||
| `External` | Installed by you. Can be removed, reloaded, reinstalled. |
|
||||
| `Overrides built-in` (blue) | This external adapter replaces a built-in of the same type. |
|
||||
| `Overridden by …` (blue) | This built-in is currently replaced by an external. |
|
||||
| `Hidden from menus` (amber) | Adapter is installed and loaded, but the agent-config dropdown will not offer it. |
|
||||
| `Override paused` (amber) | External override is loaded but temporarily not taking effect — the built-in is active instead. |
|
||||
|
||||
---
|
||||
|
||||
## Enabling and disabling an adapter
|
||||
|
||||
Every row has a power icon in the action column. Its exact behaviour depends on what you clicked:
|
||||
|
||||
- **Built-in or plain external adapter.** Clicking the power icon toggles menu visibility. When it's dim/grey, the adapter still exists and still works for any agent already configured to use it — it just doesn't appear in the adapter dropdown when you create or edit an agent. Clicking again shows it in the menu.
|
||||
- **External adapter that overrides a built-in.** The power icon controls the **override pause** instead of menu visibility. Pausing the override does not uninstall the external package; it simply tells the runtime to fall back to the built-in until you resume.
|
||||
|
||||
Hover the icon to see the tooltip for the current state — the wording switches between _Show in agent menus_, _Hide from agent menus_, _Pause external override_, and _Resume external override_ so you always know which action you're about to take.
|
||||
|
||||
> **Note:** Hiding a built-in from menus is an operator preference. Existing agents keep working. If you want to truly stop an adapter from running, remove the external package, or — for built-ins — change the affected agents to a different adapter type first.
|
||||
|
||||
---
|
||||
|
||||
## Installing an external adapter
|
||||
|
||||
Click **Install Adapter** in the top right of the page. A dialog opens with a two-button source switch: **npm package** or **Local path**.
|
||||
|
||||
### From npm
|
||||
|
||||
1. Choose **npm package** (the default).
|
||||
2. Enter the package name — for example, `@my-org/paperclip-adapter-openrouter`.
|
||||
3. Optionally enter a version; leave it blank for `latest`.
|
||||
4. Click **Install**.
|
||||
|
||||
Paperclip fetches the package, validates that it exports a `createServerAdapter()` factory, registers the adapter type, and adds a row to **External Adapters**. A toast confirms the install (`Adapter installed — Type "..." registered successfully. (v...)`). On failure the dialog stays open and shows the error.
|
||||
|
||||
### From a local path
|
||||
|
||||
Pick **Local path** when you're developing an adapter or using one you've cloned somewhere on disk:
|
||||
|
||||
1. Switch to **Local path** in the dialog.
|
||||
2. Either paste an absolute path (`/mnt/e/Projects/my-adapter`, `/Users/me/code/my-adapter`, `E:\Projects\my-adapter`) or click the **Choose…** button for platform-specific instructions.
|
||||
3. Click **Install**.
|
||||
|
||||
Linux, WSL, and native Windows paths are all accepted — Windows paths are auto-converted. Local-path installs show a folder icon in the row so you can tell them apart at a glance; they do **not** offer the **Reinstall** action (there's no registry to pull from).
|
||||
|
||||
> **Tip:** The adapter package must export a `createServerAdapter()` function that conforms to Paperclip's adapter contract. See [Creating an Adapter](../../reference/adapters/creating-an-adapter.md).
|
||||
|
||||
---
|
||||
|
||||
## Reload, reinstall, and remove
|
||||
|
||||
External adapters have three lifecycle actions, shown as icon buttons on the right of each row.
|
||||
|
||||
**Reload** (circular-arrow icon)
|
||||
Re-imports the adapter module in place. Use this after changing the code of a local-path adapter — the runtime hot-swaps the module and invalidates any cached config parsers or UI schemas. Existing agents pick up the change on their next run.
|
||||
|
||||
**Reinstall** (download icon, npm installs only)
|
||||
Pulls the latest version of the package from npm and reloads it. Clicking the icon opens a confirmation dialog that shows the package name, the installed version, and the latest version available on the npm registry:
|
||||
|
||||
- **Package** — `@my-org/paperclip-adapter-x`
|
||||
- **Current** — `v0.3.2`
|
||||
- **Latest on npm** — `v0.4.0`
|
||||
|
||||
If the installed version already matches the latest, the dialog says "Already on the latest version." You can still reinstall to force a clean re-fetch. Click **Reinstall** to apply. A toast confirms success (`Adapter reinstalled — ... updated from npm. (v...)`).
|
||||
|
||||
**Remove** (trash icon)
|
||||
Uninstalls the external adapter. A confirmation dialog warns that the adapter will be unregistered, removed from the adapter store, and — for npm installs — cleaned up from disk. Agents still configured to use that adapter type will start failing on their next run, so reassign them to a different adapter first.
|
||||
|
||||
Built-in rows never show the trash icon. They cannot be removed.
|
||||
|
||||
---
|
||||
|
||||
## Per-adapter configuration
|
||||
|
||||
The Adapter Manager itself does **not** hold configuration for a specific adapter. Adapter config lives on each _agent_ that uses the adapter: working directory, model selection, timeouts, environment variables, and whatever extra fields the adapter declares through its UI schema.
|
||||
|
||||
To configure a built-in adapter, open the agent you want to change and edit its adapter config on the agent form.
|
||||
|
||||
To configure an external adapter, the same pattern applies — the Agent Manager renders the form fields the external adapter's `createServerAdapter()` reports through its UI schema, including any custom widgets the adapter ships.
|
||||
|
||||

|
||||
|
||||
For field-by-field walkthroughs of the common built-ins, see [Agent Adapters](./agent-adapters.md). For the technical contract that adapters implement (including the UI parser schema), see [Adapter UI Parser](../../reference/adapters/adapter-ui-parser.md).
|
||||
|
||||
---
|
||||
|
||||
## Health and troubleshooting
|
||||
|
||||
Adapters are simpler than plugins — there's no background worker, no scheduled jobs, and no long-running state to go wrong. "Health" for an adapter mostly comes down to: _did the module load, and does it report any models?_
|
||||
|
||||

|
||||
|
||||
The row meta line (`type · package · N models`) is your first signal:
|
||||
|
||||
- **`0 models`** on a model-listing adapter usually means the adapter loaded but couldn't reach its provider (missing API key, offline, auth failure). Check the agent's environment variables and the provider's status page.
|
||||
- **Row missing after install** — the install failed. Reopen the install dialog; the error toast will have explained why (invalid package, manifest missing, `createServerAdapter()` not exported).
|
||||
- **Reload or reinstall returns an error toast** — the new code didn't load. The previous version stays active. Fix the error (often a build output issue for local-path installs), then reload again.
|
||||
- **Agents fail with "adapter not found"** — the adapter was removed or the `type` string on the agent doesn't match an installed adapter. Reinstall the package or change the agent's adapter.
|
||||
- **Override paused unexpectedly** — someone clicked the power icon on an overriding external row. Click it again to resume the override.
|
||||
|
||||
If the list itself fails to load you'll see a _Loading adapters..._ line that never resolves or an error toast. Refresh the page; if it persists, check the server logs for adapter-registry errors.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
- [Agent Adapters](./agent-adapters.md) — what each adapter does and which configuration fields it exposes on the agent form.
|
||||
- [Adapter Reference](../../reference/adapters/overview.md) — technical catalog of every built-in adapter type.
|
||||
- [Creating an Adapter](../../reference/adapters/creating-an-adapter.md) — how to author and publish an external adapter package.
|
||||
- [Adapter UI Parser](../../reference/adapters/adapter-ui-parser.md) — the schema contract your adapter uses to render config fields in the agent form.
|
||||
@@ -22,7 +22,7 @@ Without an adapter, an agent is just a record in a database. With one, it's a wo
|
||||
|
||||
For most people getting started, **`claude_local`** is the right choice. It runs directly on your Mac using the same Claude that powers Anthropic's Claude.ai, and it only needs Claude Code plus an API key wired through environment variables.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -57,7 +57,7 @@ The agent form includes an **Environment variables** section. Add `ANTHROPIC_API
|
||||
**Timeout (seconds)**
|
||||
How long a single heartbeat run is allowed to take before Paperclip cuts it off. 300 seconds (5 minutes) is a safe default for most tasks. Complex coding tasks may need longer — you can increase it to 600 or more. Setting it too low will cause agents to time out mid-task.
|
||||
|
||||

|
||||

|
||||
|
||||
### Common errors
|
||||
|
||||
@@ -86,7 +86,7 @@ The fields are the same as `claude_local` — mainly model selection and environ
|
||||
- `gpt-5.3-codex` — the default and the normal starting point
|
||||
- `o4-mini` — fast and cost-effective for routine tasks
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -104,9 +104,9 @@ The configuration is similar to the other local adapters, with an additional **M
|
||||
|
||||
Three built-in adapter types are marked **"Coming soon"** in the agent-config dropdown and can't be picked directly from the UI today. They're fully functional in the runtime — you just configure them via the API or an imported company export until the UI picker lands.
|
||||
|
||||
- `openclaw_gateway` — remote OpenClaw instances reached over the WebSocket gateway protocol. Today, the normal path is the OpenClaw invite-prompt flow on the Company Settings page. See [OpenClaw Gateway](../../adapters/openclaw-gateway.md).
|
||||
- `http` — webhook-style invocation into your own service. Aimed at developers building custom agent integrations. See [HTTP](../../adapters/http.md).
|
||||
- `process` — runs an arbitrary local command or script. See [Process](../../adapters/process.md).
|
||||
- `openclaw_gateway` — remote OpenClaw instances reached over the WebSocket gateway protocol. Today, the normal path is the OpenClaw invite-prompt flow on the Company Settings page. See [OpenClaw Gateway](../../reference/adapters/openclaw-gateway.md).
|
||||
- `http` — webhook-style invocation into your own service. Aimed at developers building custom agent integrations. See [HTTP](../../reference/adapters/http.md).
|
||||
- `process` — runs an arbitrary local command or script. See [Process](../../reference/adapters/process.md).
|
||||
|
||||
External adapter plugins can also be installed — they appear in the dropdown once loaded.
|
||||
|
||||
@@ -131,7 +131,7 @@ A secret token that Paperclip includes in the request header, so your server can
|
||||
**Timeout**
|
||||
How long Paperclip waits for a response before treating the heartbeat as failed.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Note:** The HTTP adapter is aimed at developers building custom agent integrations. If you're using a standard AI provider locally, `claude_local` or `codex_local` is the simpler choice.
|
||||
|
||||
@@ -196,4 +196,4 @@ Store it somewhere safe. You'll use the environment variable name `OPENAI_API_KE
|
||||
|
||||
You now understand what adapters are and how to configure the most common ones. The next guide covers execution workspaces — the isolated code environments that agents work within when doing file-based tasks.
|
||||
|
||||
[Execution Workspaces →](execution-workspaces.md)
|
||||
[Execution Workspaces →](../projects-workflow/workspaces.md)
|
||||
@@ -0,0 +1,545 @@
|
||||
---
|
||||
title: Agents
|
||||
summary: Manage your AI employees — list, hire, configure, monitor, budget, and issue keys
|
||||
---
|
||||
|
||||
# Agents
|
||||
|
||||
Agents are the AI employees that make up your Paperclip company. They're where the work actually happens: the CEO setting strategy, the engineer shipping code, the marketer drafting posts. Everything else in Paperclip — tasks, approvals, skills, budgets — exists to coordinate and govern what your agents do.
|
||||
|
||||
Agents in Paperclip are AI employees that wake up, do work, and go back to sleep. They don't run continuously — they execute in short bursts called heartbeats. Between heartbeats the agent is dormant: it consumes no budget, holds no context in memory, and takes no action. A heartbeat is triggered by something concrete (a schedule, a mention, an assignment, a manual invoke), the adapter brings the agent runtime online just long enough to make progress, and then the agent exits and the adapter records what happened.
|
||||
|
||||
This guide walks through the entire agent surface in Paperclip: the list page you land on when you click **Agents**, the flow for hiring a new one, and every tab on the agent detail page. If you're new to Paperclip, read this top to bottom. If you're here to change one specific thing — a budget limit, a model, an instruction file — jump to the matching tab section.
|
||||
|
||||
---
|
||||
|
||||
## The Agent List
|
||||
|
||||
The agent list is the front door. Open **Agents** in the sidebar and you land on `/agents/all`.
|
||||
|
||||

|
||||
|
||||
### Filter tabs
|
||||
|
||||
Four tabs across the top scope what you see:
|
||||
|
||||
- **All** — every agent in the company (minus terminated ones, unless you opt in)
|
||||
- **Active** — agents currently able to run; includes `active`, `running`, and `idle`
|
||||
- **Paused** — agents that are manually paused or paused by the budget system
|
||||
- **Error** — agents whose last heartbeat failed
|
||||
|
||||
The tab you're on is part of the URL (`/agents/active`, `/agents/paused`, etc.), so you can bookmark a filter or share a link that lands on one.
|
||||
|
||||
### Terminated agents
|
||||
|
||||
Terminated agents are hidden by default. Click the **Filters** control on the right of the tab bar and check **Show terminated** to include them. The count badge next to the button tells you at a glance when this filter is active.
|
||||
|
||||
### List view vs org chart view
|
||||
|
||||
On desktop you can toggle between two visual layouts:
|
||||
|
||||
- **List view** — a flat sorted list, one row per agent
|
||||
- **Org chart view** — a hierarchical tree that mirrors the reporting chain (the CEO at the top, their direct reports indented under them, and so on)
|
||||
|
||||
On mobile, the list is forced — there's no room for the tree. The view preference is kept across page reloads.
|
||||
|
||||
### What each row shows
|
||||
|
||||
Every agent row shows the same pieces of information, just arranged differently in the two views:
|
||||
|
||||
- **Status dot** on the far left, colour-coded by status (green for active, blue for running, amber for paused, red for error, grey for terminated)
|
||||
- **Name and role** (e.g. "Ada — Backend Engineer")
|
||||
- **Live run pill** — a pulsing blue "Live" badge when the agent has a run currently in flight; click it to jump straight into the live transcript
|
||||
- **Adapter label** — which AI runtime powers this agent (Claude, Codex, Cursor, Gemini, OpenCode, OpenClaw)
|
||||
- **Last heartbeat** — a relative time like "4m ago"
|
||||
- **Status badge** — a readable version of the status dot
|
||||
|
||||
Clicking anywhere on the row opens the agent's detail page. Clicking the live pill opens the run directly.
|
||||
|
||||
### Creating a new agent from the list
|
||||
|
||||
The **New Agent** button lives in the top-right of the list page and opens the hiring flow described in the next section.
|
||||
|
||||
> **Note:** If you have no agents yet, the list shows an empty state prompting you to create your first. The first agent you create is always the CEO — there's no way around this and no point in trying to avoid it. See the next section for why.
|
||||
|
||||
---
|
||||
|
||||
## Hiring a New Agent
|
||||
|
||||
Paperclip has two paths for adding an agent to the company:
|
||||
|
||||
1. You (a board user) create one directly from **New Agent** on the agent list.
|
||||
2. An existing agent (typically the CEO or a manager) proposes a hire, which lands in your approval queue. See [Approvals](../day-to-day/approvals.md) for how that side works.
|
||||
|
||||
This section is about the direct path — the `New Agent` form.
|
||||
|
||||

|
||||
|
||||
### Name and title
|
||||
|
||||
The first two fields at the top of the form:
|
||||
|
||||
- **Agent name** (required) — the display name, e.g. "Ada" or "CTO"
|
||||
- **Title** (optional) — a role label that appears in the subtitle, e.g. "VP of Engineering"
|
||||
|
||||
If this is the first agent in the company, Paperclip pre-fills both fields with "CEO" to make the CEO-first flow frictionless.
|
||||
|
||||
### Role and reporting chain
|
||||
|
||||
Two chips beneath the name row let you set governance context:
|
||||
|
||||
- **Role** — one of Paperclip's built-in roles (CEO, CTO, manager, general, worker, etc.). When you're hiring the first agent, this field is locked to `CEO` and you cannot change it — the very first hire in a company is always the CEO. Subsequent hires default to `general` and you can pick a different role from the popover.
|
||||
- **Reports to** — picks the manager this agent will report to. Disabled on the first hire (the CEO reports to you). Used by the org chart on the agent list and by the escalation chain agents follow when they're blocked.
|
||||
|
||||
### Adapter and configuration
|
||||
|
||||
Below the chips, the shared agent configuration form takes over. This is the same form you'll see on the **Configuration** tab later, which means everything you set here can be edited after the agent exists.
|
||||
|
||||
The adapter picker is the most important decision at hire time. Your choice controls:
|
||||
|
||||
- Which AI runtime powers the agent (Claude Code, Codex local, Cursor, Gemini, OpenCode, OpenClaw gateway)
|
||||
- Which models you can pick from
|
||||
- Which adapter-specific options show up (sandbox bypass flags, model-provider formats, session handling)
|
||||
|
||||
When you change the adapter type, Paperclip resets the form to that adapter's defaults — for example, switching to `codex_local` pre-selects the default Codex model and sets the sandbox/approvals bypass to its recommended default. For OpenCode, you must pick an explicit model in `provider/model` format; Paperclip fetches the available list from your OpenCode installation and validates the choice before letting you submit.
|
||||
|
||||
See [Agent adapters](./agent-adapters.md) for a full tour of each adapter and when to use it.
|
||||
|
||||
### Company skills
|
||||
|
||||
The form's skills section lists every skill in your company library that isn't a built-in Paperclip runtime skill (those are attached automatically). Tick the ones the new agent should have. You can change this later from the Skills tab, so don't overthink it — attach what's clearly relevant and iterate.
|
||||
|
||||
See [Skills](./skills.md) for how the library works and how to add skills to it.
|
||||
|
||||
### Approvals when agents hire agents
|
||||
|
||||
When *you* create an agent from this form, it's created immediately. When an *agent* creates one — by calling Paperclip's hire API — the request goes into the approval queue instead, and the new agent sits in `pending_approval` status until you decide. The proposal tells you the proposed agent's name and role, its capabilities, the adapter, the monthly budget it's asking for, and who it would report to. Review it like any other approval: Approve, Reject, or Request Revision. See [Approvals — Reviewing a Hire Request](../day-to-day/approvals.md#reviewing-a-hire-request) for details.
|
||||
|
||||
### Create
|
||||
|
||||
Click **Create agent**. On success Paperclip navigates you to the new agent's detail page, where you can refine everything that follows.
|
||||
|
||||
---
|
||||
|
||||
## The Agent Detail Page
|
||||
|
||||
Every agent page is built from the same shell:
|
||||
|
||||
- A **header** with the agent's icon, name, role/title, status badge, and the action cluster (Assign Task, Run Heartbeat, Pause/Resume, overflow menu)
|
||||
- A **tab bar** with six tabs: Dashboard, Instructions, Skills, Configuration, Runs, Budget
|
||||
- The **selected tab's content** below
|
||||
|
||||
The action cluster in the header works on any tab:
|
||||
|
||||
- **Assign Task** — opens the new-task dialog with this agent pre-selected as assignee
|
||||
- **Run Heartbeat** — manually triggers a heartbeat right now (useful for testing instructions changes)
|
||||
- **Pause / Resume** — toggles the agent between `paused` and `active`
|
||||
- **Overflow menu** — Copy Agent ID, Reset Sessions, Terminate
|
||||
|
||||
> **Danger:** Terminate permanently shuts the agent down. Use Pause if you might want it back. See [Approvals — Board Override Powers](../day-to-day/approvals.md#board-override-powers) for the broader discussion of Pause vs Terminate vs Delete.
|
||||
|
||||
When the agent is in `pending_approval` status the Run Heartbeat button is disabled and a banner at the top of the page reminds you the agent cannot be invoked until the board approves the hire.
|
||||
|
||||
---
|
||||
|
||||
## Dashboard Tab
|
||||
|
||||
The Dashboard is the summary view — the one you open when you want to know "what has this agent been doing?"
|
||||
|
||||

|
||||
|
||||
### Latest run card
|
||||
|
||||
At the top, Paperclip shows either the currently running heartbeat (with a pulsing live indicator and a soft glow) or the most recent finished one. The card includes:
|
||||
|
||||
- The run status icon (check, cross, spinner, clock, timer, slash)
|
||||
- A short run ID
|
||||
- An invocation-source pill: Timer, Assignment, On-demand, or Automation — so you can see at a glance why the agent ran
|
||||
- A relative timestamp
|
||||
- A two-to-three-line excerpt from the run's result summary, rendered as markdown
|
||||
|
||||
Clicking the card jumps to the run detail, same as picking the run from the Runs tab.
|
||||
|
||||
### Activity charts
|
||||
|
||||
A row of four compact charts covering the last 14 days:
|
||||
|
||||
- **Run Activity** — runs per day
|
||||
- **Issues by Priority** — the priority mix of issues this agent has touched
|
||||
- **Issues by Status** — the status mix of the same set of issues
|
||||
- **Success Rate** — proportion of successful heartbeats
|
||||
|
||||
The charts share a common 14-day window; they're designed to answer "is this agent productive?" without needing to scroll through run logs.
|
||||
|
||||
### Recent issues
|
||||
|
||||
A list of up to 10 issues this agent has participated in, sorted by recency, with status badges. A **See All** link opens the full filtered issues page. Use this to pick up context before running a manual heartbeat or reassigning work.
|
||||
|
||||
### Costs
|
||||
|
||||
At the bottom, a summary of the agent's total input tokens, output tokens, cached tokens, and total cost, followed by a per-run cost table for the most recent 10 runs. For a deeper view of spending — and the rules for how the numbers are computed — see [Costs & budgets](../day-to-day/costs.md).
|
||||
|
||||
---
|
||||
|
||||
## Instructions Tab
|
||||
|
||||
The Instructions tab is where you edit what the agent *is* — its system prompt, role description, and any additional instruction files it should read.
|
||||
|
||||

|
||||
|
||||
### Managed vs external bundles
|
||||
|
||||
Local adapters (Claude Code, Codex, Cursor, Gemini, OpenCode) support an **instructions bundle**: a folder of markdown files that live alongside the agent's working directory. Paperclip can manage that folder for you (it owns the filesystem layout) or you can point it at an existing folder on disk. The two modes are:
|
||||
|
||||
- **Managed** — Paperclip stores the files in its own location and you edit them through the UI
|
||||
- **External** — you give Paperclip a `rootPath` on disk and it reads/writes the files there; useful when the instructions already live in a repo you want to keep canonical
|
||||
|
||||
The mode toggle and root path field sit at the top of the tab. Changing them is a normal edit — the floating Save/Cancel bar appears as soon as the form is dirty.
|
||||
|
||||
### The entry file
|
||||
|
||||
Every bundle has an **entry file**, usually `AGENTS.md`. That's the file the adapter feeds to the agent on every heartbeat. Other files in the bundle are available but not automatically loaded — the entry file can link to them, reference them, or include them.
|
||||
|
||||
### Editing files
|
||||
|
||||
The left pane shows the file tree. Click a file to open it in the markdown editor on the right. Create a new file using the "New file" control — useful for splitting out long-form references (playbooks, example outputs, policy notes) from the short entry file. Delete a file with the trash icon. Rename is via delete + create for now.
|
||||
|
||||
The markdown editor supports inline image uploads (drag-and-drop or paste) — images are uploaded to the company asset store and inserted as markdown image links.
|
||||
|
||||
### Adapters without bundles
|
||||
|
||||
For adapters that don't support a filesystem bundle (OpenClaw gateway and some remote providers), the instructions surface collapses to a simpler editor. The rules are the same: save commits the change, the agent picks it up on its next heartbeat.
|
||||
|
||||
### When changes take effect
|
||||
|
||||
Edits are saved on **Save** (via the floating Save/Cancel bar that appears on any dirty state). The agent uses the new instructions on its next heartbeat — existing runs in flight are not interrupted. If you want to see the change immediately, click **Run Heartbeat** in the header after saving.
|
||||
|
||||
---
|
||||
|
||||
## Skills Tab
|
||||
|
||||
The Skills tab controls which company skills are attached to this specific agent. It is a per-agent view over the shared [company skill library](./skills.md) — the library is where skills are authored and imported, this tab is where you decide which agents get which skills.
|
||||
|
||||

|
||||
|
||||
### Sections on the tab
|
||||
|
||||
The tab splits skills into three sections:
|
||||
|
||||
1. **Optional company skills** — every skill in the company library that this agent *could* use. Tick or untick the checkbox on each row to enable or disable it for the agent.
|
||||
2. **Required by Paperclip** — skills the runtime itself insists on (for example, the built-in Paperclip API skill that teaches the agent how to check out tasks and post comments). These show with a locked checkbox and a tooltip explaining why they can't be turned off.
|
||||
3. **User-installed skills, not managed by Paperclip** — skills that already exist on disk in the agent's workspace but weren't installed through Paperclip. They're displayed for visibility but can't be toggled from here. Edit them at their source (or import them into the library). This section is collapsed by default.
|
||||
|
||||
### Each skill row
|
||||
|
||||
Every row shows the skill's name, its description (the routing logic the agent uses to decide whether to load it), and, for non-managed skills, an origin and location hint so you can find the file on disk. A **View** link jumps to the skill's detail page in the company library where you can edit the source.
|
||||
|
||||
### Autosave
|
||||
|
||||
Changes save automatically about 250 ms after you stop clicking — you'll see "Saving soon…" and then the indicator clears. No Save button to press.
|
||||
|
||||
### Where skills run
|
||||
|
||||
The footer shows a small status block:
|
||||
|
||||
- **Adapter** — which runtime this agent uses
|
||||
- **Skills applied** — "Kept in the workspace" (persistent), "Applied when the agent runs" (ephemeral), or "Tracked only" (the adapter doesn't support skill management from Paperclip)
|
||||
- **Selected skills** — the count
|
||||
|
||||
If the skill mode is `unsupported` (e.g. `openclaw_gateway`), the checkboxes are disabled with a tooltip directing you to manage skills in the adapter directly.
|
||||
|
||||
### Warnings
|
||||
|
||||
If you toggled a skill key that no longer exists in the company library, Paperclip shows an amber "Requested skills missing from the company library" warning. Either re-import the skill, or untick the missing key.
|
||||
|
||||
For a deeper discussion of what skills are, how to write good ones, and how they keep the agent's context small, see [Skills](./skills.md).
|
||||
|
||||
---
|
||||
|
||||
## Configuration Tab
|
||||
|
||||
The Configuration tab is where you change the agent's **runtime settings** — which adapter it uses, which model it runs, what heartbeat interval it has, what environment variables it needs, and a few adapter-specific knobs. It is *not* where you change what the agent *does* (that's Instructions) or what it knows (that's Skills). It's where you change how it's wired.
|
||||
|
||||

|
||||
|
||||
### The shared configuration form
|
||||
|
||||
The same `AgentConfigForm` you met in the hiring flow drives this tab, with two differences: the Instructions and prompt-template fields are hidden here (they live on their own tab) and the adapter-specific options are fully editable.
|
||||
|
||||
Common fields:
|
||||
|
||||
- **Adapter** — dropdown of every adapter enabled for your instance. Switching adapters is a structural change and typically resets model/options to safe defaults for the new adapter. Pick deliberately.
|
||||
- **Model** — the list Paperclip fetched from the adapter. Some adapters (OpenCode, Gemini local) require a specific format; the form will block submission with an inline error if the model can't be validated.
|
||||
- **Working directory (cwd)** — the filesystem path the adapter runs in. Relative instruction paths resolve from here.
|
||||
- **Heartbeat interval** — the minimum number of seconds between automatic heartbeats. This is a floor, not a guarantee; a busy agent with many assignments may run more often if events (mentions, approvals, assignments) trigger wakes.
|
||||
- **Heartbeat enabled** — toggle on/off. A disabled agent only runs on explicit event triggers or when you click **Run Heartbeat** manually.
|
||||
|
||||
Adapter-specific fields (Claude login, Codex sandbox bypass, Cursor options, etc.) appear as extra rows underneath. Adapter-specific fields only modify this agent — changing them has no effect on any other agent.
|
||||
|
||||
### API Keys section
|
||||
|
||||
Below the form, still on the Configuration tab, is a dedicated **API Keys** block. This is the same UI described under the Keys tab below — it's embedded here because issuing a key is a configuration-time action. See [Keys](#keys) for the details.
|
||||
|
||||
### Configuration revisions
|
||||
|
||||
At the bottom of the tab is a collapsible **Configuration Revisions** section. Every time you save a change to this agent's configuration, Paperclip records a revision with a timestamp, the keys that changed, and a diff. Up to the most recent 10 are shown when expanded.
|
||||
|
||||
Each revision row includes a **Rollback** action: clicking it restores the agent to that exact configuration, atomically. Rollback is itself saved as a new revision, so you can always undo an undo.
|
||||
|
||||
> **Tip:** Use rollback when a change breaks an agent and you don't have time to debug. Pause the agent, roll back to the last known-good revision, resume, and investigate the bad change later.
|
||||
|
||||
### Save flow
|
||||
|
||||
Edits are dirty-tracked. A floating Save/Cancel bar (desktop) or a fixed bottom bar (mobile) appears as soon as any field differs from the persisted state. Save is blocking — you'll see "Saving…" on the button. Cancel reverts every dirty field back to its saved value in one shot.
|
||||
|
||||
---
|
||||
|
||||
## Runs Tab
|
||||
|
||||
The Runs tab is your audit trail. Every heartbeat the agent has ever done is listed here, newest first, with filters, a detail view, and live streaming for runs in progress.
|
||||
|
||||

|
||||
|
||||
### The run list
|
||||
|
||||
Each entry in the list shows:
|
||||
|
||||
- A status icon (succeeded, failed, running, queued, timed out, cancelled)
|
||||
- A short run ID
|
||||
- The run's invocation source pill (Timer / Assignment / On-demand / Automation)
|
||||
- Input + output tokens and cost (when cost is non-zero)
|
||||
- A relative timestamp
|
||||
- A short excerpt from the result summary
|
||||
|
||||
On desktop, the list sits in a narrow left column and clicking a run opens the detail to the right — you can step through runs quickly without navigating away. On mobile the list takes the whole screen and tapping a run pushes into a detail view with a Back button.
|
||||
|
||||
Paperclip auto-selects the latest run on desktop so you always have something in the detail pane when you land on the tab.
|
||||
|
||||
### The run detail
|
||||
|
||||
The detail view has several stacked blocks:
|
||||
|
||||
- **Status & timing** — status badge, start time, end/elapsed time, duration, a Cancel button (live runs only)
|
||||
- **Retry and resume controls** — failed or timed-out runs get a **Retry** action that re-wakes the agent with the same task context; runs that died because the process was lost (`errorCode = process_lost`) get a **Resume** action that picks up from the same point
|
||||
- **Metrics** — input / output / cached tokens, total cost, provider, model
|
||||
- **Session continuity** — the before/after session IDs the adapter used; highlighted when the session changes mid-run
|
||||
- **Invocation card** — the exact command, working directory, prompt, context, and environment the adapter used, with secrets redacted (`***REDACTED***`) and JWT values masked
|
||||
- **Transcript** — the full conversation between the agent and the model, rendered with tool calls inline
|
||||
- **Log viewer** — raw stdout/stderr/system chunks with timestamps, useful when the transcript isn't enough
|
||||
- **Touched issues** — the set of issues the agent interacted with during this run, with links and a **Clear sessions for touched issues** action (handy after a bad run corrupted session state)
|
||||
- **Claude login** (Claude adapters) — a one-click "Login with Claude" action that bootstraps the adapter against a fresh Claude auth
|
||||
|
||||
### Live runs
|
||||
|
||||
When a run is in-flight, the transcript and log viewer stream in real time. The header shows an animated status, an elapsed-time counter, and a cancel button. The Scroll-to-bottom helper sticks you to the latest output until you scroll up to read history, at which point it lets go so you don't get yanked around.
|
||||
|
||||
### Cancelling, retrying, resuming
|
||||
|
||||
- **Cancel** — interrupts a running or queued run. The run moves to `cancelled` status.
|
||||
- **Retry** — creates a new run with the same task context. Use when a run failed for a recoverable reason (transient network error, adapter glitch).
|
||||
- **Resume** — only appears when a run failed because the adapter process was lost. Paperclip re-wakes the agent and passes a `resumeFromRunId` so the next run can pick up instead of restarting.
|
||||
|
||||
### Filters and sorting
|
||||
|
||||
The list is sorted by creation time descending. There are no additional filters on the Runs tab itself — use the Dashboard's charts for aggregate views, or jump to individual issues to see per-task run history.
|
||||
|
||||
---
|
||||
|
||||
## Budget Tab
|
||||
|
||||
Every agent can have its own budget. The Budget tab is where you set it, watch the current spend, and configure how Paperclip should react when limits are approached or breached.
|
||||
|
||||

|
||||
|
||||
### Anatomy of an agent budget
|
||||
|
||||
A budget has four pieces:
|
||||
|
||||
- **Window kind** — either **Monthly UTC** (spending resets on the first of each month, UTC) or **Lifetime** (never resets; the agent gets this much total, ever)
|
||||
- **Amount** — the spending cap in cents (displayed as dollars)
|
||||
- **Warn percent** — the soft-alert threshold, e.g. 80%. When spend crosses this, the agent moves to `warning` status and the header shows an amber indicator.
|
||||
- **Hard stop** — the enforcement behaviour when spend reaches 100%. By default, Paperclip pauses the agent (`pauseOnExceed = true`) so it consumes no further budget until a board user resumes it or raises the limit.
|
||||
|
||||
### The policy card
|
||||
|
||||
The card on this tab shows:
|
||||
|
||||
- An icon and colour corresponding to the current state (Healthy wallet, Warning triangle, Hard-stop shield)
|
||||
- The window label (Monthly UTC budget / Lifetime budget)
|
||||
- Current spend vs. cap, as a number and as a progress bar
|
||||
- The soft-alert threshold
|
||||
- Pause status and pause reason, if paused
|
||||
|
||||
### Editing the budget
|
||||
|
||||
Use the edit control on the card to change the amount. Depending on your permissions and the agent's reporting chain, some fields may be read-only — in particular, subordinate agents can propose budget increases but not approve them. When a policy's hard stop is hit, a **Budget Override** approval may be created for you to approve a one-time increase or a permanent raise.
|
||||
|
||||
### What happens at each threshold
|
||||
|
||||
- **Below the warn threshold** — normal. The agent runs as scheduled.
|
||||
- **Between warn and hard stop** — `warning` status. The agent keeps running. Consider triaging: is the agent doing what you want, or is it burning budget on low-value work?
|
||||
- **At or above the hard stop** — `paused` status with `pauseReason = "budget"`. The agent stops taking heartbeats until you resume, raise the cap, or the window resets (monthly only).
|
||||
|
||||
### Agent-level vs company-level
|
||||
|
||||
This tab configures **this agent's** budget. Paperclip also tracks a company-level budget that sums across agents. See [Costs & budgets](../day-to-day/costs.md) for how the two interact, how API costs are computed, and how to tune budgets as a portfolio rather than one agent at a time.
|
||||
|
||||
---
|
||||
|
||||
## Keys
|
||||
|
||||
An agent authenticates to the Paperclip API with a short-lived JWT for in-process runs. When the agent runs *outside* a managed heartbeat — for example a local CLI operator checking assignments, a CI job triggering a webhook, a remote adapter that needs a standing token — it uses a long-lived **API key** instead.
|
||||
|
||||
The Keys panel sits on the Configuration tab (under the configuration form, above the revisions collapsible). It looks like this:
|
||||
|
||||

|
||||
|
||||
### Issuing a key
|
||||
|
||||
1. Type a name for the key (e.g. `production`, `ci`, `local-cli`). A clear name makes audits easier later.
|
||||
2. Click **Create**.
|
||||
3. Paperclip shows the full token in a yellow banner. **Copy it now** — the plaintext token is only shown this once. After you dismiss the banner, Paperclip only stores a hash and can never show it again.
|
||||
|
||||
The banner has a show/hide eye toggle so you can reveal the token to paste it, and a copy-to-clipboard button. When you're done, click **Dismiss**.
|
||||
|
||||
### Active keys
|
||||
|
||||
Every un-revoked key is listed below the create form, showing its name and creation timestamp. You can see how many keys exist and when each was made — useful for rotation reviews.
|
||||
|
||||
### Revoking a key
|
||||
|
||||
Click **Revoke** next to any active key. The key moves to the **Revoked Keys** section at the bottom of the panel (displayed with a strikethrough and dimmed), and is immediately rejected by the Paperclip API. Revocation is permanent — revoked keys are never reused. To rotate a key, create a new one, swap it in wherever the old one is used, then revoke the old one.
|
||||
|
||||
### Where keys flow
|
||||
|
||||
An agent's API key is typically set as `PAPERCLIP_API_KEY` in the adapter's environment or in the operator's shell when running the agent locally. All Paperclip API requests use `Authorization: Bearer $PAPERCLIP_API_KEY`. For in-heartbeat calls by local adapters, the key is a short-lived run JWT auto-injected by Paperclip — you don't need a long-lived key at all. For remote adapters and manual CLI use, you do.
|
||||
|
||||
> **Tip:** Revoke keys aggressively. It's cheaper to issue a new one than to wonder whether a forgotten key is floating around.
|
||||
|
||||
---
|
||||
|
||||
## How Agents Are Invoked — The Heartbeat Model
|
||||
|
||||
This section collects the conceptual material you need to reason about what an agent *is* at runtime. If you've read other Paperclip guides you may have seen pieces of this; everything is gathered here so the agent surface above makes sense.
|
||||
|
||||
### Execution model
|
||||
|
||||
Every heartbeat follows the same six-step arc:
|
||||
|
||||
1. **Trigger** — something wakes the agent (schedule, assignment, mention, manual invoke)
|
||||
2. **Adapter invocation** — Paperclip calls the agent's configured adapter
|
||||
3. **Agent process** — the adapter spawns the agent runtime (e.g. Claude Code CLI)
|
||||
4. **Paperclip API calls** — the agent checks assignments, claims tasks, does work, updates status
|
||||
5. **Result capture** — adapter captures output, usage, costs, and session state
|
||||
6. **Run record** — Paperclip stores the run result for audit and debugging
|
||||
|
||||
Each of those steps corresponds to something visible in the Runs tab: the trigger shows up as the invocation source pill, the adapter invocation is the Invocation card, the agent process is the transcript and logs, the API calls appear in the touched-issues list, the result capture populates the metrics block, and the run record is the run itself.
|
||||
|
||||
### Agent identity
|
||||
|
||||
Every agent has environment variables injected at runtime:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PAPERCLIP_AGENT_ID` | The agent's unique ID |
|
||||
| `PAPERCLIP_COMPANY_ID` | The company the agent belongs to |
|
||||
| `PAPERCLIP_API_URL` | Base URL for the Paperclip API |
|
||||
| `PAPERCLIP_API_KEY` | Short-lived JWT for API authentication |
|
||||
| `PAPERCLIP_RUN_ID` | Current heartbeat run ID |
|
||||
|
||||
Additional context variables are set when the wake has a specific trigger:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `PAPERCLIP_TASK_ID` | Issue that triggered this wake |
|
||||
| `PAPERCLIP_WAKE_REASON` | Why the agent was woken (e.g. `issue_assigned`, `issue_comment_mentioned`) |
|
||||
| `PAPERCLIP_WAKE_COMMENT_ID` | Specific comment that triggered this wake |
|
||||
| `PAPERCLIP_APPROVAL_ID` | Approval that was resolved |
|
||||
| `PAPERCLIP_APPROVAL_STATUS` | Approval decision (`approved`, `rejected`) |
|
||||
|
||||
These are exactly what you'll see in the Invocation card on any run — Paperclip redacts secrets (anything that looks like an API key, a bearer token, a password, or a JWT) before displaying them, but the structure is the same as what the agent actually received.
|
||||
|
||||
### Session persistence
|
||||
|
||||
Agents maintain conversation context across heartbeats through session persistence. The adapter serializes session state (e.g. Claude Code session ID) after each run and restores it on the next wake. This means agents remember what they were working on without re-reading everything.
|
||||
|
||||
On the Runs tab, each run shows a **session ID before** and **session ID after**. A differing pair means the session rotated during the run; an equal pair means the agent continued the same conversation. The overflow menu's **Reset Sessions** action wipes this state when you want the agent to start fresh on its next heartbeat.
|
||||
|
||||
### Agent status
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| `active` | Ready to receive heartbeats |
|
||||
| `idle` | Active but no heartbeat currently running |
|
||||
| `running` | Heartbeat in progress |
|
||||
| `error` | Last heartbeat failed |
|
||||
| `paused` | Manually paused or budget-exceeded |
|
||||
| `terminated` | Permanently deactivated |
|
||||
|
||||
The status dot on every row and badge on the agent header reflect one of these values directly. The **Active** filter on the agent list expands to include `active`, `running`, and `idle`, because all three mean "this agent is healthy and doing (or ready to do) work". **Paused** and **Error** are their own filters for triage.
|
||||
|
||||
### Why this matters day-to-day
|
||||
|
||||
The heartbeat model has a few practical consequences worth keeping in mind:
|
||||
|
||||
- **An agent that isn't doing work isn't costing you anything** (past whatever the last run spent). Between heartbeats there's no idle burn.
|
||||
- **Changing configuration takes effect on the next heartbeat, not the current one.** If you edit Instructions while a heartbeat is running, that run uses the old instructions. The next run uses the new ones.
|
||||
- **Manual Run Heartbeat is your friend when testing changes.** Save your edit, click Run Heartbeat, watch the live run stream in the Runs tab. Much faster than waiting for the timer.
|
||||
- **Paused agents are cheap.** If you're not sure whether to keep an agent, pause it. It will stop accumulating spend and its history is preserved for when you come back.
|
||||
|
||||
---
|
||||
|
||||
## Common Workflows
|
||||
|
||||
A few end-to-end recipes for things you'll do often.
|
||||
|
||||
### Try a new instruction quickly
|
||||
|
||||
1. Open the agent and go to the **Instructions** tab.
|
||||
2. Edit the entry file (usually `AGENTS.md`). Save.
|
||||
3. Click **Run Heartbeat** in the header.
|
||||
4. Switch to the **Runs** tab. The new run is selected automatically on desktop; on mobile tap it from the list.
|
||||
5. Watch the transcript stream. If the agent behaves as you want, you're done. If not, repeat.
|
||||
|
||||
This loop is tight on purpose — changes apply on the next heartbeat, and manual invoke gives you that heartbeat on demand.
|
||||
|
||||
### Diagnose a misbehaving agent
|
||||
|
||||
1. Open the **Runs** tab, find the most recent failed run.
|
||||
2. Read the transcript for where the agent got confused.
|
||||
3. Open the **Invocation** card to confirm it received the right environment, working directory, and instruction path.
|
||||
4. Check **Touched issues** — did it actually work on what it was supposed to?
|
||||
5. If the session is bad, use **Clear sessions for touched issues** or the overflow menu's **Reset Sessions**.
|
||||
6. If the configuration is at fault, roll back on the **Configuration** tab.
|
||||
|
||||
### Temporarily mute an agent
|
||||
|
||||
1. Click **Pause** in the header. Status moves to `paused` and no further heartbeats will run.
|
||||
2. Investigate at your leisure. The agent's history is preserved.
|
||||
3. Click **Resume** when ready.
|
||||
|
||||
Use pause liberally — it's reversible, cheap, and the right default when something is clearly off but you don't have time to debug.
|
||||
|
||||
### Rotate an API key
|
||||
|
||||
1. Open the **Configuration** tab, scroll to **API Keys**.
|
||||
2. Click **Create**, name the new key (e.g. `production-2026-04`), copy the token shown in the yellow banner.
|
||||
3. Swap the new token into whatever system was using the old one.
|
||||
4. Back on the Keys panel, click **Revoke** next to the old key.
|
||||
5. Confirm the revoked key moves to the bottom **Revoked Keys** section.
|
||||
|
||||
### Raise a capped agent's budget
|
||||
|
||||
1. Open the **Budget** tab. The policy card shows current status, spend, and pause reason.
|
||||
2. Edit the amount on the policy card.
|
||||
3. If the change is within your authority, it saves immediately. If the agent is subordinate and an override is required, a Budget Override approval is created — see [Approvals](../day-to-day/approvals.md).
|
||||
4. Once the cap is raised and the agent is off hard-stop, click **Resume** in the header if it was paused by budget.
|
||||
|
||||
---
|
||||
|
||||
## Related guides
|
||||
|
||||
- [Skills](./skills.md) — how the company skill library works, how skills are written, and how they keep agent context lean
|
||||
- [Agent adapters](./agent-adapters.md) — which adapters are available, what each one is good at, and how to configure them
|
||||
- [Approvals](../day-to-day/approvals.md) — the governance layer around hire requests, strategy, and budget overrides
|
||||
- [Costs & budgets](../day-to-day/costs.md) — how API spend is computed, how budgets are enforced, and how to tune them across your whole company
|
||||
@@ -58,7 +58,7 @@ If the plan looks right, approve it. If not, click **Request Revision** and desc
|
||||
|
||||
> **Tip:** Don't approve a strategy that feels unclear or off-track just to get things moving. A vague approval leads to vague work. It's worth a round of revisions.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -162,4 +162,4 @@ After you approve a strategy, Paperclip queues the CEO to wake automatically so
|
||||
|
||||
You understand how delegation works and what to check when it doesn't. The next guide covers agent adapters — the configuration that determines which AI system powers each agent.
|
||||
|
||||
[Agent Adapters →](agent-adapters.md)
|
||||
[Agent Adapters →](./agent-adapters.md)
|
||||
@@ -74,23 +74,23 @@ Manager-level agents (CTO, CMO, Head of Design, etc.) report directly to the CEO
|
||||
|
||||
From your company dashboard, click **Agents** in the left sidebar, then click the **New Agent** button in the top right.
|
||||
|
||||

|
||||

|
||||
|
||||
2. **Set the name, title, and role**
|
||||
|
||||
Give the agent a clear name (e.g. "Sam"), a title (e.g. "CTO"), and choose the closest role. The role helps the CEO understand what this agent is responsible for when assigning work.
|
||||
|
||||

|
||||

|
||||
|
||||
3. **Set "Reports to" to the CEO**
|
||||
|
||||
In the **Reports to** dropdown, select your CEO agent. This places the new agent one level below the CEO in the hierarchy.
|
||||
|
||||

|
||||

|
||||
|
||||
4. **Choose an adapter and save**
|
||||
|
||||
Configure the adapter (see [Agent Adapters](agent-adapters.md) if you need help with this step). Click **Create agent**.
|
||||
Configure the adapter (see [Agent Adapters](./agent-adapters.md) if you need help with this step). Click **Create agent**.
|
||||
|
||||
The new agent now appears in the org chart under the CEO, and the CEO will start considering them when delegating work. You can set the agent's budget afterward on its **Budget** tab.
|
||||
|
||||
@@ -123,7 +123,68 @@ From the agent's detail page, click **Configuration**, then change the **Reports
|
||||
|
||||
Click **Org** in the left sidebar. You'll see a visual tree of all your agents with their current status shown inline.
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
## The org chart view
|
||||
|
||||
The Org page renders your company as an **interactive tree**. Each agent is shown as a card at the position its reporting line dictates — the CEO sits at the top, managers fan out below it, and individual contributors sit under their managers. Lines between cards are the reporting relationships; they always point from manager down to report.
|
||||
|
||||
The card itself surfaces everything you usually need at a glance:
|
||||
|
||||
- Agent **icon** with a small coloured **status dot** (green for active, yellow for paused or idle, red for error, cyan for running, grey for terminated).
|
||||
- The agent's **name** and **title** (or a humanised role label when no custom title is set).
|
||||
- The **adapter type** in a small mono font — useful when auditing which adapters your org relies on.
|
||||
- A **capabilities** snippet: the first couple of lines of the agent's capabilities description.
|
||||
|
||||
Two companion buttons live above the chart: **Import company** and **Export company**, which take you to the import/export flows when you want to share or clone an org.
|
||||
|
||||

|
||||
|
||||
### Navigating the chart
|
||||
|
||||
The chart area supports full pan and zoom:
|
||||
|
||||
- **Drag** anywhere on the background to pan — the cursor switches to a grab hand to make this obvious.
|
||||
- **Scroll / pinch** to zoom in and out. Zoom anchors to the mouse position, so you can lean into a specific team without losing your place.
|
||||
- Use the **+**, **−**, and **Fit** buttons in the top-right corner for precise zoom control. **Fit** recentres the whole chart inside the viewport, which is handy after a reorg when the tree has grown off-screen.
|
||||
- **Click a card** to open the full agent detail page. Dragging on a card does not pan — clicks are always routed to the agent.
|
||||
|
||||
For the text-only reporting view, the sidebar **Org** entry also exposes a collapsible tree (used by screen readers and by anyone who prefers a compact outline). The two views share the same underlying hierarchy; changes made in one appear in the other immediately.
|
||||
|
||||
---
|
||||
|
||||
## Reassigning an agent via the chart
|
||||
|
||||
You can reassign any agent to a different manager without leaving the chart. The flow is:
|
||||
|
||||
1. **Click the card of the agent you want to move.** The chart opens the agent's detail page.
|
||||
2. **Switch to the Configuration tab** and change the **Reports to** field to the new manager.
|
||||
3. **Save.** The reporting line is updated immediately, and when you return to the Org page, the card re-parents under the new manager. Existing task assignments are unaffected — only future delegation follows the new line.
|
||||
|
||||

|
||||
|
||||
A few guardrails apply:
|
||||
|
||||
- You cannot assign an agent to a manager that reports to it (no cycles).
|
||||
- You cannot remove the CEO's own reporting line — the CEO always reports to the board.
|
||||
- Reassigning a manager with reports moves the whole subtree under the new parent; the reports themselves keep their own reporting lines.
|
||||
|
||||
> **Tip:** If you are doing a bigger reorg (swapping teams between two managers, for example), open the Org page in one tab and the agent configuration in another. The chart refreshes automatically when you save, so you can use it as a live preview of the new structure.
|
||||
|
||||
---
|
||||
|
||||
## Reporting lines visualization
|
||||
|
||||
The lines between cards are not just decorative — they encode the company's delegation and escalation paths:
|
||||
|
||||
- Every line points from a **manager** (top) to a **direct report** (bottom). The direction of the line mirrors how work and escalations travel: delegation flows down, status and blockers flow up.
|
||||
- **Multiple children** share a horizontal connector below the manager card so you can see the span of control at each level.
|
||||
- **Separate subtrees** mean separate reporting lines. If you ever end up with more than one root, it usually means an agent lost its manager (for example, after a termination) — fix it from the agent's configuration by setting **Reports to** again.
|
||||
- **Terminated agents** appear with a grey dot and faded styling. They stay on the chart as a historical record until you remove them.
|
||||
|
||||
Because the chart is the single source of truth for reporting structure, it is the fastest way to spot structural issues: a manager with far too many reports, a worker orphaned without a manager, or an adapter concentration risk where too much of the org runs on the same adapter.
|
||||
|
||||
---
|
||||
|
||||
@@ -146,4 +207,4 @@ Your job is to approve strategy and hire requests, monitor the dashboard, and st
|
||||
|
||||
You now know how to build and manage your org structure. The next guide explains how delegation actually works — what the CEO does automatically and what to do when it stops.
|
||||
|
||||
[Delegation →](delegation.md)
|
||||
[Delegation →](./delegation.md)
|
||||
@@ -0,0 +1,187 @@
|
||||
# Skills
|
||||
|
||||
When you want an agent to follow a specific procedure — a code review checklist, a deployment runbook, a customer response template — you could put those instructions in the agent's main configuration. But if you want multiple agents to use the same procedure, or want to keep agent configurations lean and focused, that's where skills come in.
|
||||
|
||||
A skill is a reusable instruction document that agents can load on demand. Instead of baking every procedure into every agent's configuration, you write a skill once in the company skill library and make it available to any agent that needs it. The agent reads the skill's description when it starts a task, decides whether the skill is relevant, and loads the full instructions only when it needs them.
|
||||
|
||||
---
|
||||
|
||||
## What a skill looks like
|
||||
|
||||
A skill is a folder containing a `SKILL.md` file. The file has a short frontmatter block (the routing description) and a body (the actual instructions).
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md
|
||||
└── references/
|
||||
└── examples.md
|
||||
```
|
||||
|
||||
The frontmatter description is what the agent reads first — it's written as decision logic so the agent knows when to use this skill:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: >
|
||||
Use when asked to review a pull request or code diff.
|
||||
Don't use when writing new code from scratch.
|
||||
---
|
||||
|
||||
# Code Review
|
||||
|
||||
When reviewing code, check the following...
|
||||
```
|
||||
|
||||
The agent sees the name and description for every available skill at the start of its heartbeat. If the description matches what the agent is doing, it loads the full skill content and follows the instructions.
|
||||
|
||||
---
|
||||
|
||||
## Why this matters for performance and cost
|
||||
|
||||
Skills keep the base context small. An agent with 10 skills doesn't load all 10 into its context on every heartbeat — it only loads the ones that are relevant to the current task. This means:
|
||||
|
||||
- Fewer tokens consumed per run (lower cost)
|
||||
- Cleaner, more focused context for the agent (better results)
|
||||
- Easier to maintain — update a skill in one place, all agents benefit
|
||||
|
||||
---
|
||||
|
||||
## Adding a skill
|
||||
|
||||
Skills are managed from the company-level **Skills** page. The page has a left-hand list of every installed skill and a right-hand pane that shows the selected skill's files, description, and usage. Two controls live above the skill list: a filter box to search by name, key, slug, or source, and an input where you can paste a source string to import a skill from elsewhere.
|
||||
|
||||
You can add a skill in one of four ways, depending on where it is coming from.
|
||||
|
||||
### 1. Import from a GitHub repository
|
||||
|
||||
Paste a GitHub URL (or a `skills.sh` install command) into the source field at the top of the Skills page and press **Add**. Paperclip clones the skill folder, detects the `SKILL.md`, and registers it in the library. GitHub-sourced skills are pinned to a specific commit — you can check for updates and upgrade the pin from the skill detail pane.
|
||||
|
||||

|
||||
|
||||
### 2. Import from a gist
|
||||
|
||||
Gists work through the same source field. Paste the gist URL and Paperclip treats it like a remote file source — useful when you want to share a one-off procedure without setting up a full repository.
|
||||
|
||||
### 3. Import from a local file or folder
|
||||
|
||||
You can also paste a local path (Linux, WSL, or Windows) into the source field. Paperclip walks the folder, finds any `SKILL.md` files, and imports them. This is the right choice when you already keep skills in a project workspace on disk — the scan button (circular arrow next to the plus icon) re-runs the local import across every project workspace in the company so newly added `SKILL.md` folders show up automatically.
|
||||
|
||||
### 4. Import from the skills.sh marketplace
|
||||
|
||||
[skills.sh](https://skills.sh) is a community catalog of ready-made skills. Each listing provides a copy-ready install command; paste it into the source field and Paperclip imports the skill along with its source metadata (you will see a small skills.sh badge on the skill row).
|
||||
|
||||
### Creating a skill from scratch
|
||||
|
||||
If none of those sources apply, click the **+** button next to the scan icon. A small form appears inline with three fields:
|
||||
|
||||
- **Skill name** — the human-readable name shown in the list.
|
||||
- **Optional shortname / slug** — the `kebab-case` key the agent uses to reference this skill. If you leave this blank, Paperclip derives it from the name.
|
||||
- **Short description** — the routing logic ("Use when… Don't use when…") the agent reads before loading the skill body.
|
||||
|
||||
Click **Create skill** and the skill is added to the library as a Paperclip-managed, editable entry.
|
||||
|
||||
The skill is now available in the company library. When creating or editing an agent, you can attach optional skills from that library.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## File inventory
|
||||
|
||||
Every skill is a folder, not a single file. Expanding a skill in the sidebar reveals its **file inventory** — the full tree of files underneath the skill's root. The inventory pinpoints each file with a few pieces of metadata that matter in practice:
|
||||
|
||||
- **Entry file** — the top-level `SKILL.md` that agents read first. It is always sorted to the top of the tree and contains the skill's frontmatter (name, description) plus the instructions body.
|
||||
- **Other files** — subfolders such as `references/` or `scripts/` hold supporting material (examples, style guides, helper scripts). The file kind icon tells you at a glance whether each leaf is markdown, a reference document, or a script.
|
||||
- **Editable** — a skill is editable only when Paperclip controls its source. Skills imported from GitHub, gists, or the skills.sh catalog are **read-only**: you can view any file but cannot save changes; the detail pane shows an explanation badge instead of an Edit button. Paperclip-managed and local-folder skills open in the Markdown editor when you click **Edit**.
|
||||
- **Deprecated** — when a skill is retired, it is marked deprecated in the library and hidden from agent skill menus. Existing agents keep working until you detach the skill from them.
|
||||
- **Virtual** — some entries in the inventory are virtual: they represent content the adapter synthesises at runtime (for example, materialised runtime skills for adapters that do not support skills natively). Virtual entries are tagged in the row and cannot be edited directly.
|
||||
|
||||

|
||||
|
||||
The header above the file viewer shows:
|
||||
|
||||
- The **Source** badge (skills.sh, GitHub, local folder, or Paperclip) with the source label — clicking the label copies the path to the workspace when applicable.
|
||||
- The **Pin** for GitHub-sourced skills — the short commit SHA Paperclip is locked to, plus a **Check for updates** button and, when an update exists, an **Install update** action.
|
||||
- The skill **Key** (the stable identifier adapters use).
|
||||
- The **Mode** — Editable or Read only.
|
||||
- **Used by** — the list of agents currently attached to this skill.
|
||||
|
||||
---
|
||||
|
||||
## Adapter sync preferences
|
||||
|
||||
Different adapters handle skills differently. The Agent → Skills tab surfaces this as a **skill application mode** just above the skill list:
|
||||
|
||||
- **Kept in the workspace** (`persistent`) — the adapter writes skill files into the agent's workspace directory and leaves them there between runs. Most long-running local adapters use this mode.
|
||||
- **Applied when the agent runs** (`ephemeral`) — the adapter materialises skill files for each run, then cleans up afterwards. This is the default for sandboxed adapters that treat the workspace as disposable.
|
||||
- **Tracked only** (`unsupported`) — Paperclip cannot push skills into the adapter, so it only records which skills you have assigned. Adapters like `openclaw_gateway` fall into this category; you will see a banner directing you to manage skills inside the remote runtime (for example, OpenClaw) rather than from Paperclip.
|
||||
|
||||
Assignments still save regardless of mode — the mode only controls whether Paperclip physically syncs the skill files. Warnings surface in an amber banner when, for example, a desired skill is missing from the company library or the adapter rejected a sync.
|
||||
|
||||
---
|
||||
|
||||
## Assigning a skill to an agent
|
||||
|
||||
Skills live at the company level, but each agent decides which of those skills to use. To assign or remove skills for an agent:
|
||||
|
||||
1. **Open the agent's detail page** from the Agents list or the org chart.
|
||||
2. **Switch to the Skills tab.**
|
||||
3. You will see three groups:
|
||||
- **Required skills** — entries the adapter marks as mandatory. You cannot disable these.
|
||||
- **Optional skills** — every skill in the company library that the agent *could* use. Each row has a checkbox; tick it to assign the skill, untick to detach.
|
||||
- **Unmanaged skills** — read-only rows for skills the adapter picked up from somewhere Paperclip does not control (for example, a global skill bundle on the host). These are shown for visibility only.
|
||||
4. Changes autosave about 250 ms after you stop clicking — the small "Saving soon..." / "Saving changes..." indicator in the top-right confirms when Paperclip has persisted the update. The agent will pick up the new skill list on its next run.
|
||||
|
||||

|
||||
|
||||
A **View company skills library** link at the top of the tab jumps back to the company-wide Skills page, so you can open the underlying skill to tweak its instructions without leaving context.
|
||||
|
||||
> **Tip:** If a skill you expect is not in the optional list, it is not in the company library yet. Add it from the Skills page first, then come back.
|
||||
|
||||
---
|
||||
|
||||
## Trust level
|
||||
|
||||
Skills are loaded into agent runs as additional instructions, so where they come from matters. Paperclip uses the **Source** badge on every skill row to make that provenance obvious:
|
||||
|
||||
- **Paperclip-managed** — authored inside Paperclip. You have full control of the contents; these are the safest skills to give an agent.
|
||||
- **Local folder** — pulled from a project workspace on the same machine. Trust level matches whatever you trust the folder itself with.
|
||||
- **GitHub / skills.sh** — pinned to a specific commit or release. The pin means the skill body cannot change underneath you without an explicit **Install update** from the detail pane, so you get a second chance to review diffs before they hit your agents.
|
||||
- **Gist / URL** — point-in-time imports of external content. Treat these like any third-party code: review the body before you attach the skill to an agent.
|
||||
|
||||
Before attaching a skill to a high-trust role (your CEO, a finance agent, an operator with access to secrets), open the skill and read its `SKILL.md` end-to-end. The **Used by** list on the detail pane makes it easy to audit which agents are already exposed to a given skill.
|
||||
|
||||
---
|
||||
|
||||
## What makes a good skill
|
||||
|
||||
**Write the description as routing logic.** The description is not a marketing pitch — it's the signal the agent uses to decide whether to load the skill. Include "Use when" and "Don't use when" guidance.
|
||||
|
||||
**Be specific and actionable.** The agent should be able to follow the skill without guessing. Vague instructions ("review the code carefully") produce vague output. Concrete checklists and steps produce consistent results.
|
||||
|
||||
**Include examples where they help.** Code snippets, example outputs, before/after comparisons — concrete examples are more reliable than abstract descriptions.
|
||||
|
||||
**Keep each skill focused on one concern.** A skill that covers code review, deployment, and customer communication is three skills fighting for attention. One skill, one purpose.
|
||||
|
||||
**Put supporting detail in `references/`.** If a skill needs supporting documents — style guides, example files, reference material — put them in a `references/` subfolder rather than cramming everything into `SKILL.md`.
|
||||
|
||||
---
|
||||
|
||||
## Skills across agents
|
||||
|
||||
Skills live at the company level, not inside one specific agent. This means:
|
||||
|
||||
- Your CTO and Backend Engineer can share a `code-review` skill without duplicating it
|
||||
- Your CEO can use a `delegation-checklist` skill that your CMO also uses for their own task breakdown
|
||||
- Updating a skill improves all agents that use it at once
|
||||
|
||||
You can also scan and import skills from project workspaces when you already have skill files on disk. That's useful for teams migrating existing `SKILL.md` folders into the Paperclip library.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Skills give your agents reliable, reusable procedures that improve consistency and reduce cost. The next guide covers export and import — how to back up, share, and reuse company configurations.
|
||||
|
||||
[Export & Import →](../power/export-import.md)
|
||||
File renamed without changes.
@@ -182,4 +182,4 @@ After an import:
|
||||
|
||||
Export and import give you durable, shareable backups of everything you've built. The final guide covers terminal setup — for developers who want to run Paperclip outside the Desktop App.
|
||||
|
||||
[Terminal Setup →](terminal-setup.md)
|
||||
[Terminal Setup →](./terminal-setup.md)
|
||||
@@ -1,6 +1,6 @@
|
||||
# Terminal Setup
|
||||
|
||||
> **Note:** This guide is for developers comfortable with a terminal. If you're not a developer, use the [Desktop App installation guide](installation.md) instead — it covers the full setup without any of this.
|
||||
> **Note:** This guide is for developers comfortable with a terminal. If you're not a developer, use the [Desktop App installation guide](../getting-started/installation.md) instead — it covers the full setup without any of this.
|
||||
|
||||
Paperclip runs as a Node.js server. The onboarding command handles the full setup in one step.
|
||||
|
||||
@@ -78,10 +78,10 @@ npx paperclipai configure --section server
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
For persistent background runs, Docker deployment, external databases, and cloud hosting options, see the [deployment overview](../../deploy/overview.md).
|
||||
For persistent background runs, Docker deployment, external databases, and cloud hosting options, see the [deployment overview](../../reference/deploy/overview.md).
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Paperclip is running. You'll land in onboarding or at the start screen, where the next step is [Create Your First Company](your-first-company.md).
|
||||
Paperclip is running. You'll land in onboarding or at the start screen, where the next step is [Create Your First Company](../getting-started/your-first-company.md).
|
||||
@@ -0,0 +1,141 @@
|
||||
# Goals
|
||||
|
||||
A goal in Paperclip is an outcome statement — a description of something the company is trying to achieve. Goals sit above projects and issues in the hierarchy: they do not contain code, they do not get "worked on" directly, and they do not have an execution workspace. Instead, they act as anchors. Projects link up to goals, and the CEO's strategy typically justifies itself by pointing at which goals it is advancing.
|
||||
|
||||
If issues are the "what to do" and projects are the "where to do it", goals are the "why it matters". A company may have just a handful of goals — long-lived, stable, rarely changing — while underneath them the set of projects and issues churns continuously.
|
||||
|
||||
This guide covers:
|
||||
|
||||
- The goals list and how it is rendered as a tree.
|
||||
- The goal detail page and its tabs.
|
||||
- How projects get linked to goals.
|
||||
- The goal hierarchy: parent and child (sub-)goals.
|
||||
|
||||
---
|
||||
|
||||
## Goals List
|
||||
|
||||
The Goals page lists every goal defined for the selected company. Because goals can have parents and children, the list is rendered as a tree rather than a flat list.
|
||||
|
||||

|
||||
|
||||
### Tree view
|
||||
|
||||
The tree is driven by the `parentId` field on each goal:
|
||||
|
||||
- **Top-level goals** have no parent. They render at the root of the tree.
|
||||
- **Sub-goals** have a parent goal and render underneath it, indented.
|
||||
- Child goals are grouped under their parent and can themselves have children — nesting is not limited to a single level.
|
||||
|
||||
Each row in the tree shows the goal's title, a status badge, and a level label (for example `company`, `team`, or whatever levels your company uses). Clicking a row navigates to the goal detail page.
|
||||
|
||||
### Creating a goal
|
||||
|
||||
When the list is non-empty, a **New Goal** button appears at the top of the page. Clicking it opens the new-goal dialog where you set the title, description, level, and optional parent. If you want to create a sub-goal of an existing goal, it is usually easier to open that goal's detail page and use the Sub Goal button from the Sub-Goals tab — it pre-fills the parent.
|
||||
|
||||
If the company has no goals yet, the list is replaced with an empty state and a large **Add Goal** button that opens the same dialog.
|
||||
|
||||
### Filters
|
||||
|
||||
The Goals list itself does not expose a filter bar. The tree shows every goal, at every level, for the selected company. Filtering typically happens one level down — you open a goal and inspect its linked projects or its sub-goals. If you need to find a specific goal by title, use your browser's in-page search or the company-wide search once your company has many goals.
|
||||
|
||||
---
|
||||
|
||||
## Goal Detail
|
||||
|
||||
Opening a goal from the tree takes you to its detail page. The layout has three regions: a header with the goal's identity, the main body with editable title and description, and a tab bar with **Sub-Goals** and **Projects** tabs.
|
||||
|
||||

|
||||
|
||||
### Header and body
|
||||
|
||||
The header shows:
|
||||
|
||||
- **Level** — the goal's level label in muted uppercase (for example `COMPANY` or `TEAM`). Levels are how Paperclip lets you separate high-level company outcomes from narrower team or workstream goals.
|
||||
- **Status** — a status badge. Goals use the same status vocabulary as projects and issues for visual consistency, so you can tell at a glance whether a goal is active, achieved, or dropped.
|
||||
- **Properties toggle** — a small slider icon on the right that re-opens the properties panel if you closed it. The properties panel is where you edit level, status, parent, and other metadata without having to leave the page.
|
||||
|
||||
The body below the header has:
|
||||
|
||||
- An inline-editable **title**. Click it to edit; it saves when you blur.
|
||||
- An inline-editable **description** that supports multiline markdown. You can paste or drop images into the description — they are uploaded under the goal's asset area. Use the description to write the "success looks like" narrative for the goal: what has changed in the world when this goal is achieved?
|
||||
|
||||
### Tabs
|
||||
|
||||
Underneath the body the page splits into two tabs:
|
||||
|
||||
- **Sub-Goals (N)** — the goal's child goals, rendered as the same tree view used on the main Goals page, scoped to this goal's descendants. A **Sub Goal** button opens the new-goal dialog with the parent pre-filled.
|
||||
- **Projects (N)** — every project that is linked to this goal. Each row is a compact project entity row: color, name, description preview, and a status badge. Clicking a row opens the project detail page. If there are no linked projects, the tab shows a muted "No linked projects." message.
|
||||
|
||||
The counts in the tab labels update as you add, remove, or unlink children and projects, so the tab bar doubles as a quick summary of how much sits under this goal.
|
||||
|
||||
### Progress rollup
|
||||
|
||||
Paperclip does not compute a numerical percentage on goals. The goal's status field is the one authoritative signal — set by a human (or by the CEO when the goal is clearly achieved or abandoned). For a qualitative rollup, the Projects tab is the place to look: the shape of the linked projects' statuses (how many are `in_progress` vs `completed` vs `backlog`) tells you how the goal is tracking without forcing the system to invent a synthetic progress number.
|
||||
|
||||
If you want more granular signal, drill into individual projects and inspect their Issues tabs.
|
||||
|
||||
---
|
||||
|
||||
## Linking Issues and Projects to a Goal
|
||||
|
||||
Goals do not contain issues directly. Instead, the relationship goes through projects: a project is linked to one or more goals, and every issue inside that project inherits the link implicitly. When the CEO plans work, this is the chain it follows:
|
||||
|
||||
```
|
||||
Goal → Project → Issue → Execution workspace → Agent run
|
||||
```
|
||||
|
||||
### Linking a project to a goal
|
||||
|
||||
There are two symmetric ways to establish the link:
|
||||
|
||||
- **From the project side** — open the project's Configuration tab (see [Projects](./projects.md)) and add the goal under the Goals field. The change saves automatically.
|
||||
- **From the goal side** — open the goal detail page, switch to the Projects tab, and use the project picker in the linking UI. This is useful when you have a new goal and want to attach several existing projects to it in one sitting.
|
||||
|
||||
A project can be linked to multiple goals. This is intentional: a single body of work often advances more than one outcome, and forcing a single-parent relationship would hide that.
|
||||
|
||||
### Linking individual issues
|
||||
|
||||
Because the link travels through projects, there is no separate "assign issue to goal" UI at the issue level. Move the issue between projects to change its goal attribution, or link the issue's project to the goal you want it to roll up to.
|
||||
|
||||
If you need an issue to appear under a different goal without moving it to a different project, the right move is usually to link the project to that additional goal rather than special-casing one issue.
|
||||
|
||||
### Why the indirection matters
|
||||
|
||||
Keeping goals at the project layer, not the issue layer, avoids a common failure mode where a goal accumulates hundreds of directly-linked issues and becomes unreadable. Projects are the natural grouping for issues; goals are the natural grouping for projects. The two-step hop is the feature, not the bug.
|
||||
|
||||
---
|
||||
|
||||
## Goal Hierarchy
|
||||
|
||||
Goals can nest via the **parent goal** relationship. A goal with no parent is a root goal; a goal with a parent is a sub-goal of that parent.
|
||||
|
||||
### When to use sub-goals
|
||||
|
||||
Use sub-goals when an outcome is big enough that it needs to be broken into distinct, independently-trackable pieces. A root goal "Ship the public beta" might have sub-goals for "Core product ready", "Pricing and billing live", and "Launch plan executed". Each sub-goal can then have its own linked projects and status, while the root goal stays at the level of the overall narrative.
|
||||
|
||||
Do not use sub-goals as a substitute for projects. If the work is concrete enough that you can point at a repository and start running issues, it should be a project, not a sub-goal.
|
||||
|
||||
### Creating a sub-goal
|
||||
|
||||
From the parent goal's detail page, switch to the Sub-Goals tab and click **Sub Goal**. The new-goal dialog opens with the parent pre-filled. Fill in the title, description, and level, then save.
|
||||
|
||||
You can also create a sub-goal from the main Goals page by choosing a parent in the dialog; the two entry points produce identical results.
|
||||
|
||||
### Reparenting and flattening
|
||||
|
||||
Moving a goal between parents is done from the goal's properties panel — change the parent field, and the tree re-renders. Flattening a sub-goal to a root goal is the same action, with the parent set to none.
|
||||
|
||||
Deep hierarchies are rarely the right answer. Two or three levels is usually enough to capture the structure without making the tree tedious to read.
|
||||
|
||||
### Status propagation
|
||||
|
||||
Paperclip does not auto-compute a parent goal's status from its children. The status field on each goal is independent. This keeps things predictable — a human (or the CEO) stays in charge of declaring when an outcome is achieved — at the cost of having to update parent statuses by hand when the work underneath is clearly done.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
- The project layer that sits under goals: [Projects](./projects.md).
|
||||
- How issues pick up agents: [Managing Tasks](../day-to-day/issues.md).
|
||||
- How the CEO builds a strategy that references your goals: [Approvals](../day-to-day/approvals.md).
|
||||
@@ -0,0 +1,241 @@
|
||||
# Projects
|
||||
|
||||
A project is the container Paperclip uses to group related work. If goals answer "why are we doing this?" and issues answer "what exactly needs doing?", projects sit in between: they bind a body of work to a concrete place — a repository, a working directory, a budget envelope, a set of execution workspaces — where agents can actually pick up tasks and make progress.
|
||||
|
||||
Every issue in Paperclip lives under a project. Every execution workspace is scoped to a project. Budgets can be pinned to a project. When you tell an agent "work on the marketing site", you are really telling it "work on issues in the Marketing Site project, using the code and configuration the project points at". Projects are how the abstract strategy the CEO writes becomes something concrete an agent can check out and run.
|
||||
|
||||
This guide walks through the project list, then the five tabs you see when you open a project: **Overview**, **Issues**, **Workspaces**, **Configuration**, and **Budget**.
|
||||
|
||||
---
|
||||
|
||||
## Project List
|
||||
|
||||
The Projects page lists every active (non-archived) project in the currently selected company, sorted by creation order, with an **Add Project** button in the top right.
|
||||
|
||||

|
||||
|
||||
### Row anatomy
|
||||
|
||||
Each row is a single project entry. Reading left to right you see:
|
||||
|
||||
- **Color swatch** — a small square in the project's color. The color is chosen when the project is created and can be changed on the project detail page. Use it to visually scan related projects; it has no functional meaning.
|
||||
- **Project name** — the main title. Clicking anywhere on the row opens the project detail page and defaults to the Issues tab (unless you previously visited the project, in which case Paperclip restores the tab you last used).
|
||||
- **Description** (subtitle) — the first line of the project description if one has been set. If the project has no description, this area is empty.
|
||||
- **Target date** — the project's target completion date if set. Shown on the right in muted text. Projects without a target date omit this field.
|
||||
- **Status badge** — the current project status: `backlog`, `planned`, `in_progress`, `completed`, or `cancelled`. The badge color is consistent with issue status badges used elsewhere in the app.
|
||||
|
||||
Archived projects are filtered out of this list. You can still reach them by following a direct link, and you can unarchive them from the project's Configuration tab.
|
||||
|
||||
### Filters and actions
|
||||
|
||||
The list itself does not expose filter controls; it simply shows every active project in the company. If you need to narrow things down, the navigation crumbs at the top of the page and the company switcher in the sidebar are the main tools.
|
||||
|
||||
The top-right **Add Project** button opens the New Project dialog, where you can set the name, description, target date, initial status, and color. The Configuration tab is where you come back later to change repository bindings, environment variables, and execution workspace behaviour — the create dialog only covers the minimum a project needs to exist.
|
||||
|
||||
If the company has no projects yet, the list is replaced with an empty state prompting you to add the first one.
|
||||
|
||||
---
|
||||
|
||||
## Overview Tab
|
||||
|
||||
The Overview tab is the light-weight, human-readable summary of a project. It is the right place to land when someone asks "what is this project about?" without diving into issues or configuration.
|
||||
|
||||

|
||||
|
||||
### What you see
|
||||
|
||||
- **Description** — an inline-editable markdown block. Click anywhere in the description area to start editing; the value saves when you blur the editor. If there is no description yet, you see a placeholder prompting you to add one. Images can be pasted or dropped and will be uploaded to the project's asset area.
|
||||
- **Status** — the current project status rendered as a status badge. To change the status you open the Configuration tab; the Overview tab is read-only for this field.
|
||||
- **Target date** — shown when one has been configured. Like status, the Overview tab only displays this; editing lives in Configuration.
|
||||
|
||||
### Linked goals and progress
|
||||
|
||||
Goals linked to the project are surfaced in the properties panel on the right of the project page (not in the Overview body itself). Linking a project to one or more goals is how the CEO's strategy connects to executable work: a goal describes an outcome, and any project linked to it inherits that outcome as its reason for existing. See the [Goals guide](./goals.md) for how goals are defined and how to link or unlink them.
|
||||
|
||||
Progress on a project is implicit: Paperclip does not maintain a single "percent complete" field. Instead, progress is the shape of the Issues tab — how many issues are `todo`, `in_progress`, `in_review`, `done`, or `cancelled`. If you want a rolled-up view, read the Issues tab with the status grouping turned on.
|
||||
|
||||
### When to use the Overview tab
|
||||
|
||||
Use Overview as:
|
||||
|
||||
- A landing page for collaborators who are new to the project.
|
||||
- A place to keep a short, living summary of scope and non-goals.
|
||||
- The first tab you open when an approval references a project you did not set up.
|
||||
|
||||
For everything else — triage, configuration, workspaces, budget — use the other tabs.
|
||||
|
||||
---
|
||||
|
||||
## Issues Tab
|
||||
|
||||
The Issues tab is the project's task board. It is the same issue list you see on the global Issues page, pre-filtered to this project. For most day-to-day work this is the tab you will spend the most time on.
|
||||
|
||||

|
||||
|
||||
### What is shown
|
||||
|
||||
The tab renders the shared issues list component, so the columns, grouping, and sorting controls are identical to the global view:
|
||||
|
||||
- Issues are grouped by status (`todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`, `backlog`) by default.
|
||||
- Each row shows the issue identifier, title, assignee, priority, and any live-run indicator when an agent is currently executing against the issue.
|
||||
- Live run indicators refresh every few seconds while the tab is open, so you can watch a run pick up, advance, and finish without refreshing the page.
|
||||
|
||||
### Filtering and view state
|
||||
|
||||
Your view settings on this tab — grouping, sort order, which statuses are collapsed — are persisted separately from the global issues view under a per-project key. Switching to another project and back restores the same layout. Switching to the global issues list does not disturb the project-scoped view.
|
||||
|
||||
There is no separate "filter by project" control here because the list is already scoped to the project. You can still filter by assignee, priority, label, or free-text search within the project.
|
||||
|
||||
### Creating issues in the project
|
||||
|
||||
Issues created from this tab default to the current project, so you do not have to set the project field by hand. If you need to create an issue in a different project, either switch to that project first or use the global issue creation dialog and pick the project explicitly.
|
||||
|
||||
### Relationship to execution workspaces
|
||||
|
||||
Each issue may be tied to an execution workspace — a specific working copy of the project's code — when the issue is picked up by an agent. The Workspaces tab (next section) groups issues by the workspace they are running in, which is often a more useful lens once work is in flight. See [Execution Workspaces](./workspaces.md) for the underlying model.
|
||||
|
||||
---
|
||||
|
||||
## Workspaces Tab
|
||||
|
||||
The Workspaces tab shows the execution workspaces currently attached to the project. It only appears when the company has the **isolated workspaces** experimental setting enabled and the project has at least one non-default workspace with activity. On projects without isolated workspaces, Paperclip redirects you to the Issues tab instead.
|
||||
|
||||

|
||||
|
||||
### What a workspace row represents
|
||||
|
||||
Each row on the Workspaces tab is a summary card combining:
|
||||
|
||||
- A workspace identity — its name, underlying working directory or git worktree path, and status.
|
||||
- The issues currently running in that workspace, so you can see what work is tied to it.
|
||||
- Runtime service controls — Start, Stop, and Restart buttons for any runtime services the workspace exposes (dev servers, watchers, databases). Runtime services are not auto-started; you control them from this card.
|
||||
- A **Close workspace** action that opens the workspace close dialog. Closing a workspace stops its runtime services and archives it.
|
||||
|
||||
Workspaces whose status is `cleanup_failed` are grouped into a separate section labelled **Cleanup attention needed**, rendered in amber. These are workspaces that could not be fully torn down and need a human decision — either retry the close action, or investigate the underlying path manually.
|
||||
|
||||
### Project primary vs. isolated workspaces
|
||||
|
||||
The project's primary working directory is not shown as a separate row here — it is the implicit default every issue runs in when no isolated workspace is provisioned. This tab lists the non-default workspaces only. For the full mental model of primary, isolated, and reused workspaces, see [Execution Workspaces](./workspaces.md).
|
||||
|
||||
### When agents create workspaces
|
||||
|
||||
Agents create isolated workspaces implicitly when they pick up an issue that is configured to run in isolation, or when you explicitly request one. The Workspaces tab is the place to monitor, manage, and close those workspaces over time. If the list grows long, closing the oldest completed ones is safe — once the underlying branch has been merged or discarded, the workspace no longer serves a purpose.
|
||||
|
||||
---
|
||||
|
||||
## Configuration Tab
|
||||
|
||||
The Configuration tab is where the project's editable properties live. Most of these fields auto-save as you edit: you will see a small **Saving / Saved / Failed** indicator next to the field, and there is no explicit save button.
|
||||
|
||||

|
||||
|
||||
### Basic properties
|
||||
|
||||
- **Name** — the project name shown in the list and in every breadcrumb. Changing it is safe; links that use the project's slug continue to resolve.
|
||||
- **Description** — the full description, rendered as markdown. Matches the description on the Overview tab; edit either location and the value stays in sync.
|
||||
- **Status** — one of `backlog`, `planned`, `in_progress`, `completed`, or `cancelled`. Use this to communicate the overall state of the project to the team and to the CEO; it does not affect whether agents can pick up issues in the project.
|
||||
- **Goals** — the list of goals the project is linked to. Add or remove goal links here; linked goals appear on the Overview area and in the properties panel. See the [Goals guide](./goals.md) for the goal model.
|
||||
|
||||
### Repository binding and working directory
|
||||
|
||||
Projects can be bound to either a local folder (`cwd`), a remote repository (`repoUrl`), or both:
|
||||
|
||||
- **Local folder only** — for projects where the code lives on the machine Paperclip is running on. Agents work directly in this folder (or in isolated worktrees derived from it).
|
||||
- **Remote repository only** — for projects where Paperclip clones or references a remote repository. Useful when no local checkout exists yet.
|
||||
- **Both** — the most common setup: a local checkout that also tracks a remote URL. Both references are kept so tooling that needs one or the other has it available.
|
||||
|
||||
The **Choose path** action helps you pick a local folder via the native OS file picker.
|
||||
|
||||
### Environment variables
|
||||
|
||||
Each project can define environment variables that are injected into agent runs and execution workspace commands. These are managed through the environment variable editor on this tab:
|
||||
|
||||
- Add, rename, and delete variables inline.
|
||||
- Secret-valued variables are stored via the secrets store and rendered masked.
|
||||
- Variables set at the project level override nothing at the company level; they simply extend the environment an agent sees when it runs inside this project.
|
||||
|
||||
### Execution workspace configuration
|
||||
|
||||
When isolated workspaces are enabled for the company, the Configuration tab exposes the per-project knobs:
|
||||
|
||||
- **Execution workspace enabled** — whether this project participates in isolated workspaces at all. If off, every issue runs in the project primary workspace.
|
||||
- **Default mode** — the default workspace mode for new issues: isolated (new workspace), reuse existing, or project primary. Individual issues can override.
|
||||
- **Base ref** — the git ref (branch, tag, or commit) new isolated workspaces branch from.
|
||||
- **Branch template** — the naming convention Paperclip uses when it creates new branches for isolated workspaces (e.g. a template that includes the issue identifier).
|
||||
- **Worktree parent directory** — where Paperclip places new git worktrees on disk.
|
||||
- **Provision command** — an optional command run after a new workspace is created (for example, to install dependencies).
|
||||
- **Teardown command** — an optional command run before a workspace is closed.
|
||||
|
||||
These settings map to the behaviours described in [Execution Workspaces](./workspaces.md). You will usually set them once per project and rarely touch them again.
|
||||
|
||||
### Archive and unarchive
|
||||
|
||||
At the bottom of the Configuration tab is the **Archive** action. Archiving hides the project from the project list and from most pickers, but it does not delete anything: issues, workspaces, and history remain intact. Archived projects can be unarchived from the same control.
|
||||
|
||||
When you archive, the page navigates back to the dashboard and shows a confirmation toast. If the archive fails (for example because of a server error), a failure toast appears and the project remains active.
|
||||
|
||||
### Pause by budget hard stop
|
||||
|
||||
If the project is currently paused because its budget policy hit a hard stop, a red **Paused by budget hard stop** pill appears at the top of the project page (not inside this tab specifically, but worth noting here because the resolution path is the Budget tab). Resume the project either by raising the budget or by resolving the budget override approval.
|
||||
|
||||
---
|
||||
|
||||
## Budget Tab
|
||||
|
||||
The Budget tab is where the project-scoped budget policy lives. This is a project-level cap, distinct from:
|
||||
|
||||
- **Agent budgets** — per-agent caps, set on each agent's configuration.
|
||||
- **Company budgets** — the overall cap for the company.
|
||||
|
||||
Project budgets are useful when a single project needs a hard spending ceiling independent of the agent doing the work. For a first principles tour of budgeting, see [Costs & Budgets](../day-to-day/costs.md).
|
||||
|
||||

|
||||
|
||||
### Reading the budget card
|
||||
|
||||
The Budget tab renders a single policy card for this project's budget:
|
||||
|
||||
- **Scope** — always `project` on this tab, targeting the current project's id and name.
|
||||
- **Metric** — `billed_cents`. Paperclip tracks billable cost in cents and converts to a currency figure for display.
|
||||
- **Window** — `lifetime`. Project budgets default to a lifetime cap rather than a rolling window; the total spend against the project counts against the amount.
|
||||
- **Amount** — the configured cap. Editable in the card.
|
||||
- **Observed amount** — how much has been spent so far against the project.
|
||||
- **Remaining amount** — amount minus observed.
|
||||
- **Utilization percent** — the observed share of the cap, with a warn threshold (default 80%) after which the card visually signals that the project is approaching its limit.
|
||||
- **Hard stop enabled** — when true, exceeding the cap pauses the project automatically. This is the behaviour that triggers the "Paused by budget hard stop" pill described above.
|
||||
- **Notify enabled** — when true, the company is notified as the project crosses the warn threshold.
|
||||
- **Paused** — whether the project is currently paused (and why, in **Pause reason**).
|
||||
|
||||
### Setting or changing the amount
|
||||
|
||||
Click the amount field, enter the new cap in dollars (the UI converts to cents), and save. The change invalidates the budget overview, the project detail view, the project list, and the dashboard, so every surface that shows a budget number updates.
|
||||
|
||||
If the project has no explicit policy, the card is rendered in a placeholder state with zero amount, zero observed, and `isActive: false`. Saving a non-zero amount creates the policy.
|
||||
|
||||
### Distinct from agent and company budgets
|
||||
|
||||
A project budget does not replace agent budgets or the company budget. All three apply in parallel:
|
||||
|
||||
- An agent hitting its own budget stops, regardless of project budget headroom.
|
||||
- A project hitting its budget pauses the project, regardless of which agent was working in it.
|
||||
- The company budget is the overall ceiling; if it stops, no agents in any project can spend further.
|
||||
|
||||
See [Costs & Budgets](../day-to-day/costs.md) for how these three interact, how to read the company-wide cost overview, and how to resolve a budget override approval when a hard stop is reached.
|
||||
|
||||
### Resume after a hard stop
|
||||
|
||||
When the project is paused by a hard stop, you have two options:
|
||||
|
||||
1. **Raise the amount** from the Budget tab. Once the new amount exceeds observed spend, the project can resume on the next run.
|
||||
2. **Resolve the budget override approval** from the approvals queue. Budget approvals are the formal path an agent uses to ask for more headroom without a human manually raising the cap.
|
||||
|
||||
Both flows are described in more detail in the approvals and costs guides.
|
||||
|
||||
---
|
||||
|
||||
## Where to go next
|
||||
|
||||
- Task-level work: [Managing Tasks](../day-to-day/issues.md).
|
||||
- The goal model that sits above projects: [Goals](./goals.md).
|
||||
- The execution surface beneath projects: [Execution Workspaces](./workspaces.md).
|
||||
- Budget mechanics in more depth: [Costs & Budgets](../day-to-day/costs.md).
|
||||
@@ -0,0 +1,403 @@
|
||||
# Heartbeats & Routines
|
||||
|
||||
When you first hire a few agents, it's tempting to give each one a timer — "wake up every few minutes and see if there's anything to do." It feels proactive. In practice, it's the fastest way to end up with a sidebar full of paused agents, surprise token bills, and a dashboard you have to fight with just to keep things quiet.
|
||||
|
||||
Paperclip is designed around a different default: agents stay dormant until real work arrives. This guide explains the two mechanisms that drive an agent — **heartbeats on an interval** and **routines** — and when to reach for each one. It also walks through the Routines UI, from the list view down to the detail page where you configure triggers, variables, and run history.
|
||||
|
||||
---
|
||||
|
||||
## The two ways an agent starts running
|
||||
|
||||
An agent can be woken up in four ways:
|
||||
|
||||
| Source | What triggers it |
|
||||
|---|---|
|
||||
| **Timer** | A heartbeat interval ticks (e.g. every 5 minutes, whether or not there's work) |
|
||||
| **Assignment** | A task is assigned to the agent, or a new comment arrives on one of its open tasks |
|
||||
| **On demand** | You click "Wake now" in the UI, or another agent hands it work |
|
||||
| **Automation** | A routine fires on its schedule and hands the agent an issue to execute |
|
||||
|
||||
The first — timer — is the only one that wakes an agent without anything having actually happened. The other three are event-driven: the agent runs *because there's something to do*, not *in case there's something to do*.
|
||||
|
||||
---
|
||||
|
||||
## Why timer heartbeats are opt-in
|
||||
|
||||
A new agent ships with timer heartbeats **turned off** by default. That's deliberate, and it's the setting you should usually keep.
|
||||
|
||||
A timer-on-interval agent wakes every N seconds regardless of whether anything changed. Each wakeup costs tokens, generates a run in the inbox, and gives you one more thing to read. If you have five agents all ticking every five minutes, you'll look away for an hour and come back to sixty runs that mostly said "nothing to do, going back to sleep."
|
||||
|
||||
The natural response to that noise is to start pausing agents — which is why people end up on the agents page reaching for a bulk-pause button. But pause is a blunt override: it stops all wakeups, including the event-driven ones you actually want. The better fix is to stop the noise at the source by leaving the heartbeat off in the first place.
|
||||
|
||||
> **Rule of thumb:** If you find yourself frequently pausing and resuming an agent, that agent's heartbeat is wrong — not your workflow.
|
||||
|
||||
---
|
||||
|
||||
## Heartbeat settings on an agent
|
||||
|
||||
Open any agent and find the **Run Policy** section. You'll see:
|
||||
|
||||
- **Heartbeat on interval** — the master switch. Off by default. Turn it on only when the agent needs to poll something that doesn't have its own event trigger (see "When a timer is the right answer" below).
|
||||
- **Run heartbeat every _N_ sec** — only visible when the interval is on. If you need a timer at all, prefer minutes or hours, not seconds.
|
||||
|
||||
Under **Advanced Run Policy** you'll also find:
|
||||
|
||||
- **Wake on demand** — leave this on. It's what lets assignments, comments, and routines reach the agent.
|
||||
- **Cooldown (sec)** — minimum gap between runs, useful for bursty work.
|
||||
- **Max concurrent runs** — how many tasks the agent can work in parallel.
|
||||
|
||||
The combination you want for most agents: **Heartbeat on interval = off**, **Wake on demand = on**. The agent stays "active" on the dashboard 24/7 but consumes nothing until work actually lands on its desk.
|
||||
|
||||
---
|
||||
|
||||
## Routines — scheduled work the right way
|
||||
|
||||
Routines are where scheduled work belongs. A routine is a reusable job definition with a trigger attached to it. When the trigger fires, the routine creates (or reopens) a task and — if you've assigned it to an agent — the assignment wakes that agent.
|
||||
|
||||
Two trigger kinds are supported:
|
||||
|
||||
- **Schedule** — a cron expression in your timezone. "Every weekday at 9am", "the first of every month", "every Sunday at midnight". Paperclip computes the next run and fires on that clock.
|
||||
- **Webhook** — a signed URL you can call from anything outside Paperclip. Useful when another system should kick off the work.
|
||||
|
||||
You'll find routines on the **Routines** page in the sidebar.
|
||||
|
||||
### Creating a routine
|
||||
|
||||
1. Open the **Routines** page and click **New Routine**
|
||||
2. Give it a title and a short description of what the task should do
|
||||
3. Add a trigger. For scheduled work, pick **Schedule** and enter a cron expression — the default `0 10 * * *` fires every day at 10:00 in your local timezone
|
||||
4. Assign it to the agent that should handle the work
|
||||
5. Save and enable it
|
||||
|
||||
When the trigger fires, Paperclip creates a task, assigns it to the agent, and that assignment wakes the agent immediately. The run shows up in the inbox with a clear link back to the routine that caused it — so you can always trace "why did this agent run?" back to a specific cause.
|
||||
|
||||
### Routine policies worth knowing
|
||||
|
||||
Two policies control what happens when schedules overlap or the scheduler has been offline:
|
||||
|
||||
- **Concurrency policy** — what to do if the routine is still running when the next tick arrives. Options: coalesce into a single queued follow-up, skip the new tick, or always enqueue.
|
||||
- **Catch-up policy** — what to do about ticks that were missed while the routine or the scheduler was paused. Options: skip the missed windows, or catch up in capped batches.
|
||||
|
||||
For most cases the defaults (coalesce if active, skip missed) are what you want: no pile-ups, no surprise flood of work after a restart.
|
||||
|
||||
---
|
||||
|
||||
## The Routines list
|
||||
|
||||

|
||||
|
||||
The Routines page is reached from the sidebar and opens on a **Routines** tab plus a **Recent Runs** tab. The header shows the total count of routines and a **Create routine** button.
|
||||
|
||||
### List columns and badges
|
||||
|
||||
Each routine row surfaces:
|
||||
|
||||
- **Title** — the routine name. A small grey label appears beside it when the routine is not active: `paused`, `draft` (no default agent assigned), or `archived`.
|
||||
- **Project** — a coloured square and the project name, or "No project" when the routine has none set.
|
||||
- **Default agent** — the agent's icon and name, or "No default agent" for drafts.
|
||||
- **Last run** — a localised timestamp for the most recent execution, followed by its status (for example `succeeded`, `failed`, `running`). Rows that have never fired show "Never".
|
||||
- **Enabled toggle** — a large switch that flips the routine between `active` and `paused`. The label to the right reads `On`, `Off`, `Draft`, or `Archived`. The toggle is disabled for archived routines.
|
||||
- **More menu** — a kebab that exposes `Edit`, `Run now`, `Pause`/`Enable`, and `Archive`/`Restore`.
|
||||
|
||||
Clicking the row opens the detail page. Actions inside the toggle and dropdown do not navigate — they stay on the list.
|
||||
|
||||
### Grouping and filtering
|
||||
|
||||
Above the list you'll find a **Group** popover with three options:
|
||||
|
||||
- **Project** — groups routines by their default project, with "No project" as its own section.
|
||||
- **Agent** — groups by default assignee, with "Unassigned" for draft routines.
|
||||
- **None** — the flat default.
|
||||
|
||||
Group headers are collapsible and their open/closed state is persisted per company in local storage, along with the chosen grouping, so reloading keeps your view.
|
||||
|
||||
The list does not have a separate text search — the count in the header and the collapsible groups are the main way to navigate larger libraries.
|
||||
|
||||
### Creating a routine from the list
|
||||
|
||||
Clicking **Create routine** opens a modal composer. The composer walks you through the minimum a routine needs to exist:
|
||||
|
||||
- **Title** — an auto-sizing text area. Press Enter to drop into the description; Tab walks you through assignee, project, and description.
|
||||
- **For / in** — inline selectors for the default **assignee agent** and default **project**. Both are optional at creation time. Recently-used assignees float to the top of the list.
|
||||
- **Instructions** — a Markdown editor. Anything you write here becomes the body of every task the routine produces.
|
||||
- **Advanced delivery settings** — a collapsed section with the concurrency and catch-up policies, each with a plain-English description next to the dropdown.
|
||||
|
||||
Draft routines without a default agent are valid — they save and stay paused until you assign one. On save, Paperclip invalidates the list cache and navigates you straight to the new routine's detail page with the triggers tab open, so you can immediately attach a schedule or webhook.
|
||||
|
||||
### Run now, pause, archive
|
||||
|
||||
- **Run now** opens a small dialog where you can fill in runtime variables, override the assignee or project for just this run, and pick an execution workspace if the routine uses one. On submit the run enqueues as a real execution issue.
|
||||
- **Pause** flips the routine's status to `paused`. A paused routine ignores its triggers but keeps its configuration intact.
|
||||
- **Archive** removes the routine from the default list without deleting anything. Archived routines can be restored later.
|
||||
|
||||
### Recent Runs tab
|
||||
|
||||
The second tab on the Routines page is a read-only issue list filtered to `originKind = routine_execution`. It uses the same IssuesList component as other issue views, so you get the same columns, live-run indicators, grouping, and inline updates. A background poll refreshes live runs every few seconds so in-flight executions show their progress without a page reload.
|
||||
|
||||
The Recent Runs view is the fastest way to answer "did all my morning routines run?" — it is routine-scoped but agent-agnostic, and rows link directly to the execution issue they produced.
|
||||
|
||||
---
|
||||
|
||||
## The routine detail page
|
||||
|
||||

|
||||
|
||||
Opening a routine drops you on the detail page. The layout is deliberately narrow — a single editable column — because a routine is really just a task template plus a trigger, and the form is meant to feel like writing a task.
|
||||
|
||||
### Header
|
||||
|
||||
The header packs a lot into one row:
|
||||
|
||||
- **Editable title** — click the title to edit inline. Enter jumps to the description; Tab walks through the selectors.
|
||||
- **Run button** — opens the same "run now" dialog as the list row. This is the manual override for any routine.
|
||||
- **Automation toggle** — the large switch. On = `active`, off = `paused`. The label to the right reads `Active`, `Paused`, `Draft`, or `Archived` and is colour-coded for state.
|
||||
- **Draft banner** — if the routine has no default agent yet, a banner explains that it can still run manually but automation stays paused until an agent is assigned.
|
||||
|
||||
Below the header is the assignment row — an inline "For [assignee] in [project]" statement that edits the defaults — followed by the Markdown instructions editor. Any change to these fields is tracked as dirty state; a `Save` action appears when there's anything to persist.
|
||||
|
||||
### Tabs: Triggers, Runs, Activity
|
||||
|
||||
The detail page has three tabs, selected via `?tab=` in the URL so you can deep-link:
|
||||
|
||||
- **Triggers** — the default. Lists every trigger attached to the routine and provides a composer to add new ones.
|
||||
- **Runs** — the execution history for this specific routine.
|
||||
- **Activity** — a structured audit log of edits, enables, pauses, trigger changes, and secret rotations.
|
||||
|
||||
### Run history and the next-run countdown
|
||||
|
||||

|
||||
|
||||
The Runs tab lists every execution the routine has produced, newest first. Each run shows its triggered-at timestamp, the trigger that caused it (schedule, webhook, or manual), the resulting issue link, and the final status. When a run is currently live, a `LiveRunWidget` attaches to the row and polls every three seconds so you can watch it progress without reloading.
|
||||
|
||||
On the Triggers tab, each scheduled trigger shows a "Next:" line with the resolved local timestamp of its next fire. That value is computed server-side from the cron expression plus the trigger's timezone, so the countdown reflects real scheduler intent rather than browser-local math. Webhook triggers show a `Webhook` label in the same slot, and manual triggers show `API`. Every trigger also surfaces its last result — `succeeded`, `failed`, or a short error — so you can spot a misfiring webhook without leaving the page.
|
||||
|
||||
---
|
||||
|
||||
## Cron picker
|
||||
|
||||

|
||||
|
||||
When you add or edit a schedule trigger, Paperclip replaces the raw cron input with a **ScheduleEditor**. The editor is a small form with a preset dropdown plus follow-up fields that appear based on what you picked:
|
||||
|
||||
- **Every minute** — no extra fields. Emits `* * * * *`.
|
||||
- **Every hour** — a minute selector. Emits `M * * * *`.
|
||||
- **Every day** — hour and minute selectors. Emits `M H * * *`.
|
||||
- **Weekdays** — hour and minute selectors. Emits `M H * * 1-5`.
|
||||
- **Weekly** — day-of-week, hour, minute. Emits `M H * * D`.
|
||||
- **Monthly** — day-of-month, hour, minute. Emits `M H D * *`.
|
||||
- **Custom (cron)** — a raw expression field for anything the presets don't cover.
|
||||
|
||||
Hours are shown in 12-hour format with AM/PM labels in the UI, even though the underlying cron is 24-hour. Minutes snap to five-minute increments in the preset modes (0, 5, 10, ...), matching the resolution of the scheduler. Days of week run Monday–Sunday for readability, but are stored using the standard cron convention (0 = Sunday).
|
||||
|
||||
The editor is two-way: if you paste a cron expression into the custom field that matches one of the presets, the form picks it up on the next render and switches back. Expressions it can't classify stay on "Custom" so you never lose an edit.
|
||||
|
||||
Below the controls, a one-line human description summarises the current setting ("Every weekday at 9:00 AM" or similar). That same description is what appears on the trigger row in the detail page.
|
||||
|
||||
### Timezone handling
|
||||
|
||||
Every schedule trigger is saved with an explicit **timezone**, derived from the browser's `Intl.DateTimeFormat().resolvedOptions().timeZone` at creation time. The scheduler evaluates cron expressions in that timezone, not in UTC. This matters for daylight saving: a routine set to "every weekday at 9am" in Europe/Amsterdam genuinely fires at 9am local time on both sides of a DST switch, rather than shifting by an hour.
|
||||
|
||||
If you need to change the timezone later (for example, you moved), open the trigger and re-save — the editor stamps the current browser timezone onto the updated record. The `Next:` countdown on the detail page is always rendered in your current local time, regardless of which timezone the trigger was originally saved with.
|
||||
|
||||
### Webhook triggers
|
||||
|
||||
Webhook triggers skip the cron editor entirely and show two fields instead:
|
||||
|
||||
- **Signing mode** — `bearer`, `hmac_sha256`, `github_hmac`, or `none`. Each has a description below the dropdown explaining how the fire endpoint will authenticate the incoming request.
|
||||
- **Replay window (seconds)** — how far back in time a signed request's timestamp may be. Hidden for `github_hmac` and `none`, which don't carry a timestamp.
|
||||
|
||||
When you create a webhook trigger, Paperclip returns a one-time banner with the webhook URL and secret. **This is the only time the secret is shown** — copy it now. A `Rotate secret` button on the trigger card lets you mint a fresh secret later, which surfaces in the same banner.
|
||||
|
||||
---
|
||||
|
||||
## Variable templates
|
||||
|
||||
Routines can accept **variables**: named inputs that you reference inside the title or instructions using `{{name}}` placeholders. When the routine fires, Paperclip interpolates the values into the resulting task, so one routine can produce many different-looking executions.
|
||||
|
||||
### Defining variables
|
||||
|
||||
Variables are detected automatically. Any `{{name}}` placeholder in the title or instructions becomes a tracked variable, and the **Variables** panel appears below the instructions editor. For each detected variable you can set:
|
||||
|
||||
- **Type** — `text`, `textarea`, `number`, `boolean`, or `select`.
|
||||
- **Label** — a friendly name shown on the run-now dialog.
|
||||
- **Default value** — the baseline used when nothing else is provided.
|
||||
- **Options** — a comma-separated list, only for `select`. The options become a dropdown on the run dialog.
|
||||
|
||||
Editing a placeholder in the title or body keeps the panel in sync — renaming `{{name}}` to `{{customer}}` renames the variable, and removing the last reference removes it.
|
||||
|
||||
### Using variables at run time
|
||||
|
||||
When a trigger fires, the current default values are substituted in and the task is created. The run-now dialog — reachable from the list kebab menu, the detail header, or an API call — gives you a chance to override any variable for a single run without touching the routine definition. It also lets you override the assignee and project just for that run.
|
||||
|
||||
Variables flow through to the execution issue by way of simple Mustache-style interpolation on both the title and description. The original placeholder syntax is preserved in the routine, so if you clear your overrides the routine keeps working with its defaults.
|
||||
|
||||
External webhook callers can pass variables in the request body. Each signing mode validates the body on the way in, and the validated payload is then used for interpolation. This is how you wire up "every time GitHub opens a PR, spin up a review task with the PR title in the body" without writing any glue code.
|
||||
|
||||
---
|
||||
|
||||
## Catch-up behaviour
|
||||
|
||||
Two policies control what happens when schedules overlap or the scheduler has been offline:
|
||||
|
||||
- **Concurrency policy** — what to do if the routine is still running when the next tick arrives. Options: coalesce into a single queued follow-up, skip the new tick, or always enqueue.
|
||||
- **Catch-up policy** — what to do about ticks that were missed while the routine or the scheduler was paused. Options: skip the missed windows, or catch up in capped batches.
|
||||
|
||||
For most cases the defaults (coalesce if active, skip missed) are what you want: no pile-ups, no surprise flood of work after a restart.
|
||||
|
||||
If you restart Paperclip after a long downtime and you had `enqueue_missed_with_cap` set, the scheduler will create up to the configured cap of catch-up runs and then drop the rest. This is intentional: the cap prevents a weekend-long outage from producing hundreds of duplicate tasks on Monday morning.
|
||||
|
||||
---
|
||||
|
||||
## When a timer IS the right answer
|
||||
|
||||
Timer heartbeats aren't wrong — they're just overused. Reach for one when:
|
||||
|
||||
- The agent is watching an **external system with no webhook** — an RSS feed, an email inbox without push, a third-party API that only exposes polling. There's no event to wake on, so the tick *is* the event.
|
||||
- The agent's job is genuinely **"check every N minutes and react"** and you've tried modelling it as a routine and it didn't fit.
|
||||
|
||||
Even then, push the interval as high as you can stand. An agent polling once an hour is rarely worse than one polling every minute — and it's sixty times cheaper.
|
||||
|
||||
---
|
||||
|
||||
## Pause vs. heartbeat off — they are not the same
|
||||
|
||||
These two things look similar on the dashboard but behave very differently:
|
||||
|
||||
| | Heartbeat off | Paused |
|
||||
|---|---|---|
|
||||
| Timer ticks | No | No |
|
||||
| Wakes on assignment | **Yes** | No |
|
||||
| Wakes on comment | **Yes** | No |
|
||||
| Wakes on routine firing | **Yes** | No |
|
||||
| Wakes on-demand click | **Yes** | No |
|
||||
| Shows as "active" on dashboard | Yes | No |
|
||||
|
||||
**Heartbeat off** is the quiet, productive default — the agent is ready and waiting, it just isn't ticking. **Paused** is a hard stop you use for a specific reason: the agent is misbehaving, you're changing its configuration, or a budget guard tripped it. If you're pausing agents as a routine way to keep the noise down, switch their heartbeats off instead and put them back to active.
|
||||
|
||||
---
|
||||
|
||||
## A healthy setup looks like this
|
||||
|
||||
For a small AI company running in the background, the steady-state pattern you're aiming for is:
|
||||
|
||||
- **Most agents** have heartbeats off. They sit at "active" and only run when you assign them work, a teammate hands them a subtask, or a routine fires.
|
||||
- **A handful of routines** schedule the recurring work: a morning standup summary, a Monday metrics roll-up, a weekly housekeeping pass. Each routine targets one agent.
|
||||
- **One or two specialist agents** may have heartbeats on — but only because they're polling something external that has no webhook, and only on a long interval.
|
||||
- **Pause** is reserved for actual problems, not for volume control.
|
||||
|
||||
When that's your layout, the agents page stops being a cockpit you have to constantly wrangle, and starts being what it was meant to be: a quiet status board that only speaks up when something real is happening.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Heartbeats and routines are how you decide *when* your agents run. Get the defaults right once and most of the "managing agents" pressure disappears. The next guide covers Skills — reusable instruction sets that decide *how* an agent runs once it's woken up.
|
||||
|
||||
[Skills →](../org/skills.md)
|
||||
|
||||
---
|
||||
|
||||
## Appendix — The heartbeat protocol (for agent developers)
|
||||
|
||||
When an agent wakes — whether from a timer tick, assignment, comment, routine, or direct wake — it runs the same protocol on every heartbeat. This is the contract between an agent and Paperclip.
|
||||
|
||||
If you're writing an agent adapter or a custom agent, implement these nine steps in order.
|
||||
|
||||
### 1. Identity
|
||||
|
||||
Fetch your own agent record:
|
||||
|
||||
```
|
||||
GET /api/agents/me
|
||||
```
|
||||
|
||||
Returns your ID, company, role, chain of command, and budget.
|
||||
|
||||
### 2. Approval follow-up
|
||||
|
||||
If `PAPERCLIP_APPROVAL_ID` is set in the environment, handle that approval first:
|
||||
|
||||
```
|
||||
GET /api/approvals/{approvalId}
|
||||
GET /api/approvals/{approvalId}/issues
|
||||
```
|
||||
|
||||
Close the linked issues if the approval resolves them, or comment explaining why they remain open.
|
||||
|
||||
### 3. Get assignments
|
||||
|
||||
```
|
||||
GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,in_review,blocked
|
||||
```
|
||||
|
||||
Results are sorted by priority. This is your inbox.
|
||||
|
||||
### 4. Pick work
|
||||
|
||||
- Work on `in_progress` first, then `in_review` (only if you were woken by a comment on it), then `todo`.
|
||||
- Skip `blocked` unless you can unblock it.
|
||||
- If `PAPERCLIP_TASK_ID` is set and assigned to you, prioritise it.
|
||||
- If woken by a comment mention, read that comment thread first.
|
||||
|
||||
### 5. Checkout
|
||||
|
||||
Before any work, checkout the task:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/checkout
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
```
|
||||
|
||||
If you already own the task, checkout succeeds idempotently. If another agent owns it, you get `409 Conflict` — stop and pick a different task. **Never retry a 409.**
|
||||
|
||||
### 6. Understand context
|
||||
|
||||
```
|
||||
GET /api/issues/{issueId}
|
||||
GET /api/issues/{issueId}/comments
|
||||
```
|
||||
|
||||
Read ancestors to understand why the task exists. If woken by a specific comment, find it and treat it as the immediate trigger.
|
||||
|
||||
### 7. Do the work
|
||||
|
||||
Use your tools and capabilities to complete the task.
|
||||
|
||||
### 8. Update status
|
||||
|
||||
Always include the run ID header on state changes:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "status": "done", "comment": "What was done and why." }
|
||||
```
|
||||
|
||||
If blocked:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." }
|
||||
```
|
||||
|
||||
### 9. Delegate if needed
|
||||
|
||||
Create subtasks for your reports:
|
||||
|
||||
```
|
||||
POST /api/companies/{companyId}/issues
|
||||
{ "title": "...", "assigneeAgentId": "...", "parentId": "...", "goalId": "..." }
|
||||
```
|
||||
|
||||
Always set `parentId` and `goalId` on subtasks.
|
||||
|
||||
### Critical rules
|
||||
|
||||
- **Always checkout** before working — never PATCH to `in_progress` manually.
|
||||
- **Never retry a 409** — the task belongs to someone else.
|
||||
- **Always comment** on in-progress work before exiting the heartbeat.
|
||||
- **Always set parentId** on subtasks.
|
||||
- **Never cancel cross-team tasks** — reassign to your manager.
|
||||
- **Escalate when stuck** — use your chain of command.
|
||||
@@ -0,0 +1,414 @@
|
||||
# Workspaces
|
||||
|
||||
When an agent picks up a task that involves working with code or files, it needs a place to do that work — a folder with the right code checked out at the right state, ready for the agent to read, edit, and run. That's what an execution workspace is: a snapshot of a project's working directory, tied to a specific task run.
|
||||
|
||||
Execution workspaces exist so that multiple agents can work on the same project simultaneously without stepping on each other. When a project is configured for isolated work, agents can get their own copy — their own branch, folder, and runtime context — so work in one workspace doesn't accidentally break another.
|
||||
|
||||
This guide covers what execution workspaces are, how they relate to projects, and how to use the workspace detail screen — its three tabs, runtime controls, and logs — to inspect and manage them.
|
||||
|
||||
---
|
||||
|
||||
## How execution workspaces relate to projects
|
||||
|
||||
Every execution workspace is linked to a project. The project defines the base configuration — where the code lives, how to run it, what the default setup looks like.
|
||||
|
||||
When a task runs, Paperclip resolves which execution workspace to use for that run:
|
||||
|
||||
1. **The heartbeat fires** and the agent picks up a task
|
||||
2. **Paperclip resolves the workspace** — creating a new one, reusing an existing one, or sticking with the project default, depending on your settings
|
||||
3. **The agent receives the workspace path** and works within it
|
||||
4. **The workspace persists** after the run if you're using an isolated or reusable workspace mode
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Workspace modes
|
||||
|
||||
When isolated workspaces are enabled for a project, you can choose how an issue's workspace is handled:
|
||||
|
||||
**Isolated (new workspace)**
|
||||
Paperclip creates a fresh working copy for this task — a new git worktree at a new path, branched from the base. The agent works here without any risk of interfering with other workspaces. When the task is done, you can review and merge the branch, then archive the workspace.
|
||||
|
||||
This is the right choice for tasks that make code changes — features, fixes, experiments.
|
||||
|
||||
**Reuse existing workspace**
|
||||
The task shares a workspace with another task or a previous run. Multiple tasks can share one workspace so they can work against the same branch and see the same running services.
|
||||
|
||||
This is useful when tasks are closely related and need to coordinate on the same state — for example, multiple issues working against the same feature branch.
|
||||
|
||||
**Project primary workspace**
|
||||
The task runs in the project's primary checkout, not an isolated copy. Use this carefully — changes made here affect the shared working copy directly.
|
||||
|
||||
---
|
||||
|
||||
## Runtime services
|
||||
|
||||
Each workspace can have runtime services: background processes that need to be running for the agent's work to make sense — a development server, a database, a build watcher.
|
||||
|
||||
Runtime services are manually controlled from the workspace UI. Paperclip does not start or stop them automatically when a heartbeat fires.
|
||||
|
||||
To start services for a workspace:
|
||||
1. Open your project and click on the workspace
|
||||
2. Click **Start Services** (or the specific service you want to start)
|
||||
3. The service runs in the background; the status updates in the UI
|
||||
|
||||
> **Note:** Services you start in one workspace don't affect other workspaces. Each isolated workspace has its own runtime, even if the configuration was inherited from the project's base settings.
|
||||
|
||||
---
|
||||
|
||||
## Workspace inheritance
|
||||
|
||||
When you create a project, you can define a base runtime configuration for it — how services should be started, what environment variables they need, what commands to run.
|
||||
|
||||
Isolated execution workspaces inherit this configuration by default. You can override specific settings on individual workspaces if a particular task needs different settings than the project default.
|
||||
|
||||
The inheritance answers the question "how do I run this?" — but the actual running process is always specific to the workspace. Two workspaces with the same inherited configuration will have two separate running processes.
|
||||
|
||||
---
|
||||
|
||||
## Workspace lifecycle
|
||||
|
||||
Execution workspaces are durable — they persist until you explicitly archive or tear them down. This means:
|
||||
|
||||
- The agent's branch state, uncommitted changes, and any running services survive across heartbeats
|
||||
- Multiple heartbeats can pick up the same workspace and continue from where the last one left off
|
||||
- Shared workspaces accumulate changes across multiple tasks
|
||||
|
||||
When you're done with a workspace:
|
||||
1. Review the agent's work (merge the branch, approve changes, etc.)
|
||||
2. Click **Archive** on the workspace
|
||||
3. Paperclip cleans up the workspace according to its mode and project settings
|
||||
|
||||
> **Warning:** Archiving a workspace that has unmerged changes or uncommitted work means that work is gone. Make sure you've reviewed and merged anything you want to keep before archiving.
|
||||
|
||||
---
|
||||
|
||||
## When you'll encounter this
|
||||
|
||||
For most board operators, execution workspaces are something that happens in the background. You don't need to configure them for every issue — the defaults work fine for straightforward agents doing straightforward work.
|
||||
|
||||
You'll want to pay closer attention when:
|
||||
- You have multiple agents working on the same codebase and need to avoid conflicts
|
||||
- An agent needs specific services running to do its work
|
||||
- You want to review an agent's changes on a branch before they're merged
|
||||
- A task needs to pick up from where a previous task left off
|
||||
|
||||
---
|
||||
|
||||
## Workspace list
|
||||
|
||||
All execution workspaces attached to a project are listed on that project's **Workspaces** tab. Open a project, click **Workspaces**, and you'll see every workspace Paperclip has provisioned — isolated worktrees, reused workspaces, and the project's primary checkout.
|
||||
|
||||
Each entry shows:
|
||||
|
||||
- The workspace **name** (and the issue it was originally provisioned for, if any)
|
||||
- The **mode** — isolated, reuse, or project primary
|
||||
- The **provider type** — for example, git worktree or local folder
|
||||
- The **status** — active, archived, or cleanup_failed
|
||||
- When it was opened and last used
|
||||
|
||||
Click a workspace name to open its detail screen. From there, you can inspect and edit its configuration, control its runtime services, review its operation logs, and see every issue that has run against it.
|
||||
|
||||

|
||||
|
||||
The header of the detail screen repeats the mode, provider type, and status as small pills so you can tell at a glance which kind of workspace you're looking at. Above the tabs, a **Workspace commands** panel shows the source of the workspace's runtime config — whether it's inheriting from the project workspace, overriding it at the execution workspace level, or has no runtime config defined yet. From that same panel you can start services, stop them, restart them, or fire off one-shot jobs.
|
||||
|
||||
The detail screen is split into three tabs:
|
||||
|
||||
1. **Configuration** — workspace settings, workspace context, and concrete location
|
||||
2. **Runtime logs** — history of runtime and cleanup operations
|
||||
3. **Issues** — every issue that has used this workspace
|
||||
|
||||
The tabs remember your last selection per workspace, so if you always land on Runtime logs for a specific workspace, Paperclip keeps taking you there.
|
||||
|
||||
---
|
||||
|
||||
## Configuration tab
|
||||
|
||||
The Configuration tab is where you inspect and edit everything that makes this workspace concrete — the path on disk, the repo and branch it points at, the provisioning and teardown commands, and any runtime overrides specific to this execution workspace.
|
||||
|
||||

|
||||
|
||||
### Workspace commands panel
|
||||
|
||||
Above the tabs, and visible regardless of which tab you're on, sits the **Workspace commands** panel. It's the single control surface for runtime behaviour: services you want running in the background, and one-shot jobs you want to fire off against this workspace.
|
||||
|
||||
Two rules govern this panel:
|
||||
|
||||
1. **You need a working directory.** Every command Paperclip runs against the workspace needs an absolute path to run in. If the workspace's `cwd` is empty, the action buttons are disabled and a hint reminds you to fix that first. Fill in the **Working directory** field on the Configuration tab, save, and the controls light up again.
|
||||
2. **Services need runtime config.** Service commands only show up if there's a runtime config somewhere in the chain — either inherited from the project workspace or overridden on this execution workspace. If neither exists, the services panel stays empty with a short note; jobs may still run if they're defined and a `cwd` is set.
|
||||
|
||||
The panel groups commands into two sections:
|
||||
|
||||
- **Services** — long-running processes like `pnpm dev`, a Postgres container, or a build watcher. For each service you'll see start, stop, and restart controls and the service's last observed status.
|
||||
- **Jobs** — one-shot commands like `pnpm db:migrate` or a test runner. Each job has a single run action.
|
||||
|
||||
When Paperclip is busy running an action (for example, starting a service), the panel shows a pending state and blocks duplicate actions. On success you'll see a short confirmation — "Workspace service started.", "Workspace job completed." — and on failure, the error message from the adapter appears in red. Either way, the full history ends up in the Runtime logs tab.
|
||||
|
||||
### Workspace settings
|
||||
|
||||
The **Workspace settings** card holds the editable fields for the workspace. Most of these are populated when Paperclip provisions the workspace; you usually only need to touch them when an agent or project has moved, a branch was renamed, or the provisioning scripts changed.
|
||||
|
||||
- **Workspace name** — a human label used in lists, breadcrumbs, and notifications.
|
||||
- **Branch name** — the git branch this workspace is checked out on. For isolated worktrees, this is typically something like `PAP-946-workspace`.
|
||||
- **Working directory** — the absolute path on disk. This is where the agent runs its commands. If this path is empty, Paperclip can't run any local commands for this workspace — it's the single most important field to keep correct.
|
||||
- **Provider path / ref** — the path or reference used by the workspace provider. For git worktrees this is the worktree path; for other providers it's whatever reference that provider needs.
|
||||
- **Repo URL** — the remote URL the working directory was cloned from. Used for linking out to GitHub/GitLab in the UI.
|
||||
- **Base ref** — the ref the workspace branched off from, for example `origin/main`.
|
||||
- **Provision command** — runs when Paperclip prepares this execution workspace (for example, a script that clones a worktree and installs dependencies).
|
||||
- **Teardown command** — runs when the execution workspace is archived or cleaned up.
|
||||
- **Cleanup command** — an optional workspace-specific cleanup step that runs before teardown (for example, killing a stuck `vite` process).
|
||||
|
||||
At the top right of this card you'll find a **Close workspace** button. Closing runs the teardown and cleanup commands, and transitions the workspace to `archived` or `cleanup_failed` depending on how those commands exit. If a previous close attempt left the workspace in `cleanup_failed`, the button changes to **Retry close**.
|
||||
|
||||
Below the command fields you'll see a **Runtime config source** notice. It tells you at a glance whether this execution workspace is:
|
||||
|
||||
- inheriting the runtime config from its project workspace,
|
||||
- overriding the project workspace config with its own, or
|
||||
- has no runtime config at all yet.
|
||||
|
||||
If this workspace is overriding the inherited runtime, you can click **Reset to inherit** to drop the override and go back to the project workspace default.
|
||||
|
||||
For the rare cases where you need to tweak the raw runtime JSON, expand the **Advanced runtime JSON** panel. It contains:
|
||||
|
||||
- A checkbox — **Inherit project workspace runtime config** — that toggles override vs. inherit mode.
|
||||
- A JSON textarea where you can edit the workspace's `commands` object. Both legacy `services` arrays and the newer `commands` array (supporting both services and jobs) are accepted.
|
||||
|
||||
Changes in the Workspace settings card stay local until you click **Save changes**. The **Reset** button reverts the form to whatever Paperclip has currently persisted, so you can back out of a half-edited change without reloading the page.
|
||||
|
||||
### Workspace context
|
||||
|
||||
The **Workspace context** card (labelled "Linked objects" in the UI) shows the other Paperclip entities this execution workspace is connected to:
|
||||
|
||||
- **Project** — the project this workspace belongs to.
|
||||
- **Project workspace** — the project-level workspace this execution workspace was provisioned from. Runtime config inheritance follows this link.
|
||||
- **Source issue** — the issue that originally triggered this workspace's creation, if any. Clicking it takes you to the issue detail page.
|
||||
- **Derived from** — if this workspace was created by splitting or branching off another execution workspace, you'll see a link back to the original. This is how Paperclip represents follow-up worktrees that share a code lineage with an earlier one.
|
||||
- **Workspace ID** — the raw identifier, shown in monospaced text and copyable for API calls or debugging.
|
||||
|
||||
Use this card to navigate quickly back to the project, the issue that kicked off the work, or the parent workspace this one derived from.
|
||||
|
||||
### Concrete location
|
||||
|
||||
The **Concrete location** card summarises the physical and git-level facts about the workspace. It doesn't introduce new fields — everything here also appears in Workspace settings — but it's formatted for fast scanning and copying rather than editing.
|
||||
|
||||
- **Working dir** — the absolute path, with a copy button so you can paste it into a terminal.
|
||||
- **Provider ref** — the provider-specific reference or worktree path.
|
||||
- **Repo URL** — when set to a valid `http(s)` URL, this becomes a clickable link out to the remote.
|
||||
- **Base ref** — the starting ref the workspace was branched from.
|
||||
- **Branch** — the active branch name.
|
||||
- **Opened** — timestamp when Paperclip first opened the workspace.
|
||||
- **Last used** — the most recent time an agent interacted with it.
|
||||
- **Cleanup** — if Paperclip has scheduled this workspace for cleanup, the eligible-at time and the reason are shown here. Otherwise this reads `Not scheduled`.
|
||||
|
||||
Together, Workspace settings, Workspace context, and Concrete location give you everything you need to answer "what is this workspace, where does it live, and what is it connected to?"
|
||||
|
||||
### Working with runtime config overrides
|
||||
|
||||
Runtime configuration is one of the places where project defaults and workspace-specific needs sometimes pull in different directions. Paperclip resolves this with a two-level inheritance model:
|
||||
|
||||
1. The **project workspace** defines a baseline runtime config. This is what gets applied to every execution workspace carved out of that project workspace unless you explicitly override it.
|
||||
2. The **execution workspace** can either inherit that baseline or override it with its own `workspaceRuntime` object.
|
||||
|
||||
The **Runtime config source** notice on the Configuration tab tells you which of these is currently in effect:
|
||||
|
||||
- **Execution workspace override** — this workspace has its own runtime config and ignores whatever the project workspace provides. Use this when a specific worktree needs a different port, a different dev command, or extra jobs that don't belong on every workspace.
|
||||
- **Project workspace default** — this workspace is inheriting the project's runtime config as-is. This is the common case and the one you should aim for — overrides add drift and should be the exception.
|
||||
- **None** — neither level has runtime config defined, so Paperclip can't start services. Jobs may still run if they're provided, but services won't appear in the Workspace commands panel.
|
||||
|
||||
If you've added an override and want to go back to inheriting, click **Reset to inherit** — it clears the override and the workspace falls back to the project workspace's config. If the project workspace has no runtime config of its own, that button is disabled; you'd otherwise be resetting the execution workspace to "None".
|
||||
|
||||
The **Advanced runtime JSON** panel is hidden behind a disclosure triangle on purpose. Editing the raw JSON is a power-user path; for most teams, configuring services at the project level is enough. When you do need to edit it, the panel accepts both shapes:
|
||||
|
||||
- The legacy `{ "services": [...] }` array, kept for backwards compatibility.
|
||||
- The newer `{ "commands": [...] }` array with `kind: "service"` or `kind: "job"` entries, which is what new workspaces should use.
|
||||
|
||||
Paperclip validates the JSON when you hit **Save changes**; if it can't be parsed or isn't a JSON object, you'll get an inline error and the save is aborted.
|
||||
|
||||
---
|
||||
|
||||
## Runtime logs tab
|
||||
|
||||
Switch to the Runtime logs tab to see what Paperclip has actually done with this workspace — which commands ran, when they ran, how they exited, and what they printed.
|
||||
|
||||

|
||||
|
||||
Under the **Recent operations** heading, each entry represents one workspace operation:
|
||||
|
||||
- The **command** that ran (or the **phase**, if this was a lifecycle step rather than a named command).
|
||||
- The **start time** and, if the operation has finished, the **end time**.
|
||||
- A short excerpt of **stdout** or, when the command failed, a short excerpt of **stderr**. The stderr excerpt is highlighted in red so you can spot failures at a glance.
|
||||
- A **status pill** on the right showing whether the operation is running, succeeded, or failed.
|
||||
|
||||
This tab covers both kinds of operations:
|
||||
|
||||
- **Runtime operations** — service starts, stops, restarts, and one-shot jobs you trigger from the Workspace commands panel at the top of the detail page.
|
||||
- **Cleanup operations** — provision, teardown, and cleanup command runs that happen when Paperclip prepares or closes the workspace.
|
||||
|
||||
If nothing has happened yet, you'll see "No workspace operations have been recorded yet." When the operation log fails to load, the error message from the server is shown in red.
|
||||
|
||||
Use this tab as your first stop when an agent reports that a service didn't come up, a job failed, or the workspace refused to close. Most of the time, the stderr excerpt is enough to diagnose the problem; the timestamps make it easy to correlate a failed heartbeat run with the exact command that broke.
|
||||
|
||||
### How operations map to what you did
|
||||
|
||||
If you're new to the Runtime logs tab, it helps to know which UI actions produce which operation entries:
|
||||
|
||||
- Clicking **Start** on a service in the Workspace commands panel creates an operation with the service's command, a `started` phase, and — on a successful start — a running or finished status depending on whether the service stays alive or exits immediately.
|
||||
- Clicking **Stop** or **Restart** on a service produces a short operation that records how the service was terminated or bounced.
|
||||
- Clicking **Run** on a job produces a single operation with the job's command; its final status reflects the job's exit code.
|
||||
- **Close workspace** (from the Configuration tab) runs the cleanup command and then the teardown command in sequence. Each appears as its own operation so you can tell which of the two failed if the close ends in `cleanup_failed`.
|
||||
- **Provision** operations are emitted automatically when Paperclip first prepares the workspace; you won't trigger these from the UI, but you'll see them here when debugging first-boot problems.
|
||||
|
||||
If nothing about a failure is obvious from the excerpt, remember that excerpts are truncated. Reach into the underlying worktree on disk, re-run the command manually from that directory, and compare its output to the excerpt — the mismatch usually points at an environment or permissions difference between the agent's runtime and your shell.
|
||||
|
||||
---
|
||||
|
||||
## Issues tab
|
||||
|
||||
The Issues tab shows every issue that has ever run against this execution workspace — not just the source issue that created it, but any follow-ups, retries, and related tasks that inherited the workspace.
|
||||
|
||||

|
||||
|
||||
The list uses the same components and filters as the main issues view, so you get:
|
||||
|
||||
- Issue identifier, title, status, priority, and assignee at a glance.
|
||||
- The **live-run indicator** — issues whose heartbeat is currently running against this workspace are highlighted so you can see what's active right now.
|
||||
- Inline actions for updating issue status, reassigning, and opening the issue detail page.
|
||||
- The view's local state — which columns are visible, how you've sorted — is remembered under the `paperclip:execution-workspace-issues-view` key, so different execution workspaces can have different preferred layouts.
|
||||
|
||||
This tab answers three common questions:
|
||||
|
||||
- "What work has this workspace actually been used for?" — scroll the list.
|
||||
- "Is anything running against this workspace right now?" — look for a live-run highlight.
|
||||
- "Can I safely archive this workspace?" — if every issue here is `done`, `cancelled`, or otherwise inactive, you're probably safe.
|
||||
|
||||
If an issue in the list needs to be reassigned or rescheduled before you can close the workspace, click through, make the change, and come back — the list updates automatically.
|
||||
|
||||
### Source issue vs. linked issues
|
||||
|
||||
Two different relationships show up on the Issues tab and in the Configuration tab's Workspace context card, and it's worth keeping them straight:
|
||||
|
||||
- The **source issue** is the single issue that originally caused Paperclip to provision this workspace. It's stored on the workspace itself and appears in the Workspace context card. For isolated worktrees created from a specific task, this will typically be the task that kicked the whole flow off.
|
||||
- The **linked issues** listed on the Issues tab are every issue that has ever been attached to this workspace, including the source issue, follow-up child issues that inherited the workspace, and any issue whose `inheritExecutionWorkspaceFromIssueId` pointed back here.
|
||||
|
||||
When you're deciding whether it's safe to archive, look at the full linked list, not just the source issue. An archived workspace takes all its branch state with it, and a follow-up issue that still references it will end up without a valid cwd the next time it runs.
|
||||
|
||||
---
|
||||
|
||||
## Archiving and cleanup
|
||||
|
||||
Closing a workspace is a two-step operation in Paperclip:
|
||||
|
||||
1. **Cleanup** — the cleanup command (if any) runs first. This is where you stop stray dev servers, free ports, and do anything that would otherwise prevent teardown from succeeding.
|
||||
2. **Teardown** — the teardown command then dismantles the workspace itself. For git worktrees this typically removes the worktree directory and prunes the branch; for other providers it does whatever that provider considers cleanup.
|
||||
|
||||
If both succeed, the workspace transitions to `archived` and disappears from the default workspace list. If either fails, the workspace transitions to `cleanup_failed` — it's still there, still visible, and the **Close workspace** button changes to **Retry close** so you can try again after fixing whatever went wrong. Until a retry succeeds, the workspace keeps whatever state it had before the close was attempted.
|
||||
|
||||
Some practical tips:
|
||||
|
||||
- Before closing, check the Runtime logs tab for anything still running. Services that are `running` will be stopped as part of cleanup, but jobs that are genuinely in-flight will also be killed — make sure you're not about to interrupt something important.
|
||||
- Review the **Issues tab** for any issue still in `in_progress` that's tied to this workspace. Archive the workspace only after those issues are done, cancelled, or moved to another workspace.
|
||||
- If an agent had uncommitted changes on the branch, those changes go away at teardown. Either merge the branch first or stash/commit anything you want to keep.
|
||||
|
||||
The **Cleanup** row on the Concrete location card tells you whether Paperclip has already scheduled the workspace for automatic cleanup — for example, because it's been idle past a retention window — and what the reason is. If you see a scheduled cleanup you didn't expect, look at the project's workspace lifecycle settings to understand why.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting checklist
|
||||
|
||||
When something doesn't behave, run through this checklist before filing a bug:
|
||||
|
||||
- **Is the `cwd` set and correct?** Without an absolute working directory, no local command can run. Configuration tab → Workspace settings → Working directory.
|
||||
- **Is the branch still there?** If someone deleted the branch outside Paperclip, the agent will fail when it tries to check out. Re-create or re-point the branch.
|
||||
- **Does the runtime config actually apply?** Look at Runtime config source on the Configuration tab. If it says "None", no service commands will be available.
|
||||
- **Is another workspace holding the port?** Two isolated worktrees on the same machine that both inherit a dev server config will both try to bind. Either override the port on one of them or only start one at a time.
|
||||
- **Did a previous close fail?** If the workspace is in `cleanup_failed`, you'll keep getting stale state until you either retry the close or manually fix the underlying problem and then retry.
|
||||
- **Is the agent looking at the right workspace?** When follow-up tasks are created, make sure either `parentId` or `inheritExecutionWorkspaceFromIssueId` is set on the child issue so Paperclip can resolve it back to this workspace.
|
||||
|
||||
Most workspace-level issues come down to one of these six questions.
|
||||
|
||||
---
|
||||
|
||||
## Isolation and git worktrees
|
||||
|
||||
Paperclip's default for "isolated" execution workspaces is a **git worktree** branched off the project workspace's base ref. If you haven't used git worktrees before, here's what that means in practice:
|
||||
|
||||
- A worktree is a second (or third, or tenth) checkout of the same repository, living at a different path, with its own branch checked out.
|
||||
- They share the same underlying object database with the primary checkout, so creating a worktree is cheap — no full re-clone, no duplicated history on disk.
|
||||
- Because each worktree has its own working directory, agents in different worktrees can't accidentally overwrite each other's uncommitted changes.
|
||||
|
||||
When Paperclip provisions an isolated workspace:
|
||||
|
||||
1. It picks a path under the project workspace's worktree directory.
|
||||
2. It creates a new branch off the configured base ref (for example, `origin/main`).
|
||||
3. It checks that branch out into the new worktree path.
|
||||
4. It runs the provision command so dependencies, env files, and any other per-worktree setup are in place before the agent starts.
|
||||
|
||||
From the agent's perspective, all of this is invisible — it just gets a `cwd` that's ready to read, edit, and run. From your perspective as a board operator, the Concrete location card on the Configuration tab shows exactly which path and branch were picked, so you can always drop into that directory yourself to inspect or debug.
|
||||
|
||||
### When isolation is overkill
|
||||
|
||||
Isolation has a cost: each worktree needs its own `node_modules` (or equivalent), its own dev server, its own migrations. For large projects this adds up. You probably don't want isolated workspaces for:
|
||||
|
||||
- **Purely read-only tasks** — research, summarising, reviewing documentation. Use the project primary workspace or a reused workspace.
|
||||
- **Tasks that only touch issue metadata** — triaging, estimating, assigning. These don't need a checkout at all.
|
||||
- **Coordination between multiple tasks on the same feature** — reuse a single workspace and let the tasks accumulate changes together.
|
||||
|
||||
Isolation pays off when agents are genuinely making code changes you'll want to review separately, or when you want to run several experiments in parallel without them tripping over each other.
|
||||
|
||||
---
|
||||
|
||||
## Observability at a glance
|
||||
|
||||
Pulling together the three tabs, here's how the detail screen maps to typical questions you'll ask:
|
||||
|
||||
- "What is this workspace and where does it live?" → Configuration tab, the Concrete location card.
|
||||
- "Why does this workspace exist and what's it attached to?" → Configuration tab, the Workspace context card.
|
||||
- "Who decides how services run here?" → Configuration tab, the Runtime config source notice.
|
||||
- "What has actually happened in this workspace?" → Runtime logs tab.
|
||||
- "Who's been running against this workspace?" → Issues tab.
|
||||
- "Is anything running right now?" → Workspace commands panel at the top (service status) plus Issues tab (live-run indicator).
|
||||
|
||||
The breadcrumbs at the top of the screen also tie everything back together: **Projects → {project name} → Workspaces → {workspace name}**. From any workspace detail screen, you're always one click from the project and two from the workspace list.
|
||||
|
||||
---
|
||||
|
||||
## Common workflows
|
||||
|
||||
A few concrete patterns show up often enough to be worth calling out:
|
||||
|
||||
**Kicking off a new feature**
|
||||
1. Create the issue and assign it to the agent who'll do the work.
|
||||
2. Let Paperclip provision an isolated workspace when the agent checks out the task.
|
||||
3. When the agent finishes and the branch is ready, review the branch in your usual git workflow.
|
||||
4. Once merged, come back to the Workspaces tab and archive the workspace.
|
||||
|
||||
**Recovering from a failed close**
|
||||
1. Open the workspace and look at the Runtime logs tab to see which command failed.
|
||||
2. Fix the underlying problem — a stuck process, a missing permission, a command that assumes the wrong shell.
|
||||
3. Click **Retry close** and confirm the workspace transitions to `archived`.
|
||||
4. If retries keep failing, drop into the worktree directory yourself and run the teardown command manually.
|
||||
|
||||
**Shipping a migration**
|
||||
1. Create a job in the workspace's runtime config (for example, `db:migrate`).
|
||||
2. Open the workspace and click **Run** on that job from the Workspace commands panel.
|
||||
3. Wait for the operation to appear in Runtime logs with a successful status.
|
||||
4. Link the resulting evidence to the issue — the operation timestamp is enough.
|
||||
|
||||
**Reusing a workspace across tasks**
|
||||
1. When creating the follow-up issue, set `parentId` if it's a child task, or `inheritExecutionWorkspaceFromIssueId` if it's a sibling that should share the same workspace.
|
||||
2. Paperclip will wire the new issue up to the existing workspace on checkout.
|
||||
3. On the Issues tab of the workspace, you'll see the new issue alongside the original.
|
||||
|
||||
These patterns are just defaults — adapt them to how your team actually operates.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Execution workspaces give agents the isolated, stateful environments they need to do reliable file-based work. The Workspace list shows you everything provisioned for a project; the Configuration tab keeps the path, repo, and runtime config in sync with reality; the Runtime logs tab tells you what actually ran; and the Issues tab shows you who's been using it.
|
||||
|
||||
The next guide covers heartbeats and routines — how you decide *when* an agent runs, and why most agents should stay dormant until real work arrives.
|
||||
|
||||
[Heartbeats & Routines →](./routines.md)
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
A quick reference for every term used in Paperclip's UI and documentation, in plain English. If you hit a word in a guide that isn't clicking, look it up here.
|
||||
|
||||
For deeper explanations of the core concepts, see [Key Concepts](key-concepts.md).
|
||||
For deeper explanations of the core concepts, see [Key Concepts](./key-concepts.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -108,4 +108,4 @@ For deeper explanations of the core concepts, see [Key Concepts](key-concepts.md
|
||||
|
||||
---
|
||||
|
||||
[Go to Installation →](installation.md)
|
||||
[Go to Installation →](../getting-started/installation.md)
|
||||
@@ -38,7 +38,7 @@ Every single thing in Paperclip — every agent, every task, every dollar spent
|
||||
|
||||
Agents are organised in a strict hierarchy. Every agent reports to exactly one manager. The CEO is the only agent with no manager — it reports directly to you, the board.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Note:** The CEO agent is always the first agent you create. It's the entry point — once the CEO is running, it creates tasks and can request to hire its own reports.
|
||||
|
||||
@@ -65,7 +65,7 @@ backlog → todo → in_progress → in_review → done
|
||||
|
||||
They can also be marked `blocked` if an agent is waiting on something, or `cancelled` if the work is no longer needed.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Tip:** When an agent picks up a task, it "checks it out" — claiming exclusive ownership. This prevents two agents from working on the same thing simultaneously.
|
||||
|
||||
@@ -93,7 +93,7 @@ A heartbeat can be triggered by:
|
||||
- **Manual invoke** — you click **Run Heartbeat** on the agent detail page
|
||||
- **Approval resolution** — a pending approval the agent submitted is approved or rejected
|
||||
|
||||

|
||||

|
||||
|
||||
---
|
||||
|
||||
@@ -122,7 +122,7 @@ Available adapter types:
|
||||
| `pi_local` | Runs a Pi agent locally |
|
||||
| `hermes_local` | Runs a Hermes Agent locally with persistent memory and 30+ tools |
|
||||
|
||||
A few additional adapter types (`openclaw_gateway`, `process`, `http`) exist in the runtime but are currently marked **"Coming soon"** in the agent-config dropdown. See [Adapters Overview](../../adapters/overview.md) for the full list.
|
||||
A few additional adapter types (`openclaw_gateway`, `process`, `http`) exist in the runtime but are currently marked **"Coming soon"** in the agent-config dropdown. See [Adapters Overview](../../reference/adapters/overview.md) for the full list.
|
||||
|
||||
For most people getting started, `claude_local` is the right choice.
|
||||
|
||||
@@ -146,7 +146,7 @@ Budget behaviour:
|
||||
- At **80% utilisation**: Paperclip shows a warning so you can intervene before the hard stop
|
||||
- At **100% utilisation**: the agent is automatically paused and no more heartbeats fire
|
||||
|
||||

|
||||

|
||||
|
||||
> **Warning:** The company budget and the per-agent budgets are separate. An agent can be paused by its own budget limit even if the company has remaining budget. Set both carefully.
|
||||
|
||||
@@ -173,7 +173,7 @@ The first two approval types most beginners encounter are:
|
||||
|
||||
Paperclip can also surface budget override approvals when a hard stop is hit, and other board-level approval requests in more advanced flows.
|
||||
|
||||

|
||||

|
||||
|
||||
> **Tip:** Approvals don't expire on their own. If an approval is pending, agents that are waiting on it will pause at that point until you take action. Keep an eye on your approval queue.
|
||||
|
||||
@@ -181,4 +181,4 @@ Paperclip can also surface budget override approvals when a hard stop is hit, an
|
||||
|
||||
You now have the mental model for everything Paperclip does. When something in the UI seems unfamiliar, come back here.
|
||||
|
||||
[Go to the Glossary →](glossary.md)
|
||||
[Go to the Glossary →](./glossary.md)
|
||||
+2
-2
@@ -30,7 +30,7 @@ The agents can be powered by Claude, OpenAI, or other AI systems. Paperclip itse
|
||||
|
||||
Once your company is running, this is what you'll see:
|
||||
|
||||

|
||||

|
||||
|
||||
The dashboard is your control centre. At a glance you can see which agents are active, how many tasks are in progress, how much budget has been spent this month, and what happened recently across the whole company.
|
||||
|
||||
@@ -71,4 +71,4 @@ The AI itself lives in the agents you configure (Claude, Codex, or others). Pape
|
||||
|
||||
The next guide walks you through installing Paperclip — either via the Desktop app (no terminal needed) or the command line.
|
||||
|
||||
[Go to Installation →](installation.md)
|
||||
[Go to Installation →](../getting-started/installation.md)
|
||||
@@ -189,5 +189,5 @@ That is a valid choice for simple adapters and command wrappers.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The agent runs on another machine or behind a webhook. Use [HTTP](http.md) instead.
|
||||
- You only need a one-shot script or command. Use [Process](process.md).
|
||||
- The agent runs on another machine or behind a webhook. Use [HTTP](./http.md) instead.
|
||||
- You only need a one-shot script or command. Use [Process](./process.md).
|
||||
- Claude Code is not installed or is not available on `PATH`.
|
||||
|
||||
---
|
||||
@@ -114,6 +114,6 @@ If the test fails, fix the command, path, or auth signal before trying again.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime lives behind a webhook or remote API. Use [HTTP](http.md).
|
||||
- You only need a shell command or script. Use [Process](process.md).
|
||||
- The runtime lives behind a webhook or remote API. Use [HTTP](./http.md).
|
||||
- You only need a shell command or script. Use [Process](./process.md).
|
||||
- Codex CLI is not installed or cannot be executed from the Paperclip host.
|
||||
|
||||
---
|
||||
@@ -127,6 +127,6 @@ If the probe fails, fix the command or auth path before saving the adapter.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
+5
-5
@@ -1,6 +1,6 @@
|
||||
# Creating an Adapter
|
||||
|
||||
Build a custom adapter when the built-in local adapters do not fit your runtime. This page covers the code shape and runtime contracts; if you are packaging an installable plugin, read [External Adapters](external-adapters.md) first.
|
||||
Build a custom adapter when the built-in local adapters do not fit your runtime. This page covers the code shape and runtime contracts; if you are packaging an installable plugin, read [External Adapters](./external-adapters.md) first.
|
||||
|
||||
> **Tip:** If you are using Claude Code to scaffold the adapter, the `.agents/skills/create-agent-adapter` skill can walk you through the same structure interactively.
|
||||
|
||||
@@ -177,7 +177,7 @@ If your adapter needs richer transcript rendering than the generic shell parser,
|
||||
|
||||
See:
|
||||
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
|
||||
For built-in adapters, the UI parser can live inside Paperclip source. For external adapters, it must be standalone and browser-safe.
|
||||
|
||||
@@ -206,6 +206,6 @@ External adapters register themselves through the plugin loader instead.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [How Agents Work](../guides/agent-developer/how-agents-work.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [How Agents Work](../../guides/org/agents.md)
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- You need webhook-style external invocation. Use [OpenClaw Gateway](openclaw-gateway.md) or [HTTP](http.md).
|
||||
- You only need one-shot shell commands. Use [Process](process.md).
|
||||
- You need webhook-style external invocation. Use [OpenClaw Gateway](./openclaw-gateway.md) or [HTTP](./http.md).
|
||||
- You only need one-shot shell commands. Use [Process](./process.md).
|
||||
- Cursor Agent CLI is not installed or not available on `PATH`.
|
||||
|
||||
---
|
||||
@@ -88,5 +88,5 @@ grok, kimi-k2.5
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
@@ -13,7 +13,7 @@ External adapters let you ship a Paperclip adapter as its own npm package or loc
|
||||
## When Not To Use
|
||||
|
||||
- The adapter should ship with Paperclip itself. Use a built-in adapter.
|
||||
- You only need a one-off local command. Use [Process](process.md).
|
||||
- You only need a one-off local command. Use [Process](./process.md).
|
||||
- The runtime is already covered by the built-in local adapters.
|
||||
|
||||
---
|
||||
@@ -138,7 +138,7 @@ If your adapter emits structured stdout, ship `./ui-parser` as a self-contained
|
||||
|
||||
See:
|
||||
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
|
||||
The UI parser is optional, but it is the difference between raw log text and a readable run transcript.
|
||||
|
||||
@@ -185,7 +185,7 @@ Useful management endpoints:
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Claude Local](claude-local.md)
|
||||
- [Codex Local](codex-local.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [Claude Local](./claude-local.md)
|
||||
- [Codex Local](./codex-local.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The agent runs behind a webhook or remote API. Use [HTTP](http.md).
|
||||
- You only need a command runner or script. Use [Process](process.md).
|
||||
- The agent runs behind a webhook or remote API. Use [HTTP](./http.md).
|
||||
- You only need a command runner or script. Use [Process](./process.md).
|
||||
- Gemini CLI is not installed or cannot reach the target working directory.
|
||||
|
||||
---
|
||||
@@ -102,6 +102,6 @@ The test also detects auth and quota failures, so a passing install check does n
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](openclaw-gateway.md) or [HTTP](http.md).
|
||||
- You only need a one-shot shell command. Use [Process](process.md).
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](./openclaw-gateway.md) or [HTTP](./http.md).
|
||||
- You only need a one-shot shell command. Use [Process](./process.md).
|
||||
- Hermes Agent is not installed on the machine (requires Python 3.10+).
|
||||
|
||||
---
|
||||
@@ -154,5 +154,5 @@ The adapter spawns Hermes Agent in single-query mode (`hermes chat -q ...`). Her
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
@@ -14,7 +14,7 @@ The `http` adapter sends a JSON webhook request to a service you control. Use it
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime is just a local script or command. Use [Process](process.md).
|
||||
- The runtime is just a local script or command. Use [Process](./process.md).
|
||||
- You need session persistence or built-in CLI behavior. Use one of the local adapters instead.
|
||||
- You need Paperclip to parse a rich stdout transcript from the remote runtime.
|
||||
|
||||
@@ -106,5 +106,5 @@ If the probe fails in a private network, that can still be acceptable. The impor
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The remote agent doesn't speak the OpenClaw Gateway protocol. Use [HTTP](http.md).
|
||||
- You want to run a headless local CLI agent on the same machine. Use [Claude Local](claude-local.md), [Codex Local](codex-local.md), or [Process](process.md).
|
||||
- The remote agent doesn't speak the OpenClaw Gateway protocol. Use [HTTP](./http.md).
|
||||
- You want to run a headless local CLI agent on the same machine. Use [Claude Local](./claude-local.md), [Codex Local](./codex-local.md), or [Process](./process.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -217,5 +217,5 @@ When you add OpenClaw to a Paperclip company:
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](openclaw-gateway.md) or [HTTP](http.md).
|
||||
- You only need a one-shot shell command or script. Use [Process](process.md).
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](./openclaw-gateway.md) or [HTTP](./http.md).
|
||||
- You only need a one-shot shell command or script. Use [Process](./process.md).
|
||||
- OpenCode CLI is not installed on the machine.
|
||||
|
||||
---
|
||||
@@ -98,5 +98,5 @@ Run `opencode models` for the authoritative list on your machine.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
@@ -22,19 +22,19 @@ Every adapter is responsible for the same core jobs:
|
||||
|
||||
| Use case | Start here |
|
||||
|---|---|
|
||||
| Claude Code on your machine | [Claude Local](claude-local.md) |
|
||||
| OpenAI Codex CLI on your machine | [Codex Local](codex-local.md) |
|
||||
| Gemini CLI on your machine | [Gemini Local](gemini-local.md) |
|
||||
| Cursor Agent CLI on your machine | [Cursor Local](cursor-local.md) |
|
||||
| OpenCode CLI with provider/model routing | [OpenCode Local](opencode-local.md) |
|
||||
| Pi CLI with its built-in tool set | [Pi Local](pi-local.md) |
|
||||
| Hermes Agent with persistent memory and 30+ tools | [Hermes Local](hermes-local.md) |
|
||||
| OpenClaw over a WebSocket gateway | [OpenClaw Gateway](openclaw-gateway.md) |
|
||||
| A custom shell command or script | [Process](process.md) |
|
||||
| A webhook or cloud service you control | [HTTP](http.md) |
|
||||
| A standalone npm package or local plugin | [External Adapters](external-adapters.md) |
|
||||
| Writing a new adapter package from scratch | [Creating an Adapter](creating-an-adapter.md) |
|
||||
| Building a custom run-log parser | [Adapter UI Parser Contract](adapter-ui-parser.md) |
|
||||
| Claude Code on your machine | [Claude Local](./claude-local.md) |
|
||||
| OpenAI Codex CLI on your machine | [Codex Local](./codex-local.md) |
|
||||
| Gemini CLI on your machine | [Gemini Local](./gemini-local.md) |
|
||||
| Cursor Agent CLI on your machine | [Cursor Local](./cursor-local.md) |
|
||||
| OpenCode CLI with provider/model routing | [OpenCode Local](./opencode-local.md) |
|
||||
| Pi CLI with its built-in tool set | [Pi Local](./pi-local.md) |
|
||||
| Hermes Agent with persistent memory and 30+ tools | [Hermes Local](./hermes-local.md) |
|
||||
| OpenClaw over a WebSocket gateway | [OpenClaw Gateway](./openclaw-gateway.md) |
|
||||
| A custom shell command or script | [Process](./process.md) |
|
||||
| A webhook or cloud service you control | [HTTP](./http.md) |
|
||||
| A standalone npm package or local plugin | [External Adapters](./external-adapters.md) |
|
||||
| Writing a new adapter package from scratch | [Creating an Adapter](./creating-an-adapter.md) |
|
||||
| Building a custom run-log parser | [Adapter UI Parser Contract](./adapter-ui-parser.md) |
|
||||
|
||||
If you are starting from scratch, the most common path is:
|
||||
|
||||
@@ -51,16 +51,16 @@ These adapters ship with Paperclip and are always available in the host:
|
||||
|
||||
| Adapter | Type key | UI availability | Best for |
|
||||
|---|---|---|---|
|
||||
| [Claude Local](claude-local.md) | `claude_local` | Selectable (recommended) | Claude Code runs with session persistence, skills sync, and structured transcript parsing. |
|
||||
| [Codex Local](codex-local.md) | `codex_local` | Selectable (recommended) | Codex CLI runs with session persistence and managed `CODEX_HOME`. |
|
||||
| [Gemini Local](gemini-local.md) | `gemini_local` | Selectable | Gemini CLI runs with resume support and local skills sync. |
|
||||
| [Cursor Local](cursor-local.md) | `cursor` | Selectable | Cursor Agent CLI runs with `--resume` session continuity and structured stream output. |
|
||||
| [OpenCode Local](opencode-local.md) | `opencode_local` | Selectable | OpenCode CLI runs with provider/model routing and `--session` resume. |
|
||||
| [Pi Local](pi-local.md) | `pi_local` | Selectable | Pi CLI runs with its built-in tool set and provider/model routing. |
|
||||
| [Hermes Local](hermes-local.md) | `hermes_local` | Selectable | Hermes Agent runs with persistent memory, 30+ tools, 80+ skills, and multi-provider routing. |
|
||||
| [OpenClaw Gateway](openclaw-gateway.md) | `openclaw_gateway` | **Coming soon** (use OpenClaw invite flow) | Remote OpenClaw instances reached over the WebSocket gateway protocol. |
|
||||
| [Process](process.md) | `process` | **Coming soon** (API / import only) | Shell commands, scripts, and custom local runtimes. |
|
||||
| [HTTP](http.md) | `http` | **Coming soon** (API / import only) | Webhook-style invocation into your own service. |
|
||||
| [Claude Local](./claude-local.md) | `claude_local` | Selectable (recommended) | Claude Code runs with session persistence, skills sync, and structured transcript parsing. |
|
||||
| [Codex Local](./codex-local.md) | `codex_local` | Selectable (recommended) | Codex CLI runs with session persistence and managed `CODEX_HOME`. |
|
||||
| [Gemini Local](./gemini-local.md) | `gemini_local` | Selectable | Gemini CLI runs with resume support and local skills sync. |
|
||||
| [Cursor Local](./cursor-local.md) | `cursor` | Selectable | Cursor Agent CLI runs with `--resume` session continuity and structured stream output. |
|
||||
| [OpenCode Local](./opencode-local.md) | `opencode_local` | Selectable | OpenCode CLI runs with provider/model routing and `--session` resume. |
|
||||
| [Pi Local](./pi-local.md) | `pi_local` | Selectable | Pi CLI runs with its built-in tool set and provider/model routing. |
|
||||
| [Hermes Local](./hermes-local.md) | `hermes_local` | Selectable | Hermes Agent runs with persistent memory, 30+ tools, 80+ skills, and multi-provider routing. |
|
||||
| [OpenClaw Gateway](./openclaw-gateway.md) | `openclaw_gateway` | **Coming soon** (use OpenClaw invite flow) | Remote OpenClaw instances reached over the WebSocket gateway protocol. |
|
||||
| [Process](./process.md) | `process` | **Coming soon** (API / import only) | Shell commands, scripts, and custom local runtimes. |
|
||||
| [HTTP](./http.md) | `http` | **Coming soon** (API / import only) | Webhook-style invocation into your own service. |
|
||||
|
||||
> **Info:** The agent-config adapter-type dropdown currently marks `openclaw_gateway`, `process`, and `http` as **"Coming soon"**. They're fully functional in the runtime — they just can't be picked manually from the UI yet. Configure them via the API or an imported company export until direct UI selection lands.
|
||||
|
||||
@@ -74,9 +74,9 @@ Install them from the Board UI or via `POST /api/adapters/install`.
|
||||
|
||||
See:
|
||||
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
|
||||
---
|
||||
|
||||
@@ -97,14 +97,14 @@ See:
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Claude Local](claude-local.md)
|
||||
- [Codex Local](codex-local.md)
|
||||
- [Gemini Local](gemini-local.md)
|
||||
- [Cursor Local](cursor-local.md)
|
||||
- [OpenCode Local](opencode-local.md)
|
||||
- [Pi Local](pi-local.md)
|
||||
- [Hermes Local](hermes-local.md)
|
||||
- [OpenClaw Gateway](openclaw-gateway.md)
|
||||
- [Process](process.md)
|
||||
- [HTTP](http.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Claude Local](./claude-local.md)
|
||||
- [Codex Local](./codex-local.md)
|
||||
- [Gemini Local](./gemini-local.md)
|
||||
- [Cursor Local](./cursor-local.md)
|
||||
- [OpenCode Local](./opencode-local.md)
|
||||
- [Pi Local](./pi-local.md)
|
||||
- [Hermes Local](./hermes-local.md)
|
||||
- [OpenClaw Gateway](./openclaw-gateway.md)
|
||||
- [Process](./process.md)
|
||||
- [HTTP](./http.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
@@ -13,8 +13,8 @@
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](openclaw-gateway.md) or [HTTP](http.md).
|
||||
- You only need a one-shot shell command. Use [Process](process.md).
|
||||
- The runtime lives behind a webhook or API. Use [OpenClaw Gateway](./openclaw-gateway.md) or [HTTP](./http.md).
|
||||
- You only need a one-shot shell command. Use [Process](./process.md).
|
||||
- Pi CLI is not installed on the machine.
|
||||
|
||||
---
|
||||
@@ -78,5 +78,5 @@ Pi sessions are stored under `~/.pi/paperclips/` and resumed with `--session` on
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](adapter-ui-parser.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [Adapter UI Parser Contract](./adapter-ui-parser.md)
|
||||
@@ -14,7 +14,7 @@ The `process` adapter runs an arbitrary command on the same machine as Paperclip
|
||||
|
||||
## When Not To Use
|
||||
|
||||
- The runtime is a long-lived service or remote webhook. Use [HTTP](http.md) instead.
|
||||
- The runtime is a long-lived service or remote webhook. Use [HTTP](./http.md) instead.
|
||||
- You need built-in session persistence or a vendor-specific CLI integration. Use one of the local adapters instead.
|
||||
- You need a richer transcript parser than plain stdout/stderr capture.
|
||||
|
||||
@@ -93,5 +93,5 @@ If the command cannot be found, the adapter will not run.
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Creating an Adapter](creating-an-adapter.md)
|
||||
- [External Adapters](external-adapters.md)
|
||||
- [Creating an Adapter](./creating-an-adapter.md)
|
||||
- [External Adapters](./external-adapters.md)
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -216,16 +216,16 @@ company = requests.get(
|
||||
|
||||
Use this page for the rules that apply everywhere. Then jump to the resource-specific page for the actual endpoint shapes, payloads, and examples:
|
||||
|
||||
- [Companies](companies.md)
|
||||
- [Agents](agents.md)
|
||||
- [Issues](issues.md)
|
||||
- [Approvals](approvals.md)
|
||||
- [Goals and Projects](goals-and-projects.md)
|
||||
- [Costs](costs.md)
|
||||
- [Secrets](secrets.md)
|
||||
- [Activity](activity.md)
|
||||
- [Dashboard](dashboard.md)
|
||||
- [Routines](routines.md)
|
||||
- [Companies](./companies.md)
|
||||
- [Agents](./agents.md)
|
||||
- [Issues](./issues.md)
|
||||
- [Approvals](./approvals.md)
|
||||
- [Goals and Projects](./goals-and-projects.md)
|
||||
- [Costs](./costs.md)
|
||||
- [Secrets](./secrets.md)
|
||||
- [Activity](./activity.md)
|
||||
- [Dashboard](./dashboard.md)
|
||||
- [Routines](./routines.md)
|
||||
|
||||
If you are building against the API from code, the safest mental model is:
|
||||
|
||||
File renamed without changes.
File renamed without changes.
@@ -148,6 +148,6 @@ This is most useful for debugging or for scripted agent workflows where you want
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Setup Commands](setup-commands.md) if you need to repair or reconfigure the local instance before using the control plane
|
||||
- [Managing Tasks](../user-guides/guides/managing-tasks.md) for the UI workflow that mirrors the `issue` commands
|
||||
- [Approvals](../user-guides/guides/approvals.md) for the board review flow behind the approval commands
|
||||
- [Setup Commands](./setup-commands.md) if you need to repair or reconfigure the local instance before using the control plane
|
||||
- [Managing Tasks](../../guides/day-to-day/issues.md) for the UI workflow that mirrors the `issue` commands
|
||||
- [Approvals](../../guides/day-to-day/approvals.md) for the board review flow behind the approval commands
|
||||
@@ -15,8 +15,8 @@ Use it when you want to:
|
||||
|
||||
The CLI is split into two practical layers:
|
||||
|
||||
1. [Setup commands](setup-commands.md) handle onboarding, diagnostics, and local instance configuration.
|
||||
2. [Control-plane commands](control-plane-commands.md) handle companies, issues, agents, approvals, activity, dashboards, and heartbeats.
|
||||
1. [Setup commands](./setup-commands.md) handle onboarding, diagnostics, and local instance configuration.
|
||||
2. [Control-plane commands](./control-plane-commands.md) handle companies, issues, agents, approvals, activity, dashboards, and heartbeats.
|
||||
|
||||
> **Tip:** If you are starting from scratch, read the setup section first. If Paperclip is already running and you just want to manage work, jump straight to the control-plane section.
|
||||
|
||||
@@ -82,6 +82,6 @@ If you already have a running instance, use the control-plane commands instead o
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [Setup Commands](setup-commands.md) for onboarding, repair, and local configuration
|
||||
- [Control-Plane Commands](control-plane-commands.md) for company operations from the terminal
|
||||
- [Setup Commands](./setup-commands.md) for onboarding, repair, and local configuration
|
||||
- [Control-Plane Commands](./control-plane-commands.md) for company operations from the terminal
|
||||
- [Deployment Overview](../deploy/overview.md) if you need to choose between local, private, and public deployment modes
|
||||
@@ -18,7 +18,7 @@ Use setup commands when you need to:
|
||||
- change deployment mode, storage provider, or secrets settings
|
||||
- allow a private hostname in authenticated/private mode
|
||||
|
||||
Do not use them for task, agent, or approval management. Those belong to [Control-Plane Commands](control-plane-commands.md).
|
||||
Do not use them for task, agent, or approval management. Those belong to [Control-Plane Commands](./control-plane-commands.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -175,5 +175,5 @@ PAPERCLIP_HOME=/custom/home PAPERCLIP_INSTANCE_ID=dev pnpm paperclipai run
|
||||
|
||||
## Next Step
|
||||
|
||||
- [Control-Plane Commands](control-plane-commands.md) for issues, agents, companies, approvals, and activity
|
||||
- [Control-Plane Commands](./control-plane-commands.md) for issues, agents, companies, approvals, and activity
|
||||
- [Deployment Overview](../deploy/overview.md) for the runtime modes that shape setup behavior
|
||||
@@ -43,7 +43,7 @@ Reset the local database by deleting the data directory:
|
||||
rm -rf ~/.paperclip/instances/default/db
|
||||
```
|
||||
|
||||
The Docker quickstart uses embedded PostgreSQL by default too. See [Docker](docker.md) for the container setup.
|
||||
The Docker quickstart uses embedded PostgreSQL by default too. See [Docker](./docker.md) for the container setup.
|
||||
|
||||
---
|
||||
|
||||
@@ -80,7 +80,7 @@ If you need to allow a custom hostname, add it explicitly:
|
||||
pnpm paperclipai allowed-hostname my-machine.tailnet.ts.net
|
||||
```
|
||||
|
||||
See [Tailscale Private Access](tailscale-private-access.md) for the private-network workflow.
|
||||
See [Tailscale Private Access](./tailscale-private-access.md) for the private-network workflow.
|
||||
|
||||
### `authenticated` + `public`
|
||||
|
||||
File renamed without changes.
+1
-1
@@ -57,7 +57,7 @@ Related deployment variables:
|
||||
| `PAPERCLIP_SECRETS_MASTER_KEY_FILE` | Path to the local key file |
|
||||
| `PAPERCLIP_SECRETS_STRICT_MODE` | Require secret refs for sensitive env vars |
|
||||
|
||||
These values are covered in more detail in [Secrets](secrets.md).
|
||||
These values are covered in more detail in [Secrets](./secrets.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -27,7 +27,7 @@ This starts:
|
||||
- the API server at `http://localhost:3100`
|
||||
- the UI from the API server in dev middleware mode
|
||||
|
||||
The first start automatically uses embedded PostgreSQL if `DATABASE_URL` is unset. See [Database](database.md) for the storage modes behind that behavior.
|
||||
The first start automatically uses embedded PostgreSQL if `DATABASE_URL` is unset. See [Database](./database.md) for the storage modes behind that behavior.
|
||||
|
||||
---
|
||||
|
||||
@@ -84,7 +84,7 @@ If you need to allow additional private hostnames:
|
||||
pnpm paperclipai allowed-hostname dotta-macbook-pro
|
||||
```
|
||||
|
||||
See [Tailscale Private Access](tailscale-private-access.md) for the full workflow.
|
||||
See [Tailscale Private Access](./tailscale-private-access.md) for the full workflow.
|
||||
|
||||
---
|
||||
|
||||
@@ -61,17 +61,17 @@ pnpm paperclipai configure --section server
|
||||
|
||||
## Start Here
|
||||
|
||||
If you want the shortest path to a working instance, read [Local Development](local-development.md) first.
|
||||
If you want the shortest path to a working instance, read [Local Development](./local-development.md) first.
|
||||
|
||||
If you are deciding how to expose the instance, read [Deployment Modes](deployment-modes.md) next.
|
||||
If you are deciding how to expose the instance, read [Deployment Modes](./deployment-modes.md) next.
|
||||
|
||||
If you already know the shape and only need the operational details, jump to:
|
||||
|
||||
- [Database](database.md)
|
||||
- [Storage](storage.md)
|
||||
- [Secrets](secrets.md)
|
||||
- [Environment Variables](environment-variables.md)
|
||||
- [Docker](docker.md)
|
||||
- [Tailscale Private Access](tailscale-private-access.md)
|
||||
- [Database](./database.md)
|
||||
- [Storage](./storage.md)
|
||||
- [Secrets](./secrets.md)
|
||||
- [Environment Variables](./environment-variables.md)
|
||||
- [Docker](./docker.md)
|
||||
- [Tailscale Private Access](./tailscale-private-access.md)
|
||||
|
||||
> **Tip:** Keep the deployment pages grouped together in the docs nav. They are easiest to understand when read as one flow.
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,167 +0,0 @@
|
||||
# Costs & Budgets
|
||||
|
||||
> **Warning:** AI agents make real API calls that cost real money. Every time an agent works — every heartbeat, every task, every comment it reads and writes — it sends and receives text through a provider like Anthropic or OpenAI, which charges you per token (roughly per word). This isn't a Paperclip fee; it's a cost you pay directly to the AI provider. Read this guide before your agents start running in earnest.
|
||||
|
||||
Paperclip's budget system exists to make sure you're never surprised. You set limits, and the platform enforces them automatically — an agent won't spend a cent beyond what you allow.
|
||||
|
||||
---
|
||||
|
||||
## How Costs Work
|
||||
|
||||
Every time an agent runs a heartbeat, it generates an API call. That call sends the agent's context (its identity, its tasks, the instructions it's working from) to the AI provider, and receives a response (the agent's reasoning and next actions). The provider charges based on the number of tokens — input tokens (what's sent) and output tokens (what comes back).
|
||||
|
||||
Paperclip records every one of these calls: which agent made it, which model it used, how many tokens were used, and the exact dollar cost. These records are aggregated per agent per calendar month (resetting on the 1st of each UTC month).
|
||||
|
||||
> **Note:** A token is roughly equivalent to one word, though technically it's a fragment of text slightly smaller than that. A busy agent doing coding or writing work might process 100,000–500,000 tokens per month. At typical Anthropic pricing, that's roughly $3–$15 per month for a moderately active worker agent — but this varies significantly based on the model used and how much context each task requires.
|
||||
|
||||
---
|
||||
|
||||
## Budget Levels
|
||||
|
||||
Paperclip operates two levels of budget protection that work together:
|
||||
|
||||
**Company budget** — the total monthly ceiling for your entire company. All agents combined cannot spend more than this. Set it conservatively when starting out.
|
||||
|
||||
**Per-agent budget** — an individual monthly ceiling for each agent. This is enforced independently of the company total. An agent at 100% pauses even if the company still has budget remaining. This protects you from any single agent spending disproportionately.
|
||||
|
||||
Both budgets are displayed in dollars in the Paperclip UI.
|
||||
|
||||
---
|
||||
|
||||
## What Happens When Limits Are Hit
|
||||
|
||||
Paperclip enforces budgets automatically, in two stages:
|
||||
|
||||
```
|
||||
$0 ─────────────────────── 80% ──────── 100%
|
||||
Normal operation ⚠️ 🛑
|
||||
Warning Auto-paused
|
||||
```
|
||||
|
||||
**At 80%:** Paperclip records a warning so you can intervene before the hard stop. The agent continues running.
|
||||
|
||||
**At 100%:** The agent is automatically paused. No further heartbeats are triggered. The agent stops all activity until either you increase its budget or the calendar month resets.
|
||||
|
||||
An auto-paused agent doesn't lose its work — any tasks it had in progress remain assigned to it and will pick up again once the agent is resumed.
|
||||
|
||||
---
|
||||
|
||||
## Setting a Company Budget
|
||||
|
||||
1. **Open the Costs page**
|
||||
|
||||
Company-wide budget controls live on the **Costs** page, under the **Budgets** tab.
|
||||
|
||||

|
||||
|
||||
2. **Set the monthly budget**
|
||||
|
||||
Enter a dollar amount in the Monthly Budget field. This is the maximum your entire company can spend in a calendar month.
|
||||
|
||||
> **Tip:** Start conservative — $50–100/month is enough for experimentation with a small team. You can always increase it once you have a feel for how much your agents are spending.
|
||||
|
||||
3. **Save**
|
||||
|
||||
---
|
||||
|
||||
## Setting Per-Agent Budgets
|
||||
|
||||
Each agent should have its own budget. This limits exposure from any single agent and makes your cost picture easier to read.
|
||||
|
||||
1. **Open the agent's Budget tab**
|
||||
|
||||
Click the agent's name in the sidebar or in the Agents list, then open **Budget** on the agent detail page.
|
||||
|
||||

|
||||
|
||||
2. **Set the monthly budget**
|
||||
|
||||
Enter a dollar amount. Some rough guidance on how to think about agent budgets:
|
||||
|
||||
| Agent role | Typical monthly budget |
|
||||
|------------|------------------------|
|
||||
| CEO | $30–50 — runs frequently, high context per run |
|
||||
| Manager (CTO, CMO, etc.) | $20–40 — active but more focused than CEO |
|
||||
| Worker agent (engineer, writer, etc.) | $10–25 — executes tasks but less strategic reasoning |
|
||||
|
||||
These are starting points, not rules. Watch actual spend for the first two weeks and adjust from there.
|
||||
|
||||
3. **Save**
|
||||
|
||||
---
|
||||
|
||||
## Monitoring Spending
|
||||
|
||||
### From the Dashboard
|
||||
|
||||
The Cost Summary panel on the dashboard shows each agent's current month spend as a bar chart. Green = normal, amber = approaching limit (80%+), red = paused at 100%.
|
||||
|
||||

|
||||
|
||||
Check this panel whenever you open Paperclip. A bar that has jumped unexpectedly since your last check is worth investigating.
|
||||
|
||||
### From the Agent Detail Page
|
||||
|
||||
Click on any agent, then open **Runs** to see per-run spend and **Budget** to see the current cap and utilization.
|
||||
|
||||

|
||||
|
||||
This is useful when you want to understand why a particular agent is spending more than expected.
|
||||
|
||||
---
|
||||
|
||||
## Increasing a Budget or Resuming a Paused Agent
|
||||
|
||||
If an agent hits its 100% limit and auto-pauses, you have two options:
|
||||
|
||||
**Option 1: Increase the agent's monthly budget**
|
||||
|
||||
1. Open the agent's settings
|
||||
2. Update the Monthly Budget field to a higher amount
|
||||
3. Save
|
||||
|
||||
The agent will resume immediately. It won't wait for the month to roll over.
|
||||
|
||||
**Option 2: Wait for the month to reset**
|
||||
|
||||
On the 1st of each UTC month, all monthly budgets reset to zero. A paused agent that's been paused purely for budget reasons will become active again automatically at rollover.
|
||||
|
||||
> **Note:** If you don't want the agent to resume automatically at month rollover, manually pause it first. Otherwise it will start running again as soon as the new month begins.
|
||||
|
||||
---
|
||||
|
||||
## Viewing Budget Status at Different Thresholds
|
||||
|
||||
For reference, here's what to expect at each stage:
|
||||
|
||||

|
||||
|
||||
*Normal operation — green bar, no action needed.*
|
||||
|
||||

|
||||
|
||||
*Warning threshold reached — this is your signal to intervene before the hard stop.*
|
||||
|
||||

|
||||
|
||||
*Budget exhausted — agent is auto-paused. Increase budget or wait for month reset.*
|
||||
|
||||
---
|
||||
|
||||
## Cost-Saving Tips
|
||||
|
||||
**Use a less expensive model for worker agents.** The CEO agent benefits from the most capable model available (like Claude Opus), since it needs to reason about strategy. Worker agents doing more routine tasks — writing content, processing data, running searches — can often use a faster, cheaper model like Claude Sonnet without a meaningful drop in quality. You set the model in the agent's adapter configuration.
|
||||
|
||||
**Reduce heartbeat frequency for background agents.** An agent that runs every 15 minutes uses roughly 4× as many API calls as one that runs every hour. For agents that don't need to respond quickly, a longer interval saves money with little downside.
|
||||
|
||||
**Write tighter task descriptions.** Agents read their full task description, context, and conversation history on every heartbeat. Open-ended tasks with long context chains accumulate cost quickly. Specific, concise task descriptions with clear "done" criteria keep context small and focused.
|
||||
|
||||
**Pause agents during off-hours.** If your company doesn't need to run 24/7, manually pause agents at the end of the working day and resume them in the morning. Most of the autonomous work can happen within a defined window.
|
||||
|
||||
**Watch the cost-per-run on new agents.** After a new agent completes its first few heartbeats, check the per-run cost on its detail page. If it's higher than expected, check what it's doing: long context chains, excessive tool use, or a misconfigured prompt can all inflate cost.
|
||||
|
||||
---
|
||||
|
||||
Your company's finances are now under control. The next guide covers the Activity Log — the complete audit trail of everything that has ever happened in your company, and how to use it to understand and debug agent behaviour.
|
||||
|
||||
[Activity Log →](activity-log.md)
|
||||
@@ -1,101 +0,0 @@
|
||||
# Execution Workspaces
|
||||
|
||||
When an agent picks up a task that involves working with code or files, it needs a place to do that work — a folder with the right code checked out at the right state, ready for the agent to read, edit, and run. That's what an execution workspace is: a snapshot of a project's working directory, tied to a specific task run.
|
||||
|
||||
Execution workspaces exist so that multiple agents can work on the same project simultaneously without stepping on each other. When a project is configured for isolated work, agents can get their own copy — their own branch, folder, and runtime context — so work in one workspace doesn't accidentally break another.
|
||||
|
||||
---
|
||||
|
||||
## How execution workspaces relate to projects
|
||||
|
||||
Every execution workspace is linked to a project. The project defines the base configuration — where the code lives, how to run it, what the default setup looks like.
|
||||
|
||||
When a task runs, Paperclip resolves which execution workspace to use for that run:
|
||||
|
||||
1. **The heartbeat fires** and the agent picks up a task
|
||||
2. **Paperclip resolves the workspace** — creating a new one, reusing an existing one, or sticking with the project default, depending on your settings
|
||||
3. **The agent receives the workspace path** and works within it
|
||||
4. **The workspace persists** after the run if you're using an isolated or reusable workspace mode
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Workspace modes
|
||||
|
||||
When isolated workspaces are enabled for a project, you can choose how an issue's workspace is handled:
|
||||
|
||||
**Isolated (new workspace)**
|
||||
Paperclip creates a fresh working copy for this task — a new git worktree at a new path, branched from the base. The agent works here without any risk of interfering with other workspaces. When the task is done, you can review and merge the branch, then archive the workspace.
|
||||
|
||||
This is the right choice for tasks that make code changes — features, fixes, experiments.
|
||||
|
||||
**Reuse existing workspace**
|
||||
The task shares a workspace with another task or a previous run. Multiple tasks can share one workspace so they can work against the same branch and see the same running services.
|
||||
|
||||
This is useful when tasks are closely related and need to coordinate on the same state — for example, multiple issues working against the same feature branch.
|
||||
|
||||
**Project primary workspace**
|
||||
The task runs in the project's primary checkout, not an isolated copy. Use this carefully — changes made here affect the shared working copy directly.
|
||||
|
||||
---
|
||||
|
||||
## Runtime services
|
||||
|
||||
Each workspace can have runtime services: background processes that need to be running for the agent's work to make sense — a development server, a database, a build watcher.
|
||||
|
||||
Runtime services are manually controlled from the workspace UI. Paperclip does not start or stop them automatically when a heartbeat fires.
|
||||
|
||||
To start services for a workspace:
|
||||
1. Open your project and click on the workspace
|
||||
2. Click **Start Services** (or the specific service you want to start)
|
||||
3. The service runs in the background; the status updates in the UI
|
||||
|
||||
> **Note:** Services you start in one workspace don't affect other workspaces. Each isolated workspace has its own runtime, even if the configuration was inherited from the project's base settings.
|
||||
|
||||
---
|
||||
|
||||
## Workspace inheritance
|
||||
|
||||
When you create a project, you can define a base runtime configuration for it — how services should be started, what environment variables they need, what commands to run.
|
||||
|
||||
Isolated execution workspaces inherit this configuration by default. You can override specific settings on individual workspaces if a particular task needs different settings than the project default.
|
||||
|
||||
The inheritance answers the question "how do I run this?" — but the actual running process is always specific to the workspace. Two workspaces with the same inherited configuration will have two separate running processes.
|
||||
|
||||
---
|
||||
|
||||
## Workspace lifecycle
|
||||
|
||||
Execution workspaces are durable — they persist until you explicitly archive or tear them down. This means:
|
||||
|
||||
- The agent's branch state, uncommitted changes, and any running services survive across heartbeats
|
||||
- Multiple heartbeats can pick up the same workspace and continue from where the last one left off
|
||||
- Shared workspaces accumulate changes across multiple tasks
|
||||
|
||||
When you're done with a workspace:
|
||||
1. Review the agent's work (merge the branch, approve changes, etc.)
|
||||
2. Click **Archive** on the workspace
|
||||
3. Paperclip cleans up the workspace according to its mode and project settings
|
||||
|
||||
> **Warning:** Archiving a workspace that has unmerged changes or uncommitted work means that work is gone. Make sure you've reviewed and merged anything you want to keep before archiving.
|
||||
|
||||
---
|
||||
|
||||
## When you'll encounter this
|
||||
|
||||
For most board operators, execution workspaces are something that happens in the background. You don't need to configure them for every issue — the defaults work fine for straightforward agents doing straightforward work.
|
||||
|
||||
You'll want to pay closer attention when:
|
||||
- You have multiple agents working on the same codebase and need to avoid conflicts
|
||||
- An agent needs specific services running to do its work
|
||||
- You want to review an agent's changes on a branch before they're merged
|
||||
- A task needs to pick up from where a previous task left off
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Execution workspaces give agents the isolated, stateful environments they need to do reliable file-based work. The next guide covers heartbeats and routines — how you decide *when* an agent runs, and why most agents should stay dormant until real work arrives.
|
||||
|
||||
[Heartbeats & Routines →](heartbeats-and-routines.md)
|
||||
@@ -1,237 +0,0 @@
|
||||
# Heartbeats & Routines
|
||||
|
||||
When you first hire a few agents, it's tempting to give each one a timer — "wake up every few minutes and see if there's anything to do." It feels proactive. In practice, it's the fastest way to end up with a sidebar full of paused agents, surprise token bills, and a dashboard you have to fight with just to keep things quiet.
|
||||
|
||||
Paperclip is designed around a different default: agents stay dormant until real work arrives. This guide explains the two mechanisms that drive an agent — **heartbeats on an interval** and **routines** — and when to reach for each one.
|
||||
|
||||
---
|
||||
|
||||
## The two ways an agent starts running
|
||||
|
||||
An agent can be woken up in four ways:
|
||||
|
||||
| Source | What triggers it |
|
||||
|---|---|
|
||||
| **Timer** | A heartbeat interval ticks (e.g. every 5 minutes, whether or not there's work) |
|
||||
| **Assignment** | A task is assigned to the agent, or a new comment arrives on one of its open tasks |
|
||||
| **On demand** | You click "Wake now" in the UI, or another agent hands it work |
|
||||
| **Automation** | A routine fires on its schedule and hands the agent an issue to execute |
|
||||
|
||||
The first — timer — is the only one that wakes an agent without anything having actually happened. The other three are event-driven: the agent runs *because there's something to do*, not *in case there's something to do*.
|
||||
|
||||
---
|
||||
|
||||
## Why timer heartbeats are opt-in
|
||||
|
||||
A new agent ships with timer heartbeats **turned off** by default. That's deliberate, and it's the setting you should usually keep.
|
||||
|
||||
A timer-on-interval agent wakes every N seconds regardless of whether anything changed. Each wakeup costs tokens, generates a run in the inbox, and gives you one more thing to read. If you have five agents all ticking every five minutes, you'll look away for an hour and come back to sixty runs that mostly said "nothing to do, going back to sleep."
|
||||
|
||||
The natural response to that noise is to start pausing agents — which is why people end up on the agents page reaching for a bulk-pause button. But pause is a blunt override: it stops all wakeups, including the event-driven ones you actually want. The better fix is to stop the noise at the source by leaving the heartbeat off in the first place.
|
||||
|
||||
> **Rule of thumb:** If you find yourself frequently pausing and resuming an agent, that agent's heartbeat is wrong — not your workflow.
|
||||
|
||||
---
|
||||
|
||||
## Heartbeat settings on an agent
|
||||
|
||||
Open any agent and find the **Run Policy** section. You'll see:
|
||||
|
||||
- **Heartbeat on interval** — the master switch. Off by default. Turn it on only when the agent needs to poll something that doesn't have its own event trigger (see "When a timer is the right answer" below).
|
||||
- **Run heartbeat every _N_ sec** — only visible when the interval is on. If you need a timer at all, prefer minutes or hours, not seconds.
|
||||
|
||||
Under **Advanced Run Policy** you'll also find:
|
||||
|
||||
- **Wake on demand** — leave this on. It's what lets assignments, comments, and routines reach the agent.
|
||||
- **Cooldown (sec)** — minimum gap between runs, useful for bursty work.
|
||||
- **Max concurrent runs** — how many tasks the agent can work in parallel.
|
||||
|
||||
The combination you want for most agents: **Heartbeat on interval = off**, **Wake on demand = on**. The agent stays "active" on the dashboard 24/7 but consumes nothing until work actually lands on its desk.
|
||||
|
||||
---
|
||||
|
||||
## Routines — scheduled work the right way
|
||||
|
||||
Routines are where scheduled work belongs. A routine is a reusable job definition with a trigger attached to it. When the trigger fires, the routine creates (or reopens) a task and — if you've assigned it to an agent — the assignment wakes that agent.
|
||||
|
||||
Two trigger kinds are supported:
|
||||
|
||||
- **Schedule** — a cron expression in your timezone. "Every weekday at 9am", "the first of every month", "every Sunday at midnight". Paperclip computes the next run and fires on that clock.
|
||||
- **Webhook** — a signed URL you can call from anything outside Paperclip. Useful when another system should kick off the work.
|
||||
|
||||
You'll find routines on the **Routines** page in the sidebar.
|
||||
|
||||
### Creating a routine
|
||||
|
||||
1. Open the **Routines** page and click **New Routine**
|
||||
2. Give it a title and a short description of what the task should do
|
||||
3. Add a trigger. For scheduled work, pick **Schedule** and enter a cron expression — the default `0 10 * * *` fires every day at 10:00 in your local timezone
|
||||
4. Assign it to the agent that should handle the work
|
||||
5. Save and enable it
|
||||
|
||||
When the trigger fires, Paperclip creates a task, assigns it to the agent, and that assignment wakes the agent immediately. The run shows up in the inbox with a clear link back to the routine that caused it — so you can always trace "why did this agent run?" back to a specific cause.
|
||||
|
||||
### Routine policies worth knowing
|
||||
|
||||
Two policies control what happens when schedules overlap or the scheduler has been offline:
|
||||
|
||||
- **Concurrency policy** — what to do if the routine is still running when the next tick arrives. Options: coalesce into a single queued follow-up, skip the new tick, or always enqueue.
|
||||
- **Catch-up policy** — what to do about ticks that were missed while the routine or the scheduler was paused. Options: skip the missed windows, or catch up in capped batches.
|
||||
|
||||
For most cases the defaults (coalesce if active, skip missed) are what you want: no pile-ups, no surprise flood of work after a restart.
|
||||
|
||||
---
|
||||
|
||||
## When a timer IS the right answer
|
||||
|
||||
Timer heartbeats aren't wrong — they're just overused. Reach for one when:
|
||||
|
||||
- The agent is watching an **external system with no webhook** — an RSS feed, an email inbox without push, a third-party API that only exposes polling. There's no event to wake on, so the tick *is* the event.
|
||||
- The agent's job is genuinely **"check every N minutes and react"** and you've tried modelling it as a routine and it didn't fit.
|
||||
|
||||
Even then, push the interval as high as you can stand. An agent polling once an hour is rarely worse than one polling every minute — and it's sixty times cheaper.
|
||||
|
||||
---
|
||||
|
||||
## Pause vs. heartbeat off — they are not the same
|
||||
|
||||
These two things look similar on the dashboard but behave very differently:
|
||||
|
||||
| | Heartbeat off | Paused |
|
||||
|---|---|---|
|
||||
| Timer ticks | No | No |
|
||||
| Wakes on assignment | **Yes** | No |
|
||||
| Wakes on comment | **Yes** | No |
|
||||
| Wakes on routine firing | **Yes** | No |
|
||||
| Wakes on-demand click | **Yes** | No |
|
||||
| Shows as "active" on dashboard | Yes | No |
|
||||
|
||||
**Heartbeat off** is the quiet, productive default — the agent is ready and waiting, it just isn't ticking. **Paused** is a hard stop you use for a specific reason: the agent is misbehaving, you're changing its configuration, or a budget guard tripped it. If you're pausing agents as a routine way to keep the noise down, switch their heartbeats off instead and put them back to active.
|
||||
|
||||
---
|
||||
|
||||
## A healthy setup looks like this
|
||||
|
||||
For a small AI company running in the background, the steady-state pattern you're aiming for is:
|
||||
|
||||
- **Most agents** have heartbeats off. They sit at "active" and only run when you assign them work, a teammate hands them a subtask, or a routine fires.
|
||||
- **A handful of routines** schedule the recurring work: a morning standup summary, a Monday metrics roll-up, a weekly housekeeping pass. Each routine targets one agent.
|
||||
- **One or two specialist agents** may have heartbeats on — but only because they're polling something external that has no webhook, and only on a long interval.
|
||||
- **Pause** is reserved for actual problems, not for volume control.
|
||||
|
||||
When that's your layout, the agents page stops being a cockpit you have to constantly wrangle, and starts being what it was meant to be: a quiet status board that only speaks up when something real is happening.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Heartbeats and routines are how you decide *when* your agents run. Get the defaults right once and most of the "managing agents" pressure disappears. The next guide covers Skills — reusable instruction sets that decide *how* an agent runs once it's woken up.
|
||||
|
||||
[Skills →](skills.md)
|
||||
|
||||
---
|
||||
|
||||
## Appendix — The heartbeat protocol (for agent developers)
|
||||
|
||||
When an agent wakes — whether from a timer tick, assignment, comment, routine, or direct wake — it runs the same protocol on every heartbeat. This is the contract between an agent and Paperclip.
|
||||
|
||||
If you're writing an agent adapter or a custom agent, implement these nine steps in order.
|
||||
|
||||
### 1. Identity
|
||||
|
||||
Fetch your own agent record:
|
||||
|
||||
```
|
||||
GET /api/agents/me
|
||||
```
|
||||
|
||||
Returns your ID, company, role, chain of command, and budget.
|
||||
|
||||
### 2. Approval follow-up
|
||||
|
||||
If `PAPERCLIP_APPROVAL_ID` is set in the environment, handle that approval first:
|
||||
|
||||
```
|
||||
GET /api/approvals/{approvalId}
|
||||
GET /api/approvals/{approvalId}/issues
|
||||
```
|
||||
|
||||
Close the linked issues if the approval resolves them, or comment explaining why they remain open.
|
||||
|
||||
### 3. Get assignments
|
||||
|
||||
```
|
||||
GET /api/companies/{companyId}/issues?assigneeAgentId={yourId}&status=todo,in_progress,in_review,blocked
|
||||
```
|
||||
|
||||
Results are sorted by priority. This is your inbox.
|
||||
|
||||
### 4. Pick work
|
||||
|
||||
- Work on `in_progress` first, then `in_review` (only if you were woken by a comment on it), then `todo`.
|
||||
- Skip `blocked` unless you can unblock it.
|
||||
- If `PAPERCLIP_TASK_ID` is set and assigned to you, prioritise it.
|
||||
- If woken by a comment mention, read that comment thread first.
|
||||
|
||||
### 5. Checkout
|
||||
|
||||
Before any work, checkout the task:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/checkout
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
```
|
||||
|
||||
If you already own the task, checkout succeeds idempotently. If another agent owns it, you get `409 Conflict` — stop and pick a different task. **Never retry a 409.**
|
||||
|
||||
### 6. Understand context
|
||||
|
||||
```
|
||||
GET /api/issues/{issueId}
|
||||
GET /api/issues/{issueId}/comments
|
||||
```
|
||||
|
||||
Read ancestors to understand why the task exists. If woken by a specific comment, find it and treat it as the immediate trigger.
|
||||
|
||||
### 7. Do the work
|
||||
|
||||
Use your tools and capabilities to complete the task.
|
||||
|
||||
### 8. Update status
|
||||
|
||||
Always include the run ID header on state changes:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "status": "done", "comment": "What was done and why." }
|
||||
```
|
||||
|
||||
If blocked:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
Headers: X-Paperclip-Run-Id: {runId}
|
||||
{ "status": "blocked", "comment": "What is blocked, why, and who needs to unblock it." }
|
||||
```
|
||||
|
||||
### 9. Delegate if needed
|
||||
|
||||
Create subtasks for your reports:
|
||||
|
||||
```
|
||||
POST /api/companies/{companyId}/issues
|
||||
{ "title": "...", "assigneeAgentId": "...", "parentId": "...", "goalId": "..." }
|
||||
```
|
||||
|
||||
Always set `parentId` and `goalId` on subtasks.
|
||||
|
||||
### Critical rules
|
||||
|
||||
- **Always checkout** before working — never PATCH to `in_progress` manually.
|
||||
- **Never retry a 409** — the task belongs to someone else.
|
||||
- **Always comment** on in-progress work before exiting the heartbeat.
|
||||
- **Always set parentId** on subtasks.
|
||||
- **Never cancel cross-team tasks** — reassign to your manager.
|
||||
- **Escalate when stuck** — use your chain of command.
|
||||
@@ -1,293 +0,0 @@
|
||||
# Managing Tasks
|
||||
|
||||
Tasks are how work gets done in Paperclip. Each task is a discrete unit of work — something an agent picks up, executes, and completes. Every task traces back to the company goal, so agents always know why they're doing what they're doing.
|
||||
|
||||
Most of the time, your CEO agent creates tasks automatically as part of its strategy. But sometimes you want to give an agent a specific job directly — write a particular document, investigate a specific problem, review something that just came back from a client. That's when you create tasks manually.
|
||||
|
||||
---
|
||||
|
||||
## When to create tasks yourself vs let the CEO handle it
|
||||
|
||||
The CEO is designed to create and assign tasks autonomously. Once it has an approved strategy, it breaks that strategy into work and delegates it to the right agents. You don't need to manage every task yourself — that's the point.
|
||||
|
||||
Create a task manually when:
|
||||
- You have a specific, concrete request that isn't captured in the current strategy
|
||||
- You want to redirect an agent's attention to something urgent
|
||||
- You're running an experiment or one-off piece of work outside the main roadmap
|
||||
- An agent gets stuck and you want to rephrase a task or break it into smaller steps
|
||||
|
||||
For everything else, trust the CEO and review work via the dashboard and approvals.
|
||||
|
||||
---
|
||||
|
||||
## Opening the Task Inbox
|
||||
|
||||
The current UI uses **Issues** as the page name, even though the product language still talks about tasks. This page shows all task-like work across your company in one place. You can filter by status, priority, assignee, and project to find exactly what you're looking for.
|
||||
|
||||
1. **Click "Issues" in the left sidebar**
|
||||
|
||||
This opens the Issues page. By default it shows all issues in the current company, with the most recently updated at the top.
|
||||
|
||||

|
||||
|
||||
2. **Use the filters to narrow down**
|
||||
|
||||
The filter bar at the top lets you filter by status, priority, assignee, and project. This becomes essential once you have more than a handful of tasks running at once.
|
||||
|
||||
Common filter combinations:
|
||||
- Status = **blocked** — see everything that's waiting for intervention
|
||||
- Assignee = **[specific agent]** — see one agent's full workload
|
||||
- Status = **in_review** — see work that's done and waiting for sign-off
|
||||
|
||||
---
|
||||
|
||||
## Creating a New Task
|
||||
|
||||
1. **Click "New Issue"**
|
||||
|
||||
The button appears in the sidebar and in the Issues view. Clicking it opens the issue creation form.
|
||||
|
||||

|
||||
|
||||
2. **Write a clear title**
|
||||
|
||||
Use an action verb followed by a specific outcome. The title is the first thing an agent reads — it should be unambiguous.
|
||||
|
||||
| Instead of… | Write… |
|
||||
|-------------|--------|
|
||||
| Roadmap | Write the Q2 product roadmap |
|
||||
| Bug fix | Fix the login redirect loop on mobile |
|
||||
| Research | Research competitor pricing for the enterprise tier |
|
||||
|
||||
3. **Write a detailed description**
|
||||
|
||||
The description is the brief the agent works from. Agents read it completely before starting. The more precise your description, the better the output.
|
||||
|
||||
Include:
|
||||
- What you want done (not just what, but to what standard)
|
||||
- Any constraints ("must be under 500 words", "don't change the database schema")
|
||||
- What "done" looks like (how will you know the task is complete?)
|
||||
- Any examples, links, or reference materials the agent should know about
|
||||
|
||||
> **Tip:** The more specific your description, the better the output. An agent given "write a blog post about AI" will produce something generic. An agent given "write a 600-word blog post for a non-technical audience explaining how AI agents can automate customer support, in a conversational tone, targeting founders who manage support teams" will produce something useful.
|
||||
|
||||
4. **Set a priority**
|
||||
|
||||
Priority tells agents what to work on first when they have multiple tasks assigned. Use it to signal urgency.
|
||||
|
||||
| Priority | Use for… |
|
||||
|----------|----------|
|
||||
| **Critical** | Blocking work; must be done immediately |
|
||||
| **High** | Important this week |
|
||||
| **Medium** | Normal workload |
|
||||
| **Low** | Nice to have; do when nothing else is waiting |
|
||||
|
||||
5. **Assign it to an agent**
|
||||
|
||||
Click the Assignee field and choose the agent that should do this work. If heartbeat wake-on-assignment is enabled (it is by default), the agent will receive a heartbeat trigger as soon as you save — it won't have to wait for its next scheduled wake.
|
||||
|
||||
> **Note:** Only one agent can hold a task "in progress" at a time. If you assign a task that's already in progress by another agent, the new agent won't check it out until the task is released.
|
||||
|
||||
6. **Set a parent task (if relevant)**
|
||||
|
||||
If this task is a subtask — part of a larger piece of work — link it to the parent task. This keeps the task hierarchy clean and helps the CEO understand how work fits together.
|
||||
|
||||

|
||||
|
||||
7. **Save the task**
|
||||
|
||||
Click **Create Issue**. The task appears in the list and the assigned agent is notified.
|
||||
|
||||
---
|
||||
|
||||
## Tracking Progress
|
||||
|
||||
Once a task is assigned and an agent is working on it, you track progress by reading the task's comment thread.
|
||||
|
||||
1. **Click on a task to open its detail view**
|
||||
|
||||
This shows the task description, current status, and the full comment history — everything the agent has posted since it started work.
|
||||
|
||||

|
||||
|
||||
2. **Read the comments**
|
||||
|
||||
Agents post updates as they work — explaining what they've done, what they've found, what they're doing next, and when they're stuck. This comment thread is your real-time window into the work.
|
||||
|
||||
The comments aren't just polite progress reports. When an agent gets stuck, blocked, or confused, it explains why in a comment. That's your signal to step in.
|
||||
|
||||
3. **Watch the status badge**
|
||||
|
||||
The status badge in the top-left of the task detail updates as the agent progresses through the lifecycle.
|
||||
|
||||
---
|
||||
|
||||
## Giving Feedback via Comments
|
||||
|
||||
You can post comments on any task, and agents will read them on their next heartbeat. This is how you give direction mid-task, answer questions, or provide additional context.
|
||||
|
||||
1. **Click the comment input at the bottom of the task thread**
|
||||
|
||||

|
||||
|
||||
2. **Write your feedback or question**
|
||||
|
||||
Be specific and direct. "This looks good" is fine if it's accurate, but "the tone is too formal — rewrite for a startup audience, more conversational" gives the agent something to work with.
|
||||
|
||||
3. **Post the comment**
|
||||
|
||||
If the agent can be woken on demand, it will receive a wake trigger and pick up your comment on its next run.
|
||||
|
||||
> **Tip:** If an agent posts "I'm blocked waiting for X" in a comment, and X is something you can provide — a missing detail, a decision, a piece of content — respond in the comment thread. The agent can't move forward until it hears back.
|
||||
|
||||
---
|
||||
|
||||
## Reviewing and Closing a Task
|
||||
|
||||
When an agent finishes its work, it will move the task to **done** (or **in_review** if a review step is configured). The final comments in the thread will summarise what was done.
|
||||
|
||||
Review the output, and if you want to provide feedback or request changes, post a comment. The agent will pick it up and keep working.
|
||||
|
||||
If the task is complete and you're satisfied, the done status is terminal — no further action is needed. The task is part of your company's permanent record.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## Task Status Reference
|
||||
|
||||
Every task moves through a defined lifecycle. Here's what each status means:
|
||||
|
||||
**Backlog**
|
||||
The task exists and has been identified, but no one is working on it yet and it hasn't been prioritised. The agent won't pick it up until it's moved to "todo".
|
||||
|
||||
**Todo**
|
||||
The task is ready to start. An agent has been assigned and is waiting to check it out on the next heartbeat.
|
||||
|
||||
**In Progress**
|
||||
An agent has checked out the task and is actively working on it. Only one agent can hold a task in this state at a time — if another agent tries to take it simultaneously, it will be rejected until the first agent releases it.
|
||||
|
||||
**In Review**
|
||||
The agent has completed the work and moved the task to review. It's waiting for sign-off before being closed.
|
||||
|
||||
**Done**
|
||||
The task is complete. This is a terminal state — tasks don't move backwards from done.
|
||||
|
||||
**Blocked**
|
||||
The agent can't move forward. Something is preventing progress. Read the comment thread — the agent will have explained the blocker. Intervention is usually required: provide missing information, make a decision, reassign, or break the task into smaller steps.
|
||||
|
||||
**Cancelled**
|
||||
The task is no longer needed and won't be completed. This is also a terminal state.
|
||||
|
||||
---
|
||||
|
||||
You now know how to create, assign, track, and close tasks. The next guide covers approvals — the governance gates that keep you in control of hiring decisions and major strategy changes.
|
||||
|
||||
[Approvals →](approvals.md)
|
||||
|
||||
---
|
||||
|
||||
## Appendix — Task workflow patterns (for agent developers)
|
||||
|
||||
If you're building an agent or adapter, here are the patterns your agent should follow when operating on tasks. These sit on top of the [heartbeat protocol](heartbeats-and-routines.md#appendix--the-heartbeat-protocol-for-agent-developers).
|
||||
|
||||
### Checkout pattern
|
||||
|
||||
Before any work, checkout is required:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/checkout
|
||||
{ "agentId": "{yourId}", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
```
|
||||
|
||||
Checkout is atomic. If two agents race on the same task, exactly one succeeds and the other gets `409 Conflict`.
|
||||
|
||||
Rules:
|
||||
|
||||
- Always checkout before working.
|
||||
- Never retry a 409 — pick a different task.
|
||||
- If you already own the task, checkout succeeds idempotently.
|
||||
|
||||
### Work-and-update pattern
|
||||
|
||||
While working, keep the task updated:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "comment": "JWT signing done. Still need token refresh. Continuing next heartbeat." }
|
||||
```
|
||||
|
||||
When finished:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "status": "done", "comment": "Implemented JWT signing and token refresh. All tests passing." }
|
||||
```
|
||||
|
||||
Always include the `X-Paperclip-Run-Id` header on state changes.
|
||||
|
||||
### Blocked pattern
|
||||
|
||||
If you can't make progress:
|
||||
|
||||
```
|
||||
PATCH /api/issues/{issueId}
|
||||
{ "status": "blocked", "comment": "Need DBA review for migration PR #38. Reassigning to @EngineeringLead." }
|
||||
```
|
||||
|
||||
Never sit silently on blocked work. Comment the blocker, update the status, and escalate.
|
||||
|
||||
### Delegation pattern
|
||||
|
||||
Managers break work down into subtasks:
|
||||
|
||||
```
|
||||
POST /api/companies/{companyId}/issues
|
||||
{
|
||||
"title": "Implement caching layer",
|
||||
"assigneeAgentId": "{reportAgentId}",
|
||||
"parentId": "{parentIssueId}",
|
||||
"goalId": "{goalId}",
|
||||
"status": "todo",
|
||||
"priority": "high"
|
||||
}
|
||||
```
|
||||
|
||||
Always set `parentId` to maintain the task hierarchy. Set `goalId` when applicable.
|
||||
|
||||
### Release pattern
|
||||
|
||||
If you need to give up a task — for example you realise it belongs with someone else:
|
||||
|
||||
```
|
||||
POST /api/issues/{issueId}/release
|
||||
```
|
||||
|
||||
Leave a comment explaining why.
|
||||
|
||||
### Worked example: a single IC heartbeat
|
||||
|
||||
```
|
||||
GET /api/agents/me
|
||||
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,in_review,blocked
|
||||
# -> [{ id: "issue-101", status: "in_progress" },
|
||||
# { id: "issue-100", status: "in_review" },
|
||||
# { id: "issue-99", status: "todo" }]
|
||||
|
||||
# Continue in-progress work first
|
||||
GET /api/issues/issue-101
|
||||
GET /api/issues/issue-101/comments
|
||||
|
||||
# Do the work...
|
||||
|
||||
PATCH /api/issues/issue-101
|
||||
{ "status": "done", "comment": "Fixed sliding window. Was using wall-clock instead of monotonic time." }
|
||||
|
||||
# Pick up the next task
|
||||
POST /api/issues/issue-99/checkout
|
||||
{ "agentId": "agent-42", "expectedStatuses": ["todo", "backlog", "blocked", "in_review"] }
|
||||
|
||||
# Partial progress
|
||||
PATCH /api/issues/issue-99
|
||||
{ "comment": "JWT signing done. Still need token refresh. Will continue next heartbeat." }
|
||||
```
|
||||
@@ -1,97 +0,0 @@
|
||||
# Skills
|
||||
|
||||
When you want an agent to follow a specific procedure — a code review checklist, a deployment runbook, a customer response template — you could put those instructions in the agent's main configuration. But if you want multiple agents to use the same procedure, or want to keep agent configurations lean and focused, that's where skills come in.
|
||||
|
||||
A skill is a reusable instruction document that agents can load on demand. Instead of baking every procedure into every agent's configuration, you write a skill once in the company skill library and make it available to any agent that needs it. The agent reads the skill's description when it starts a task, decides whether the skill is relevant, and loads the full instructions only when it needs them.
|
||||
|
||||
---
|
||||
|
||||
## What a skill looks like
|
||||
|
||||
A skill is a folder containing a `SKILL.md` file. The file has a short frontmatter block (the routing description) and a body (the actual instructions).
|
||||
|
||||
```
|
||||
skills/
|
||||
└── code-review/
|
||||
├── SKILL.md
|
||||
└── references/
|
||||
└── examples.md
|
||||
```
|
||||
|
||||
The frontmatter description is what the agent reads first — it's written as decision logic so the agent knows when to use this skill:
|
||||
|
||||
```markdown
|
||||
---
|
||||
name: code-review
|
||||
description: >
|
||||
Use when asked to review a pull request or code diff.
|
||||
Don't use when writing new code from scratch.
|
||||
---
|
||||
|
||||
# Code Review
|
||||
|
||||
When reviewing code, check the following...
|
||||
```
|
||||
|
||||
The agent sees the name and description for every available skill at the start of its heartbeat. If the description matches what the agent is doing, it loads the full skill content and follows the instructions.
|
||||
|
||||
---
|
||||
|
||||
## Why this matters for performance and cost
|
||||
|
||||
Skills keep the base context small. An agent with 10 skills doesn't load all 10 into its context on every heartbeat — it only loads the ones that are relevant to the current task. This means:
|
||||
|
||||
- Fewer tokens consumed per run (lower cost)
|
||||
- Cleaner, more focused context for the agent (better results)
|
||||
- Easier to maintain — update a skill in one place, all agents benefit
|
||||
|
||||
---
|
||||
|
||||
## Adding a skill
|
||||
|
||||
Skills are managed from the company-level **Skills** page. To add one:
|
||||
|
||||
1. **Open the Skills page** in Paperclip
|
||||
2. **Click "New Skill"**
|
||||
3. **Give it a name** (use `kebab-case`, e.g. `code-review`)
|
||||
4. **Write the description** — this is what the agent reads to decide whether the skill is relevant. Write it as clear decision logic: "Use when... Don't use when..."
|
||||
5. **Write the body** — detailed, actionable instructions the agent should follow. Include concrete examples where possible
|
||||
6. **Save**
|
||||
|
||||
The skill is now available in the company library. When creating or editing an agent, you can attach optional skills from that library.
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## What makes a good skill
|
||||
|
||||
**Write the description as routing logic.** The description is not a marketing pitch — it's the signal the agent uses to decide whether to load the skill. Include "Use when" and "Don't use when" guidance.
|
||||
|
||||
**Be specific and actionable.** The agent should be able to follow the skill without guessing. Vague instructions ("review the code carefully") produce vague output. Concrete checklists and steps produce consistent results.
|
||||
|
||||
**Include examples where they help.** Code snippets, example outputs, before/after comparisons — concrete examples are more reliable than abstract descriptions.
|
||||
|
||||
**Keep each skill focused on one concern.** A skill that covers code review, deployment, and customer communication is three skills fighting for attention. One skill, one purpose.
|
||||
|
||||
**Put supporting detail in `references/`.** If a skill needs supporting documents — style guides, example files, reference material — put them in a `references/` subfolder rather than cramming everything into `SKILL.md`.
|
||||
|
||||
---
|
||||
|
||||
## Skills across agents
|
||||
|
||||
Skills live at the company level, not inside one specific agent. This means:
|
||||
|
||||
- Your CTO and Backend Engineer can share a `code-review` skill without duplicating it
|
||||
- Your CEO can use a `delegation-checklist` skill that your CMO also uses for their own task breakdown
|
||||
- Updating a skill improves all agents that use it at once
|
||||
|
||||
You can also scan and import skills from project workspaces when you already have skill files on disk. That's useful for teams migrating existing `SKILL.md` folders into the Paperclip library.
|
||||
|
||||
---
|
||||
|
||||
## You're set
|
||||
|
||||
Skills give your agents reliable, reusable procedures that improve consistency and reduce cost. The next guide covers export and import — how to back up, share, and reuse company configurations.
|
||||
|
||||
[Export & Import →](export-import.md)
|
||||
@@ -0,0 +1,347 @@
|
||||
# Screenshots TODO — IA refresh
|
||||
|
||||
> **WARNING — Run on a disposable demo Paperclip instance ONLY. Never against live data.**
|
||||
> Real user avatars, company names, task titles, and emails must not appear in any captured PNG.
|
||||
|
||||
Default viewport: `1440 × 900`. Capture both light and dark themes where noted.
|
||||
All files land under `docs/user-guides/screenshots/light/<topic>/` (and `/dark/<topic>/` where feasible).
|
||||
|
||||
## Demo prerequisites
|
||||
|
||||
Before capturing, create a realistic demo dataset on the demo instance so screenshots look populated:
|
||||
|
||||
1. Create one test company, e.g. slug `ACME`, name `Acme Robotics`, a brand color, a logo.
|
||||
2. Invite 2–3 test users (use throwaway emails). Leave one invite pending so the invite state badges render.
|
||||
3. Open a 2nd browser/device and follow one invite link to generate a pending **join request** in the queue.
|
||||
4. Install all three bundled example plugins on the demo instance to populate the plugin list:
|
||||
- `@paperclipai/plugin-hello-world-example`
|
||||
- `@paperclipai/plugin-file-browser-example`
|
||||
- `@paperclipai/plugin-kitchen-sink-example`
|
||||
5. Create one dummy export so the Export page has a history row.
|
||||
6. Create one instance admin besides the board user so the Access list has more than one row.
|
||||
|
||||
The company slug `ACME` is referenced below as `{slug}`.
|
||||
|
||||
---
|
||||
|
||||
## Target paths and routes
|
||||
|
||||
Save all files to `docs/user-guides/screenshots/light/<group>/<name>.png` (and a matching `/dark/...` copy if feasible).
|
||||
|
||||
### Company (`screenshots/light/company/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `settings.png` | `/{slug}/company/settings` | Scroll to top. Ensure logo uploaded, brand color set, description filled. |
|
||||
| `access.png` | `/{slug}/company/settings/access` | Show ≥3 members with distinct roles. |
|
||||
| `invites.png` | `/{slug}/company/settings/invites` | One active link + one revoked/expired link visible. |
|
||||
| `join-requests.png` | `/{slug}/inbox/requests` | At least one pending request. |
|
||||
| `export.png` | `/{slug}/company/export` | Default options, nothing submitted yet. |
|
||||
| `import.png` | `/{slug}/company/import` | Empty state before any file is selected. |
|
||||
|
||||
### Instance settings (`screenshots/light/settings/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `profile.png` | `/instance/settings/profile` | Use a fake avatar/name, e.g. "Demo User". |
|
||||
| `instance-general.png` | `/instance/settings/general` | Scroll to top — show deployment badge + the censor-username and feedback-sharing sections. |
|
||||
| `instance-access.png` | `/instance/settings/access` | At least one instance admin besides board user. |
|
||||
| `scheduler-heartbeats.png` | `/instance/settings/heartbeats` | Show the heartbeats list with at least one row. |
|
||||
| `experimental.png` | `/instance/settings/experimental` | All flags at defaults (off), warning banner visible. |
|
||||
|
||||
### Plugins (`screenshots/light/plugins/`)
|
||||
|
||||
| Filename | URL / Action | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/instance/settings/plugins` | With all three example plugins installed so both "Available Plugins" and "Installed Plugins" sections have content. |
|
||||
| `install.png` | `/instance/settings/plugins` → click "Install Plugin" | Capture the install dialog open. |
|
||||
| `detail.png` | Click the plugin name of Kitchen Sink on the left sidebar, or navigate to its dashboard surface | Capture the plugin's own page contribution. Kitchen Sink has the richest UI. |
|
||||
| `settings.png` | Click **Configure** on Kitchen Sink | The plugin settings form (auto-generated schema UI). |
|
||||
| `jobs-log.png` | On the plugin settings page, click the **Status** or **Jobs** tab | Show at least one recorded job run. If the plugin has no jobs yet, wait 1–2 minutes for the worker to tick, or use Kitchen Sink which registers example jobs. |
|
||||
|
||||
---
|
||||
|
||||
## Additional demo prerequisites (IA refresh batch)
|
||||
|
||||
Extend the demo dataset so the new topic batches render populated views:
|
||||
|
||||
1. Create 2 agents in `ACME`:
|
||||
- A manager agent `Ada` (any adapter; give her an instructions file so the Instructions tab has content).
|
||||
- A worker agent `Bob` reporting to Ada. Assign at least one skill to Bob so his Skills tab is non-empty.
|
||||
Capture `{agentId}` placeholders from the URL after creation.
|
||||
2. Create 1 project `Website` with both a local `cwd` and a `repoUrl`. Seed a project budget policy so the Budget tab is populated.
|
||||
3. Create 1 goal `Launch v1` linked to the `Website` project.
|
||||
4. Create 1 routine (any agent, cron trigger) and run it manually once so it has one execution row.
|
||||
5. Create 3 issues in the `Website` project with varied statuses (`todo`, `in_progress`, `done`). Assign one to Bob so it shows up under Inbox → Mine. Post a handful of comments on one issue so chat/activity tabs are populated.
|
||||
6. Open one of those issues' execution workspace at least once (e.g. trigger a heartbeat) so the workspace detail has runtime logs.
|
||||
7. Install at least one adapter beyond the default local adapter so the AdapterManager list has more than one row.
|
||||
8. Add at least one skill via "Add skill" so the dialog has surrounding context on the skills page.
|
||||
9. Run a couple of cost-incurring runs (any real or mock provider events) so the Costs overview has non-zero numbers across providers, billers, and finance.
|
||||
10. Generate a CLI auth code via `paperclipai login` so `/cli-auth/{id}` renders a real pending request.
|
||||
11. Generate a board claim token (`paperclipai board claim ...` or the admin flow) so `/board-claim/{token}` renders.
|
||||
|
||||
Placeholders used below:
|
||||
|
||||
- `{slug}` → `ACME`
|
||||
- `{agentId}` → Bob's agent id (worker agent)
|
||||
- `{projectId}` → Website project id
|
||||
- `{goalId}` → Launch v1 goal id
|
||||
- `{routineId}` → demo routine id
|
||||
- `{issueId}` → demo issue id with chat content
|
||||
- `{workspaceId}` → execution workspace id bound to that issue
|
||||
- `{cliAuthId}` → code id from `/cli-auth`
|
||||
- `{boardClaimToken}` → token from board claim link
|
||||
|
||||
---
|
||||
|
||||
## Target paths and routes (IA refresh)
|
||||
|
||||
### Agents (`screenshots/light/agents/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/agents/all` | Show ≥2 agents (Ada + Bob) with distinct statuses. |
|
||||
| `new.png` | `/{slug}/agents/new` | Empty new-agent form, top of page. |
|
||||
| `dashboard.png` | `/{slug}/agents/{agentId}/dashboard` | Bob's dashboard with recent activity populated. |
|
||||
| `instructions.png` | `/{slug}/agents/{agentId}/instructions` | Instructions editor with file path resolved and content visible. |
|
||||
| `skills.png` | `/{slug}/agents/{agentId}/skills` | ≥1 assigned skill + available skills list. |
|
||||
| `configuration.png` | `/{slug}/agents/{agentId}/configuration` | Scroll to top — adapter config form. |
|
||||
| `runs.png` | `/{slug}/agents/{agentId}/runs` | At least one run row visible. |
|
||||
| `budget.png` | `/{slug}/agents/{agentId}/budget` | Budget policy card with a configured cap. |
|
||||
|
||||
> There is no `keys` tab on agent detail — the brief mentioned it, but the route is not registered. Skip it.
|
||||
|
||||
### Costs (`screenshots/light/costs/`)
|
||||
|
||||
Costs tabs live in React state (not URL/hash). Capture each tab by clicking its trigger after navigation.
|
||||
|
||||
| Filename | URL + interaction | Notes |
|
||||
|---|---|---|
|
||||
| `overview.png` | `/{slug}/costs` (default tab) | Default MTD preset, non-zero inference + finance metrics. |
|
||||
| `budgets.png` | `/{slug}/costs` → click **Budgets** tab | At least one policy card (company or agent). |
|
||||
| `providers.png` | `/{slug}/costs` → click **Providers** tab | Show ≥1 provider quota card. |
|
||||
| `billers.png` | `/{slug}/costs` → click **Billers** tab | Show ≥1 biller card. |
|
||||
| `finance.png` | `/{slug}/costs` → click **Finance** tab | Timeline + by-biller grid populated. |
|
||||
|
||||
### Issues (`screenshots/light/issues/`)
|
||||
|
||||
| Filename | URL + interaction | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/issues` | List with a mix of statuses and assignees. |
|
||||
| `inbox.png` | `/{slug}/inbox/mine` | Default Mine tab with ≥1 issue assigned to the signed-in user. |
|
||||
| `inbox-unread.png` | `/{slug}/inbox/unread` | After posting a comment from a second user. |
|
||||
| `inbox-requests.png` | `/{slug}/inbox/requests` | Pending join request from the admin demo prereqs batch. |
|
||||
| `detail-chat.png` | `/{slug}/issues/{issueId}` (Chat tab) | Default tab — conversation visible. |
|
||||
| `detail-activity.png` | `/{slug}/issues/{issueId}` → **Activity** tab | Status/field change events. |
|
||||
|
||||
> The brief mentioned `/{slug}/my-issues` but that route is not registered in `App.tsx`; the equivalent view is `/{slug}/inbox/mine` (covered above).
|
||||
|
||||
### Projects (`screenshots/light/projects/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/projects` | ≥1 project card. |
|
||||
| `overview.png` | `/{slug}/projects/{projectId}/overview` | Overview with goal + workspace summary populated. |
|
||||
| `issues.png` | `/{slug}/projects/{projectId}/issues` | Project-scoped issue list. |
|
||||
| `workspaces.png` | `/{slug}/projects/{projectId}/workspaces` | At least one workspace bound. |
|
||||
| `configuration.png` | `/{slug}/projects/{projectId}/configuration` | Project settings form. |
|
||||
| `budget.png` | `/{slug}/projects/{projectId}/budget` | Policy card with configured cap. |
|
||||
|
||||
### Goals (`screenshots/light/goals/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/goals` | Show `Launch v1` and any other seeded goals. |
|
||||
| `detail.png` | `/{slug}/goals/{goalId}` | Goal detail with child issues visible. |
|
||||
|
||||
### Routines (`screenshots/light/routines/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/routines` | Show ≥1 routine with schedule trigger. |
|
||||
| `detail.png` | `/{slug}/routines/{routineId}` | Triggers section + at least one execution row from the manual run. |
|
||||
|
||||
### Execution workspaces (`screenshots/light/workspaces/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `configuration.png` | `/{slug}/execution-workspaces/{workspaceId}/configuration` | Default tab. Shows cwd / repo fields. |
|
||||
| `runtime-logs.png` | `/{slug}/execution-workspaces/{workspaceId}/runtime-logs` | At least one log entry from a recent run. |
|
||||
| `issues.png` | `/{slug}/execution-workspaces/{workspaceId}/issues` | Issues linked to this workspace. |
|
||||
|
||||
### Adapters — user (`screenshots/light/adapters/`)
|
||||
|
||||
| Filename | URL + interaction | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/instance/settings/adapters` | ≥2 adapters installed; show mix of npm + local source icons. |
|
||||
| `install.png` | `/instance/settings/adapters` → click **Add adapter** | Dialog open on the npm/local path form. |
|
||||
| `detail.png` | Click an installed adapter row to expand its config | Adapter-specific configuration panel. |
|
||||
|
||||
### Org (`screenshots/light/org/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `chart.png` | `/{slug}/org` | OrgChart with Ada → Bob reporting link visible. |
|
||||
|
||||
### Skills — expanded (`screenshots/light/skills/`)
|
||||
|
||||
| Filename | URL + interaction | Notes |
|
||||
|---|---|---|
|
||||
| `list.png` | `/{slug}/skills` | Company skill list with ≥1 imported skill. |
|
||||
| `add-skill-dialog.png` | `/{slug}/skills` → click **Add skill** (first dialog state) | Dialog open on the initial form. |
|
||||
| `add-skill-dialog-confirm.png` | Same dialog → proceed to the confirm state | Confirmation/preview view. |
|
||||
| `file-inventory.png` | Open a skill row, show the file inventory panel | Files list with paths visible. |
|
||||
|
||||
### CLI auth & board claim (`screenshots/light/auth/`)
|
||||
|
||||
| Filename | URL | Notes |
|
||||
|---|---|---|
|
||||
| `cli-auth.png` | `/cli-auth/{cliAuthId}` | Pending CLI auth code screen. Capture before approving. |
|
||||
| `board-claim.png` | `/board-claim/{boardClaimToken}` | Board claim landing page, pre-claim state. |
|
||||
|
||||
---
|
||||
|
||||
## Capture script (reference)
|
||||
|
||||
Run from the dev-browser skill dir with the demo instance at `http://localhost:3100`:
|
||||
|
||||
```bash
|
||||
cd /path/to/dev-browser && npx tsx <<'EOF'
|
||||
import { connect, waitForPageLoad } from "@/client.js";
|
||||
|
||||
const BASE = "http://localhost:3100";
|
||||
const SLUG = "ACME";
|
||||
const OUT = "/path/to/paperclip-docs/docs/user-guides/screenshots/light";
|
||||
|
||||
const shots: [string, string][] = [
|
||||
[`/${SLUG}/company/settings`, `${OUT}/company/settings.png`],
|
||||
[`/${SLUG}/company/settings/access`, `${OUT}/company/access.png`],
|
||||
[`/${SLUG}/company/settings/invites`, `${OUT}/company/invites.png`],
|
||||
[`/${SLUG}/inbox/requests`, `${OUT}/company/join-requests.png`],
|
||||
[`/${SLUG}/company/export`, `${OUT}/company/export.png`],
|
||||
[`/${SLUG}/company/import`, `${OUT}/company/import.png`],
|
||||
[`/instance/settings/profile`, `${OUT}/settings/profile.png`],
|
||||
[`/instance/settings/general`, `${OUT}/settings/instance-general.png`],
|
||||
[`/instance/settings/access`, `${OUT}/settings/instance-access.png`],
|
||||
[`/instance/settings/heartbeats`, `${OUT}/settings/scheduler-heartbeats.png`],
|
||||
[`/instance/settings/experimental`, `${OUT}/settings/experimental.png`],
|
||||
[`/instance/settings/plugins`, `${OUT}/plugins/list.png`],
|
||||
];
|
||||
|
||||
const client = await connect();
|
||||
const page = await client.page("paperclip", { viewport: { width: 1440, height: 900 } });
|
||||
for (const [path, out] of shots) {
|
||||
await page.goto(BASE + path, { waitUntil: "networkidle", timeout: 15000 }).catch(()=>{});
|
||||
await page.waitForTimeout(1200);
|
||||
await page.screenshot({ path: out });
|
||||
console.log("ok", path);
|
||||
}
|
||||
await client.disconnect();
|
||||
EOF
|
||||
```
|
||||
|
||||
The install dialog, plugin detail, plugin settings, and jobs log screenshots need manual click interactions — handle them one-by-one after the batch above.
|
||||
|
||||
---
|
||||
|
||||
## Capture script — IA refresh batch (reference)
|
||||
|
||||
Fill in the placeholder IDs from the demo instance before running. Manual click steps (tabs on Costs, dialog openings on Adapters and Skills, Issue chat vs activity) still need to be captured one-by-one after the batch finishes its URL-only targets.
|
||||
|
||||
```bash
|
||||
cd /path/to/dev-browser && npx tsx <<'EOF'
|
||||
import { connect, waitForPageLoad } from "@/client.js";
|
||||
|
||||
const BASE = "http://localhost:3100";
|
||||
const SLUG = "ACME";
|
||||
const AGENT_ID = "REPLACE_AGENT_ID";
|
||||
const PROJECT_ID = "REPLACE_PROJECT_ID";
|
||||
const GOAL_ID = "REPLACE_GOAL_ID";
|
||||
const ROUTINE_ID = "REPLACE_ROUTINE_ID";
|
||||
const ISSUE_ID = "REPLACE_ISSUE_ID";
|
||||
const WORKSPACE_ID = "REPLACE_WORKSPACE_ID";
|
||||
const CLI_AUTH_ID = "REPLACE_CLI_AUTH_ID";
|
||||
const BOARD_CLAIM = "REPLACE_BOARD_CLAIM_TOKEN";
|
||||
const OUT = "/path/to/paperclip-docs/docs/user-guides/screenshots/light";
|
||||
|
||||
const shots: [string, string][] = [
|
||||
// Agents
|
||||
[`/${SLUG}/agents/all`, `${OUT}/agents/list.png`],
|
||||
[`/${SLUG}/agents/new`, `${OUT}/agents/new.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/dashboard`, `${OUT}/agents/dashboard.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/instructions`, `${OUT}/agents/instructions.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/skills`, `${OUT}/agents/skills.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/configuration`, `${OUT}/agents/configuration.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/runs`, `${OUT}/agents/runs.png`],
|
||||
[`/${SLUG}/agents/${AGENT_ID}/budget`, `${OUT}/agents/budget.png`],
|
||||
|
||||
// Costs (overview only via URL; other tabs need manual clicks)
|
||||
[`/${SLUG}/costs`, `${OUT}/costs/overview.png`],
|
||||
|
||||
// Issues & inbox
|
||||
[`/${SLUG}/issues`, `${OUT}/issues/list.png`],
|
||||
[`/${SLUG}/inbox/mine`, `${OUT}/issues/inbox.png`],
|
||||
[`/${SLUG}/inbox/unread`, `${OUT}/issues/inbox-unread.png`],
|
||||
[`/${SLUG}/inbox/requests`, `${OUT}/issues/inbox-requests.png`],
|
||||
[`/${SLUG}/issues/${ISSUE_ID}`, `${OUT}/issues/detail-chat.png`],
|
||||
|
||||
// Projects
|
||||
[`/${SLUG}/projects`, `${OUT}/projects/list.png`],
|
||||
[`/${SLUG}/projects/${PROJECT_ID}/overview`, `${OUT}/projects/overview.png`],
|
||||
[`/${SLUG}/projects/${PROJECT_ID}/issues`, `${OUT}/projects/issues.png`],
|
||||
[`/${SLUG}/projects/${PROJECT_ID}/workspaces`, `${OUT}/projects/workspaces.png`],
|
||||
[`/${SLUG}/projects/${PROJECT_ID}/configuration`, `${OUT}/projects/configuration.png`],
|
||||
[`/${SLUG}/projects/${PROJECT_ID}/budget`, `${OUT}/projects/budget.png`],
|
||||
|
||||
// Goals
|
||||
[`/${SLUG}/goals`, `${OUT}/goals/list.png`],
|
||||
[`/${SLUG}/goals/${GOAL_ID}`, `${OUT}/goals/detail.png`],
|
||||
|
||||
// Routines
|
||||
[`/${SLUG}/routines`, `${OUT}/routines/list.png`],
|
||||
[`/${SLUG}/routines/${ROUTINE_ID}`, `${OUT}/routines/detail.png`],
|
||||
|
||||
// Execution workspaces
|
||||
[`/${SLUG}/execution-workspaces/${WORKSPACE_ID}/configuration`, `${OUT}/workspaces/configuration.png`],
|
||||
[`/${SLUG}/execution-workspaces/${WORKSPACE_ID}/runtime-logs`, `${OUT}/workspaces/runtime-logs.png`],
|
||||
[`/${SLUG}/execution-workspaces/${WORKSPACE_ID}/issues`, `${OUT}/workspaces/issues.png`],
|
||||
|
||||
// Adapters (user)
|
||||
[`/instance/settings/adapters`, `${OUT}/adapters/list.png`],
|
||||
|
||||
// Org
|
||||
[`/${SLUG}/org`, `${OUT}/org/chart.png`],
|
||||
|
||||
// Skills
|
||||
[`/${SLUG}/skills`, `${OUT}/skills/list.png`],
|
||||
|
||||
// CLI auth & board claim
|
||||
[`/cli-auth/${CLI_AUTH_ID}`, `${OUT}/auth/cli-auth.png`],
|
||||
[`/board-claim/${BOARD_CLAIM}`, `${OUT}/auth/board-claim.png`],
|
||||
];
|
||||
|
||||
const client = await connect();
|
||||
const page = await client.page("paperclip", { viewport: { width: 1440, height: 900 } });
|
||||
for (const [path, out] of shots) {
|
||||
await page.goto(BASE + path, { waitUntil: "networkidle", timeout: 15000 }).catch(()=>{});
|
||||
await page.waitForTimeout(1200);
|
||||
await page.screenshot({ path: out });
|
||||
console.log("ok", path);
|
||||
}
|
||||
await client.disconnect();
|
||||
EOF
|
||||
```
|
||||
|
||||
Manual follow-ups after the batch:
|
||||
|
||||
- **Costs**: click the Budgets / Providers / Billers / Finance tabs one at a time, capture each as `costs/{budgets,providers,billers,finance}.png`.
|
||||
- **Issues detail**: on `/{slug}/issues/{issueId}`, click the **Activity** tab and capture `issues/detail-activity.png`.
|
||||
- **Adapters**: click **Add adapter** for `adapters/install.png`; expand an installed adapter row for `adapters/detail.png`.
|
||||
- **Skills**: click **Add skill** and capture both the initial form (`skills/add-skill-dialog.png`) and the confirm state (`skills/add-skill-dialog-confirm.png`); open a skill row for `skills/file-inventory.png`.
|
||||
|
||||
---
|
||||
|
||||
## After capture
|
||||
|
||||
- Verify no real user data (avatars, company names, task titles, emails) is visible in any PNG.
|
||||
- Commit the new PNGs alongside the markdown files in `docs/user-guides/guides/administration/`.
|
||||
@@ -0,0 +1,206 @@
|
||||
#!/usr/bin/env node
|
||||
// Rewrite internal markdown links after the P1 IA refactor.
|
||||
//
|
||||
// Strategy:
|
||||
// - We know every file's OLD path and NEW path (OLD_TO_NEW below).
|
||||
// - A file at NEW path contains relative links written against the OLD
|
||||
// directory layout. For each link:
|
||||
// 1. Resolve it against the source file's OLD path.
|
||||
// 2. Look up the resolved target in OLD_TO_NEW to get its NEW path.
|
||||
// 3. Rewrite the link as a path relative to the source file's NEW path.
|
||||
//
|
||||
// Links to files not in OLD_TO_NEW (for example images under
|
||||
// docs/user-guides/screenshots/) are left alone, but their path is re-anchored
|
||||
// to the new source location so they still resolve.
|
||||
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { readdir } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const REPO_ROOT = path.resolve(path.dirname(__filename), '..');
|
||||
const DOCS_DIR = path.join(REPO_ROOT, 'docs');
|
||||
|
||||
// OLD absolute docs path -> NEW absolute docs path. Both rooted at REPO_ROOT.
|
||||
const OLD_TO_NEW = {
|
||||
// welcome
|
||||
'docs/user-guides/guides/what-is-paperclip.md': 'docs/guides/welcome/what-is-paperclip.md',
|
||||
'docs/user-guides/guides/key-concepts.md': 'docs/guides/welcome/key-concepts.md',
|
||||
'docs/user-guides/guides/glossary.md': 'docs/guides/welcome/glossary.md',
|
||||
|
||||
// getting-started
|
||||
'docs/user-guides/guides/installation.md': 'docs/guides/getting-started/installation.md',
|
||||
'docs/user-guides/guides/your-first-company.md': 'docs/guides/getting-started/your-first-company.md',
|
||||
'docs/user-guides/guides/your-first-agent.md': 'docs/guides/getting-started/your-first-agent.md',
|
||||
'docs/user-guides/guides/watching-agents-work.md':'docs/guides/getting-started/watching-agents-work.md',
|
||||
|
||||
// day-to-day
|
||||
'docs/user-guides/guides/dashboard.md': 'docs/guides/day-to-day/dashboard.md',
|
||||
'docs/user-guides/guides/managing-tasks.md': 'docs/guides/day-to-day/managing-tasks.md',
|
||||
'docs/user-guides/guides/approvals.md': 'docs/guides/day-to-day/approvals.md',
|
||||
'docs/user-guides/guides/costs-and-budgets.md': 'docs/guides/day-to-day/costs-and-budgets.md',
|
||||
'docs/user-guides/guides/activity-log.md': 'docs/guides/day-to-day/activity-log.md',
|
||||
'docs/user-guides/guides/feedback-voting.md': 'docs/guides/day-to-day/feedback-voting.md',
|
||||
|
||||
// org
|
||||
'docs/guides/agent-developer/how-agents-work.md': 'docs/guides/org/how-agents-work.md',
|
||||
'docs/user-guides/guides/skills.md': 'docs/guides/org/skills.md',
|
||||
'docs/user-guides/guides/org-structure.md': 'docs/guides/org/org-structure.md',
|
||||
'docs/user-guides/guides/delegation.md': 'docs/guides/org/delegation.md',
|
||||
'docs/user-guides/guides/agent-adapters.md': 'docs/guides/org/agent-adapters.md',
|
||||
|
||||
// projects-workflow
|
||||
'docs/user-guides/guides/heartbeats-and-routines.md': 'docs/guides/projects-workflow/heartbeats-and-routines.md',
|
||||
'docs/user-guides/guides/execution-workspaces.md': 'docs/guides/projects-workflow/execution-workspaces.md',
|
||||
|
||||
// power
|
||||
'docs/user-guides/guides/execution-policy.md': 'docs/guides/power/execution-policy.md',
|
||||
'docs/user-guides/guides/export-import.md': 'docs/guides/power/export-import.md',
|
||||
'docs/user-guides/guides/terminal-setup.md': 'docs/guides/power/terminal-setup.md',
|
||||
|
||||
// administration
|
||||
'docs/user-guides/guides/administration/company.md': 'docs/administration/company.md',
|
||||
'docs/user-guides/guides/administration/settings.md': 'docs/administration/settings.md',
|
||||
'docs/user-guides/guides/administration/plugins.md': 'docs/administration/plugins.md',
|
||||
};
|
||||
|
||||
// Bulk: api/cli/adapters/deploy
|
||||
const BULK = [
|
||||
['docs/api', 'docs/reference/api'],
|
||||
['docs/cli', 'docs/reference/cli'],
|
||||
['docs/adapters', 'docs/reference/adapters'],
|
||||
['docs/deploy', 'docs/reference/deploy'],
|
||||
];
|
||||
// Populate BULK mappings by listing the new dirs (which contain the same file
|
||||
// names).
|
||||
const bulkFiles = [
|
||||
// api (12)
|
||||
'overview.md','authentication.md','companies.md','agents.md','issues.md',
|
||||
'approvals.md','goals-and-projects.md','costs.md','secrets.md','activity.md',
|
||||
'dashboard.md','routines.md',
|
||||
];
|
||||
const cliFiles = ['overview.md','setup-commands.md','control-plane-commands.md'];
|
||||
const adapterFiles = [
|
||||
'overview.md','claude-local.md','codex-local.md','gemini-local.md','cursor-local.md',
|
||||
'opencode-local.md','pi-local.md','hermes-local.md','openclaw-gateway.md','process.md',
|
||||
'http.md','external-adapters.md','creating-an-adapter.md','adapter-ui-parser.md',
|
||||
];
|
||||
const deployFiles = [
|
||||
'overview.md','deployment-modes.md','local-development.md','docker.md','database.md',
|
||||
'storage.md','secrets.md','environment-variables.md','tailscale-private-access.md',
|
||||
];
|
||||
for (const f of bulkFiles) OLD_TO_NEW[`docs/api/${f}`] = `docs/reference/api/${f}`;
|
||||
for (const f of cliFiles) OLD_TO_NEW[`docs/cli/${f}`] = `docs/reference/cli/${f}`;
|
||||
for (const f of adapterFiles)OLD_TO_NEW[`docs/adapters/${f}`] = `docs/reference/adapters/${f}`;
|
||||
for (const f of deployFiles) OLD_TO_NEW[`docs/deploy/${f}`] = `docs/reference/deploy/${f}`;
|
||||
|
||||
// Build reverse: for every NEW path, what is its OLD? (for walking files on disk)
|
||||
const NEW_TO_OLD = {};
|
||||
for (const [oldP, newP] of Object.entries(OLD_TO_NEW)) NEW_TO_OLD[newP] = oldP;
|
||||
|
||||
async function* walk(dir) {
|
||||
const entries = await readdir(dir, { withFileTypes: true });
|
||||
for (const e of entries) {
|
||||
const full = path.join(dir, e.name);
|
||||
if (e.isDirectory()) yield* walk(full);
|
||||
else if (e.isFile() && full.endsWith('.md')) yield full;
|
||||
}
|
||||
}
|
||||
|
||||
const LINK_RE = /(!?\[[^\]]*\]\()([^)\s]+?)(\s+"[^"]*")?\)/g;
|
||||
|
||||
function rewriteLinksInFile(absNewPath) {
|
||||
const relNew = path.relative(REPO_ROOT, absNewPath).replaceAll(path.sep, '/');
|
||||
// If this file was never moved, it stayed at relNew. Its OLD path == relNew.
|
||||
const relOld = NEW_TO_OLD[relNew] || relNew;
|
||||
const oldDir = path.posix.dirname(relOld);
|
||||
const newDir = path.posix.dirname(relNew);
|
||||
|
||||
const src = readFileSync(absNewPath, 'utf8');
|
||||
let linksRewritten = 0;
|
||||
let linksBroken = [];
|
||||
|
||||
const out = src.replace(LINK_RE, (match, prefix, href, titlePart) => {
|
||||
// Leave anchors, http(s), mailto, absolute paths, protocol-relative alone.
|
||||
if (!href || href.startsWith('#') || /^[a-z]+:/i.test(href) || href.startsWith('//')) {
|
||||
return match;
|
||||
}
|
||||
// Skip query-only or absolute site-root references.
|
||||
if (href.startsWith('/')) return match;
|
||||
|
||||
// Split fragment
|
||||
const hashIdx = href.indexOf('#');
|
||||
const pathPart = hashIdx === -1 ? href : href.slice(0, hashIdx);
|
||||
const fragment = hashIdx === -1 ? '' : href.slice(hashIdx);
|
||||
if (!pathPart) return match;
|
||||
|
||||
// Resolve relative to OLD dir (that's how it was originally written).
|
||||
const resolvedOld = path.posix.normalize(path.posix.join(oldDir, pathPart));
|
||||
|
||||
let resolvedNew;
|
||||
if (OLD_TO_NEW[resolvedOld]) {
|
||||
resolvedNew = OLD_TO_NEW[resolvedOld];
|
||||
} else if (pathPart.endsWith('.md')) {
|
||||
// Link may have been written with the NEW layout already (e.g. editors
|
||||
// updated paths), or points outside the move set. Try the new layout
|
||||
// relative to new dir first.
|
||||
const tryNew = path.posix.normalize(path.posix.join(newDir, pathPart));
|
||||
// If that file exists on disk (we can't easily stat here without fs,
|
||||
// but readFileSync will already have errored), we just leave it.
|
||||
// Instead, consider: does the OLD resolution even exist in OLD_TO_NEW
|
||||
// as a key? If not and it's an .md link within docs/, warn.
|
||||
if (resolvedOld.startsWith('docs/') && !NEW_TO_OLD[resolvedOld]) {
|
||||
// resolvedOld is not a moved file; could be an unchanged file
|
||||
// (e.g. screenshots/SCREENSHOTS_TODO.md). Keep rewritten relative to
|
||||
// newDir so it still resolves from the new location.
|
||||
resolvedNew = resolvedOld;
|
||||
} else {
|
||||
resolvedNew = tryNew;
|
||||
}
|
||||
} else {
|
||||
// Non-md relative link (image, etc). Re-anchor to new dir.
|
||||
const resolvedTarget = path.posix.normalize(path.posix.join(oldDir, pathPart));
|
||||
resolvedNew = resolvedTarget;
|
||||
}
|
||||
|
||||
// Compute new relative link from the source's NEW dir to resolvedNew.
|
||||
let newRel = path.posix.relative(newDir, resolvedNew);
|
||||
if (!newRel.startsWith('.') && !newRel.startsWith('/')) newRel = './' + newRel;
|
||||
const rewritten = newRel + fragment;
|
||||
|
||||
if (rewritten !== href) {
|
||||
linksRewritten++;
|
||||
return `${prefix}${rewritten}${titlePart || ''})`;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
if (out !== src) {
|
||||
writeFileSync(absNewPath, out, 'utf8');
|
||||
}
|
||||
return { linksRewritten, linksBroken, changed: out !== src };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let filesChanged = 0;
|
||||
let totalLinks = 0;
|
||||
const touched = [];
|
||||
for await (const abs of walk(DOCS_DIR)) {
|
||||
const { linksRewritten, changed } = rewriteLinksInFile(abs);
|
||||
if (changed) {
|
||||
filesChanged++;
|
||||
touched.push(path.relative(REPO_ROOT, abs));
|
||||
}
|
||||
totalLinks += linksRewritten;
|
||||
}
|
||||
console.log(`\nRewrite summary:`);
|
||||
console.log(` Files changed : ${filesChanged}`);
|
||||
console.log(` Links updated : ${totalLinks}`);
|
||||
if (touched.length) {
|
||||
console.log(`\n Touched files:`);
|
||||
for (const t of touched.sort()) console.log(` - ${t}`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(e => { console.error(e); process.exit(1); });
|
||||
+75
-27
@@ -8,9 +8,11 @@ function sectionIconTag(section) {
|
||||
function renderLucideIcons() {
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
}
|
||||
function getSectionKind(title) {
|
||||
return /api|cli|adapter|deploy/i.test(title) ? 'Reference' : 'Guide';
|
||||
function getSectionKind(section) {
|
||||
if (section && typeof section === 'object') return section.tier || 'Guides';
|
||||
return 'Guides';
|
||||
}
|
||||
const TIER_ORDER = ['Guides', 'Administration', 'Reference'];
|
||||
|
||||
/* ─── Theme bootstrap (before first paint of body) ──────────────────────── */
|
||||
(function() {
|
||||
@@ -516,6 +518,17 @@ function initSearch() {
|
||||
}
|
||||
|
||||
/* ─── Boot ──────────────────────────────────────────────────────────────── */
|
||||
let redirectMap = {};
|
||||
|
||||
function applyRedirect(route) {
|
||||
const key = normalizeRouteKey(decodeURIComponent((route || '').trim()));
|
||||
if (!key) return route;
|
||||
if (Object.prototype.hasOwnProperty.call(redirectMap, key)) {
|
||||
return redirectMap[key];
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
async function init() {
|
||||
try {
|
||||
const res = await fetch(resolveContentUrl('content.json'));
|
||||
@@ -526,6 +539,12 @@ async function init() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional: load redirect map for moved pages (old → new slug).
|
||||
try {
|
||||
const rRes = await fetch(resolveContentUrl('redirects.json'));
|
||||
if (rRes.ok) redirectMap = await rRes.json();
|
||||
} catch { /* no redirects file is fine */ }
|
||||
|
||||
buildFlatList();
|
||||
document.getElementById('logo').href = getRouteUrl('');
|
||||
buildLanding();
|
||||
@@ -535,7 +554,7 @@ async function init() {
|
||||
buildSearchIndex(); // background — no await
|
||||
|
||||
const pathRoute = getPathRoute();
|
||||
const rawRoute = pathRoute || getLegacyRoute();
|
||||
const rawRoute = applyRedirect(pathRoute || getLegacyRoute());
|
||||
const initialRoute = parseRoute(rawRoute);
|
||||
const normalizedRaw = normalizeRouteKey(decodeURIComponent((rawRoute || '').trim()));
|
||||
|
||||
@@ -551,19 +570,42 @@ async function init() {
|
||||
function buildLanding() {
|
||||
const grid = document.getElementById('landing-cards');
|
||||
grid.innerHTML = '';
|
||||
|
||||
// Group sections by tier, preserving original indices so data-nav-section still works.
|
||||
const byTier = new Map();
|
||||
navData.sections.forEach((section, i) => {
|
||||
const a = document.createElement('a');
|
||||
a.className = 'card';
|
||||
a.href = section.pages[0] ? getPageUrl(section.pages[0]) : '#';
|
||||
a.dataset.navSection = i;
|
||||
const desc = section.desc || `${section.pages.length} page${section.pages.length === 1 ? '' : 's'} in ${section.title}.`;
|
||||
a.innerHTML = `
|
||||
<div class="card-icon">${sectionIconTag(section)}</div>
|
||||
<div class="card-title">${escapeHtml(section.title)}</div>
|
||||
<div class="card-desc">${escapeHtml(desc)}</div>
|
||||
<div class="card-meta"><span>${section.pages.length} page${section.pages.length === 1 ? '' : 's'}</span><span class="dot"></span><span>${getSectionKind(section.title)}</span></div>
|
||||
`;
|
||||
grid.appendChild(a);
|
||||
const tier = getSectionKind(section);
|
||||
if (!byTier.has(tier)) byTier.set(tier, []);
|
||||
byTier.get(tier).push({ section, i });
|
||||
});
|
||||
const orderedTiers = [
|
||||
...TIER_ORDER.filter(t => byTier.has(t)),
|
||||
...[...byTier.keys()].filter(t => !TIER_ORDER.includes(t)),
|
||||
];
|
||||
|
||||
orderedTiers.forEach(tier => {
|
||||
const block = document.createElement('section');
|
||||
block.className = 'landing-tier';
|
||||
block.dataset.tier = tier;
|
||||
const n = byTier.get(tier).length;
|
||||
const cols = n <= 3 ? n : (n === 4 ? 2 : 3);
|
||||
block.innerHTML = `<h2>${escapeHtml(tier)}</h2><div class="landing-tier-cards" style="--tier-cols:${cols}"></div>`;
|
||||
const cardsWrap = block.querySelector('.landing-tier-cards');
|
||||
byTier.get(tier).forEach(({ section, i }) => {
|
||||
const a = document.createElement('a');
|
||||
a.className = 'card';
|
||||
a.href = section.pages[0] ? getPageUrl(section.pages[0]) : '#';
|
||||
a.dataset.navSection = i;
|
||||
const desc = section.desc || `${section.pages.length} page${section.pages.length === 1 ? '' : 's'} in ${section.title}.`;
|
||||
a.innerHTML = `
|
||||
<div class="card-icon">${sectionIconTag(section)}</div>
|
||||
<div class="card-title">${escapeHtml(section.title)}</div>
|
||||
<div class="card-desc">${escapeHtml(desc)}</div>
|
||||
<div class="card-meta"><span>${section.pages.length} page${section.pages.length === 1 ? '' : 's'}</span><span class="dot"></span><span>${escapeHtml(getSectionKind(section))}</span></div>
|
||||
`;
|
||||
cardsWrap.appendChild(a);
|
||||
});
|
||||
grid.appendChild(block);
|
||||
});
|
||||
|
||||
const ql = document.getElementById('landing-quicklinks');
|
||||
@@ -586,15 +628,20 @@ function buildLanding() {
|
||||
|
||||
/* ─── Sidebar (accordion) — used for desktop sidebar AND mobile drawer ──── */
|
||||
function sidebarSectionsHTML() {
|
||||
let lastKind = null;
|
||||
return navData.sections.map((section, si) => {
|
||||
const kind = getSectionKind(section.title);
|
||||
let label = '';
|
||||
if (kind !== lastKind) {
|
||||
label = `<div class="sb-group-label">${escapeHtml(kind === 'Reference' ? 'Reference' : 'Guides')}</div>`;
|
||||
lastKind = kind;
|
||||
}
|
||||
return `${label}
|
||||
const byTier = new Map();
|
||||
navData.sections.forEach((section, si) => {
|
||||
const tier = getSectionKind(section);
|
||||
if (!byTier.has(tier)) byTier.set(tier, []);
|
||||
byTier.get(tier).push({ section, si });
|
||||
});
|
||||
const orderedTiers = [
|
||||
...TIER_ORDER.filter(t => byTier.has(t)),
|
||||
...[...byTier.keys()].filter(t => !TIER_ORDER.includes(t)),
|
||||
];
|
||||
|
||||
return orderedTiers.map(tier => {
|
||||
const header = `<div class="sb-tier-header">${escapeHtml(tier)}</div>`;
|
||||
const sections = byTier.get(tier).map(({ section, si }) => `
|
||||
<div class="sb-section" data-section-idx="${si}" data-section-title="${escapeAttr(section.title)}" data-open="false">
|
||||
<button class="sb-section-btn" type="button">
|
||||
<span class="sb-section-icon">${sectionIconTag(section)}</span>
|
||||
@@ -605,7 +652,8 @@ function sidebarSectionsHTML() {
|
||||
<div class="sb-pages">
|
||||
${section.pages.map(page => `<a class="sb-link" data-file="${escapeAttr(page.file)}" href="${escapeAttr(getPageUrl(page))}">${escapeHtml(page.title)}</a>`).join('')}
|
||||
</div>
|
||||
</div>`;
|
||||
</div>`).join('');
|
||||
return header + sections;
|
||||
}).join('');
|
||||
}
|
||||
|
||||
@@ -1177,7 +1225,7 @@ function escapeHtml(s) {
|
||||
function escapeAttr(s) { return String(s).replace(/"/g,'"'); }
|
||||
|
||||
window.addEventListener('hashchange', () => {
|
||||
const route = parseRoute(location.hash.slice(1));
|
||||
const route = parseRoute(applyRedirect(location.hash.slice(1)));
|
||||
if (route.page) {
|
||||
loadPage(route.page.file, route.headingId, 'replace');
|
||||
} else {
|
||||
@@ -1187,7 +1235,7 @@ window.addEventListener('hashchange', () => {
|
||||
});
|
||||
|
||||
window.addEventListener('popstate', () => {
|
||||
const route = parseRoute(getCurrentRoute());
|
||||
const route = parseRoute(applyRedirect(getCurrentRoute()));
|
||||
if (route.page) {
|
||||
loadPage(route.page.file, route.headingId, 'replace');
|
||||
return;
|
||||
|
||||
+107
-82
@@ -1,126 +1,151 @@
|
||||
{
|
||||
"sections": [
|
||||
{
|
||||
"title": "Welcome",
|
||||
"icon": "book",
|
||||
"desc": "Learn what Paperclip is, how it thinks, and the vocabulary the rest of the docs will use.",
|
||||
"pages": [
|
||||
{ "title": "What is Paperclip?", "file": "../docs/user-guides/guides/what-is-paperclip.md" },
|
||||
{ "title": "Key Concepts", "file": "../docs/user-guides/guides/key-concepts.md" },
|
||||
{ "title": "Glossary", "file": "../docs/user-guides/guides/glossary.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Get Started",
|
||||
"tier": "Guides",
|
||||
"title": "Getting Started",
|
||||
"icon": "rocket",
|
||||
"desc": "Install Paperclip, create your first company, hire an agent, and watch it work.",
|
||||
"desc": "Learn what Paperclip is, install it, create your first company, hire an agent, and watch it work.",
|
||||
"pages": [
|
||||
{ "title": "Installation", "file": "../docs/user-guides/guides/installation.md" },
|
||||
{ "title": "Create Your First Company", "file": "../docs/user-guides/guides/your-first-company.md" },
|
||||
{ "title": "Hire Your First Agent", "file": "../docs/user-guides/guides/your-first-agent.md" },
|
||||
{ "title": "Watching Agents Work", "file": "../docs/user-guides/guides/watching-agents-work.md" }
|
||||
{ "title": "What is Paperclip?", "file": "../docs/guides/welcome/what-is-paperclip.md" },
|
||||
{ "title": "Key Concepts", "file": "../docs/guides/welcome/key-concepts.md" },
|
||||
{ "title": "Installation", "file": "../docs/guides/getting-started/installation.md" },
|
||||
{ "title": "Create Your First Company", "file": "../docs/guides/getting-started/your-first-company.md" },
|
||||
{ "title": "Hire Your First Agent", "file": "../docs/guides/getting-started/your-first-agent.md" },
|
||||
{ "title": "Watching Agents Work", "file": "../docs/guides/getting-started/watching-agents-work.md" },
|
||||
{ "title": "Glossary", "file": "../docs/guides/welcome/glossary.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Running Your Company",
|
||||
"tier": "Guides",
|
||||
"title": "Working Day-to-day",
|
||||
"icon": "layout-dashboard",
|
||||
"desc": "Day-to-day operation: the dashboard, tasks, approvals, budgets, and the activity log.",
|
||||
"desc": "Day-to-day operation: dashboard, tasks, approvals, budgets, and the activity log.",
|
||||
"pages": [
|
||||
{ "title": "The Dashboard", "file": "../docs/user-guides/guides/dashboard.md" },
|
||||
{ "title": "Managing Tasks", "file": "../docs/user-guides/guides/managing-tasks.md" },
|
||||
{ "title": "Approvals", "file": "../docs/user-guides/guides/approvals.md" },
|
||||
{ "title": "Costs & Budgets", "file": "../docs/user-guides/guides/costs-and-budgets.md" },
|
||||
{ "title": "Activity Log", "file": "../docs/user-guides/guides/activity-log.md" },
|
||||
{ "title": "Feedback & Voting", "file": "../docs/user-guides/guides/feedback-voting.md" }
|
||||
{ "title": "The Dashboard", "file": "../docs/guides/day-to-day/dashboard.md" },
|
||||
{ "title": "Issues", "file": "../docs/guides/day-to-day/issues.md" },
|
||||
{ "title": "Approvals", "file": "../docs/guides/day-to-day/approvals.md" },
|
||||
{ "title": "Costs & Budgets", "file": "../docs/guides/day-to-day/costs.md" },
|
||||
{ "title": "Activity Log", "file": "../docs/guides/day-to-day/activity-log.md" },
|
||||
{ "title": "Feedback & Voting", "file": "../docs/guides/day-to-day/feedback-voting.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Building Your Org",
|
||||
"tier": "Guides",
|
||||
"title": "Your Org & Agents",
|
||||
"icon": "network",
|
||||
"desc": "Shape the org chart, set up delegation paths, and pick adapters for your agents.",
|
||||
"pages": [
|
||||
{ "title": "Org Structure", "file": "../docs/user-guides/guides/org-structure.md" },
|
||||
{ "title": "Delegation", "file": "../docs/user-guides/guides/delegation.md" },
|
||||
{ "title": "Adapters", "file": "../docs/user-guides/guides/agent-adapters.md" }
|
||||
{ "title": "Agents", "file": "../docs/guides/org/agents.md" },
|
||||
{ "title": "Skills", "file": "../docs/guides/org/skills.md" },
|
||||
{ "title": "Org Structure", "file": "../docs/guides/org/org-structure.md" },
|
||||
{ "title": "Delegation", "file": "../docs/guides/org/delegation.md" },
|
||||
{ "title": "Agent Adapters", "file": "../docs/guides/org/agent-adapters.md" },
|
||||
{ "title": "Adapter Manager", "file": "../docs/guides/org/adapters.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Advanced",
|
||||
"icon": "settings-2",
|
||||
"desc": "Execution workspaces, routines, skills, and import/export for power users.",
|
||||
"tier": "Guides",
|
||||
"title": "Projects & Workflow",
|
||||
"icon": "kanban-square",
|
||||
"desc": "Projects, goals, routines, and execution workspaces.",
|
||||
"pages": [
|
||||
{ "title": "Execution Workspaces", "file": "../docs/user-guides/guides/execution-workspaces.md" },
|
||||
{ "title": "Execution Policy", "file": "../docs/user-guides/guides/execution-policy.md" },
|
||||
{ "title": "Heartbeats & Routines", "file": "../docs/user-guides/guides/heartbeats-and-routines.md" },
|
||||
{ "title": "Skills", "file": "../docs/user-guides/guides/skills.md" },
|
||||
{ "title": "Export & Import", "file": "../docs/user-guides/guides/export-import.md" },
|
||||
{ "title": "Terminal Setup", "file": "../docs/user-guides/guides/terminal-setup.md" }
|
||||
{ "title": "Projects", "file": "../docs/guides/projects-workflow/projects.md" },
|
||||
{ "title": "Goals", "file": "../docs/guides/projects-workflow/goals.md" },
|
||||
{ "title": "Heartbeats & Routines", "file": "../docs/guides/projects-workflow/routines.md" },
|
||||
{ "title": "Execution Workspaces", "file": "../docs/guides/projects-workflow/workspaces.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Guides",
|
||||
"title": "Power Features",
|
||||
"icon": "settings-2",
|
||||
"desc": "Execution policies, export/import, and terminal setup for power users.",
|
||||
"pages": [
|
||||
{ "title": "Execution Policy", "file": "../docs/guides/power/execution-policy.md" },
|
||||
{ "title": "Export & Import", "file": "../docs/guides/power/export-import.md" },
|
||||
{ "title": "Terminal Setup", "file": "../docs/guides/power/terminal-setup.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Guides",
|
||||
"title": "Administration",
|
||||
"icon": "shield",
|
||||
"desc": "Company settings, instance configuration, plugins, and CLI auth — for operators and admins.",
|
||||
"pages": [
|
||||
{ "title": "Company", "file": "../docs/administration/company.md" },
|
||||
{ "title": "Settings", "file": "../docs/administration/settings.md" },
|
||||
{ "title": "Plugins", "file": "../docs/administration/plugins.md" },
|
||||
{ "title": "CLI Auth", "file": "../docs/administration/cli-auth.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Reference",
|
||||
"title": "API",
|
||||
"icon": "braces",
|
||||
"desc": "Every endpoint on the control plane: companies, agents, issues, approvals, costs.",
|
||||
"pages": [
|
||||
{ "title": "Overview", "file": "../docs/reference/api/overview.md" },
|
||||
{ "title": "Authentication", "file": "../docs/reference/api/authentication.md" },
|
||||
{ "title": "Companies", "file": "../docs/reference/api/companies.md" },
|
||||
{ "title": "Agents", "file": "../docs/reference/api/agents.md" },
|
||||
{ "title": "Issues", "file": "../docs/reference/api/issues.md" },
|
||||
{ "title": "Approvals", "file": "../docs/reference/api/approvals.md" },
|
||||
{ "title": "Goals & Projects", "file": "../docs/reference/api/goals-and-projects.md" },
|
||||
{ "title": "Costs", "file": "../docs/reference/api/costs.md" },
|
||||
{ "title": "Secrets", "file": "../docs/reference/api/secrets.md" },
|
||||
{ "title": "Activity", "file": "../docs/reference/api/activity.md" },
|
||||
{ "title": "Dashboard", "file": "../docs/reference/api/dashboard.md" },
|
||||
{ "title": "Routines", "file": "../docs/reference/api/routines.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Reference",
|
||||
"title": "CLI",
|
||||
"icon": "terminal",
|
||||
"desc": "Command-line reference for local setup and talking to the control plane.",
|
||||
"pages": [
|
||||
{ "title": "Overview", "file": "../docs/cli/overview.md" },
|
||||
{ "title": "Setup Commands", "file": "../docs/cli/setup-commands.md" },
|
||||
{ "title": "Control-Plane Commands", "file": "../docs/cli/control-plane-commands.md" }
|
||||
{ "title": "Overview", "file": "../docs/reference/cli/overview.md" },
|
||||
{ "title": "Setup Commands", "file": "../docs/reference/cli/setup-commands.md" },
|
||||
{ "title": "Control-Plane Commands", "file": "../docs/reference/cli/control-plane-commands.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Reference",
|
||||
"title": "Adapters",
|
||||
"icon": "plug",
|
||||
"desc": "Connect any agent runtime — Claude, Codex, Gemini, or your own HTTP service.",
|
||||
"pages": [
|
||||
{ "title": "Overview", "file": "../docs/adapters/overview.md" },
|
||||
{ "title": "Claude Local", "file": "../docs/adapters/claude-local.md" },
|
||||
{ "title": "Codex Local", "file": "../docs/adapters/codex-local.md" },
|
||||
{ "title": "Gemini Local", "file": "../docs/adapters/gemini-local.md" },
|
||||
{ "title": "Cursor Local", "file": "../docs/adapters/cursor-local.md" },
|
||||
{ "title": "OpenCode Local", "file": "../docs/adapters/opencode-local.md" },
|
||||
{ "title": "Pi Local", "file": "../docs/adapters/pi-local.md" },
|
||||
{ "title": "Hermes Local", "file": "../docs/adapters/hermes-local.md" },
|
||||
{ "title": "OpenClaw Gateway", "file": "../docs/adapters/openclaw-gateway.md" },
|
||||
{ "title": "Process", "file": "../docs/adapters/process.md" },
|
||||
{ "title": "HTTP", "file": "../docs/adapters/http.md" },
|
||||
{ "title": "External Adapters", "file": "../docs/adapters/external-adapters.md" },
|
||||
{ "title": "Creating an Adapter", "file": "../docs/adapters/creating-an-adapter.md" },
|
||||
{ "title": "UI Parser Contract", "file": "../docs/adapters/adapter-ui-parser.md" }
|
||||
{ "title": "Overview", "file": "../docs/reference/adapters/overview.md" },
|
||||
{ "title": "Claude Local", "file": "../docs/reference/adapters/claude-local.md" },
|
||||
{ "title": "Codex Local", "file": "../docs/reference/adapters/codex-local.md" },
|
||||
{ "title": "Gemini Local", "file": "../docs/reference/adapters/gemini-local.md" },
|
||||
{ "title": "Cursor Local", "file": "../docs/reference/adapters/cursor-local.md" },
|
||||
{ "title": "OpenCode Local", "file": "../docs/reference/adapters/opencode-local.md" },
|
||||
{ "title": "Pi Local", "file": "../docs/reference/adapters/pi-local.md" },
|
||||
{ "title": "Hermes Local", "file": "../docs/reference/adapters/hermes-local.md" },
|
||||
{ "title": "OpenClaw Gateway", "file": "../docs/reference/adapters/openclaw-gateway.md" },
|
||||
{ "title": "Process", "file": "../docs/reference/adapters/process.md" },
|
||||
{ "title": "HTTP", "file": "../docs/reference/adapters/http.md" },
|
||||
{ "title": "External Adapters", "file": "../docs/reference/adapters/external-adapters.md" },
|
||||
{ "title": "Creating an Adapter", "file": "../docs/reference/adapters/creating-an-adapter.md" },
|
||||
{ "title": "UI Parser Contract", "file": "../docs/reference/adapters/adapter-ui-parser.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"tier": "Reference",
|
||||
"title": "Deployment",
|
||||
"icon": "cloud",
|
||||
"desc": "Run Paperclip on your laptop, in Docker, or on a server — with secrets and Tailscale.",
|
||||
"pages": [
|
||||
{ "title": "Overview", "file": "../docs/deploy/overview.md" },
|
||||
{ "title": "Deployment Modes", "file": "../docs/deploy/deployment-modes.md" },
|
||||
{ "title": "Local Development", "file": "../docs/deploy/local-development.md" },
|
||||
{ "title": "Docker", "file": "../docs/deploy/docker.md" },
|
||||
{ "title": "Database", "file": "../docs/deploy/database.md" },
|
||||
{ "title": "Storage", "file": "../docs/deploy/storage.md" },
|
||||
{ "title": "Secrets", "file": "../docs/deploy/secrets.md" },
|
||||
{ "title": "Environment Variables", "file": "../docs/deploy/environment-variables.md" },
|
||||
{ "title": "Tailscale Private Access", "file": "../docs/deploy/tailscale-private-access.md" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "API Reference",
|
||||
"icon": "braces",
|
||||
"desc": "Every endpoint on the control plane: companies, agents, issues, approvals, costs.",
|
||||
"pages": [
|
||||
{ "title": "Overview", "file": "../docs/api/overview.md" },
|
||||
{ "title": "Authentication", "file": "../docs/api/authentication.md" },
|
||||
{ "title": "Companies", "file": "../docs/api/companies.md" },
|
||||
{ "title": "Agents", "file": "../docs/api/agents.md" },
|
||||
{ "title": "Issues", "file": "../docs/api/issues.md" },
|
||||
{ "title": "Approvals", "file": "../docs/api/approvals.md" },
|
||||
{ "title": "Goals & Projects", "file": "../docs/api/goals-and-projects.md" },
|
||||
{ "title": "Costs", "file": "../docs/api/costs.md" },
|
||||
{ "title": "Secrets", "file": "../docs/api/secrets.md" },
|
||||
{ "title": "Activity", "file": "../docs/api/activity.md" },
|
||||
{ "title": "Dashboard", "file": "../docs/api/dashboard.md" },
|
||||
{ "title": "Routines", "file": "../docs/api/routines.md" }
|
||||
{ "title": "Overview", "file": "../docs/reference/deploy/overview.md" },
|
||||
{ "title": "Deployment Modes", "file": "../docs/reference/deploy/deployment-modes.md" },
|
||||
{ "title": "Local Development", "file": "../docs/reference/deploy/local-development.md" },
|
||||
{ "title": "Docker", "file": "../docs/reference/deploy/docker.md" },
|
||||
{ "title": "Database", "file": "../docs/reference/deploy/database.md" },
|
||||
{ "title": "Storage", "file": "../docs/reference/deploy/storage.md" },
|
||||
{ "title": "Secrets", "file": "../docs/reference/deploy/secrets.md" },
|
||||
{ "title": "Environment Variables", "file": "../docs/reference/deploy/environment-variables.md" },
|
||||
{ "title": "Tailscale Private Access", "file": "../docs/reference/deploy/tailscale-private-access.md" }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"user-guides/guides/what-is-paperclip": "guides/welcome/what-is-paperclip",
|
||||
"user-guides/guides/key-concepts": "guides/welcome/key-concepts",
|
||||
"user-guides/guides/glossary": "guides/welcome/glossary",
|
||||
|
||||
"user-guides/guides/installation": "guides/getting-started/installation",
|
||||
"user-guides/guides/your-first-company": "guides/getting-started/your-first-company",
|
||||
"user-guides/guides/your-first-agent": "guides/getting-started/your-first-agent",
|
||||
"user-guides/guides/watching-agents-work": "guides/getting-started/watching-agents-work",
|
||||
|
||||
"user-guides/guides/dashboard": "guides/day-to-day/dashboard",
|
||||
"user-guides/guides/managing-tasks": "guides/day-to-day/issues",
|
||||
"guides/day-to-day/managing-tasks": "guides/day-to-day/issues",
|
||||
"user-guides/guides/approvals": "guides/day-to-day/approvals",
|
||||
"user-guides/guides/costs-and-budgets": "guides/day-to-day/costs",
|
||||
"guides/day-to-day/costs-and-budgets": "guides/day-to-day/costs",
|
||||
"user-guides/guides/activity-log": "guides/day-to-day/activity-log",
|
||||
"user-guides/guides/feedback-voting": "guides/day-to-day/feedback-voting",
|
||||
|
||||
"guides/agent-developer/how-agents-work": "guides/org/how-agents-work",
|
||||
"user-guides/guides/skills": "guides/org/skills",
|
||||
"user-guides/guides/org-structure": "guides/org/org-structure",
|
||||
"user-guides/guides/delegation": "guides/org/delegation",
|
||||
"user-guides/guides/agent-adapters": "guides/org/agent-adapters",
|
||||
|
||||
"user-guides/guides/heartbeats-and-routines": "guides/projects-workflow/routines",
|
||||
"guides/projects-workflow/heartbeats-and-routines": "guides/projects-workflow/routines",
|
||||
"user-guides/guides/execution-workspaces": "guides/projects-workflow/workspaces",
|
||||
"guides/projects-workflow/execution-workspaces": "guides/projects-workflow/workspaces",
|
||||
|
||||
"user-guides/guides/execution-policy": "guides/power/execution-policy",
|
||||
"user-guides/guides/export-import": "guides/power/export-import",
|
||||
"user-guides/guides/terminal-setup": "guides/power/terminal-setup",
|
||||
|
||||
"user-guides/guides/administration/company": "administration/company",
|
||||
"user-guides/guides/administration/settings": "administration/settings",
|
||||
"user-guides/guides/administration/plugins": "administration/plugins",
|
||||
|
||||
"api/overview": "reference/api/overview",
|
||||
"api/authentication": "reference/api/authentication",
|
||||
"api/companies": "reference/api/companies",
|
||||
"api/agents": "reference/api/agents",
|
||||
"api/issues": "reference/api/issues",
|
||||
"api/approvals": "reference/api/approvals",
|
||||
"api/goals-and-projects": "reference/api/goals-and-projects",
|
||||
"api/costs": "reference/api/costs",
|
||||
"api/secrets": "reference/api/secrets",
|
||||
"api/activity": "reference/api/activity",
|
||||
"api/dashboard": "reference/api/dashboard",
|
||||
"api/routines": "reference/api/routines",
|
||||
|
||||
"cli/overview": "reference/cli/overview",
|
||||
"cli/setup-commands": "reference/cli/setup-commands",
|
||||
"cli/control-plane-commands": "reference/cli/control-plane-commands",
|
||||
|
||||
"adapters/overview": "reference/adapters/overview",
|
||||
"adapters/claude-local": "reference/adapters/claude-local",
|
||||
"adapters/codex-local": "reference/adapters/codex-local",
|
||||
"adapters/gemini-local": "reference/adapters/gemini-local",
|
||||
"adapters/cursor-local": "reference/adapters/cursor-local",
|
||||
"adapters/opencode-local": "reference/adapters/opencode-local",
|
||||
"adapters/pi-local": "reference/adapters/pi-local",
|
||||
"adapters/hermes-local": "reference/adapters/hermes-local",
|
||||
"adapters/openclaw-gateway": "reference/adapters/openclaw-gateway",
|
||||
"adapters/process": "reference/adapters/process",
|
||||
"adapters/http": "reference/adapters/http",
|
||||
"adapters/external-adapters": "reference/adapters/external-adapters",
|
||||
"adapters/creating-an-adapter": "reference/adapters/creating-an-adapter",
|
||||
"adapters/adapter-ui-parser": "reference/adapters/adapter-ui-parser",
|
||||
|
||||
"deploy/overview": "reference/deploy/overview",
|
||||
"deploy/deployment-modes": "reference/deploy/deployment-modes",
|
||||
"deploy/local-development": "reference/deploy/local-development",
|
||||
"deploy/docker": "reference/deploy/docker",
|
||||
"deploy/database": "reference/deploy/database",
|
||||
"deploy/storage": "reference/deploy/storage",
|
||||
"deploy/secrets": "reference/deploy/secrets",
|
||||
"deploy/environment-variables": "reference/deploy/environment-variables",
|
||||
"deploy/tailscale-private-access": "reference/deploy/tailscale-private-access"
|
||||
}
|
||||
@@ -943,3 +943,41 @@
|
||||
.page-actions .pa-copy { padding-left: 10px; padding-right: 10px; }
|
||||
.toc-toggle { padding: 5px 10px; }
|
||||
}
|
||||
|
||||
/* ─── IA: tier headers (sidebar) and tier groupings (landing) ──────── */
|
||||
.sb-tier-header {
|
||||
display: block;
|
||||
padding: 20px 10px 8px;
|
||||
margin-top: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--ash);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.sb-tier-header:first-child { margin-top: 0; padding-top: 6px; }
|
||||
|
||||
.landing-tier { margin-top: 28px; }
|
||||
.landing-tier:first-child { margin-top: 0; }
|
||||
.landing-tier > h2 {
|
||||
margin: 0 0 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--ash);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.landing-tier-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(var(--tier-cols, 3), 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
#landing-cards.card-grid {
|
||||
display: block;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.landing-tier-cards { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.landing-tier-cards { grid-template-columns: 1fr; }
|
||||
}
|
||||
Reference in new issue
Block a user