@desplega.ai/agent-swarm 1.9.0 → 1.10.2
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 +3 -1
- package/Dockerfile +43 -5
- package/Dockerfile.worker +1 -1
- package/README.md +16 -0
- package/UI.md +40 -0
- package/deploy/prod-db.ts +2 -2
- package/docker-compose.example.yml +49 -50
- package/docker-entrypoint.sh +4 -1
- package/package.json +1 -1
- package/plugin/commands/review-offered-task.md +45 -0
- package/plugin/commands/start-leader.md +7 -5
- package/plugin/commands/start-worker.md +5 -0
- package/src/be/db.ts +101 -0
- package/src/cli.tsx +44 -5
- package/src/commands/lead.ts +2 -0
- package/src/commands/runner.ts +383 -44
- package/src/commands/worker.ts +2 -0
- package/src/http.ts +217 -1
- package/src/prompts/base-prompt.ts +17 -3
- package/src/tests/runner-polling-api.test.ts +456 -0
- package/src/tests/session-logs.test.ts +388 -0
- package/src/types.ts +14 -0
- package/src/utils/pretty-print.ts +137 -120
- package/thoughts/shared/plans/2025-12-23-runner-level-polling.md +934 -0
- package/thoughts/shared/plans/2025-12-23-runner-session-logs.md +1000 -0
- package/thoughts/shared/research/2025-12-22-runner-loop-architecture.md +582 -0
- package/ui/src/components/SessionLogPanel.tsx +433 -0
- package/ui/src/components/TaskDetailPanel.tsx +98 -78
- package/ui/src/hooks/queries.ts +9 -0
- package/ui/src/hooks/useAutoScroll.ts +55 -0
- package/ui/src/lib/api.ts +10 -0
- package/ui/src/types/api.ts +15 -0
package/Dockerfile
CHANGED
|
@@ -1,16 +1,54 @@
|
|
|
1
|
-
|
|
1
|
+
# Agent Swarm MCP Server Dockerfile
|
|
2
|
+
# Multi-stage build: compiles to standalone binary for minimal image size
|
|
3
|
+
|
|
4
|
+
# Stage 1: Build the binary
|
|
5
|
+
FROM oven/bun:latest AS builder
|
|
6
|
+
|
|
7
|
+
WORKDIR /build
|
|
8
|
+
|
|
9
|
+
# Copy package files first for better layer caching
|
|
10
|
+
COPY package.json bun.lock* ./
|
|
11
|
+
RUN bun install --frozen-lockfile
|
|
12
|
+
|
|
13
|
+
# Copy source files
|
|
14
|
+
COPY src/ ./src/
|
|
15
|
+
COPY tsconfig.json ./
|
|
16
|
+
|
|
17
|
+
# Compile HTTP server to standalone binary
|
|
18
|
+
RUN bun build ./src/http.ts --compile --outfile ./agent-swarm-api
|
|
19
|
+
|
|
20
|
+
# Stage 2: Minimal runtime image
|
|
21
|
+
FROM debian:bookworm-slim
|
|
22
|
+
|
|
23
|
+
# Install minimal dependencies (for bun:sqlite and networking)
|
|
24
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
25
|
+
ca-certificates \
|
|
26
|
+
wget \
|
|
27
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
2
28
|
|
|
3
29
|
WORKDIR /app
|
|
4
30
|
|
|
5
|
-
|
|
6
|
-
|
|
31
|
+
# Copy compiled binary from builder
|
|
32
|
+
COPY --from=builder /build/agent-swarm-api /usr/local/bin/agent-swarm-api
|
|
33
|
+
RUN chmod +x /usr/local/bin/agent-swarm-api
|
|
7
34
|
|
|
8
|
-
|
|
35
|
+
# Copy package.json for version info
|
|
36
|
+
COPY package.json ./
|
|
37
|
+
|
|
38
|
+
# Database will be created in /app (mount /app for persistence)
|
|
39
|
+
VOLUME /app
|
|
9
40
|
|
|
10
41
|
ENV PORT=3013
|
|
42
|
+
|
|
11
43
|
EXPOSE 3013
|
|
12
44
|
|
|
45
|
+
# Health check
|
|
13
46
|
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
|
14
47
|
CMD wget -qO- http://localhost:3013/health || exit 1
|
|
15
48
|
|
|
16
|
-
|
|
49
|
+
# Print version on startup and run the server
|
|
50
|
+
CMD echo "=== Agent Swarm API v$(cat /app/package.json | grep '\"version\"' | cut -d'"' -f4) ===" && \
|
|
51
|
+
echo "Port: $PORT" && \
|
|
52
|
+
echo "Database: /app/agent-swarm-db.sqlite" && \
|
|
53
|
+
echo "==============================" && \
|
|
54
|
+
exec /usr/local/bin/agent-swarm-api
|
package/Dockerfile.worker
CHANGED
|
@@ -118,7 +118,7 @@ WORKDIR /workspace
|
|
|
118
118
|
VOLUME ["/logs"]
|
|
119
119
|
|
|
120
120
|
RUN mkdir -p ./personal ./shared
|
|
121
|
-
VOLUME ["/workspace/personal" "/workspace/shared"]
|
|
121
|
+
VOLUME ["/workspace/personal", "/workspace/shared"]
|
|
122
122
|
|
|
123
123
|
# Expose service port for PM2 processes
|
|
124
124
|
EXPOSE 3000
|
package/README.md
CHANGED
|
@@ -26,6 +26,21 @@ Agent Swarm MCP enables multi-agent coordination for AI coding assistants. It pr
|
|
|
26
26
|
- **Service Discovery** - Register and discover background services
|
|
27
27
|
- **Docker Workers** - Run isolated Claude workers in containers
|
|
28
28
|
- **Lead/Worker Pattern** - Coordinate work with a lead agent and multiple workers
|
|
29
|
+
- **Dashboard UI** - Real-time monitoring dashboard for agents, tasks, and channels
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
## Dashboard UI
|
|
34
|
+
|
|
35
|
+
A React-based monitoring dashboard is available in the `ui/` directory. See [UI.md](./UI.md) for details.
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd ui
|
|
39
|
+
pnpm install
|
|
40
|
+
pnpm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The dashboard runs at `http://localhost:5173` by default.
|
|
29
44
|
|
|
30
45
|
---
|
|
31
46
|
|
|
@@ -166,6 +181,7 @@ Full deployment options are documented in [DEPLOYMENT.md](./DEPLOYMENT.md).
|
|
|
166
181
|
|
|
167
182
|
| Document | Description |
|
|
168
183
|
|----------|-------------|
|
|
184
|
+
| [UI.md](./UI.md) | Dashboard UI overview |
|
|
169
185
|
| [DEPLOYMENT.md](./DEPLOYMENT.md) | Docker, Docker Compose, systemd deployment |
|
|
170
186
|
| [CONTRIBUTING.md](./CONTRIBUTING.md) | Development setup, code quality, project structure |
|
|
171
187
|
| [MCP.md](./MCP.md) | MCP tools reference (auto-generated) |
|
package/UI.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Dashboard UI
|
|
2
|
+
|
|
3
|
+
A React-based monitoring dashboard for Agent Swarm.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd ui
|
|
9
|
+
pnpm install
|
|
10
|
+
pnpm run dev
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The dashboard runs at `http://localhost:5173`. Make sure the API server is running (`bun run start:http` on port 3013).
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Real-time agent and task monitoring
|
|
18
|
+
- Channel-based messaging with threads
|
|
19
|
+
- Service registry and health status
|
|
20
|
+
- Dark/light theme with honeycomb aesthetic
|
|
21
|
+
- Responsive design (desktop and mobile)
|
|
22
|
+
- URL-based state for shareable links
|
|
23
|
+
|
|
24
|
+
## Tabs
|
|
25
|
+
|
|
26
|
+
| Tab | Description |
|
|
27
|
+
|-----|-------------|
|
|
28
|
+
| **Agents** | View agents, their status (idle/busy/offline), and assigned tasks |
|
|
29
|
+
| **Tasks** | Browse and filter tasks by status, agent, or search query |
|
|
30
|
+
| **Chat** | Channel messaging between agents with thread support |
|
|
31
|
+
| **Services** | Monitor registered background services and health checks |
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Click the settings icon in the header to configure:
|
|
36
|
+
|
|
37
|
+
- **API URL** - The Agent Swarm API endpoint (default: `http://localhost:3013`)
|
|
38
|
+
- **API Key** - Optional authentication key (matches your `API_KEY` env var)
|
|
39
|
+
|
|
40
|
+
Settings are stored in localStorage.
|
package/deploy/prod-db.ts
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
import { $ } from "bun";
|
|
4
4
|
import * as readline from "node:readline";
|
|
5
5
|
|
|
6
|
-
const DB_PATH = "/
|
|
7
|
-
const SSH_HOST = process.argv[2] || "
|
|
6
|
+
const DB_PATH = "/var/lib/docker/volumes/agent-swarm-nrz8v0_swarm_api/_data/agent-swarm-db.sqlite";
|
|
7
|
+
const SSH_HOST = process.argv[2] || "swarm";
|
|
8
8
|
|
|
9
9
|
console.log(`Connected to ${SSH_HOST}:${DB_PATH}`);
|
|
10
10
|
console.log("Type SQL queries or .tables, .schema, etc. Ctrl+C to exit.\n");
|
|
@@ -10,16 +10,14 @@
|
|
|
10
10
|
|
|
11
11
|
services:
|
|
12
12
|
api:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
dockerfile: Dockerfile
|
|
13
|
+
image: "ghcr.io/desplega-ai/agent-swarm:latest"
|
|
14
|
+
pull_policy: always
|
|
16
15
|
|
|
17
16
|
environment:
|
|
18
|
-
- ENV=${ENV:-development}
|
|
19
17
|
- API_KEY=${API_KEY}
|
|
20
18
|
|
|
21
|
-
- MCP_BASE_URL=${MCP_BASE_URL
|
|
22
|
-
- APP_URL=${APP_URL
|
|
19
|
+
- MCP_BASE_URL=${MCP_BASE_URL}
|
|
20
|
+
- APP_URL=${APP_URL}
|
|
23
21
|
|
|
24
22
|
# Optional: Enable Slack integration
|
|
25
23
|
- SLACK_DISABLE=${SLACK_DISABLE:-false}
|
|
@@ -30,97 +28,102 @@ services:
|
|
|
30
28
|
- "3013:3013"
|
|
31
29
|
|
|
32
30
|
volumes:
|
|
33
|
-
# For the sqlite database
|
|
34
31
|
- swarm_api:/app
|
|
35
32
|
|
|
36
33
|
restart: unless-stopped
|
|
37
34
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
dockerfile: Dockerfile.worker
|
|
35
|
+
lead:
|
|
36
|
+
image: "ghcr.io/desplega-ai/agent-swarm-worker:latest"
|
|
37
|
+
pull_policy: always
|
|
42
38
|
|
|
43
39
|
environment:
|
|
44
40
|
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
|
|
45
41
|
- API_KEY=${API_KEY}
|
|
46
|
-
|
|
47
|
-
-
|
|
42
|
+
# Replace by a valid UUID
|
|
43
|
+
- AGENT_ID=d454d1a5-4df9-49bd-8a89-e58d6a657dc3
|
|
44
|
+
|
|
45
|
+
# Important: Lead agent role
|
|
46
|
+
- AGENT_ROLE=lead
|
|
48
47
|
|
|
49
|
-
- MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
48
|
+
# - MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
49
|
+
# If in the same Docker network, use the service name
|
|
50
|
+
- MCP_BASE_URL=http://api:3013
|
|
50
51
|
|
|
51
|
-
|
|
52
|
-
- SESSION_ID=${SESSION_ID:-}
|
|
53
|
-
- YOLO=${YOLO:-false}
|
|
54
|
-
# GitHub credentials for git push and PR operations
|
|
52
|
+
- YOLO=true
|
|
55
53
|
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
|
56
54
|
- GITHUB_EMAIL=${GITHUB_EMAIL:-}
|
|
57
55
|
- GITHUB_NAME=${GITHUB_NAME:-}
|
|
58
|
-
# Service registry URL (for service discovery)
|
|
59
56
|
- SWARM_URL=${SWARM_URL:-localhost}
|
|
60
57
|
|
|
61
58
|
ports:
|
|
62
|
-
|
|
63
|
-
- "${SERVICE_PORT:-3001}:3000"
|
|
59
|
+
- "${SERVICE_PORT:-3020}:3000"
|
|
64
60
|
|
|
65
61
|
volumes:
|
|
66
62
|
- swarm_logs:/logs
|
|
67
|
-
# Optional: mount a workspace directory for persistent work
|
|
68
63
|
- swarm_shared:/workspace/shared
|
|
69
|
-
-
|
|
64
|
+
- swarm_lead:/workspace/personal
|
|
70
65
|
|
|
71
66
|
restart: unless-stopped
|
|
72
67
|
|
|
73
|
-
worker-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
dockerfile: Dockerfile.worker
|
|
68
|
+
worker-1:
|
|
69
|
+
image: "ghcr.io/desplega-ai/agent-swarm-worker:latest"
|
|
70
|
+
pull_policy: always
|
|
77
71
|
|
|
78
72
|
environment:
|
|
79
73
|
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
|
|
80
74
|
- API_KEY=${API_KEY}
|
|
81
|
-
|
|
75
|
+
# Replace by a valid UUID
|
|
76
|
+
- AGENT_ID=38d36438-58a0-45b5-8602-a5d52b07c2f1
|
|
82
77
|
- AGENT_ROLE=worker
|
|
83
78
|
|
|
84
|
-
- MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
79
|
+
# - MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
80
|
+
# If in the same Docker network, use the service name
|
|
81
|
+
- MCP_BASE_URL=http://api:3013
|
|
85
82
|
|
|
86
|
-
|
|
83
|
+
- YOLO=true
|
|
84
|
+
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
|
85
|
+
- GITHUB_EMAIL=${GITHUB_EMAIL:-}
|
|
86
|
+
- GITHUB_NAME=${GITHUB_NAME:-}
|
|
87
|
+
- SWARM_URL=${SWARM_URL:-localhost}
|
|
87
88
|
|
|
88
89
|
ports:
|
|
89
|
-
|
|
90
|
-
- "${SERVICE_PORT:-3002}:3000"
|
|
90
|
+
- "${SERVICE_PORT:-3021}:3000"
|
|
91
91
|
|
|
92
92
|
volumes:
|
|
93
93
|
- swarm_logs:/logs
|
|
94
94
|
- swarm_shared:/workspace/shared
|
|
95
|
-
-
|
|
95
|
+
- swarm_worker_1:/workspace/personal
|
|
96
96
|
|
|
97
97
|
restart: unless-stopped
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
dockerfile: Dockerfile.worker
|
|
99
|
+
worker-2:
|
|
100
|
+
image: "ghcr.io/desplega-ai/agent-swarm-worker:latest"
|
|
101
|
+
pull_policy: always
|
|
103
102
|
|
|
104
103
|
environment:
|
|
105
104
|
- CLAUDE_CODE_OAUTH_TOKEN=${CLAUDE_CODE_OAUTH_TOKEN}
|
|
106
105
|
- API_KEY=${API_KEY}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
- AGENT_ROLE=lead
|
|
106
|
+
# Replace by a valid UUID
|
|
107
|
+
- AGENT_ID=c1e2f3a4-5678-90ab-cdef-1234567890ab
|
|
108
|
+
- AGENT_ROLE=worker
|
|
111
109
|
|
|
112
|
-
- MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
110
|
+
# - MCP_BASE_URL=${MCP_BASE_URL:-http://api:3013}
|
|
111
|
+
# If in the same Docker network, use the service name
|
|
112
|
+
- MCP_BASE_URL=http://api:3013
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
- YOLO=true
|
|
115
|
+
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
|
116
|
+
- GITHUB_EMAIL=${GITHUB_EMAIL:-}
|
|
117
|
+
- GITHUB_NAME=${GITHUB_NAME:-}
|
|
118
|
+
- SWARM_URL=${SWARM_URL:-localhost}
|
|
115
119
|
|
|
116
120
|
ports:
|
|
117
|
-
|
|
118
|
-
- "${SERVICE_PORT:-3003}:3000"
|
|
121
|
+
- "${SERVICE_PORT:-3022}:3000"
|
|
119
122
|
|
|
120
123
|
volumes:
|
|
121
124
|
- swarm_logs:/logs
|
|
122
125
|
- swarm_shared:/workspace/shared
|
|
123
|
-
-
|
|
126
|
+
- swarm_worker_2:/workspace/personal
|
|
124
127
|
|
|
125
128
|
restart: unless-stopped
|
|
126
129
|
|
|
@@ -131,7 +134,3 @@ volumes:
|
|
|
131
134
|
swarm_lead:
|
|
132
135
|
swarm_worker_1:
|
|
133
136
|
swarm_worker_2:
|
|
134
|
-
|
|
135
|
-
networks:
|
|
136
|
-
default:
|
|
137
|
-
driver: bridge
|
package/docker-entrypoint.sh
CHANGED
|
@@ -16,6 +16,9 @@ fi
|
|
|
16
16
|
ROLE="${AGENT_ROLE:-worker}"
|
|
17
17
|
MCP_URL="${MCP_BASE_URL:-http://host.docker.internal:3013}"
|
|
18
18
|
|
|
19
|
+
# Get version from compiled binary (extract just the version number)
|
|
20
|
+
VERSION=$(/usr/local/bin/agent-swarm version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || echo "unknown")
|
|
21
|
+
|
|
19
22
|
# Determine YOLO mode based on role
|
|
20
23
|
if [ "$ROLE" = "lead" ]; then
|
|
21
24
|
YOLO_MODE="${LEAD_YOLO:-false}"
|
|
@@ -23,7 +26,7 @@ else
|
|
|
23
26
|
YOLO_MODE="${WORKER_YOLO:-false}"
|
|
24
27
|
fi
|
|
25
28
|
|
|
26
|
-
echo "=== Agent Swarm ${ROLE^} ==="
|
|
29
|
+
echo "=== Agent Swarm ${ROLE^} v${VERSION} ==="
|
|
27
30
|
echo "Agent ID: ${AGENT_ID:-<not set>}"
|
|
28
31
|
echo "MCP Base URL: $MCP_URL"
|
|
29
32
|
echo "YOLO Mode: $YOLO_MODE"
|
package/package.json
CHANGED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Review a task that has been offered to you and decide whether to accept or reject it
|
|
3
|
+
argument-hint: [taskId]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Review Offered Task
|
|
7
|
+
|
|
8
|
+
You have been offered a task. Your job is to review it and decide whether to accept or reject it based on your capabilities and current workload.
|
|
9
|
+
|
|
10
|
+
## Workflow
|
|
11
|
+
|
|
12
|
+
1. **Get task details**: Call the `get-task-details` tool with the provided `taskId` to understand what the task involves.
|
|
13
|
+
|
|
14
|
+
2. **Evaluate the task**: Consider:
|
|
15
|
+
- Does this task match your capabilities?
|
|
16
|
+
- Do you have the necessary context or access to complete it?
|
|
17
|
+
- Is the task description clear enough to proceed?
|
|
18
|
+
|
|
19
|
+
3. **Make a decision**:
|
|
20
|
+
- **Accept**: If you can complete this task, call `task-action` with `action: "accept"` and `taskId: "<taskId>"`. Then immediately use `/work-on-task <taskId>` to start working on it.
|
|
21
|
+
- **Reject**: If you cannot complete this task, call `task-action` with `action: "reject"`, `taskId: "<taskId>"`, and provide a `reason` explaining why you're rejecting it (e.g., "Task requires Python expertise which I don't have", "Task description is too vague").
|
|
22
|
+
|
|
23
|
+
## Example Accept Flow
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
1. get-task-details taskId="abc-123"
|
|
27
|
+
2. [Review the task details]
|
|
28
|
+
3. task-action action="accept" taskId="abc-123"
|
|
29
|
+
4. /work-on-task abc-123
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Example Reject Flow
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
1. get-task-details taskId="abc-123"
|
|
36
|
+
2. [Review the task details]
|
|
37
|
+
3. task-action action="reject" taskId="abc-123" reason="Task requires access to production database which I don't have"
|
|
38
|
+
4. Reply "DONE" to end the session
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Important Notes
|
|
42
|
+
|
|
43
|
+
- Always provide a clear reason when rejecting a task - this helps the lead agent reassign it appropriately
|
|
44
|
+
- If you accept, you must immediately start working on the task using `/work-on-task`
|
|
45
|
+
- If you reject, the task returns to the unassigned pool for reassignment
|
|
@@ -89,13 +89,15 @@ When you assign tasks to workers, they might need to let them know to use some o
|
|
|
89
89
|
- `/create-plan` - Useful command for workers to create a detailed plan for how they will approach and complete the task. Will store in the shared filesystem automatically, no need to tell them to do it.
|
|
90
90
|
- `/implement-plan` - Useful command for workers to implement the plan they created for the task. It can be used to continue working on the implementation too (not just start it). Will store in the shared filesystem automatically, no need to tell them to do it.
|
|
91
91
|
|
|
92
|
-
## Communication Etiquette
|
|
93
|
-
|
|
94
|
-
- You should ALWAYS follow-up to the user messages using the `/swarm-chat` command. You should also use it to communicate with workers when needed.
|
|
95
|
-
- If you already provided an update to the user and nothing happened in the swarm, you should NOT spam the user with repeated updates. Only provide updates when something relevant happens.
|
|
96
|
-
|
|
97
92
|
## Filesystem
|
|
98
93
|
|
|
99
94
|
You will have your own persisted directory at `/workspace/personal`. Use it to store any files you need to keep between sessions.
|
|
100
95
|
|
|
101
96
|
If you want to share files with workers, use the shared `/workspace/shared` directory, which all agents in the swarm can access. The same way, workers can share files with you there. Take this into account when assigning tasks that require file access, or that you want check later, or pass to other workers.
|
|
97
|
+
|
|
98
|
+
## Communication Etiquette
|
|
99
|
+
|
|
100
|
+
- ONLY follow-up if there are relevant updates (check history to avoid spamming), or if stated by the user (human). If not, avoid unnecessary messages.
|
|
101
|
+
- When communicating, ALWAYS use the `/swarm-chat` command. You may also use it to communicate with workers when needed, but that should be rare.
|
|
102
|
+
- If you already provided an update to the user and nothing happened in the swarm, you should NOT SPAM the user with repeated updates (e.g. do not send messages like "Ready to lead"). Only provide meaningful updates when something relevant happens.
|
|
103
|
+
|
|
@@ -54,3 +54,8 @@ To do so, use the `agent-swarm` MCP server and call the `join-swarm` with a name
|
|
|
54
54
|
You will have your own persisted directory at `/workspace/personal`. Use it to store any files you need to keep between sessions.
|
|
55
55
|
|
|
56
56
|
If you want to share files with workers and the lead, use the shared `/workspace/shared` directory, which all agents in the swarm can access. Make sure to use it if the task requires sharing files.
|
|
57
|
+
|
|
58
|
+
## Communication Etiquette
|
|
59
|
+
|
|
60
|
+
- ONLY follow-up if clearly stated by the user or the lead. Do NOT send random updates about your status unless explicitly requested.
|
|
61
|
+
- When communicating, ALWAYS use the `/swarm-chat` command.
|
package/src/be/db.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type {
|
|
|
13
13
|
ChannelType,
|
|
14
14
|
Service,
|
|
15
15
|
ServiceStatus,
|
|
16
|
+
SessionLog,
|
|
16
17
|
} from "../types";
|
|
17
18
|
|
|
18
19
|
let db: Database | null = null;
|
|
@@ -149,6 +150,21 @@ export function initDb(dbPath = "./agent-swarm-db.sqlite"): Database {
|
|
|
149
150
|
|
|
150
151
|
CREATE INDEX IF NOT EXISTS idx_services_agentId ON services(agentId);
|
|
151
152
|
CREATE INDEX IF NOT EXISTS idx_services_status ON services(status);
|
|
153
|
+
|
|
154
|
+
-- Session logs table (raw CLI output from runner)
|
|
155
|
+
CREATE TABLE IF NOT EXISTS session_logs (
|
|
156
|
+
id TEXT PRIMARY KEY,
|
|
157
|
+
taskId TEXT,
|
|
158
|
+
sessionId TEXT NOT NULL,
|
|
159
|
+
iteration INTEGER NOT NULL,
|
|
160
|
+
cli TEXT NOT NULL DEFAULT 'claude',
|
|
161
|
+
content TEXT NOT NULL,
|
|
162
|
+
lineNumber INTEGER NOT NULL,
|
|
163
|
+
createdAt TEXT NOT NULL
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
CREATE INDEX IF NOT EXISTS idx_session_logs_taskId ON session_logs(taskId);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_session_logs_sessionId ON session_logs(sessionId);
|
|
152
168
|
`);
|
|
153
169
|
|
|
154
170
|
// Seed default general channel if it doesn't exist
|
|
@@ -1919,3 +1935,88 @@ export function deleteServicesByAgentId(agentId: string): number {
|
|
|
1919
1935
|
const result = getDb().run("DELETE FROM services WHERE agentId = ?", [agentId]);
|
|
1920
1936
|
return result.changes;
|
|
1921
1937
|
}
|
|
1938
|
+
|
|
1939
|
+
// ============================================================================
|
|
1940
|
+
// Session Log Operations (raw CLI output)
|
|
1941
|
+
// ============================================================================
|
|
1942
|
+
|
|
1943
|
+
type SessionLogRow = {
|
|
1944
|
+
id: string;
|
|
1945
|
+
taskId: string | null;
|
|
1946
|
+
sessionId: string;
|
|
1947
|
+
iteration: number;
|
|
1948
|
+
cli: string;
|
|
1949
|
+
content: string;
|
|
1950
|
+
lineNumber: number;
|
|
1951
|
+
createdAt: string;
|
|
1952
|
+
};
|
|
1953
|
+
|
|
1954
|
+
function rowToSessionLog(row: SessionLogRow): SessionLog {
|
|
1955
|
+
return {
|
|
1956
|
+
id: row.id,
|
|
1957
|
+
taskId: row.taskId ?? undefined,
|
|
1958
|
+
sessionId: row.sessionId,
|
|
1959
|
+
iteration: row.iteration,
|
|
1960
|
+
cli: row.cli,
|
|
1961
|
+
content: row.content,
|
|
1962
|
+
lineNumber: row.lineNumber,
|
|
1963
|
+
createdAt: row.createdAt,
|
|
1964
|
+
};
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
export const sessionLogQueries = {
|
|
1968
|
+
insert: () =>
|
|
1969
|
+
getDb().prepare<SessionLogRow, [string, string | null, string, number, string, string, number]>(
|
|
1970
|
+
`INSERT INTO session_logs (id, taskId, sessionId, iteration, cli, content, lineNumber, createdAt)
|
|
1971
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now')) RETURNING *`,
|
|
1972
|
+
),
|
|
1973
|
+
|
|
1974
|
+
insertBatch: () =>
|
|
1975
|
+
getDb().prepare<null, [string, string | null, string, number, string, string, number]>(
|
|
1976
|
+
`INSERT INTO session_logs (id, taskId, sessionId, iteration, cli, content, lineNumber, createdAt)
|
|
1977
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))`,
|
|
1978
|
+
),
|
|
1979
|
+
|
|
1980
|
+
getByTaskId: () =>
|
|
1981
|
+
getDb().prepare<SessionLogRow, [string]>(
|
|
1982
|
+
"SELECT * FROM session_logs WHERE taskId = ? ORDER BY iteration ASC, lineNumber ASC",
|
|
1983
|
+
),
|
|
1984
|
+
|
|
1985
|
+
getBySessionId: () =>
|
|
1986
|
+
getDb().prepare<SessionLogRow, [string, number]>(
|
|
1987
|
+
"SELECT * FROM session_logs WHERE sessionId = ? AND iteration = ? ORDER BY lineNumber ASC",
|
|
1988
|
+
),
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
export function createSessionLogs(logs: {
|
|
1992
|
+
taskId?: string;
|
|
1993
|
+
sessionId: string;
|
|
1994
|
+
iteration: number;
|
|
1995
|
+
cli: string;
|
|
1996
|
+
lines: string[];
|
|
1997
|
+
}): void {
|
|
1998
|
+
const stmt = sessionLogQueries.insertBatch();
|
|
1999
|
+
getDb().transaction(() => {
|
|
2000
|
+
for (let i = 0; i < logs.lines.length; i++) {
|
|
2001
|
+
const line = logs.lines[i];
|
|
2002
|
+
if (line === undefined) continue;
|
|
2003
|
+
stmt.run(
|
|
2004
|
+
crypto.randomUUID(),
|
|
2005
|
+
logs.taskId ?? null,
|
|
2006
|
+
logs.sessionId,
|
|
2007
|
+
logs.iteration,
|
|
2008
|
+
logs.cli,
|
|
2009
|
+
line,
|
|
2010
|
+
i,
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
})();
|
|
2014
|
+
}
|
|
2015
|
+
|
|
2016
|
+
export function getSessionLogsByTaskId(taskId: string): SessionLog[] {
|
|
2017
|
+
return sessionLogQueries.getByTaskId().all(taskId).map(rowToSessionLog);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
export function getSessionLogsBySession(sessionId: string, iteration: number): SessionLog[] {
|
|
2021
|
+
return sessionLogQueries.getBySessionId().all(sessionId, iteration).map(rowToSessionLog);
|
|
2022
|
+
}
|