@@ -3,6 +3,7 @@ import { BrowserAgent, FileAgent } from "@jarvis-agent/electron";
33import type { EkoResult } from "@jarvis-agent/core/types" ;
44import { BrowserWindow , WebContentsView , app } from "electron" ;
55import path from "node:path" ;
6+ import fs from "node:fs" ;
67import { randomUUID } from "node:crypto" ;
78import { ConfigManager } from "../utils/config-manager" ;
89import type { HumanRequestMessage , HumanResponseMessage , HumanInteractionContext } from "../../../src/models/human-interaction" ;
@@ -12,7 +13,7 @@ export class EkoService {
1213 private mainWindow : BrowserWindow ;
1314 private detailView : WebContentsView ;
1415 private mcpClient ! : SimpleSseMcpClient ;
15- private agents ! : any [ ] ;
16+ private browserAgent : BrowserAgent | null = null ;
1617
1718 // Store pending human interaction requests
1819 private pendingHumanRequests = new Map < string , {
@@ -104,13 +105,14 @@ export class EkoService {
104105 agentContext : AgentContext ,
105106 prompt : string ,
106107 options : string [ ] ,
107- multiple : boolean
108+ multiple ?: boolean ,
109+ _extInfo ?: any
108110 ) : Promise < string [ ] > => {
109111 const result = await this . requestHumanInteraction ( agentContext , {
110112 interactType : 'select' ,
111113 prompt,
112114 selectOptions : options ,
113- selectMultiple : multiple
115+ selectMultiple : multiple ?? false
114116 } ) ;
115117 return Array . isArray ( result ) ? result : [ ] ;
116118 } ,
@@ -142,31 +144,63 @@ export class EkoService {
142144 } ;
143145 }
144146
145- private initializeEko ( ) {
146- const configManager = ConfigManager . getInstance ( ) ;
147- const llms : LLMs = configManager . getLLMsConfig ( ) ;
148- const agentConfig = configManager . getAgentConfig ( ) ;
149-
150- const appPath = app . isPackaged
147+ /**
148+ * Get base work path for file storage
149+ */
150+ private getBaseWorkPath ( ) : string {
151+ return app . isPackaged
151152 ? path . join ( app . getPath ( 'userData' ) , 'static' )
152153 : path . join ( process . cwd ( ) , 'public' , 'static' ) ;
154+ }
153155
154- this . mcpClient = new SimpleSseMcpClient ( "http://localhost:5173/api/mcp/sse" ) ;
155- this . agents = [ ] ;
156+ /**
157+ * Get task-specific work path with unique taskId
158+ */
159+ private getTaskWorkPath ( taskId : string ) : string {
160+ return path . join ( this . getBaseWorkPath ( ) , taskId ) ;
161+ }
156162
157- if ( agentConfig . browserAgent . enabled ) {
158- this . agents . push (
159- new BrowserAgent ( this . detailView , this . mcpClient , agentConfig . browserAgent . customPrompt )
160- ) ;
163+ /**
164+ * Create Eko instance for a specific task with unique work directory
165+ */
166+ private createEkoForTask ( taskId : string ) : Eko {
167+ const configManager = ConfigManager . getInstance ( ) ;
168+ const llms : LLMs = configManager . getLLMsConfig ( ) ;
169+ const agentConfig = configManager . getAgentConfig ( ) ;
170+ const agents : any [ ] = [ ] ;
171+
172+ // Reuse BrowserAgent (no file storage involved)
173+ if ( this . browserAgent ) {
174+ agents . push ( this . browserAgent ) ;
161175 }
162176
177+ // Create FileAgent with task-specific work directory
163178 if ( agentConfig . fileAgent . enabled ) {
164- this . agents . push (
165- new FileAgent ( this . detailView , appPath , this . mcpClient , agentConfig . fileAgent . customPrompt )
179+ const taskWorkPath = this . getTaskWorkPath ( taskId ) ;
180+ fs . mkdirSync ( taskWorkPath , { recursive : true } ) ;
181+ agents . push (
182+ new FileAgent ( this . detailView , taskWorkPath , this . mcpClient , agentConfig . fileAgent . customPrompt )
166183 ) ;
167184 }
168185
169- this . eko = new Eko ( { llms, agents : this . agents , callback : this . createCallback ( ) } ) ;
186+ return new Eko ( { llms, agents, callback : this . createCallback ( ) } ) ;
187+ }
188+
189+ private initializeEko ( ) {
190+ const configManager = ConfigManager . getInstance ( ) ;
191+ const llms : LLMs = configManager . getLLMsConfig ( ) ;
192+ const agentConfig = configManager . getAgentConfig ( ) ;
193+
194+ this . mcpClient = new SimpleSseMcpClient ( "http://localhost:5173/api/mcp/sse" ) ;
195+
196+ // Only create BrowserAgent once (no file storage involved)
197+ if ( agentConfig . browserAgent . enabled ) {
198+ this . browserAgent = new BrowserAgent ( this . detailView , this . mcpClient , agentConfig . browserAgent . customPrompt ) ;
199+ }
200+
201+ // Create default Eko instance with only BrowserAgent for restore/modify scenarios
202+ const defaultAgents = this . browserAgent ? [ this . browserAgent ] : [ ] ;
203+ this . eko = new Eko ( { llms, agents : defaultAgents , callback : this . createCallback ( ) } ) ;
170204 }
171205
172206 /**
@@ -187,8 +221,18 @@ export class EkoService {
187221
188222 const configManager = ConfigManager . getInstance ( ) ;
189223 const llms : LLMs = configManager . getLLMsConfig ( ) ;
224+ const agentConfig = configManager . getAgentConfig ( ) ;
190225
191- this . eko = new Eko ( { llms, agents : this . agents , callback : this . createCallback ( ) } ) ;
226+ // Recreate BrowserAgent with new config
227+ if ( agentConfig . browserAgent . enabled ) {
228+ this . browserAgent = new BrowserAgent ( this . detailView , this . mcpClient , agentConfig . browserAgent . customPrompt ) ;
229+ } else {
230+ this . browserAgent = null ;
231+ }
232+
233+ // Create default Eko instance
234+ const defaultAgents = this . browserAgent ? [ this . browserAgent ] : [ ] ;
235+ this . eko = new Eko ( { llms, agents : defaultAgents , callback : this . createCallback ( ) } ) ;
192236
193237 if ( this . mainWindow && ! this . mainWindow . isDestroyed ( ) ) {
194238 this . mainWindow . webContents . send ( 'eko-config-reloaded' , {
@@ -199,14 +243,15 @@ export class EkoService {
199243 }
200244
201245 async run ( message : string ) : Promise < EkoResult | null > {
202- if ( ! this . eko ) {
203- console . error ( '[EkoService] Eko service not initialized' ) ;
204- this . sendErrorToFrontend ( 'Eko service not initialized' ) ;
205- return null ;
206- }
207-
208246 try {
209- return await this . eko . run ( message ) ;
247+ // Generate unique taskId for this execution
248+ const taskId = randomUUID ( ) ;
249+
250+ // Create Eko instance with task-specific work directory
251+ this . eko = this . createEkoForTask ( taskId ) ;
252+
253+ // Execute with the specified taskId
254+ return await this . eko . run ( message , taskId ) ;
210255 } catch ( error : any ) {
211256 console . error ( '[EkoService] Run error:' , error ) ;
212257 this . sendErrorToFrontend ( error ?. message || 'Unknown error occurred' , error ) ;
@@ -325,20 +370,20 @@ export class EkoService {
325370 chainPlanRequest ?: any ,
326371 chainPlanResult ?: string
327372 ) : Promise < string | null > {
328- if ( ! this . eko ) {
329- console . error ( '[EkoService] Eko service not initialized' ) ;
330- return null ;
331- }
332-
333373 try {
374+ const taskId = workflow . taskId ;
375+
376+ // Create Eko instance with task-specific work directory for restored task
377+ this . eko = this . createEkoForTask ( taskId ) ;
378+
334379 const context = await this . eko . initContext ( workflow , contextParams ) ;
335380
336381 if ( chainPlanRequest && chainPlanResult ) {
337382 context . chain . planRequest = chainPlanRequest ;
338383 context . chain . planResult = chainPlanResult ;
339384 }
340385
341- return workflow . taskId ;
386+ return taskId ;
342387 } catch ( error : any ) {
343388 console . error ( '[EkoService] Failed to restore task:' , error ) ;
344389 return null ;
0 commit comments