Skip to content

Persistent sessions, streaming fixes, stop button#3

Merged
PureWeen merged 1 commit intomainfrom
persistent-sessions-and-ui-fixes
Feb 6, 2026
Merged

Persistent sessions, streaming fixes, stop button#3
PureWeen merged 1 commit intomainfrom
persistent-sessions-and-ui-fixes

Conversation

@PureWeen
Copy link
Owner

@PureWeen PureWeen commented Feb 6, 2026

Summary

Adds persistent Copilot sessions that survive app restarts, fixes critical streaming/rendering bugs, and adds a stop button + settings page.

Features

  • Persistent sessions: Toggle between Embedded (default) and Persistent mode in Settings. Persistent mode spawns a detached copilot --headless --port N server that survives app restarts. Sessions auto-reconnect on relaunch.
  • Stop button: Red ■ button appears next to Send when a response is streaming. Calls SDK AbortAsync() to interrupt.
  • Settings page: New ⚙️ tab in sidebar to configure connection mode, start/stop persistent server.
  • Session reconnection indicator: Shows "🔄 Session reconnected" system message after resume.

Bug Fixes

  • Message duplication (10x+ repeated text): SDK fires AssistantMessageEvent once per stacked resume handler. Fixed by deduplicating on MessageId.
  • Vanishing messages: streamingContent was being cleared between turns in multi-turn agent loops. Now accumulates across turns until the full response completes.
  • Invisible text: Bootstrap's dark text color overriding our dark theme. Added explicit color: white to assistant message text and markdown body.
  • Green lines filling chat: Hundreds of individual tool call/reasoning cards rendering as thin colored lines. Replaced with single inline tool indicator for the currently running tool.
  • Per-turn delta tracking: HasReceivedDeltasThisTurn flag prevents double-append when both delta and full message events fire for the same turn.
  • Enter key inserting newline: JS interop handler prevents default on Enter (Shift+Enter for newlines).

New Files

  • Models/ConnectionSettings.cs — Connection mode enum, JSON persistence
  • Services/ServerManager.cs — Server lifecycle: spawn, detect, stop with PID file tracking
  • Components/Pages/Settings.razor — Settings UI
  • README.md — Project documentation

Technical Notes

  • Uses undocumented copilot --headless --port N for JSON-RPC TCP server (compatible with .NET SDK's CliUrl)
  • PID file at ~/.copilot/autopilot-server.pid for cross-restart server detection
  • Active sessions persisted to ~/.copilot/autopilot-active-sessions.json

Features:
- Persistent Copilot sessions that survive app restarts (headless TCP server mode)
- ServerManager: spawn/detect/stop detached copilot --headless servers with PID tracking
- Settings page: toggle between Embedded and Persistent connection modes
- Stop button to abort running chat responses (SDK AbortAsync)
- Session reconnection with '🔄 Session reconnected' indicator
- Auto-restore previous sessions on app launch

Streaming & rendering fixes:
- Fix message duplication: deduplicate AssistantMessageEvent by MessageId
  (SDK fires events N times for sessions resumed N times)
- Fix per-turn delta tracking: HasReceivedDeltasThisTurn flag prevents
  double-append when both delta and full message events fire
- Fix streaming content lifecycle: accumulate across turns, clear only
  when full response completes (no more vanishing messages)
- Remove tool call/reasoning cards from chat history (caused hundreds of
  thin green lines filling the screen). Show single inline tool indicator
  for currently running tool instead
- Add explicit color: white to assistant message text and markdown body
  (fixes invisible text from Bootstrap color override on dark background)

UI improvements:
- System message type and styling for reconnection indicators
- Enter key fix: JS interop prevents default on Enter without Shift
- Connection mode indicator in sidebar (Persistent/Embedded)
- Settings tab in sidebar navigation

New files:
- Models/ConnectionSettings.cs - Connection mode enum and settings persistence
- Services/ServerManager.cs - Server lifecycle management with PID files
- Components/Pages/Settings.razor - Connection settings UI
- README.md - Comprehensive project documentation
@PureWeen PureWeen merged commit fc7719f into main Feb 6, 2026
PureWeen added a commit that referenced this pull request Feb 20, 2026
Concurrency fixes:
- Swap _sessions before wiring event handler on reconnect (#2)
- Block ALL events from orphaned handlers, not just terminal (#3)
- Add lock(_imageQueueLock) to all image queue mutations (#4)
  including dequeue, reinsert, ClearQueue, rename, close, dispose
- Clear IsResumed on error and watchdog paths (#5)
- Add RunContinuationsAsynchronously to remaining TCS (#6)

Architecture/contract fixes:
- Add [JsonIgnore] to ShouldWarnOnStall, LastSimilarity (#7)
- Fix ConsecutiveErrors increment-before-check ordering (#8)
- Set IsCancelled on all non-success termination paths (#10)
  including stall, error-stall, max-iteration, OperationCanceled,
  empty-assignment error stall, and single-agent StopReflectionCycle
- Add session dir deletion for ghost evaluator pruning (#12)
- Add CompletedAt to StopReflectionCycle (#12 related)

Already correct (no changes needed):
- #9: CurrentIteration == 1 check was already fixed
- #11: Comments already reference string-based stall detection

Documentation:
- Update stall detection from 'hash match' to 'string equality'
- Update error handling to show ConsecutiveErrors (not ConsecutiveStalls)
- Add IsCancelled invariant to exit conditions table
- Add 5 new invariants: orphan gate, reconnect ordering,
  image queue locking, IsResumed clearing, TCS creation
- Document empty-assignment retry behavior

817/817 tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@PureWeen PureWeen deleted the persistent-sessions-and-ui-fixes branch February 22, 2026 00:16
PureWeen added a commit that referenced this pull request Feb 26, 2026
1. Move FlushCurrentResponse inside Invoke() lambda in AssistantTurnEndEvent
   handler to avoid racing with UI thread on List<ChatMessage> (Finding #1)

2. Preserve isRecentlyActive across catch block in GetEventsFileRestoreHints
   — malformed JSON in a recently-written events.jsonl no longer loses the
   file-age signal that bypasses the 30s quiescence timeout (Finding #2)

3. Add Debug log when dedup guard fires in FlushCurrentResponse so
   legitimate content drops are observable in diagnostics (Finding #3)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant