diff options
| author | Peter Stone <thepeterstone@gmail.com> | 2026-02-05 10:43:19 -1000 |
|---|---|---|
| committer | Peter Stone <thepeterstone@gmail.com> | 2026-02-05 10:43:19 -1000 |
| commit | 1eab4d59454fa5999675d51b99e77ac6580aba95 (patch) | |
| tree | 6b653e39d33fd879f29f769cdf3bd3f6bfcd3f05 /issues/feature_agent_context_api.md | |
| parent | 5ddb419137b814481a208d1dd0d18ac36ed554ea (diff) | |
Improve session handling, shopping UI, and cleanup
Session improvements:
- Extend session lifetime to 7 days for mobile convenience
- Add idle timeout to extend session on activity
- Use standard cookie name for better compatibility
Shopping model:
- Add FlattenItemsForStore helper for extracting store items
- Add StoreNames helper for store list
- Improve shopping-tab.html with inline add forms
Frontend:
- Add WebSocket reconnection and agent approval UI to app.js
- Simplify timeline calendar JS (move event positioning to CSS)
- Update login page styling
Deployment:
- Remove unused git checkout step from deploy.sh
- Update apache.conf WebSocket proxy settings
Documentation:
- Add Agent Context API feature spec to issues/
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'issues/feature_agent_context_api.md')
| -rw-r--r-- | issues/feature_agent_context_api.md | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/issues/feature_agent_context_api.md b/issues/feature_agent_context_api.md new file mode 100644 index 0000000..7be5f50 --- /dev/null +++ b/issues/feature_agent_context_api.md @@ -0,0 +1,287 @@ +# 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` | |
