diff --git a/backend/apps/apps_sdk/apps_sdk.py b/backend/apps/apps_sdk/apps_sdk.py index 39de0e4d..635b4523 100644 --- a/backend/apps/apps_sdk/apps_sdk.py +++ b/backend/apps/apps_sdk/apps_sdk.py @@ -103,18 +103,25 @@ async def spawn_agent(body: SpawnAgentRequest) -> SpawnAgentReply: if not body.prompt.strip(): raise HTTPException(status_code=422, detail="prompt is empty") + dashboard_id = body.dashboard_id + if dashboard_id is None: + # An app webview has no idea which dashboard it lives on; without one the canvas lifecycle + # never creates a card, so the spawn is real but invisible. Most-recent dashboard wins. + from backend.apps.dashboards.dashboards import load_all + boards = sorted(load_all(), key=lambda d: d.updated_at, reverse=True) + dashboard_id = boards[0].id if boards else None config = AgentConfig( name=body.name, prompt=body.prompt, model=body.model or "sonnet", - dashboard_id=body.dashboard_id, + dashboard_id=dashboard_id, ) session = await agent_manager.launch_agent(config) asyncio.create_task(agent_manager.send_message(session.id, body.prompt)) if body.x is not None and body.y is not None: await ws_manager.broadcast_global("apps_sdk:place_agent_card", { "session_id": session.id, - "dashboard_id": body.dashboard_id, + "dashboard_id": dashboard_id, "x": body.x, "y": body.y, }) diff --git a/backend/tests/test_apps_sdk.py b/backend/tests/test_apps_sdk.py index a992164d..7f5012e1 100644 --- a/backend/tests/test_apps_sdk.py +++ b/backend/tests/test_apps_sdk.py @@ -89,10 +89,12 @@ def test_llm_rejects_an_empty_prompt(): def test_spawn_agent_launches_and_broadcasts_position(monkeypatch): + import backend.apps.dashboards.dashboards as dash_mod from backend.apps.agents import agent_manager as am_mod from backend.apps.agents.core import ws_manager as ws_mod from backend.apps.agents.core.models import AgentSession + monkeypatch.setattr(dash_mod, "load_all", lambda: [], raising=True) launched = [] messaged = [] broadcasts = [] @@ -122,10 +124,12 @@ def test_spawn_agent_launches_and_broadcasts_position(monkeypatch): def test_spawn_agent_without_position_skips_the_broadcast(monkeypatch): + import backend.apps.dashboards.dashboards as dash_mod from backend.apps.agents import agent_manager as am_mod from backend.apps.agents.core import ws_manager as ws_mod from backend.apps.agents.core.models import AgentSession + monkeypatch.setattr(dash_mod, "load_all", lambda: [], raising=True) broadcasts = [] async def p_launch(config):