From cb73d276a35810c1d37601ff716bd1ad14350e46 Mon Sep 17 00:00:00 2001 From: Rob Colbert Date: Tue, 5 May 2026 14:34:52 -0400 Subject: [PATCH] more progress along ChatTurn processing The agent has been failing and failing and failing, so I: 1. Swapped models 2. Did some by-hand enhancements 3. Set this checkpoint for continuing --- .../frontend/src/components/ChatTurn.tsx | 14 ++++++- gadget-code/frontend/src/lib/api.ts | 1 + .../frontend/src/pages/ChatSessionView.tsx | 37 ++++++++---------- gadget-code/src/lib/code-session.ts | 38 +++++++++++++++++-- gadget-code/src/lib/drone-session.ts | 10 ++--- gadget-code/src/models/chat-turn.ts | 1 + packages/api/src/interfaces/chat-turn.ts | 1 + packages/api/src/messages/ide.ts | 16 +++++++- 8 files changed, 86 insertions(+), 32 deletions(-) diff --git a/gadget-code/frontend/src/components/ChatTurn.tsx b/gadget-code/frontend/src/components/ChatTurn.tsx index f4022d0..26433ae 100644 --- a/gadget-code/frontend/src/components/ChatTurn.tsx +++ b/gadget-code/frontend/src/components/ChatTurn.tsx @@ -80,9 +80,21 @@ const ChatTurn = memo(function ChatTurn({ turn, onUpdate }: ChatTurnProps) { )} + {/* Error Alert */} + {turn.errorMessage && turn.status === "error" && ( +
+
+
+ ⚠️ Error +
+
{turn.errorMessage}
+
+
+ )} + {/* User Prompt */}
-
+
You
{turn.prompts?.user || ""}
diff --git a/gadget-code/frontend/src/lib/api.ts b/gadget-code/frontend/src/lib/api.ts index be7b8b8..992da22 100644 --- a/gadget-code/frontend/src/lib/api.ts +++ b/gadget-code/frontend/src/lib/api.ts @@ -311,6 +311,7 @@ export interface ChatTurn { prompts: ChatTurnPrompts; thinking?: string; response?: string; + errorMessage?: string; toolCalls: Array<{ callId: string; name: string; diff --git a/gadget-code/frontend/src/pages/ChatSessionView.tsx b/gadget-code/frontend/src/pages/ChatSessionView.tsx index e4ab486..81af8ba 100644 --- a/gadget-code/frontend/src/pages/ChatSessionView.tsx +++ b/gadget-code/frontend/src/pages/ChatSessionView.tsx @@ -19,7 +19,6 @@ interface LogEntry { export default function ChatSessionView() { const { projectId, sessionId } = useParams<{ projectId: string; sessionId: string }>(); const navigate = useNavigate(); - const socket = socketClient.socket; const appContext = useContext(AppContext); const [project, setProject] = useState(null); @@ -91,27 +90,23 @@ export default function ChatSessionView() { }; const setupSocketListeners = () => { - if (!socket) return; - - socket.on('thinking', handleThinking); - socket.on('response', handleResponse); - socket.on('toolCall', handleToolCall); - socket.on('workOrderComplete', handleWorkOrderComplete); - socket.on('workspaceModeChanged', handleWorkspaceModeChanged); - socket.on('log:entry', handleLogEntry); - socket.on('status', handleStatus); + socketClient.on('thinking', handleThinking); + socketClient.on('response', handleResponse); + socketClient.on('toolCall', handleToolCall); + socketClient.on('workOrderComplete', handleWorkOrderComplete); + socketClient.on('workspaceModeChanged', handleWorkspaceModeChanged); + socketClient.on('log:entry', handleLogEntry); + socketClient.on('status', handleStatus); }; const cleanupSocketListeners = () => { - if (!socket) return; - - socket.off('thinking', handleThinking); - socket.off('response', handleResponse); - socket.off('toolCall', handleToolCall); - socket.off('workOrderComplete', handleWorkOrderComplete); - socket.off('workspaceModeChanged', handleWorkspaceModeChanged); - socket.off('log:entry', handleLogEntry); - socket.off('status', handleStatus); + socketClient.off('thinking', handleThinking); + socketClient.off('response', handleResponse); + socketClient.off('toolCall', handleToolCall); + socketClient.off('workOrderComplete', handleWorkOrderComplete); + socketClient.off('workspaceModeChanged', handleWorkspaceModeChanged); + socketClient.off('log:entry', handleLogEntry); + socketClient.off('status', handleStatus); }; const scheduleUpdate = useCallback(() => { @@ -194,7 +189,7 @@ export default function ChatSessionView() { setTurns(prevTurns => prevTurns.map(turn => turn._id === turnId - ? { ...turn, status: success ? 'finished' : 'error', response: message && !success ? message : turn.response } + ? { ...turn, status: success ? 'finished' : 'error', errorMessage: message && !success ? message : turn.errorMessage } : turn ) ); @@ -264,7 +259,7 @@ export default function ChatSessionView() { const handleSubmitPrompt = async (e: React.FormEvent) => { e.preventDefault(); - if (!promptInput.trim() || isProcessing || !socket || !sessionLocked) return; + if (!promptInput.trim() || isProcessing || !socketClient.connected || !sessionLocked) return; if (workspaceMode !== WorkspaceMode.Agent) return; const userTurn: ChatTurn = { diff --git a/gadget-code/src/lib/code-session.ts b/gadget-code/src/lib/code-session.ts index 39f1fc1..c258d66 100644 --- a/gadget-code/src/lib/code-session.ts +++ b/gadget-code/src/lib/code-session.ts @@ -16,6 +16,7 @@ import { GadgetId, ChatTurnDocument, WorkspaceMode, + SubmitPromptCallback, } from "@gadget/api"; import { ChatSessionService, SocketService } from "../services/index.ts"; @@ -134,18 +135,21 @@ export class CodeSession extends SocketSession { * Called when the IDE submits a prompt to be processed by the agent. * Creates a ChatTurn document and sends a work order to the selected drone. */ - async onSubmitPrompt(content: string): Promise { + async onSubmitPrompt( + content: string, + cb: SubmitPromptCallback, + ): Promise { if (!this.selectedDrone) { this.log.warn("prompt rejected: no drone selected"); - throw new Error("No drone selected"); + return cb(false, { message: "No drone selected" }); } if (!this.chatSession) { this.log.warn("prompt rejected: no chat session active"); - throw new Error("No chat session active"); + return cb(false, { message: "No chat session active" }); } if (!this.project) { this.log.warn("prompt rejected: no project selected"); - throw new Error("No project selected"); + return cb(false, { message: "No project selected" }); } const droneSession = SocketService.getDroneSession(this.selectedDrone); @@ -162,6 +166,7 @@ export class CodeSession extends SocketSession { }); droneSession.setCurrentTurnId(turn._id); + cb(true, { turnId: turn._id, message: "turn created successfully" }); droneSession.socket.emit( "processWorkOrder", @@ -198,4 +203,29 @@ export class CodeSession extends SocketSession { onStatus(content: string): void { this.socket.emit("status", content); } + + onThinking(content: string): void { + this.socket.emit("thinking", content); + } + + onResponse(content: string): void { + this.socket.emit("response", content); + } + + onToolCall( + callId: string, + name: string, + params: string, + response: string, + ): void { + this.socket.emit("toolCall", callId, name, params, response); + } + + onWorkOrderComplete( + turnId: string, + success: boolean, + message?: string, + ): void { + this.socket.emit("workOrderComplete", turnId, success, message); + } } diff --git a/gadget-code/src/lib/drone-session.ts b/gadget-code/src/lib/drone-session.ts index 8224057..6a4141b 100644 --- a/gadget-code/src/lib/drone-session.ts +++ b/gadget-code/src/lib/drone-session.ts @@ -82,7 +82,7 @@ export class DroneSession extends SocketSession { const codeSession = SocketService.getCodeSessionByChatSessionId( this.chatSessionId, ); - codeSession.socket.emit("thinking", content); + codeSession.onThinking(content); if (this.currentTurnId) { await ChatTurn.findByIdAndUpdate(this.currentTurnId, { @@ -107,7 +107,7 @@ export class DroneSession extends SocketSession { const codeSession = SocketService.getCodeSessionByChatSessionId( this.chatSessionId, ); - codeSession.socket.emit("response", content); + codeSession.onResponse(content); if (this.currentTurnId) { await ChatTurn.findByIdAndUpdate(this.currentTurnId, { @@ -137,7 +137,7 @@ export class DroneSession extends SocketSession { const codeSession = SocketService.getCodeSessionByChatSessionId( this.chatSessionId, ); - codeSession.socket.emit("toolCall", callId, name, params, response); + codeSession.onToolCall(callId, name, params, response); if (this.currentTurnId) { const turn = await ChatTurn.findById(this.currentTurnId); @@ -177,7 +177,7 @@ export class DroneSession extends SocketSession { if (turn) { turn.status = success ? ChatTurnStatus.Finished : ChatTurnStatus.Error; if (!success && message) { - turn.response = message; + turn.errorMessage = message; } await turn.save(); } @@ -185,7 +185,7 @@ export class DroneSession extends SocketSession { const codeSession = SocketService.getCodeSessionByChatSessionId( this.chatSessionId, ); - codeSession.socket.emit("workOrderComplete", turnId, success, message); + codeSession.onWorkOrderComplete(turnId, success, message); this.currentTurnId = undefined; } catch (error) { diff --git a/gadget-code/src/models/chat-turn.ts b/gadget-code/src/models/chat-turn.ts index db17ebe..b614107 100644 --- a/gadget-code/src/models/chat-turn.ts +++ b/gadget-code/src/models/chat-turn.ts @@ -67,6 +67,7 @@ export const ChatTurnSchema = new Schema({ prompts: { type: ChatTurnPromptsSchema, required: true }, thinking: { type: String, required: false }, response: { type: String, required: false }, + errorMessage: { type: String, required: false }, toolCalls: { type: [ChatToolCallSchema], default: [], required: true }, subagents: { type: [ChatSubagentProcessSchema], default: [], required: true }, stats: { diff --git a/packages/api/src/interfaces/chat-turn.ts b/packages/api/src/interfaces/chat-turn.ts index d0bed69..b7fb8a9 100644 --- a/packages/api/src/interfaces/chat-turn.ts +++ b/packages/api/src/interfaces/chat-turn.ts @@ -65,6 +65,7 @@ export interface IChatTurn { prompts: IChatTurnPrompts; thinking?: string; response?: string; + errorMessage?: string; toolCalls: IChatToolCall[]; subagents: IChatSubagentProcess[]; // subagents used while processing this turn stats: IChatTurnStats; diff --git a/packages/api/src/messages/ide.ts b/packages/api/src/messages/ide.ts index f2bd769..f8b0728 100644 --- a/packages/api/src/messages/ide.ts +++ b/packages/api/src/messages/ide.ts @@ -5,6 +5,7 @@ import { IChatSession } from "../interfaces/chat-session.ts"; import { IDroneRegistration } from "../interfaces/drone-registration.ts"; import { IProject } from "../interfaces/project.ts"; +import { GadgetId } from "../lib/gadget-id.ts"; /* * requestSessionLock @@ -51,4 +52,17 @@ export type RequestWorkspaceModeMessage = ( * submitPrompt */ -export type SubmitPromptMessage = (prompt: string) => void; +export interface SubmitPromptCallbackData { + turnId?: GadgetId; + message?: string; +} + +export type SubmitPromptCallback = ( + success: boolean, + data: SubmitPromptCallbackData, +) => void; + +export type SubmitPromptMessage = ( + prompt: string, + cb: SubmitPromptCallback, +) => void;