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
10 changes: 5 additions & 5 deletions docs/tools/agent-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

The agent tools provide ways to create and interact with sub-agents. There are two approaches available:

1. The original `subAgent` tool (synchronous, blocking)
1. The original `agentExecute` tool (synchronous, blocking)
2. The new `agentStart` and `agentMessage` tools (asynchronous, non-blocking)

## subAgent Tool
## agentExecute Tool

The `subAgent` tool creates a sub-agent that runs synchronously until completion. The parent agent waits for the sub-agent to complete before continuing.
The `agentExecute` tool creates a sub-agent that runs synchronously until completion. The parent agent waits for the sub-agent to complete before continuing.

```typescript
subAgent({
agentExecute({
description: "A brief description of the sub-agent's purpose",
goal: 'The main objective that the sub-agent needs to achieve',
projectContext: 'Context about the problem or environment',
Expand Down Expand Up @@ -123,7 +123,7 @@ while (!agent1Completed || !agent2Completed) {

## Choosing Between Approaches

- Use `subAgent` for simpler tasks where blocking execution is acceptable
- Use `agentExecute` for simpler tasks where blocking execution is acceptable
- Use `agentStart` and `agentMessage` for:
- Parallel execution of multiple sub-agents
- Tasks where you need to monitor progress
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/core/toolAgent/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function getDefaultSystemPrompt(toolContext: ToolContext): string {
githubModeInstructions,
'',
'You prefer to call tools in parallel when possible because it leads to faster execution and less resource usage.',
'When done, call the sequenceComplete tool with your results to indicate that the sequence has completed.',
'When done, call the agentDone tool with your results to indicate that the sequence has completed.',
'',
'For coding tasks:',
'0. Try to break large tasks into smaller sub-tasks that can be completed and verified sequentially.',
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/core/toolAgent/toolAgentCore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('toolAgentCore empty response detection', () => {
content: [
{
type: 'text',
text: 'I notice you sent an empty response. If you are done with your tasks, please call the sequenceComplete tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.',
text: 'I notice you sent an empty response. If you are done with your tasks, please call the agentDone tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.',
},
],
});
Expand Down
12 changes: 8 additions & 4 deletions packages/agent/src/core/toolAgent/toolAgentCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ export const toolAgent = async (
messages.push({
role: 'user',
content:
'I notice you sent an empty response. If you are done with your tasks, please call the sequenceComplete tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.',
'I notice you sent an empty response. If you are done with your tasks, please call the agentDone tool with your results. If you are waiting for other tools to complete, you can use the sleep tool to wait before checking again.',
});
continue;
}
Expand Down Expand Up @@ -129,8 +129,12 @@ export const toolAgent = async (
);

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

if (respawn) {
logger.info('Respawning agent with new context');
Expand All @@ -143,7 +147,7 @@ export const toolAgent = async (
continue;
}

if (sequenceCompleted) {
if (agentDoned) {
const result: ToolAgentResult = {
result: completionResult ?? 'Sequence explicitly completed',
interactions,
Expand Down
16 changes: 7 additions & 9 deletions packages/agent/src/core/toolAgent/toolExecutor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
context: ToolContext,
): Promise<ToolCallResult> {
if (toolCalls.length === 0) {
return { sequenceCompleted: false, toolResults: [] };
return { agentDoned: false, toolResults: [] };
}

const { logger } = context;
Expand All @@ -46,7 +46,7 @@
addToolResultToMessages(messages, respawnCall.id, { success: true }, false);

return {
sequenceCompleted: false,
agentDoned: false,
toolResults: [
{
toolCallId: respawnCall.id,
Expand All @@ -69,7 +69,7 @@
...context,
tokenTracker: new TokenTracker(call.name, context.tokenTracker),
});
} catch (errorStr: any) {

Check warning on line 72 in packages/agent/src/core/toolAgent/toolExecutor.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
isError = true;
if (errorStr instanceof Error) {
if (errorStr.stack) {
Expand Down Expand Up @@ -97,19 +97,17 @@
}),
);

const sequenceCompletedTool = toolResults.find(
(r) => r.toolName === 'sequenceComplete',
);
const completionResult = sequenceCompletedTool
? (sequenceCompletedTool.result as { result: string }).result
const agentDonedTool = toolResults.find((r) => r.toolName === 'agentDone');
const completionResult = agentDonedTool
? (agentDonedTool.result as { result: string }).result
: undefined;

if (sequenceCompletedTool) {
if (agentDonedTool) {
logger.verbose('Sequence completed', { completionResult });
}

return {
sequenceCompleted: sequenceCompletedTool !== undefined,
agentDoned: agentDonedTool !== undefined,
completionResult,
toolResults,
};
Expand Down
2 changes: 1 addition & 1 deletion packages/agent/src/core/toolAgent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export interface ToolAgentResult {
}

export interface ToolCallResult {
sequenceCompleted: boolean;
agentDoned: boolean;
completionResult?: string;
toolResults: unknown[];
respawn?: { context: string };
Expand Down
8 changes: 4 additions & 4 deletions packages/agent/src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { z } from 'zod';
import { JsonSchema7Type } from 'zod-to-json-schema';

import { BrowserTracker } from '../tools/browser/browserTracker.js';
import { AgentTracker } from '../tools/interaction/agentTracker.js';
import { ShellTracker } from '../tools/system/shellTracker.js';
import { AgentTracker } from '../tools/agent/AgentTracker.js';
import { SessionTracker } from '../tools/session/SessionTracker.js';
import { ShellTracker } from '../tools/shell/ShellTracker.js';
import { Logger } from '../utils/logger.js';

import { TokenTracker } from './tokens.js';
Expand Down Expand Up @@ -34,10 +34,10 @@
temperature: number;
agentTracker: AgentTracker;
shellTracker: ShellTracker;
browserTracker: BrowserTracker;
browserTracker: SessionTracker;
};

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

Check warning on line 40 in packages/agent/src/core/types.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type

Check warning on line 40 in packages/agent/src/core/types.ts

View workflow job for this annotation

GitHub Actions / ci

Unexpected any. Specify a different type
name: string;
description: string;
parameters: z.ZodType<TParams>;
Expand Down
32 changes: 16 additions & 16 deletions packages/agent/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,28 @@
export * from './tools/io/fetch.js';

// Tools - System
export * from './tools/system/shellStart.js';
export * from './tools/shell/shellStart.js';
export * from './tools/system/sleep.js';
export * from './tools/system/respawn.js';
export * from './tools/system/sequenceComplete.js';
export * from './tools/system/shellMessage.js';
export * from './tools/system/shellExecute.js';
export * from './tools/system/listShells.js';
export * from './tools/system/shellTracker.js';
export * from './tools/agent/agentDone.js';
export * from './tools/shell/shellMessage.js';
export * from './tools/shell/shellExecute.js';
export * from './tools/shell/listShells.js';
export * from './tools/shell/ShellTracker.js';

// Tools - Browser
export * from './tools/browser/BrowserManager.js';
export * from './tools/browser/types.js';
export * from './tools/browser/browseMessage.js';
export * from './tools/browser/browseStart.js';
export * from './tools/browser/PageController.js';
export * from './tools/browser/BrowserAutomation.js';
export * from './tools/browser/listBrowsers.js';
export * from './tools/browser/browserTracker.js';
export * from './tools/session/lib/SessionManager.js';
export * from './tools/session/lib/types.js';
export * from './tools/session/sessionMessage.js';
export * from './tools/session/sessionStart.js';
export * from './tools/session/lib/PageController.js';
export * from './tools/session/lib/BrowserAutomation.js';
export * from './tools/session/listSessions.js';
export * from './tools/session/SessionTracker.js';

export * from './tools/interaction/agentTracker.js';
export * from './tools/agent/AgentTracker.js';
// Tools - Interaction
export * from './tools/interaction/subAgent.js';
export * from './tools/agent/agentExecute.js';
export * from './tools/interaction/userPrompt.js';

// Core
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ const returnSchema = z.object({
type Parameters = z.infer<typeof parameterSchema>;
type ReturnType = z.infer<typeof returnSchema>;

export const sequenceCompleteTool: Tool<Parameters, ReturnType> = {
name: 'sequenceComplete',
export const agentDoneTool: Tool<Parameters, ReturnType> = {
name: 'agentDone',
description: 'Completes the tool use sequence and returns the final result',
logPrefix: '✅',
parameters: parameterSchema,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { describe, expect, it, vi } from 'vitest';
import { TokenTracker } from '../../core/tokens.js';
import { ToolContext } from '../../core/types.js';
import { MockLogger } from '../../utils/mockLogger.js';
import { BrowserTracker } from '../browser/browserTracker.js';
import { ShellTracker } from '../system/shellTracker.js';
import { SessionTracker } from '../session/SessionTracker.js';
import { ShellTracker } from '../shell/ShellTracker.js';

import { AgentTracker } from './agentTracker.js';
import { subAgentTool } from './subAgent.js';
import { agentExecuteTool } from './agentExecute.js';
import { AgentTracker } from './AgentTracker.js';

// Mock the toolAgent function
vi.mock('../../core/toolAgent/toolAgentCore.js', () => ({
Expand Down Expand Up @@ -37,12 +37,12 @@ const mockContext: ToolContext = {
temperature: 0.7,
agentTracker: new AgentTracker('test'),
shellTracker: new ShellTracker('test'),
browserTracker: new BrowserTracker('test'),
browserTracker: new SessionTracker('test'),
};

describe('subAgentTool', () => {
describe('agentExecuteTool', () => {
it('should create a sub-agent and return its response', async () => {
const result = await subAgentTool.execute(
const result = await agentExecuteTool.execute(
{
description: 'Test sub-agent',
goal: 'Test the sub-agent tool',
Expand All @@ -58,7 +58,7 @@ describe('subAgentTool', () => {
it('should use custom working directory when provided', async () => {
const { toolAgent } = await import('../../core/toolAgent/toolAgentCore.js');

await subAgentTool.execute(
await agentExecuteTool.execute(
{
description: 'Test sub-agent with custom directory',
goal: 'Test the sub-agent tool',
Expand All @@ -82,7 +82,7 @@ describe('subAgentTool', () => {
it('should include relevant files in the prompt when provided', async () => {
const { toolAgent } = await import('../../core/toolAgent/toolAgentCore.js');

await subAgentTool.execute(
await agentExecuteTool.execute(
{
description: 'Test sub-agent with relevant files',
goal: 'Test the sub-agent tool',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ import {
} from '../../core/toolAgent/config.js';
import { toolAgent } from '../../core/toolAgent/toolAgentCore.js';
import { Tool, ToolContext } from '../../core/types.js';
import { BrowserTracker } from '../browser/browserTracker.js';
import { getTools } from '../getTools.js';
import { ShellTracker } from '../system/shellTracker.js';
import { SessionTracker } from '../session/SessionTracker.js';
import { ShellTracker } from '../shell/ShellTracker.js';

import { AgentTracker } from './agentTracker.js';
import { AgentTracker } from './AgentTracker.js';

const parameterSchema = z.object({
description: z
Expand Down Expand Up @@ -45,22 +45,22 @@ type Parameters = z.infer<typeof parameterSchema>;
type ReturnType = z.infer<typeof returnSchema>;

// Sub-agent specific configuration
const subAgentConfig: AgentConfig = {
const agentConfig: AgentConfig = {
maxIterations: 200,
getSystemPrompt: (context: ToolContext) => {
return [
getDefaultSystemPrompt(context),
'You are a focused AI sub-agent handling a specific task.',
'You have access to the same tools as the main agent but should focus only on your assigned task.',
'When complete, call the sequenceComplete tool with your results.',
'When complete, call the agentDone tool with your results.',
'Follow any specific conventions or requirements provided in the task context.',
'Ask the main agent for clarification if critical information is missing.',
].join('\n');
},
};

export const subAgentTool: Tool<Parameters, ReturnType> = {
name: 'subAgent',
export const agentExecuteTool: Tool<Parameters, ReturnType> = {
name: 'agentExecute',
description:
'Creates a sub-agent that has access to all tools to solve a specific task',
logPrefix: '🤖',
Expand Down Expand Up @@ -89,7 +89,7 @@ export const subAgentTool: Tool<Parameters, ReturnType> = {
workingDirectory: workingDirectory ?? context.workingDirectory,
agentTracker: new AgentTracker(subAgentId),
shellTracker: new ShellTracker(subAgentId),
browserTracker: new BrowserTracker(subAgentId),
browserTracker: new SessionTracker(subAgentId),
};

// Construct a well-structured prompt
Expand All @@ -107,9 +107,9 @@ export const subAgentTool: Tool<Parameters, ReturnType> = {

const tools = getTools({ userPrompt: false });

// Use the subAgentConfig
// Use the agentConfig
const config: AgentConfig = {
...subAgentConfig,
...agentConfig,
};

try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { toolAgent } from '../../core/toolAgent/toolAgentCore.js';
import { Tool, ToolContext } from '../../core/types.js';
import { getTools } from '../getTools.js';

import { AgentStatus, AgentState } from './agentTracker.js';
import { AgentStatus, AgentState } from './AgentTracker.js';

// For backward compatibility
export const agentStates = new Map<string, AgentState>();
Expand Down Expand Up @@ -49,14 +49,14 @@ type Parameters = z.infer<typeof parameterSchema>;
type ReturnType = z.infer<typeof returnSchema>;

// Sub-agent specific configuration
const subAgentConfig: AgentConfig = {
const agentConfig: AgentConfig = {
maxIterations: 200,
getSystemPrompt: (context: ToolContext) => {
return [
getDefaultSystemPrompt(context),
'You are a focused AI sub-agent handling a specific task.',
'You have access to the same tools as the main agent but should focus only on your assigned task.',
'When complete, call the sequenceComplete tool with your results.',
'When complete, call the agentDone tool with your results.',
'Follow any specific conventions or requirements provided in the task context.',
'Ask the main agent for clarification if critical information is missing.',
].join('\n');
Expand Down Expand Up @@ -128,7 +128,7 @@ export const agentStartTool: Tool<Parameters, ReturnType> = {
// eslint-disable-next-line promise/catch-or-return
Promise.resolve().then(async () => {
try {
const result = await toolAgent(prompt, tools, subAgentConfig, {
const result = await toolAgent(prompt, tools, agentConfig, {
...context,
workingDirectory: workingDirectory ?? context.workingDirectory,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import { describe, expect, it, vi } from 'vitest';
import { TokenTracker } from '../../core/tokens.js';
import { ToolContext } from '../../core/types.js';
import { MockLogger } from '../../utils/mockLogger.js';
import { BrowserTracker } from '../browser/browserTracker.js';
import { ShellTracker } from '../system/shellTracker.js';
import { SessionTracker } from '../session/SessionTracker.js';
import { ShellTracker } from '../shell/ShellTracker.js';

import { agentMessageTool } from './agentMessage.js';
import { agentStartTool, agentStates } from './agentStart.js';
import { AgentTracker } from './agentTracker.js';
import { AgentTracker } from './AgentTracker.js';

// Mock the toolAgent function
vi.mock('../../core/toolAgent/toolAgentCore.js', () => ({
Expand All @@ -33,7 +33,7 @@ const mockContext: ToolContext = {
temperature: 0.7,
agentTracker: new AgentTracker('test'),
shellTracker: new ShellTracker('test'),
browserTracker: new BrowserTracker('test'),
browserTracker: new SessionTracker('test'),
};

describe('Agent Tools', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

import { Tool } from '../../core/types.js';
import { AgentStatus } from '../interaction/agentTracker.js';

import { AgentStatus } from './AgentTracker.js';

const parameterSchema = z.object({
status: z
Expand Down
Loading
Loading