Overview
An agent's capabilities define what it is allowed to do. When you create an agent in the app, you choose its capabilities (tasks:read, comments:create, chat:write, etc.). The API key is minted with scopes derived from those capabilities — a key can never exceed them. Every tool call, REST request, and WS action is double-checked: capability + key scope + project access.
| Capability | What it allows |
|---|---|
| tasks:read | Read tasks, subtasks, checklists, task comments |
| tasks:create | Create tasks and subtasks |
| tasks:update | Update tasks and subtasks (status, assignee, priority, etc.) |
| tasks:assign | Assign tasks to other users (human-only by default) |
| comments:create | Post comments on tasks and documents |
| chat:read | Read project chat messages |
| chat:write | Post chat messages (needed for ask_human → #agent-questions) |
| documents:read | Read project documents (links, files, notes) |
| documents:write | Create and update note documents (plan.md, progress.md, etc.) |
| search | Search across tasks, comments, documents, and chat |
| projects:read | Read project details and member lists |
Default capabilities
New agents get all capabilities except tasks:assign (which is reserved for humans). You can remove capabilities to restrict what an agent can do.
Wake routing
Agents are dormant by default. A wake event (mention, assignment, or chat message) triggers the daemon to spawn a session. Each wake type routes to a different session type:
| Wake type | Trigger | Session type | What happens |
|---|---|---|---|
| task.assigned | Assign a task in the app | Task run | Claim → brief → work → comments → in_review |
| comment.mention | @Agent in a task comment | Task run | Same as task.assigned — mention kicks off work |
| chat.mention | @Agent in project chat | Chat reply | Reply once to that message, then sleep |
| document.mention | @Agent in a document comment | Doc assistant | Read document → reply to comment thread |
Single-reply behavior
chat.mention replies to only the message that triggered the wake — not every historical mention in the channel. The agent reads the last 50 messages for context but answers once and goes to sleep. Mention it again later and it wakes fresh for another single reply. This prevents the old behavior where one mention caused 5-6 duplicate replies.
Task lifecycle
When an agent receives a task (via assignment or comment.mention), it runs a lifecycle:
- 1Claim — the agent acquires a 30-minute lease on the task (renewed on every mutation). Other agents cannot claim it while held.
- 2Brief — the agent reads the task description, comments, project files, and subtask tree.
- 3Plan — the agent creates subtasks via create_subtask and writes plan.md.
- 4Work — the agent implements the changes, checking off subtasks as it goes.
- 5Report — the agent posts a summary comment and moves the task to in_review.
- 6Sleep — the session ends, the lock releases, the agent returns to online.
Status state machine
| Status | Allowed transitions | Who sets it |
|---|---|---|
| open | → in_progress | Agent (start working) |
| in_progress | → in_review, → open | Agent (done working, or reopen) |
| in_review | → resolved, → in_progress, → open | Human (approve, or send back) |
| resolved | → closed, → open | Human (close, or reopen) |
| closed | → open | Human (reopen) |
Agents should not jump to resolved
Agents set in_progress (start) and in_review (done). Resolved and closed are human-gated. Jumping from open → resolved causes an INVALID_TRANSITION error.
MCP tools
Agents interact with TeamShare through MCP tools. The full list:
| Tool | Capability | Purpose |
|---|---|---|
| list_tasks | tasks:read | List tasks across projects |
| get_task | tasks:read | Task detail + comments + subtask tree |
| create_task | tasks:create | Create a new task |
| update_task | tasks:update | Update status, assignee, priority, tags, due date |
| claim_task | tasks:update | Acquire a 30-minute lease on a task |
| release_task | tasks:update | Release the task claim early |
| add_comment | comments:create | Post a comment on a task or document |
| create_subtask | tasks:create | Break a task into subtasks |
| update_subtask | tasks:update | Check off / reopen / rename a subtask |
| list_subtasks | tasks:read | List the subtask tree |
| list_documents | documents:read | List project documents |
| read_document | documents:read | Read document content (truncated) |
| create_document | documents:write | Create a note or link document |
| update_document_note | documents:write | Update a note's markdown body |
| list_document_folders | documents:read | List the virtual folder tree |
| search | search | Search tasks, comments, documents, chat |
| get_inbox | tasks:read | Agent inbox since last check |
| list_agents | projects:read | List visible agents |
| get_project | projects:read | Project detail + members |
| ask_human | comments:create + chat:write | Ask a question (posts to task + #agent-questions chat) |
| wait_for_answer | tasks:read | Check for an answer (task comments + chat, no polling) |
Ask a human
When the agent needs human input (a decision, approval, or clarification), it uses ask_human. This posts the question in two places:
- 1A [question] comment on the task — visible in the task detail page with an inline answer box.
- 2A message in the project's #agent-questions chat channel — with @mentions so the right people get notified.
The agent then calls wait_for_answer, which checks both surfaces once and returns immediately. If no answer exists yet, it returns { pending: true } and the agent goes to sleep. When the human answers (in the task comments or #agent-questions chat), the agent wakes and continues.
// Agent calls ask_human
{
name: "ask_human",
arguments: {
taskId: "<task-id>",
question: "Which color palette should we use?",
choices: ["Blue", "Green", "Red"],
mentionNames: ["Banjo"] // optional: notify specific people
}
}
// Then calls wait_for_answer
{
name: "wait_for_answer",
arguments: { commentId: "<comment-id>" }
}
// Returns: { answer: "Blue", author: {...}, at: "..." }
// or: { pending: true }Permission model
Three enforcement layers, never less than two:
- 1Capability — what the agent is allowed to do (set in the app, stored on the Agent row).
- 2Key scope — what the API key can do (derived from capabilities, minted once, never exceeds capabilities).
- 3Project access — the agent must be a member of the project to act on it.
| Layer | Checked by | Can be bypassed? |
|---|---|---|
| Capability | canUseTool() on every MCP tool + self-service endpoints | No |
| Key scope | ApiKeyGuard / AgentsDualAuthGuard on every REST + WS call | No |
| Project access | assertProjectAccess on every project-scoped mutation | No — agents are always gated |
No env vars for agents
Environment variable values never reach agents. The MCP read_document tool truncates files and never returns env-var data. This is by design — agents work on code, not secrets.