@desplega.ai/agent-swarm 1.2.1 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +20 -1
- package/.env.docker.example +22 -1
- package/.env.example +17 -0
- package/.github/workflows/docker-publish.yml +92 -0
- package/CONTRIBUTING.md +270 -0
- package/DEPLOYMENT.md +391 -0
- package/Dockerfile.worker +29 -1
- package/FAQ.md +19 -0
- package/LICENSE +21 -0
- package/MCP.md +249 -0
- package/README.md +103 -207
- package/assets/agent-swarm-logo-orange.png +0 -0
- package/assets/agent-swarm-logo.png +0 -0
- package/docker-compose.example.yml +137 -0
- package/docker-entrypoint.sh +223 -7
- package/package.json +8 -3
- package/{cc-plugin → plugin}/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -0
- package/plugin/agents/.gitkeep +0 -0
- package/plugin/agents/codebase-analyzer.md +143 -0
- package/plugin/agents/codebase-locator.md +122 -0
- package/plugin/agents/codebase-pattern-finder.md +227 -0
- package/plugin/agents/web-search-researcher.md +109 -0
- package/plugin/commands/create-plan.md +415 -0
- package/plugin/commands/implement-plan.md +89 -0
- package/plugin/commands/research.md +200 -0
- package/plugin/commands/start-leader.md +101 -0
- package/plugin/commands/start-worker.md +56 -0
- package/plugin/commands/swarm-chat.md +78 -0
- package/plugin/commands/todos.md +66 -0
- package/plugin/commands/work-on-task.md +44 -0
- package/plugin/skills/.gitkeep +0 -0
- package/scripts/generate-mcp-docs.ts +415 -0
- package/slack-manifest.json +69 -0
- package/src/be/db.ts +1431 -25
- package/src/cli.tsx +135 -11
- package/src/commands/lead.ts +13 -0
- package/src/commands/runner.ts +255 -0
- package/src/commands/worker.ts +8 -220
- package/src/hooks/hook.ts +102 -14
- package/src/http.ts +361 -5
- package/src/prompts/base-prompt.ts +131 -0
- package/src/server.ts +56 -0
- package/src/slack/app.ts +73 -0
- package/src/slack/commands.ts +88 -0
- package/src/slack/handlers.ts +281 -0
- package/src/slack/index.ts +3 -0
- package/src/slack/responses.ts +175 -0
- package/src/slack/router.ts +170 -0
- package/src/slack/types.ts +20 -0
- package/src/slack/watcher.ts +119 -0
- package/src/tools/create-channel.ts +80 -0
- package/src/tools/get-tasks.ts +54 -21
- package/src/tools/join-swarm.ts +28 -4
- package/src/tools/list-channels.ts +37 -0
- package/src/tools/list-services.ts +110 -0
- package/src/tools/poll-task.ts +46 -3
- package/src/tools/post-message.ts +87 -0
- package/src/tools/read-messages.ts +192 -0
- package/src/tools/register-service.ts +118 -0
- package/src/tools/send-task.ts +80 -7
- package/src/tools/store-progress.ts +9 -3
- package/src/tools/task-action.ts +211 -0
- package/src/tools/unregister-service.ts +110 -0
- package/src/tools/update-profile.ts +105 -0
- package/src/tools/update-service-status.ts +118 -0
- package/src/types.ts +110 -3
- package/src/utils/pretty-print.ts +224 -0
- package/thoughts/shared/plans/.gitkeep +0 -0
- package/thoughts/shared/plans/2025-12-18-inverse-teleport.md +1142 -0
- package/thoughts/shared/plans/2025-12-18-slack-integration.md +1195 -0
- package/thoughts/shared/plans/2025-12-19-agent-log-streaming.md +732 -0
- package/thoughts/shared/plans/2025-12-19-role-based-swarm-plugin.md +361 -0
- package/thoughts/shared/plans/2025-12-20-mobile-responsive-ui.md +501 -0
- package/thoughts/shared/plans/2025-12-20-startup-team-swarm.md +560 -0
- package/thoughts/shared/research/.gitkeep +0 -0
- package/thoughts/shared/research/2025-12-18-slack-integration.md +442 -0
- package/thoughts/shared/research/2025-12-19-agent-log-streaming.md +339 -0
- package/thoughts/shared/research/2025-12-19-agent-secrets-cli-research.md +390 -0
- package/thoughts/shared/research/2025-12-21-gemini-cli-integration.md +376 -0
- package/thoughts/shared/research/2025-12-22-setup-experience-improvements.md +264 -0
- package/tsconfig.json +3 -1
- package/ui/bun.lock +692 -0
- package/ui/index.html +22 -0
- package/ui/package.json +32 -0
- package/ui/pnpm-lock.yaml +3034 -0
- package/ui/postcss.config.js +6 -0
- package/ui/public/logo.png +0 -0
- package/ui/src/App.tsx +43 -0
- package/ui/src/components/ActivityFeed.tsx +415 -0
- package/ui/src/components/AgentDetailPanel.tsx +534 -0
- package/ui/src/components/AgentsPanel.tsx +549 -0
- package/ui/src/components/ChatPanel.tsx +1820 -0
- package/ui/src/components/ConfigModal.tsx +232 -0
- package/ui/src/components/Dashboard.tsx +534 -0
- package/ui/src/components/Header.tsx +168 -0
- package/ui/src/components/ServicesPanel.tsx +612 -0
- package/ui/src/components/StatsBar.tsx +288 -0
- package/ui/src/components/StatusBadge.tsx +124 -0
- package/ui/src/components/TaskDetailPanel.tsx +807 -0
- package/ui/src/components/TasksPanel.tsx +575 -0
- package/ui/src/hooks/queries.ts +170 -0
- package/ui/src/index.css +235 -0
- package/ui/src/lib/api.ts +161 -0
- package/ui/src/lib/config.ts +35 -0
- package/ui/src/lib/theme.ts +214 -0
- package/ui/src/lib/utils.ts +48 -0
- package/ui/src/main.tsx +32 -0
- package/ui/src/types/api.ts +164 -0
- package/ui/src/vite-env.d.ts +1 -0
- package/ui/tailwind.config.js +35 -0
- package/ui/tsconfig.json +31 -0
- package/ui/vite.config.ts +22 -0
- package/cc-plugin/README.md +0 -49
- package/cc-plugin/commands/setup-leader.md +0 -73
- package/cc-plugin/commands/start-worker.md +0 -64
- package/docker-compose.worker.yml +0 -35
- package/example-req-meta.json +0 -24
- /package/{cc-plugin → plugin}/hooks/hooks.json +0 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
---
|
|
2
|
+
date: 2025-12-19T10:45:00-05:00
|
|
3
|
+
researcher: Claude-Main
|
|
4
|
+
git_commit: 7cb27a1
|
|
5
|
+
branch: main
|
|
6
|
+
repository: cc-orch-mcp
|
|
7
|
+
topic: "Agent Log Streaming and SSE Implementation"
|
|
8
|
+
tags: [research, logging, sse, streaming, worker-agents, frontend]
|
|
9
|
+
status: complete
|
|
10
|
+
last_updated: 2025-12-19
|
|
11
|
+
last_updated_by: Claude-Main
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Research: Agent Log Streaming and SSE Implementation
|
|
15
|
+
|
|
16
|
+
**Date**: 2025-12-19T10:45:00-05:00
|
|
17
|
+
**Researcher**: Claude-Main
|
|
18
|
+
**Git Commit**: 7cb27a1
|
|
19
|
+
**Branch**: main
|
|
20
|
+
**Repository**: cc-orch-mcp
|
|
21
|
+
|
|
22
|
+
## Research Question
|
|
23
|
+
|
|
24
|
+
How to store logs of Claude agents in the file system (not DB), stream them to the API for storage, and stream them via SSE to the frontend when viewing a task being executed.
|
|
25
|
+
|
|
26
|
+
## Summary
|
|
27
|
+
|
|
28
|
+
Three existing log sources were identified in the codebase. The recommended approach uses Claude Code hooks to track the current task ID via `/tmp/.task.json`, then streams log chunks from the runner to the API, which broadcasts to frontend via SSE.
|
|
29
|
+
|
|
30
|
+
## Detailed Findings
|
|
31
|
+
|
|
32
|
+
### Existing Log Sources
|
|
33
|
+
|
|
34
|
+
| Source | Location | Content | Format |
|
|
35
|
+
|--------|----------|---------|--------|
|
|
36
|
+
| **Claude Code native** | `~/.claude/projects/{path}/*.jsonl` | Full conversation + tool calls + thinking | JSONL per-message |
|
|
37
|
+
| **Worker runner logs** | `./logs/{sessionId}/{timestamp}.jsonl` | Raw Claude CLI stream output | JSONL continuous |
|
|
38
|
+
| **Database event logs** | `agent_log` table in SQLite | Task state changes + progress | Structured rows |
|
|
39
|
+
|
|
40
|
+
### Claude Code Native JSONL Files
|
|
41
|
+
|
|
42
|
+
Located at `~/.claude/projects/{escaped-project-path}/`:
|
|
43
|
+
|
|
44
|
+
**File types:**
|
|
45
|
+
- `{uuid}.jsonl` - Main conversation sessions
|
|
46
|
+
- `agent-{short-id}.jsonl` - Sub-agent (Task tool) sessions
|
|
47
|
+
|
|
48
|
+
**Record structure:**
|
|
49
|
+
- `sessionId` - UUID linking all messages
|
|
50
|
+
- `type` - `user`, `assistant`, `system`, `summary`
|
|
51
|
+
- `timestamp` - ISO datetime
|
|
52
|
+
- `message.content` - Array of thinking blocks, text, tool_use
|
|
53
|
+
- `message.model` - Model used
|
|
54
|
+
- `message.usage` - Token counts
|
|
55
|
+
|
|
56
|
+
**Sub-agent files include:**
|
|
57
|
+
- `agentId` - Short ID matching filename
|
|
58
|
+
- `slug` - Human-readable name
|
|
59
|
+
- `isSidechain: true` - Marker for sub-agent
|
|
60
|
+
|
|
61
|
+
### Worker Runner Implementation
|
|
62
|
+
|
|
63
|
+
**File:** `src/commands/runner.ts`
|
|
64
|
+
|
|
65
|
+
The runner spawns Claude CLI with `--output-format stream-json` (line 43) and captures:
|
|
66
|
+
|
|
67
|
+
**Stdout parsing (lines 77-126):**
|
|
68
|
+
```typescript
|
|
69
|
+
for await (const chunk of proc.stdout) {
|
|
70
|
+
const text = new TextDecoder().decode(chunk);
|
|
71
|
+
logFileHandle.write(text); // writes to local JSONL file
|
|
72
|
+
|
|
73
|
+
// Parses JSON lines into event types:
|
|
74
|
+
// - assistant (message content)
|
|
75
|
+
// - tool_use (tool calls)
|
|
76
|
+
// - result (tool results)
|
|
77
|
+
// - error (errors)
|
|
78
|
+
// - system (system messages)
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Stderr capture (lines 128-142):**
|
|
83
|
+
```typescript
|
|
84
|
+
for await (const chunk of proc.stderr) {
|
|
85
|
+
const text = new TextDecoder().decode(chunk);
|
|
86
|
+
logFileHandle.write(JSON.stringify({ type: "stderr", content: text, timestamp }));
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Log directory structure:**
|
|
91
|
+
```
|
|
92
|
+
./logs/{sessionId}/
|
|
93
|
+
├── {timestamp}.jsonl # Per-iteration logs
|
|
94
|
+
└── errors.jsonl # Error entries
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Hook System
|
|
98
|
+
|
|
99
|
+
**File:** `src/hooks/hook.ts`
|
|
100
|
+
|
|
101
|
+
Hooks intercept Claude Code events via stdin JSON:
|
|
102
|
+
|
|
103
|
+
**HookMessage fields:**
|
|
104
|
+
- `hook_event_name` - Event type (SessionStart, PostToolUse, Stop, etc.)
|
|
105
|
+
- `tool_name` - For tool events
|
|
106
|
+
- `tool_input` - Tool input parameters
|
|
107
|
+
- `tool_response` - Tool response data
|
|
108
|
+
|
|
109
|
+
**PostToolUse event** can intercept:
|
|
110
|
+
- `poll-task` response containing `task.id`
|
|
111
|
+
- `store-progress` input containing `status` (completed/failed)
|
|
112
|
+
|
|
113
|
+
### HTTP Server
|
|
114
|
+
|
|
115
|
+
**File:** `src/http.ts`
|
|
116
|
+
|
|
117
|
+
Current endpoints:
|
|
118
|
+
- `GET /api/tasks/:id` - Returns task with database logs
|
|
119
|
+
- `GET /api/logs` - Returns recent activity logs
|
|
120
|
+
|
|
121
|
+
MCP SSE support exists via `StreamableHTTPServerTransport` (lines 339-347).
|
|
122
|
+
|
|
123
|
+
### Frontend
|
|
124
|
+
|
|
125
|
+
**File:** `ui/src/components/TaskDetailPanel.tsx`
|
|
126
|
+
|
|
127
|
+
Currently uses polling via TanStack Query with 5-second interval (`ui/src/main.tsx:13`).
|
|
128
|
+
|
|
129
|
+
## Proposed Architecture
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
Worker Container API Server Frontend
|
|
133
|
+
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
|
134
|
+
│ Claude CLI │ │ │ │ │
|
|
135
|
+
│ (stream-json) │ │ POST /api/tasks │ │ EventSource │
|
|
136
|
+
│ ↓ │ │ /:id/logs │──────────▶│ subscription │
|
|
137
|
+
│ runner.ts │──────────────────▶│ ↓ │ │ ↓ │
|
|
138
|
+
│ (parse chunks) │ │ Store + SSE │ │ TaskDetailPanel │
|
|
139
|
+
│ ↑ │ │ broadcast │ │ (live logs) │
|
|
140
|
+
│ hook writes │ │ ↓ │ │ │
|
|
141
|
+
│ /tmp/.task.json │ │ GET /api/tasks │◀──────────│ │
|
|
142
|
+
└─────────────────┘ │ /:id/logs/stream│ └─────────────────┘
|
|
143
|
+
└─────────────────┘
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Implementation Plan
|
|
147
|
+
|
|
148
|
+
### Step 1: Hook - Track Current Task ID
|
|
149
|
+
|
|
150
|
+
**File:** `src/hooks/hook.ts`
|
|
151
|
+
|
|
152
|
+
In `PostToolUse` handler:
|
|
153
|
+
- When `poll-task` succeeds, write `/tmp/.task.json` with `{ taskId, assignedAt }`
|
|
154
|
+
- When `store-progress` completes/fails, clear `/tmp/.task.json`
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const TASK_FILE = "/tmp/.task.json";
|
|
158
|
+
|
|
159
|
+
case "PostToolUse":
|
|
160
|
+
// Track task assignment from poll-task
|
|
161
|
+
if (msg.tool_name?.endsWith("poll-task")) {
|
|
162
|
+
const response = msg.tool_response as { success?: boolean; task?: { id: string } };
|
|
163
|
+
if (response?.success && response?.task?.id) {
|
|
164
|
+
await Bun.write(TASK_FILE, JSON.stringify({
|
|
165
|
+
taskId: response.task.id,
|
|
166
|
+
assignedAt: new Date().toISOString()
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Clear on task completion/failure
|
|
172
|
+
if (msg.tool_name?.endsWith("store-progress")) {
|
|
173
|
+
const input = msg.tool_input as { status?: string };
|
|
174
|
+
if (input?.status === "completed" || input?.status === "failed") {
|
|
175
|
+
try {
|
|
176
|
+
const file = Bun.file(TASK_FILE);
|
|
177
|
+
if (await file.exists()) {
|
|
178
|
+
await Bun.write(TASK_FILE, "");
|
|
179
|
+
}
|
|
180
|
+
} catch {}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Step 2: Runner - Stream Logs to API
|
|
187
|
+
|
|
188
|
+
**File:** `src/commands/runner.ts`
|
|
189
|
+
|
|
190
|
+
- Add `getCurrentTaskId()` function to read `/tmp/.task.json`
|
|
191
|
+
- In stdout/stderr loops, POST log chunks to `/api/tasks/:id/logs`
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
const TASK_FILE = "/tmp/.task.json";
|
|
195
|
+
const API_BASE_URL = process.env.MCP_BASE_URL || "http://localhost:3013";
|
|
196
|
+
const API_KEY = process.env.API_KEY || "";
|
|
197
|
+
|
|
198
|
+
async function getCurrentTaskId(): Promise<string | null> {
|
|
199
|
+
try {
|
|
200
|
+
const file = Bun.file(TASK_FILE);
|
|
201
|
+
if (await file.exists()) {
|
|
202
|
+
const content = await file.text();
|
|
203
|
+
if (!content.trim()) return null;
|
|
204
|
+
const data = JSON.parse(content);
|
|
205
|
+
return data.taskId || null;
|
|
206
|
+
}
|
|
207
|
+
} catch {}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function streamLogToApi(taskId: string, logEntry: object): Promise<void> {
|
|
212
|
+
try {
|
|
213
|
+
await fetch(`${API_BASE_URL}/api/tasks/${taskId}/logs`, {
|
|
214
|
+
method: "POST",
|
|
215
|
+
headers: {
|
|
216
|
+
"Content-Type": "application/json",
|
|
217
|
+
...(API_KEY ? { Authorization: `Bearer ${API_KEY}` } : {}),
|
|
218
|
+
},
|
|
219
|
+
body: JSON.stringify(logEntry),
|
|
220
|
+
});
|
|
221
|
+
} catch (err) {
|
|
222
|
+
console.error(`[runner] Failed to stream log: ${err}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Step 3: API Server - Log Ingestion + SSE Broadcast
|
|
228
|
+
|
|
229
|
+
**File:** `src/http.ts`
|
|
230
|
+
|
|
231
|
+
New endpoints:
|
|
232
|
+
- `POST /api/tasks/:id/logs` - Worker pushes log chunks
|
|
233
|
+
- `GET /api/tasks/:id/logs` - Get logs from disk
|
|
234
|
+
- `GET /api/tasks/:id/logs/stream` - SSE subscription for new logs
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
// File-based storage in /logs directory
|
|
238
|
+
const LOG_DIR = process.env.LOG_DIR || "./logs";
|
|
239
|
+
const taskLogSubscribers: Record<string, Set<ServerResponse>> = {};
|
|
240
|
+
|
|
241
|
+
// POST /api/tasks/:id/logs - Receive log chunks
|
|
242
|
+
// - Append to /logs/{taskId}.jsonl file
|
|
243
|
+
// - Broadcast to SSE subscribers
|
|
244
|
+
|
|
245
|
+
// GET /api/tasks/:id/logs - Read existing logs
|
|
246
|
+
// - Read and parse /logs/{taskId}.jsonl
|
|
247
|
+
// - Return all log entries
|
|
248
|
+
|
|
249
|
+
// GET /api/tasks/:id/logs/stream - SSE endpoint
|
|
250
|
+
// - Register subscriber for new logs
|
|
251
|
+
// - Broadcast when POST receives new entries
|
|
252
|
+
// - Cleanup on close
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Step 4: Frontend - SSE Subscription
|
|
256
|
+
|
|
257
|
+
**Files:** `ui/src/lib/api.ts`, `ui/src/components/TaskDetailPanel.tsx`
|
|
258
|
+
|
|
259
|
+
- Add `subscribeToTaskLogs()` SSE helper using EventSource
|
|
260
|
+
- Subscribe to SSE for in-progress tasks
|
|
261
|
+
- Display streaming logs in real-time
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
export function subscribeToTaskLogs(
|
|
265
|
+
taskId: string,
|
|
266
|
+
onLog: (log: { type: string; content: unknown; timestamp: string }) => void,
|
|
267
|
+
onError?: (error: Event) => void
|
|
268
|
+
): () => void {
|
|
269
|
+
const url = `${baseUrl}/api/tasks/${taskId}/logs/stream`;
|
|
270
|
+
const eventSource = new EventSource(url);
|
|
271
|
+
eventSource.onmessage = (event) => onLog(JSON.parse(event.data));
|
|
272
|
+
eventSource.onerror = onError;
|
|
273
|
+
return () => eventSource.close();
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Step 5: Types
|
|
278
|
+
|
|
279
|
+
**Files:** `src/types.ts`, `ui/src/types/api.ts`
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
export interface StreamingLogEntry {
|
|
283
|
+
type: string;
|
|
284
|
+
content: unknown;
|
|
285
|
+
timestamp: string;
|
|
286
|
+
receivedAt?: string;
|
|
287
|
+
}
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
## Code References
|
|
291
|
+
|
|
292
|
+
- `src/commands/runner.ts:37-164` - Claude CLI spawning and stream capture
|
|
293
|
+
- `src/commands/runner.ts:77-126` - Stdout parsing loop
|
|
294
|
+
- `src/commands/runner.ts:128-142` - Stderr capture loop
|
|
295
|
+
- `src/hooks/hook.ts:172-188` - PostToolUse handler
|
|
296
|
+
- `src/hooks/hook.ts:175-181` - send-task response handling (pattern to follow)
|
|
297
|
+
- `src/tools/poll-task.ts:101-108` - poll-task response structure with task.id
|
|
298
|
+
- `src/tools/store-progress.ts:79-101` - store-progress status handling
|
|
299
|
+
- `src/http.ts:339-347` - Existing SSE/MCP transport pattern
|
|
300
|
+
- `ui/src/main.tsx:13` - Current 5-second polling interval
|
|
301
|
+
- `ui/src/components/TaskDetailPanel.tsx:29-30` - Current data fetching
|
|
302
|
+
|
|
303
|
+
## Files to Modify
|
|
304
|
+
|
|
305
|
+
| File | Changes |
|
|
306
|
+
|------|---------|
|
|
307
|
+
| `src/hooks/hook.ts` | Add task file tracking in PostToolUse |
|
|
308
|
+
| `src/commands/runner.ts` | Add API streaming in stdout/stderr loops |
|
|
309
|
+
| `src/http.ts` | Add POST + GET + SSE endpoints for task logs |
|
|
310
|
+
| `src/types.ts` | Add StreamingLogEntry type |
|
|
311
|
+
| `ui/src/lib/api.ts` | Add SSE subscription helper |
|
|
312
|
+
| `ui/src/components/TaskDetailPanel.tsx` | Add SSE subscription + live log display |
|
|
313
|
+
| `ui/src/types/api.ts` | Add StreamingLogEntry type |
|
|
314
|
+
|
|
315
|
+
## Testing Plan
|
|
316
|
+
|
|
317
|
+
1. Start API server: `bun run dev:http`
|
|
318
|
+
2. Run a worker with a task
|
|
319
|
+
3. Verify `/tmp/.task.json` is created on poll-task
|
|
320
|
+
4. Verify logs appear at `GET /api/tasks/:id/logs`
|
|
321
|
+
5. Verify SSE stream at `GET /api/tasks/:id/logs/stream`
|
|
322
|
+
6. Verify `/tmp/.task.json` is cleared on completion
|
|
323
|
+
7. Verify frontend shows live logs
|
|
324
|
+
|
|
325
|
+
## Future Enhancements (Out of Scope)
|
|
326
|
+
|
|
327
|
+
- Persist logs to file system instead of memory
|
|
328
|
+
- Add log pagination/search
|
|
329
|
+
- Add log level filtering
|
|
330
|
+
- Stream Claude Code native JSONL files (`~/.claude/projects/...`)
|
|
331
|
+
- Tail existing log files for historical context
|
|
332
|
+
|
|
333
|
+
## Design Decisions
|
|
334
|
+
|
|
335
|
+
**Log Storage:** Persist to disk in the mounted `/logs` directory (not in-memory). API serves existing logs via GET, then streams new logs via SSE.
|
|
336
|
+
|
|
337
|
+
**Log Retention:** Keep all logs per task (no limit).
|
|
338
|
+
|
|
339
|
+
**Claude Code native JSONL:** Out of scope for now, potentially in future iterations.
|