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
4 changes: 2 additions & 2 deletions docs/core/subagents.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ it yourself; just report it.
| `tools` | array | No | List of tool names this agent can use. If omitted, it may have access to a default set. |
| `model` | string | No | Specific model to use (e.g., `gemini-2.5-pro`). Defaults to `inherit` (uses the main session model). |
| `temperature` | number | No | Model temperature (0.0 - 2.0). |
| `max_turns` | number | No | Maximum number of conversation turns allowed for this agent before it must return. |
| `timeout_mins` | number | No | Maximum execution time in minutes. |
| `max_turns` | number | No | Maximum number of conversation turns allowed for this agent before it must return. Defaults to `15`. |
| `timeout_mins` | number | No | Maximum execution time in minutes. Defaults to `5`. |

### Optimizing your sub-agent

Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/agents/agentLoader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
} from './agentLoader.js';
import { GEMINI_MODEL_ALIAS_PRO } from '../config/models.js';
import type { LocalAgentDefinition } from './types.js';
import { DEFAULT_MAX_TIME_MINUTES, DEFAULT_MAX_TURNS } from './types.js';

describe('loader', () => {
let tempDir: string;
Expand Down Expand Up @@ -237,7 +238,8 @@ Body`);
},
},
runConfig: {
maxTimeMinutes: 5,
maxTimeMinutes: DEFAULT_MAX_TIME_MINUTES,
maxTurns: DEFAULT_MAX_TURNS,
},
inputConfig: {
inputSchema: {
Expand Down
10 changes: 7 additions & 3 deletions packages/core/src/agents/agentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@ import { type Dirent } from 'node:fs';
import * as path from 'node:path';
import * as crypto from 'node:crypto';
import { z } from 'zod';
import type { AgentDefinition } from './types.js';
import {
type AgentDefinition,
DEFAULT_MAX_TURNS,
DEFAULT_MAX_TIME_MINUTES,
} from './types.js';
import { isValidToolName } from '../tools/tool-names.js';
import { FRONTMATTER_REGEX } from '../skills/skillLoader.js';
import { getErrorMessage } from '../utils/errors.js';
Expand Down Expand Up @@ -290,8 +294,8 @@ export function markdownToAgentDefinition(
},
},
runConfig: {
maxTurns: markdown.max_turns,
maxTimeMinutes: markdown.timeout_mins || 5,
maxTurns: markdown.max_turns ?? DEFAULT_MAX_TURNS,
maxTimeMinutes: markdown.timeout_mins ?? DEFAULT_MAX_TIME_MINUTES,
},
toolConfig: markdown.tools
? {
Expand Down
26 changes: 16 additions & 10 deletions packages/core/src/agents/local-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ import type {
OutputObject,
SubagentActivityEvent,
} from './types.js';
import { AgentTerminateMode, DEFAULT_QUERY_STRING } from './types.js';
import {
AgentTerminateMode,
DEFAULT_QUERY_STRING,
DEFAULT_MAX_TURNS,
DEFAULT_MAX_TIME_MINUTES,
} from './types.js';
import { templateString } from './utils.js';
import { DEFAULT_GEMINI_MODEL, isAutoModel } from '../config/models.js';
import type { RoutingContext } from '../routing/routingStrategy.js';
Expand Down Expand Up @@ -406,7 +411,10 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
let terminateReason: AgentTerminateMode = AgentTerminateMode.ERROR;
let finalResult: string | null = null;

const { maxTimeMinutes } = this.definition.runConfig;
const maxTimeMinutes =
this.definition.runConfig.maxTimeMinutes ?? DEFAULT_MAX_TIME_MINUTES;
const maxTurns = this.definition.runConfig.maxTurns ?? DEFAULT_MAX_TURNS;

const timeoutController = new AbortController();
const timeoutId = setTimeout(
() => timeoutController.abort(new Error('Agent timed out.')),
Expand Down Expand Up @@ -441,7 +449,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {

while (true) {
// Check for termination conditions like max turns.
const reason = this.checkTermination(startTime, turnCounter);
const reason = this.checkTermination(turnCounter, maxTurns);
if (reason) {
terminateReason = reason;
break;
Expand Down Expand Up @@ -499,13 +507,13 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
} else {
// Recovery Failed. Set the final error message based on the *original* reason.
if (terminateReason === AgentTerminateMode.TIMEOUT) {
finalResult = `Agent timed out after ${this.definition.runConfig.maxTimeMinutes} minutes.`;
finalResult = `Agent timed out after ${maxTimeMinutes} minutes.`;
this.emitActivity('ERROR', {
error: finalResult,
context: 'timeout',
});
} else if (terminateReason === AgentTerminateMode.MAX_TURNS) {
finalResult = `Agent reached max turns limit (${this.definition.runConfig.maxTurns}).`;
finalResult = `Agent reached max turns limit (${maxTurns}).`;
this.emitActivity('ERROR', {
error: finalResult,
context: 'max_turns',
Expand Down Expand Up @@ -569,7 +577,7 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
}

// Recovery failed or wasn't possible
finalResult = `Agent timed out after ${this.definition.runConfig.maxTimeMinutes} minutes.`;
finalResult = `Agent timed out after ${maxTimeMinutes} minutes.`;
this.emitActivity('ERROR', {
error: finalResult,
context: 'timeout',
Expand Down Expand Up @@ -1160,12 +1168,10 @@ Important Rules:
* @returns The reason for termination, or `null` if execution can continue.
*/
private checkTermination(
startTime: number,
turnCounter: number,
maxTurns: number,
): AgentTerminateMode | null {
const { runConfig } = this.definition;

if (runConfig.maxTurns && turnCounter >= runConfig.maxTurns) {
if (turnCounter >= maxTurns) {
return AgentTerminateMode.MAX_TURNS;
}

Expand Down
22 changes: 19 additions & 3 deletions packages/core/src/agents/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@ export interface OutputObject {
*/
export const DEFAULT_QUERY_STRING = 'Get Started!';

/**
* The default maximum number of conversational turns for an agent.
*/
export const DEFAULT_MAX_TURNS = 15;

/**
* The default maximum execution time for an agent in minutes.
*/
export const DEFAULT_MAX_TIME_MINUTES = 5;

/**
* Represents the validated input parameters passed to an agent upon invocation.
* Used primarily for templating the system prompt. (Replaces ContextState)
Expand Down Expand Up @@ -183,8 +193,14 @@ export interface OutputConfig<T extends z.ZodTypeAny> {
* Configures the execution environment and constraints for the agent.
*/
export interface RunConfig {
/** The maximum execution time for the agent in minutes. */
maxTimeMinutes: number;
/** The maximum number of conversational turns. */
/**
* The maximum execution time for the agent in minutes.
* If not specified, defaults to DEFAULT_MAX_TIME_MINUTES (5).
*/
maxTimeMinutes?: number;
/**
* The maximum number of conversational turns.
* If not specified, defaults to DEFAULT_MAX_TURNS (15).
*/
maxTurns?: number;
}
Loading