+
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;