Files
kandev/docs/agentctl-lifecycle.md
T
2026-01-17 19:02:22 +00:00

128 lines
5.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agentctl session lifecycle (Kandev)
This document explains how **agentctl** fits into the Kandev execution flow, what “ready” means, and which parts of the system depend on agentctl being up.
## What agentctl is
`agentctl` is a sidecar process that runs inside each agent container (or standalone runtime). It provides:
- **HTTP API** for lifecycle operations (`/health`, `/api/v1/start`, `/api/v1/stop`, etc.)
- **ACP bridge** (stdin/stdout JSON‑RPC) to the agent process (Auggie/Codex/etc.)
- **WebSockets** for streaming ACP updates and agent output
- **Shell access** to the workspace (terminal output + input)
It is the backend’s *control plane* for an agent execution.
## High‑level flow (runtime‑agnostic)
```
User action (start task)
|
v
Kandev orchestrator
|
| 1) Create task session in DB
| 2) Launch agent runtime (Docker or standalone)
v
agentctl process starts
|
| 3) HTTP server comes up (health + control APIs)
v
Backend waits for agentctl readiness
|
| 4) agentctl ready => shell/workspace access available
v
ACP session initialization
|
| 5) initialize -> session/new or session/load
| 6) prompt is sent
v
Agent runs, ACP updates stream
|
| 7) session/update notifications => WS to client
v
Task session state transitions
```
## “Ready” concepts (important distinction)
There are **two different “ready” moments**:
1) **agentctl ready**
- Meaning: agentctl HTTP server is healthy and can accept requests.
- Practical impact: shell/workspace endpoints become usable.
- Source: `lifecycle.Manager.waitForAgentctlReady`.
- Today: only logged; **not emitted as a WS event**.
2) **agent ready** (prompt completed)
- Meaning: agent finished the current prompt and is idle/ready for follow‑up.
- Source: `events.AgentReady` (after prompt completion).
- Today: internal event for orchestrator state transitions.
The frontend should not rely on “agent ready” when it needs shell access; shell access depends on agentctl readiness instead.
## Current backend signals
- **agentctl readiness**
- `agentctl is ready` (agentctl client log)
- `agentctl ready - shell/workspace access available` (lifecycle manager log)
- **agent ready** (post‑prompt)
- `events.AgentReady` internal event (used by orchestrator state transitions)
There is **no WS event** that tells the client “agentctl is ready.”
## Where shell streaming depends on agentctl
- Client calls `shell.subscribe` (WS request)
- Backend checks:
- agent execution exists for the task
- agentctl is connected
- Backend starts a streaming loop that relays agentctl shell output as `shell.output` WS notifications
If agentctl isn’t ready yet, `shell.subscribe` fails and the frontend currently retries.
## Suggested improvement (optional)
Introduce a WS notification like:
```
{ "action": "task.session.agentctl_ready", "payload": { "task_id": "...", "task_session_id": "..." } }
```
So the client can subscribe once and only request shell after it receives readiness.
## End‑to‑end diagram (detailed)
```
Client Backend (Kandev) agentctl Agent
| | | |
| orchestrator.start | | |
|------------------------------->| | |
| | create task session | |
| | launch runtime | |
| |---------------------------->| process starts |
| | | HTTP server up |
| | waitForAgentctlReady | |
| |---------------------------->| /health |
| |<----------------------------| 200 OK |
| | (agentctl ready) | |
| | ACP initialize | |
| |---------------------------->| initialize |
| | ACP session/new | |
| |---------------------------->| session/new |
| | ACP prompt | |
| |---------------------------->| session/prompt |
| | |--------------------->|
| | | agent runs |
| | WS stream | |
|<-------------------------------| (session/update notifications) |
```
## References
- `apps/backend/internal/agent/lifecycle/manager.go` (waitForAgentctlReady)
- `apps/backend/internal/agentctl/client/client.go` (WaitForReady)
- `apps/backend/internal/agent/handlers/shell_handlers.go` (shell.subscribe)
- `apps/backend/internal/events/types.go` (`AgentReady`)