How to Debug Kimi-Code Sessions Using the Kap-Server REST and WebSocket APIs
You can debug Kimi-Code sessions by querying the REST API for transcript snapshots and incremental operation batches, or by subscribing to the WebSocket API for real-time events with configurable transcript grades.
Kimi-Code, developed by MoonshotAI, runs an engine-side server (kap-server) that exposes comprehensive debugging surfaces for session introspection. Whether you need to inspect historical transcripts or monitor live agent operations, the combination of REST endpoints and WebSocket streams provides full visibility into session state. This guide explains how to debug Kimi-Code sessions using these APIs based on the actual source code implementation in the MoonshotAI/kimi-code repository.
REST API Endpoints for Session Transcripts
The REST API provides stable, request-response access to session data through four primary endpoints defined in packages/kap-server/src/routes/transcript.ts. These endpoints leverage the TranscriptService class, which maintains a live TranscriptStore per in-memory session and journals every dispatched batch with a monotonic seq number.
Full Transcript Retrieval
Fetch a paginated, turn-granular transcript for a specific agent:
curl -s "http://localhost:58627/api/v1/sessions/${SESSION_ID}/transcript?agent_id=main&page_size=50"
Use before_turn=t5 to page backward or after_turn=t6 to page forward. The endpoint uses TranscriptService.forSessionLive for active sessions or performs a cold rebuild from persisted wire logs for archived sessions.
Incremental Operations Catch-Up
Retrieve only the operation batches that occurred after a specific sequence number:
curl -s "http://localhost:58627/api/v1/sessions/${SESSION_ID}/transcript/ops?agent_id=main&since_seq=${LAST_SEQ}"
The JSON response contains:
batches: Array of operation batches, each withseqandTranscriptOperationarraysseq: The latest sequence numbercomplete: Boolean indicating whether the journal still covers the requested range
When complete is false, the journal no longer contains the requested history, and you must fall back to a full transcript fetch.
Plan Document Retrieval
Inspect plan documents generated by specific tool calls:
curl -s "http://localhost:58627/api/v1/sessions/${SESSION_ID}/transcript/plan?agent_id=main&tool_call_id=${CALL_ID}"
Omit the tool_call_id parameter to retrieve all plans for the specified agent.
User Messages Filter
Project only user-message events for streamlined debugging:
curl -s "http://localhost:58627/api/v1/sessions/${SESSION_ID}/transcript/user-messages?agent_id=main"
WebSocket API for Real-Time Monitoring
The WebSocket API at ws://<host>/api/v1/ws enables subscription to live session events, including global facts, per-agent events, and graded transcript streams. The implementation resides in packages/kap-server/src/transport/ws/v1/wsConnectionV1.ts.
Connection Handshake and Subscription
Upon connection, the server sends a server_hello frame describing the protocol version and buffer limits. Clients must subscribe to sessions using specific frame types:
const WebSocket = require('ws');
const ws = new WebSocket('ws://localhost:58627/api/v1/ws');
ws.on('open', () => {
// Handshake
ws.send(JSON.stringify({ type: 'client_hello', payload: {} }));
// Legacy subscription for all session events
ws.send(JSON.stringify({
type: 'subscribe',
payload: { session_id: SESSION_ID }
}));
// Modern grade-based subscription
ws.send(JSON.stringify({
type: 'subscribe_v2',
payload: {
session_id: SESSION_ID,
grades: [{ agent_id: 'main', grade: 'turn' }]
}
}));
});
Transcript Grades
The subscribe_v2 frame supports four grades that control the granularity of transcript events:
off: No transcript eventsblock: Block-level operationsturn: Turn-level operations (recommended for debugging)agent: Agent-level aggregation
The SessionEventBroadcaster (located in packages/kap-server/src/transport/ws/v1/sessionEventBroadcaster.ts) applies these filters and respects the suppressedByTranscript flag to prevent duplicate delivery of events already included in the transcript stream.
Buffering and Back-Pressure
Outbound frames are coalesced into a buffer with a default flush interval of 16ms and a maximum of 64 frames to optimize network usage while maintaining low latency. This logic is implemented in the WsConnectionV1 class between lines 58-66.
Step-by-Step Debugging Workflow
Follow this workflow to effectively debug Kimi-Code sessions using both API surfaces:
-
Identify the session ID from CLI output, UI logs, or by enumerating live sessions via
/api/v1/sessions. -
Establish a baseline by fetching the current transcript via REST to understand the session's current state.
-
Open a WebSocket connection and subscribe to
turngrade events for real-time monitoring. -
Track the sequence cursor (
seq) from WebSockettranscript.opspayloads to request incremental updates via REST if the connection drops. -
Inspect plan documents when debugging tool execution failures using the
/transcript/planendpoint. -
Monitor for resync signals via the WebSocket connection.
Handling Resync Scenarios
When a client's cursor (combination of seq and epoch) falls too far behind the server's journal, the WsConnectionV1 class sends a resync_required control frame. This occurs in the onMessage handler when processing subscribe_v2 requests (lines 119-138).
The payload includes a reason field (gap, epoch_mismatch, etc.). Upon receiving this frame:
ws.on('message', (data) => {
const frame = JSON.parse(data);
if (frame.type === 'resync_required') {
// Fetch full operation history via REST
fetch(`http://localhost:58627/api/v1/sessions/${SESSION_ID}/transcript/ops?agent_id=main&since_seq=0`)
.then(r => r.json())
.then(json => {
console.log('Recovered ops:', json.data.batches);
// Re-subscribe with fresh state
ws.send(JSON.stringify({
type: 'subscribe_v2',
payload: {
session_id: SESSION_ID,
grades: [{ agent_id: 'main', grade: 'turn' }]
}
}));
});
}
});
Post-Mortem Analysis with Session Event Journals
For offline debugging, the SessionEventJournal class (in packages/kap-server/src/transport/ws/v1/sessionEventJournal.ts) records every inbound envelope before it reaches the broadcaster. This journal enables complete replay of session events and is particularly useful for diagnosing race conditions or message ordering issues.
Summary
- The REST API provides four endpoints (
/transcript,/transcript/ops,/transcript/plan,/transcript/user-messages) for stable snapshot retrieval and incremental catch-up. - The WebSocket API supports real-time subscriptions with configurable grades (
off,block,turn,agent) viasubscribe_v2frames. - Resync handling is required when the client's
seqcursor falls behind the journal; recovery involves fetchingsince_seq=0via REST and re-subscribing. - Core implementation files include
transcriptService.tsfor session management,wsConnectionV1.tsfor WebSocket protocol handling, andsessionEventBroadcaster.tsfor event distribution. - Frame buffering coalesces messages into 16ms batches with a 64-frame maximum to balance latency and throughput.
Frequently Asked Questions
How do I handle a resync_required error when debugging?
When the WebSocket connection sends a resync_required frame, your client's cursor is too stale for the server to catch up. Fetch the full operation history via the REST endpoint /api/v1/sessions/{session_id}/transcript/ops?since_seq=0, then re-issue the subscribe_v2 frame to restart the stream from the current position.
What are the different transcript grades available in the WebSocket API?
The WebSocket API supports four grades: off (no events), block (fine-grained blocks), turn (conversation turns), and agent (high-level agent summaries). Specify these in the grades array of the subscribe_v2 payload to filter the event stream.
How can I retrieve only user messages from a session transcript?
Use the dedicated REST endpoint GET /api/v1/sessions/{session_id}/transcript/user-messages with the agent_id parameter. This projects only user-message events, filtering out system operations and agent responses for focused debugging.
What is the difference between subscribe and subscribe_v2 WebSocket frames?
The subscribe frame is the legacy allowlist approach that subscribes to all events for a session. The subscribe_v2 frame provides granular control via grades, allowing you to receive only specific transcript granularities (block, turn, etc.) per agent, and is the recommended approach for new debugging implementations.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →