Mobile UX improvements + processing status indicator#164
Merged
Conversation
…enu items - Report Bug on mobile uses Launcher.Default.OpenAsync to open a pre-filled GitHub issue URL in the browser (desktop still uses gh CLI) - Hide Fix with Copilot, Copilot Console, Terminal, and VS Code menu items on mobile (they spawn local processes that don't exist on mobile) - Report Bug and Rename/Pin/Move/Close remain available on all platforms Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Track ProcessingStartedAt, TurnRoundCount, HasReceivedFirstEvent in AgentSessionInfo - Show 'Waiting for first response...' before any events arrive - Show 'Working · Xm Xs · N tool rounds...' during active processing - Display compact status in sidebar session list items - Sync processing fields via bridge for remote/mobile clients - Reset fields on SendPromptAsync, clear on CompleteResponse Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AbortSessionAsync now clears MessageQueue so queued messages don't auto-send after stop (fixes mobile stop button not working) - Clear ProcessingStartedAt/TurnRoundCount/HasReceivedFirstEvent on abort - Both local and remote-mode paths clear all processing fields - Add 3 new tests: default processing fields, set/clear, abort clears queue - Extend bridge serialization tests for processing status fields - Update copilot-instructions.md with processing status, abort behavior, mobile platform differences - Add 2 new scenarios: abort-clears-queue, processing-status-indicator Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace TurnRoundCount with ToolCallCount (increments on each ToolExecutionCompleteEvent instead of AssistantTurnEndEvent) - Replace HasReceivedFirstEvent bool with ProcessingPhase int: 0=Sending, 1=ServerConnected, 2=Thinking, 3=Working - Phase transitions: SendPromptAsync→0, SessionUsageInfoEvent→1, AssistantTurnStartEvent→2, ToolExecutionStartEvent→3 - UI shows: Sending → Server connected → Thinking → Working · Xm Xs · N tool calls - Update all tests, bridge sync, and documentation Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ToolCallCount: use Interlocked.Increment via backing field for thread-safe increment on background SDK event threads (4/4 reviewers) - Reset ProcessingStartedAt/ToolCallCount/ProcessingPhase on all error paths: SessionErrorEvent, watchdog timeout, SendAsync failures (Opus) - Fire OnStateChanged when ProcessingPhase transitions to Thinking/Working so sidebar updates beyond ServerConnected phase (Sonnet) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen
added a commit
that referenced
this pull request
Feb 21, 2026
Invariant audit found 4 pre-existing gaps where IsProcessing was cleared without resetting all companion fields: 1. CompleteResponse: missing ActiveToolCallCount reset 2. SendAsync reconnect+retry failure: missing IsResumed, HasUsedToolsThisTurn, ActiveToolCallCount, FlushCurrentResponse 3. SendAsync initial failure: same missing fields 4. Remote mode abort: missing IsResumed These are the exact same class of bug that caused regressions in PRs #148, #158, and #164. Every path that sets IsProcessing=false must also clear: IsResumed, HasUsedToolsThisTurn, ActiveToolCallCount, ProcessingStartedAt, ToolCallCount, ProcessingPhase, and FlushCurrentResponse. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen
added a commit
that referenced
this pull request
Feb 21, 2026
…edge Add comprehensive documentation of the recurring stuck-session bug pattern (7 PRs, 16 fix/regression cycles) to copilot-instructions.md: - Full cleanup checklist for all IsProcessing=false paths - Table of all 7 paths with locations - 7 common mistakes with PR references where each occurred - Staleness check and IsResumed clearing documentation - Cross-thread volatile field requirements - ProcessingGeneration guard explanation - Watchdog diagnostic log tag additions This knowledge was hard-won across PRs #141, #147, #148, #153, #158, #163, #164 and should prevent future regressions by making the invariants explicit and discoverable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PureWeen
added a commit
that referenced
this pull request
Feb 25, 2026
## Problem After app restart, resumed sessions that were mid-turn show **Thinking...** with a Stop button. The user must manually click Stop every time. The existing watchdog waited 600s (10 min!) before clearing stuck IsProcessing. ## Solution Add a **30s resume quiescence timeout** for sessions that receive zero SDK events after restart. If no events flow within 30s of app start, the session is cleared as stuck. ### Key design decisions (informed by 4-model consultation: Opus 4.6, Sonnet 4.6, Codex 5.3, GPT-5.1): 1. **30s quiescence** — short enough users don't wait, long enough for SDK reconnect (~5s typical, 6x safety margin) 2. **Event-gated** — only fires when \HasReceivedEventsSinceResume == false\. Once events start flowing, transitions to normal 120s/600s timeout tiers 3. **Seed from DateTime.UtcNow, NOT file time** — all 3 models independently flagged that seeding from events.jsonl would cause immediate kills for sessions >15s old (exact PR #148 regression pattern) 4. **Reuses existing watchdog fire path** — no new IsProcessing cleanup code, all 8 invariants preserved ### Timeout tiers (3-tier, was 2-tier): | Condition | Timeout | |-----------|---------| | Resumed, zero events since restart | **30s** (NEW) | | Normal processing, no tools | 120s | | Active tools / resumed with events / multi-agent | 600s | ## Tests - **16 new regression guard tests** covering quiescence edge cases, seed time safety, exhaustive timeout matrix - Updated existing tests to use \ComputeEffectiveTimeout\ helper mirroring production 3-tier formula - **108 total watchdog+recovery tests pass** ✅ ## Regression history context This code has been through 7 PRs of fix/regression cycles (PRs #141→#147→#148→#153→#158→#163→#164). The most relevant precedent: PR #148 added a 10s resume timeout that killed active sessions. Our 30s timeout avoids this by being event-gated and seeded from UtcNow. Fixes the 'click Stop on every restart' UX issue. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Mobile UX improvements, processing status indicator with multi-phase display, and abort fixes.
Mobile UX
PlatformHelper.IsDesktopguardsProcessing Status Indicator
ProcessingPhase(int 0-3) tracks current phase, advances onSessionUsageInfoEvent,AssistantTurnStartEvent,ToolExecutionStartEventToolCallCountincrements on eachToolExecutionCompleteEvent(accurate per-tool counting)ToolCallCountusesInterlocked.Incrementvia backing fieldSessionSummarybridge protocolAbort Fix
ProcessingStartedAt,ToolCallCount,ProcessingPhase)Tests (+3 new, 822 total)
NewSession_HasDefaultProcessingStatusFields— field defaultsProcessingStatusFields_CanBeSetAndCleared— lifecycleAbortSessionAsync_ClearsQueueAndProcessingStatus— abort clears queue + statusSerialize_CamelCaseNamingandSessionsListPayload_RoundTripfor new bridge fieldsScenarios (+2 new)
abort-clears-queue-and-statusprocessing-status-indicatorMulti-Model Review (2 Opus 4.6 + 2 Sonnet 4.6)
All 4 reviewers found ToolCallCount atomicity issue → fixed with Interlocked. Additional fixes: missing resets on error paths, sidebar phase update notifications.