LLM Wiki Agent SSE Events: Complete Event Stream Reference

The LLM Wiki agent streams eleven distinct Server-Sent Events (SSE) through the /api/v1/projects/{projectId}/chat endpoint, including session lifecycle markers, tool invocations, file changes, and incremental message deltas.

The nashsu/llm_wiki repository implements a real-time observable agent architecture that exposes its internal workflow via SSE. According to the source code in src-tauri/src/agent/events.rs, the agent serializes an AgentEvent enum into JSON payloads wrapped in SSE frames, enabling frontend clients and external tools to react to AI reasoning as it happens.

Core AgentEvent Payload Types

All business-logic events are emitted as SSE frames with the event name agent. The payload structure is defined by the AgentEvent enum in src-tauri/src/agent/events.rs, where each variant carries a camel-case "type" field for client-side dispatch.

The complete set of payload events includes:

  • AgentStart: Marks the beginning of a new chat session, carrying the sessionId.
  • TurnStart: Signals the start of a reasoning turn, including the LLM mode (e.g., "tool" or "chat").
  • ToolStart: Indicates a tool invocation (such as web-search or file-write), containing the tool name and optional input parameters.
  • ToolEnd: Reports completion of a tool call with the tool name and optional output.
  • ReferenceAdded: Fires when a new reference (wiki page or external source) attaches to the conversation context.
  • FileChanged: Notifies when a workspace file is modified, including the file path, the tool that performed the change, and whether the file existed previously (previous content is redacted for external clients).
  • MessageDelta: Delivers incremental LLM text output—fragments of the final streaming answer.
  • Error: Communicates runtime errors inside the agent with an error message string.
  • UserInputRequired: Indicates the agent requires explicit user input, carrying an AgentUserInputRequest describing the request.
  • Done: Terminates the chat session cleanly, including the final sessionId.

SSE Frame Structure and Protocol

Beyond the agent event frames, the stream includes auxiliary frames generated in src-tauri/src/api_server.rs by the respond_chat_sse function (lines 75-84 and 119-161).

These frames use distinct event names:

  • meta: Sent immediately upon connection. Contains { projectId, sessionId, runId } to enable client-side correlation of events with a specific run.
  • done: Marks successful completion. Wraps the final chat response via external_chat_response and signals that the stream is closing normally.
  • cancelled: Indicates the chat was aborted by the user or system. Payload follows { ok: false, error: "..."}.
  • error: Signals unrecoverable failures. Uses the same error payload structure as cancelled.

All frames are transmitted with Content-Type: text/event-stream headers established in the respond_chat_sse function.

Consuming SSE Streams in Practice

JavaScript Client Implementation

Connect to the endpoint and filter by event name to handle specific lifecycle stages:

const url = `http://localhost:19828/api/v1/projects/myProject/chat`;
const evtSource = new EventSource(url, { withCredentials: true });

evtSource.addEventListener('meta', e => {
  const meta = JSON.parse(e.data);
  console.log('Chat metadata:', meta);
});

evtSource.addEventListener('agent', e => {
  const ev = JSON.parse(e.data);
  console.log('Agent event type:', ev.type);
  
  // Handle incremental text streaming
  if (ev.type === 'messageDelta') {
    document.getElementById('output').textContent += ev.text;
  }
  
  // Monitor tool usage
  if (ev.type === 'toolStart') {
    console.log(`Tool ${ev.tool} invoked with input:`, ev.input);
  }
});

evtSource.addEventListener('done', e => {
  console.log('Chat completed:', JSON.parse(e.data));
  evtSource.close();
});

evtSource.addEventListener('error', e => {
  console.error('Stream error:', e);
});

Rust Event Parsing

For Rust-based consumers, deserialize the payload using the crate's internal types:

use serde_json::Value;
use llm_wiki::src_tauri::agent::events::AgentEvent;

fn decode_agent_event(payload: &str) -> Result<AgentEvent, serde_json::Error> {
    serde_json::from_str::<Value>(payload)
        .and_then(|v| serde_json::from_value(v))
}

Filtering for Specific Tool Calls

To detect when specific tools execute:

evtSource.addEventListener('agent', e => {
  const ev = JSON.parse(e.data);
  if (ev.type === 'toolStart' && ev.tool === 'wiki.search') {
    console.log('Search query:', ev.input);
  }
  if (ev.type === 'fileChanged') {
    console.log(`File ${ev.path} modified by ${ev.tool}`);
  }
});

Summary

  • The LLM Wiki agent exposes its internal state via SSE through /api/v1/projects/{projectId}/chat.
  • Business events use the agent SSE event name and are defined in src-tauri/src/agent/events.rs.
  • The protocol includes ten operational events (AgentStart through Done) plus dedicated frames for metadata, completion, cancellation, and errors.
  • The respond_chat_sse function in src-tauri/src/api_server.rs (lines 119-161) handles the serialization and transmission logic.
  • Consumers should listen for meta to obtain run correlation IDs, agent for workflow updates, and done/error for stream termination.

Frequently Asked Questions

What is the SSE endpoint URL for the LLM Wiki agent?

The agent exposes the stream at /api/v1/projects/{projectId}/chat, where {projectId} is the target workspace identifier. According to src-tauri/src/api_server.rs, this endpoint triggers the respond_chat_sse function which establishes the text/event-stream response.

How are agent events serialized in the SSE protocol?

Each AgentEvent variant serializes to JSON with a camel-case "type" discriminator (e.g., "messageDelta", "toolStart"). The JSON payload is wrapped in an SSE frame with the event field set to agent. The meta frame is sent first to provide projectId, sessionId, and runId for correlation.

What is the difference between the done and Done events?

Done (capital D) is an AgentEvent variant sent inside an agent frame indicating the agent's internal session termination. done (lowercase) is a top-level SSE event name that wraps the final chat response and signals the HTTP stream is closing successfully. The former represents application state; the latter represents transport state.

How can I detect file modifications in the stream?

Listen for the agent event where type === 'fileChanged'. As implemented in src-tauri/src/agent/events.rs, this event includes the file path, the tool that performed the modification, and a boolean indicating whether the file existed previously. Note that previous file contents are redacted in external-facing streams for security.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →