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

13 KiB

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:

{
  "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.

{
  "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:

{
  "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.