Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/shotscreen/home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
109 changes: 77 additions & 32 deletions electron/main/services/eko-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BrowserAgent, FileAgent } from "@jarvis-agent/electron";
import type { EkoResult } from "@jarvis-agent/core/types";
import { BrowserWindow, WebContentsView, app } from "electron";
import path from "node:path";
import fs from "node:fs";
import { randomUUID } from "node:crypto";
import { ConfigManager } from "../utils/config-manager";
import type { HumanRequestMessage, HumanResponseMessage, HumanInteractionContext } from "../../../src/models/human-interaction";
Expand All @@ -12,7 +13,7 @@ export class EkoService {
private mainWindow: BrowserWindow;
private detailView: WebContentsView;
private mcpClient!: SimpleSseMcpClient;
private agents!: any[];
private browserAgent: BrowserAgent | null = null;

// Store pending human interaction requests
private pendingHumanRequests = new Map<string, {
Expand Down Expand Up @@ -104,13 +105,14 @@ export class EkoService {
agentContext: AgentContext,
prompt: string,
options: string[],
multiple: boolean
multiple?: boolean,
_extInfo?: any
): Promise<string[]> => {
const result = await this.requestHumanInteraction(agentContext, {
interactType: 'select',
prompt,
selectOptions: options,
selectMultiple: multiple
selectMultiple: multiple ?? false
});
return Array.isArray(result) ? result : [];
},
Expand Down Expand Up @@ -142,31 +144,63 @@ export class EkoService {
};
}

private initializeEko() {
const configManager = ConfigManager.getInstance();
const llms: LLMs = configManager.getLLMsConfig();
const agentConfig = configManager.getAgentConfig();

const appPath = app.isPackaged
/**
* Get base work path for file storage
*/
private getBaseWorkPath(): string {
return app.isPackaged
? path.join(app.getPath('userData'), 'static')
: path.join(process.cwd(), 'public', 'static');
}

this.mcpClient = new SimpleSseMcpClient("http://localhost:5173/api/mcp/sse");
this.agents = [];
/**
* Get task-specific work path with unique taskId
*/
private getTaskWorkPath(taskId: string): string {
return path.join(this.getBaseWorkPath(), taskId);
}

if (agentConfig.browserAgent.enabled) {
this.agents.push(
new BrowserAgent(this.detailView, this.mcpClient, agentConfig.browserAgent.customPrompt)
);
/**
* Create Eko instance for a specific task with unique work directory
*/
private createEkoForTask(taskId: string): Eko {
const configManager = ConfigManager.getInstance();
const llms: LLMs = configManager.getLLMsConfig();
const agentConfig = configManager.getAgentConfig();
const agents: any[] = [];

// Reuse BrowserAgent (no file storage involved)
if (this.browserAgent) {
agents.push(this.browserAgent);
}

// Create FileAgent with task-specific work directory
if (agentConfig.fileAgent.enabled) {
this.agents.push(
new FileAgent(this.detailView, appPath, this.mcpClient, agentConfig.fileAgent.customPrompt)
const taskWorkPath = this.getTaskWorkPath(taskId);
fs.mkdirSync(taskWorkPath, { recursive: true });
agents.push(
new FileAgent(this.detailView, taskWorkPath, this.mcpClient, agentConfig.fileAgent.customPrompt)
);
}

this.eko = new Eko({ llms, agents: this.agents, callback: this.createCallback() });
return new Eko({ llms, agents, callback: this.createCallback() });
}

private initializeEko() {
const configManager = ConfigManager.getInstance();
const llms: LLMs = configManager.getLLMsConfig();
const agentConfig = configManager.getAgentConfig();

this.mcpClient = new SimpleSseMcpClient("http://localhost:5173/api/mcp/sse");

// Only create BrowserAgent once (no file storage involved)
if (agentConfig.browserAgent.enabled) {
this.browserAgent = new BrowserAgent(this.detailView, this.mcpClient, agentConfig.browserAgent.customPrompt);
}

// Create default Eko instance with only BrowserAgent for restore/modify scenarios
const defaultAgents = this.browserAgent ? [this.browserAgent] : [];
this.eko = new Eko({ llms, agents: defaultAgents, callback: this.createCallback() });
}

/**
Expand All @@ -187,8 +221,18 @@ export class EkoService {

const configManager = ConfigManager.getInstance();
const llms: LLMs = configManager.getLLMsConfig();
const agentConfig = configManager.getAgentConfig();

this.eko = new Eko({ llms, agents: this.agents, callback: this.createCallback() });
// Recreate BrowserAgent with new config
if (agentConfig.browserAgent.enabled) {
this.browserAgent = new BrowserAgent(this.detailView, this.mcpClient, agentConfig.browserAgent.customPrompt);
} else {
this.browserAgent = null;
}

// Create default Eko instance
const defaultAgents = this.browserAgent ? [this.browserAgent] : [];
this.eko = new Eko({ llms, agents: defaultAgents, callback: this.createCallback() });

if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.mainWindow.webContents.send('eko-config-reloaded', {
Expand All @@ -199,14 +243,15 @@ export class EkoService {
}

async run(message: string): Promise<EkoResult | null> {
if (!this.eko) {
console.error('[EkoService] Eko service not initialized');
this.sendErrorToFrontend('Eko service not initialized');
return null;
}

try {
return await this.eko.run(message);
// Generate unique taskId for this execution
const taskId = randomUUID();

// Create Eko instance with task-specific work directory
this.eko = this.createEkoForTask(taskId);

// Execute with the specified taskId
return await this.eko.run(message, taskId);
} catch (error: any) {
console.error('[EkoService] Run error:', error);
this.sendErrorToFrontend(error?.message || 'Unknown error occurred', error);
Expand Down Expand Up @@ -325,20 +370,20 @@ export class EkoService {
chainPlanRequest?: any,
chainPlanResult?: string
): Promise<string | null> {
if (!this.eko) {
console.error('[EkoService] Eko service not initialized');
return null;
}

try {
const taskId = workflow.taskId;

// Create Eko instance with task-specific work directory for restored task
this.eko = this.createEkoForTask(taskId);

const context = await this.eko.initContext(workflow, contextParams);

if (chainPlanRequest && chainPlanResult) {
context.chain.planRequest = chainPlanRequest;
context.chain.planResult = chainPlanResult;
}

return workflow.taskId;
return taskId;
} catch (error: any) {
console.error('[EkoService] Failed to restore task:', error);
return null;
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ai-browser",
"version": "0.0.12",
"version": "0.0.13",
"description": "DeepFundAI Browser - AI-Powered Intelligent Browser",
"author": "Shuai Liu <lsustc@mail.ustc.edu.cn>",
"license": "MIT",
Expand Down Expand Up @@ -30,7 +30,7 @@
"@ant-design/cssinjs": "^1.23.0",
"@ant-design/icons": "5.x",
"@jarvis-agent/core": "^0.1.5",
"@jarvis-agent/electron": "^0.1.10",
"@jarvis-agent/electron": "^0.1.11",
"@jest/globals": "^30.1.2",
"@react-spring/web": "^10.0.1",
"antd": "^5.26.5",
Expand All @@ -48,9 +48,9 @@
"immer": "^10.2.0",
"json-schema": "^0.4.0",
"microsoft-cognitiveservices-speech-sdk": "^1.45.0",
"next": "15.4.1",
"react": "19.1.0",
"react-dom": "19.1.0",
"next": "15.4.8",
"react": "19.1.2",
"react-dom": "19.1.2",
"react-i18next": "^16.2.3",
"react-icons": "^5.5.0",
"react-markdown": "^10.1.0",
Expand Down
Loading