diff --git a/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx b/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx
index 4388eac7eb1e..a70cfedd8e37 100644
--- a/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx
+++ b/apps/desktop/src/app/gateway/hooks/use-gateway-boot.test.tsx
@@ -134,6 +134,7 @@ const originalWebSocket = globalThis.WebSocket
beforeEach(() => {
// Drop any parked gateway left by a prior file/case (globalThis slot).
const leftover = takeGatewaySurvivor()
+
if (leftover) {
try {
leftover.gateway.close()
@@ -141,6 +142,7 @@ beforeEach(() => {
// ignore
}
}
+
vi.useFakeTimers()
FakeWebSocket.mode = 'open'
FakeWebSocket.instances = []
@@ -166,6 +168,7 @@ afterEach(() => {
// open gateway instead of tearing it down (the real HMR path). Drain + close
// that survivor so the next test boots a fresh socket instead of adoptBoot().
const survivor = takeGatewaySurvivor()
+
if (survivor) {
try {
survivor.gateway.close()
@@ -173,6 +176,7 @@ afterEach(() => {
// ignore
}
}
+
vi.useRealTimers()
;(globalThis as { WebSocket: unknown }).WebSocket = originalWebSocket
delete (window as { hermesDesktop?: unknown }).hermesDesktop
diff --git a/apps/desktop/src/app/session/hooks/use-message-stream/index.ts b/apps/desktop/src/app/session/hooks/use-message-stream/index.ts
index c921a13f0a14..53a17b193c61 100644
--- a/apps/desktop/src/app/session/hooks/use-message-stream/index.ts
+++ b/apps/desktop/src/app/session/hooks/use-message-stream/index.ts
@@ -512,11 +512,9 @@ export function useMessageStream({
// text merge — replaces the interim's text with the full final.)
const finalContinuesInterim = Boolean(
existing.interim &&
- finalText &&
- existingText &&
- (finalText === existingText ||
- finalText.startsWith(existingText) ||
- existingText.startsWith(finalText))
+ finalText &&
+ existingText &&
+ (finalText === existingText || finalText.startsWith(existingText) || existingText.startsWith(finalText))
)
if (existing.pending || (!interimBoundaryPending && finalText && existingText === finalText)) {
diff --git a/apps/desktop/src/lib/chat-messages.test.ts b/apps/desktop/src/lib/chat-messages.test.ts
index f7ef7d540f01..e86ba5593ecc 100644
--- a/apps/desktop/src/lib/chat-messages.test.ts
+++ b/apps/desktop/src/lib/chat-messages.test.ts
@@ -167,20 +167,20 @@ describe('toChatMessages', () => {
role: 'user',
content: 'opaque compaction payload',
display_kind: 'hidden',
- timestamp: 3,
+ timestamp: 3
},
{
role: 'user',
content: 'opaque model context payload',
display_kind: 'model_switch',
- timestamp: 4,
+ timestamp: 4
},
{
role: 'user',
content: 'opaque delegation context payload',
display_kind: 'async_delegation_complete',
- timestamp: 5,
- },
+ timestamp: 5
+ }
])
expect(messages.map(message => message.role)).toEqual(['user', 'assistant', 'system', 'system'])
@@ -188,7 +188,7 @@ describe('toChatMessages', () => {
'real user turn',
'real assistant reply',
'model changed',
- 'background agent work finished',
+ 'background agent work finished'
])
})
})
diff --git a/apps/desktop/src/lib/chat-messages.ts b/apps/desktop/src/lib/chat-messages.ts
index 4b20a6208dbf..00d1c73a64d4 100644
--- a/apps/desktop/src/lib/chat-messages.ts
+++ b/apps/desktop/src/lib/chat-messages.ts
@@ -313,9 +313,11 @@ function timelineDisplayContent(message: SessionMessage, content: string): strin
}
if (message.display_kind === 'async_delegation_complete') {
- const count = message.display_metadata && 'task_count' in message.display_metadata
- ? message.display_metadata.task_count
- : undefined
+ const count =
+ message.display_metadata && 'task_count' in message.display_metadata
+ ? message.display_metadata.task_count
+ : undefined
+
return count === undefined
? 'background agent work finished'
: `${count} background agent${count === 1 ? '' : 's'} finished`
@@ -905,14 +907,17 @@ export function toChatMessages(messages: SessionMessage[]): ChatMessage[] {
}
const content = message.content || message.text || message.context || message.name
+
const displayContent = transcriptContent(
message.display_kind,
timelineDisplayContent(message, displayContentForMessage(message.role, content))
)
+
const displayRole =
message.display_kind === 'model_switch' || message.display_kind === 'async_delegation_complete'
? 'system'
: message.role
+
const parts: ChatMessagePart[] = []
const reasoning =
diff --git a/apps/desktop/src/types/hermes.ts b/apps/desktop/src/types/hermes.ts
index a80d6ba8137c..0b93172c875b 100644
--- a/apps/desktop/src/types/hermes.ts
+++ b/apps/desktop/src/types/hermes.ts
@@ -425,7 +425,13 @@ export interface SessionInfo {
export type TimelineDisplayMetadata =
| { model: string; provider?: string }
- | { delegation_id: string; task_count: number; completed_count?: number; failed_count?: number; duration_seconds?: number }
+ | {
+ delegation_id: string
+ task_count: number
+ completed_count?: number
+ failed_count?: number
+ duration_seconds?: number
+ }
export interface SessionMessage {
codex_reasoning_items?: unknown
diff --git a/ui-tui/src/__tests__/messages.test.ts b/ui-tui/src/__tests__/messages.test.ts
index e9a3d4fdb2bf..d5baa1b31186 100644
--- a/ui-tui/src/__tests__/messages.test.ts
+++ b/ui-tui/src/__tests__/messages.test.ts
@@ -31,7 +31,7 @@ describe('toTranscriptMessages', () => {
const rows = [
{ role: 'user', text: 'visible prompt' },
{ role: 'user', text: '[CONTEXT COMPACTION — REFERENCE ONLY]', display_kind: 'hidden' },
- { role: 'assistant', text: 'visible reply' },
+ { role: 'assistant', text: 'visible reply' }
]
const result = toTranscriptMessages(rows)
@@ -43,14 +43,14 @@ describe('toTranscriptMessages', () => {
const rows = [
{ role: 'user', text: 'hello' },
{ role: 'user', text: '[System: model changed to gpt-5]', display_kind: 'model_switch' },
- { role: 'assistant', text: 'hi' },
+ { role: 'assistant', text: 'hi' }
]
const result = toTranscriptMessages(rows)
expect(result.map(msg => [msg.kind, msg.role, msg.text])).toEqual([
[undefined, 'user', 'hello'],
['event', 'system', 'model changed'],
- [undefined, 'assistant', 'hi'],
+ [undefined, 'assistant', 'hi']
])
})
@@ -58,8 +58,13 @@ describe('toTranscriptMessages', () => {
const rows = [
{ role: 'user', text: 'do work' },
{ role: 'assistant', text: 'done' },
- { role: 'user', text: '[IMPORTANT: delegation done]', display_kind: 'async_delegation_complete', display_metadata: { task_count: 3 } },
- { role: 'assistant', text: 'merged' },
+ {
+ role: 'user',
+ text: '[IMPORTANT: delegation done]',
+ display_kind: 'async_delegation_complete',
+ display_metadata: { task_count: 3 }
+ },
+ { role: 'assistant', text: 'merged' }
]
const result = toTranscriptMessages(rows)
@@ -67,14 +72,12 @@ describe('toTranscriptMessages', () => {
[undefined, 'do work'],
[undefined, 'done'],
['event', '3 background agents finished'],
- [undefined, 'merged'],
+ [undefined, 'merged']
])
})
it('projects async_delegation_complete without metadata as generic text', () => {
- const rows = [
- { role: 'user', text: 'event', display_kind: 'async_delegation_complete' },
- ]
+ const rows = [{ role: 'user', text: 'event', display_kind: 'async_delegation_complete' }]
const result = toTranscriptMessages(rows)
expect(result[0]?.kind).toBe('event')
diff --git a/ui-tui/src/components/messageLine.tsx b/ui-tui/src/components/messageLine.tsx
index fc42d4dc3f7f..7e0499d45694 100644
--- a/ui-tui/src/components/messageLine.tsx
+++ b/ui-tui/src/components/messageLine.tsx
@@ -131,7 +131,9 @@ export const MessageLine = memo(function MessageLine({
- ◈ {msg.text}
+
+ ◈ {msg.text}
+
)
}
diff --git a/ui-tui/src/domain/messages.ts b/ui-tui/src/domain/messages.ts
index 4f4ff426b95f..ba4340724d38 100644
--- a/ui-tui/src/domain/messages.ts
+++ b/ui-tui/src/domain/messages.ts
@@ -72,9 +72,12 @@ export const toTranscriptMessages = (rows: unknown): Msg[] => {
if (display_kind === 'async_delegation_complete') {
const meta = (row as TranscriptRow).display_metadata
const count = meta && typeof meta.task_count === 'number' ? meta.task_count : undefined
- const label = count === undefined
- ? 'background agent work finished'
- : `${count} background agent${count === 1 ? '' : 's'} finished`
+
+ const label =
+ count === undefined
+ ? 'background agent work finished'
+ : `${count} background agent${count === 1 ? '' : 's'} finished`
+
out.push({ kind: 'event', role: 'system', text: label })
pending = []