diff --git a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx index fa74ec3b7e..db86c1087b 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/ConversationTimeline.tsx @@ -211,13 +211,10 @@ const SmoothedResponse = memo<{ }); const ReadFileTimelineBlock = memo<{ - tools: readonly MergedTool[]; + tools: readonly [MergedTool, ...MergedTool[]]; }>(({ tools }) => { const [expanded, setExpanded] = useState(false); const [firstTool] = tools; - if (!firstTool) { - return null; - } if (tools.length === 1) { const readFile = getReadFileToolData(firstTool); @@ -376,17 +373,16 @@ export const BlockList: FC<{ ); case "tool-group": { - const groupTools = block.ids + const [firstGroupTool, ...restGroupTools] = block.ids .map((id) => toolByID.get(id)) .filter((tool) => tool !== undefined); - const [firstGroupTool] = groupTools; if (!firstGroupTool) { return null; } return ( ); } @@ -469,8 +465,10 @@ export const BlockList: FC<{ sources={block.sources} /> ); - default: - return null; + default: { + const _exhaustive: never = block; + return _exhaustive; + } } })} {remainingTools.map((tool) => ( diff --git a/site/src/pages/AgentsPage/components/ChatConversation/StreamingOutput.stories.tsx b/site/src/pages/AgentsPage/components/ChatConversation/StreamingOutput.stories.tsx index a467409a3a..2bd9b9d52c 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/StreamingOutput.stories.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/StreamingOutput.stories.tsx @@ -84,7 +84,6 @@ export const RetryRateLimited: Story = { attempt: 3, error: "Anthropic is rate limiting requests.", kind: "rate_limit", - delayMs: 3000, }), isAwaitingFirstStreamChunk: true, }), @@ -117,7 +116,6 @@ export const RetryInvalidTimestamp: Story = { attempt: 3, error: "Anthropic is rate limiting requests.", kind: "rate_limit", - delayMs: 3000, retryingAt: "not-a-date", }), isAwaitingFirstStreamChunk: true, diff --git a/site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.ts b/site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.ts index d955eacaa2..90d8102174 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/chatHelpers.ts @@ -46,8 +46,7 @@ export const extractContextUsageFromMessage = ( export const getLatestContextUsage = ( messages: readonly TypesGen.ChatMessage[], ): AgentContextUsage | null => { - for (let index = messages.length - 1; index >= 0; index -= 1) { - const message = messages[index]; + for (const message of messages.toReversed()) { const isCompactionSummary = message.content?.some( (part) => (part.type === "tool-call" || part.type === "tool-result") && @@ -65,16 +64,10 @@ export const getLatestContextUsage = ( return null; }; -type ChatWithHierarchyMetadata = TypesGen.Chat & { - readonly parent_chat_id?: string; -}; - export const getParentChatID = ( chat: TypesGen.Chat | undefined, ): string | undefined => { - return asNonEmptyString( - (chat as ChatWithHierarchyMetadata | undefined)?.parent_chat_id, - ); + return asNonEmptyString(chat?.parent_chat_id); }; export const resolveModelFromChatConfig = ( diff --git a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.createStore.test.ts b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.createStore.test.ts index 02ea146779..801524047e 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.createStore.test.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.createStore.test.ts @@ -279,7 +279,6 @@ describe("setRetryState / clearRetryState", () => { error: "rate limited", kind: "rate_limit", provider: "anthropic", - delayMs: 3000, retryingAt: "2025-01-01T00:00:30.000Z", }); expect(store.getSnapshot().retryState).toEqual({ @@ -287,7 +286,6 @@ describe("setRetryState / clearRetryState", () => { error: "rate limited", kind: "rate_limit", provider: "anthropic", - delayMs: 3000, retryingAt: "2025-01-01T00:00:30.000Z", }); @@ -589,7 +587,6 @@ describe("resetTransientState", () => { error: "rate limit", kind: "rate_limit", provider: "anthropic", - delayMs: 5000, retryingAt: "2025-01-01T00:01:00.000Z", }); store.setReconnectState({ diff --git a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.test.tsx b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.test.tsx index b4a87870b3..1d70610023 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.test.tsx +++ b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.test.tsx @@ -2405,7 +2405,6 @@ describe("useChatStore", () => { error: "upstream timeout", kind: "timeout", provider: "anthropic", - delayMs: 5000, retryingAt: "2025-01-01T00:01:00.000Z", }); }); @@ -2471,7 +2470,6 @@ describe("useChatStore", () => { error: "rate limited", kind: "rate_limit", provider: "anthropic", - delayMs: 3000, retryingAt: "2025-01-01T00:00:30.000Z", }); }); @@ -2551,7 +2549,6 @@ describe("useChatStore", () => { error: "rate limited", kind: "rate_limit", provider: "anthropic", - delayMs: 3000, retryingAt: "2025-01-01T00:00:30.000Z", }); }); diff --git a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.ts b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.ts index 5f82c01c0a..428e93cc08 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/chatStore.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/chatStore.ts @@ -91,17 +91,9 @@ export const chatMessagesEqualByValue = ( export const chatQueuedMessagesEqualByID = ( left: readonly TypesGen.ChatQueuedMessage[], right: readonly TypesGen.ChatQueuedMessage[], -): boolean => { - if (left.length !== right.length) { - return false; - } - for (let index = 0; index < left.length; index += 1) { - if (left[index]?.id !== right[index]?.id) { - return false; - } - } - return true; -}; +): boolean => + left.length === right.length && + left.every((message, index) => message.id === right[index].id); const retryStatesEqual = ( left: RetryState | null, @@ -118,7 +110,6 @@ const retryStatesEqual = ( left.error === right.error && left.kind === right.kind && left.provider === right.provider && - left.delayMs === right.delayMs && left.retryingAt === right.retryingAt ); }; diff --git a/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.test.ts b/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.test.ts index c35c0c7731..eedfc3464e 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.test.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.test.ts @@ -47,7 +47,6 @@ describe("deriveLiveStatus", () => { message: "Anthropic returned an unexpected error.", attempt: 2, provider: "anthropic", - delayMs: 2000, retryingAt: "2026-03-10T00:00:02.000Z", }; const reconnectingStatus = { diff --git a/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.ts b/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.ts index 88588b7aba..9672c0486a 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/liveStatusModel.ts @@ -21,7 +21,6 @@ export type LiveStatusModel = message: string; attempt: number; provider?: string; - delayMs?: number; retryingAt?: string; } & LiveStatusBase) | ({ @@ -76,7 +75,6 @@ const toRetryingLiveStatus = ( message: retryState.error, attempt: retryState.attempt, provider: retryState.provider, - delayMs: retryState.delayMs, retryingAt: retryState.retryingAt, }); diff --git a/site/src/pages/AgentsPage/components/ChatConversation/messageParsing.ts b/site/src/pages/AgentsPage/components/ChatConversation/messageParsing.ts index 3f48e6acfa..3eb647e4e4 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/messageParsing.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/messageParsing.ts @@ -111,11 +111,7 @@ export const getPendingToolCallIDs = ( } } - for (let index = messages.length - 1; index >= 0; index -= 1) { - const message = messages[index]; - if (!message) { - continue; - } + for (const message of messages.toReversed()) { if (message.role === "user") { return undefined; } diff --git a/site/src/pages/AgentsPage/components/ChatConversation/storyFixtures.ts b/site/src/pages/AgentsPage/components/ChatConversation/storyFixtures.ts index 3a7527d28d..2f70fcb80c 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/storyFixtures.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/storyFixtures.ts @@ -79,7 +79,6 @@ export const buildRetryState = ( error: "Anthropic returned an unexpected error.", kind: "generic", provider: "anthropic", - delayMs: 2000, retryingAt: "2026-03-10T00:00:02.000Z", ...overrides, }); diff --git a/site/src/pages/AgentsPage/components/ChatConversation/streamState.ts b/site/src/pages/AgentsPage/components/ChatConversation/streamState.ts index aa7b92b650..01c6c7e77e 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/streamState.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/streamState.ts @@ -24,7 +24,7 @@ export const applyMessagePartToStreamState = ( // Skip empty and whitespace-only deltas so they don't // create a non-null StreamState with empty blocks, which // would prematurely end the "starting" phase. - if (!part.text?.trim()) { + if (!part.text.trim()) { return prev; } return { @@ -33,7 +33,7 @@ export const applyMessagePartToStreamState = ( }; } case "reasoning": { - if (!part.text?.trim()) { + if (!part.text.trim()) { return prev; } return { diff --git a/site/src/pages/AgentsPage/components/ChatConversation/types.ts b/site/src/pages/AgentsPage/components/ChatConversation/types.ts index a0172abc4c..4a6b977676 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/types.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/types.ts @@ -73,7 +73,6 @@ export type RetryState = { error: string; kind: TypesGen.ChatErrorKind; provider?: string; - delayMs?: number; retryingAt?: string; }; diff --git a/site/src/pages/AgentsPage/components/ChatConversation/useChatStore.ts b/site/src/pages/AgentsPage/components/ChatConversation/useChatStore.ts index e887682d58..4bb8c72197 100644 --- a/site/src/pages/AgentsPage/components/ChatConversation/useChatStore.ts +++ b/site/src/pages/AgentsPage/components/ChatConversation/useChatStore.ts @@ -32,7 +32,6 @@ const normalizeRetryState = (retry: TypesGen.ChatStreamRetry): RetryState => ({ error: retry.error.trim() || "Retrying request shortly.", kind: retry.kind ?? "generic", provider: retry.provider?.trim() || undefined, - delayMs: retry.delay_ms, retryingAt: retry.retrying_at.trim() || undefined, }); @@ -556,9 +555,8 @@ export const useChatStore = ( pendingMessages.push(message); } if ( - message.id !== undefined && - (lastMessageIdRef.current === undefined || - message.id > lastMessageIdRef.current) + lastMessageIdRef.current === undefined || + message.id > lastMessageIdRef.current ) { lastMessageIdRef.current = message.id; } diff --git a/site/src/pages/AgentsPage/components/ChatPageContent.tsx b/site/src/pages/AgentsPage/components/ChatPageContent.tsx index 182bbcdaa3..830e6aa329 100644 --- a/site/src/pages/AgentsPage/components/ChatPageContent.tsx +++ b/site/src/pages/AgentsPage/components/ChatPageContent.tsx @@ -415,7 +415,7 @@ export const ChatPageInput: FC = ({ (b): b is TypesGen.ChatFilePart => b.type === "file", ); const files = fileBlocks.map((block, i) => { - const mt = block.media_type ?? "application/octet-stream"; + const mt = block.media_type; const ext = mt === "text/plain" ? "txt" : (mt.split("/")[1] ?? "png"); // Empty File used as a Map key only, its content is never // read because the existing file_id is reused at send time. diff --git a/site/src/pages/AgentsPage/components/QueuedMessagesList.tsx b/site/src/pages/AgentsPage/components/QueuedMessagesList.tsx index 81d6b394b0..d585200f8e 100644 --- a/site/src/pages/AgentsPage/components/QueuedMessagesList.tsx +++ b/site/src/pages/AgentsPage/components/QueuedMessagesList.tsx @@ -43,7 +43,7 @@ export const getQueuedMessageInfo = ( const fileBlocks = content.filter((p) => p.type === "file"); const textParts: string[] = []; for (const part of content) { - if (part.type === "text" && part.text?.trim()) { + if (part.type === "text" && part.text.trim()) { textParts.push(part.text); } }