Skip to content
Open
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
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,7 @@ ENV/
CLAUDE.md
.mcp.json
.playwright-mcp/
notepad/
notepad/

# Bob Agent workspace (task outputs)
bob-workspace/
4 changes: 0 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions packages/memory/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export class MemoryManager {
const transport = new StdioClientTransport({
command: pythonCommand,
args: [this.serverPath],
env: {
...process.env as Record<string, string>, // Pass all environment variables including OPENAI_API_KEY
},
});

this.client = new Client({
Expand Down
14 changes: 10 additions & 4 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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<string, any>
): Promise<any> {
// 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,
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
});

Expand Down
134 changes: 134 additions & 0 deletions src/tools.ts
Original file line number Diff line number Diff line change
@@ -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<ToolResult> {
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<ToolResult> {
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<ToolResult> {
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<ToolResult> {
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}`,
};
}
}