diff --git a/src/components/thread/index.tsx b/src/components/thread/index.tsx
index 8a40a36..98ad684 100644
--- a/src/components/thread/index.tsx
+++ b/src/components/thread/index.tsx
@@ -200,6 +200,9 @@ export function Thread() {
};
const chatStarted = !!threadId || !!messages.length;
+ const hasNoAIOrToolMessages = !messages.find(
+ (m) => m.type === "ai" || m.type === "tool",
+ );
return (
@@ -350,6 +353,16 @@ export function Thread() {
/>
),
)}
+ {/* Special rendering case where there are no AI/tool messages, but there is an interrupt.
+ We need to render it outside of the messages list, since there are no messages to render */}
+ {hasNoAIOrToolMessages && !!stream.interrupt && (
+
+ )}
{isLoading && !firstTokenReceived && (
)}
diff --git a/src/components/thread/messages/ai.tsx b/src/components/thread/messages/ai.tsx
index 75a0b16..785978b 100644
--- a/src/components/thread/messages/ai.tsx
+++ b/src/components/thread/messages/ai.tsx
@@ -70,11 +70,12 @@ export function AssistantMessage({
isLoading,
handleRegenerate,
}: {
- message: Message;
+ message: Message | undefined;
isLoading: boolean;
handleRegenerate: (parentCheckpoint: Checkpoint | null | undefined) => void;
}) {
- const contentString = getContentString(message.content);
+ const content = message?.content ?? [];
+ const contentString = getContentString(content);
const [hideToolCalls] = useQueryState(
"hideToolCalls",
parseAsBoolean.withDefault(false),
@@ -82,15 +83,20 @@ export function AssistantMessage({
const thread = useStreamContext();
const isLastMessage =
- thread.messages[thread.messages.length - 1].id === message.id;
- const meta = thread.getMessagesMetadata(message);
- const interrupt = thread.interrupt;
+ thread.messages[thread.messages.length - 1].id === message?.id;
+ const hasNoAIOrToolMessages = !thread.messages.find(
+ (m) => m.type === "ai" || m.type === "tool",
+ );
+ const meta = message ? thread.getMessagesMetadata(message) : undefined;
+ const threadInterrupt = thread.interrupt;
+
const parentCheckpoint = meta?.firstSeenState?.parent_checkpoint;
- const anthropicStreamedToolCalls = Array.isArray(message.content)
- ? parseAnthropicStreamedToolCalls(message.content)
+ const anthropicStreamedToolCalls = Array.isArray(content)
+ ? parseAnthropicStreamedToolCalls(content)
: undefined;
const hasToolCalls =
+ message &&
"tool_calls" in message &&
message.tool_calls &&
message.tool_calls.length > 0;
@@ -100,7 +106,7 @@ export function AssistantMessage({
(tc) => tc.args && Object.keys(tc.args).length > 0,
);
const hasAnthropicToolCalls = !!anthropicStreamedToolCalls?.length;
- const isToolResult = message.type === "tool";
+ const isToolResult = message?.type === "tool";
if (isToolResult && hideToolCalls) {
return null;
@@ -130,14 +136,15 @@ export function AssistantMessage({
>
)}
-
- {isAgentInboxInterruptSchema(interrupt?.value) && isLastMessage && (
-
- )}
- {interrupt?.value &&
- !isAgentInboxInterruptSchema(interrupt.value) &&
+ {message &&
}
+ {isAgentInboxInterruptSchema(threadInterrupt?.value) &&
+ (isLastMessage || hasNoAIOrToolMessages) && (
+
+ )}
+ {threadInterrupt?.value &&
+ !isAgentInboxInterruptSchema(threadInterrupt.value) &&
isLastMessage ? (
-
+
) : null}