# Feature: Agent Context API **Status:** [IN_PROGRESS] Phase 1 Complete **Created:** 2026-01-27 **Author:** Architect --- ## Overview Expose a JSON API that allows external chat agents (e.g., Claude Code) to query dashboard context and manipulate items. Authentication uses a notification-based approval flow with agent identity binding. --- ## User Stories 1. **As a user**, I can approve/deny agent access requests from the dashboard UI 2. **As a user**, I can see which agents have been granted access and revoke them 3. **As an agent**, I can request access by providing my name and unique ID 4. **As an agent**, once approved, I can query the 7-day context (tasks, meals, timeline) 5. **As an agent**, I can complete/uncomplete tasks, update due dates, modify details, and create items --- ## Authentication Flow ``` Agent Dashboard API Browser Tab │ │ │ │ POST /agent/auth/request │ │ │ {name, agent_id} │ │ │ ──────────────────────────────>│ │ │ │ │ │ {request_token, status: │ WebSocket push: │ │ "pending"} │ "Agent X wants access" │ │ <──────────────────────────────│ ────────────────────────────>│ │ │ │ │ │ User approves │ │ │ │ │ POST /agent/auth/approve │ │ │ <────────────────────────────│ │ │ │ │ GET /agent/auth/poll │ │ │ ?token=X │ │ │ ──────────────────────────────>│ │ │ │ │ │ {status: "approved", │ │ │ session_token, expiry} │ │ │ <──────────────────────────────│ │ │ │ │ │ GET /agent/context │ │ │ Authorization: Bearer X │ │ │ ──────────────────────────────>│ │ │ │ │ │ {timeline, tasks, ...} │ │ │ <──────────────────────────────│ │ ``` --- ## Agent Identity Binding | Scenario | Behavior | |----------|----------| | New agent (name + ID never seen) | Show approval prompt, store pairing on approve | | Known agent (name + ID match stored) | Show approval prompt with "Recognized" indicator | | Suspicious (known name, different ID) | Show warning: "Agent 'X' with different ID" — require explicit re-trust | --- ## Design Decisions | Decision | Choice | Rationale | |----------|--------|-----------| | Session refresh | None — re-authenticate after expiry | Keep it simple | | Concurrent sessions | One per agent — new approval invalidates previous | Predictable state | | Deny behavior | Single-request denial, no blocking | Simple first pass | --- ## Database Schema ### New Tables ```sql -- Registered/approved agents CREATE TABLE agents ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, agent_id TEXT NOT NULL UNIQUE, -- UUID from agent created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_seen DATETIME, trusted BOOLEAN DEFAULT 1 -- can be revoked ); -- Pending access requests and sessions CREATE TABLE agent_sessions ( id INTEGER PRIMARY KEY AUTOINCREMENT, request_token TEXT NOT NULL UNIQUE, agent_name TEXT NOT NULL, agent_id TEXT NOT NULL, status TEXT DEFAULT 'pending', -- pending, approved, denied, expired created_at DATETIME DEFAULT CURRENT_TIMESTAMP, expires_at DATETIME NOT NULL, -- request expires after 5 min session_token TEXT, -- populated on approval session_expires_at DATETIME -- session TTL (1 hour) ); CREATE INDEX idx_agent_sessions_request ON agent_sessions(request_token); CREATE INDEX idx_agent_sessions_session ON agent_sessions(session_token); ``` --- ## API Endpoints ### Authentication (no auth required) | Method | Path | Purpose | |--------|------|---------| | POST | `/agent/auth/request` | Request access (returns request_token) | | GET | `/agent/auth/poll?token=X` | Poll for approval status | ### Authentication (browser session required) | Method | Path | Purpose | |--------|------|---------| | POST | `/agent/auth/approve` | User approves request | | POST | `/agent/auth/deny` | User denies request | ### Context (agent session required) | Method | Path | Purpose | |--------|------|---------| | GET | `/agent/context` | Full 7-day context | ### Write Operations (agent session required) | Method | Path | Purpose | Priority | |--------|------|---------|----------| | PATCH | `/agent/tasks/{id}/due` | Update due date | P1 | | POST | `/agent/tasks/{id}/complete` | Complete task | P1 | | POST | `/agent/tasks/{id}/uncomplete` | Reopen task | P1 | | PATCH | `/agent/tasks/{id}` | Update details (title, desc) | P2 | | POST | `/agent/tasks` | Create task | P3 | | POST | `/agent/shopping/add` | Add shopping item | P3 | --- ## Context Response Format ```json { "generated_at": "2026-01-27T10:30:00-10:00", "range": { "start": "2026-01-27", "end": "2026-02-03" }, "timeline": [ { "id": "task_123", "source": "todoist", "type": "task", "title": "Buy groceries", "description": "Milk, eggs, bread", "due": "2026-01-27T17:00:00-10:00", "priority": 2, "completable": true, "url": "https://todoist.com/..." } ], "summary": { "total_items": 42, "by_source": {"todoist": 15, "trello": 20, "plantoeat": 7}, "overdue": 3, "today": 8 } } ``` --- ## Browser Components ### WebSocket Endpoint - Path: `/ws/notifications` - Purpose: Push agent request alerts to open browser tabs - Must handle: reconnect on disconnect, authentication check ### Approval UI - Trigger: WebSocket message of type `agent_request` - Display: Modal or toast notification - Content: - Agent name - Agent ID (truncated to 8 chars) - Trust indicator: "New Agent" / "Recognized" / "Warning: Different ID" - Actions: Approve / Deny buttons ### Agent Management (Phase 3) - List of trusted agents with last-seen timestamp - Revoke access button per agent --- ## Session Configuration | Parameter | Value | |-----------|-------| | Request expiry | 5 minutes | | Session TTL | 1 hour | | Poll interval (agent-side) | 2 seconds | --- ## Security Considerations 1. **Rate limiting** on `/agent/auth/request` — 10 requests/minute per IP 2. **Tokens** are cryptographically random (32 bytes, base64url) 3. **HTTPS required** — tokens in Authorization header 4. **No CSRF** for agent endpoints — token-based auth, not cookies 5. **One session per agent** — new approval invalidates previous session --- ## Implementation Phases ### Phase 1: Auth Flow + Read Context Files to create/modify: - `migrations/010_agent_tables.sql` — new tables - `internal/store/sqlite.go` — agent/session CRUD methods - `internal/handlers/agent.go` — new handler file for agent endpoints - `internal/handlers/websocket.go` — WebSocket notification hub - `cmd/dashboard/main.go` — register new routes - `web/templates/partials/agent-approval.html` — approval modal - `web/static/js/app.js` — WebSocket connection + approval UI logic Endpoints: - POST `/agent/auth/request` - GET `/agent/auth/poll` - POST `/agent/auth/approve` - POST `/agent/auth/deny` - GET `/agent/context` - WS `/ws/notifications` ### Phase 2: Write Operations (P1 + P2) - POST `/agent/tasks/{id}/complete` - POST `/agent/tasks/{id}/uncomplete` - PATCH `/agent/tasks/{id}/due` - PATCH `/agent/tasks/{id}` ### Phase 3: Create + Management - POST `/agent/tasks` - POST `/agent/shopping/add` - Agent management UI (list, revoke) --- ## Testing Strategy ### Unit Tests - `internal/store/sqlite_test.go` — agent/session CRUD - `internal/handlers/agent_test.go` — endpoint logic ### Integration Tests - Full auth flow: request → approve → poll → context - Identity binding: same name different ID triggers warning - Session expiry: requests fail after TTL ### Manual Testing - Open dashboard in browser - Run agent simulation script to request access - Verify notification appears - Approve and verify context returned --- ## Files Reference | Purpose | File | |---------|------| | Migration | `migrations/010_agent_tables.sql` | | Store methods | `internal/store/sqlite.go` | | Agent handlers | `internal/handlers/agent.go` (new) | | WebSocket hub | `internal/handlers/websocket.go` (new) | | Route registration | `cmd/dashboard/main.go` | | Approval modal | `web/templates/partials/agent-approval.html` (new) | | Client JS | `web/static/js/app.js` |