Merge pull request #2414 from aronprins/skill/routines
feat(skills): add paperclip-routines skill
This commit is contained in:
commit
3db6bdfc3c
3 changed files with 259 additions and 4 deletions
|
|
@ -3,9 +3,10 @@ name: paperclip
|
|||
description: >
|
||||
Interact with the Paperclip control plane API to manage tasks, coordinate with
|
||||
other agents, and follow company governance. Use when you need to check
|
||||
assignments, update task status, delegate work, post comments, or call any
|
||||
Paperclip API endpoint. Do NOT use for the actual domain work itself (writing
|
||||
code, research, etc.) — only for Paperclip coordination.
|
||||
assignments, update task status, delegate work, post comments, set up or manage
|
||||
routines (recurring scheduled tasks), or call any Paperclip API endpoint. Do NOT
|
||||
use for the actual domain work itself (writing code, research, etc.) — only for
|
||||
Paperclip coordination.
|
||||
---
|
||||
|
||||
# Paperclip Skill
|
||||
|
|
@ -137,6 +138,17 @@ Authorized managers can install company skills independently of hiring, then ass
|
|||
If you are asked to install a skill for the company or an agent you MUST read:
|
||||
`skills/paperclip/references/company-skills.md`
|
||||
|
||||
## Routines
|
||||
|
||||
Routines are recurring tasks. Each time a routine fires it creates an execution issue assigned to the routine's agent — the agent picks it up in the normal heartbeat flow.
|
||||
|
||||
- Create and manage routines with the routines API — agents can only manage routines assigned to themselves.
|
||||
- Add triggers per routine: `schedule` (cron), `webhook`, or `api` (manual).
|
||||
- Control concurrency and catch-up behaviour with `concurrencyPolicy` and `catchUpPolicy`.
|
||||
|
||||
If you are asked to create or manage routines you MUST read:
|
||||
`skills/paperclip/references/routines.md`
|
||||
|
||||
## Critical Rules
|
||||
|
||||
- **Always checkout** before working. Never PATCH to `in_progress` manually.
|
||||
|
|
@ -291,6 +303,17 @@ PATCH /api/agents/{agentId}/instructions-path
|
|||
| List issue attachments | `GET /api/issues/:issueId/attachments` |
|
||||
| Get attachment content | `GET /api/attachments/:attachmentId/content` |
|
||||
| Delete attachment | `DELETE /api/attachments/:attachmentId` |
|
||||
| List routines | `GET /api/companies/:companyId/routines` |
|
||||
| Get routine | `GET /api/routines/:routineId` |
|
||||
| Create routine | `POST /api/companies/:companyId/routines` |
|
||||
| Update routine | `PATCH /api/routines/:routineId` |
|
||||
| Add trigger | `POST /api/routines/:routineId/triggers` |
|
||||
| Update trigger | `PATCH /api/routine-triggers/:triggerId` |
|
||||
| Delete trigger | `DELETE /api/routine-triggers/:triggerId` |
|
||||
| Rotate webhook secret | `POST /api/routine-triggers/:triggerId/rotate-secret` |
|
||||
| Manual run | `POST /api/routines/:routineId/run` |
|
||||
| Fire webhook (external) | `POST /api/routine-triggers/public/:publicId/fire` |
|
||||
| List runs | `GET /api/routines/:routineId/runs` |
|
||||
|
||||
## Company Import / Export
|
||||
|
||||
|
|
|
|||
|
|
@ -597,7 +597,15 @@ Terminal states: `done`, `cancelled`
|
|||
| GET | `/api/agents/me/inbox/mine?userId=:userId` | Mine-tab issue list for a specific board user |
|
||||
| GET | `/api/agents/:agentId` | Agent details + chain of command |
|
||||
| GET | `/api/companies/:companyId/agents` | List all agents in company |
|
||||
| POST | `/api/companies/:companyId/agents` | Create agent directly (no approval) |
|
||||
| PATCH | `/api/agents/:agentId` | Update agent config or budget |
|
||||
| POST | `/api/agents/:agentId/pause` | Temporarily stop heartbeats |
|
||||
| POST | `/api/agents/:agentId/resume` | Resume a paused agent |
|
||||
| POST | `/api/agents/:agentId/terminate` | Permanently deactivate agent (irreversible) |
|
||||
| POST | `/api/agents/:agentId/keys` | Create long-lived API key (full value shown once) |
|
||||
| POST | `/api/agents/:agentId/heartbeat/invoke` | Manually trigger a heartbeat |
|
||||
| GET | `/api/companies/:companyId/org` | Org chart tree |
|
||||
| GET | `/api/companies/:companyId/adapters/:adapterType/models` | List selectable models for an adapter type |
|
||||
| PATCH | `/api/agents/:agentId/instructions-path` | Set/clear instructions path (`AGENTS.md`) |
|
||||
| GET | `/api/agents/:agentId/config-revisions` | List config revisions |
|
||||
| POST | `/api/agents/:agentId/config-revisions/:revisionId/rollback` | Roll back config |
|
||||
|
|
@ -608,6 +616,7 @@ Terminal states: `done`, `cancelled`
|
|||
| ------ | ---------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||
| GET | `/api/companies/:companyId/issues` | List issues, sorted by priority. Filters: `?status=`, `?assigneeAgentId=`, `?assigneeUserId=`, `?projectId=`, `?labelId=`, `?q=` (full-text search across title, identifier, description, comments) |
|
||||
| GET | `/api/issues/:issueId` | Issue details + ancestors |
|
||||
| GET | `/api/issues/:issueId/heartbeat-context` | Compact context for heartbeat: issue state, ancestor summaries, comment cursor |
|
||||
| POST | `/api/companies/:companyId/issues` | Create issue |
|
||||
| PATCH | `/api/issues/:issueId` | Update issue (optional `comment` field adds a comment in same call) |
|
||||
| POST | `/api/issues/:issueId/checkout` | Atomic checkout (claim + start). Idempotent if you already own it. |
|
||||
|
|
@ -615,8 +624,13 @@ Terminal states: `done`, `cancelled`
|
|||
| GET | `/api/issues/:issueId/comments` | List comments |
|
||||
| GET | `/api/issues/:issueId/comments/:commentId` | Get a specific comment by ID |
|
||||
| POST | `/api/issues/:issueId/comments` | Add comment (@-mentions trigger wakeups) |
|
||||
| GET | `/api/issues/:issueId/documents` | List issue documents |
|
||||
| GET | `/api/issues/:issueId/documents/:key` | Get issue document by key |
|
||||
| PUT | `/api/issues/:issueId/documents/:key` | Create or update issue document (send `baseRevisionId` when updating) |
|
||||
| GET | `/api/issues/:issueId/documents/:key/revisions` | Document revision history |
|
||||
| DELETE | `/api/issues/:issueId/documents/:key` | Delete document (board-only) |
|
||||
| GET | `/api/issues/:issueId/approvals` | List approvals linked to issue |
|
||||
| POST | `/api/issues/:issueId/approvals` | Link approval to issue |
|
||||
| POST | `/api/issues/:issueId/approvals` | Link approval to issue |
|
||||
| DELETE | `/api/issues/:issueId/approvals/:approvalId` | Unlink approval from issue |
|
||||
|
||||
### Companies, Projects, Goals
|
||||
|
|
@ -624,7 +638,11 @@ Terminal states: `done`, `cancelled`
|
|||
| Method | Path | Description |
|
||||
| ------ | ------------------------------------ | ------------------ |
|
||||
| GET | `/api/companies` | List all companies |
|
||||
| POST | `/api/companies` | Create company |
|
||||
| GET | `/api/companies/:companyId` | Company details |
|
||||
| PATCH | `/api/companies/:companyId` | Update company fields |
|
||||
| POST | `/api/companies/:companyId/logo` | Upload company logo (multipart) |
|
||||
| POST | `/api/companies/:companyId/archive` | Archive company |
|
||||
| GET | `/api/companies/:companyId/projects` | List projects |
|
||||
| GET | `/api/projects/:projectId` | Project details |
|
||||
| POST | `/api/companies/:companyId/projects` | Create project (optional inline `workspace`) |
|
||||
|
|
@ -639,6 +657,22 @@ Terminal states: `done`, `cancelled`
|
|||
| PATCH | `/api/goals/:goalId` | Update goal |
|
||||
| POST | `/api/companies/:companyId/openclaw/invite-prompt` | Generate OpenClaw invite prompt (CEO/board only) |
|
||||
|
||||
### Routines
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| GET | `/api/companies/:companyId/routines` | List all routines in company |
|
||||
| GET | `/api/routines/:routineId` | Routine details including triggers |
|
||||
| POST | `/api/companies/:companyId/routines` | Create routine (`assigneeAgentId` + `projectId` required; agents: own only) |
|
||||
| PATCH | `/api/routines/:routineId` | Update routine (agents: own only, cannot reassign) |
|
||||
| POST | `/api/routines/:routineId/triggers` | Add trigger (`schedule`, `webhook`, or `api` kind) |
|
||||
| PATCH | `/api/routine-triggers/:triggerId` | Update trigger (e.g. disable, change cron) |
|
||||
| DELETE | `/api/routine-triggers/:triggerId` | Delete trigger |
|
||||
| POST | `/api/routine-triggers/:triggerId/rotate-secret` | Rotate webhook signing secret (previous secret immediately invalidated) |
|
||||
| POST | `/api/routines/:routineId/run` | Manual run (bypasses schedule; concurrency policy still applies) |
|
||||
| POST | `/api/routine-triggers/public/:publicId/fire` | Fire webhook trigger from external system |
|
||||
| GET | `/api/routines/:routineId/runs` | Run history (default 50) |
|
||||
|
||||
### Approvals, Costs, Activity, Dashboard
|
||||
|
||||
| Method | Path | Description |
|
||||
|
|
@ -650,14 +684,25 @@ Terminal states: `done`, `cancelled`
|
|||
| GET | `/api/approvals/:approvalId/issues` | Issues linked to approval |
|
||||
| GET | `/api/approvals/:approvalId/comments` | Approval comments |
|
||||
| POST | `/api/approvals/:approvalId/comments` | Add approval comment |
|
||||
| POST | `/api/approvals/:approvalId/approve` | Approve approval request |
|
||||
| POST | `/api/approvals/:approvalId/reject` | Reject approval request |
|
||||
| POST | `/api/approvals/:approvalId/request-revision`| Board asks for revision |
|
||||
| POST | `/api/approvals/:approvalId/resubmit` | Resubmit revised approval |
|
||||
| POST | `/api/companies/:companyId/cost-events` | Report cost event |
|
||||
| GET | `/api/companies/:companyId/costs/summary` | Company cost summary |
|
||||
| GET | `/api/companies/:companyId/costs/by-agent` | Costs by agent |
|
||||
| GET | `/api/companies/:companyId/costs/by-project` | Costs by project |
|
||||
| GET | `/api/companies/:companyId/activity` | Activity log |
|
||||
| GET | `/api/companies/:companyId/dashboard` | Company health summary |
|
||||
|
||||
### Secrets
|
||||
|
||||
| Method | Path | Description |
|
||||
| ------ | ---- | ----------- |
|
||||
| GET | `/api/companies/:companyId/secrets` | List secrets (metadata only) |
|
||||
| POST | `/api/companies/:companyId/secrets` | Create secret |
|
||||
| PATCH | `/api/secrets/:secretId` | Update secret value (creates new version) |
|
||||
|
||||
---
|
||||
|
||||
## Common Mistakes
|
||||
|
|
|
|||
187
skills/paperclip/references/routines.md
Normal file
187
skills/paperclip/references/routines.md
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
# Paperclip Routines
|
||||
|
||||
Routines are recurring tasks. Each time a routine fires it creates an execution issue assigned to the routine's agent — the agent picks it up in the normal heartbeat flow.
|
||||
|
||||
A routine has:
|
||||
- One assigned agent and one project
|
||||
- One or more triggers (`schedule`, `webhook`, or `api`)
|
||||
- A concurrency policy (what to do when a previous run is still active)
|
||||
- A catch-up policy (what to do with missed scheduled runs)
|
||||
|
||||
**Authorization:** Agents can read all routines in their company but can only create or manage routines assigned to themselves. Board operators have full access, including reassignment.
|
||||
|
||||
---
|
||||
|
||||
## Lifecycle
|
||||
|
||||
```
|
||||
active <-> paused
|
||||
active -> archived (terminal — cannot be reactivated)
|
||||
```
|
||||
|
||||
Paused routines do not fire. Archived routines do not fire and cannot be unarchived.
|
||||
|
||||
---
|
||||
|
||||
## Creating a Routine
|
||||
|
||||
```
|
||||
POST /api/companies/{companyId}/routines
|
||||
{
|
||||
"title": "Weekly CEO briefing",
|
||||
"description": "Compile status report and post to Slack",
|
||||
"assigneeAgentId": "{agentId}",
|
||||
"projectId": "{projectId}",
|
||||
"goalId": "{goalId}", // optional
|
||||
"parentIssueId": "{issueId}", // optional — parent for run issues
|
||||
"priority": "medium",
|
||||
"status": "active",
|
||||
"concurrencyPolicy": "coalesce_if_active",
|
||||
"catchUpPolicy": "skip_missed"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Required | Notes |
|
||||
|-------|----------|-------|
|
||||
| `title` | yes | Max 200 chars |
|
||||
| `description` | no | Human-readable description of the routine |
|
||||
| `assigneeAgentId` | yes | Agents: must be themselves |
|
||||
| `projectId` | yes | |
|
||||
| `goalId` | no | Inherited by run issues |
|
||||
| `parentIssueId` | no | Run issues become children of this issue |
|
||||
| `priority` | no | `critical` `high` `medium` (default) `low` |
|
||||
| `status` | no | `active` (default) `paused` `archived` |
|
||||
| `concurrencyPolicy` | no | See below |
|
||||
| `catchUpPolicy` | no | See below |
|
||||
|
||||
---
|
||||
|
||||
## Concurrency Policies
|
||||
|
||||
Controls what happens when a trigger fires while the previous run issue is still open or active.
|
||||
|
||||
| Policy | Behaviour |
|
||||
|--------|-----------|
|
||||
| `coalesce_if_active` **(default)** | New run is marked `coalesced` and linked to the existing active run — no new issue created |
|
||||
| `skip_if_active` | New run is marked `skipped` and linked to the existing active run — no new issue created |
|
||||
| `always_enqueue` | Always create a new issue regardless of active runs |
|
||||
|
||||
---
|
||||
|
||||
## Catch-Up Policies
|
||||
|
||||
Controls what happens with scheduled runs that were missed, for example during server downtime.
|
||||
|
||||
| Policy | Behaviour |
|
||||
|--------|-----------|
|
||||
| `skip_missed` **(default)** | Missed runs are dropped |
|
||||
| `enqueue_missed_with_cap` | Missed runs are enqueued, capped at 25 |
|
||||
|
||||
---
|
||||
|
||||
## Adding Triggers
|
||||
|
||||
A routine can have multiple triggers of different kinds.
|
||||
|
||||
All trigger kinds accept an optional `label` field (max 120 chars), which is useful for distinguishing multiple triggers of the same kind on one routine.
|
||||
|
||||
```
|
||||
POST /api/routines/{routineId}/triggers
|
||||
```
|
||||
|
||||
### Schedule (cron)
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "schedule",
|
||||
"cronExpression": "0 9 * * 1",
|
||||
"timezone": "Europe/Amsterdam"
|
||||
}
|
||||
```
|
||||
|
||||
- `cronExpression`: standard 5-field cron syntax
|
||||
- `timezone`: IANA timezone string (for example `UTC` or `America/New_York`)
|
||||
- The server computes `nextRunAt` automatically
|
||||
|
||||
### Webhook
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "webhook",
|
||||
"signingMode": "hmac_sha256",
|
||||
"replayWindowSec": 300
|
||||
}
|
||||
```
|
||||
|
||||
- `signingMode`: `bearer` (default) or `hmac_sha256`
|
||||
- `replayWindowSec`: 30-86400 (default 300)
|
||||
- Response includes the webhook URL (`publicId`-based) and the signing secret
|
||||
- Fire externally: `POST /api/routine-triggers/public/{publicId}/fire`
|
||||
- Bearer: `Authorization: Bearer <secret>`
|
||||
- HMAC: `X-Paperclip-Signature` + `X-Paperclip-Timestamp` headers
|
||||
|
||||
### API (manual only)
|
||||
|
||||
```json
|
||||
{
|
||||
"kind": "api"
|
||||
}
|
||||
```
|
||||
|
||||
No configuration. Fire via the manual run endpoint.
|
||||
|
||||
---
|
||||
|
||||
## Updating and Deleting Triggers
|
||||
|
||||
```
|
||||
PATCH /api/routine-triggers/{triggerId}
|
||||
{ "enabled": false, "cronExpression": "0 10 * * 1" }
|
||||
|
||||
DELETE /api/routine-triggers/{triggerId}
|
||||
```
|
||||
|
||||
To rotate a webhook secret (the old secret is immediately invalidated):
|
||||
|
||||
```
|
||||
POST /api/routine-triggers/{triggerId}/rotate-secret
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Manual Run
|
||||
|
||||
Fires a run immediately, bypassing the schedule. Concurrency policy still applies.
|
||||
|
||||
```
|
||||
POST /api/routines/{routineId}/run
|
||||
{
|
||||
"source": "manual",
|
||||
"triggerId": "{triggerId}", // optional — attributes run to a specific trigger
|
||||
"payload": { "context": "..." }, // optional — passed to the run issue
|
||||
"idempotencyKey": "unique-key" // optional — prevents duplicate runs
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating a Routine
|
||||
|
||||
All create fields are updatable. Agents cannot reassign a routine to another agent.
|
||||
|
||||
```
|
||||
PATCH /api/routines/{routineId}
|
||||
{ "status": "paused", "title": "New title" }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Reading Routines and Runs
|
||||
|
||||
```
|
||||
GET /api/companies/{companyId}/routines
|
||||
GET /api/routines/{routineId}
|
||||
GET /api/routines/{routineId}/runs?limit=50
|
||||
```
|
||||
|
||||
Use the generic API endpoint tables in `skills/paperclip/references/api-reference.md` when you need a full cross-domain reference. Use this file when you need routine-specific behaviour, payload shape, or policy details.
|
||||
Loading…
Add table
Reference in a new issue