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);
}
}