diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 17099ab8..f74ccec1 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -5,7 +5,7 @@ use goose::config::permission::PermissionLevel; use goose::config::ExtensionEntry; use goose::message::{ ContextLengthExceeded, FrontendToolRequest, Message, MessageContent, RedactedThinkingContent, - ThinkingContent, ToolConfirmationRequest, ToolRequest, ToolResponse, + SummarizationRequested, ThinkingContent, ToolConfirmationRequest, ToolRequest, ToolResponse, }; use goose::permission::permission_confirmation::PrincipalType; use goose::providers::base::{ConfigKey, ModelInfo, ProviderMetadata}; @@ -70,6 +70,7 @@ use utoipa::OpenApi; FrontendToolRequest, ResourceContents, ContextLengthExceeded, + SummarizationRequested, Role, ProviderMetadata, ExtensionEntry, diff --git a/crates/goose/src/message.rs b/crates/goose/src/message.rs index 81be5519..87c4ae9a 100644 --- a/crates/goose/src/message.rs +++ b/crates/goose/src/message.rs @@ -91,6 +91,11 @@ pub struct ContextLengthExceeded { pub msg: String, } +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)] +pub struct SummarizationRequested { + pub msg: String, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, ToSchema)] /// Content passed inside a message, which can be both simple content and tool content #[serde(tag = "type", rename_all = "camelCase")] @@ -104,6 +109,7 @@ pub enum MessageContent { Thinking(ThinkingContent), RedactedThinking(RedactedThinkingContent), ContextLengthExceeded(ContextLengthExceeded), + SummarizationRequested(SummarizationRequested), } impl MessageContent { @@ -172,6 +178,19 @@ impl MessageContent { MessageContent::ContextLengthExceeded(ContextLengthExceeded { msg: msg.into() }) } + pub fn summarization_requested>(msg: S) -> Self { + MessageContent::SummarizationRequested(SummarizationRequested { msg: msg.into() }) + } + + // Add this new method to check for summarization requested content + pub fn as_summarization_requested(&self) -> Option<&SummarizationRequested> { + if let MessageContent::SummarizationRequested(ref summarization_requested) = self { + Some(summarization_requested) + } else { + None + } + } + pub fn as_tool_request(&self) -> Option<&ToolRequest> { if let MessageContent::ToolRequest(ref tool_request) = self { Some(tool_request) @@ -451,6 +470,11 @@ impl Message { .iter() .all(|c| matches!(c, MessageContent::Text(_))) } + + /// Add summarization requested to the message + pub fn with_summarization_requested>(self, msg: S) -> Self { + self.with_content(MessageContent::summarization_requested(msg)) + } } #[cfg(test)] diff --git a/crates/goose/src/providers/formats/anthropic.rs b/crates/goose/src/providers/formats/anthropic.rs index ad17a5ae..55a6cabd 100644 --- a/crates/goose/src/providers/formats/anthropic.rs +++ b/crates/goose/src/providers/formats/anthropic.rs @@ -63,6 +63,9 @@ pub fn format_messages(messages: &[Message]) -> Vec { MessageContent::ContextLengthExceeded(_) => { // Skip } + MessageContent::SummarizationRequested(_) => { + // Skip + } MessageContent::Thinking(thinking) => { content.push(json!({ "type": "thinking", diff --git a/crates/goose/src/providers/formats/bedrock.rs b/crates/goose/src/providers/formats/bedrock.rs index c645fb44..3c0ea40c 100644 --- a/crates/goose/src/providers/formats/bedrock.rs +++ b/crates/goose/src/providers/formats/bedrock.rs @@ -45,6 +45,9 @@ pub fn to_bedrock_message_content(content: &MessageContent) -> Result { bail!("ContextLengthExceeded should not get passed to the provider") } + MessageContent::SummarizationRequested(_) => { + bail!("SummarizationRequested should not get passed to the provider") + } MessageContent::ToolRequest(tool_req) => { let tool_use_id = tool_req.id.to_string(); let tool_use = if let Ok(call) = tool_req.tool_call.as_ref() { diff --git a/crates/goose/src/providers/formats/databricks.rs b/crates/goose/src/providers/formats/databricks.rs index 6f0d8548..6e463476 100644 --- a/crates/goose/src/providers/formats/databricks.rs +++ b/crates/goose/src/providers/formats/databricks.rs @@ -113,6 +113,9 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec< MessageContent::ContextLengthExceeded(_) => { continue; } + MessageContent::SummarizationRequested(_) => { + continue; + } MessageContent::ToolResponse(response) => { match &response.tool_result { Ok(contents) => { diff --git a/crates/goose/src/providers/formats/openai.rs b/crates/goose/src/providers/formats/openai.rs index b05626fd..662456a5 100644 --- a/crates/goose/src/providers/formats/openai.rs +++ b/crates/goose/src/providers/formats/openai.rs @@ -55,6 +55,9 @@ pub fn format_messages(messages: &[Message], image_format: &ImageFormat) -> Vec< MessageContent::ContextLengthExceeded(_) => { continue; } + MessageContent::SummarizationRequested(_) => { + continue; + } MessageContent::ToolRequest(request) => match &request.tool_call { Ok(tool_call) => { let sanitized_name = sanitize_function_name(&tool_call.name); diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 8da7ed22..fb14d669 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -1244,6 +1244,27 @@ } } ] + }, + { + "allOf": [ + { + "$ref": "#/components/schemas/SummarizationRequested" + }, + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "summarizationRequested" + ] + } + } + } + ] } ], "description": "Content passed inside a message, which can be both simple content and tool content", @@ -1571,6 +1592,17 @@ } } }, + "SummarizationRequested": { + "type": "object", + "required": [ + "msg" + ], + "properties": { + "msg": { + "type": "string" + } + } + }, "TextContent": { "type": "object", "required": [ diff --git a/ui/desktop/src/App.tsx b/ui/desktop/src/App.tsx index 405c7bbe..980de584 100644 --- a/ui/desktop/src/App.tsx +++ b/ui/desktop/src/App.tsx @@ -503,9 +503,6 @@ export default function App() { // If GOOSE_ALLOWLIST_WARNING is true, use warning-only mode (STRICT_ALLOWLIST=false) // If GOOSE_ALLOWLIST_WARNING is not set or false, use strict blocking mode (STRICT_ALLOWLIST=true) const STRICT_ALLOWLIST = config.GOOSE_ALLOWLIST_WARNING === true ? false : true; - console.log( - `Extension security mode: ${STRICT_ALLOWLIST ? 'Strict' : 'Warning-only'} (GOOSE_ALLOWLIST_WARNING=${config.GOOSE_ALLOWLIST_WARNING})` - ); useEffect(() => { console.log('Setting up extension handler'); diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index cbbf895f..6f0b909b 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -196,6 +196,8 @@ export type MessageContent = (TextContent & { type: 'redactedThinking'; }) | (ContextLengthExceeded & { type: 'contextLengthExceeded'; +}) | (SummarizationRequested & { + type: 'summarizationRequested'; }); /** @@ -360,6 +362,10 @@ export type SessionMetadata = { working_dir: string; }; +export type SummarizationRequested = { + msg: string; +}; + export type TextContent = { annotations?: Annotations | null; text: string; diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index 0141b588..c5569d7d 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -6,6 +6,7 @@ import { Attach, Send } from './icons'; import { debounce } from 'lodash'; import BottomMenu from './bottom_menu/BottomMenu'; import { LocalMessageStorage } from '../utils/localMessageStorage'; +import { Message } from '../types/message'; interface ChatInputProps { handleSubmit: (e: React.FormEvent) => void; @@ -16,6 +17,9 @@ interface ChatInputProps { droppedFiles?: string[]; setView: (view: View) => void; numTokens?: number; + hasMessages?: boolean; + messages?: Message[]; + setMessages: (messages: Message[]) => void; } export default function ChatInput({ @@ -27,6 +31,8 @@ export default function ChatInput({ setView, numTokens, droppedFiles = [], + messages = [], + setMessages, }: ChatInputProps) { const [_value, setValue] = useState(initialValue); const [displayValue, setDisplayValue] = useState(initialValue); // For immediate visual feedback @@ -327,7 +333,13 @@ export default function ChatInput({ - + diff --git a/ui/desktop/src/components/ChatView.tsx b/ui/desktop/src/components/ChatView.tsx index 4d7636f4..dd69b5cf 100644 --- a/ui/desktop/src/components/ChatView.tsx +++ b/ui/desktop/src/components/ChatView.tsx @@ -22,8 +22,8 @@ import { Recipe } from '../recipe'; import { ChatContextManagerProvider, useChatContextManager, -} from './context_management/ContextManager'; -import { ContextLengthExceededHandler } from './context_management/ContextLengthExceededHandler'; +} from './context_management/ChatContextManager'; +import { ContextHandler } from './context_management/ContextHandler'; import { LocalMessageStorage } from '../utils/localMessageStorage'; import { Message, @@ -105,7 +105,8 @@ function ChatContent({ resetMessagesWithSummary, closeSummaryModal, updateSummary, - hasContextLengthExceededContent, + hasContextHandlerContent, + getContextHandlerType, } = useChatContextManager(); useEffect(() => { @@ -521,16 +522,29 @@ function ChatContent({ data-testid="message-container" > {isUserMessage(message) ? ( - - ) : ( <> - {/* Only render GooseMessage if it's not a CLE message */} - {hasContextLengthExceededContent(message) ? ( - + ) : ( + + )} + + ) : ( + <> + {/* Only render GooseMessage if it's not a message invoking some context management */} + {hasContextHandlerContent(message) ? ( + ) : ( diff --git a/ui/desktop/src/components/bottom_menu/BottomMenu.tsx b/ui/desktop/src/components/bottom_menu/BottomMenu.tsx index 28fbe2f6..061340fa 100644 --- a/ui/desktop/src/components/bottom_menu/BottomMenu.tsx +++ b/ui/desktop/src/components/bottom_menu/BottomMenu.tsx @@ -12,6 +12,8 @@ import { BottomMenuModeSelection } from './BottomMenuModeSelection'; import ModelsBottomBar from '../settings_v2/models/bottom_bar/ModelsBottomBar'; import { useConfig } from '../ConfigContext'; import { getCurrentModelAndProvider } from '../settings_v2/models'; +import { Message } from '../../types/message'; +import { ManualSummarizeButton } from '../context_management/ManualSummaryButton'; const TOKEN_LIMIT_DEFAULT = 128000; // fallback for custom models that the backend doesn't know about const TOKEN_WARNING_THRESHOLD = 0.8; // warning shows at 80% of the token limit @@ -20,9 +22,15 @@ const TOOLS_MAX_SUGGESTED = 60; // max number of tools before we show a warning export default function BottomMenu({ setView, numTokens = 0, + messages = [], + isLoading = false, + setMessages, }: { setView: (view: View, viewOptions?: ViewOptions) => void; numTokens?: number; + messages?: Message[]; + isLoading?: boolean; + setMessages: (messages: Message[]) => void; }) { const [isModelMenuOpen, setIsModelMenuOpen] = useState(false); const { currentModel } = useModel(); @@ -267,6 +275,18 @@ export default function BottomMenu({ {/* Goose Mode Selector Dropdown */} + + {/* Summarize Context Button - ADD THIS */} + {messages.length > 0 && ( + <> +
+ + + )}
); diff --git a/ui/desktop/src/components/context_management/ContextManager.tsx b/ui/desktop/src/components/context_management/ChatContextManager.tsx similarity index 77% rename from ui/desktop/src/components/context_management/ContextManager.tsx rename to ui/desktop/src/components/context_management/ChatContextManager.tsx index 05bc0280..9e8e666b 100644 --- a/ui/desktop/src/components/context_management/ContextManager.tsx +++ b/ui/desktop/src/components/context_management/ChatContextManager.tsx @@ -1,6 +1,10 @@ import React, { createContext, useContext, useState } from 'react'; import { Message } from '../../types/message'; -import { manageContextFromBackend, convertApiMessageToFrontendMessage } from './index'; +import { + manageContextFromBackend, + convertApiMessageToFrontendMessage, + createSummarizationRequestMessage, +} from './index'; // Define the context management interface interface ChatContextManagerState { @@ -9,10 +13,10 @@ interface ChatContextManagerState { isSummaryModalOpen: boolean; isLoadingSummary: boolean; errorLoadingSummary: boolean; + preparingManualSummary: boolean; } interface ChatContextManagerActions { - fetchSummary: (messages: Message[]) => Promise; updateSummary: (newSummaryContent: string) => void; resetMessagesWithSummary: ( messages: Message[], @@ -23,12 +27,15 @@ interface ChatContextManagerActions { ) => void; openSummaryModal: () => void; closeSummaryModal: () => void; + hasContextHandlerContent: (message: Message) => boolean; hasContextLengthExceededContent: (message: Message) => boolean; - handleContextLengthExceeded: ( + hasSummarizationRequestedContent: (message: Message) => boolean; + getContextHandlerType: (message: Message) => 'contextLengthExceeded' | 'summarizationRequested'; + handleContextLengthExceeded: (messages: Message[]) => Promise; + handleManualSummarization: ( messages: Message[], - chatId: string, - workingDir: string - ) => Promise; + setMessages: (messages: Message[]) => void + ) => void; } // Create the context @@ -45,10 +52,12 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }> const [isSummaryModalOpen, setIsSummaryModalOpen] = useState(false); const [isLoadingSummary, setIsLoadingSummary] = useState(false); const [errorLoadingSummary, setErrorLoadingSummary] = useState(false); + const [preparingManualSummary, setPreparingManualSummary] = useState(false); const handleContextLengthExceeded = async (messages: Message[]): Promise => { setIsLoadingSummary(true); setErrorLoadingSummary(false); + setPreparingManualSummary(true); try { // 2. Now get the summary from the backend @@ -75,38 +84,25 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }> console.error('Error handling context length exceeded:', err); setErrorLoadingSummary(true); setIsLoadingSummary(false); + } finally { + setPreparingManualSummary(false); } }; - const fetchSummary = async (messages: Message[]) => { - setIsLoadingSummary(true); - setErrorLoadingSummary(false); + const handleManualSummarization = ( + messages: Message[], + setMessages: (messages: Message[]) => void + ): void => { + // add some messages to the message thread + // these messages will be filtered out in chat view + // but they will also be what allows us to render some text in the chatview itself, similar to CLE events + const summarizationRequest = createSummarizationRequestMessage( + messages, + 'Summarize the session and begin a new one' + ); - try { - const response = await manageContextFromBackend({ - messages: messages, - manageAction: 'summarize', - }); - - // Convert API messages to frontend messages - const convertedMessages = response.messages.map( - (apiMessage) => convertApiMessageToFrontendMessage(apiMessage, false, true) // do not show to user but send to llm - ); - - // Extract the summary text from the first message - const summaryMessage = convertedMessages[0].content[0]; - if (summaryMessage.type === 'text') { - const summary = summaryMessage.text; - setSummaryContent(summary); - setSummarizedThread(convertedMessages); - } - - setIsLoadingSummary(false); - } catch (err) { - console.error('Error fetching summary:', err); - setErrorLoadingSummary(true); - setIsLoadingSummary(false); - } + // add the message to the message thread + setMessages([...messages, summarizationRequest]); }; const updateSummary = (newSummaryContent: string) => { @@ -212,10 +208,27 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }> setSummaryContent(''); }; + const hasContextHandlerContent = (message: Message): boolean => { + return hasContextLengthExceededContent(message) || hasSummarizationRequestedContent(message); + }; + const hasContextLengthExceededContent = (message: Message): boolean => { return message.content.some((content) => content.type === 'contextLengthExceeded'); }; + const hasSummarizationRequestedContent = (message: Message): boolean => { + return message.content.some((content) => content.type === 'summarizationRequested'); + }; + + const getContextHandlerType = ( + message: Message + ): 'contextLengthExceeded' | 'summarizationRequested' => { + if (hasContextLengthExceededContent(message)) { + return 'contextLengthExceeded'; + } + return 'summarizationRequested'; + }; + const openSummaryModal = () => { setIsSummaryModalOpen(true); }; @@ -231,15 +244,19 @@ export const ChatContextManagerProvider: React.FC<{ children: React.ReactNode }> isSummaryModalOpen, isLoadingSummary, errorLoadingSummary, + preparingManualSummary, // Actions - fetchSummary, updateSummary, resetMessagesWithSummary, openSummaryModal, closeSummaryModal, + hasContextHandlerContent, hasContextLengthExceededContent, + hasSummarizationRequestedContent, + getContextHandlerType, handleContextLengthExceeded, + handleManualSummarization, }; return ( diff --git a/ui/desktop/src/components/context_management/ContextLengthExceededHandler.tsx b/ui/desktop/src/components/context_management/ContextHandler.tsx similarity index 59% rename from ui/desktop/src/components/context_management/ContextLengthExceededHandler.tsx rename to ui/desktop/src/components/context_management/ContextHandler.tsx index 484192a9..b27b1f00 100644 --- a/ui/desktop/src/components/context_management/ContextLengthExceededHandler.tsx +++ b/ui/desktop/src/components/context_management/ContextHandler.tsx @@ -1,19 +1,21 @@ import React, { useState, useRef, useEffect } from 'react'; import { Message } from '../../types/message'; -import { useChatContextManager } from './ContextManager'; +import { useChatContextManager } from './ChatContextManager'; -interface ContextLengthExceededHandlerProps { +interface ContextHandlerProps { messages: Message[]; messageId: string; chatId: string; workingDir: string; + contextType: 'contextLengthExceeded' | 'summarizationRequested'; } -export const ContextLengthExceededHandler: React.FC = ({ +export const ContextHandler: React.FC = ({ messages, messageId, chatId, workingDir, + contextType, }) => { const { summaryContent, @@ -22,10 +24,11 @@ export const ContextLengthExceededHandler: React.FC { + handleContextLengthExceeded(messages).catch((err) => { console.error('Error handling context length exceeded:', err); }); }; @@ -109,8 +112,16 @@ export const ContextLengthExceededHandler: React.FC ( <> - {`Your conversation has exceeded the model's context capacity`} - {`This conversation has too much information to continue. Extension data often takes up significant space.`} + + {isContextLengthExceeded + ? `Your conversation has exceeded the model's context capacity` + : `Summarization requested`} + + + {isContextLengthExceeded + ? `This conversation has too much information to continue. Extension data often takes up significant space.` + : `Summarization failed. Continue chatting or start a new session.`} + + + {isContextLengthExceeded + ? `Your conversation has exceeded the model's context capacity and a summary was prepared.` + : `A summary of your conversation was prepared as requested.`} + + + {isContextLengthExceeded + ? `Messages above this line remain viewable but specific details are not included in active context.` + : `This summary includes key points from your conversation.`} + + {shouldAllowSummaryInteraction && ( + + )} ); + // Render persistent summarized notification when we shouldn't show interaction options + const renderPersistentMarker = () => ( + + Session summarized — messages above this line are not included in the conversation + + ); + const renderContentState = () => { - if (!shouldAllowSummaryInteraction) { - return null; + // If this is not the latest context event message but we have a valid summary, + // show the persistent marker + if (!shouldAllowSummaryInteraction && summaryContent) { + return renderPersistentMarker(); } - if (errorLoadingSummary) { - return retryCount >= 2 ? renderFailedState() : renderRetryState(); + // For the latest message with the context event + if (shouldAllowSummaryInteraction) { + if (errorLoadingSummary) { + return retryCount >= 2 ? renderFailedState() : renderRetryState(); + } + + if (summaryContent) { + return renderSuccessState(); + } } - return renderSuccessState(); + // Fallback to showing at least the persistent marker + return renderPersistentMarker(); }; return ( diff --git a/ui/desktop/src/components/context_management/ManualSummaryButton.tsx b/ui/desktop/src/components/context_management/ManualSummaryButton.tsx new file mode 100644 index 00000000..20a26c60 --- /dev/null +++ b/ui/desktop/src/components/context_management/ManualSummaryButton.tsx @@ -0,0 +1,97 @@ +import React, { useState } from 'react'; +import { ScrollText } from 'lucide-react'; +import Modal from '../Modal'; +import { Button } from '../ui/button'; +import { useChatContextManager } from './ChatContextManager'; +import { Message } from '../../types/message'; + +interface ManualSummarizeButtonProps { + messages: Message[]; + isLoading?: boolean; // need this prop to know if Goose is responding + setMessages: (messages: Message[]) => void; // context management is triggered via special message content types +} + +export const ManualSummarizeButton: React.FC = ({ + messages, + isLoading = false, + setMessages, +}) => { + const { handleManualSummarization, isLoadingSummary } = useChatContextManager(); + + const [isConfirmationOpen, setIsConfirmationOpen] = useState(false); + + const handleClick = () => { + setIsConfirmationOpen(true); + }; + + const handleSummarize = async () => { + setIsConfirmationOpen(false); + + try { + handleManualSummarization(messages, setMessages); + } catch (error) { + console.error('Error in handleSummarize:', error); + } + }; + + // Footer content for the confirmation modal + const footerContent = ( + <> + + + + ); + + return ( + <> +
+ +
+ + {/* Confirmation Modal */} + {isConfirmationOpen && ( + setIsConfirmationOpen(false)}> +
+
+ +
+
+

Summarize Conversation

+
+
+ +
+

+ This will summarize your conversation history to save context space. +

+

+ Previous messages will remain visible but only the summary will be included in the + active context for Goose. This is useful for long conversations that are approaching + the context limit. +

+
+
+ )} + + ); +}; diff --git a/ui/desktop/src/components/context_management/index.ts b/ui/desktop/src/components/context_management/index.ts index aef55632..547b6652 100644 --- a/ui/desktop/src/components/context_management/index.ts +++ b/ui/desktop/src/components/context_management/index.ts @@ -4,6 +4,7 @@ import { MessageContent as FrontendMessageContent, ToolCallResult, ToolCall, + Role, } from '../../types/message'; import { ContextManageRequest, @@ -115,9 +116,40 @@ function mapApiContentToFrontendMessageContent( type: 'contextLengthExceeded', msg: apiContent.msg, }; + } else if (apiContent.type === 'summarizationRequested') { + return { + type: 'summarizationRequested', + msg: apiContent.msg, + }; } // For types that exist in API but not in frontend, either skip or convert console.warn(`Skipping unsupported content type: ${apiContent.type}`); return null; } + +export function createSummarizationRequestMessage( + messages: FrontendMessage[], + requestMessage: string +): FrontendMessage { + // Get the last message + const lastMessage = messages[messages.length - 1]; + + // Determine the next role (opposite of the last message) + const nextRole: Role = lastMessage.role === 'user' ? 'assistant' : 'user'; + + // Create the new message with SummarizationRequestedContent + return { + id: generateId(), + role: nextRole, + created: Math.floor(Date.now() / 1000), + content: [ + { + type: 'summarizationRequested', + msg: requestMessage, + }, + ], + sendToLLM: false, + display: true, + }; +} diff --git a/ui/desktop/src/hooks/useMessageStream.ts b/ui/desktop/src/hooks/useMessageStream.ts index 71e096c0..e7bb9718 100644 --- a/ui/desktop/src/hooks/useMessageStream.ts +++ b/ui/desktop/src/hooks/useMessageStream.ts @@ -312,7 +312,7 @@ export function useMessageStream({ ...extraMetadataRef.current.headers, }, body: JSON.stringify({ - messages: requestMessages, + messages: filteredMessages, ...extraMetadataRef.current.body, }), signal: abortController.signal, @@ -439,7 +439,6 @@ export function useMessageStream({ event?.preventDefault?.(); if (!input.trim()) return; - console.log('handleSubmit called with input:', input); await append(input); setInput(''); }, diff --git a/ui/desktop/src/types/message.ts b/ui/desktop/src/types/message.ts index f6227327..a3ec14ec 100644 --- a/ui/desktop/src/types/message.ts +++ b/ui/desktop/src/types/message.ts @@ -78,21 +78,27 @@ export interface ContextLengthExceededContent { msg: string; } +export interface SummarizationRequestedContent { + type: 'summarizationRequested'; + msg: string; +} + export type MessageContent = | TextContent | ImageContent | ToolRequestMessageContent | ToolResponseMessageContent | ToolConfirmationRequestMessageContent - | ContextLengthExceededContent; + | ContextLengthExceededContent + | SummarizationRequestedContent; export interface Message { id?: string; role: Role; created: number; content: MessageContent[]; - display: boolean; - sendToLLM: boolean; + display?: boolean; + sendToLLM?: boolean; } // Helper functions to create messages