Skip to content

fix(desktop): treat draining assistant text as settled for live-only chrome - #507

Merged
Astro-Han merged 3 commits into
apache:mainfrom
sunheyi6:fix/distinguish-streaming-draining-phase
Jul 4, 2026
Merged

fix(desktop): treat draining assistant text as settled for live-only chrome#507
Astro-Han merged 3 commits into
apache:mainfrom
sunheyi6:fix/distinguish-streaming-draining-phase

Conversation

@sunheyi6

@sunheyi6 sunheyi6 commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

背景

当前会话在 assistant 文本进入 draining 阶段(最终文本正在经 smoother 平滑写入历史)时,仍被当作"活跃流式输出"处理,导致:

  • 会话事件流健康巡检被 draining 文本保活,无法及时收敛;
  • 侧栏会话脉冲在 draining 期间继续闪烁;
  • Composer 仍处于 streaming 禁用态,权限模式切换也被阻断;
  • complete 事件中 deferMessageRefresh 会跳过已提交历史的刷新,draining 期间看不到完整消息。

改动

将"活跃流式"的判定从 activeStreaming.length > 0 收紧为 activeStreamingLivephase === 'streaming' 才算活跃),让 draining 阶段视为已 settled:

  • app-shell.tsx
    • 新增 activeStreamingLive,用于 Composer streaming、权限模式禁用原因、侧栏 streamingSessionIds
    • 新增 effect:draining 中对应的 committed assistant 消息一旦进入 messages,主动调用 settleAssistantStreaming 清掉 draining slot,无需切换会话。
  • app-shell-session-events.ts:移除 deferMessageRefreshcomplete 始终刷新 refreshMessages
  • app-shell-effects.tsuseSessionEventHealthPolling 依赖 activeStreamingLive 而非 activeStreaming.length
  • app-shell-chat-actions.ts:新增 readMessagesWithSettleRetry[120, 360]ms 重试)缓解读模型 settle 竞态,refreshMessages 走该 helper。
  • 测试:同步更新 session-event-health / session-message-lifecycle-contract / session-status-presentation / streaming-handoff 四份契约测试,锁定新行为。

自测

  • 相关契约测试(streaming-handoff、session-event-health、session-message-lifecycle-contract、session-status-presentation)已随本改动同步更新并通过。

风险

  • 行为收紧:draining 阶段不再保活健康巡检、不再脉冲。若后台仍依赖该保活逻辑,需一并关注。

…chrome

Distinguish the streaming 'streaming' phase from the 'draining' phase so
final assistant text smoothing through the smoother no longer keeps
session event health polling alive, drives the sidebar pulse, disables
the composer, or blocks permission mode transitions.

- Introduce activeStreamingLive (phase === 'streaming') in app-shell.tsx
  and use it for composer streaming flag, permission mode disabled
  reason, and sidebar streamingSessionIds.
- Switch useSessionEventHealthPolling to depend on activeStreamingLive
  instead of activeStreaming.length, so draining text does not hold the
  event stream health checker open.
- Remove deferMessageRefresh in the 'complete' session event handler so
  committed history is always refreshed even while final text drains.
- Add an effect that settles the draining slot once the committed
  assistant message arrives in messages, without requiring a session
  switch.
- Add readMessagesWithSettleRetry with [120, 360]ms delays to tolerate
  read-model settle races during refreshMessages.
- Update session-event-health, session-message-lifecycle-contract,
  session-status-presentation, and streaming-handoff contract tests to
  lock in the new behavior.
@sunheyi6
sunheyi6 force-pushed the fix/distinguish-streaming-draining-phase branch from 9833b8c to 5fada48 Compare July 4, 2026 10:53

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I pushed a small follow-up for the remaining non-blocking comments before merge:

  • added a behavior test for complete firing before the committed assistant message is readable
  • scoped the settle retry to complete/draining refreshes that can require the committed assistant message id; ordinary refreshes stay single-read
  • kept the draining slot until that committed assistant message is readable

Verified:

  • npm --workspace @maka/desktop run typecheck
  • npm --workspace @maka/desktop test

@Astro-Han
Astro-Han merged commit ea48d7b into apache:main Jul 4, 2026
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.

2 participants