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:
LZH-YS1998
2026-07-09 20:46:14 +08:00
parent 35fa717cf6
commit a0402522da
13 changed files with 461 additions and 274 deletions
+25 -2
View File
@@ -80,9 +80,32 @@ class WSHandlerProgressParsingTests(unittest.TestCase):
self.assertIsNone(entry)
def test_runtime_assistant_delta_is_not_recorded_as_progress_entry(self) -> None:
def test_company_assistant_delta_becomes_assistant_progress_entry(self) -> None:
# Company mode surfaces the role's narration/final reply in its
# progress transcript (parity with external agents' [External:*:result]).
entry = WSHandler._runtime_event_to_progress_entry(
{"type": "assistant_delta", "text": "final answer token"},
{
"type": "assistant_delta",
"text": "final answer token",
"execution_mode": "company_mode",
},
)
self.assertIsNotNone(entry)
assert entry is not None
self.assertEqual(entry["type"], "assistant")
self.assertEqual(entry["summary"], "final answer token")
self.assertEqual(entry["detail"], "final answer token")
def test_task_mode_assistant_delta_is_not_recorded_as_progress_entry(self) -> None:
# Task mode already streams the reply as the draft; a progress entry
# would duplicate it.
entry = WSHandler._runtime_event_to_progress_entry(
{
"type": "assistant_delta",
"text": "final answer token",
"execution_mode": "task_mode",
},
)
self.assertIsNone(entry)