--- title: "Worker Service" description: "HTTP API and Bun process management" --- # Worker Service The worker service is a long-running HTTP API built with Express.js and managed natively by Bun. It processes observations through the Claude Agent SDK separately from hook execution to prevent timeout issues. ## Overview - **Technology**: Express.js HTTP server - **Runtime**: Bun (auto-installed if missing) - **Process Manager**: Native Bun process management via ProcessManager - **Port**: Fixed port 37777 (configurable via `CLAUDE_MEM_WORKER_PORT`) - **Location**: `src/services/worker-service.ts` - **Built Output**: `plugin/scripts/worker-service.cjs` - **Model**: Configurable via `CLAUDE_MEM_MODEL` environment variable (default: sonnet) ## REST API Endpoints The worker service exposes 22 HTTP endpoints organized into six categories: ### Viewer & Health Endpoints #### 1. Viewer UI ``` GET / ``` **Purpose**: Serves the web-based viewer UI (v5.1.0+) **Response**: HTML page with embedded React application **Features**: - Real-time memory stream visualization - Infinite scroll pagination - Project filtering - SSE-based live updates - Theme toggle (light/dark mode) as of v5.1.2 #### 2. Health Check ``` GET /health ``` **Purpose**: Worker health status check **Response**: ```json { "status": "ok", "uptime": 12345, "port": 37777 } ``` #### 3. Server-Sent Events Stream ``` GET /stream ``` **Purpose**: Real-time updates for viewer UI **Response**: SSE stream with events: - `observation-created`: New observation added - `session-summary-created`: New summary generated - `user-prompt-created`: New prompt recorded **Event Format**: ``` event: observation-created data: {"id": 123, "title": "...", ...} ``` ### Data Retrieval Endpoints #### 4. Get Prompts ``` GET /api/prompts?project=my-project&limit=20&offset=0 ``` **Purpose**: Retrieve paginated user prompts **Query Parameters**: - `project` (optional): Filter by project name - `limit` (default: 20): Number of results - `offset` (default: 0): Pagination offset **Response**: ```json { "prompts": [{ "id": 1, "session_id": "abc123", "prompt": "User's prompt text", "prompt_number": 1, "created_at": "2025-11-06T10:30:00Z" }], "total": 150, "hasMore": true } ``` #### 5. Get Observations ``` GET /api/observations?project=my-project&limit=20&offset=0 ``` **Purpose**: Retrieve paginated observations **Query Parameters**: - `project` (optional): Filter by project name - `limit` (default: 20): Number of results - `offset` (default: 0): Pagination offset **Response**: ```json { "observations": [{ "id": 123, "title": "Fix authentication bug", "type": "bugfix", "narrative": "...", "created_at": "2025-11-06T10:30:00Z" }], "total": 500, "hasMore": true } ``` #### 6. Get Summaries ``` GET /api/summaries?project=my-project&limit=20&offset=0 ``` **Purpose**: Retrieve paginated session summaries **Query Parameters**: - `project` (optional): Filter by project name - `limit` (default: 20): Number of results - `offset` (default: 0): Pagination offset **Response**: ```json { "summaries": [{ "id": 456, "session_id": "abc123", "request": "User's original request", "completed": "Work finished", "created_at": "2025-11-06T10:30:00Z" }], "total": 100, "hasMore": true } ``` #### 7. Get Observation by ID ``` GET /api/observation/:id ``` **Purpose**: Retrieve a single observation by its ID **Path Parameters**: - `id` (required): Observation ID **Response**: ```json { "id": 123, "sdk_session_id": "abc123", "project": "my-project", "type": "bugfix", "title": "Fix authentication bug", "narrative": "...", "created_at": "2025-11-06T10:30:00Z", "created_at_epoch": 1730886600000 } ``` **Error Response** (404): ```json { "error": "Observation #123 not found" } ``` #### 8. Get Observations by IDs (Batch) ``` POST /api/observations/batch ``` **Purpose**: Retrieve multiple observations by their IDs in a single request **Request Body**: ```json { "ids": [123, 456, 789], "orderBy": "date_desc", "limit": 10, "project": "my-project" } ``` **Body Parameters**: - `ids` (required): Array of observation IDs - `orderBy` (optional): Sort order - `date_desc` or `date_asc` (default: `date_desc`) - `limit` (optional): Maximum number of results to return - `project` (optional): Filter by project name **Response**: ```json [ { "id": 789, "sdk_session_id": "abc123", "project": "my-project", "type": "feature", "title": "Add new feature", "narrative": "...", "created_at": "2025-11-06T12:00:00Z", "created_at_epoch": 1730891400000 }, { "id": 456, "sdk_session_id": "abc124", "project": "my-project", "type": "bugfix", "title": "Fix authentication bug", "narrative": "...", "created_at": "2025-11-06T10:30:00Z", "created_at_epoch": 1730886600000 } ] ``` **Error Responses**: - `400 Bad Request`: `{"error": "ids must be an array of numbers"}` - `400 Bad Request`: `{"error": "All ids must be integers"}` **Use Case**: This endpoint is used by the `get_observations` MCP tool to efficiently retrieve multiple observations in a single request, avoiding the overhead of multiple individual requests. #### 9. Get Session by ID ``` GET /api/session/:id ``` **Purpose**: Retrieve a single session by its ID **Path Parameters**: - `id` (required): Session ID **Response**: ```json { "id": 456, "sdk_session_id": "abc123", "project": "my-project", "request": "User's original request", "completed": "Work finished", "created_at": "2025-11-06T10:30:00Z" } ``` **Error Response** (404): ```json { "error": "Session #456 not found" } ``` #### 10. Get Prompt by ID ``` GET /api/prompt/:id ``` **Purpose**: Retrieve a single user prompt by its ID **Path Parameters**: - `id` (required): Prompt ID **Response**: ```json { "id": 1, "session_id": "abc123", "prompt": "User's prompt text", "prompt_number": 1, "created_at": "2025-11-06T10:30:00Z" } ``` **Error Response** (404): ```json { "error": "Prompt #1 not found" } ``` #### 12. Get Stats ``` GET /api/stats ``` **Purpose**: Get database statistics by project **Response**: ```json { "byProject": { "my-project": { "observations": 245, "summaries": 12, "prompts": 48 }, "other-project": { "observations": 156, "summaries": 8, "prompts": 32 } }, "total": { "observations": 401, "summaries": 20, "prompts": 80, "sessions": 20 } } ``` #### 13. Get Projects ``` GET /api/projects ``` **Purpose**: Get list of distinct projects from observations **Response**: ```json { "projects": ["my-project", "other-project", "test-project"] } ``` ### Settings Endpoints #### 14. Get Settings ``` GET /api/settings ``` **Purpose**: Retrieve user settings **Response**: ```json { "sidebarOpen": true, "selectedProject": "my-project", "theme": "dark" } ``` #### 15. Save Settings ``` POST /api/settings ``` **Purpose**: Persist user settings **Request Body**: ```json { "sidebarOpen": false, "selectedProject": "other-project", "theme": "light" } ``` **Response**: ```json { "success": true } ``` ### Queue Management Endpoints #### 16. Get Pending Queue Status ``` GET /api/pending-queue ``` **Purpose**: View current processing queue status and identify stuck messages **Response**: ```json { "queue": { "messages": [ { "id": 123, "session_db_id": 45, "claude_session_id": "abc123", "message_type": "observation", "status": "pending", "retry_count": 0, "created_at_epoch": 1730886600000, "started_processing_at_epoch": null, "completed_at_epoch": null } ], "totalPending": 5, "totalProcessing": 2, "totalFailed": 0, "stuckCount": 1 }, "recentlyProcessed": [ { "id": 122, "session_db_id": 44, "status": "processed", "completed_at_epoch": 1730886500000 } ], "sessionsWithPendingWork": [44, 45, 46] } ``` **Status Definitions**: - `pending`: Message queued, not yet processed - `processing`: Message currently being processed by SDK agent - `processed`: Message completed successfully - `failed`: Message failed after max retry attempts (3 by default) **Stuck Detection**: Messages in `processing` status for >5 minutes are considered stuck and included in `stuckCount` **Use Case**: Check queue health after worker crashes or restarts to identify unprocessed observations #### 17. Trigger Manual Recovery ``` POST /api/pending-queue/process ``` **Purpose**: Manually trigger processing of pending queues (replaces automatic recovery in v5.x+) **Request Body**: ```json { "sessionLimit": 10 } ``` **Body Parameters**: - `sessionLimit` (optional): Maximum number of sessions to process (default: 10, max: 100) **Response**: ```json { "success": true, "totalPendingSessions": 15, "sessionsStarted": 10, "sessionsSkipped": 2, "startedSessionIds": [44, 45, 46, 47, 48, 49, 50, 51, 52, 53] } ``` **Response Fields**: - `totalPendingSessions`: Total sessions with pending messages in database - `sessionsStarted`: Number of sessions we started processing this request - `sessionsSkipped`: Sessions already actively processing (not restarted) - `startedSessionIds`: Database IDs of sessions started **Behavior**: - Processes up to `sessionLimit` sessions with pending work - Skips sessions already actively processing (prevents duplicate agents) - Starts non-blocking SDK agents for each session - Returns immediately with status (processing continues in background) **Use Case**: Manually recover stuck observations after worker crashes, or when automatic recovery was disabled **Recovery Strategy Note**: As of v5.x, automatic recovery on worker startup is disabled by default. Users must manually trigger recovery using this endpoint or the CLI tool (`bun scripts/check-pending-queue.ts`) to maintain explicit control over reprocessing. ### Session Management Endpoints #### 19. Initialize Session ``` POST /sessions/:sessionDbId/init ``` **Request Body**: ```json { "sdk_session_id": "abc-123", "project": "my-project" } ``` **Response**: ```json { "success": true, "session_id": "abc-123" } ``` #### 20. Add Observation ``` POST /sessions/:sessionDbId/observations ``` **Request Body**: ```json { "tool_name": "Read", "tool_input": {...}, "tool_result": "...", "correlation_id": "xyz-789" } ``` **Response**: ```json { "success": true, "observation_id": 123 } ``` #### 21. Generate Summary ``` POST /sessions/:sessionDbId/summarize ``` **Request Body**: ```json { "trigger": "stop" } ``` **Response**: ```json { "success": true, "summary_id": 456 } ``` #### 22. Session Status ``` GET /sessions/:sessionDbId/status ``` **Response**: ```json { "session_id": "abc-123", "status": "active", "observation_count": 42, "summary_count": 1 } ``` #### 23. Delete Session ``` DELETE /sessions/:sessionDbId ``` **Response**: ```json { "success": true } ``` **Note**: As of v4.1.0, the cleanup hook no longer calls this endpoint. Sessions are marked complete instead of deleted to allow graceful worker shutdown. ## Bun Process Management ### Overview The worker is managed by the native `ProcessManager` class which handles: - Process spawning with Bun runtime - PID file tracking at `~/.claude-mem/worker.pid` - Health checks with automatic retry - Graceful shutdown with SIGTERM/SIGKILL fallback ### Commands ```bash # Start worker (auto-starts on first session) npm run worker:start # Stop worker npm run worker:stop # Restart worker npm run worker:restart # View logs npm run worker:logs # Check status npm run worker:status ``` ### Auto-Start Behavior The worker service auto-starts when the SessionStart hook fires. Manual start is optional. ### Bun Requirement Bun is required to run the worker service. If Bun is not installed, the smart-install script will automatically install it on first run: - **Windows**: `powershell -c "irm bun.sh/install.ps1 | iex"` - **macOS/Linux**: `curl -fsSL https://bun.sh/install | bash` You can also install manually via: - `winget install Oven-sh.Bun` (Windows) - `brew install oven-sh/bun/bun` (macOS) ## Claude Agent SDK Integration The worker service routes observations to the Claude Agent SDK for AI-powered processing: ### Processing Flow 1. **Observation Queue**: Observations accumulate in memory 2. **SDK Processing**: Observations sent to Claude via Agent SDK 3. **XML Parsing**: Responses parsed for structured data 4. **Database Storage**: Processed observations stored in SQLite ### SDK Components - **Prompts** (`src/sdk/prompts.ts`): Builds XML-structured prompts - **Parser** (`src/sdk/parser.ts`): Parses Claude's XML responses - **Worker** (`src/sdk/worker.ts`): Main SDK agent loop ### Model Configuration Set the AI model used for processing via environment variable: ```bash export CLAUDE_MEM_MODEL=sonnet ``` Available shorthand models (forward to latest version): - `haiku` - Fast, cost-efficient - `sonnet` - Balanced (default) - `opus` - Most capable ## Port Allocation The worker uses a fixed port (37777 by default) for consistent communication: - **Default**: Port 37777 - **Override**: Set `CLAUDE_MEM_WORKER_PORT` environment variable - **Port File**: `${CLAUDE_PLUGIN_ROOT}/data/worker.port` tracks current port If port 37777 is in use, the worker will fail to start. Set a custom port via environment variable. ## Data Storage The worker service stores data in the user data directory: ``` ~/.claude-mem/ ├── claude-mem.db # SQLite database (bun:sqlite) ├── worker.pid # PID file for process tracking ├── settings.json # User settings └── logs/ └── worker-YYYY-MM-DD.log # Daily rotating logs ``` ## Error Handling The worker implements graceful degradation: - **Database Errors**: Logged but don't crash the service - **SDK Errors**: Retried with exponential backoff - **Network Errors**: Logged and skipped - **Invalid Input**: Validated and rejected with error response ## Performance - **Async Processing**: Observations processed asynchronously - **In-Memory Queue**: Fast observation accumulation - **Batch Processing**: Multiple observations processed together - **Connection Pooling**: SQLite connections reused ## Troubleshooting See [Troubleshooting - Worker Issues](../troubleshooting.md#worker-service-issues) for common problems and solutions.