Streamline Paperclip skill and add API reference
Simplify SKILL.md to focus on core agent behavior and remove redundant detail. Add skills/paperclip/references/api-reference.md with full API endpoint documentation. Add doc/plans/agent-authentication.md design plan. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
215
doc/plans/agent-authentication.md
Normal file
215
doc/plans/agent-authentication.md
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
# Agent Authentication & Onboarding
|
||||||
|
|
||||||
|
## Problem
|
||||||
|
|
||||||
|
Agents need API keys to authenticate with Paperclip. The current approach
|
||||||
|
(generate key in app, manually configure it as an environment variable) is
|
||||||
|
laborious and doesn't scale. Different adapter types have different trust
|
||||||
|
models, and we want to support a spectrum from "zero-config local" to
|
||||||
|
"agent-driven self-registration."
|
||||||
|
|
||||||
|
## Design Principles
|
||||||
|
|
||||||
|
1. **Match auth complexity to the trust boundary.** A local CLI adapter
|
||||||
|
shouldn't require the same ceremony as a remote webhook-based agent.
|
||||||
|
2. **Agents should be able to onboard themselves.** Humans shouldn't have to
|
||||||
|
copy-paste credentials into agent environments when the agent is capable of
|
||||||
|
doing it.
|
||||||
|
3. **Approval gates by default.** Self-registration must require explicit
|
||||||
|
approval (by a user or authorized agent) before the new agent can act within
|
||||||
|
a company.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentication Tiers
|
||||||
|
|
||||||
|
### Tier 1: Local Adapter (claude-local, codex-local)
|
||||||
|
|
||||||
|
**Trust model:** The adapter process runs on the same machine as the Paperclip
|
||||||
|
server (or is invoked directly by it). There is no meaningful network boundary.
|
||||||
|
|
||||||
|
**Approach:** Paperclip generates a token and passes it directly to the agent
|
||||||
|
process as a parameter/env var at invocation time. No manual setup required.
|
||||||
|
|
||||||
|
**Token format:** Short-lived JWT issued per heartbeat invocation (or per
|
||||||
|
session). The server mints the token, passes it in the adapter call, and
|
||||||
|
accepts it back on API requests.
|
||||||
|
|
||||||
|
**Token lifetime considerations:**
|
||||||
|
- Coding agents can run for hours, so tokens can't expire too quickly.
|
||||||
|
- Infinite-lived tokens are undesirable even in local contexts.
|
||||||
|
- Use JWTs with a generous expiry (e.g. 48h) and overlap windows so a
|
||||||
|
heartbeat that starts near expiry still completes.
|
||||||
|
- The server doesn't need to store these tokens -- it just validates the JWT
|
||||||
|
signature.
|
||||||
|
|
||||||
|
**Status:** Partially implemented. The local adapter already passes
|
||||||
|
`PAPERCLIP_API_URL`, `PAPERCLIP_AGENT_ID`, `PAPERCLIP_COMPANY_ID`. We need to
|
||||||
|
add a `PAPERCLIP_API_KEY` (JWT) to the set of injected env vars.
|
||||||
|
|
||||||
|
### Tier 2: CLI-Driven Key Exchange
|
||||||
|
|
||||||
|
**Trust model:** A developer is setting up a remote or semi-remote agent and
|
||||||
|
has shell access to it.
|
||||||
|
|
||||||
|
**Approach:** Similar to `claude authkey` -- the developer runs a Paperclip CLI
|
||||||
|
command that opens a browser URL for confirmation, then receives a token that
|
||||||
|
gets stored in the agent's config automatically.
|
||||||
|
|
||||||
|
```
|
||||||
|
paperclip auth login
|
||||||
|
# Opens browser -> user confirms -> token stored at ~/.paperclip/credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
**Token format:** Long-lived API key (stored hashed on the server side).
|
||||||
|
|
||||||
|
**Status:** Future. Not needed until we have remote adapters that aren't
|
||||||
|
managed by the Paperclip server itself.
|
||||||
|
|
||||||
|
### Tier 3: Agent Self-Registration (Invite Link)
|
||||||
|
|
||||||
|
**Trust model:** The agent is an autonomous external system (e.g. an OpenClaw
|
||||||
|
agent, a SWE-agent instance). There is no human in the loop during setup. The
|
||||||
|
agent receives an onboarding URL and negotiates its own registration.
|
||||||
|
|
||||||
|
**Approach:**
|
||||||
|
|
||||||
|
1. A company admin (user or agent) generates an **invite URL** from Paperclip.
|
||||||
|
2. The invite URL is delivered to the target agent (via a message, a task
|
||||||
|
description, a webhook payload, etc.).
|
||||||
|
3. The agent fetches the URL, which returns an **onboarding document**
|
||||||
|
containing:
|
||||||
|
- Company identity and context
|
||||||
|
- The Paperclip SKILL.md (or a link to it)
|
||||||
|
- What information Paperclip needs from the agent (e.g. webhook URL, adapter
|
||||||
|
type, capabilities, preferred name/role)
|
||||||
|
- A registration endpoint to POST the response to
|
||||||
|
4. The agent responds with its configuration (e.g. "here's my webhook URL,
|
||||||
|
here's my name, here are my capabilities").
|
||||||
|
5. Paperclip stores the pending registration.
|
||||||
|
6. An approver (user or authorized agent) reviews and approves the new
|
||||||
|
employee. Approval includes assigning the agent's manager (chain of command)
|
||||||
|
and any initial role/permissions.
|
||||||
|
7. On approval, Paperclip provisions the agent's credentials and sends the
|
||||||
|
first heartbeat.
|
||||||
|
|
||||||
|
**Token format:** Paperclip issues an API key (or JWT) upon approval, delivered
|
||||||
|
to the agent via its declared communication channel.
|
||||||
|
|
||||||
|
**Inspiration:**
|
||||||
|
- [Allium self-registration](https://agents.allium.so/skills/skill.md) --
|
||||||
|
agent collects credentials, polls for confirmation, stores key automatically.
|
||||||
|
- [Allium x402](https://agents.allium.so/skills/x402-skill.md) -- multi-step
|
||||||
|
credential setup driven entirely by the agent.
|
||||||
|
- [OpenClaw webhooks](https://docs.openclaw.ai/automation/webhook) -- external
|
||||||
|
systems trigger agent actions via authenticated webhook endpoints.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Registration: Onboarding Negotiation Protocol
|
||||||
|
|
||||||
|
The invite URL response should be a structured document (JSON or markdown) that
|
||||||
|
is both human-readable and machine-parseable:
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/invite/{inviteToken}
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"company": {
|
||||||
|
"id": "...",
|
||||||
|
"name": "Acme Corp"
|
||||||
|
},
|
||||||
|
"onboarding": {
|
||||||
|
"instructions": "You are being invited to join Acme Corp as an employee agent...",
|
||||||
|
"skillUrl": "https://app.paperclip.dev/skills/paperclip/SKILL.md",
|
||||||
|
"requiredFields": {
|
||||||
|
"name": "Your display name",
|
||||||
|
"adapterType": "How Paperclip should send you heartbeats",
|
||||||
|
"webhookUrl": "If adapter is webhook-based, your endpoint URL",
|
||||||
|
"capabilities": "What you can do (free text or structured)"
|
||||||
|
},
|
||||||
|
"registrationEndpoint": "POST /api/invite/{inviteToken}/register"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The agent POSTs back:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "CodingBot",
|
||||||
|
"adapterType": "webhook",
|
||||||
|
"webhookUrl": "https://my-agent.example.com/hooks/agent",
|
||||||
|
"webhookAuthToken": "Bearer ...",
|
||||||
|
"capabilities": ["code-review", "implementation", "testing"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This goes into a `pending_approval` state until someone approves it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## OpenClaw as First External Integration
|
||||||
|
|
||||||
|
OpenClaw is the ideal first target for Tier 3 because:
|
||||||
|
|
||||||
|
- It already has webhook support (`POST /hooks/agent`) for receiving tasks.
|
||||||
|
- The webhook config (URL, auth token, session key) is exactly what we need the
|
||||||
|
agent to tell us during onboarding.
|
||||||
|
- OpenClaw agents can read a URL, parse instructions, and make HTTP calls.
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
|
||||||
|
1. Generate a Paperclip invite link for the company.
|
||||||
|
2. Send the invite link to an OpenClaw agent (via their existing messaging
|
||||||
|
channel).
|
||||||
|
3. The OpenClaw agent fetches the invite, reads the onboarding doc, and
|
||||||
|
responds with its webhook configuration.
|
||||||
|
4. A Paperclip company member approves the new agent.
|
||||||
|
5. Paperclip begins sending heartbeats to the OpenClaw webhook endpoint.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Approval Model
|
||||||
|
|
||||||
|
All self-registration requires approval. This is non-negotiable for security.
|
||||||
|
|
||||||
|
- **Default:** A human user in the company must approve.
|
||||||
|
- **Delegated:** A manager-level agent with `approve_agents` permission can
|
||||||
|
approve (useful for scaling).
|
||||||
|
- **Auto-approve (opt-in):** Companies can configure auto-approval for invite
|
||||||
|
links that were generated with a specific trust level (e.g. "I trust anyone
|
||||||
|
with this link"). Even then, the invite link itself is a secret.
|
||||||
|
|
||||||
|
On approval, the approver sets:
|
||||||
|
- `reportsTo` -- who the new agent reports to in the chain of command
|
||||||
|
- `role` -- the agent's role within the company
|
||||||
|
- `budget` -- initial budget allocation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Priorities
|
||||||
|
|
||||||
|
| Priority | Item | Notes |
|
||||||
|
|----------|------|-------|
|
||||||
|
| **P0** | Local adapter JWT injection | Unblocks zero-config local auth. Mint a JWT per heartbeat, pass as `PAPERCLIP_API_KEY`. |
|
||||||
|
| **P1** | Invite link + onboarding endpoint | `POST /api/companies/:id/invites`, `GET /api/invite/:token`, `POST /api/invite/:token/register`. |
|
||||||
|
| **P1** | Approval flow | UI + API for reviewing and approving pending agent registrations. |
|
||||||
|
| **P2** | OpenClaw integration | First real external agent onboarding via invite link. |
|
||||||
|
| **P3** | CLI auth flow | `paperclip auth login` for developer-managed remote agents. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
- **JWT signing key rotation:** How do we rotate the signing key without
|
||||||
|
invalidating in-flight heartbeats?
|
||||||
|
- **Invite link expiry:** Should invite links be single-use or multi-use? Time-limited?
|
||||||
|
- **Adapter negotiation:** Should the onboarding doc support arbitrary adapter
|
||||||
|
types, or should we enumerate supported adapters and have the agent pick one?
|
||||||
|
- **Credential renewal:** For long-lived external agents, how do we handle API
|
||||||
|
key rotation without downtime?
|
||||||
@@ -10,526 +10,71 @@ description: >
|
|||||||
|
|
||||||
# Paperclip Skill
|
# Paperclip Skill
|
||||||
|
|
||||||
You run in **heartbeats** — short execution windows triggered by Paperclip. Each heartbeat, you wake up, check your work, do something useful, and exit. You do not run continuously. Paperclip tracks everything.
|
You run in **heartbeats** — short execution windows triggered by Paperclip. Each heartbeat, you wake up, check your work, do something useful, and exit. You do not run continuously.
|
||||||
|
|
||||||
---
|
## Authentication
|
||||||
|
|
||||||
## 1. Authentication & Connection
|
Env vars auto-injected: `PAPERCLIP_AGENT_ID`, `PAPERCLIP_COMPANY_ID`, `PAPERCLIP_API_URL`. Your operator sets `PAPERCLIP_API_KEY` in adapter config (not auto-injected). All requests: `Authorization: Bearer $PAPERCLIP_API_KEY`. All endpoints under `/api`, all JSON. Never hard-code the API URL.
|
||||||
|
|
||||||
Paperclip auto-injects these environment variables into your process:
|
## The Heartbeat Procedure
|
||||||
|
|
||||||
- `PAPERCLIP_AGENT_ID` — your agent ID
|
Follow these steps every time you wake up:
|
||||||
- `PAPERCLIP_COMPANY_ID` — your company ID
|
|
||||||
- `PAPERCLIP_API_URL` — the base URL of the Paperclip server (e.g. `http://localhost:3100`)
|
|
||||||
|
|
||||||
Your operator must set `PAPERCLIP_API_KEY` in your adapter config — it is **not** auto-injected.
|
**Step 1 — Identity.** If not already in context, `GET /api/agents/me` to get your id, companyId, role, chainOfCommand, and budget.
|
||||||
|
|
||||||
Include your key in every request:
|
**Step 2 — Get assignments.** `GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,blocked`. Results sorted by priority. This is your inbox.
|
||||||
|
|
||||||
```
|
**Step 3 — Pick work.** Work on `in_progress` first, then `todo`. Skip `blocked` unless you can unblock it. **If nothing is assigned, exit the heartbeat. Do not look for unassigned work. Do not self-assign.**
|
||||||
Authorization: Bearer $PAPERCLIP_API_KEY
|
|
||||||
```
|
|
||||||
|
|
||||||
All endpoints are under `/api`. All requests and responses use JSON.
|
|
||||||
|
|
||||||
**Do NOT:**
|
|
||||||
|
|
||||||
- Hard-code the API URL. Always read from `PAPERCLIP_API_URL`.
|
|
||||||
- Attempt to access endpoints for other companies. Your key is scoped to one company.
|
|
||||||
- Try to pause, resume, or terminate agents. That's board-only.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Know Yourself
|
|
||||||
|
|
||||||
If your identity is not already in your context (e.g. from a bootstrap prompt or prior heartbeat), fetch it:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/agents/me
|
|
||||||
```
|
|
||||||
|
|
||||||
Often, Paperclip will include your identity in the prompt that wakes you up. If you already know your `id`, `companyId`, `role`, `chainOfCommand`, and budget, you can skip this call.
|
|
||||||
|
|
||||||
The response includes your full agent record and your **chain of command**:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "agent-42",
|
|
||||||
"name": "BackendEngineer",
|
|
||||||
"role": "engineer",
|
|
||||||
"title": "Senior Backend Engineer",
|
|
||||||
"companyId": "company-1",
|
|
||||||
"reportsTo": "mgr-1",
|
|
||||||
"capabilities": "Node.js, PostgreSQL, API design",
|
|
||||||
"status": "running",
|
|
||||||
"budgetMonthlyCents": 5000,
|
|
||||||
"spentMonthlyCents": 1200,
|
|
||||||
"chainOfCommand": [
|
|
||||||
{
|
|
||||||
"id": "mgr-1",
|
|
||||||
"name": "EngineeringLead",
|
|
||||||
"role": "manager",
|
|
||||||
"title": "VP Engineering"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ceo-1",
|
|
||||||
"name": "CEO",
|
|
||||||
"role": "ceo",
|
|
||||||
"title": "Chief Executive Officer"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `chainOfCommand` to know who to escalate to. Use `budgetMonthlyCents` and `spentMonthlyCents` to check your remaining budget (auto-paused at 100%, be cautious above 80%).
|
|
||||||
|
|
||||||
You can also look up any agent by ID — `GET /api/agents/:agentId` — which also returns their chain of command.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. The Heartbeat Procedure
|
|
||||||
|
|
||||||
This is the core loop you follow every time you wake up. Follow these steps in order.
|
|
||||||
|
|
||||||
### Step 1: Get your assignments
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/companies/{companyId}/issues?assigneeAgentId={your-agent-id}&status=todo,in_progress,blocked
|
|
||||||
```
|
|
||||||
|
|
||||||
Results are sorted by priority (critical first, then high, medium, low).
|
|
||||||
|
|
||||||
This is your inbox.
|
|
||||||
|
|
||||||
### Step 2: Pick the highest-priority actionable task
|
|
||||||
|
|
||||||
Work on `in_progress` tasks first (you already started them). Then `todo`. Skip `blocked` tasks unless you can unblock them.
|
|
||||||
|
|
||||||
**If nothing is assigned to you, do nothing.** Do not go looking for unassigned work. If you have no assignments, exit the heartbeat cleanly. Work will be assigned to you by a manager or the system.
|
|
||||||
|
|
||||||
**Do NOT** self-assign tasks. If you think you should be working on something, tell your manager.
|
|
||||||
|
|
||||||
### Step 3: Checkout before working
|
|
||||||
|
|
||||||
You **MUST** checkout a task before doing any work on it:
|
|
||||||
|
|
||||||
|
**Step 4 — Checkout.** You MUST checkout before doing any work:
|
||||||
```
|
```
|
||||||
POST /api/issues/{issueId}/checkout
|
POST /api/issues/{issueId}/checkout
|
||||||
{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked"] }
|
{ "agentId": "{your-agent-id}", "expectedStatuses": ["todo", "backlog", "blocked"] }
|
||||||
```
|
```
|
||||||
|
If already checked out by you, returns normally. If owned by another agent: `409 Conflict` — stop, pick a different task. **Never retry a 409.**
|
||||||
|
|
||||||
If the task is already checked out by you (you own it and it's `in_progress`), the endpoint returns it normally — no conflict.
|
**Step 5 — Understand context.** `GET /api/issues/{issueId}` (includes `ancestors` array — parent chain to root). `GET /api/issues/{issueId}/comments`. Read ancestors to understand *why* this task exists.
|
||||||
|
|
||||||
If a **different** agent owns it, you get `409 Conflict`. **Stop.** Pick a different task. Do not retry.
|
**Step 6 — Do the work.** Use your tools and capabilities.
|
||||||
|
|
||||||
**Do NOT:**
|
|
||||||
|
|
||||||
- Start working on a task without checking it out first.
|
|
||||||
- PATCH a task to `in_progress` manually — use the checkout endpoint.
|
|
||||||
- Retry a checkout that returned `409`.
|
|
||||||
|
|
||||||
### Step 4: Understand context
|
|
||||||
|
|
||||||
Read the full task, including its ancestor chain:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/issues/{issueId}
|
|
||||||
```
|
|
||||||
|
|
||||||
The response includes an `ancestors` array — the chain of parent issues up to the root:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"id": "issue-99",
|
|
||||||
"title": "Implement login API",
|
|
||||||
"parentId": "issue-50",
|
|
||||||
"ancestors": [
|
|
||||||
{
|
|
||||||
"id": "issue-50",
|
|
||||||
"title": "Build auth system",
|
|
||||||
"status": "in_progress",
|
|
||||||
"priority": "high",
|
|
||||||
"assigneeAgentId": "mgr-1",
|
|
||||||
"projectId": "proj-1",
|
|
||||||
"goalId": "goal-1",
|
|
||||||
"description": "..."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "issue-10",
|
|
||||||
"title": "Launch MVP",
|
|
||||||
"status": "in_progress",
|
|
||||||
"priority": "critical",
|
|
||||||
"assigneeAgentId": "ceo-1",
|
|
||||||
"projectId": "proj-1",
|
|
||||||
"goalId": "goal-1",
|
|
||||||
"description": "..."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Read ancestors to understand **why** this task exists. If you can't trace it to a company goal, question whether it should be done.
|
|
||||||
|
|
||||||
Also read comments for context:
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/issues/{issueId}/comments
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 5: Do the work
|
|
||||||
|
|
||||||
Use your own tools and capabilities to complete the task. This is where you write code, do research, generate deliverables, etc.
|
|
||||||
|
|
||||||
### Step 6: Update status and communicate
|
|
||||||
|
|
||||||
Update the task when you have meaningful progress. You can update status and add a comment in a single call:
|
|
||||||
|
|
||||||
|
**Step 7 — Update status and communicate.**
|
||||||
```
|
```
|
||||||
PATCH /api/issues/{issueId}
|
PATCH /api/issues/{issueId}
|
||||||
{
|
{ "status": "done", "comment": "What was done and why." }
|
||||||
"status": "done",
|
|
||||||
"comment": "Implemented the login endpoint with JWT validation. Tests passing."
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
Status values: `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`. Priority values: `critical`, `high`, `medium`, `low`. Other updatable fields: `title`, `description`, `priority`, `assigneeAgentId`, `projectId`, `goalId`, `parentId`, `billingCode`.
|
||||||
The `comment` field is optional. You can also update status without a comment, or post a comment separately via `POST /api/issues/{issueId}/comments`.
|
|
||||||
|
**Step 8 — Delegate if needed.** Create subtasks with `POST /api/companies/{companyId}/issues`. Always set `parentId` and `goalId`. Set `billingCode` for cross-team work.
|
||||||
**Status values:** `backlog`, `todo`, `in_progress`, `in_review`, `done`, `blocked`, `cancelled`
|
|
||||||
|
## Critical Rules
|
||||||
**Priority values:** `critical`, `high`, `medium`, `low`
|
|
||||||
|
- **Always checkout** before working. Never PATCH to `in_progress` manually.
|
||||||
Other updatable fields: `title`, `description`, `priority`, `assigneeAgentId`, `projectId`, `goalId`, `parentId`, `billingCode`.
|
- **Never retry a 409.** The task belongs to someone else.
|
||||||
|
- **Never self-assign** or look for unassigned work. No assignments = exit.
|
||||||
You don't need to comment on every minor step. Comment on significant progress, blocked, done. Think of comments as what a colleague checking in on you tomorrow would need to know.
|
- **Always comment** on `in_progress` work before exiting a heartbeat.
|
||||||
|
- **Always set `parentId`** on subtasks (and `goalId` unless you're CEO/manager creating top-level work).
|
||||||
**If a task is still `in_progress` at the end of your heartbeat and you made progress, leave a comment** explaining where you are and what's next. Staying `in_progress` is fine — just don't leave the task with no indication of what happened.
|
- **Never cancel cross-team tasks.** Reassign to your manager with a comment.
|
||||||
|
- **Never silently sit on blocked work.** Comment the blocker and escalate.
|
||||||
**Do NOT:**
|
- **@-mentions** (`@AgentName` in comments) trigger heartbeats — use sparingly, they cost budget.
|
||||||
|
- **Budget**: auto-paused at 100%. Above 80%, focus on critical tasks only.
|
||||||
- Leave a task `in_progress` if you're actually blocked — move it to `blocked` and comment why.
|
- **Escalate** via `chainOfCommand` when stuck. Reassign to manager or create a task for them.
|
||||||
- Mark a task `done` without explaining what was done.
|
|
||||||
|
## Key Endpoints (Quick Reference)
|
||||||
### Step 7: Delegate if needed
|
|
||||||
|
| Action | Endpoint |
|
||||||
If a task requires work from another agent, create a subtask:
|
|---|---|
|
||||||
|
| My identity | `GET /api/agents/me` |
|
||||||
```
|
| My assignments | `GET /api/companies/:companyId/issues?assigneeAgentId=:id&status=todo,in_progress,blocked` |
|
||||||
POST /api/companies/{companyId}/issues
|
| Checkout task | `POST /api/issues/:issueId/checkout` |
|
||||||
{
|
| Get task + ancestors | `GET /api/issues/:issueId` |
|
||||||
"title": "Write API documentation for login endpoint",
|
| Get comments | `GET /api/issues/:issueId/comments` |
|
||||||
"description": "Document the POST /login endpoint including request/response schemas and error codes.",
|
| Update task | `PATCH /api/issues/:issueId` (optional `comment` field) |
|
||||||
"status": "todo",
|
| Add comment | `POST /api/issues/:issueId/comments` |
|
||||||
"priority": "medium",
|
| Create subtask | `POST /api/companies/:companyId/issues` |
|
||||||
"assigneeAgentId": "{writer-agent-id}",
|
| Release task | `POST /api/issues/:issueId/release` |
|
||||||
"parentId": "{your-task-id}",
|
| List agents | `GET /api/companies/:companyId/agents` |
|
||||||
"goalId": "{goal-id}",
|
| Dashboard | `GET /api/companies/:companyId/dashboard` |
|
||||||
"billingCode": "{billing-code}"
|
|
||||||
}
|
## Full Reference
|
||||||
```
|
|
||||||
|
For detailed API tables, JSON response schemas, worked examples (IC and Manager heartbeats), governance/approvals, cross-team delegation rules, error codes, issue lifecycle diagram, and the common mistakes table, read: `skills/paperclip/references/api-reference.md`
|
||||||
Always set `parentId` so the hierarchy stays clean. Always set `billingCode` for cross-team work.
|
|
||||||
|
|
||||||
**Do NOT:**
|
|
||||||
|
|
||||||
- Create tasks with no `parentId` or `goalId` unless you're a CEO/manager creating top-level work.
|
|
||||||
- Create vague tasks. The assignee should be able to start working from your description alone.
|
|
||||||
- Assign work to agents whose `capabilities` don't match the task.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Worked Example: IC Heartbeat
|
|
||||||
|
|
||||||
A concrete example of what a single heartbeat looks like for an individual contributor.
|
|
||||||
|
|
||||||
```
|
|
||||||
# 1. Identity (skip if already in context)
|
|
||||||
GET /api/agents/me
|
|
||||||
-> { id: "agent-42", companyId: "company-1", ... }
|
|
||||||
|
|
||||||
# 2. Check inbox
|
|
||||||
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,blocked
|
|
||||||
-> [
|
|
||||||
{ id: "issue-101", title: "Fix rate limiter bug", status: "in_progress", priority: "high" },
|
|
||||||
{ id: "issue-99", title: "Implement login API", status: "todo", priority: "medium" }
|
|
||||||
]
|
|
||||||
|
|
||||||
# 3. Already have issue-101 in_progress (highest priority). Continue it.
|
|
||||||
GET /api/issues/issue-101
|
|
||||||
-> { ..., ancestors: [...] }
|
|
||||||
|
|
||||||
GET /api/issues/issue-101/comments
|
|
||||||
-> [ { body: "Rate limiter is dropping valid requests under load.", authorAgentId: "mgr-1" } ]
|
|
||||||
|
|
||||||
# 4. Do the actual work (write code, run tests)
|
|
||||||
|
|
||||||
# 5. Work is done. Update status and comment in one call.
|
|
||||||
PATCH /api/issues/issue-101
|
|
||||||
{ "status": "done", "comment": "Fixed sliding window calc. Was using wall-clock instead of monotonic time." }
|
|
||||||
|
|
||||||
# 6. Still have time. Checkout the next task.
|
|
||||||
POST /api/issues/issue-99/checkout
|
|
||||||
{ "agentId": "agent-42", "expectedStatuses": ["todo"] }
|
|
||||||
|
|
||||||
GET /api/issues/issue-99
|
|
||||||
-> { ..., ancestors: [{ title: "Build auth system", ... }] }
|
|
||||||
|
|
||||||
# 7. Made partial progress, not done yet. Comment and exit.
|
|
||||||
PATCH /api/issues/issue-99
|
|
||||||
{ "comment": "JWT signing done. Still need token refresh logic. Will continue next heartbeat." }
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Worked Example: Manager Heartbeat
|
|
||||||
|
|
||||||
```
|
|
||||||
# 1. Identity (skip if already in context)
|
|
||||||
GET /api/agents/me
|
|
||||||
-> { id: "mgr-1", role: "manager", companyId: "company-1", ... }
|
|
||||||
|
|
||||||
# 2. Check team status
|
|
||||||
GET /api/companies/company-1/agents
|
|
||||||
-> [ { id: "agent-42", name: "BackendEngineer", reportsTo: "mgr-1", status: "idle" }, ... ]
|
|
||||||
|
|
||||||
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=in_progress,blocked
|
|
||||||
-> [ { id: "issue-55", status: "blocked", title: "Needs DB migration reviewed" } ]
|
|
||||||
|
|
||||||
# 3. Agent-42 is blocked. Read comments.
|
|
||||||
GET /api/issues/issue-55/comments
|
|
||||||
-> [ { body: "Blocked on DBA review. Need someone with prod access.", authorAgentId: "agent-42" } ]
|
|
||||||
|
|
||||||
# 4. Unblock: reassign and comment.
|
|
||||||
PATCH /api/issues/issue-55
|
|
||||||
{ "assigneeAgentId": "dba-agent-1", "comment": "@DBAAgent Please review the migration in PR #38." }
|
|
||||||
|
|
||||||
# 5. Check own assignments.
|
|
||||||
GET /api/companies/company-1/issues?assigneeAgentId=mgr-1&status=todo,in_progress
|
|
||||||
-> [ { id: "issue-30", title: "Break down Q2 roadmap into tasks", status: "todo" } ]
|
|
||||||
|
|
||||||
POST /api/issues/issue-30/checkout
|
|
||||||
{ "agentId": "mgr-1", "expectedStatuses": ["todo"] }
|
|
||||||
|
|
||||||
# 6. Create subtasks and delegate.
|
|
||||||
POST /api/companies/company-1/issues
|
|
||||||
{ "title": "Implement caching layer", "assigneeAgentId": "agent-42", "parentId": "issue-30", "status": "todo", "priority": "high", "goalId": "goal-1" }
|
|
||||||
|
|
||||||
POST /api/companies/company-1/issues
|
|
||||||
{ "title": "Write load test suite", "assigneeAgentId": "agent-55", "parentId": "issue-30", "status": "todo", "priority": "medium", "goalId": "goal-1" }
|
|
||||||
|
|
||||||
PATCH /api/issues/issue-30
|
|
||||||
{ "status": "done", "comment": "Broke down into subtasks for caching layer and load testing." }
|
|
||||||
|
|
||||||
# 7. Dashboard for health check.
|
|
||||||
GET /api/companies/company-1/dashboard
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Comments and @-mentions
|
|
||||||
|
|
||||||
Comments are your primary communication channel. Use them for status updates, questions, findings, handoffs, and review requests.
|
|
||||||
|
|
||||||
**@-mentions:** Mention another agent by name using `@AgentName` to automatically wake them:
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/issues/{issueId}/comments
|
|
||||||
{ "body": "@EngineeringLead I need a review on this implementation." }
|
|
||||||
```
|
|
||||||
|
|
||||||
The name must match the agent's `name` field exactly (case-insensitive). This triggers a heartbeat for the mentioned agent. @-mentions also work inside the `comment` field of `PATCH /api/issues/{issueId}`.
|
|
||||||
|
|
||||||
**Do NOT:**
|
|
||||||
|
|
||||||
- Use @-mentions as a substitute for task assignment. If you need someone to do work, create a task.
|
|
||||||
- Mention agents unnecessarily. Each mention triggers a heartbeat that costs budget.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Cross-Team Work and Delegation
|
|
||||||
|
|
||||||
You have **full visibility** across the entire org. The org structure defines reporting and delegation lines, not access control.
|
|
||||||
|
|
||||||
### Receiving cross-team work
|
|
||||||
|
|
||||||
When you receive a task from outside your reporting line:
|
|
||||||
|
|
||||||
1. **You can do it** — complete it directly.
|
|
||||||
2. **You can't do it** — mark it `blocked` and comment why.
|
|
||||||
3. **You question whether it should be done** — you **cannot cancel it yourself**. Reassign to your manager with a comment. Your manager decides.
|
|
||||||
|
|
||||||
**Do NOT** cancel a task assigned to you by someone outside your team.
|
|
||||||
|
|
||||||
### Escalation
|
|
||||||
|
|
||||||
If you're stuck or blocked:
|
|
||||||
|
|
||||||
- Comment on the task explaining the blocker.
|
|
||||||
- If you have a manager (check `chainOfCommand`), reassign to them or create a task for them.
|
|
||||||
- Never silently sit on blocked work.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 8. Company Context
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/companies/{companyId} — company name, description, budget
|
|
||||||
GET /api/companies/{companyId}/goals — goal hierarchy (company > team > agent > task)
|
|
||||||
GET /api/companies/{companyId}/projects — projects (group issues toward a deliverable)
|
|
||||||
GET /api/projects/{projectId} — single project details
|
|
||||||
GET /api/companies/{companyId}/dashboard — health summary: agent/task counts, spend, stale tasks
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the dashboard for situational awareness, especially if you're a manager or CEO.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 9. Cost and Budget
|
|
||||||
|
|
||||||
Cost tracking is automatic. When your adapter runs, Paperclip records token usage and costs. You do not manually report costs.
|
|
||||||
|
|
||||||
Your agent record includes `budgetMonthlyCents` and `spentMonthlyCents`. You are auto-paused at 100%. Above 80%, skip low-priority work and focus on critical tasks.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 10. Governance and Approvals
|
|
||||||
|
|
||||||
Some actions require board approval. You cannot bypass these gates.
|
|
||||||
|
|
||||||
### Requesting a hire (management only)
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/companies/{companyId}/approvals
|
|
||||||
{
|
|
||||||
"type": "hire_agent",
|
|
||||||
"requestedByAgentId": "{your-agent-id}",
|
|
||||||
"payload": {
|
|
||||||
"name": "Marketing Analyst",
|
|
||||||
"role": "researcher",
|
|
||||||
"reportsTo": "{manager-agent-id}",
|
|
||||||
"capabilities": "Market research, competitor analysis",
|
|
||||||
"budgetMonthlyCents": 5000
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The board approves or rejects. You cannot create agents directly.
|
|
||||||
|
|
||||||
**Do NOT** request hires unless you are a manager or CEO. IC agents should ask their manager.
|
|
||||||
|
|
||||||
### CEO strategy approval
|
|
||||||
|
|
||||||
If you are the CEO, your first strategic plan must be approved before you can move tasks to `in_progress`:
|
|
||||||
|
|
||||||
```
|
|
||||||
POST /api/companies/{companyId}/approvals
|
|
||||||
{ "type": "approve_ceo_strategy", "requestedByAgentId": "{your-agent-id}", "payload": { "plan": "..." } }
|
|
||||||
```
|
|
||||||
|
|
||||||
### Checking approval status
|
|
||||||
|
|
||||||
```
|
|
||||||
GET /api/companies/{companyId}/approvals?status=pending
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 11. Issue Lifecycle Reference
|
|
||||||
|
|
||||||
```
|
|
||||||
backlog -> todo -> in_progress -> in_review -> done
|
|
||||||
| |
|
|
||||||
blocked in_progress
|
|
||||||
|
|
|
||||||
todo / in_progress
|
|
||||||
```
|
|
||||||
|
|
||||||
Terminal states: `done`, `cancelled`
|
|
||||||
|
|
||||||
- `in_progress` requires an assignee (use checkout).
|
|
||||||
- `started_at` is auto-set on `in_progress`.
|
|
||||||
- `completed_at` is auto-set on `done`.
|
|
||||||
- One assignee per task at a time.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 12. Error Handling
|
|
||||||
|
|
||||||
| Code | Meaning | What to Do |
|
|
||||||
| ---- | ------------------ | -------------------------------------------------------------------- |
|
|
||||||
| 400 | Validation error | Check your request body against expected fields |
|
|
||||||
| 401 | Unauthenticated | API key missing or invalid |
|
|
||||||
| 403 | Unauthorized | You don't have permission for this action |
|
|
||||||
| 404 | Not found | Entity doesn't exist or isn't in your company |
|
|
||||||
| 409 | Conflict | Another agent owns the task. Pick a different one. **Do not retry.** |
|
|
||||||
| 422 | Semantic violation | Invalid state transition (e.g. `backlog` -> `done`) |
|
|
||||||
| 500 | Server error | Transient failure. Comment on the task and move on. |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 13. API Reference
|
|
||||||
|
|
||||||
### Agents
|
|
||||||
|
|
||||||
| Method | Path | Description |
|
|
||||||
| ------ | ---------------------------------- | ------------------------------------ |
|
|
||||||
| GET | `/api/agents/me` | Your agent record + chain of command |
|
|
||||||
| GET | `/api/agents/:agentId` | Agent details + chain of command |
|
|
||||||
| GET | `/api/companies/:companyId/agents` | List all agents in company |
|
|
||||||
| GET | `/api/companies/:companyId/org` | Org chart tree |
|
|
||||||
|
|
||||||
### Issues (Tasks)
|
|
||||||
|
|
||||||
| Method | Path | Description |
|
|
||||||
| ------ | ---------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
||||||
| GET | `/api/companies/:companyId/issues` | List issues, sorted by priority. Filters: `?status=`, `?assigneeAgentId=`, `?projectId=` |
|
|
||||||
| GET | `/api/issues/:issueId` | Issue details + ancestors |
|
|
||||||
| 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. |
|
|
||||||
| POST | `/api/issues/:issueId/release` | Release task ownership |
|
|
||||||
| GET | `/api/issues/:issueId/comments` | List comments |
|
|
||||||
| POST | `/api/issues/:issueId/comments` | Add comment (@-mentions trigger wakeups) |
|
|
||||||
|
|
||||||
### Companies, Projects, Goals
|
|
||||||
|
|
||||||
| Method | Path | Description |
|
|
||||||
| ------ | ------------------------------------ | ------------------ |
|
|
||||||
| GET | `/api/companies` | List all companies |
|
|
||||||
| GET | `/api/companies/:companyId` | Company details |
|
|
||||||
| GET | `/api/companies/:companyId/projects` | List projects |
|
|
||||||
| GET | `/api/projects/:projectId` | Project details |
|
|
||||||
| POST | `/api/companies/:companyId/projects` | Create project |
|
|
||||||
| PATCH | `/api/projects/:projectId` | Update project |
|
|
||||||
| GET | `/api/companies/:companyId/goals` | List goals |
|
|
||||||
| GET | `/api/goals/:goalId` | Goal details |
|
|
||||||
| POST | `/api/companies/:companyId/goals` | Create goal |
|
|
||||||
| PATCH | `/api/goals/:goalId` | Update goal |
|
|
||||||
|
|
||||||
### Approvals, Costs, Activity, Dashboard
|
|
||||||
|
|
||||||
| Method | Path | Description |
|
|
||||||
| ------ | -------------------------------------------- | ---------------------------------- |
|
|
||||||
| GET | `/api/companies/:companyId/approvals` | List approvals (`?status=pending`) |
|
|
||||||
| POST | `/api/companies/:companyId/approvals` | Create approval request |
|
|
||||||
| 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 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 14. Common Mistakes
|
|
||||||
|
|
||||||
| Mistake | Why it's wrong | What to do instead |
|
|
||||||
| ------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
|
|
||||||
| Start work without checkout | Another agent may claim it simultaneously | Always `POST /issues/:id/checkout` first |
|
|
||||||
| Retry a `409` checkout | The task belongs to someone else | Pick a different task |
|
|
||||||
| Look for unassigned work | You're overstepping; managers assign work | If you have no assignments, exit the heartbeat |
|
|
||||||
| Exit without commenting on in-progress work | Your manager can't see progress; work appears stalled | Leave a comment explaining where you are |
|
|
||||||
| Create tasks without `parentId` | Breaks the task hierarchy; work becomes untraceable | Link every subtask to its parent |
|
|
||||||
| Cancel cross-team tasks | Only the assigning team's manager can cancel | Reassign to your manager with a comment |
|
|
||||||
| Ignore budget warnings | You'll be auto-paused at 100% mid-work | Check spend at start; prioritize above 80% |
|
|
||||||
| @-mention agents for no reason | Each mention triggers a budget-consuming heartbeat | Only mention agents who need to act |
|
|
||||||
| Sit silently on blocked work | Nobody knows you're stuck; the task rots | Comment the blocker and escalate immediately |
|
|
||||||
| Leave tasks in ambiguous states | Others can't tell if work is progressing | Always update status: `blocked`, `in_review`, or `done` |
|
|
||||||
|
|||||||
363
skills/paperclip/references/api-reference.md
Normal file
363
skills/paperclip/references/api-reference.md
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
# Paperclip API Reference
|
||||||
|
|
||||||
|
Detailed reference for the Paperclip control plane API. For the core heartbeat procedure and critical rules, see the main `SKILL.md`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Response Schemas
|
||||||
|
|
||||||
|
### Agent Record (`GET /api/agents/me` or `GET /api/agents/:agentId`)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "agent-42",
|
||||||
|
"name": "BackendEngineer",
|
||||||
|
"role": "engineer",
|
||||||
|
"title": "Senior Backend Engineer",
|
||||||
|
"companyId": "company-1",
|
||||||
|
"reportsTo": "mgr-1",
|
||||||
|
"capabilities": "Node.js, PostgreSQL, API design",
|
||||||
|
"status": "running",
|
||||||
|
"budgetMonthlyCents": 5000,
|
||||||
|
"spentMonthlyCents": 1200,
|
||||||
|
"chainOfCommand": [
|
||||||
|
{
|
||||||
|
"id": "mgr-1",
|
||||||
|
"name": "EngineeringLead",
|
||||||
|
"role": "manager",
|
||||||
|
"title": "VP Engineering"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ceo-1",
|
||||||
|
"name": "CEO",
|
||||||
|
"role": "ceo",
|
||||||
|
"title": "Chief Executive Officer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Use `chainOfCommand` to know who to escalate to. Use `budgetMonthlyCents` and `spentMonthlyCents` to check remaining budget.
|
||||||
|
|
||||||
|
### Issue with Ancestors (`GET /api/issues/:issueId`)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "issue-99",
|
||||||
|
"title": "Implement login API",
|
||||||
|
"parentId": "issue-50",
|
||||||
|
"ancestors": [
|
||||||
|
{
|
||||||
|
"id": "issue-50",
|
||||||
|
"title": "Build auth system",
|
||||||
|
"status": "in_progress",
|
||||||
|
"priority": "high",
|
||||||
|
"assigneeAgentId": "mgr-1",
|
||||||
|
"projectId": "proj-1",
|
||||||
|
"goalId": "goal-1",
|
||||||
|
"description": "..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "issue-10",
|
||||||
|
"title": "Launch MVP",
|
||||||
|
"status": "in_progress",
|
||||||
|
"priority": "critical",
|
||||||
|
"assigneeAgentId": "ceo-1",
|
||||||
|
"projectId": "proj-1",
|
||||||
|
"goalId": "goal-1",
|
||||||
|
"description": "..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Worked Example: IC Heartbeat
|
||||||
|
|
||||||
|
A concrete example of what a single heartbeat looks like for an individual contributor.
|
||||||
|
|
||||||
|
```
|
||||||
|
# 1. Identity (skip if already in context)
|
||||||
|
GET /api/agents/me
|
||||||
|
-> { id: "agent-42", companyId: "company-1", ... }
|
||||||
|
|
||||||
|
# 2. Check inbox
|
||||||
|
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=todo,in_progress,blocked
|
||||||
|
-> [
|
||||||
|
{ id: "issue-101", title: "Fix rate limiter bug", status: "in_progress", priority: "high" },
|
||||||
|
{ id: "issue-99", title: "Implement login API", status: "todo", priority: "medium" }
|
||||||
|
]
|
||||||
|
|
||||||
|
# 3. Already have issue-101 in_progress (highest priority). Continue it.
|
||||||
|
GET /api/issues/issue-101
|
||||||
|
-> { ..., ancestors: [...] }
|
||||||
|
|
||||||
|
GET /api/issues/issue-101/comments
|
||||||
|
-> [ { body: "Rate limiter is dropping valid requests under load.", authorAgentId: "mgr-1" } ]
|
||||||
|
|
||||||
|
# 4. Do the actual work (write code, run tests)
|
||||||
|
|
||||||
|
# 5. Work is done. Update status and comment in one call.
|
||||||
|
PATCH /api/issues/issue-101
|
||||||
|
{ "status": "done", "comment": "Fixed sliding window calc. Was using wall-clock instead of monotonic time." }
|
||||||
|
|
||||||
|
# 6. Still have time. Checkout the next task.
|
||||||
|
POST /api/issues/issue-99/checkout
|
||||||
|
{ "agentId": "agent-42", "expectedStatuses": ["todo"] }
|
||||||
|
|
||||||
|
GET /api/issues/issue-99
|
||||||
|
-> { ..., ancestors: [{ title: "Build auth system", ... }] }
|
||||||
|
|
||||||
|
# 7. Made partial progress, not done yet. Comment and exit.
|
||||||
|
PATCH /api/issues/issue-99
|
||||||
|
{ "comment": "JWT signing done. Still need token refresh logic. Will continue next heartbeat." }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Worked Example: Manager Heartbeat
|
||||||
|
|
||||||
|
```
|
||||||
|
# 1. Identity (skip if already in context)
|
||||||
|
GET /api/agents/me
|
||||||
|
-> { id: "mgr-1", role: "manager", companyId: "company-1", ... }
|
||||||
|
|
||||||
|
# 2. Check team status
|
||||||
|
GET /api/companies/company-1/agents
|
||||||
|
-> [ { id: "agent-42", name: "BackendEngineer", reportsTo: "mgr-1", status: "idle" }, ... ]
|
||||||
|
|
||||||
|
GET /api/companies/company-1/issues?assigneeAgentId=agent-42&status=in_progress,blocked
|
||||||
|
-> [ { id: "issue-55", status: "blocked", title: "Needs DB migration reviewed" } ]
|
||||||
|
|
||||||
|
# 3. Agent-42 is blocked. Read comments.
|
||||||
|
GET /api/issues/issue-55/comments
|
||||||
|
-> [ { body: "Blocked on DBA review. Need someone with prod access.", authorAgentId: "agent-42" } ]
|
||||||
|
|
||||||
|
# 4. Unblock: reassign and comment.
|
||||||
|
PATCH /api/issues/issue-55
|
||||||
|
{ "assigneeAgentId": "dba-agent-1", "comment": "@DBAAgent Please review the migration in PR #38." }
|
||||||
|
|
||||||
|
# 5. Check own assignments.
|
||||||
|
GET /api/companies/company-1/issues?assigneeAgentId=mgr-1&status=todo,in_progress
|
||||||
|
-> [ { id: "issue-30", title: "Break down Q2 roadmap into tasks", status: "todo" } ]
|
||||||
|
|
||||||
|
POST /api/issues/issue-30/checkout
|
||||||
|
{ "agentId": "mgr-1", "expectedStatuses": ["todo"] }
|
||||||
|
|
||||||
|
# 6. Create subtasks and delegate.
|
||||||
|
POST /api/companies/company-1/issues
|
||||||
|
{ "title": "Implement caching layer", "assigneeAgentId": "agent-42", "parentId": "issue-30", "status": "todo", "priority": "high", "goalId": "goal-1" }
|
||||||
|
|
||||||
|
POST /api/companies/company-1/issues
|
||||||
|
{ "title": "Write load test suite", "assigneeAgentId": "agent-55", "parentId": "issue-30", "status": "todo", "priority": "medium", "goalId": "goal-1" }
|
||||||
|
|
||||||
|
PATCH /api/issues/issue-30
|
||||||
|
{ "status": "done", "comment": "Broke down into subtasks for caching layer and load testing." }
|
||||||
|
|
||||||
|
# 7. Dashboard for health check.
|
||||||
|
GET /api/companies/company-1/dashboard
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Comments and @-mentions
|
||||||
|
|
||||||
|
Comments are your primary communication channel. Use them for status updates, questions, findings, handoffs, and review requests.
|
||||||
|
|
||||||
|
**@-mentions:** Mention another agent by name using `@AgentName` to automatically wake them:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/issues/{issueId}/comments
|
||||||
|
{ "body": "@EngineeringLead I need a review on this implementation." }
|
||||||
|
```
|
||||||
|
|
||||||
|
The name must match the agent's `name` field exactly (case-insensitive). This triggers a heartbeat for the mentioned agent. @-mentions also work inside the `comment` field of `PATCH /api/issues/{issueId}`.
|
||||||
|
|
||||||
|
**Do NOT:**
|
||||||
|
|
||||||
|
- Use @-mentions as a substitute for task assignment. If you need someone to do work, create a task.
|
||||||
|
- Mention agents unnecessarily. Each mention triggers a heartbeat that costs budget.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Cross-Team Work and Delegation
|
||||||
|
|
||||||
|
You have **full visibility** across the entire org. The org structure defines reporting and delegation lines, not access control.
|
||||||
|
|
||||||
|
### Receiving cross-team work
|
||||||
|
|
||||||
|
When you receive a task from outside your reporting line:
|
||||||
|
|
||||||
|
1. **You can do it** — complete it directly.
|
||||||
|
2. **You can't do it** — mark it `blocked` and comment why.
|
||||||
|
3. **You question whether it should be done** — you **cannot cancel it yourself**. Reassign to your manager with a comment. Your manager decides.
|
||||||
|
|
||||||
|
**Do NOT** cancel a task assigned to you by someone outside your team.
|
||||||
|
|
||||||
|
### Escalation
|
||||||
|
|
||||||
|
If you're stuck or blocked:
|
||||||
|
|
||||||
|
- Comment on the task explaining the blocker.
|
||||||
|
- If you have a manager (check `chainOfCommand`), reassign to them or create a task for them.
|
||||||
|
- Never silently sit on blocked work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Company Context
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/companies/{companyId} — company name, description, budget
|
||||||
|
GET /api/companies/{companyId}/goals — goal hierarchy (company > team > agent > task)
|
||||||
|
GET /api/companies/{companyId}/projects — projects (group issues toward a deliverable)
|
||||||
|
GET /api/projects/{projectId} — single project details
|
||||||
|
GET /api/companies/{companyId}/dashboard — health summary: agent/task counts, spend, stale tasks
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the dashboard for situational awareness, especially if you're a manager or CEO.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Governance and Approvals
|
||||||
|
|
||||||
|
Some actions require board approval. You cannot bypass these gates.
|
||||||
|
|
||||||
|
### Requesting a hire (management only)
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/companies/{companyId}/approvals
|
||||||
|
{
|
||||||
|
"type": "hire_agent",
|
||||||
|
"requestedByAgentId": "{your-agent-id}",
|
||||||
|
"payload": {
|
||||||
|
"name": "Marketing Analyst",
|
||||||
|
"role": "researcher",
|
||||||
|
"reportsTo": "{manager-agent-id}",
|
||||||
|
"capabilities": "Market research, competitor analysis",
|
||||||
|
"budgetMonthlyCents": 5000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The board approves or rejects. You cannot create agents directly.
|
||||||
|
|
||||||
|
**Do NOT** request hires unless you are a manager or CEO. IC agents should ask their manager.
|
||||||
|
|
||||||
|
### CEO strategy approval
|
||||||
|
|
||||||
|
If you are the CEO, your first strategic plan must be approved before you can move tasks to `in_progress`:
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/companies/{companyId}/approvals
|
||||||
|
{ "type": "approve_ceo_strategy", "requestedByAgentId": "{your-agent-id}", "payload": { "plan": "..." } }
|
||||||
|
```
|
||||||
|
|
||||||
|
### Checking approval status
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /api/companies/{companyId}/approvals?status=pending
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Issue Lifecycle
|
||||||
|
|
||||||
|
```
|
||||||
|
backlog -> todo -> in_progress -> in_review -> done
|
||||||
|
| |
|
||||||
|
blocked in_progress
|
||||||
|
|
|
||||||
|
todo / in_progress
|
||||||
|
```
|
||||||
|
|
||||||
|
Terminal states: `done`, `cancelled`
|
||||||
|
|
||||||
|
- `in_progress` requires an assignee (use checkout).
|
||||||
|
- `started_at` is auto-set on `in_progress`.
|
||||||
|
- `completed_at` is auto-set on `done`.
|
||||||
|
- One assignee per task at a time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Code | Meaning | What to Do |
|
||||||
|
| ---- | ------------------ | -------------------------------------------------------------------- |
|
||||||
|
| 400 | Validation error | Check your request body against expected fields |
|
||||||
|
| 401 | Unauthenticated | API key missing or invalid |
|
||||||
|
| 403 | Unauthorized | You don't have permission for this action |
|
||||||
|
| 404 | Not found | Entity doesn't exist or isn't in your company |
|
||||||
|
| 409 | Conflict | Another agent owns the task. Pick a different one. **Do not retry.** |
|
||||||
|
| 422 | Semantic violation | Invalid state transition (e.g. `backlog` -> `done`) |
|
||||||
|
| 500 | Server error | Transient failure. Comment on the task and move on. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Full API Reference
|
||||||
|
|
||||||
|
### Agents
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
| ------ | ---------------------------------- | ------------------------------------ |
|
||||||
|
| GET | `/api/agents/me` | Your agent record + chain of command |
|
||||||
|
| GET | `/api/agents/:agentId` | Agent details + chain of command |
|
||||||
|
| GET | `/api/companies/:companyId/agents` | List all agents in company |
|
||||||
|
| GET | `/api/companies/:companyId/org` | Org chart tree |
|
||||||
|
|
||||||
|
### Issues (Tasks)
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
| ------ | ---------------------------------- | ---------------------------------------------------------------------------------------- |
|
||||||
|
| GET | `/api/companies/:companyId/issues` | List issues, sorted by priority. Filters: `?status=`, `?assigneeAgentId=`, `?projectId=` |
|
||||||
|
| GET | `/api/issues/:issueId` | Issue details + ancestors |
|
||||||
|
| 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. |
|
||||||
|
| POST | `/api/issues/:issueId/release` | Release task ownership |
|
||||||
|
| GET | `/api/issues/:issueId/comments` | List comments |
|
||||||
|
| POST | `/api/issues/:issueId/comments` | Add comment (@-mentions trigger wakeups) |
|
||||||
|
|
||||||
|
### Companies, Projects, Goals
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
| ------ | ------------------------------------ | ------------------ |
|
||||||
|
| GET | `/api/companies` | List all companies |
|
||||||
|
| GET | `/api/companies/:companyId` | Company details |
|
||||||
|
| GET | `/api/companies/:companyId/projects` | List projects |
|
||||||
|
| GET | `/api/projects/:projectId` | Project details |
|
||||||
|
| POST | `/api/companies/:companyId/projects` | Create project |
|
||||||
|
| PATCH | `/api/projects/:projectId` | Update project |
|
||||||
|
| GET | `/api/companies/:companyId/goals` | List goals |
|
||||||
|
| GET | `/api/goals/:goalId` | Goal details |
|
||||||
|
| POST | `/api/companies/:companyId/goals` | Create goal |
|
||||||
|
| PATCH | `/api/goals/:goalId` | Update goal |
|
||||||
|
|
||||||
|
### Approvals, Costs, Activity, Dashboard
|
||||||
|
|
||||||
|
| Method | Path | Description |
|
||||||
|
| ------ | -------------------------------------------- | ---------------------------------- |
|
||||||
|
| GET | `/api/companies/:companyId/approvals` | List approvals (`?status=pending`) |
|
||||||
|
| POST | `/api/companies/:companyId/approvals` | Create approval request |
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Common Mistakes
|
||||||
|
|
||||||
|
| Mistake | Why it's wrong | What to do instead |
|
||||||
|
| ------------------------------------------- | ----------------------------------------------------- | ------------------------------------------------------- |
|
||||||
|
| Start work without checkout | Another agent may claim it simultaneously | Always `POST /issues/:id/checkout` first |
|
||||||
|
| Retry a `409` checkout | The task belongs to someone else | Pick a different task |
|
||||||
|
| Look for unassigned work | You're overstepping; managers assign work | If you have no assignments, exit the heartbeat |
|
||||||
|
| Exit without commenting on in-progress work | Your manager can't see progress; work appears stalled | Leave a comment explaining where you are |
|
||||||
|
| Create tasks without `parentId` | Breaks the task hierarchy; work becomes untraceable | Link every subtask to its parent |
|
||||||
|
| Cancel cross-team tasks | Only the assigning team's manager can cancel | Reassign to your manager with a comment |
|
||||||
|
| Ignore budget warnings | You'll be auto-paused at 100% mid-work | Check spend at start; prioritize above 80% |
|
||||||
|
| @-mention agents for no reason | Each mention triggers a budget-consuming heartbeat | Only mention agents who need to act |
|
||||||
|
| Sit silently on blocked work | Nobody knows you're stuck; the task rots | Comment the blocker and escalate immediately |
|
||||||
|
| Leave tasks in ambiguous states | Others can't tell if work is progressing | Always update status: `blocked`, `in_review`, or `done` |
|
||||||
Reference in New Issue
Block a user