22
33Two responsibilities:
44
5- 1. The RPC protocol on the default `/ ` namespace — `call` / `cancel`
5+ 1. The RPC protocol on the `/rpc ` namespace — `call` / `cancel`
66 / `call:result` / `call:error`.
77
882. Dynamic namespace forwarding. When a worker-side `agentix.Namespace`
1111 the worker. Outbound `sio_emit` frames become real SIO emits on the
1212 corresponding namespace.
1313
14- Reserved namespace paths (claimed by agentix-core): `/`, `/trace`,
14+ Reserved namespace paths (claimed by agentix-core): `/rpc `, `/trace`,
1515`/log`. Plugins use their own paths (typically `/<package-name>`).
1616"""
1717
3333from agentix .runtime .shared .models import RemoteError , RemoteRequest
3434
3535logger = logging .getLogger ("agentix.runtime.sio" )
36+ RPC_NAMESPACE = "/rpc"
3637
3738
3839def _u (data : Any ) -> dict :
@@ -149,7 +150,7 @@ async def _emit_task_result(task: asyncio.Task, call_id: str) -> None:
149150 # disconnected the emit is a no-op and the cached entry
150151 # carries the result through to the next `resume`.
151152 pending_results [call_id ] = (event , frame )
152- await sio .emit (event , pack (frame ))
153+ await sio .emit (event , pack (frame ), namespace = RPC_NAMESPACE )
153154
154155 def _track_call (call_id : str , task : asyncio .Task ) -> None :
155156 calls [call_id ] = task
@@ -189,18 +190,16 @@ async def submit_http_call(payload: dict[str, Any], *, prefer_sync_ms: int = 100
189190 # Runtime internal hook used by the HTTP fast-path endpoint.
190191 setattr (sio , "submit_http_call" , submit_http_call )
191192
192- @sio .event
193- async def connect (sid : str , environ : dict , auth : Any = None ) -> None :
193+ async def on_connect (sid : str , environ : dict , auth : Any = None ) -> None :
194194 logger .debug ("sio connect %s" , sid )
195195
196- @sio .event
197- async def disconnect (sid : str ) -> None :
196+ async def on_disconnect (sid : str ) -> None :
198197 # Tasks intentionally outlive the connection. Their results
199198 # land in `pending_results` and will be replayed on the next
200199 # `resume`. The host may also cancel explicitly via `cancel`.
201200 logger .debug ("sio disconnect %s" , sid )
202201
203- # ── RPC on `/` ─── ────────────────────────────────────────────
202+ # ── RPC on `/rpc` ────────────────────────────────────────────
204203
205204 async def on_call (sid : str , data : Any ) -> None :
206205 payload = _u (data )
@@ -211,6 +210,7 @@ async def on_call(sid: str, data: Any) -> None:
211210 event ,
212211 pack (frame ),
213212 to = sid ,
213+ namespace = RPC_NAMESPACE ,
214214 )
215215 return
216216
@@ -240,6 +240,7 @@ async def on_cancel(sid: str, data: Any) -> None:
240240 "call:error" ,
241241 pack (_cancelled_error (call_id )),
242242 to = sid ,
243+ namespace = RPC_NAMESPACE ,
243244 )
244245
245246 async def on_resume (sid : str , data : Any ) -> None :
@@ -256,7 +257,7 @@ async def on_resume(sid: str, data: Any) -> None:
256257 if cached is None :
257258 continue
258259 event , frame = cached
259- await sio .emit (event , pack (frame ), to = sid )
260+ await sio .emit (event , pack (frame ), to = sid , namespace = RPC_NAMESPACE )
260261
261262 async def on_ack (sid : str , data : Any ) -> None :
262263 """Host confirms it has consumed the result. Free the slot."""
@@ -283,7 +284,7 @@ async def _on_worker_sio_frame(frame: dict[str, Any]) -> None:
283284 return
284285 await sio .emit (event , pack (frame .get ("data" )), namespace = namespace )
285286 elif kind == "sio_open" :
286- if namespace in opened_namespaces or namespace == "/" :
287+ if namespace in opened_namespaces or namespace in { "/" , RPC_NAMESPACE } :
287288 return
288289 opened_namespaces .add (namespace )
289290 _register_namespace (namespace )
@@ -306,13 +307,15 @@ async def trigger_event(self, event: str, *args: Any) -> Any:
306307
307308 worker .set_sio_handler (_on_worker_sio_frame )
308309
309- # Register RPC handlers on `/` non-decorator-style — `@sio.on(name)`
310+ # Register RPC handlers on `/rpc ` non-decorator-style — `@sio.on(name)`
310311 # decorates by side effect and pyright can't tell that the wrapped
311312 # function is still usable.
312- sio .on ("call" , on_call )
313- sio .on ("cancel" , on_cancel )
314- sio .on ("resume" , on_resume )
315- sio .on ("ack" , on_ack )
313+ sio .on ("connect" , on_connect , namespace = RPC_NAMESPACE )
314+ sio .on ("disconnect" , on_disconnect , namespace = RPC_NAMESPACE )
315+ sio .on ("call" , on_call , namespace = RPC_NAMESPACE )
316+ sio .on ("cancel" , on_cancel , namespace = RPC_NAMESPACE )
317+ sio .on ("resume" , on_resume , namespace = RPC_NAMESPACE )
318+ sio .on ("ack" , on_ack , namespace = RPC_NAMESPACE )
316319
317320 # Pre-register core namespaces so the host can connect to them
318321 # immediately — the worker subscribes lazily, but the SIO server
0 commit comments