From e923182c2bd21639fd278cca886fd7575822f7b3 Mon Sep 17 00:00:00 2001 From: Arul Sharma <31745423+arul28@users.noreply.github.com> Date: Mon, 30 Mar 2026 16:17:44 -0400 Subject: [PATCH 1/6] Add synthetic tool_result and artifact serving Introduce synthetic tool_result generation and local artifact serving to improve proof ingestion and previews. Key changes: - Add new syntheticToolResult module and unit/integration tests that scan tool args for absolute artifact file paths and build synthetic `tool_result` events so the proof observer can ingest screenshots, videos and traces. - Integrate maybeSyntheticToolResult into agentChatService to emit synthetic tool_result events when tools (e.g. Bash, Read, MCP tools) run but the SDK stream omits tool results. - Register a custom ade-artifact protocol in main.ts and add a handler that streams local files (with MIME detection and Range support for video seeking) for renderer previews. - Add an IPC handler (computerUseReadArtifactPreview) that returns base64 data URLs for image previews. - Improve lane import/rebase logic in laneService: resolve remote/local branch refs more robustly, attempt to detect parent lane via merge-base, create tracking branches when needed, and perform cleanup (worktree/branch) on failure. - Update conflictService to deduplicate PR-target rebase needs and emit a deduplicated rebase-needs-updated event. - Add tests for lane import and many synthetic tool result flows. These changes enable the UI to preview and ingest local artifacts produced by tools and make branch import/rebase detection more resilient. --- apps/desktop/src/main/main.ts | 77 ++- .../main/services/chat/agentChatService.ts | 13 + .../computerUse/syntheticToolResult.test.ts | 343 ++++++++++++++ .../computerUse/syntheticToolResult.ts | 82 ++++ .../services/conflicts/conflictService.ts | 12 +- .../src/main/services/ipc/registerIpc.ts | 23 + .../main/services/lanes/laneService.test.ts | 162 +++++++ .../src/main/services/lanes/laneService.ts | 308 +++++++++--- apps/desktop/src/preload/global.d.ts | 1 + apps/desktop/src/preload/preload.ts | 2 + .../chat/AgentChatComposer.test.tsx | 45 +- .../components/chat/AgentChatComposer.tsx | 370 ++------------- .../components/chat/AgentChatPane.tsx | 1 + .../components/chat/ChatComputerUsePanel.tsx | 444 ++++++++---------- .../components/lanes/CreateLaneDialog.tsx | 310 ++++++------ .../components/lanes/LanesPage.test.ts | 26 +- .../renderer/components/lanes/LanesPage.tsx | 62 ++- .../components/prs/CreatePrModal.test.tsx | 36 +- .../renderer/components/prs/CreatePrModal.tsx | 86 +++- apps/desktop/src/shared/ipc.ts | 1 + 20 files changed, 1558 insertions(+), 846 deletions(-) create mode 100644 apps/desktop/src/main/services/computerUse/syntheticToolResult.test.ts create mode 100644 apps/desktop/src/main/services/computerUse/syntheticToolResult.ts diff --git a/apps/desktop/src/main/main.ts b/apps/desktop/src/main/main.ts index f465ed47c..621c0d199 100644 --- a/apps/desktop/src/main/main.ts +++ b/apps/desktop/src/main/main.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, nativeImage, shell } from "electron"; +import { app, BrowserWindow, nativeImage, protocol, shell } from "electron"; import path from "node:path"; type NodePtyType = typeof import("node-pty"); import { registerIpc } from "./services/ipc/registerIpc"; @@ -394,7 +394,82 @@ async function createWindow(logger?: Logger): Promise { return win; } +// Register custom protocol for serving local artifact files (images, videos) to the renderer. +// Must be called before app.whenReady(). +protocol.registerSchemesAsPrivileged([ + { scheme: "ade-artifact", privileges: { standard: false, supportFetchAPI: true, stream: true, bypassCSP: true } }, +]); + app.whenReady().then(async () => { + // Handle ade-artifact:// requests — serves local files for proof drawer previews. + // Path is encoded in the URL: ade-artifact:///absolute/path/to/file.png + protocol.handle("ade-artifact", (request) => { + const url = new URL(request.url); + let filePath = decodeURIComponent(url.pathname); + // On Windows, pathname starts with /C:/... — strip leading slash + if (process.platform === "win32" && /^\/[a-zA-Z]:/.test(filePath)) { + filePath = filePath.slice(1); + } + try { + const stat = fs.statSync(filePath); + if (!stat.isFile()) return new Response("Not found", { status: 404 }); + const fileSize = stat.size; + const ext = path.extname(filePath).replace(/^\./, "").toLowerCase(); + const mimeMap: Record = { + png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg", webp: "image/webp", + gif: "image/gif", bmp: "image/bmp", svg: "image/svg+xml", + mp4: "video/mp4", webm: "video/webm", mov: "video/quicktime", avi: "video/x-msvideo", mkv: "video/x-matroska", + }; + const mime = mimeMap[ext] ?? "application/octet-stream"; + + // Support Range requests — required for