From b070c1661eb3322109943ca534ca892473ad44e4 Mon Sep 17 00:00:00 2001 From: AlanAAG Date: Thu, 25 Jun 2026 20:31:41 -0600 Subject: [PATCH 1/4] feat(browser-adapter): add enhance_prompt WebSocket handler Add new message handler for enhance_prompt type that optimizes user prompts using LLM before submission. Includes _handle_enhance_prompt method that calls generate_response_async with a prompt optimization system message, sends the enhanced content back via WebSocket, and falls back to original content on error. Co-Authored-By: Claude Sonnet 4.6 --- app/ui_layer/adapters/browser_adapter.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/ui_layer/adapters/browser_adapter.py b/app/ui_layer/adapters/browser_adapter.py index d7cbde5c..e7eaa753 100644 --- a/app/ui_layer/adapters/browser_adapter.py +++ b/app/ui_layer/adapters/browser_adapter.py @@ -1062,6 +1062,25 @@ async def submit_message( living_ui_id=living_ui_id, ) + async def _handle_enhance_prompt(self, content: str, ws) -> None: + """Enhance a user's prompt using the LLM for clarity and precision.""" + SYSTEM = ( + "You are a prompt optimizer for CraftBot, an AI agent that manages files, tasks, " + "memory, and scheduling. Rewrite the user's message to be unambiguous, specific, and " + "actionable. Preserve the original intent exactly. Do not add new scope or features. " + "Return only the rewritten prompt — no explanation, no preamble." + ) + try: + enhanced = await self._controller.agent.llm.generate_response_async( + system_prompt=SYSTEM, + user_prompt=content, + log_response=False, + ) + await ws.send_json({"type": "prompt_enhanced", "content": enhanced.strip()}) + except Exception as e: + logger.warning(f"[BROWSER ADAPTER] enhance_prompt failed: {e}") + await ws.send_json({"type": "prompt_enhanced", "content": content}) + def _handle_task_start(self, event: UIEvent) -> None: """Handle task start event with metrics tracking.""" # Call parent implementation @@ -1437,6 +1456,11 @@ async def _handle_ws_message(self, data: Dict[str, Any], ws=None) -> None: if command: await self.submit_message(command) + elif msg_type == "enhance_prompt": + content = data.get("content", "") + if content and ws: + asyncio.create_task(self._handle_enhance_prompt(content, ws)) + elif msg_type == "chat_history": before_timestamp = data.get("beforeTimestamp") limit = data.get("limit", 50) From 22bb8df0ee061632e4ad96a82c73520fca9e6075 Mon Sep 17 00:00:00 2001 From: AlanAAG Date: Thu, 25 Jun 2026 20:36:04 -0600 Subject: [PATCH 2/4] feat(frontend): add enhance prompt UI with sparkles button Wire WebSocketContext to handle prompt_enhanced WS roundtrip and expose enhancePrompt/clearEnhancedPrompt; add Sparkles IconButton with Loader2 spin state to Chat input area. Co-Authored-By: Claude Sonnet 4.6 --- .../src/components/Chat/Chat.module.css | 4 +++ .../frontend/src/components/Chat/Chat.tsx | 28 ++++++++++++++++++- .../src/contexts/WebSocketContext.tsx | 24 ++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/app/ui_layer/browser/frontend/src/components/Chat/Chat.module.css b/app/ui_layer/browser/frontend/src/components/Chat/Chat.module.css index b6550918..c24ea1a1 100644 --- a/app/ui_layer/browser/frontend/src/components/Chat/Chat.module.css +++ b/app/ui_layer/browser/frontend/src/components/Chat/Chat.module.css @@ -226,6 +226,10 @@ animation: spin 1s linear infinite; } +.spinIcon { + animation: spin 1s linear infinite; +} + @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } diff --git a/app/ui_layer/browser/frontend/src/components/Chat/Chat.tsx b/app/ui_layer/browser/frontend/src/components/Chat/Chat.tsx index 3780f357..1d82ebe9 100644 --- a/app/ui_layer/browser/frontend/src/components/Chat/Chat.tsx +++ b/app/ui_layer/browser/frontend/src/components/Chat/Chat.tsx @@ -1,5 +1,5 @@ import React, { useState, useRef, useEffect, useLayoutEffect, KeyboardEvent, useCallback, ChangeEvent, useMemo } from 'react' -import { Send, Paperclip, X, Loader2, File, AlertCircle, Reply, Mic, MicOff, ChevronDown } from 'lucide-react' +import { Send, Paperclip, X, Loader2, File, AlertCircle, Reply, Mic, MicOff, ChevronDown, Sparkles } from 'lucide-react' import { useVirtualizer } from '@tanstack/react-virtual' import { useWebSocket } from '../../contexts/WebSocketContext' import { useToast } from '../../contexts/ToastContext' @@ -114,6 +114,9 @@ export function Chat({ livingUIId, placeholder, emptyMessage }: ChatProps) { loadOlderMessages, hasMoreMessages, loadingOlderMessages, + enhancedPrompt, + enhancePrompt, + clearEnhancedPrompt, } = useWebSocket() const status = useDerivedAgentStatus({ actions, messages, connected }) @@ -130,6 +133,7 @@ export function Chat({ livingUIId, placeholder, emptyMessage }: ChatProps) { }, [messages]) const [input, setInput] = useState('') + const [enhancing, setEnhancing] = useState(false) const dispatch = useAppDispatch() const pendingPrefill = useAppSelector(selectPendingPrefill) const [pendingAttachments, setPendingAttachments] = useState([]) @@ -301,6 +305,21 @@ export function Chat({ livingUIId, placeholder, emptyMessage }: ChatProps) { }, 0) }, [pendingPrefill, dispatch]) + // Consume enhanced prompt from context when WS response arrives + useEffect(() => { + if (enhancedPrompt === null) return + setInput(enhancedPrompt) + setEnhancing(false) + clearEnhancedPrompt() + inputRef.current?.focus() + }, [enhancedPrompt, clearEnhancedPrompt]) + + const handleEnhancePrompt = useCallback(() => { + if (!input.trim() || enhancing) return + setEnhancing(true) + enhancePrompt(input.trim()) + }, [input, enhancing, enhancePrompt]) + const handleChatReply = useCallback(( sessionId: string | undefined, displayName: string, @@ -730,6 +749,13 @@ export function Chat({ livingUIId, placeholder, emptyMessage }: ChatProps) {
} variant="ghost" tooltip="Attach file" onClick={handleAttachClick} /> + : } + variant="ghost" + tooltip={enhancing ? 'Enhancing...' : 'Enhance prompt'} + onClick={handleEnhancePrompt} + disabled={!input.trim() || enhancing} + />