* feat: add workspace sources from files panel * fix: preserve worktree identity during recovery * feat: unify workspace source actions * fix: validate recovered workspace repositories * fix: harden workspace source filesystem boundaries * test: split workspace repository chip coverage * fix: restrict repository rescan recovery paths * fix: stabilize workspace source CI checks * test: canonicalize workspace source paths * test: align workspace source container paths * fix: populate direct workspace repository names * test: preserve git fixture rewrites across restarts * test: isolate backend git fixture config * fix: resume remote workspace executions before creation * fix: preserve ssh agent identity during resume * fix: preserve SSH workspace across backend restart * fix: simplify workspace actions trigger * fix: refine add workspace repositories UX * docs: narrow workspace source UX updates * fix: avoid phantom turn on session resume * test(web): normalize workspace source request headers * feat(web): streamline workspace source picker * docs: remove trailing spaces from workspace source plan * fix: prevent resume status from creating phantom turns * fix(web): stabilize workspace source attachment * refactor(web): extract workspace source store contract * fix: avoid phantom turns on idle agent resume * fix: address backend static analysis findings * fix: preserve agentctl readiness status value * fix: preserve resume messages without active turns * fix: resume agents from promoted workspace root * chore: merge origin/main into feature/add-new-repo-folder-d97 * feat(web): explain workspace source consequences * test(websocket): count session activity subscription
acpdbg
Headless CLI that speaks raw ACP JSON-RPC to any registered kandev agent (or an arbitrary binary) and records every wire frame to a JSONL file. Useful for answering "what models/modes does this agent advertise?", "does session/load work?", or "what does the prompt stream actually look like?" without spinning up the full kandev backend.
Build
make acpdbg ARGS=list # from repo root
make -C apps/backend build-acpdbg # just build
Binary lands at apps/backend/bin/acpdbg.
Usage
make acpdbg ARGS="<subcommand> [flags]"
Or invoke the binary directly: apps/backend/bin/acpdbg <subcommand> [flags].
Sub-commands
| Command | Purpose |
|---|---|
list |
Enumerate registered ACP agents with their spawn command |
probe <agent> |
initialize → session/new → close. Shows models, modes, auth methods. |
probe --exec "<cmd> [args...]" |
Same but against an arbitrary binary not in the registry |
prompt <agent> --prompt "..." [--model M] [--mode M] |
Full prompt round-trip, collects text chunks from session/update |
session-load <agent> --session-id <id> [--prompt "..."] |
Load an existing session in --workdir, then optionally verify it with a prompt |
matrix |
Probe every registered ACP agent in parallel, write one JSONL per agent + matrix-summary.json |
Shared flags
--out DIR— JSONL output directory (default./acp-debug/)--file PATH— exact JSONL path, overrides--out--timeout DUR— overall run timeout (default30s; use60s+formatrixsincenpx-spawned agents are slow to cold-start)--workdir PATH— child cwd (default: fresh/tmp/kandev-acpdbg-<pid>-*)--verbose— mirror frames to stderr as they're sent/received--stderr— capture child stderr into the JSONL (useful when an agent crashes before the handshake completes)
Verify resume under a different working directory
Create a session in one directory, then load it from another and ask the agent to report its working directory:
apps/backend/bin/acpdbg prompt codex-acp \
--workdir /tmp/folder-a \
--prompt "Reply with pwd only" \
--file /tmp/codex-new.jsonl
apps/backend/bin/acpdbg session-load codex-acp \
--session-id <session-id-from-first-run> \
--workdir /tmp/folder-b \
--prompt "Reply with pwd only" \
--file /tmp/codex-load.jsonl
session-load sends the selected --workdir as the ACP cwd parameter. The
JSONL wire frames are authoritative when a provider replays earlier session
messages during load.
JSONL format
One JSON object per line, chronologically ordered. direction is one of:
direction |
Meaning |
|---|---|
meta |
acpdbg marker (event: start with agent/command/workdir, event: close with exit_code/reason) |
sent |
Frame written to child stdin |
received |
Frame read from child stdout |
stderr |
Child stderr line (only with --stderr) |
jq recipes
# Full initialize response
jq -c 'select(.direction == "received" and .frame.id == 1)' acp-debug/<file>.jsonl
# Just the advertised models
jq -r 'select(.direction == "received") | .frame.result.models.availableModels[]?.modelId' acp-debug/<file>.jsonl
# Close event (exit code + reason)
jq -c 'select(.direction == "meta" and .event == "close")' acp-debug/<file>.jsonl
Design notes
- Agent registry is the source of truth.
acpdbgimportsinternal/agent/registrydirectly, so adding/removing agents ininternal/agent/agents/*.goautomatically updates the CLI. No parallel config file. - Raw JSON-RPC, not the SDK. Uses a custom line-delimited framer (
internal/agent/acpdbg/framer.go) so the recorded frames are authoritative wire bytes, not SDK-parsed events. - Env inheritance. The child process inherits the parent shell's env and credential files — if it works in kandev it works here. Auth failures are captured in the JSONL.
- Agent-initiated requests (
fs/read_text_file,session/request_permission, etc.) are auto-replied to with-32601 method not foundso sessions don't hang. For real permission flows, use the full kandev backend.
See .agents/skills/acp-debug/SKILL.md for the agent-facing usage guide.