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
22 changes: 22 additions & 0 deletions packages/agent/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,25 @@
# [mycoder-agent-v1.1.0](https://github.com/drivecore/mycoder/compare/mycoder-agent-v1.0.0...mycoder-agent-v1.1.0) (2025-03-12)

### Bug Fixes

- convert absolute paths to relative paths in textEditor log output ([a5ea845](https://github.com/drivecore/mycoder/commit/a5ea845c32bc569cda4330f59f1bf1553a236aea))
- implement resource cleanup to prevent CLI hanging issue ([d33e729](https://github.com/drivecore/mycoder/commit/d33e7298686a30661ee8b36f2fdffb16f5f3da71)), closes [#141](https://github.com/drivecore/mycoder/issues/141)
- llm choice working well for openai, anthropic and ollama ([68d34ab](https://github.com/drivecore/mycoder/commit/68d34abf8a73ed533a072359ce334a9364753425))
- **openai:** add OpenAI dependency to agent package and enable provider in config ([30b0807](https://github.com/drivecore/mycoder/commit/30b0807d4f3ecdd24f53b7ee4160645a4ed10444))
- replace @semantic-release/npm with @anolilab/semantic-release-pnpm to properly resolve workspace references ([bacb51f](https://github.com/drivecore/mycoder/commit/bacb51f637f2b2d3b1039bdfdbd33e3d704b6cde))
- up subagent iterations to 200 from 50 ([b405f1e](https://github.com/drivecore/mycoder/commit/b405f1e6d62eb5304dc1aa6c0ff28dc49dc67dce))

### Features

- add agent tracking to background tools ([4a3bcc7](https://github.com/drivecore/mycoder/commit/4a3bcc72f27af5fdbeeb407a748d5ecf3b7faed5))
- add Ollama configuration options ([d5c3a96](https://github.com/drivecore/mycoder/commit/d5c3a96ce9463c98504c2a346796400df36bf3b0))
- **agent:** implement agentStart and agentMessage tools ([62f8df3](https://github.com/drivecore/mycoder/commit/62f8df3dd083e2838c97ce89112f390461550ee6)), closes [#111](https://github.com/drivecore/mycoder/issues/111) [#111](https://github.com/drivecore/mycoder/issues/111)
- allow textEditor to overwrite existing files with create command ([d1cde65](https://github.com/drivecore/mycoder/commit/d1cde65df65bfcca288a47f14eedf5ad5939ed37)), closes [#192](https://github.com/drivecore/mycoder/issues/192)
- implement background tool tracking (issue [#112](https://github.com/drivecore/mycoder/issues/112)) ([b5bb489](https://github.com/drivecore/mycoder/commit/b5bb48981791acda74ee46b93d2d85e27e93a538))
- implement Ollama provider for LLM abstraction ([597211b](https://github.com/drivecore/mycoder/commit/597211b90e43c4d52969eb5994d393c15d85ec97))
- **llm:** add OpenAI support to LLM abstraction ([7bda811](https://github.com/drivecore/mycoder/commit/7bda811658e15b8dd41135cd9b2b90e9ea925e15))
- **refactor:** agent ([a2f59c2](https://github.com/drivecore/mycoder/commit/a2f59c2f51643a44d6e1ff0c16b319deb1adc3f2))

# mycoder-agent-v1.0.0 (2025-03-11)

### Bug Fixes
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "mycoder-agent",
"version": "1.0.1",
"version": "1.1.0",
"description": "Agent module for mycoder - an AI-powered software development assistant",
"type": "module",
"main": "dist/index.js",
Expand Down
84 changes: 13 additions & 71 deletions packages/agent/src/core/backgroundTools.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, it, vi, beforeEach } from 'vitest';

import {
backgroundToolRegistry,
BackgroundTools,
BackgroundToolStatus,
BackgroundToolType,
} from './backgroundTools.js';
Expand All @@ -12,53 +12,49 @@
}));

describe('BackgroundToolRegistry', () => {
let backgroundTools: BackgroundTools;
beforeEach(() => {
// Clear all registered tools before each test
const registry = backgroundToolRegistry as any;
registry.tools = new Map();
backgroundTools = new BackgroundTools('test');
backgroundTools.tools = new Map();
});

it('should register a shell process', () => {
const id = backgroundToolRegistry.registerShell('agent-1', 'ls -la');
const id = backgroundTools.registerShell('ls -la');

expect(id).toBe('test-id-1');

const tool = backgroundToolRegistry.getToolById(id);
const tool = backgroundTools.getToolById(id);
expect(tool).toBeDefined();
if (tool) {
expect(tool.type).toBe(BackgroundToolType.SHELL);
expect(tool.status).toBe(BackgroundToolStatus.RUNNING);
expect(tool.agentId).toBe('agent-1');
if (tool.type === BackgroundToolType.SHELL) {
expect(tool.metadata.command).toBe('ls -la');
}
}
});

it('should register a browser process', () => {
const id = backgroundToolRegistry.registerBrowser(
'agent-1',
'https://example.com',
);
const id = backgroundTools.registerBrowser('https://example.com');

expect(id).toBe('test-id-1');

const tool = backgroundToolRegistry.getToolById(id);
const tool = backgroundTools.getToolById(id);
expect(tool).toBeDefined();
if (tool) {
expect(tool.type).toBe(BackgroundToolType.BROWSER);
expect(tool.status).toBe(BackgroundToolStatus.RUNNING);
expect(tool.agentId).toBe('agent-1');
if (tool.type === BackgroundToolType.BROWSER) {
expect(tool.metadata.url).toBe('https://example.com');
}
}
});

it('should update tool status', () => {
const id = backgroundToolRegistry.registerShell('agent-1', 'sleep 10');
const id = backgroundTools.registerShell('sleep 10');

const updated = backgroundToolRegistry.updateToolStatus(
const updated = backgroundTools.updateToolStatus(
id,
BackgroundToolStatus.COMPLETED,
{
Expand All @@ -68,7 +64,7 @@

expect(updated).toBe(true);

const tool = backgroundToolRegistry.getToolById(id);
const tool = backgroundTools.getToolById(id);
expect(tool).toBeDefined();
if (tool) {
expect(tool.status).toBe(BackgroundToolStatus.COMPLETED);
Expand All @@ -80,57 +76,17 @@
});

it('should return false when updating non-existent tool', () => {
const updated = backgroundToolRegistry.updateToolStatus(
const updated = backgroundTools.updateToolStatus(
'non-existent-id',
BackgroundToolStatus.COMPLETED,
);

expect(updated).toBe(false);
});

it('should get tools by agent ID', () => {
// For this test, we'll directly manipulate the tools map
const registry = backgroundToolRegistry as any;
registry.tools = new Map();

// Add tools directly to the map with different agent IDs
registry.tools.set('id1', {
id: 'id1',
type: BackgroundToolType.SHELL,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId: 'agent-1',
metadata: { command: 'ls -la' },
});

registry.tools.set('id2', {
id: 'id2',
type: BackgroundToolType.BROWSER,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId: 'agent-1',
metadata: { url: 'https://example.com' },
});

registry.tools.set('id3', {
id: 'id3',
type: BackgroundToolType.SHELL,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId: 'agent-2',
metadata: { command: 'echo hello' },
});

const agent1Tools = backgroundToolRegistry.getToolsByAgent('agent-1');
const agent2Tools = backgroundToolRegistry.getToolsByAgent('agent-2');

expect(agent1Tools.length).toBe(2);
expect(agent2Tools.length).toBe(1);
});

it('should clean up old completed tools', () => {
// Create tools with specific dates
const registry = backgroundToolRegistry as any;
const registry = backgroundTools as any;

Check warning on line 89 in packages/agent/src/core/backgroundTools.test.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

// Add a completed tool from 25 hours ago
const oldTool = {
Expand Down Expand Up @@ -167,19 +123,5 @@
registry.tools.set('old-tool', oldTool);
registry.tools.set('recent-tool', recentTool);
registry.tools.set('old-running-tool', oldRunningTool);

// Clean up tools older than 24 hours
backgroundToolRegistry.cleanupOldTools(24);

// Old completed tool should be removed
expect(backgroundToolRegistry.getToolById('old-tool')).toBeUndefined();

// Recent completed tool should remain
expect(backgroundToolRegistry.getToolById('recent-tool')).toBeDefined();

// Old running tool should remain (not completed)
expect(
backgroundToolRegistry.getToolById('old-running-tool'),
).toBeDefined();
});
});
53 changes: 8 additions & 45 deletions packages/agent/src/core/backgroundTools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
status: BackgroundToolStatus;
startTime: Date;
endTime?: Date;
agentId: string; // To track which agent created this process
metadata: Record<string, any>; // Additional tool-specific information

Check warning on line 25 in packages/agent/src/core/backgroundTools.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
}

// Shell process specific data
Expand Down Expand Up @@ -61,30 +60,20 @@
/**
* Registry to keep track of all background processes
*/
export class BackgroundToolRegistry {
private static instance: BackgroundToolRegistry;
private tools: Map<string, AnyBackgroundTool> = new Map();
export class BackgroundTools {
tools: Map<string, AnyBackgroundTool> = new Map();

// Private constructor for singleton pattern
private constructor() {}

// Get the singleton instance
public static getInstance(): BackgroundToolRegistry {
if (!BackgroundToolRegistry.instance) {
BackgroundToolRegistry.instance = new BackgroundToolRegistry();
}
return BackgroundToolRegistry.instance;
}
constructor(readonly ownerName: string) {}

// Register a new shell process
public registerShell(agentId: string, command: string): string {
public registerShell(command: string): string {
const id = uuidv4();
const tool: ShellBackgroundTool = {
id,
type: BackgroundToolType.SHELL,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId,
metadata: {
command,
},
Expand All @@ -94,14 +83,13 @@
}

// Register a new browser process
public registerBrowser(agentId: string, url?: string): string {
public registerBrowser(url?: string): string {
const id = uuidv4();
const tool: BrowserBackgroundTool = {
id,
type: BackgroundToolType.BROWSER,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId,
metadata: {
url,
},
Expand All @@ -111,14 +99,13 @@
}

// Register a new agent process (for future use)
public registerAgent(agentId: string, goal?: string): string {
public registerAgent(goal?: string): string {
const id = uuidv4();
const tool: AgentBackgroundTool = {
id,
type: BackgroundToolType.AGENT,
status: BackgroundToolStatus.RUNNING,
startTime: new Date(),
agentId,
metadata: {
goal,
},
Expand All @@ -131,7 +118,7 @@
public updateToolStatus(
id: string,
status: BackgroundToolStatus,
metadata?: Record<string, any>,

Check warning on line 121 in packages/agent/src/core/backgroundTools.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
): boolean {
const tool = this.tools.get(id);
if (!tool) {
Expand All @@ -155,13 +142,10 @@
return true;
}

// Get all processes for a specific agent
public getToolsByAgent(agentId: string): AnyBackgroundTool[] {
public getTools(): AnyBackgroundTool[] {
const result: AnyBackgroundTool[] = [];
for (const tool of this.tools.values()) {
if (tool.agentId === agentId) {
result.push(tool);
}
result.push(tool);
}
return result;
}
Expand All @@ -170,25 +154,4 @@
public getToolById(id: string): AnyBackgroundTool | undefined {
return this.tools.get(id);
}

// Clean up completed processes (optional, for maintenance)
public cleanupOldTools(olderThanHours: number = 24): void {
const cutoffTime = new Date(Date.now() - olderThanHours * 60 * 60 * 1000);

for (const [id, tool] of this.tools.entries()) {
// Remove if it's completed/error/terminated AND older than cutoff
if (
tool.endTime &&
tool.endTime < cutoffTime &&
(tool.status === BackgroundToolStatus.COMPLETED ||
tool.status === BackgroundToolStatus.ERROR ||
tool.status === BackgroundToolStatus.TERMINATED)
) {
this.tools.delete(id);
}
}
}
}

// Export singleton instance
export const backgroundToolRegistry = BackgroundToolRegistry.getInstance();
14 changes: 9 additions & 5 deletions packages/agent/src/core/toolAgent/toolAgentCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ export const toolAgent = async (

logger.debug('User message:', initialPrompt);

const localContext = {
...context,
};

// Get the system prompt once at the start
const systemPrompt = config.getSystemPrompt(context);
const systemPrompt = config.getSystemPrompt(localContext);

// Create the LLM provider
const provider = createProvider(context.provider, context.model);
const provider = createProvider(localContext.provider, localContext.model);

for (let i = 0; i < config.maxIterations; i++) {
logger.verbose(
Expand Down Expand Up @@ -74,8 +78,8 @@ export const toolAgent = async (
const generateOptions = {
messages: messagesWithSystem,
functions: functionDefinitions,
temperature: context.temperature,
maxTokens: context.maxTokens,
temperature: localContext.temperature,
maxTokens: localContext.maxTokens,
};

const { text, toolCalls, tokenUsage } = await generateText(
Expand Down Expand Up @@ -123,7 +127,7 @@ export const toolAgent = async (

// Execute the tools and get results
const { sequenceCompleted, completionResult, respawn } =
await executeTools(toolCalls, tools, messages, context);
await executeTools(toolCalls, tools, messages, localContext);

if (respawn) {
logger.info('Respawning agent with new context');
Expand Down
2 changes: 2 additions & 0 deletions packages/agent/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { JsonSchema7Type } from 'zod-to-json-schema';

import { Logger } from '../utils/logger.js';

import { BackgroundTools } from './backgroundTools.js';
import { TokenTracker } from './tokens.js';
import { ModelProvider } from './toolAgent/config.js';

Expand All @@ -26,6 +27,7 @@ export type ToolContext = {
model: string;
maxTokens: number;
temperature: number;
backgroundTools: BackgroundTools;
};

export type Tool<TParams = Record<string, any>, TReturn = any> = {
Expand Down
Loading
Loading