Skip to content

Commit 1a83d74

Browse files
author
Avishay Mashiach
committed
fix: address CodeRabbit comments on PR #770
- websocket.ts: wrap message handler in try/catch with shape validation - daemon-bootstrap.ts: register missing task.start/session.resume/permission.respond RPCs - pnpm-lock.yaml: regenerate after adding daemon package Addresses: 2971335381, 2971335383, 2971335385
1 parent 5a7de55 commit 1a83d74

3 files changed

Lines changed: 677 additions & 39 deletions

File tree

apps/daemon/src/websocket.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,29 @@ export function setupWebSocket(server: Server): WebSocketServer {
2929
console.log('[WebSocket] Client connected. Total:', wss!.clients.size);
3030

3131
ws.on('message', (raw) => {
32-
const msg = JSON.parse(raw.toString()) as ClientMessage;
33-
messageHandlers.forEach((handler) => handler(msg));
32+
let msg: ClientMessage;
33+
try {
34+
const parsed = JSON.parse(raw.toString()) as unknown;
35+
if (
36+
typeof parsed !== 'object' ||
37+
parsed === null ||
38+
typeof (parsed as Record<string, unknown>).type !== 'string'
39+
) {
40+
console.warn('[WebSocket] Received message with invalid shape, ignoring');
41+
return;
42+
}
43+
msg = parsed as ClientMessage;
44+
} catch (err) {
45+
console.warn('[WebSocket] Failed to parse incoming message:', err);
46+
return;
47+
}
48+
messageHandlers.forEach((handler) => {
49+
try {
50+
handler(msg);
51+
} catch (err) {
52+
console.error('[WebSocket] Handler error:', err);
53+
}
54+
});
3455
});
3556

3657
ws.on('close', () => {
@@ -65,4 +86,4 @@ export function onClientMessage(handler: MessageHandler): () => void {
6586

6687
export function getConnectedClientCount(): number {
6788
return wss?.clients.size ?? 0;
68-
}
89+
}

apps/desktop/src/main/daemon-bootstrap.ts

Lines changed: 105 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ import {
2020
listScheduledTasks,
2121
cancelScheduledTask,
2222
disposeScheduler,
23+
mapResultToStatus,
24+
createTaskId,
25+
} from '@accomplish_ai/agent-core';
26+
import type {
27+
TaskManagerAPI,
28+
StorageAPI,
29+
TaskCallbacks,
30+
TaskMessage,
31+
TaskResult,
32+
TaskStatus,
33+
PermissionResponse,
2334
} from '@accomplish_ai/agent-core';
24-
import type { TaskManagerAPI, StorageAPI } from '@accomplish_ai/agent-core';
2535
import { app } from 'electron';
2636

2737
let server: DaemonServer | null = null;
@@ -161,6 +171,48 @@ function getDaemonEntryPath(): string {
161171
return path.join(app.getAppPath(), 'out', 'main', 'daemon', 'entry.js');
162172
}
163173

174+
/**
175+
* Build task lifecycle callbacks that forward events back through the in-process DaemonServer.
176+
*/
177+
function buildInProcessCallbacks(
178+
taskId: string,
179+
srv: DaemonServer,
180+
storage: StorageAPI,
181+
): TaskCallbacks {
182+
return {
183+
onBatchedMessages: (messages: TaskMessage[]) => {
184+
for (const msg of messages) {
185+
storage.addTaskMessage(taskId, msg);
186+
}
187+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
188+
srv.notify('task.message' as any, { taskId, messages });
189+
},
190+
onProgress: (progress) => {
191+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
192+
srv.notify('task.progress' as any, { taskId, ...progress });
193+
},
194+
onPermissionRequest: (request) => {
195+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
196+
srv.notify('permission.request' as any, request);
197+
},
198+
onComplete: (result: TaskResult) => {
199+
const taskStatus = mapResultToStatus(result);
200+
storage.updateTaskStatus(taskId, taskStatus, new Date().toISOString());
201+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
202+
srv.notify('task.complete' as any, { taskId, result });
203+
},
204+
onError: (error: Error) => {
205+
storage.updateTaskStatus(taskId, 'failed', new Date().toISOString());
206+
console.error(`[DaemonBootstrap] Task ${taskId} error:`, error.message);
207+
},
208+
onStatusChange: (status: TaskStatus) => {
209+
storage.updateTaskStatus(taskId, status, new Date().toISOString());
210+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
211+
srv.notify('task.statusChange' as any, { taskId, status });
212+
},
213+
};
214+
}
215+
164216
/**
165217
* Register in-process daemon handlers (same as daemon/entry.ts).
166218
*/
@@ -273,6 +325,57 @@ function registerInProcessHandlers(
273325
}
274326
});
275327

328+
// ── Task execution ────────────────────────────────────────────────
329+
330+
srv.registerMethod('task.start', async (params) => {
331+
const { taskId: providedTaskId, config } = params;
332+
const taskId = providedTaskId ?? createTaskId();
333+
334+
const callbacks = buildInProcessCallbacks(taskId, srv, storage);
335+
const task = await taskManager.startTask(taskId, config, callbacks);
336+
storage.saveTask(task);
337+
return task;
338+
});
339+
340+
srv.registerMethod('session.resume', async (params) => {
341+
const { sessionId, prompt, existingTaskId } = params;
342+
const taskId = existingTaskId ?? createTaskId();
343+
344+
const callbacks = buildInProcessCallbacks(taskId, srv, storage);
345+
const task = await taskManager.startTask(taskId, { prompt, sessionId }, callbacks);
346+
347+
if (existingTaskId) {
348+
storage.updateTaskStatus(existingTaskId, task.status, new Date().toISOString());
349+
} else {
350+
storage.saveTask(task);
351+
}
352+
353+
return task;
354+
});
355+
356+
srv.registerMethod('permission.respond', async (params) => {
357+
const { response } = params;
358+
const { taskId, decision, requestId, selectedOptions } = response as PermissionResponse;
359+
360+
if (!taskManager.hasActiveTask(taskId)) {
361+
console.warn(`[DaemonBootstrap] Permission response for inactive task ${taskId}`);
362+
return;
363+
}
364+
365+
if (requestId) {
366+
// requestId-based responses are handled externally (permission-api); log and skip
367+
console.warn(`[DaemonBootstrap] Unhandled requestId-based permission.respond: ${requestId}`);
368+
return;
369+
}
370+
371+
if (decision === 'allow') {
372+
const message = selectedOptions?.join(', ') || 'yes';
373+
await taskManager.sendResponse(taskId, message);
374+
} else {
375+
await taskManager.sendResponse(taskId, 'no');
376+
}
377+
});
378+
276379
// ── Scheduling ───────────────────────────────────────────────────
277380

278381
srv.registerMethod('task.schedule', (params) => {
@@ -338,4 +441,4 @@ export function shutdownDaemon(): void {
338441
disposeScheduler();
339442
mode = null;
340443
console.log('[DaemonBootstrap] Daemon shut down');
341-
}
444+
}

0 commit comments

Comments
 (0)