Skip to content

Implement JsonEventOutputHandler in C++ framework #522

@kovtcharov

Description

@kovtcharov

Context

Part of the C++ WebUI Integration milestone. See docs/plans/cpp-webui-integration.md for full architecture.

Description

Create a new OutputHandler subclass in the C++ framework that emits JSON-line events to stdout. Each line is a JSON object matching the SSE event format the WebUI frontend already consumes.

Files:

  • cpp/include/gaia/json_event_handler.h
  • cpp/src/json_event_handler.cpp

Event Mapping

Every OutputHandler virtual method maps to one JSON event:

Method Event JSON
printStepHeader(n, total) {"type": "step", "step": n, "total": total, "status": "started"}
printStreamToken(token) {"type": "chunk", "content": "token"}
printStreamEnd() (internal state, no output)
printThought(text) {"type": "thinking", "content": "text"}
printGoal(text) {"type": "status", "status": "working", "message": "text"}
printPlan(plan, current) {"type": "plan", "steps": [...], "current_step": n}
printToolUsage(name) {"type": "tool_start", "tool": "name"}
prettyPrintJson(args, "Tool Args") {"type": "tool_args", "tool": "name", "args": {...}, "detail": "..."}
printToolComplete() {"type": "tool_end", "success": true}
prettyPrintJson(result, "Tool Result") {"type": "tool_result", "title": "...", "summary": "...", "success": true}
printFinalAnswer(text) {"type": "answer", "content": "text", "steps": n, "tools_used": n}
printError(msg) {"type": "agent_error", "content": "msg"}
printWarning(msg) {"type": "status", "status": "warning", "message": "msg"}
printInfo(msg) {"type": "status", "status": "info", "message": "msg"}
printCompletion(steps, limit) {"type": "status", "status": "complete", "steps": n}

IMPORTANT: Do NOT Emit done Event

The done SSE event ({"type": "done", "message_id": N}) is emitted by the Python wrapper after the C++ subprocess exits and the message is saved to the database. The C++ JsonEventOutputHandler must NOT emit done — it doesn't have access to the database message ID.

Tool Result command_output Extraction

For tool results containing shell command output, parse the result JSON to extract structured command_output:

{
  "type": "tool_result",
  "title": "Tool Result",
  "summary": "...",
  "success": true,
  "command_output": {
    "command": "netsh wlan show interfaces",
    "stdout": "...",
    "return_code": 0
  }
}

C++ agent tools typically return JSON with "tool", "command", "output", and "exitCode" fields. The handler should map these to the command_output structure the frontend expects.

Distinguishing Tool Args vs Results

The prettyPrintJson(data, title) method is called with:

  • title = "Tool Args" → emit as tool_args event
  • title = "Tool Result" → emit as tool_result event
  • Other titles → emit as generic tool_result

This is already how the C++ agent uses it (verified in agent.cpp lines 501, 508).

Internal State Tracking

The handler must track:

  • currentTool_ — name of the tool currently executing (set by printToolUsage)
  • stepsTaken_ — step counter (incremented by printStepHeader)
  • toolsUsed_ — tool counter (incremented by printToolComplete)
  • These are needed for the answer event's steps and tools_used fields

Acceptance Criteria

  • JsonEventOutputHandler implements all 19 OutputHandler virtual methods
  • Each method emits exactly one JSON line to stdout (flushed immediately)
  • JSON format matches the SSE event types the WebUI expects
  • Tool args and results correctly distinguished via title parameter
  • command_output structured format for shell tool results
  • Internal step/tool counters maintained for answer event stats
  • Does NOT emit done event (Python wrapper responsibility)
  • Unit tests validate JSON output for each method
  • Registered in CMakeLists.txt build

Dependencies

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions