non-linear-docs/05-AGENT-INTEGRATION.md

349 lines
13 KiB
Markdown

# Non-Linear: AI Agent Integration
## Overview
Non-Linear treats AI agents as first-class actors — not assistants bolted onto a human interface, but independent participants with their own accounts, roles, permissions, and graph navigation capabilities.
## Two Agent Modes
### Agent as User
The agent operates independently within the project graph. It has its own account, picks up tasks, reports status, and navigates the decomposition tree autonomously.
**Primary use case:** One-person team where an AI agent acts as a focused collaborator.
**How the graph helps:** The decomposition tree gives the agent *rails*:
- "What's the highest-priority unblocked leaf node in Backend?" → graph traversal query
- "What's the status of the Auth component?" → read subtree, aggregate child statuses
- "What should I work on next?" → find unblocked, assigned, in-progress leaves
### Agent as Assistant
The agent helps human users by analyzing the graph and suggesting actions.
**Primary use case:** Teams where AI augments human decision-making.
**Examples:**
- "Break this component into tasks" → agent suggests child nodes, human approves
- "What's blocking the Auth epic?" → agent traverses subtree and lateral links
- "Summarize progress on Dashboard this week" → agent reads subtree statuses and recent comments
- "Suggest priority order for these tasks" → agent considers dependencies and blockers
## Agent API (v0.1)
Governed by the same policy engine as human users. Agents authenticate with API tokens tied to their actor account.
### Layer Filtering (post-alpha)
> **Alpha:** No layer filtering. All read endpoints return structure + work data (the only data that exists in alpha). The `layers` query parameter is introduced post-alpha when Layers 3 and 4 are available.
All read endpoints accept an optional `layers` query parameter that controls which data layers are included in the response. This mirrors the UI layer toggles and lets agents scope their view to only the information they need.
```
?layers=structure,work # Default: structure + issues (Layers 1+2)
?layers=structure,connections # Architecture view: structure + code dependencies (Layers 1+3)
?layers=structure,work,connections # Structure + issues + code dependencies (Layers 1+2+3)
?layers=all # All four layers
```
| Layer Value | Layer | What's Included |
|-------------|-------|-----------------|
| `structure` | 1 | Component nodes and component→component edges |
| `work` | 2 | Issue nodes, work coordination links (blocks, duplicates, relates_to) |
| `connections` | 3 | Code connection edges (depends_on, imports, calls_api, shares_db) |
| `artifacts` | 4 | Artifact nodes and attachment edges |
If `layers` is omitted, the default is `structure,work` (backward-compatible with the pre-layer API). The `structure` layer is always implicitly included — it cannot be excluded since it provides the base topology.
### Graph Traversal
```
GET /api/v1/nodes/{id} # Read single node
GET /api/v1/nodes/{id}/children # Direct children
GET /api/v1/nodes/{id}/subtree # Full subtree (depth-limited)
GET /api/v1/nodes/{id}/parent # Parent node
GET /api/v1/nodes/{id}/links # Outbound lateral links (filtered by active layers)
GET /api/v1/nodes/{id}/backlinks # Inbound lateral links (filtered by active layers)
GET /api/v1/nodes/{id}/path # Path from root to this node
GET /api/v1/nodes/{id}/history # Change history timeline
GET /api/v1/nodes/{id}/artifacts # Attached artifacts (Layer 4)
GET /api/v1/nodes/{id}/connections # Code connections for this component (Layer 3)
GET /api/v1/projects/{id}/root # Project root node
GET /api/v1/projects/{id}/inbox # Triage inbox (rootless nodes)
```
### Queries
```
GET /api/v1/projects/{id}/nodes?status=in_progress&assignee=agent-123
GET /api/v1/projects/{id}/nodes?label=bug&status=backlog
GET /api/v1/projects/{id}/nodes?unblocked=true&status=todo
GET /api/v1/projects/{id}/nodes?cycle=current
GET /api/v1/projects/{id}/nodes?layers=structure,connections&type=component
```
### Write Actions (v0.1)
```
POST /api/v1/nodes/{id}/comments # Add comment
PATCH /api/v1/nodes/{id}/status # Change status
PATCH /api/v1/nodes/{id}/parent # Triage: assign parent to inbox item
```
### Write Actions (v0.1 — Layer 4, post-alpha)
> **Alpha:** No artifact endpoints. Introduced post-alpha with Layer 4.
```
POST /api/v1/nodes/{id}/artifacts # Attach an artifact (link or file)
DELETE /api/v1/nodes/{id}/artifacts/{aid} # Remove an artifact
```
### Deferred Actions (v0.2+)
```
POST /api/v1/nodes/{id}/children # Create child node
POST /api/v1/nodes/{id}/links # Create lateral link (Layer 2 or Layer 3)
DELETE /api/v1/nodes/{id}/links/{link_id} # Remove lateral link
PATCH /api/v1/nodes/{id} # Edit node details
POST /api/v1/nodes/{id}/connections # Declare a code connection (Layer 3, manual)
```
### Authentication
- Agents authenticate via API tokens (bearer tokens)
- Each token is tied to an actor (agent account)
- The actor has roles/policies assigned via the policy engine
- Token scoping: a token can optionally be restricted to a subset of the agent's permissions
### Pagination (post-alpha)
> **Alpha:** No pagination. Graphs will be small (<500 nodes). All list endpoints return full results. Pagination is introduced post-alpha when projects grow beyond alpha scale.
All list endpoints use **cursor-based pagination**. Responses include a cursor for the next page and a flag indicating whether more results exist.
**Response envelope:**
```json
{
"items": [ ... ],
"next_cursor": "eyJpZCI6IjEyMyIsInRzIjoiMjAyNS0wMS0wMVQwMDowMDowMFoifQ==",
"has_more": true
}
```
**Query parameters:**
| Parameter | Default | Max | Description |
|-----------|---------|-----|-------------|
| `limit` | 50 | 200 | Items per page |
| `cursor` | null | — | Opaque cursor from previous response |
| `max_depth` | 3 | 10 | Subtree query depth limit (subtree endpoint only) |
- **Subtree queries** (`GET /nodes/{id}/subtree`) accept `max_depth` to cap traversal depth. The graph visualization uses this to load progressively — fetch immediate children on expand, not the entire tree at once.
- **Children queries** (`GET /nodes/{id}/children`) are paginated but not depth-limited (they return a single level).
- **Search/filter queries** (`GET /projects/{id}/nodes?...`) are always paginated.
- Cursor encoding is opaque to the client. Internally it encodes the sort key (e.g., `created_at` + `id`) for stable pagination under concurrent writes.
### Response Format
All responses include effective permissions on the returned resource. Links and backlinks are grouped by layer when multiple layers are requested.
```json
{
"node": {
"id": "uuid-123",
"short_id": "NL-42",
"title": "Login component",
"status": "in_progress",
"labels": ["frontend", "p1"],
"parent_id": "uuid-456",
"children_count": 3,
"active_layers": ["structure", "work", "connections"],
"backlinks": {
"work": {
"blocked_by_count": 1,
"relates_to_count": 2
},
"connections": {
"depended_on_by_count": 3
}
},
"links": {
"work": [
{
"type": "blocked_by",
"target_id": "uuid-789",
"target_accessible": true,
"target_title": "SSO integration"
}
],
"connections": [
{
"type": "imports",
"target_id": "uuid-lib",
"target_accessible": true,
"target_title": "jwt-lib",
"source": "inferred"
}
]
},
"artifacts_count": 2
},
"permissions": {
"can_edit": false,
"can_change_status": true,
"can_comment": true,
"can_create_child": false,
"can_attach_artifact": true
}
}
```
When `layers` excludes a layer, the corresponding key is omitted from `links` and `backlinks`. The `artifacts_count` field is only present when `layers` includes `artifacts`.
```
## Agent Design Patterns
### Focused Worker Agent
```
Loop:
1. Query subtree for unblocked, assigned, todo items
2. Pick highest priority
3. Change status to in_progress
4. Do work (external to tracker)
5. Post comment with results
6. Change status to done or in_review
```
### Triage Agent
```
Loop:
1. Query inbox for unplaced nodes
2. For each, analyze title/description and match to existing subtrees
3. Assign parent (place in tree)
4. Post comment with rationale
```
### Impact Analysis Agent (post-alpha, requires Layer 3)
```
Uses layers: structure + connections (Layer 1 + Layer 3)
1. Query component with ?layers=structure,connections
2. Traverse DEPENDS_ON / IMPORTS / CALLS_API edges outward
3. Build a blast radius: "changing auth-service affects 4 downstream components"
4. Post comment with dependency diagram
5. Optionally cross-reference with Layer 2 (work): "3 in-progress issues touch affected components"
```
### Decomposition Agent (v0.2+)
```
Triggered when: a node has label "needs-decomposition"
1. Read node context (parent, siblings, description)
2. Propose child nodes via comment or plan-then-apply
3. Human reviews and approves
4. Agent creates children
```
## Webhooks (v0.1)
Webhooks follow a **GitLab-style hooks model** — per-project registration with event filtering, signed payloads, and automatic retry.
### Registration
Webhooks are registered per project via the API or settings UI:
```
POST /api/v1/projects/{id}/webhooks
```
```json
{
"url": "https://agent.example.com/hooks/nonlinear",
"secret": "whsec_...",
"events": ["node.status_changed", "node.comment_added"],
"active": true
}
```
| Field | Type | Description |
|-------|------|-------------|
| `url` | string | HTTPS endpoint to receive POST requests |
| `secret` | string | Shared secret for HMAC-SHA256 signature |
| `events` | string[] | Event types to subscribe to (empty = all) |
| `active` | bool | Enable/disable without deleting |
### Event Catalog
| Event | Layer | Trigger |
|-------|-------|---------|
| `node.created` | 1/2 | New node (component or issue) created |
| `node.status_changed` | 2 | Issue status transition |
| `node.comment_added` | 2 | Comment posted on a node |
| `node.link_created` | 2/3 | Lateral link created (work or code connection) |
| `node.link_removed` | 2/3 | Lateral link removed |
| `node.reparented` | 1/2 | Node moved to a different parent |
| `node.cycle_added` | 2 | Issue added to a cycle |
| `node.deleted` | 1/2 | Node (and subtree) deleted |
| `artifact.attached` | 4 | Artifact attached to a node — POST-ALPHA |
| `artifact.removed` | 4 | Artifact removed from a node — POST-ALPHA |
| `connection.inferred` | 3 | Code connection auto-detected from repo analysis — POST-ALPHA |
| `repo.commit_associated` | 1 | Commit auto-associated with a component |
Webhook subscriptions can filter by layer: `"events": ["layer:3"]` subscribes to all Layer 3 events only.
### Payload Format
All webhook payloads share a common envelope:
```json
{
"event": "node.status_changed",
"timestamp": "2025-06-15T14:30:00Z",
"project_id": "uuid-project",
"actor": {
"id": "uuid-actor",
"type": "user",
"name": "alice"
},
"data": {
"node_id": "uuid-123",
"short_id": "NL-42",
"previous_status": "in_progress",
"new_status": "in_review"
}
}
```
The `data` field varies by event type. Each event includes the affected node's `node_id` and `short_id` at minimum.
### Signature Verification (post-alpha)
> **Alpha:** No HMAC signatures on webhook payloads. Single delivery attempt, failures logged. Signature verification and retry logic are introduced post-alpha.
Every webhook request includes an `X-NonLinear-Signature` header containing an HMAC-SHA256 hex digest of the raw request body, computed with the webhook's shared secret:
```
X-NonLinear-Signature: sha256=a1b2c3d4e5f6...
```
Receivers should verify the signature before processing to confirm authenticity.
### Delivery & Retry (post-alpha)
> **Alpha:** Single delivery attempt per event. Failures logged but not retried. The retry and auto-deactivation logic below is introduced post-alpha.
- Webhooks are delivered asynchronously via the Taskiq worker.
- **Timeout:** 10 seconds per delivery attempt.
- **Retry:** 3 attempts with exponential backoff (10s, 60s, 300s).
- **Failure handling:** After 3 failed attempts, the event is logged and dropped. After 50 consecutive failures across any events, the webhook is automatically deactivated and the project owner is notified.
- **Ordering:** Best-effort chronological. Not guaranteed under high concurrency.
## MCP Compatibility (v0.2+)
Expose the Non-Linear API as an MCP server so AI agents built on LLM frameworks can connect natively.