From 61ec5c780a0348d732cf55692bf049b11ba563f7 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Wed, 18 Feb 2026 20:33:53 -0800 Subject: [PATCH 1/3] fix(desktop): run agent prompt in separate terminal pane and fix Electric CORS - Store agent command separately in PendingTerminalSetup instead of appending to initialCommands, so setup script and agent run in separate panes within the same tab - Add X-Electric-Backend to CORS Access-Control-Allow-Headers (needed after Electric Cloud header was added in #1572) - Use wildcard for Access-Control-Expose-Headers to expose all Electric response headers (electric-offset, electric-handle, electric-schema, etc.) - Fix auth.apikeys where clause to use LIKE instead of ::jsonb cast which Docker Electric doesn't support --- .../src/app/api/electric/[...path]/utils.ts | 2 +- apps/api/src/proxy.ts | 15 +------ .../tools/start-claude-session.ts | 3 +- .../main/components/WorkspaceInitEffects.tsx | 39 ++++++++++++++++++- .../src/renderer/stores/workspace-init.ts | 2 + 5 files changed, 45 insertions(+), 16 deletions(-) diff --git a/apps/api/src/app/api/electric/[...path]/utils.ts b/apps/api/src/app/api/electric/[...path]/utils.ts index 345b41c2842..f28980cd0e2 100644 --- a/apps/api/src/app/api/electric/[...path]/utils.ts +++ b/apps/api/src/app/api/electric/[...path]/utils.ts @@ -113,7 +113,7 @@ export async function buildWhereClause( return build(agentCommands, agentCommands.organizationId, organizationId); case "auth.apikeys": { - const fragment = `"metadata"::jsonb->>'organizationId' = $1`; + const fragment = `"metadata" LIKE '%"organizationId":"' || $1 || '"%'`; return { fragment, params: [organizationId] }; } diff --git a/apps/api/src/proxy.ts b/apps/api/src/proxy.ts index e73d197459e..9b0f6ecc100 100644 --- a/apps/api/src/proxy.ts +++ b/apps/api/src/proxy.ts @@ -24,19 +24,8 @@ function getCorsHeaders(origin: string | null) { "Access-Control-Allow-Origin": isAllowed ? origin : "", "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS", "Access-Control-Allow-Headers": - "Content-Type, Authorization, x-trpc-source, trpc-accept", - "Access-Control-Expose-Headers": [ - "Stream-Next-Offset", - "Stream-Cursor", - "Stream-Up-To-Date", - "Stream-Closed", - "Stream-Total-Size", - "Stream-Write-Units", - "Producer-Epoch", - "Producer-Expected-Seq", - "Producer-Received-Seq", - "ETag", - ].join(", "), + "Content-Type, Authorization, x-trpc-source, trpc-accept, X-Electric-Backend", + "Access-Control-Expose-Headers": "*", "Access-Control-Allow-Credentials": "true", }; } diff --git a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts index 6c7bb002ab9..23237fc497d 100644 --- a/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts +++ b/apps/desktop/src/renderer/routes/_authenticated/components/AgentHooks/hooks/useCommandWatcher/tools/start-claude-session.ts @@ -32,8 +32,9 @@ async function execute( store.addPendingTerminalSetup({ workspaceId: workspace.id, projectId: pending?.projectId ?? workspace.projectId, - initialCommands: [...(pending?.initialCommands ?? []), params.command], + initialCommands: pending?.initialCommands ?? null, defaultPresets: pending?.defaultPresets, + agentCommand: params.command, }); return { diff --git a/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx b/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx index 9f5813a08b9..7be5c96864f 100644 --- a/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx +++ b/apps/desktop/src/renderer/screens/main/components/WorkspaceInitEffects.tsx @@ -29,6 +29,7 @@ export function WorkspaceInitEffects() { const processingRef = useRef>(new Set()); const addTab = useTabsStore((state) => state.addTab); + const addPane = useTabsStore((state) => state.addPane); const setTabAutoTitle = useTabsStore((state) => state.setTabAutoTitle); const { openPreset } = useTabsWithPresets(); const createOrAttach = useCreateOrAttachWithTheme(); @@ -43,6 +44,7 @@ export function WorkspaceInitEffects() { (p) => p.commands.length > 0, ); const hasPresets = shouldApplyPreset && presets.length > 0; + const { agentCommand } = setup; if (hasSetupScript && hasPresets) { const { tabId: setupTabId, paneId: setupPaneId } = addTab( @@ -53,6 +55,12 @@ export function WorkspaceInitEffects() { openPreset(setup.workspaceId, preset); } + if (agentCommand) { + addPane(setupTabId, { + initialCommands: [agentCommand], + }); + } + createOrAttach.mutate( { paneId: setupPaneId, @@ -81,6 +89,13 @@ export function WorkspaceInitEffects() { if (hasSetupScript) { const { tabId, paneId } = addTab(setup.workspaceId); setTabAutoTitle(tabId, "Workspace Setup"); + + if (agentCommand) { + addPane(tabId, { + initialCommands: [agentCommand], + }); + } + createOrAttach.mutate( { paneId, @@ -124,13 +139,35 @@ export function WorkspaceInitEffects() { for (const preset of presets) { openPreset(setup.workspaceId, preset); } + if (agentCommand) { + const { tabId: agentTabId } = addTab(setup.workspaceId, { + initialCommands: [agentCommand], + }); + setTabAutoTitle(agentTabId, "Agent"); + } + onComplete(); + return; + } + + if (agentCommand) { + const { tabId: agentTabId } = addTab(setup.workspaceId, { + initialCommands: [agentCommand], + }); + setTabAutoTitle(agentTabId, "Agent"); onComplete(); return; } onComplete(); }, - [addTab, setTabAutoTitle, createOrAttach, openPreset, shouldApplyPreset], + [ + addTab, + addPane, + setTabAutoTitle, + createOrAttach, + openPreset, + shouldApplyPreset, + ], ); useEffect(() => { diff --git a/apps/desktop/src/renderer/stores/workspace-init.ts b/apps/desktop/src/renderer/stores/workspace-init.ts index 26aad9b4654..0bcce8f890c 100644 --- a/apps/desktop/src/renderer/stores/workspace-init.ts +++ b/apps/desktop/src/renderer/stores/workspace-init.ts @@ -9,6 +9,8 @@ export interface PendingTerminalSetup { initialCommands: string[] | null; /** When undefined, signals that presets haven't been fetched yet and should be loaded from the backend */ defaultPresets?: TerminalPreset[]; + /** Agent command to run in a separate pane from the setup script */ + agentCommand?: string; } interface WorkspaceInitState { From 99f07714551bcf7e4edeb0e66780751131eff4c8 Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Wed, 18 Feb 2026 20:44:14 -0800 Subject: [PATCH 2/3] fix: explicitly list CORS expose headers and remove debug console.logs - Replace wildcard Access-Control-Expose-Headers with explicit list (wildcard is ignored when Access-Control-Allow-Credentials is true) - Remove stray console.log calls from trpc-storage.ts --- apps/api/src/proxy.ts | 10 +++++++++- apps/desktop/src/renderer/lib/trpc-storage.ts | 6 +----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/api/src/proxy.ts b/apps/api/src/proxy.ts index 9b0f6ecc100..31c86bc6598 100644 --- a/apps/api/src/proxy.ts +++ b/apps/api/src/proxy.ts @@ -25,7 +25,15 @@ function getCorsHeaders(origin: string | null) { "Access-Control-Allow-Methods": "GET, POST, PUT, PATCH, DELETE, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization, x-trpc-source, trpc-accept, X-Electric-Backend", - "Access-Control-Expose-Headers": "*", + "Access-Control-Expose-Headers": [ + "electric-offset", + "electric-handle", + "electric-schema", + "electric-cursor", + "electric-chunk-last-offset", + "electric-up-to-date", + "ETag", + ].join(", "), "Access-Control-Allow-Credentials": "true", }; } diff --git a/apps/desktop/src/renderer/lib/trpc-storage.ts b/apps/desktop/src/renderer/lib/trpc-storage.ts index bb4e67d94f1..f2a92e74c69 100644 --- a/apps/desktop/src/renderer/lib/trpc-storage.ts +++ b/apps/desktop/src/renderer/lib/trpc-storage.ts @@ -34,8 +34,7 @@ function createTrpcStorageAdapter(config: TrpcStorageConfig): StateStorage { localStorage.getItem(`${name}:version`) ?? "0", 10, ); - console.log(`[trpc-storage] getItem "${name}" version=${version}`); - return JSON.stringify({ state, version }); + return JSON.stringify({ state, version }); } catch (error) { console.error("[trpc-storage] Failed to get state:", error); return null; @@ -48,9 +47,6 @@ function createTrpcStorageAdapter(config: TrpcStorageConfig): StateStorage { version: number; }; // Persist version in localStorage, bare state via tRPC. - console.log( - `[trpc-storage] setItem "${name}" version=${parsed.version}`, - ); localStorage.setItem(`${name}:version`, String(parsed.version)); await config.set(parsed.state); } catch (error) { From 468af7abf716f30b7244110ecd7b5e37932290ff Mon Sep 17 00:00:00 2001 From: Satya Patel Date: Wed, 18 Feb 2026 20:46:24 -0800 Subject: [PATCH 3/3] fix: remove empty line left by console.log removal --- apps/desktop/src/renderer/lib/trpc-storage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/desktop/src/renderer/lib/trpc-storage.ts b/apps/desktop/src/renderer/lib/trpc-storage.ts index f2a92e74c69..71b95fded62 100644 --- a/apps/desktop/src/renderer/lib/trpc-storage.ts +++ b/apps/desktop/src/renderer/lib/trpc-storage.ts @@ -34,7 +34,7 @@ function createTrpcStorageAdapter(config: TrpcStorageConfig): StateStorage { localStorage.getItem(`${name}:version`) ?? "0", 10, ); - return JSON.stringify({ state, version }); + return JSON.stringify({ state, version }); } catch (error) { console.error("[trpc-storage] Failed to get state:", error); return null;