feat(core): add default execution limits for subagents (#18274)

This commit is contained in:
Abhi
2026-02-04 01:28:00 -05:00
committed by GitHub
parent 94f4e5cc15
commit b39cefe14e
5 changed files with 47 additions and 19 deletions

View File

@@ -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

View File

@@ -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;
@@ -237,7 +238,8 @@ Body`);
},
},
runConfig: {
maxTimeMinutes: 5,
maxTimeMinutes: DEFAULT_MAX_TIME_MINUTES,
maxTurns: DEFAULT_MAX_TURNS,
},
inputConfig: {
inputSchema: {

View File

@@ -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';
@@ -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
? {

View File

@@ -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';
@@ -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.')),
@@ -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;
@@ -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',
@@ -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',
@@ -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;
}

View File

@@ -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)
@@ -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;
}