package com.example.aiapp import org.json.JSONObject // The common event model, mirrored from server/src/session/driver.rs -- // the app renders purely from this stream (replayed from the transcript by // cursor, then live), so there is no separate "load history" shape to keep // in sync with it. /** One transcript line: the event plus its resume cursor and time. */ data class SeqEvent(val seq: Long, val ts: Double, val event: SessionEvent) sealed class SessionEvent { data class UserMessage(val text: String) : SessionEvent() data class AssistantText(val delta: String) : SessionEvent() data class ToolStart(val id: String, val tool: String, val input: String) : SessionEvent() data class ToolUpdate(val id: String, val output: String) : SessionEvent() data class ToolEnd(val id: String, val output: String) : SessionEvent() data class Image(val ref: String) : SessionEvent() data class Question(val id: String, val prompt: String, val options: List) : SessionEvent() data class Answered(val id: String, val answer: String) : SessionEvent() data class Status(val state: String) : SessionEvent() data class UsageDelta(val tokens: Long) : SessionEvent() data class Error(val message: String) : SessionEvent() /** * An event type this app build doesn't know -- a newer server. Kept * (not thrown) so one new event kind degrades to a placeholder row * instead of killing the stream. */ data class Unknown(val type: String) : SessionEvent() } fun parseSeqEvent(json: String): SeqEvent { val body = JSONObject(json) val event = when (val type = body.getString("type")) { "userMessage" -> SessionEvent.UserMessage(body.getString("text")) "assistantText" -> SessionEvent.AssistantText(body.getString("delta")) "toolStart" -> SessionEvent.ToolStart( id = body.getString("id"), tool = body.getString("tool"), // Kept as raw JSON text: the input shape is the tool's own // business, and the UI only ever shows it verbatim. input = body.get("input").toString(), ) "toolUpdate" -> SessionEvent.ToolUpdate(body.getString("id"), body.getString("output")) "toolEnd" -> SessionEvent.ToolEnd(body.getString("id"), body.getString("output")) "image" -> SessionEvent.Image(body.getString("ref")) "question" -> SessionEvent.Question( id = body.getString("id"), prompt = body.getString("prompt"), options = body.getJSONArray("options").let { options -> (0 until options.length()).map { options.getString(it) } }, ) "answered" -> SessionEvent.Answered(body.getString("id"), body.getString("answer")) "status" -> SessionEvent.Status(body.getString("state")) "usageDelta" -> SessionEvent.UsageDelta(body.getLong("tokens")) "error" -> SessionEvent.Error(body.getString("message")) else -> SessionEvent.Unknown(type) } return SeqEvent(seq = body.getLong("seq"), ts = body.getDouble("ts"), event = event) }