Associate connector hello metadata with chat context

Store remote tool metadata through a dedicated hello path, bind a declared context id to the websocket SID, and return acknowledged remote tool state so clients can verify gated tools such as code_execution_remote are visible to the active chat.

Alessandro committed May 7, 2026 at 03:47 UTC bb86e5a7a9ba220c32bb00d52fc83fd1f70ddfa8
1 file changed +54 -16
plugins/_a0_connector/api/ws_connector.py
+54 -16
@@ -15,10 +15,13 @@ from plugins._a0_connector.helpers.ws_runtime import (
15 clear_sid_computer_use_metadata,
16 clear_sid_remote_exec_metadata,
17 clear_sid_remote_file_metadata,
18 + computer_use_metadata_for_sid,
19 fail_pending_computer_use_ops_for_sid,
19 - fail_pending_file_ops_for_sid,
20 fail_pending_exec_ops_for_sid,
21 + fail_pending_file_ops_for_sid,
22 register_sid,
23 + remote_exec_metadata_for_sid,
24 + remote_file_metadata_for_sid,
25 resolve_pending_computer_use_op,
26 resolve_pending_exec_op,
27 resolve_pending_file_op,
@@ -96,25 +99,13 @@ class WsConnector(WsHandler):
99 sid: str,
100 ) -> dict[str, Any] | WsResult | None:
101 if event == "connector_hello":
99 - computer_use = data.get("computer_use")
100 - remote_files = data.get("remote_files")
101 - remote_exec = data.get("remote_exec")
102 - if isinstance(computer_use, dict):
103 - store_sid_computer_use_metadata(sid, computer_use)
104 - else:
105 - clear_sid_computer_use_metadata(sid)
106 - if isinstance(remote_files, dict):
107 - store_sid_remote_file_metadata(sid, remote_files)
108 - else:
109 - clear_sid_remote_file_metadata(sid)
110 - if isinstance(remote_exec, dict):
111 - store_sid_remote_exec_metadata(sid, remote_exec)
112 - else:
113 - clear_sid_remote_exec_metadata(sid)
102 + self._store_remote_tool_metadata(data, sid)
103 + self._associate_declared_context(data, sid)
104 return {
105 "protocol": PROTOCOL_VERSION,
106 "features": WS_FEATURES,
107 "exec_config": build_exec_config(),
108 + "remote_tools": self._remote_tool_state(sid),
109 }
110
111 if event == "connector_subscribe_context":
@@ -147,6 +138,53 @@ class WsConnector(WsHandler):
138
139 return None
140
141 + def _store_remote_tool_metadata(self, data: dict[str, Any], sid: str) -> None:
142 + computer_use = data.get("computer_use")
143 + remote_files = data.get("remote_files")
144 + remote_exec = data.get("remote_exec")
145 + if isinstance(computer_use, dict):
146 + store_sid_computer_use_metadata(sid, computer_use)
147 + else:
148 + clear_sid_computer_use_metadata(sid)
149 + if isinstance(remote_files, dict):
150 + store_sid_remote_file_metadata(sid, remote_files)
151 + else:
152 + clear_sid_remote_file_metadata(sid)
153 + if isinstance(remote_exec, dict):
154 + store_sid_remote_exec_metadata(sid, remote_exec)
155 + else:
156 + clear_sid_remote_exec_metadata(sid)
157 +
158 + def _associate_declared_context(self, data: dict[str, Any], sid: str) -> str:
159 + context_id = str(data.get("context_id", "") or "").strip()
160 + if not context_id:
161 + return ""
162 +
163 + try:
164 + from agent import AgentContext
165 +
166 + if AgentContext.get(context_id) is None:
167 + return ""
168 + except Exception:
169 + return ""
170 +
171 + subscribe_sid_to_context(sid, context_id)
172 + return context_id
173 +
174 + def _remote_tool_state(self, sid: str) -> dict[str, Any]:
175 + computer_use = computer_use_metadata_for_sid(sid) or {}
176 + remote_files = remote_file_metadata_for_sid(sid) or {}
177 + remote_exec = remote_exec_metadata_for_sid(sid) or {}
178 + return {
179 + "contexts": sorted(subscribed_contexts_for_sid(sid)),
180 + "computer_use": bool(
181 + computer_use.get("supported") and computer_use.get("enabled")
182 + ),
183 + "remote_files": bool(remote_files.get("enabled", False)),
184 + "remote_file_writes": bool(remote_files.get("write_enabled", False)),
185 + "remote_exec": bool(remote_exec.get("enabled", False)),
186 + }
187 +
188 async def _handle_subscribe_context(
189 self,
190 data: dict[str, Any],