Skip to content

Commit 813d5be

Browse files
jsell-rhclaude
andcommitted
fix(ambient-ui): address review — lazy export, orphan fix, build fix
- Defer message fetch for export to on-click (was eager on every render) - Remove non-deterministic orphan tool_result fallback (always standalone) - Replace useSearchParams with window.location to fix SSG build failure - Memoize adapter in useSendMessage via useRef - Remove dead TOOL_CALL_START/ARGS from _SKIP_TYPES - Add comment explaining empty assistant message drop Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 69e687a commit 813d5be

6 files changed

Lines changed: 32 additions & 24 deletions

File tree

components/ambient-ui/src/app/(dashboard)/[projectId]/fleet/[sessionId]/_components/session-header.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import { ExternalLink, Square, RotateCcw, Download, Trash2, MoreVertical } from
66
import type { DomainSession } from '@/domain/types'
77
import { getPreviewAnnotations } from '@/domain/annotations'
88
import { useStopSession, useStartSession, useDeleteSession } from '@/queries/use-sessions'
9-
import { useSessionMessages } from '@/queries/use-session-messages'
109
import { useSendFeedback } from '@/queries/use-send-feedback'
1110
import { PhaseBadge } from '../../_components/phase-badge'
1211
import { formatDuration, formatRelativeTime } from '@/lib/format-timestamp'
@@ -45,7 +44,6 @@ export function SessionHeader({ session }: { session: DomainSession }) {
4544
const startSession = useStartSession()
4645
const deleteSession = useDeleteSession()
4746
const sendFeedback = useSendFeedback()
48-
const { data: messages } = useSessionMessages(session.id)
4947

5048
const preview = getPreviewAnnotations(session.annotations)
5149
const canStop = STOPPABLE_PHASES.has(session.phase)
@@ -65,7 +63,11 @@ export function SessionHeader({ session }: { session: DomainSession }) {
6563
setDeleteDialogOpen(false)
6664
}, [deleteSession, session.id, router, projectId])
6765

68-
const handleExport = useCallback(() => {
66+
const handleExport = useCallback(async () => {
67+
const { createSessionMessagesAdapterWithFetch } = await import('@/adapters/session-messages')
68+
const adapter = createSessionMessagesAdapterWithFetch()
69+
const result = await adapter.list(session.id, { size: 1000 })
70+
6971
const exportData = {
7072
session: {
7173
id: session.id,
@@ -77,7 +79,7 @@ export function SessionHeader({ session }: { session: DomainSession }) {
7779
startTime: session.startTime,
7880
completionTime: session.completionTime,
7981
},
80-
messages: messages ?? [],
82+
messages: result.items,
8183
exportedAt: new Date().toISOString(),
8284
}
8385

@@ -92,7 +94,7 @@ export function SessionHeader({ session }: { session: DomainSession }) {
9294
link.click()
9395
document.body.removeChild(link)
9496
URL.revokeObjectURL(url)
95-
}, [session, messages])
97+
}, [session])
9698

9799
return (
98100
<>

components/ambient-ui/src/app/(dashboard)/[projectId]/fleet/[sessionId]/page.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use client'
22

3-
import { useParams, useSearchParams } from 'next/navigation'
3+
import { useState } from 'react'
4+
import { useParams } from 'next/navigation'
45
import { Skeleton } from '@/components/ui/skeleton'
56
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
67
import { useSession } from '@/queries/use-sessions'
@@ -11,11 +12,14 @@ import { ChatTab } from './_components/chat-tab'
1112

1213
export default function SessionDetailPage() {
1314
const { sessionId } = useParams<{ projectId: string; sessionId: string }>()
14-
const searchParams = useSearchParams()
15-
const activeTab = searchParams.get('tab') ?? 'phase'
15+
const [activeTab, setActiveTab] = useState(() => {
16+
if (typeof window === 'undefined') return 'phase'
17+
return new URL(window.location.href).searchParams.get('tab') ?? 'phase'
18+
})
1619
const { data: session, isLoading, error } = useSession(sessionId)
1720

1821
const handleTabChange = (value: string) => {
22+
setActiveTab(value)
1923
const url = new URL(window.location.href)
2024
url.searchParams.set('tab', value)
2125
window.history.replaceState({}, '', url.toString())
@@ -41,7 +45,7 @@ export default function SessionDetailPage() {
4145
return (
4246
<div className="space-y-6">
4347
<SessionHeader session={session} />
44-
<Tabs defaultValue={activeTab} onValueChange={handleTabChange}>
48+
<Tabs value={activeTab} onValueChange={handleTabChange}>
4549
<TabsList className="w-full *:flex-1">
4650
<TabsTrigger value="phase">Phase</TabsTrigger>
4751
<TabsTrigger value="logs">Logs</TabsTrigger>

components/ambient-ui/src/components/chat-messages.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ export function enrichMessages(messages: DomainSessionMessage[]): DomainSessionM
120120
}
121121
}
122122
}
123+
// Empty assistant messages with no extractable text are dropped — the
124+
// runner sometimes pushes an empty "assistant" record alongside a system
125+
// event that contains the actual response in last_assistant_message.
123126
continue
124127
}
125128
enriched.push(msg)
@@ -166,12 +169,7 @@ export function groupChatItems(messages: DomainSessionMessage[]): ChatItem[] {
166169
if (toolCallId && pendingToolUses.has(toolCallId)) {
167170
pendingToolUses.get(toolCallId)!.toolResult = msg
168171
} else {
169-
const lastPending = Array.from(pendingToolUses.values()).pop()
170-
if (lastPending && !lastPending.toolResult) {
171-
lastPending.toolResult = msg
172-
} else {
173-
items.push({ kind: 'message', message: msg })
174-
}
172+
items.push({ kind: 'message', message: msg })
175173
}
176174
} else {
177175
items.push({ kind: 'message', message: msg })

components/ambient-ui/src/components/chat-sidebar-context.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,9 @@ import {
55
useContext,
66
useState,
77
useCallback,
8-
useEffect,
98
useMemo,
109
type ReactNode,
1110
} from 'react'
12-
import { useSearchParams, useRouter, usePathname } from 'next/navigation'
1311

1412
type ChatSidebarState = {
1513
openSessionId: string | null
@@ -20,6 +18,11 @@ type ChatSidebarState = {
2018

2119
const ChatSidebarContext = createContext<ChatSidebarState | null>(null)
2220

21+
function readChatParam(): string | null {
22+
if (typeof window === 'undefined') return null
23+
return new URL(window.location.href).searchParams.get('chat')
24+
}
25+
2326
function updateChatParam(sessionId: string | null) {
2427
const url = new URL(window.location.href)
2528
if (sessionId) {
@@ -31,9 +34,7 @@ function updateChatParam(sessionId: string | null) {
3134
}
3235

3336
export function ChatSidebarProvider({ children }: { children: ReactNode }) {
34-
const searchParams = useSearchParams()
35-
const initialChat = searchParams.get('chat')
36-
const [openSessionId, setOpenSessionId] = useState<string | null>(initialChat)
37+
const [openSessionId, setOpenSessionId] = useState<string | null>(readChatParam)
3738

3839
const openSidebar = useCallback((sessionId: string) => {
3940
setOpenSessionId(sessionId)

components/ambient-ui/src/queries/use-send-message.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
'use client'
22

3+
import { useRef } from 'react'
34
import { useMutation, useQueryClient } from '@tanstack/react-query'
45
import type { SessionMessagesPort } from '@/ports/session-messages'
56
import { createSessionMessagesAdapterWithFetch } from '@/adapters/session-messages'
67
import { queryKeys } from './query-keys'
78

89
export function useSendMessage(sessionId: string, port?: SessionMessagesPort) {
9-
const adapter = port ?? createSessionMessagesAdapterWithFetch()
10+
const defaultPortRef = useRef<SessionMessagesPort | null>(null)
11+
if (!defaultPortRef.current && !port) {
12+
defaultPortRef.current = createSessionMessagesAdapterWithFetch()
13+
}
14+
const adapter = port ?? defaultPortRef.current!
1015
const queryClient = useQueryClient()
1116

1217
return useMutation({

components/runners/ambient-runner/ambient_runner/bridges/claude/operational_events.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,9 @@
3131
"RUN_ERROR": "error",
3232
}
3333

34-
# TOOL_CALL_START and TOOL_CALL_ARGS are accumulated, pushed on TOOL_CALL_END.
34+
# TOOL_CALL_START and TOOL_CALL_ARGS are handled by explicit early returns in consume().
3535
_SKIP_TYPES = frozenset(
3636
{
37-
"TOOL_CALL_START",
38-
"TOOL_CALL_ARGS",
3937
"TEXT_MESSAGE_START",
4038
"TEXT_MESSAGE_CONTENT",
4139
"TEXT_MESSAGE_END",

0 commit comments

Comments
 (0)