From e3f8beae45ec6ba12fd1f4af1c01a62a1abebdd8 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Mon, 22 Dec 2025 18:25:23 -0600 Subject: [PATCH] MAESTRO: Add thinking-chunk IPC event for streaming AI reasoning (Phase 3) Implement the IPC infrastructure for Show Thinking feature: - Emit 'thinking-chunk' event in process-manager.ts when partial text events arrive - Forward thinking-chunk events to renderer via index.ts - Add onThinkingChunk handler to preload.ts process API - Add TypeScript interface declaration for onThinkingChunk This enables the renderer to receive real-time streaming content from AI agents (Claude Code, OpenCode, Codex) for display when the tab's showThinking setting is enabled. The renderer decides whether to display based on per-tab settings. --- src/main/index.ts | 7 +++++++ src/main/preload.ts | 9 +++++++++ src/main/process-manager.ts | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/src/main/index.ts b/src/main/index.ts index 2e22e1d1..a663b950 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -2234,6 +2234,13 @@ function setupProcessListeners() { mainWindow?.webContents.send('process:slash-commands', sessionId, slashCommands); }); + // Handle thinking/streaming content chunks from AI agents + // Emitted when agents produce partial text events (isPartial: true) + // Renderer decides whether to display based on tab's showThinking setting + processManager.on('thinking-chunk', (sessionId: string, content: string) => { + mainWindow?.webContents.send('process:thinking-chunk', sessionId, content); + }); + // Handle stderr separately from runCommand (for clean command execution) processManager.on('stderr', (sessionId: string, data: string) => { mainWindow?.webContents.send('process:stderr', sessionId, data); diff --git a/src/main/preload.ts b/src/main/preload.ts index 20dfdbfb..76ade49a 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -130,6 +130,14 @@ contextBridge.exposeInMainWorld('maestro', { ipcRenderer.on('process:slash-commands', handler); return () => ipcRenderer.removeListener('process:slash-commands', handler); }, + // Thinking/streaming content chunks from AI agents + // Emitted when agents produce partial text events (isPartial: true) + // Renderer decides whether to display based on tab's showThinking setting + onThinkingChunk: (callback: (sessionId: string, content: string) => void) => { + const handler = (_: any, sessionId: string, content: string) => callback(sessionId, content); + ipcRenderer.on('process:thinking-chunk', handler); + return () => ipcRenderer.removeListener('process:thinking-chunk', handler); + }, // Remote command execution from web interface // This allows web commands to go through the same code path as desktop commands // inputMode is optional - if provided, renderer should use it instead of session state @@ -1181,6 +1189,7 @@ export interface MaestroAPI { onExit: (callback: (sessionId: string, code: number) => void) => () => void; onSessionId: (callback: (sessionId: string, agentSessionId: string) => void) => () => void; onSlashCommands: (callback: (sessionId: string, slashCommands: string[]) => void) => () => void; + onThinkingChunk: (callback: (sessionId: string, content: string) => void) => () => void; onRemoteCommand: (callback: (sessionId: string, command: string) => void) => () => void; onRemoteSwitchMode: (callback: (sessionId: string, mode: 'ai' | 'terminal') => void) => () => void; onRemoteInterrupt: (callback: (sessionId: string) => void) => () => void; diff --git a/src/main/process-manager.ts b/src/main/process-manager.ts index fa8eeda2..53ac7299 100644 --- a/src/main/process-manager.ts +++ b/src/main/process-manager.ts @@ -743,6 +743,10 @@ export class ProcessManager extends EventEmitter { // Accumulate text from partial streaming events (OpenCode text messages) // Skip error events - they're handled separately by detectErrorFromLine if (event.type === 'text' && event.isPartial && event.text) { + // Emit thinking chunk for real-time display (let renderer decide to display based on tab setting) + this.emit('thinking-chunk', sessionId, event.text); + + // Existing: accumulate for result fallback managedProcess.streamedText = (managedProcess.streamedText || '') + event.text; }