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; }