Commit Graph

3 Commits

Author SHA1 Message Date
Markus Hartung
12228626c1 fix(conversation): show a placeholder for truncated bodies with no known message count
Follow-up to the earlier truncated-request-body transcript fix, found by
re-checking the live dashboard: a specific /v1/responses request still
showed nothing for its own turn even though its body genuinely was
truncated by logTruncation.ts's truncateForLog().

Root cause (two parts):

1. truncateForLog() only counted messages[] (Chat Completions) and
   contents[] (Gemini) — never input[] (Responses API) — so a truncated
   Responses API request's summary carried NO count field at all.

2. buildMultiRowConversation()'s earlier fix defaulted an unknown count to
   0, which silently produced "0 new turns" instead of surfacing that the
   count was simply unavailable — same end symptom as the original bug
   (nothing shown) despite the row being genuinely truncated.

Fixes:
- logTruncation.ts now also sets messageCount for input[] bodies (root
  fix, only helps requests logged from here forward).
- multiRowConversation.ts now distinguishes "known count" (existing
  specific "N messages not shown" placeholder + correct bookkeeping) from
  "unknown count" (a generic placeholder, since we can't safely diff
  against previousTotal without a real number) — needed for the
  already-persisted historical data on omniroute-dev that will never
  retroactively get a messageCount.

Test plan:
- New TDD tests for both gaps (Responses API count capture in
  logTruncation, unknown-count placeholder in multiRowConversation),
  confirmed failing before each fix and passing after
- npm run typecheck:core / npm run lint / npm run check:file-size — clean
- npm run test:unit — 27223 tests, same 4 pre-existing/unrelated failures
  as the last confirmed-clean run (no new regressions)
- npm run test:vitest — 291/291 passed
- Rebuilt and redeployed to omniroute-dev
2026-08-05 11:14:11 +02:00
Markus Hartung
b954a1cf76 fix(ci): resolve migration collision, file-size gate, and truncated-body transcript bug
CI failures on PR #9439:

1. Migration version collision: upstream/release/v3.8.50 landed
   134_proxy_logs_egress_ip.sql (#9291) after this branch's original rebase,
   colliding with this branch's own 134_agentic_conversations.sql. Renamed
   to 135_agentic_conversations.sql (re-rebased onto the current tip first).

2. check:file-size: rebaselined the files this PR's own feature growth pushed
   over their frozen/cap thresholds (RequestLoggerDetail.tsx, RequestTimeline.tsx,
   RequestLoggerV2.tsx, chat.ts, chatCore.ts — see the new
   _rebaseline_2026_08_04_9439 entry for the itemized justification) plus
   open-sse/executors/base.ts, which was already over its own frozen baseline
   on release/v3.8.50 independent of this branch (confirmed via `git diff
   upstream/release/v3.8.50 HEAD -- open-sse/executors/base.ts` — empty).

3. A third real bug, found by re-checking the live dashboard after the
   previous round's fixes: a request with a long real conversation chain
   showed only its own response in the "Full Conversation" panel. Root
   cause: open-sse/handlers/chatCore/logTruncation.ts's truncateForLog()
   replaces any request body over ~8KB with a bare {_truncated,
   _originalBytes, messageCount, ...} summary, dropping messages/input
   entirely — the norm, not the exception, for any conversation with real
   substance. buildRequestTurns() legitimately found nothing to parse, so
   the transcript silently rendered only that row's response, and (more
   subtly) every subsequent row's delta-slicing bookkeeping was computed
   against the wrong running total (0 instead of the row's real turn
   count), which would have corrupted the rest of the reconstruction too
   for any longer chain built on top of a truncated row.

   Fix: buildMultiRowConversation now detects a truncated request body via
   its messageCount field, uses that count for delta bookkeeping instead of
   silently treating it as zero, and renders one explicit placeholder turn
   ("N messages not shown — the request body was too large to log")
   instead of just disappearing.

Test plan:
- New regression tests for the truncation case (single truncated row, and
  a truncated row followed by a real row to verify bookkeeping stays
  correct)
- npm run check:migration-numbering / check:file-size — clean
- npm run typecheck:core / npm run lint — clean
- npm run test:unit — 27086 tests, only 4 failures remain (down from 18 —
  2 were fixed by the newer upstream commits pulled in by this re-rebase),
  all independently pre-existing/unrelated (ServiceSupervisor timing,
  monaco-editor path, npm-pack)
- npm run test:vitest — 291/291 passed
2026-08-05 11:14:11 +02:00
Markus Hartung
47b4a55f2d feat(dashboard): add agentic conversation tracking with live transcript view
Every agentic chat request now gets a conversation id (X-ConversationId
response header), and OmniRoute detects when a follow-up request continues
the same conversation via fingerprint + bounded prefix-hash matching, with a
strict-growth invariant to prevent false merges between independent
single-shot requests.

Dashboard changes:
- /dashboard/logs: toggleable Conversation column
- /dashboard/logs/timeline: same-conversation requests share a timeline lane,
  connected by an arrow, with a configurable lane-reuse window
- Request detail panel: new "Full Conversation" transcript above the raw SSE
  event stream, with Markdown rendering, per-turn timestamps, turn-relative
  view (only turns up to the one you opened, with a jump-to-next link),
  click-any-turn-to-open-its-log navigation, and live auto-refresh (with an
  auto-follow toggle, matching the event stream's autoscroll pattern) that
  rebuilds the transcript in real time from the in-flight SSE chunk buffer
  while a request is still streaming
- New /dashboard/conversations page listing only conversations with 2+ turns
- Configurable auto-refresh intervals on both the timeline and conversations
  list pages

Also fixes a pre-existing bug where the timeline view never showed SSE/
stream-chunk events or respected email-masking, because RequestTimeline.tsx
hardcoded debugEnabled/emailsVisible instead of reading the same
server-side/store state RequestLoggerV2.tsx already used, and makes the
request detail panel and conversations list responsive on mobile.
2026-08-05 11:14:11 +02:00