From 6e9a3ff657901c1f7c9615d6ac6bf08c207e9c7c Mon Sep 17 00:00:00 2001 From: Blomios Date: Fri, 24 Jul 2026 15:00:53 +0200 Subject: [PATCH] =?UTF-8?q?fix(agent):=20timeout=20MCP=20OpenCode=20align?= =?UTF-8?q?=C3=A9=20sur=20le=20plafond=20du=20rendez-vous=20(#95)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Le timeout hardcoded 15000ms (15s) dans le bloc mcp.idea de l'opencode.json généré par IdeA était far too short pour idea_ask_agent, qui bloque pendant que l'agent cible travaille. OpenCode tuait la requête à 15s avec l'erreur -32001 « Request timed out », même si la cible répondait correctement (visible dans le workstate). Claude/Codex n'étaient pas affectés car leur config MCP n'a pas de champ timeout. Désormais le timeout est résolu par resolve_opencode_mcp_timeout_ms() : - défaut 4h (14_400_000 ms), aligné sur DEFAULT_RENDEZVOUS_CEILING ; - overridable via IDEA_OPENCODE_MCP_TIMEOUT_MS ; - fallback sûr sur 0/parse-error (jamais d'expiration instantanée). Les 4 occurrences (lifecycle.rs x2 + assistant/mod.rs x2) utilisent la même fonction exportée depuis application::agent. Aucune modification des configs MCP Claude/Codex. --- crates/application/src/agent/lifecycle.rs | 29 ++++++++++++++++++++-- crates/application/src/agent/mod.rs | 3 ++- crates/infrastructure/src/assistant/mod.rs | 4 +-- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/application/src/agent/lifecycle.rs b/crates/application/src/agent/lifecycle.rs index 85a1dad..786d246 100644 --- a/crates/application/src/agent/lifecycle.rs +++ b/crates/application/src/agent/lifecycle.rs @@ -2645,6 +2645,31 @@ fn mcp_server_wiring( /// /// The MCP server is named `idea`; OpenCode therefore exposes IdeA tools as /// `idea_idea_*` (`_`), matching the observed contract. +/// Default MCP request timeout (milliseconds) injected into the OpenCode agent +/// config (`opencode.json` → `mcp.idea.timeout`). OpenCode applies this **per +/// `tools/call`** to its MCP servers; the original hardcoded `15_000` (15 s) was +/// far too short for `idea_ask_agent`, which blocks while the target agent works +/// (potentially minutes). Aligning on the rendezvous ceiling (4 h) ensures +/// OpenCode's client never kills an in-flight delegation before the server-side +/// watchdog resolves. +/// +/// **Must stay ≥ `DEFAULT_RENDEZVOUS_CEILING`** so the MCP client timeout and the +/// server-side absolute ceiling expire together — never the client first. +/// Overridable via `IDEA_OPENCODE_MCP_TIMEOUT_MS`. +pub const DEFAULT_OPENCODE_MCP_TIMEOUT_MS: u64 = 4 * 60 * 60 * 1000; + +/// Resolves the effective OpenCode MCP request timeout (ms) from the +/// `IDEA_OPENCODE_MCP_TIMEOUT_MS` env var, falling back to +/// [`DEFAULT_OPENCODE_MCP_TIMEOUT_MS`]. Parse failure or `0` ⇒ default, so a +/// misconfigured value never collapses the timeout to an instant kill. +pub fn resolve_opencode_mcp_timeout_ms() -> u64 { + std::env::var("IDEA_OPENCODE_MCP_TIMEOUT_MS") + .ok() + .and_then(|v| v.trim().parse::().ok()) + .filter(|&ms| ms > 0) + .unwrap_or(DEFAULT_OPENCODE_MCP_TIMEOUT_MS) +} + fn opencode_config_json( config: &domain::profile::OpenCodeConfig, project_root: &str, @@ -2725,7 +2750,7 @@ fn opencode_config_json( "command": command_array, "cwd": project_root, "enabled": true, - "timeout": 15000 + "timeout": resolve_opencode_mcp_timeout_ms() } }), ); @@ -2808,7 +2833,7 @@ fn opencode_provider_config_json( "command": command_array, "cwd": project_root, "enabled": true, - "timeout": 15000 + "timeout": resolve_opencode_mcp_timeout_ms() } }), ); diff --git a/crates/application/src/agent/mod.rs b/crates/application/src/agent/mod.rs index 02f7bf5..944394f 100644 --- a/crates/application/src/agent/mod.rs +++ b/crates/application/src/agent/mod.rs @@ -40,7 +40,8 @@ pub use lifecycle::{ ListAgentsOutput, LiveStateLeanProvider, McpRuntime, PermissionProjectorRegistry, ProviderSessionProvider, ReadAgentContext, ReadAgentContextInput, ReadAgentContextOutput, StructuredRoutingMode, StructuredSessionDescriptor, UpdateAgentContext, - UpdateAgentContextInput, AGENT_MEMORY_RECALL_BUDGET, LIVE_STATE_INJECT_MAX, + UpdateAgentContextInput, DEFAULT_OPENCODE_MCP_TIMEOUT_MS, AGENT_MEMORY_RECALL_BUDGET, + LIVE_STATE_INJECT_MAX, resolve_opencode_mcp_timeout_ms, }; pub use resume::{ ListResumableAgents, ListResumableAgentsInput, ListResumableAgentsOutput, ResumableAgent, diff --git a/crates/infrastructure/src/assistant/mod.rs b/crates/infrastructure/src/assistant/mod.rs index 7540f7a..c357bef 100644 --- a/crates/infrastructure/src/assistant/mod.rs +++ b/crates/infrastructure/src/assistant/mod.rs @@ -407,7 +407,7 @@ fn opencode_config_json( "command": command_array, "cwd": project_root, "enabled": true, - "timeout": 15000 + "timeout": application::agent::resolve_opencode_mcp_timeout_ms() } }), ); @@ -492,7 +492,7 @@ fn opencode_provider_config_json( "command": command_array, "cwd": project_root, "enabled": true, - "timeout": 15000 + "timeout": application::agent::resolve_opencode_mcp_timeout_ms() } }), );