diff --git a/.gitignore b/.gitignore index 61e500a..55dd8b4 100644 --- a/.gitignore +++ b/.gitignore @@ -73,4 +73,7 @@ ENV/ CLAUDE.md .mcp.json .playwright-mcp/ -notepad/ \ No newline at end of file +notepad/ + +# Bob Agent workspace (task outputs) +bob-workspace/ \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 43ddcd5..87fe9f8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -389,7 +389,6 @@ "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -752,7 +751,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", @@ -1522,7 +1520,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -1599,7 +1596,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/packages/memory/index.ts b/packages/memory/index.ts index 8849725..4b81e90 100644 --- a/packages/memory/index.ts +++ b/packages/memory/index.ts @@ -47,6 +47,9 @@ export class MemoryManager { const transport = new StdioClientTransport({ command: pythonCommand, args: [this.serverPath], + env: { + ...process.env as Record, // Pass all environment variables including OPENAI_API_KEY + }, }); this.client = new Client({ diff --git a/src/agent.ts b/src/agent.ts index e7fb894..a7efc0e 100644 --- a/src/agent.ts +++ b/src/agent.ts @@ -18,6 +18,7 @@ import { AgentGoal, AgentProgress } from './types'; import MemoryManager from '../packages/memory'; import ObservabilityManager from '../packages/observability'; import AbilityManager from '../packages/ability'; +import { executeTool } from './tools'; import { ExecutionDAG } from './dag/ExecutionDAG'; import { PlanNode, DAGPlan, DAGSchema } from './types/dag'; import { getMCPServers } from './mcp-config'; @@ -592,25 +593,30 @@ OUTPUT VALID JSON ONLY (no markdown, no explanations outside JSON): } /** - * Direct tool execution (no LLM call) + * Direct tool execution (no LLM call) - REAL IMPLEMENTATION */ private async executeDirectToolCall( task: PlanNode, dependencyResults: Record ): Promise { - // Simulated tool execution for now - // In real implementation, this would call actual tools directly console.log( ` [Direct Tool] ${task.tool} with input:`, JSON.stringify(task.toolInput) ); + // Execute the tool for real! + const toolResult = await executeTool(task.tool || '', task.toolInput || {}); + + if (!toolResult.success) { + throw new Error(`Tool execution failed: ${toolResult.error}`); + } + return { method: 'direct_tool', tool: task.tool, input: task.toolInput, dependencies: dependencyResults, - result: `Executed ${task.tool}: ${task.action}`, + result: toolResult.result, }; } diff --git a/src/index.ts b/src/index.ts index 79e6d2b..c9cf768 100644 --- a/src/index.ts +++ b/src/index.ts @@ -16,7 +16,7 @@ import { AgentGoal } from './types'; // Load environment variables from .env file in project root // Use override: true to ensure .env file takes precedence over shell environment dotenv.config({ - path: path.resolve(__dirname, '../.env'), + path: path.resolve(__dirname, '../../.env'), // Go up from dist/src to project root override: true }); diff --git a/src/tools.ts b/src/tools.ts new file mode 100644 index 0000000..af44318 --- /dev/null +++ b/src/tools.ts @@ -0,0 +1,134 @@ +/** + * Real Tool Implementations for Bob Agent + * + * These tools actually execute operations instead of simulating them. + */ + +import * as fs from 'fs/promises'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import * as path from 'path'; + +const execAsync = promisify(exec); + +export interface ToolResult { + success: boolean; + result?: any; + error?: string; +} + +/** + * Write tool - Creates or overwrites files + */ +export async function executeWriteTool(input: { + path: string; + content: string; +}): Promise { + try { + const { path: filePath, content } = input; + + // Ensure the directory exists + const dir = path.dirname(filePath); + await fs.mkdir(dir, { recursive: true }); + + // Write the file + await fs.writeFile(filePath, content, 'utf-8'); + + return { + success: true, + result: `File written to ${filePath} (${content.length} bytes)`, + }; + } catch (error: any) { + return { + success: false, + error: error.message, + }; + } +} + +/** + * Read tool - Reads file contents + */ +export async function executeReadTool(input: { + path: string; +}): Promise { + try { + const { path: filePath } = input; + const content = await fs.readFile(filePath, 'utf-8'); + + return { + success: true, + result: content, + }; + } catch (error: any) { + return { + success: false, + error: error.message, + }; + } +} + +/** + * Bash tool - Executes shell commands + */ +export async function executeBashTool(input: { + command: string; + timeout?: number; +}): Promise { + try { + const { command, timeout = 30000 } = input; + + console.log(` [Bash] Executing: ${command}`); + + const { stdout, stderr } = await execAsync(command, { + timeout, + maxBuffer: 1024 * 1024 * 10, // 10MB buffer + }); + + return { + success: true, + result: { + stdout: stdout.trim(), + stderr: stderr.trim(), + command, + }, + }; + } catch (error: any) { + return { + success: false, + error: `Command failed: ${error.message}`, + }; + } +} + +/** + * Main tool executor - routes to appropriate tool + */ +export async function executeTool( + toolName: string, + input: any +): Promise { + switch (toolName) { + case 'Write': + case 'write': + case 'write_file': + return executeWriteTool(input); + + case 'Read': + case 'read': + case 'read_file': + return executeReadTool(input); + + case 'Bash': + case 'bash': + case 'shell': + case 'exec': + return executeBashTool(input); + + default: + return { + success: false, + error: `Unknown tool: ${toolName}`, + }; + } +}