Send connector exec config in ws hello

## Summary - include `exec_config` in `_a0_connector` `connector_hello` - source execution timeouts and prompt/dialog patterns from `_code_execution` config - make the connector advertise execution policy explicitly to the CLI ## Why The CLI should not depend on a local Agent Zero Core checkout just to run `code_execution_remote`. On Windows this broke remote execution even when the connector was active, because the CLI could not see the container's internal Core tree. The backend already owns the execution policy, so it should send that contract directly. ## What changed - add `_a0_connector.helpers.exec_config.build_exec_config()` - read `_code_execution` settings/defaults through plugin config resolution - return `exec_config` from `_a0_connector.api.ws_connector` during `connector_hello` ## Impact - removes an implicit host-side Core dependency from the connector flow - lets the CLI keep only platform-specific shell / TTY behavior locally - aligns Linux and Windows behavior behind the same handshake contract

Alessandro committed Apr 14, 2026 at 22:52 UTC 9db0edd89abe682fe8fd2df6452a50229c41539a
2 files changed +98
plugins/_a0_connector/api/ws_connector.py
+2
@@ -9,6 +9,7 @@ from helpers.ws import WsHandler
9 from helpers.ws_manager import WsResult
10
11 from plugins._a0_connector.helpers.event_bridge import get_context_log_entries
12 +from plugins._a0_connector.helpers.exec_config import build_exec_config
13 from plugins._a0_connector.helpers.ws_runtime import (
14 clear_remote_tree_snapshot,
15 fail_pending_file_ops_for_sid,
@@ -82,6 +83,7 @@ class WsConnector(WsHandler):
83 return {
84 "protocol": PROTOCOL_VERSION,
85 "features": WS_FEATURES,
86 + "exec_config": build_exec_config(),
87 }
88
89 if event == "connector_subscribe_context":
plugins/_a0_connector/helpers/exec_config.py new
+96
@@ -0,0 +1,96 @@
1 +from __future__ import annotations
2 +
3 +from typing import Any
4 +
5 +_TIMEOUT_KEYS = (
6 + "first_output_timeout",
7 + "between_output_timeout",
8 + "max_exec_timeout",
9 + "dialog_timeout",
10 +)
11 +
12 +_DEFAULT_CODE_EXEC_TIMEOUTS = {
13 + "first_output_timeout": 30,
14 + "between_output_timeout": 15,
15 + "max_exec_timeout": 180,
16 + "dialog_timeout": 5,
17 +}
18 +_DEFAULT_OUTPUT_TIMEOUTS = {
19 + "first_output_timeout": 90,
20 + "between_output_timeout": 45,
21 + "max_exec_timeout": 300,
22 + "dialog_timeout": 5,
23 +}
24 +_DEFAULT_PROMPT_PATTERNS = [
25 + r"(\(venv\)).+[$#] ?$",
26 + r"root@[^:]+:[^#]+# ?$",
27 + r"[a-zA-Z0-9_.-]+@[^:]+:[^$#]+[$#] ?$",
28 + r"\(?.*\)?\s*PS\s+[^>]+> ?$",
29 +]
30 +_DEFAULT_DIALOG_PATTERNS = [
31 + r"Y/N",
32 + r"yes/no",
33 + r":\s*$",
34 + r"\?\s*$",
35 +]
36 +
37 +
38 +def _coerce_timeout_group(raw: Any, defaults: dict[str, int]) -> dict[str, int]:
39 + group = raw if isinstance(raw, dict) else {}
40 + result: dict[str, int] = {}
41 + for key in _TIMEOUT_KEYS:
42 + value = group.get(key, defaults[key])
43 + try:
44 + result[key] = int(value)
45 + except (TypeError, ValueError):
46 + result[key] = defaults[key]
47 + return result
48 +
49 +
50 +def _pattern_lines(raw: Any, defaults: list[str]) -> list[str]:
51 + if isinstance(raw, list):
52 + values = raw
53 + elif isinstance(raw, str):
54 + values = raw.splitlines()
55 + else:
56 + values = defaults
57 +
58 + patterns = [str(value).strip() for value in values if str(value).strip()]
59 + return patterns or list(defaults)
60 +
61 +
62 +def build_exec_config() -> dict[str, Any]:
63 + from helpers import plugins
64 +
65 + try:
66 + config = plugins.get_plugin_config("_code_execution") or {}
67 + except Exception:
68 + config = {}
69 +
70 + return {
71 + "version": 1,
72 + "code_exec_timeouts": _coerce_timeout_group(
73 + config.get("code_exec_timeouts") or {
74 + key: config.get(f"code_exec_{key}")
75 + for key in _TIMEOUT_KEYS
76 + if f"code_exec_{key}" in config
77 + },
78 + _DEFAULT_CODE_EXEC_TIMEOUTS,
79 + ),
80 + "output_timeouts": _coerce_timeout_group(
81 + config.get("output_timeouts") or {
82 + key: config.get(f"output_{key}")
83 + for key in _TIMEOUT_KEYS
84 + if f"output_{key}" in config
85 + },
86 + _DEFAULT_OUTPUT_TIMEOUTS,
87 + ),
88 + "prompt_patterns": _pattern_lines(
89 + config.get("prompt_patterns"),
90 + _DEFAULT_PROMPT_PATTERNS,
91 + ),
92 + "dialog_patterns": _pattern_lines(
93 + config.get("dialog_patterns"),
94 + _DEFAULT_DIALOG_PATTERNS,
95 + ),
96 + }