feat(tui): event-derived ordering + retire Kotlin TUI

Rebuild the Go TUI transcript from the event stream instead of
optimistic local echoes. ChatTurnEvent now maps to ServerMessage.ChatTurn
(chat.turn); the TUI auto-vivifies sessions and renders user+router turns
from events. SessionStarted is de-special-cased into SessionAnnounced
(still carries workflowId), dropping the router.response shadow and
optimistic echoes.

Delete the unused Kotlin TUI (apps/tui) and add a kotlinx<->Go
golden-fixture test to lock the wire protocol. Gitignore server runtime
logs.
This commit is contained in:
2026-06-02 23:38:20 +04:00
parent b2f60d09bb
commit 1017bfffef
60 changed files with 231 additions and 4813 deletions
+6 -7
View File
@@ -545,15 +545,14 @@ func (m Model) submit() (tea.Model, tea.Cmd) {
if text == "" {
return m, nil
}
// New optimistic chat session.
// New chat session. The client owns the id, so create the entry locally and
// focus it; the USER/ROUTER turns arrive as chat.turn events (no optimistic echo).
id := newSessionID()
m.sessions = append(m.sessions, Session{
ID: id, Status: "STARTING", WorkflowID: "chat", Name: "chat",
LastEventAt: nowMillis(),
})
s := m.ensureSession(id)
s.WorkflowID = "chat"
s.Name = "chat"
m.selectedID = id
m.sessionEntered = true
m.appendRouter(id, RouterEntry{"user", text})
m.client.Send(protocol.StartChatSession(id, text))
m.clearInput()
return m, nil
@@ -569,7 +568,7 @@ func (m Model) submit() (tea.Model, tea.Cmd) {
hist = hist[len(hist)-50:]
}
m.history[sid] = hist
m.appendRouter(sid, RouterEntry{"user", text})
// No optimistic echo — the USER turn arrives as a chat.turn event.
m.client.Send(protocol.ChatInput(sid, text, m.chatMode))
m.clearInput()
return m, nil