feat(desktop): support adaptive tool timeouts#409
Conversation
📝 WalkthroughSummary by CodeRabbit
WalkthroughThis PR adds per-call timeout override capability for tool execution. Agents can now request specific timeouts via ChangesAgent-directed tool call timeout control
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/desktop/src/services/AgentService/execution/executor.ts`:
- Around line 350-383: The sanitization logic in parseRequestedTimeoutMs uses a
conditional shallow copy which is more complex and can return the original
object; always create a shallow copy and remove the meta key to avoid reference
mutations. Replace the conditional copy with a single copy of toolArgs into
sanitizedToolArgs and always delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY];
keep the rest of the validation logic for timeoutMs unchanged so
parseRequestedTimeoutMs and usage of TOOL_TIMEOUT_META_KEY remain consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: e29b5843-a224-4513-b59e-fc2775f440b3
📒 Files selected for processing (7)
apps/desktop/src/services/AgentService/catalog/tools.tsapps/desktop/src/services/AgentService/execution/executor.tsapps/desktop/src/services/AgentService/infrastructure/mcp/McpManager.tsapps/desktop/src/services/BuiltInToolService/service.tsapps/desktop/src/services/BuiltInToolService/tools/bash/index.tsapps/desktop/src/services/BuiltInToolService/types.tsapps/desktop/src/utils/timeouts.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL (rust)
- GitHub Check: Desktop E2E Smoke (Windows)
🔇 Additional comments (8)
apps/desktop/src/utils/timeouts.ts (1)
1-13: LGTM!apps/desktop/src/services/AgentService/catalog/tools.ts (2)
14-26: LGTM!
28-39: LGTM!Also applies to: 57-63
apps/desktop/src/services/AgentService/execution/executor.ts (1)
777-779: LGTM!Also applies to: 783-783, 793-793, 802-802, 808-808
apps/desktop/src/services/BuiltInToolService/types.ts (1)
45-45: LGTM!apps/desktop/src/services/BuiltInToolService/service.ts (1)
52-52: LGTM!Also applies to: 261-261
apps/desktop/src/services/BuiltInToolService/tools/bash/index.ts (1)
157-164: LGTM!Also applies to: 171-171
apps/desktop/src/services/AgentService/infrastructure/mcp/McpManager.ts (1)
442-442: LGTM!Also applies to: 460-466, 472-472
| function parseRequestedTimeoutMs(toolArgs: Record<string, unknown>): { | ||
| requestedTimeoutMs?: number; | ||
| sanitizedToolArgs: Record<string, unknown>; | ||
| } { | ||
| const meta = toolArgs[TOOL_TIMEOUT_META_KEY]; | ||
| const sanitizedToolArgs = TOOL_TIMEOUT_META_KEY in toolArgs ? { ...toolArgs } : toolArgs; | ||
| if (TOOL_TIMEOUT_META_KEY in sanitizedToolArgs) { | ||
| delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY]; | ||
| } | ||
|
|
||
| if (!meta || typeof meta !== 'object' || Array.isArray(meta)) { | ||
| return { | ||
| requestedTimeoutMs: undefined, | ||
| sanitizedToolArgs, | ||
| }; | ||
| } | ||
|
|
||
| const timeoutMs = (meta as Record<string, unknown>).timeoutMs; | ||
| if ( | ||
| typeof timeoutMs !== 'number' || | ||
| !Number.isFinite(timeoutMs) || | ||
| !Number.isInteger(timeoutMs) | ||
| ) { | ||
| return { | ||
| requestedTimeoutMs: undefined, | ||
| sanitizedToolArgs, | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| requestedTimeoutMs: timeoutMs, | ||
| sanitizedToolArgs, | ||
| }; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Consider simplifying the sanitization copy logic.
The conditional shallow copy at line 355 works correctly but is more complex than necessary. The code only creates a copy when _meta exists, then deletes it. A simpler and safer approach would be to always copy and always delete:
const sanitizedToolArgs = { ...toolArgs };
delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY];This is clearer (one code path instead of two) and safer (always returns a new object, preventing potential reference-based mutations).
♻️ Proposed refactor
function parseRequestedTimeoutMs(toolArgs: Record<string, unknown>): {
requestedTimeoutMs?: number;
sanitizedToolArgs: Record<string, unknown>;
} {
const meta = toolArgs[TOOL_TIMEOUT_META_KEY];
- const sanitizedToolArgs = TOOL_TIMEOUT_META_KEY in toolArgs ? { ...toolArgs } : toolArgs;
- if (TOOL_TIMEOUT_META_KEY in sanitizedToolArgs) {
- delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY];
- }
+ const sanitizedToolArgs = { ...toolArgs };
+ delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY];
if (!meta || typeof meta !== 'object' || Array.isArray(meta)) {
return {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/desktop/src/services/AgentService/execution/executor.ts` around lines
350 - 383, The sanitization logic in parseRequestedTimeoutMs uses a conditional
shallow copy which is more complex and can return the original object; always
create a shallow copy and remove the meta key to avoid reference mutations.
Replace the conditional copy with a single copy of toolArgs into
sanitizedToolArgs and always delete sanitizedToolArgs[TOOL_TIMEOUT_META_KEY];
keep the rest of the validation logic for timeoutMs unchanged so
parseRequestedTimeoutMs and usage of TOOL_TIMEOUT_META_KEY remain consistent.
Summary
This change allows the LLM to suggest tool call timeout values dynamically instead of relying only on static defaults.
User-facing impact:
_meta.timeoutMsas execution metadataImplementation summary:
_meta.timeoutMsin tool schemas before tools are provided to the model_metavalues before forwarding arguments downstreamRelated issue or RFC
AI assistance disclosure
AgentService, MCP timeout handling, tool schema exposure, and built-in tool execution boundariesTesting evidence
pnpm type:checkpnpm test:prpnpm test:coverage:rustcargo-tarpaulinis not installed locally, so CI coverage evidence should be used before mergepnpm test:e2eDid you follow TDD (test-first) for feature and fix work?
Risk notes
AgentService, runtime, MCP, or schema impact:_meta.timeoutMs1000msand maximum of600000msScreenshots or recordings
_meta.timeoutMsexposed in tool schemaChecklist
[WIP]or similar title prefixes.AgentService, runtime, MCP, or schema boundaries, there is an accepted RFC.pnpm test:prfor this code PR, or this is a docs-only change.pnpm test:coverage:rustor relied on CI coverage evidence.pnpm test:e2elocally or documented why CI is the first valid proof.