feat: export Coder Agents debug logs (#25039)

Adds JSON export actions to the Coder Agents Debug panel so users can download either the current chat's recent debug runs or one expanded run for support sharing.

The export reuses the existing chat debug endpoints and react-query cache, adds Storybook and unit coverage for the JSON envelope, and updates the chat debug logging docs with UI and cURL instructions.

Refs CODAGT-280.

Generated by Coder Agents.

<details>
<summary>Implementation notes</summary>

- Chat-level export fetches full detail for each listed debug run with `queryClient.fetchQuery(chatDebugRun(chatId, run.id))` and writes a single JSON file.
- Run-level export uses the already-loaded detail query data from an expanded run card.
- The JSON envelope includes `version`, `scope`, `exported_at`, `chat_id`, and either `runs` or `run`.
- The chat-level export reflects the current backend list endpoint behavior, up to the 100 newest debug runs.
- Agent-browser dogfooding verified files were downloaded and that `jq` validated the chat-level and run-level JSON contents.

</details>
This commit is contained in:
Thomas Kosiewski
2026-05-12 17:39:57 +02:00
committed by GitHub
parent 147f50c5e8
commit 969da320ec
7 changed files with 886 additions and 12 deletions
@@ -89,12 +89,83 @@ that chat's owner. The tab lists recent debug runs and lets you expand a run
into its per-step request, response, token usage, retry attempts, errors,
and policy metadata.
### Export debug logs
You can export the same captured debug data from the UI:
1. Navigate to **Agents**.
1. Open a chat with debug logging enabled.
1. Open the **Debug** tab in the right panel.
1. Click **Export debug logs** to download the chat's recent debug runs as
JSON, or expand a run and click **Export this run** to download one run.
The chat-level export includes the full run detail for the runs returned by
the debug run list endpoint. The current list endpoint returns up to 100 of
the newest runs.
### API access
The same data is available through the experimental API:
- `GET /api/experimental/chats/{chat}/runs` lists the most recent runs for a
chat (up to 100, newest first).
- `GET /api/experimental/chats/{chat}/runs/{debugRun}` returns a single run
with all of its steps, including normalized request and response bodies.
- `GET /api/experimental/chats/{chat}/debug/runs` lists the most recent runs
for a chat (up to 100, newest first).
- `GET /api/experimental/chats/{chat}/debug/runs/{debugRun}` returns a single
run with all of its steps, including normalized request and response bodies.
Fetch a single run and save it as JSON:
```sh
export CODER_URL="https://coder.example.com"
export CODER_SESSION_TOKEN="$(coder login token)"
export CHAT_ID="00000000-0000-0000-0000-000000000000"
export RUN_ID="11111111-1111-1111-1111-111111111111"
curl -fsS \
-H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
"$CODER_URL/api/experimental/chats/$CHAT_ID/debug/runs/$RUN_ID" \
| jq . > "coder-agents-debug-run-$RUN_ID.json"
```
Fetch every run returned by the list endpoint and save a chat-level export.
Using the same `CODER_URL`, `CODER_SESSION_TOKEN`, and `CHAT_ID` variables
from above:
```sh
RUN_IDS=$(curl -fsS \
-H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
"$CODER_URL/api/experimental/chats/$CHAT_ID/debug/runs" \
| jq -r '.[].id') || {
echo "Failed to list debug runs" >&2
exit 1
}
RUN_EXPORTS=$(mktemp)
trap 'rm -f "$RUN_EXPORTS"' EXIT
for RUN_ID in $RUN_IDS; do
curl -fsS \
-H "Coder-Session-Token: $CODER_SESSION_TOKEN" \
"$CODER_URL/api/experimental/chats/$CHAT_ID/debug/runs/$RUN_ID" \
>> "$RUN_EXPORTS" || {
echo "Failed to fetch debug run $RUN_ID" >&2
exit 1
}
echo >> "$RUN_EXPORTS"
done
jq -s \
--arg chat_id "$CHAT_ID" \
--arg exported_at "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{
version: 1,
scope: "chat",
exported_at: $exported_at,
chat_id: $chat_id,
run_count: length,
limited_to_most_recent: 100,
runs: .
}' "$RUN_EXPORTS" > "coder-agents-debug-chat-$CHAT_ID.json"
```
Debug runs are stored alongside the chat and are removed when the parent
conversation is deleted (manually, by retention, or by chat purge). See