Align remote exec with code execution timeouts

Send the normal _code_execution timeout group to connected A0 CLI sessions for terminal, Python, Node.js, input, and output remote execution. Size the plugin-side wait budget from the selected timeout group with a small transport grace period and update the tool prompt to steer long-running work toward output polling.

Alessandro committed Jun 2, 2026 at 11:55 UTC 8a39542dc3bd7518d9da548b9bcb0227e43fd26e
2 files changed +50 -3
plugins/_a0_connector/prompts/agent.system.tool.code_execution_remote.md
+3
@@ -33,6 +33,9 @@ Runtime-specific fields:
33 - Use `output` to poll a running session and `runtime=reset` for a stuck session.
34 Use `reset: true` on a new command when you need a clean session and want to
35 run the replacement command immediately.
36 +- Execution and output polling timeouts follow the normal `_code_execution`
37 + plugin settings. For builds, installs, servers, tests, training, and other
38 + long work, redirect logs and poll with `runtime=output`.
39 - Paths and shell syntax are evaluated on the CLI host, not inside Agent Zero.
40 - When the user gives a relative path like `tmp/file.txt`, keep it relative to
41 the CLI host terminal. Do not prepend or `cd` to `/a0/usr/workdir`; that is the
plugins/_a0_connector/tools/code_execution_remote.py
+47 -3
@@ -9,6 +9,7 @@ from helpers.tool import Response, Tool
9 from helpers.ws import NAMESPACE
10 from helpers.ws_manager import ConnectionNotFoundError, get_shared_ws_manager
11
12 +from plugins._a0_connector.helpers.exec_config import build_exec_config
13 from plugins._a0_connector.helpers.ws_runtime import (
14 clear_pending_exec_op,
15 remote_exec_metadata_for_sid,
@@ -19,8 +20,15 @@ from plugins._a0_connector.helpers.ws_runtime import (
20 )
21
22
22 -EXEC_OP_TIMEOUT = 120.0
23 +EXEC_OP_TRANSPORT_GRACE = 15.0
24 +EXEC_OP_DEFAULT_TIMEOUT = 120.0
25 EXEC_OP_EVENT = "connector_exec_op"
26 +_TIMEOUT_KEYS = (
27 + "first_output_timeout",
28 + "between_output_timeout",
29 + "max_exec_timeout",
30 + "dialog_timeout",
31 +)
32
33
34 class CodeExecutionRemote(Tool):
@@ -40,6 +48,36 @@ class CodeExecutionRemote(Tool):
48 return value.strip().lower() in {"1", "true", "yes", "on"}
49 return False
50
51 + @staticmethod
52 + def _timeout_group_for_runtime(
53 + runtime: str,
54 + exec_config: dict[str, Any],
55 + ) -> dict[str, int] | None:
56 + if runtime == "output":
57 + source = exec_config.get("output_timeouts")
58 + elif runtime in {"terminal", "python", "nodejs", "input"}:
59 + source = exec_config.get("code_exec_timeouts")
60 + else:
61 + return None
62 +
63 + if not isinstance(source, dict):
64 + return None
65 +
66 + timeouts: dict[str, int] = {}
67 + for key in _TIMEOUT_KEYS:
68 + try:
69 + timeouts[key] = max(0, int(source[key]))
70 + except (KeyError, TypeError, ValueError):
71 + return None
72 + return timeouts
73 +
74 + @staticmethod
75 + def _wait_timeout_for_runtime(runtime: str, exec_config: dict[str, Any]) -> float:
76 + timeouts = CodeExecutionRemote._timeout_group_for_runtime(runtime, exec_config)
77 + if not timeouts:
78 + return EXEC_OP_DEFAULT_TIMEOUT
79 + return max(float(value) for value in timeouts.values()) + EXEC_OP_TRANSPORT_GRACE
80 +
81 def get_log_object(self):
82 import uuid
83
@@ -156,6 +194,11 @@ class CodeExecutionRemote(Tool):
194 if reason is not None:
195 payload["reason"] = str(reason)
196
197 + exec_config = build_exec_config(agent=self.agent)
198 + if timeouts := self._timeout_group_for_runtime(runtime, exec_config):
199 + payload["timeouts"] = timeouts
200 + wait_timeout = self._wait_timeout_for_runtime(runtime, exec_config)
201 +
202 loop = asyncio.get_running_loop()
203 future: asyncio.Future[dict[str, Any]] = loop.create_future()
204 store_pending_exec_op(
@@ -174,7 +217,7 @@ class CodeExecutionRemote(Tool):
217 payload,
218 handler_id=f"{self.__class__.__module__}.{self.__class__.__name__}",
219 )
177 - result = await asyncio.wait_for(future, timeout=EXEC_OP_TIMEOUT)
220 + result = await asyncio.wait_for(future, timeout=wait_timeout)
221 except ConnectionNotFoundError:
222 clear_pending_exec_op(op_id)
223 return Response(
@@ -189,7 +232,8 @@ class CodeExecutionRemote(Tool):
232 return Response(
233 message=(
234 "code_execution_remote: timed out waiting for CLI to respond "
192 - f"to runtime={runtime!r} in session {session}"
235 + f"to runtime={runtime!r} in session {session} after "
236 + f"{wait_timeout:g} seconds"
237 ),
238 break_loop=False,
239 )