fix(office-ui): stop progress-row flicker and surface native role replies end to end
Two user-visible defects in company mode, root-caused via project 6666/8888 DB forensics: Progress-row flicker (thinking preview appearing/disappearing): progress entries were broadcast to clients before reaching the persistence buffer, so a tool_call-triggered session_detail snapshot rebuilt from the DB erased freshly streamed entries from the live log. Buffer now fills before the broadcast and session_detail flushes it before reading. Native role transcripts incomplete (thinking only at start, no narration, no final summary — external agents unaffected): - thinking deltas shared one stream id per conversation turn while seq reset per iteration, collapsing all iterations into one entry and silently dropping live thinking from iteration 2 on; now keyed per iteration like assistant deltas - assistant_delta events were mapped to None; company mode now surfaces them as streaming 'assistant' progress entries (rendered as Reply cards, merged like thinking, excluded from inline chat rows) - thinking was persisted one row per token, flooding the 1000-entry cap and evicting interleaved tool history; append_progress now folds streaming deltas per (type, turn, stream) with seq dedup - the terminal company turn was hidden at summary detail and, worse, its id-keyed backfill merge kept the first-inserted intermediate content, so the final reply never reached any channel; terminal turns are now flagged company_final_turn, visible at summary detail, and carry their own ui_message_id so they insert as fresh rows - appendProgressEntry applied its seq guard against unrelated entries when the stream key was absent from the log, killing the first delta of any fresh stream; the guard now only applies within the same stream Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -68,7 +68,7 @@ function canMergeProgress(left: ProgressEntry, right: ProgressEntry): boolean {
|
||||
if (leftKey && rightKey) return leftKey === rightKey
|
||||
if (right.timestamp - left.timestamp > STREAM_MERGE_WINDOW_MS) return false
|
||||
if (left.type !== right.type) return false
|
||||
if (left.type === 'thinking') return true
|
||||
if (left.type === 'thinking' || left.type === 'assistant') return true
|
||||
if (left.type === 'tool_call') return left.summary === right.summary
|
||||
return false
|
||||
}
|
||||
@@ -83,14 +83,15 @@ function isDuplicateProgress(left: ProgressEntry, right: ProgressEntry): boolean
|
||||
}
|
||||
|
||||
function mergeProgress(left: ProgressEntry, right: ProgressEntry): ProgressEntry {
|
||||
if (left.type === 'thinking') {
|
||||
if (left.type === 'thinking' || left.type === 'assistant') {
|
||||
// Merge detail text only: summary is a label/preview ("Thinking",
|
||||
// truncated excerpt), so falling back to it would splice label text
|
||||
// into the middle of the merged thinking stream.
|
||||
// into the middle of the merged stream. Assistant reply streams merge
|
||||
// the same way as thinking streams.
|
||||
const detail = mergeText(left.detail ?? '', right.detail ?? '', 'thinking')
|
||||
return {
|
||||
timestamp: right.timestamp,
|
||||
type: 'thinking',
|
||||
type: left.type,
|
||||
summary: summarizeThinking(detail, right.summary || left.summary),
|
||||
detail: detail || undefined,
|
||||
turnId: right.turnId ?? left.turnId,
|
||||
@@ -136,8 +137,12 @@ export function appendProgressEntry(
|
||||
const actualIndex = targetIndex >= 0 ? log.length - 1 - targetIndex : log.length - 1
|
||||
const last = log[actualIndex]
|
||||
if (!last) return [normalized]
|
||||
// The seq guard only applies within the SAME stream: when the key was not
|
||||
// found, `last` is an unrelated entry and a fresh stream legitimately
|
||||
// restarts at seq 1 (per-iteration thinking/assistant streams).
|
||||
if (
|
||||
normalizedKey
|
||||
&& targetIndex >= 0
|
||||
&& typeof last.seq === 'number'
|
||||
&& typeof normalized.seq === 'number'
|
||||
&& normalized.seq <= last.seq
|
||||
|
||||
Reference in New Issue
Block a user