978 lines
24 KiB
Markdown
978 lines
24 KiB
Markdown
# Kandev Agent Protocol
|
|
|
|
> **For Agent Developers**: This document explains how to build agents that integrate with Kandev using the Agent Communication Protocol (ACP).
|
|
>
|
|
> **For System Architecture**: See [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
> **For WebSocket API**: See [docs/asyncapi.yaml](docs/asyncapi.yaml)
|
|
|
|
## Overview
|
|
|
|
Kandev uses a **WebSocket-only architecture** for all client-backend communication. Agents run in Docker containers with **agentctl** as a sidecar process. The agent communicates via **ACP (Agent Communication Protocol)** - a JSON-RPC 2.0 protocol over stdin/stdout. ACP messages are streamed to clients via WebSocket notifications.
|
|
|
|
```
|
|
Client (WS) Backend (kandev) agentctl (HTTP) Agent Process
|
|
│ │ │ │
|
|
│ orchestrator.start │ │ │
|
|
├────────────────────────►│ POST /api/v1/start │ │
|
|
│ ├────────────────────────────►│ spawn auggie --acp │
|
|
│ │ ├─────────────────────────►│
|
|
│ │ POST /api/v1/acp/call │ initialize │
|
|
│ ├────────────────────────────►├─────────────────────────►│
|
|
│ │◄────────────────────────────┤◄─────────────────────────┤
|
|
│ acp.progress │ WS /api/v1/acp/stream │ session/update │
|
|
│◄────────────────────────┤◄────────────────────────────┤◄─────────────────────────┤
|
|
│ │ │ │
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### WebSocket Gateway
|
|
|
|
All client communication uses a single WebSocket endpoint at `ws://localhost:8080/ws`. The backend:
|
|
|
|
1. **Receives orchestrator requests** (`orchestrator.start`, `orchestrator.stop`, `orchestrator.prompt`)
|
|
2. **Manages agent containers** via Docker and agentctl HTTP API
|
|
3. **Streams ACP messages** to subscribed clients as WebSocket notifications
|
|
4. **Publishes events** via internal event bus for real-time updates
|
|
|
|
### agentctl
|
|
|
|
`agentctl` is an HTTP server that runs inside each agent container as a sidecar process. It:
|
|
|
|
1. **Manages the agent subprocess** (e.g., `auggie --acp`)
|
|
2. **Provides HTTP API** for control operations (start, stop, status)
|
|
3. **Relays ACP messages** between the backend and agent's stdin/stdout
|
|
4. **Streams output** via WebSocket for real-time monitoring
|
|
|
|
### Communication Flow
|
|
|
|
1. Client sends `orchestrator.start` via WebSocket
|
|
2. Backend creates container with agentctl as entrypoint
|
|
3. agentctl starts HTTP server on port 9999
|
|
4. Backend waits for agentctl to be ready (`GET /health`)
|
|
5. Backend starts agent process (`POST /api/v1/start`)
|
|
6. Backend connects to agentctl WebSocket (`GET /api/v1/acp/stream`)
|
|
7. Backend sends ACP messages via HTTP (`POST /api/v1/acp/call`)
|
|
8. ACP updates are streamed to subscribed clients as `acp.*` notifications
|
|
|
|
### agentctl HTTP API (Internal)
|
|
|
|
These endpoints are used by the backend to communicate with agent containers:
|
|
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/health` | GET | Health check |
|
|
| `/api/v1/status` | GET | Get agent status |
|
|
| `/api/v1/start` | POST | Start agent process |
|
|
| `/api/v1/stop` | POST | Stop agent process |
|
|
| `/api/v1/acp/send` | POST | Send ACP message (fire and forget) |
|
|
| `/api/v1/acp/call` | POST | Send ACP request, wait for response |
|
|
| `/api/v1/acp/stream` | GET | WebSocket for ACP message streaming |
|
|
| `/api/v1/output` | GET | Get buffered output |
|
|
| `/api/v1/output/stream` | GET | WebSocket for real-time output |
|
|
|
|
### Environment Variables
|
|
|
|
| Variable | Default | Description |
|
|
|----------|---------|-------------|
|
|
| `AGENTCTL_PORT` | 9999 | HTTP server port |
|
|
| `AGENTCTL_AGENT_COMMAND` | `auggie --acp` | Command to run agent |
|
|
| `AGENTCTL_AUTO_START` | false | Auto-start agent on startup |
|
|
| `AGENTCTL_LOG_LEVEL` | info | Logging level |
|
|
|
|
---
|
|
|
|
## Agent Communication Protocol (ACP)
|
|
|
|
### Protocol Basics
|
|
|
|
- **Transport**: JSON-RPC 2.0 over stdin/stdout
|
|
- **Format**: Newline-delimited JSON
|
|
- **Direction**: Bidirectional (request/response + notifications)
|
|
|
|
### Message Types
|
|
|
|
**Backend → Agent (Requests):**
|
|
- `initialize` - Establish connection and capabilities
|
|
- `session/new` - Create new session
|
|
- `session/load` - Resume existing session
|
|
- `session/prompt` - Send task/prompt to execute
|
|
- `session/cancel` - Cancel current operation
|
|
|
|
**Agent → Backend (Notifications):**
|
|
- `session/update` - Progress updates, content, tool calls, completion
|
|
|
|
---
|
|
|
|
## Message Specifications
|
|
|
|
### 1. Initialize Connection
|
|
|
|
**Backend → Agent:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"method": "initialize",
|
|
"params": {
|
|
"protocolVersion": "1.0",
|
|
"clientInfo": {
|
|
"name": "kandev",
|
|
"version": "0.1.0"
|
|
},
|
|
"capabilities": {
|
|
"streaming": true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Agent → Backend:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 1,
|
|
"result": {
|
|
"protocolVersion": "1.0",
|
|
"serverInfo": {
|
|
"name": "augment-agent",
|
|
"version": "1.0.0"
|
|
},
|
|
"capabilities": {
|
|
"streaming": true,
|
|
"sessionResumption": true
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 2. Create New Session
|
|
|
|
**Backend → Agent:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 2,
|
|
"method": "session/new",
|
|
"params": {}
|
|
}
|
|
```
|
|
|
|
**Agent → Backend:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 2,
|
|
"result": {
|
|
"sessionId": "abc123def456"
|
|
}
|
|
}
|
|
```
|
|
|
|
### 3. Resume Existing Session
|
|
|
|
**Backend → Agent:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 3,
|
|
"method": "session/load",
|
|
"params": {
|
|
"sessionId": "abc123def456"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Agent → Backend:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 3,
|
|
"result": {
|
|
"sessionId": "abc123def456",
|
|
"loaded": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### 4. Send Prompt
|
|
|
|
**Backend → Agent:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 4,
|
|
"method": "session/prompt",
|
|
"params": {
|
|
"message": "Fix the null pointer bug in the login function"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Agent → Backend (immediate response):**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 4,
|
|
"result": {
|
|
"accepted": true
|
|
}
|
|
}
|
|
```
|
|
|
|
### 5. Progress Updates (Notifications)
|
|
|
|
**Content Update:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "session/update",
|
|
"params": {
|
|
"type": "content",
|
|
"data": {
|
|
"text": "I'm analyzing the code in auth/login.go..."
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Tool Call:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "session/update",
|
|
"params": {
|
|
"type": "toolCall",
|
|
"data": {
|
|
"toolName": "file_edit",
|
|
"status": "running",
|
|
"args": {
|
|
"file": "auth/login.go",
|
|
"line": 42
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Completion:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "session/update",
|
|
"params": {
|
|
"type": "complete",
|
|
"data": {
|
|
"sessionId": "abc123def456",
|
|
"success": true,
|
|
"summary": "Fixed null pointer by adding nil check"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
**Error:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "session/update",
|
|
"params": {
|
|
"type": "error",
|
|
"data": {
|
|
"message": "Failed to compile after changes",
|
|
"details": "syntax error on line 45"
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### 6. Cancel Operation
|
|
|
|
**Backend → Agent (notification, no response expected):**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"method": "session/cancel",
|
|
"params": {
|
|
"reason": "User requested cancellation"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Available Agent Types
|
|
|
|
### Augment Agent (`augment-agent`)
|
|
|
|
Primary AI coding agent powered by Auggie CLI with agentctl sidecar.
|
|
|
|
**Docker Image**: `kandev/augment-agent:latest`
|
|
|
|
**Architecture:**
|
|
```
|
|
Container
|
|
├── agentctl (HTTP server on port 9999)
|
|
│ └── manages → auggie --acp (subprocess)
|
|
└── /workspace (mounted project directory)
|
|
```
|
|
|
|
**Specification:**
|
|
- **Working Directory**: `/workspace`
|
|
- **Memory**: 4096 MB
|
|
- **CPU**: 2.0 cores
|
|
- **Timeout**: 1 hour
|
|
- **Exposed Port**: 9999 (agentctl HTTP API)
|
|
- **Capabilities**: code_generation, code_review, refactoring, testing, shell_execution
|
|
|
|
**Required Environment Variables:**
|
|
```bash
|
|
AUGMENT_SESSION_AUTH # From ~/.augment/session.json
|
|
TASK_DESCRIPTION # The task to execute
|
|
```
|
|
|
|
**Optional Environment Variables:**
|
|
```bash
|
|
AUGGIE_SESSION_ID # Resume previous session
|
|
AGENTCTL_AUTO_START # Auto-start agent on container start
|
|
```
|
|
|
|
**Mounted Volumes:**
|
|
- `{workspace}` → `/workspace` - Project directory
|
|
- `{augment_sessions}` → `/root/.augment/sessions` - Session persistence
|
|
|
|
**Dockerfile**: `apps/backend/dockerfiles/augment-agent/Dockerfile`
|
|
|
|
**Building the Image:**
|
|
```bash
|
|
cd apps/backend
|
|
docker build -t kandev/augment-agent:latest -f dockerfiles/augment-agent/Dockerfile .
|
|
```
|
|
|
|
---
|
|
|
|
## Creating Custom Agents
|
|
|
|
Custom agents can be created in two ways:
|
|
|
|
### Option A: With agentctl (Recommended)
|
|
|
|
Use the multi-stage Dockerfile pattern to include agentctl:
|
|
|
|
```dockerfile
|
|
# Stage 1: Build agentctl
|
|
FROM golang:1.23-alpine AS builder
|
|
WORKDIR /build
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN CGO_ENABLED=0 go build -o agentctl ./cmd/agentctl
|
|
|
|
# Stage 2: Final image
|
|
FROM ubuntu:22.04
|
|
|
|
# Install your agent dependencies
|
|
RUN apt-get update && apt-get install -y curl jq git && rm -rf /var/lib/apt/lists/*
|
|
|
|
# Copy agentctl from builder
|
|
COPY --from=builder /build/agentctl /usr/local/bin/agentctl
|
|
|
|
# Copy your agent script
|
|
COPY agent.sh /usr/local/bin/agent
|
|
RUN chmod +x /usr/local/bin/agent
|
|
|
|
WORKDIR /workspace
|
|
EXPOSE 9999
|
|
|
|
# Set agentctl as entrypoint
|
|
ENV AGENTCTL_AGENT_COMMAND="/usr/local/bin/agent --acp"
|
|
ENTRYPOINT ["/usr/local/bin/agentctl"]
|
|
```
|
|
|
|
### Option B: Standalone ACP Agent
|
|
|
|
For simpler agents that don't need HTTP control:
|
|
|
|
```dockerfile
|
|
FROM ubuntu:22.04
|
|
|
|
RUN apt-get update && apt-get install -y curl jq git && rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY agent.sh /usr/local/bin/agent
|
|
RUN chmod +x /usr/local/bin/agent
|
|
|
|
WORKDIR /workspace
|
|
ENTRYPOINT ["/usr/local/bin/agent", "--acp"]
|
|
```
|
|
|
|
### Implementing ACP Protocol
|
|
|
|
Create `agent.sh` that implements JSON-RPC 2.0:
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
# ACP mode - JSON-RPC 2.0
|
|
if [ "$1" = "--acp" ]; then
|
|
# Read requests from stdin
|
|
while IFS= read -r line; do
|
|
METHOD=$(echo "$line" | jq -r '.method')
|
|
ID=$(echo "$line" | jq -r '.id')
|
|
|
|
case "$METHOD" in
|
|
"initialize")
|
|
echo '{"jsonrpc":"2.0","id":'$ID',"result":{"protocolVersion":"1.0","serverInfo":{"name":"your-agent","version":"1.0.0"},"capabilities":{"streaming":true}}}'
|
|
;;
|
|
|
|
"session/new")
|
|
SESSION_ID=$(uuidgen)
|
|
echo '{"jsonrpc":"2.0","id":'$ID',"result":{"sessionId":"'$SESSION_ID'"}}'
|
|
;;
|
|
|
|
"session/prompt")
|
|
MESSAGE=$(echo "$line" | jq -r '.params.message')
|
|
|
|
# Accept prompt
|
|
echo '{"jsonrpc":"2.0","id":'$ID',"result":{"accepted":true}}'
|
|
|
|
# Send progress
|
|
echo '{"jsonrpc":"2.0","method":"session/update","params":{"type":"content","data":{"text":"Processing: '$MESSAGE'"}}}'
|
|
|
|
# Do work here
|
|
sleep 2
|
|
|
|
# Send completion
|
|
echo '{"jsonrpc":"2.0","method":"session/update","params":{"type":"complete","data":{"sessionId":"'$SESSION_ID'","success":true}}}'
|
|
;;
|
|
|
|
"session/cancel")
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
fi
|
|
```
|
|
|
|
### Register Agent Type
|
|
|
|
Add to `apps/backend/internal/agent/registry/defaults.go`:
|
|
|
|
```go
|
|
{
|
|
ID: "your-agent",
|
|
Name: "Your Custom Agent",
|
|
Description: "Description of what your agent does",
|
|
Image: "kandev/your-agent:latest",
|
|
WorkingDir: "/workspace",
|
|
MemoryMB: 2048,
|
|
CPUCores: 1.0,
|
|
Timeout: 30 * time.Minute,
|
|
Capabilities: []string{"custom_capability"},
|
|
Enabled: true,
|
|
}
|
|
```
|
|
|
|
### Step 4: Build and Test
|
|
|
|
```bash
|
|
# Build image
|
|
cd apps/backend/dockerfiles/your-agent
|
|
docker build -t kandev/your-agent:latest .
|
|
|
|
# Test locally
|
|
docker run -it --rm \
|
|
-v $(pwd):/workspace \
|
|
-e TASK_DESCRIPTION="Test task" \
|
|
kandev/your-agent:latest --acp
|
|
```
|
|
|
|
Test by sending JSON-RPC messages via stdin:
|
|
|
|
```bash
|
|
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1.0"}}' | \
|
|
docker run -i --rm kandev/your-agent:latest --acp
|
|
```
|
|
|
|
---
|
|
|
|
## WebSocket API Integration
|
|
|
|
All client communication uses a single WebSocket endpoint: `ws://localhost:8080/ws`
|
|
|
|
### Message Format
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request|response|notification|error",
|
|
"action": "action.name",
|
|
"payload": { ... },
|
|
"timestamp": "2026-01-10T12:00:00Z"
|
|
}
|
|
```
|
|
|
|
### Orchestrator Actions
|
|
|
|
| Action | Description |
|
|
|--------|-------------|
|
|
| `orchestrator.status` | Get orchestrator status |
|
|
| `orchestrator.queue` | Get queued tasks |
|
|
| `orchestrator.start` | Start task execution |
|
|
| `orchestrator.stop` | Stop running task |
|
|
| `orchestrator.prompt` | Send follow-up prompt |
|
|
| `orchestrator.complete` | Mark task complete |
|
|
| `orchestrator.trigger` | Queue task for execution |
|
|
|
|
### Start Task Execution
|
|
|
|
**Action**: `orchestrator.start`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "orchestrator.start",
|
|
"payload": {
|
|
"task_id": "uuid",
|
|
"agent_type": "augment-agent",
|
|
"priority": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "response",
|
|
"action": "orchestrator.start",
|
|
"payload": {
|
|
"success": true,
|
|
"task_id": "uuid",
|
|
"agent_instance_id": "instance-uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Send Follow-up Prompt
|
|
|
|
**Action**: `orchestrator.prompt`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "orchestrator.prompt",
|
|
"payload": {
|
|
"task_id": "uuid",
|
|
"prompt": "Please also add unit tests"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "response",
|
|
"action": "orchestrator.prompt",
|
|
"payload": {
|
|
"success": true,
|
|
"task_id": "uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Stop Task Execution
|
|
|
|
**Action**: `orchestrator.stop`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "orchestrator.stop",
|
|
"payload": {
|
|
"task_id": "uuid",
|
|
"reason": "User requested cancellation",
|
|
"force": false
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "response",
|
|
"action": "orchestrator.stop",
|
|
"payload": {
|
|
"success": true,
|
|
"task_id": "uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Complete Task
|
|
|
|
**Action**: `orchestrator.complete`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "orchestrator.complete",
|
|
"payload": {
|
|
"task_id": "uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "response",
|
|
"action": "orchestrator.complete",
|
|
"payload": {
|
|
"success": true,
|
|
"task_id": "uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
### Get Orchestrator Status
|
|
|
|
**Action**: `orchestrator.status`
|
|
|
|
**Request:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "orchestrator.status",
|
|
"payload": {}
|
|
}
|
|
```
|
|
|
|
**Response:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "response",
|
|
"action": "orchestrator.status",
|
|
"payload": {
|
|
"running": true,
|
|
"active_agents": 2,
|
|
"queued_tasks": 5,
|
|
"max_concurrent": 3
|
|
}
|
|
}
|
|
```
|
|
|
|
### ACP Notifications
|
|
|
|
When subscribed to a task via `task.subscribe`, clients receive real-time ACP notifications:
|
|
|
|
**Progress Notification:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "notification",
|
|
"action": "acp.progress",
|
|
"payload": {
|
|
"task_id": "uuid",
|
|
"type": "progress",
|
|
"data": {
|
|
"progress": 50,
|
|
"stage": "executing",
|
|
"message": "Processing files..."
|
|
},
|
|
"timestamp": "2026-01-10T12:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Log Notification:**
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "notification",
|
|
"action": "acp.log",
|
|
"payload": {
|
|
"task_id": "uuid",
|
|
"type": "log",
|
|
"data": {
|
|
"level": "info",
|
|
"message": "Found 5 files to modify"
|
|
},
|
|
"timestamp": "2026-01-10T12:00:00Z"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Task Subscription:**
|
|
|
|
**Action**: `task.subscribe`
|
|
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"type": "request",
|
|
"action": "task.subscribe",
|
|
"payload": {
|
|
"task_id": "uuid"
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Session Resumption
|
|
|
|
Agents can resume previous sessions for follow-up conversations.
|
|
|
|
### How It Works
|
|
|
|
1. First task completes, session ID stored in task metadata
|
|
2. Retrieve session ID via WebSocket `task.get` action → `metadata.auggie_session_id`
|
|
3. Launch new task with `AUGGIE_SESSION_ID` environment variable
|
|
|
|
### Example
|
|
|
|
```javascript
|
|
// WebSocket connection
|
|
const ws = new WebSocket('ws://localhost:8080/ws');
|
|
|
|
// Get session ID from completed task
|
|
ws.send(JSON.stringify({
|
|
id: crypto.randomUUID(),
|
|
type: 'request',
|
|
action: 'task.get',
|
|
payload: { id: previousTaskId }
|
|
}));
|
|
|
|
// Handle response and start with session resumption
|
|
ws.onmessage = (event) => {
|
|
const response = JSON.parse(event.data);
|
|
|
|
if (response.action === 'task.get') {
|
|
const sessionId = response.payload.metadata.auggie_session_id;
|
|
|
|
// Create new task with session ID in metadata
|
|
ws.send(JSON.stringify({
|
|
id: crypto.randomUUID(),
|
|
type: 'request',
|
|
action: 'task.create',
|
|
payload: {
|
|
board_id: boardId,
|
|
column_id: columnId,
|
|
title: 'Follow-up task',
|
|
description: 'What changes did you make in the previous task?',
|
|
agent_type: 'augment-agent',
|
|
metadata: {
|
|
auggie_session_id: sessionId
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
```
|
|
|
|
The agent will load the previous context and continue the conversation.
|
|
|
|
---
|
|
|
|
## Legacy Message Format
|
|
|
|
Prior versions used a simpler JSON format (not JSON-RPC). For compatibility reference:
|
|
|
|
```json
|
|
{
|
|
"type": "progress|log|result|error|status|heartbeat",
|
|
"agent_id": "uuid",
|
|
"task_id": "uuid",
|
|
"timestamp": "2026-01-10T12:00:00Z",
|
|
"data": { ... }
|
|
}
|
|
```
|
|
|
|
**Types:**
|
|
- `progress` - Progress percentage, current file
|
|
- `log` - Log messages (debug, info, warn, error)
|
|
- `result` - Task completion (completed, failed, cancelled)
|
|
- `error` - Error messages
|
|
- `status` - Status changes (started, running, paused, stopped)
|
|
- `heartbeat` - Keep-alive
|
|
|
|
**Current agents should use JSON-RPC 2.0 (ACP)**, but the backend may support legacy format for backward compatibility.
|
|
|
|
---
|
|
|
|
## Error Handling
|
|
|
|
### JSON-RPC Errors
|
|
|
|
**Error Response:**
|
|
```json
|
|
{
|
|
"jsonrpc": "2.0",
|
|
"id": 4,
|
|
"error": {
|
|
"code": -32600,
|
|
"message": "Invalid request",
|
|
"data": "Missing required parameter: message"
|
|
}
|
|
}
|
|
```
|
|
|
|
**Standard Error Codes:**
|
|
- `-32700` - Parse error
|
|
- `-32600` - Invalid request
|
|
- `-32601` - Method not found
|
|
- `-32602` - Invalid params
|
|
- `-32603` - Internal error
|
|
|
|
### HTTP API Errors
|
|
|
|
```json
|
|
{
|
|
"error": "error_code",
|
|
"message": "Human readable message",
|
|
"details": "Additional context",
|
|
"http_status": 400
|
|
}
|
|
```
|
|
|
|
**Common Codes:**
|
|
- `bad_request` (400)
|
|
- `not_found` (404)
|
|
- `validation_error` (400)
|
|
- `internal_error` (500)
|
|
|
|
---
|
|
|
|
## Best Practices
|
|
|
|
### For Agent Developers
|
|
|
|
1. **Respond quickly to initialize** - Send capabilities within 1 second
|
|
2. **Stream progress frequently** - Update every 1-5 seconds during work
|
|
3. **Handle cancellation gracefully** - Clean up and exit when receiving `session/cancel`
|
|
4. **Use proper session IDs** - Generate unique, persistent IDs for resumption
|
|
5. **Log to stderr** - Use stdout exclusively for ACP messages
|
|
6. **Validate messages** - Check for required fields before processing
|
|
7. **Set exit codes** - 0 for success, non-zero for errors
|
|
|
|
### Frontend Data Loading (SSR + WS)
|
|
|
|
For settings + dashboard data, use a consistent pattern:
|
|
|
|
1. **SSR fetch in layout/page** → hydrate the store via `StateHydrator`.
|
|
2. **Components read from store only** (no local fetches in UI components).
|
|
3. **WebSocket events update the store** for real-time sync.
|
|
|
|
Guidelines:
|
|
- Fetch once in the server component (e.g. `apps/web/app/settings/layout.tsx`) and hydrate.
|
|
- Map API responses into store-friendly shapes (e.g. agent profiles → `{ id, label, agent_id }`).
|
|
- Use WS handlers to patch lists; avoid making HTTP requests inside WS handlers.
|
|
- Prefer WS events to include full entity payloads so clients do not need follow-up HTTP fetches.
|
|
- Only re-fetch when the server cannot emit the full data and document why.
|
|
- Avoid request/response WS calls inside UI components.
|
|
- UI components should read from the store only; perform HTTP fetches in hooks or store actions.
|
|
|
|
## Local Agent Discovery
|
|
|
|
Kandev checks a bundled discovery config to detect locally installed agents. Each agent entry lists OS-specific installation paths (Linux, Windows, macOS). At runtime, the backend selects the paths for the current OS, expands environment variables and `~`, and marks the agent as available if any configured file or directory exists.
|
|
|
|
### Message Flow
|
|
|
|
```
|
|
1. Client sends orchestrator.start via WebSocket
|
|
2. Backend creates container with agentctl
|
|
3. agentctl starts HTTP server on port 9999
|
|
4. Backend waits for agentctl health check (GET /health)
|
|
5. Backend calls POST /api/v1/start
|
|
6. agentctl spawns agent subprocess
|
|
7. Backend calls POST /api/v1/acp/call with initialize
|
|
8. Agent responds with capabilities
|
|
9. Backend calls POST /api/v1/acp/call with session/new
|
|
10. Agent responds with sessionId
|
|
11. Backend connects to agentctl WS /api/v1/acp/stream
|
|
12. Backend calls POST /api/v1/acp/call with session/prompt
|
|
13. Agent sends session/update notifications (streamed to client via WebSocket)
|
|
14. Agent sends final session/update (type: complete)
|
|
15. Client sends orchestrator.complete or backend calls POST /api/v1/stop
|
|
```
|
|
|
|
### Testing Your Agent
|
|
|
|
**With agentctl:**
|
|
```bash
|
|
# Build and run container
|
|
docker run -d -p 9999:9999 kandev/your-agent:latest
|
|
|
|
# Check health
|
|
curl http://localhost:9999/health
|
|
|
|
# Start agent
|
|
curl -X POST http://localhost:9999/api/v1/start
|
|
|
|
# Send ACP message
|
|
curl -X POST http://localhost:9999/api/v1/acp/call \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"method":"initialize","params":{"protocolVersion":"1.0"}}'
|
|
```
|
|
|
|
**Direct ACP testing:**
|
|
```bash
|
|
# Test initialize
|
|
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1.0"}}' | \
|
|
docker run -i kandev/your-agent:latest /usr/local/bin/agent --acp
|
|
|
|
# Test full flow
|
|
(
|
|
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1.0"}}'
|
|
echo '{"jsonrpc":"2.0","id":2,"method":"session/new","params":{}}'
|
|
echo '{"jsonrpc":"2.0","id":3,"method":"session/prompt","params":{"message":"test"}}'
|
|
) | docker run -i kandev/your-agent:latest /usr/local/bin/agent --acp
|
|
```
|
|
|
|
---
|
|
|
|
## Reference Implementation
|
|
|
|
See `apps/backend/` for complete working examples:
|
|
|
|
**agentctl:**
|
|
- `cmd/agentctl/main.go` - Entry point
|
|
- `internal/agentctl/` - HTTP server, process manager, ACP relay
|
|
|
|
**Augment Agent:**
|
|
- `dockerfiles/augment-agent/Dockerfile` - Multi-stage build with agentctl
|
|
- `dockerfiles/augment-agent/entrypoint.sh` - Container entrypoint
|
|
|
|
**Backend Integration:**
|
|
- `internal/agent/agentctl/client.go` - HTTP client for agentctl
|
|
- `internal/agent/lifecycle/manager.go` - Container lifecycle management
|
|
|
|
---
|
|
|
|
## Additional Resources
|
|
|
|
- **System Architecture**: [ARCHITECTURE.md](ARCHITECTURE.md)
|
|
- **WebSocket API Specification**: [docs/asyncapi.yaml](docs/asyncapi.yaml)
|
|
- **Agent Registry**: `apps/backend/internal/agent/registry/defaults.go`
|
|
- **agentctl Implementation**: `apps/backend/internal/agentctl/`
|
|
- **ACP Protocol Types**: `apps/backend/pkg/acp/jsonrpc/`
|
|
- **JSON-RPC 2.0 Spec**: https://www.jsonrpc.org/specification
|
|
|
|
---
|
|
|
|
**Last Updated**: 2026-01-11
|