diff --git a/.changeset/agent-timeout-description-2h.md b/.changeset/agent-timeout-description-2h.md new file mode 100644 index 0000000000..fa8ed92624 --- /dev/null +++ b/.changeset/agent-timeout-description-2h.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Align the Agent tool description with the resolved subagent timeout (default 2 hours) so the model no longer plans around a stale 30-minute cap. diff --git a/packages/agent-core/src/tools/builtin/collaboration/agent.md b/packages/agent-core/src/tools/builtin/collaboration/agent.md index ec0533e7ef..9a8cd45f12 100644 --- a/packages/agent-core/src/tools/builtin/collaboration/agent.md +++ b/packages/agent-core/src/tools/builtin/collaboration/agent.md @@ -9,7 +9,7 @@ Writing the prompt: Usage notes: - When the task continues earlier work a subagent already did, prefer resuming that agent (pass its `resume` id) over spawning a fresh instance — the resumed agent keeps its prior context. - A subagent's result is only visible to you, not to the user. When the user needs to see what a subagent produced, summarize the relevant parts yourself in your own reply. -- Subagents use a fixed 30-minute timeout. If one times out, resume the same agent instead of starting over. +- {{SUBAGENT_TIMEOUT_NOTE}} When NOT to use Agent: skip delegation for trivial work you can do directly — reading a file whose path you already know, searching a small known set of files, or any task that takes only a step or two. Delegation has a context-handoff cost; it pays off only when the task is substantial enough to outweigh it. diff --git a/packages/agent-core/src/tools/builtin/collaboration/agent.ts b/packages/agent-core/src/tools/builtin/collaboration/agent.ts index f5d8e0f948..344561da5f 100644 --- a/packages/agent-core/src/tools/builtin/collaboration/agent.ts +++ b/packages/agent-core/src/tools/builtin/collaboration/agent.ts @@ -147,7 +147,16 @@ export class AgentTool implements BuiltinTool { subagents, options?.showModelPreferences ?? false, ); - const baseDescription = `${AGENT_DESCRIPTION_BASE}\n\n${ + const timeoutMs = this.subagentTimeoutMs ?? DEFAULT_SUBAGENT_TIMEOUT_MS; + const timeoutNote = + timeoutMs === 0 + ? 'Subagents have no timeout in this session. If one is interrupted, resume the same agent instead of starting over.' + : `Subagents use a ${formatSubagentTimeoutDescription(timeoutMs)} timeout by default (overridable via config/env). If one times out, resume the same agent instead of starting over.`; + const descriptionBase = AGENT_DESCRIPTION_BASE.replace( + '{{SUBAGENT_TIMEOUT_NOTE}}', + timeoutNote, + ); + const baseDescription = `${descriptionBase}\n\n${ this.allowBackground ? AGENT_BACKGROUND_DESCRIPTION : AGENT_BACKGROUND_DISABLED_DESCRIPTION }`; const sections = [baseDescription]; diff --git a/packages/agent-core/test/tools/agent.test.ts b/packages/agent-core/test/tools/agent.test.ts index c2c4311346..9320f0ef99 100644 --- a/packages/agent-core/test/tools/agent.test.ts +++ b/packages/agent-core/test/tools/agent.test.ts @@ -124,11 +124,13 @@ describe('AgentTool', () => { expect(properties).not.toHaveProperty('timeout'); }); - it('explains the fixed background subagent timeout', () => { + it('explains the resolved background subagent timeout (default 2 hours)', () => { const host = mockSubagentHost({ spawn: vi.fn() }); const tool = agentTool(host); - expect(tool.description).toContain('fixed 30-minute timeout'); + expect(tool.description).toContain('2 hours timeout by default'); + expect(tool.description).not.toContain('fixed 2-hour timeout'); + expect(tool.description).not.toContain('fixed 30-minute timeout'); expect(tool.description).not.toContain('operator-configured background timeout'); expect(tool.description).not.toContain('no time limit'); // Background guidance must steer foreground-by-default, so the model doesn't @@ -136,6 +138,19 @@ describe('AgentTool', () => { expect(tool.description).toContain('Default to a foreground subagent'); }); + it('renders a custom resolved timeout and no-timeout configuration in the description', () => { + const host = mockSubagentHost({ spawn: vi.fn() }); + const custom = agentTool(host, createBackgroundManager().manager, undefined, { + subagentTimeoutMs: 30 * 60 * 1000, + }); + expect(custom.description).toContain('30 minutes timeout by default'); + + const unlimited = agentTool(host, createBackgroundManager().manager, undefined, { + subagentTimeoutMs: 0, + }); + expect(unlimited.description).toContain('Subagents have no timeout in this session'); + }); + it('exposes a primary/secondary model parameter in the JSON schema when the experiment is enabled', () => { const host = mockSubagentHost({ spawn: vi.fn() }); const tool = agentTool(host, createBackgroundManager().manager, undefined, {