Fleet changelogs · dev.ecs0.net
rdmpw3275m-changelog-20260921-1305-resolve-feat-20260905-15-sse-streaming-client

rdmpw3275m-changelog-20260921-1305-resolve-feat-20260905-15-sse-streaming-client

ChatViewModel and FleetChatView adopted the SSE streaming transport (streamConversation), demoting the 2-second message polling loop to a recovery fallback, rendering real-time token deltas and interactive boundaries, and surviving daemon restarts with zero token duplication.

Scope & Context

Changes Made

  1. Adopted SSE Streaming Client in GUI (Sources/tyrell-app/ChatViewModel.swift):
    • Added @Published streaming state properties: isStreaming, streamingText, activeToolName, isExecutingTool, pendingApprovalID, streamError, streamReconnectAttempts.
    • Wired beginSelection(_:) to cancel existing streams and spawn startStreaming(for:generation:) consuming transport.streamConversation(conversationID) in an autonomous async loop.
    • Handled .token chunk kinds with replay deduplication (appendStreamToken with replay cursor), preventing duplicate token rendering upon stream reconnect or daemon restart.
    • Handled .toolStart and .toolFinish interactive boundaries, immediately exposing activeToolName and isExecutingTool to the UI.
    • Handled .approvalRequested chunks, immediately surfacing pendingApprovalID for supervisor action without waiting for ambient polling.
    • Handled .statusUpdate ("message_received") and .error chunks, triggering instant message reconciliation.
    • Handled .done chunks: seamlessly transitioning from the in-flight streaming bubble to the finalized authoritative message in messages without token duplication or flicker.
  2. Demoted Polling Path:
    • Removed the 2-second polling call refreshLatestMessages() from receiveUpdates().
    • Polling against /api/chat/conversations/{id}/messages is now demoted strictly to an initial room selection load, a reconnect/recovery fallback on stream drop/error, and a relaxed 10-second background sweep for room list and presence freshness.
  3. Timeline & Streaming UI (Sources/tyrell-app/FleetChatView.swift):
    • Added streamingRow displaying active streaming text with SignalGlass styling, live tool execution pill (TOOL: <name>, SignalGlassTone.cyan), and supervisor approval alert (APPROVAL: <id>, SignalGlassTone.warning).
    • Attached .onChange listeners on streamingText, isExecutingTool, and pendingApprovalID to automatically scroll the timeline to the in-flight stream indicator.
  4. Daemon SSE Enhancements (Sources/tyrelld/ChatStreamRoutes.swift & ChatRoutes.swift):
    • Added publishStatusUpdate(conversationID:status:) and publishTokenDirect(conversationID:token:) to ChatStreamHub.
    • Wired POST /api/chat/conversations/{id}/messages in ChatRoutes.swift to publish "message_received" status update to ChatStreamHub upon successful envelope commit.
  5. Comprehensive Test Suite (Tests/tyrell-appTests/ChatStreamAdoptionTests.swift):
    • Implemented 5 test suites covering all acceptance criteria:
      1. testChatViewModelConsumesStreamAndRendersTokenDeltas: Verifies real-time chunk consumption and final message transition.
      2. testToolCallAndApprovalBoundariesFlushAndAreObservable: Verifies immediate flush and UI observability of tool/approval boundaries.
      3. testStreamReconnectSurvivesDaemonRestartWithoutDuplicateTokens: Verifies stream reconnection and replay token deduplication.
      4. testPollingDemotedToRecoveryFallbackOnly: Verifies demotion of polling and activation on error.
      5. testFirstTokenLatencyImprovesAgainstPollingBaseline: Empirically benchmarks SSE streaming vs polling baseline.

Empirical Verification Evidence