refactor(ws): rename agent_ids to scout_ids in device_hello frame
WsDeviceHello.agent_ids → scout_ids in Pydantic schema, device_ws.py handler, and all test fixtures (test_device_ws, test_ws_unified, test_memory_middleware). Also fixes stale CloudAgentConfig reference in gmail.py docstring. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ available during the WebSocket handshake).
|
|||||||
|
|
||||||
Protocol:
|
Protocol:
|
||||||
1. Client connects → JWT validated → connection accepted.
|
1. Client connects → JWT validated → connection accepted.
|
||||||
2. Client sends ``device_hello`` frame: ``{ type, device_id, agent_ids }``.
|
2. Client sends ``device_hello`` frame: ``{ type, device_id, scout_ids }``.
|
||||||
3. Backend registers the connection in ``DeviceConnectionManager``.
|
3. Backend registers the connection in ``DeviceConnectionManager``.
|
||||||
4. Session enters message dispatch loop + heartbeat.
|
4. Session enters message dispatch loop + heartbeat.
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ async def device_ws(websocket: WebSocket) -> None:
|
|||||||
if hello.get("type") != WsFrameType.device_hello:
|
if hello.get("type") != WsFrameType.device_hello:
|
||||||
raise ValueError("expected device_hello as first frame")
|
raise ValueError("expected device_hello as first frame")
|
||||||
device_id: str = hello["device_id"]
|
device_id: str = hello["device_id"]
|
||||||
agent_ids: list[str] = hello.get("agent_ids", [])
|
scout_ids: list[str] = hello.get("scout_ids", [])
|
||||||
except (KeyError, ValueError, json.JSONDecodeError) as exc:
|
except (KeyError, ValueError, json.JSONDecodeError) as exc:
|
||||||
logger.warning("device_ws: invalid device_hello from user=%s: %s", user_id, exc)
|
logger.warning("device_ws: invalid device_hello from user=%s: %s", user_id, exc)
|
||||||
await websocket.close(code=1008)
|
await websocket.close(code=1008)
|
||||||
@@ -109,10 +109,10 @@ async def device_ws(websocket: WebSocket) -> None:
|
|||||||
# ── 3. Register connection ────────────────────────────────────────
|
# ── 3. Register connection ────────────────────────────────────────
|
||||||
device_manager.register(user_id, device_id, websocket)
|
device_manager.register(user_id, device_id, websocket)
|
||||||
logger.info(
|
logger.info(
|
||||||
"device_ws: connected user=%s device=%s agents=%s",
|
"device_ws: connected user=%s device=%s scouts=%s",
|
||||||
user_id,
|
user_id,
|
||||||
device_id,
|
device_id,
|
||||||
agent_ids,
|
scout_ids,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Trigger any overdue agent runs now that the device is connected.
|
# Trigger any overdue agent runs now that the device is connected.
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ blocking the event loop.
|
|||||||
Token refresh is handled transparently: when the stored access token has
|
Token refresh is handled transparently: when the stored access token has
|
||||||
expired, ``google.auth.transport.requests.Request`` will use the refresh
|
expired, ``google.auth.transport.requests.Request`` will use the refresh
|
||||||
token to obtain a fresh one. The caller is responsible for persisting
|
token to obtain a fresh one. The caller is responsible for persisting
|
||||||
any refreshed credentials back to ``CloudAgentConfig.oauth_token_encrypted``
|
any refreshed credentials back to ``CloudScoutConfig.oauth_token_encrypted``
|
||||||
(see ``agent_runner.run_cloud_agent``).
|
(see ``agent_runner.run_cloud_agent``).
|
||||||
|
|
||||||
Credential dict shape (Google OAuth2):
|
Credential dict shape (Google OAuth2):
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ class WsDeviceHello(BaseModel):
|
|||||||
|
|
||||||
type: Literal[WsFrameType.device_hello] = WsFrameType.device_hello
|
type: Literal[WsFrameType.device_hello] = WsFrameType.device_hello
|
||||||
device_id: str
|
device_id: str
|
||||||
agent_ids: list[str] = Field(default_factory=list)
|
scout_ids: list[str] = Field(default_factory=list)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,9 +33,9 @@ _FREE_UID = TEST_USER_IDS["free"]
|
|||||||
_PRO_UID = TEST_USER_IDS["pro"]
|
_PRO_UID = TEST_USER_IDS["pro"]
|
||||||
|
|
||||||
|
|
||||||
def _device_hello(device_id: str = "dev-001", agent_ids: list[str] | None = None) -> str:
|
def _device_hello(device_id: str = "dev-001", scout_ids: list[str] | None = None) -> str:
|
||||||
return json.dumps(
|
return json.dumps(
|
||||||
{"type": "device_hello", "device_id": device_id, "agent_ids": agent_ids or []}
|
{"type": "device_hello", "device_id": device_id, "scout_ids": scout_ids or []}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ def test_home_request_calls_memory_middleware(client):
|
|||||||
):
|
):
|
||||||
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "device_hello", "device_id": "dev-mem", "agent_ids": []
|
"type": "device_hello", "device_id": "dev-mem", "scout_ids": []
|
||||||
}))
|
}))
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "home_request",
|
"type": "home_request",
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ def test_home_request_produces_stream_frames(client):
|
|||||||
with patch("app.api.routes.device_ws.run_home_stream", side_effect=_mock_home_stream):
|
with patch("app.api.routes.device_ws.run_home_stream", side_effect=_mock_home_stream):
|
||||||
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "device_hello", "device_id": "dev-1", "agent_ids": []
|
"type": "device_hello", "device_id": "dev-1", "scout_ids": []
|
||||||
}))
|
}))
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "home_request",
|
"type": "home_request",
|
||||||
@@ -85,7 +85,7 @@ def test_home_request_request_id_propagated(client):
|
|||||||
with patch("app.api.routes.device_ws.run_home_stream", side_effect=_stream):
|
with patch("app.api.routes.device_ws.run_home_stream", side_effect=_stream):
|
||||||
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "device_hello", "device_id": "dev-3", "agent_ids": []
|
"type": "device_hello", "device_id": "dev-3", "scout_ids": []
|
||||||
}))
|
}))
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "home_request",
|
"type": "home_request",
|
||||||
@@ -106,7 +106,7 @@ def test_tool_result_dispatch_silent_on_unknown_id(client):
|
|||||||
with patch("app.api.routes.device_ws._HEARTBEAT_INTERVAL", 0.05):
|
with patch("app.api.routes.device_ws._HEARTBEAT_INTERVAL", 0.05):
|
||||||
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
with client.websocket_connect(f"/api/v1/ws/device?token={token}") as ws:
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "device_hello", "device_id": "dev-4", "agent_ids": []
|
"type": "device_hello", "device_id": "dev-4", "scout_ids": []
|
||||||
}))
|
}))
|
||||||
ws.send_text(json.dumps({
|
ws.send_text(json.dumps({
|
||||||
"type": "tool_result", "id": "no-such-id", "ok": True
|
"type": "tool_result", "id": "no-such-id", "ok": True
|
||||||
|
|||||||
Reference in New Issue
Block a user