fix: eliminate duplicate WebSocket messages during agent history priming

- Skip dispatching individual agent_stream events when replaying history
  (the snapshot is sent after priming anyway, so individual events were wasted)
- Add WebSocket message logging on client (type, size, id) for debugging
- Change default daemon URL from dev to localhost
- Clean up unused imports in session.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Mohamed Boudra
2025-11-28 17:44:07 +00:00
co-authored by Claude
parent 3dc517366d
commit ae4678bca8
4 changed files with 24 additions and 8 deletions
@@ -7,7 +7,7 @@ const REGISTRY_STORAGE_KEY = "@paseo:daemon-registry";
const LEGACY_SETTINGS_KEY = "@paseo:settings"; const LEGACY_SETTINGS_KEY = "@paseo:settings";
const FALLBACK_DAEMON_URL = "ws://localhost:6767/ws"; const FALLBACK_DAEMON_URL = "ws://localhost:6767/ws";
const DEFAULT_DAEMONS: Array<{ label: string; wsUrl: string; restUrl?: string | null }> = [ const DEFAULT_DAEMONS: Array<{ label: string; wsUrl: string; restUrl?: string | null }> = [
{ label: "dev", wsUrl: "ws://dev:6767/ws" }, { label: "localhost", wsUrl: "ws://localhost:6767/ws" },
{ label: "macbook", wsUrl: "ws://mohameds-macbook-pro:6767/ws" }, { label: "macbook", wsUrl: "ws://mohameds-macbook-pro:6767/ws" },
]; ];
const DAEMON_REGISTRY_QUERY_KEY = ["daemon-registry"]; const DAEMON_REGISTRY_QUERY_KEY = ["daemon-registry"];
+17 -5
View File
@@ -123,12 +123,17 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
ws.onmessage = (event) => { ws.onmessage = (event) => {
try { try {
const wsMessage: WSOutboundMessage = JSON.parse(event.data); const rawData = event.data;
const size = typeof rawData === "string" ? rawData.length : 0;
const wsMessage: WSOutboundMessage = JSON.parse(rawData);
// Only session messages trigger handlers // Only session messages trigger handlers
if (wsMessage.type === "session") { if (wsMessage.type === "session") {
const sessionMessage = wsMessage.message; const sessionMessage = wsMessage.message;
console.log(`[WS] Received session message type: ${sessionMessage.type}`); const id = (sessionMessage as { requestId?: string }).requestId ??
(sessionMessage as { agentId?: string }).agentId ??
(sessionMessage as { payload?: { agentId?: string } }).payload?.agentId;
console.log(`[WS] ← ${sessionMessage.type}`, { size, id: id ?? "-" });
// Track conversation ID when loaded // Track conversation ID when loaded
if (sessionMessage.type === "conversation_loaded") { if (sessionMessage.type === "conversation_loaded") {
@@ -147,8 +152,8 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
}); });
} }
} else { } else {
// pong - just log // pong
console.log(`[WS] Received ${wsMessage.type}`); console.log(`[WS] ← ${wsMessage.type}`, { size });
} }
} catch (err) { } catch (err) {
console.error("[WS] Failed to parse message:", err); console.error("[WS] Failed to parse message:", err);
@@ -200,7 +205,14 @@ export function useWebSocket(url: string, conversationId?: string | null): UseWe
const send = useCallback((message: WSInboundMessage) => { const send = useCallback((message: WSInboundMessage) => {
if (wsRef.current?.readyState === WebSocket.OPEN) { if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(message)); const payload = JSON.stringify(message);
const type = message.type === "session" ? message.message.type : message.type;
const id = message.type === "session"
? (message.message as { requestId?: string; agentId?: string }).requestId ??
(message.message as { agentId?: string }).agentId
: undefined;
console.log(`[WS] → ${type}`, { size: payload.length, id: id ?? "-" });
wsRef.current.send(payload);
} else { } else {
console.warn("[WS] Cannot send message - not connected"); console.warn("[WS] Cannot send message - not connected");
} }
@@ -707,7 +707,11 @@ export class AgentManager {
break; break;
} }
this.dispatchStream(agent.id, event); // Skip dispatching individual stream events during history replay.
// The caller will send a batched agent_stream_snapshot after priming.
if (!options?.fromHistory) {
this.dispatchStream(agent.id, event);
}
} }
private recordTimeline(agent: ManagedAgent, item: AgentTimelineItem): void { private recordTimeline(agent: ManagedAgent, item: AgentTimelineItem): void {
+1 -1
View File
@@ -1,6 +1,6 @@
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import { readFile, mkdir, writeFile } from "fs/promises"; import { readFile, mkdir, writeFile } from "fs/promises";
import { exec, spawn } from "child_process"; import { exec } from "child_process";
import { promisify, inspect } from "util"; import { promisify, inspect } from "util";
import { join } from "path"; import { join } from "path";
import invariant from "tiny-invariant"; import invariant from "tiny-invariant";