Summary
Local desktop mode (hai_agents_local, the [desktop] extra) creates trajectories through what appears to be legacy infrastructure, reached via a server-side 307 redirect from the documented endpoint to an undocumented one. That legacy pool has its own small concurrency limit (3), and sessions stuck against it are not visible or cancellable through the modern sessions API (list_sessions(), cancel_session()), so a single crashed/killed local run can silently block all future local desktop sessions with no client-side way to recover.
Repro
pip install "hai-agents[desktop]", set up a local-desktop agent per the docs (https://hub.hcompany.ai/computer-use-agents/desktop/local-control).
- Run several
client.run_session(agent=<local-desktop agent>, messages=...) calls, including at least one that gets interrupted (killed process, crashed bridge, etc.) rather than completing cleanly.
- Eventually (in our case, after ~3 local sessions across a day of testing) every subsequent local desktop
run_session()/start_session() call fails with:
httpx.HTTPStatusError: Client error '429' for url 'https://agp.eu.hcompany.ai/api/v1/agents/surferh/trajectories'
RuntimeError: local desktop bridge for environment '<env-id>' failed to start
What we found digging in
- The client library (
hai_agents_local/transport.py, CommandExchange.ensure_channel) POSTs to {base}/api/v1/trajectories/ — a normal, documented-looking path.
- The server responds with a
307 redirect to /api/v1/agents/surferh/trajectories (confirmed directly with httpx, follow_redirects=False).
surferh is not a name that appears anywhere in the current hai-agents/hai_agents_local source — this looks like an internal routing detail pointing at legacy infrastructure (possibly related to the deprecated surfer-h-cli).
- That endpoint is not present in the public OpenAPI spec (
https://agp.hcompany.ai/share/openapi.json), so there's no documented way to list or manage what it's holding.
- Direct POST to that endpoint returns:
{"message":"Concurrent trajectory limit reached (3/3)","detail":[{"type":"too_many_requests","message":"Concurrent trajectory limit reached (3/3)"}]}
with no Retry-After header.
- Meanwhile,
client.sessions.list_sessions() shows zero active/non-terminal sessions, and client.sessions.cancel_session(id=...) / hai sessions cancel <id> both report success on the sessions that are visible — none of it frees the legacy trajectory slot. We confirmed this is genuinely a separate pool, not a display lag: even with list_sessions() showing nothing active, the 429 persists.
- The block appears to be account/org-level, not tied to a specific API key — regenerating the key didn't clear it.
- In our case it eventually cleared on its own after roughly 30-40 minutes of inactivity, but we couldn't find any documented timeout to confirm that's expected/reliable behavior rather than coincidence.
Impact
Any crashed or force-killed local desktop session can silently exhaust the entire local desktop concurrency budget for the account, with no visibility into it (doesn't show in list_sessions()) and no way to clear it (cancel_session() doesn't reach it). This happened to us repeatedly over the course of a single day of normal development/testing.
Ask
- A documented way to list/cancel whatever's held in this legacy pool (or route local desktop trajectory creation through the same lifecycle as
sessions, so cancel_session()/list_sessions() actually reach it).
- Alternatively, if this is intentional legacy infra that's expected to be phased out, a heads-up in the local desktop docs (https://hub.hcompany.ai/computer-use-agents/desktop/local-control) about the separate concurrency pool and how to recover from it would save the next person the multi-hour debugging session this took us.
Happy to share full session IDs / timestamps / account details privately if useful — didn't want to post them in a public issue.
Environment: hai-agents 1.0.6 (the current/latest version per PyPI at time of writing), macOS, EU region (agp.eu.hcompany.ai), Python 3.14.
Summary
Local desktop mode (
hai_agents_local, the[desktop]extra) creates trajectories through what appears to be legacy infrastructure, reached via a server-side307redirect from the documented endpoint to an undocumented one. That legacy pool has its own small concurrency limit (3), and sessions stuck against it are not visible or cancellable through the modernsessionsAPI (list_sessions(),cancel_session()), so a single crashed/killed local run can silently block all future local desktop sessions with no client-side way to recover.Repro
pip install "hai-agents[desktop]", set up a local-desktop agent per the docs (https://hub.hcompany.ai/computer-use-agents/desktop/local-control).client.run_session(agent=<local-desktop agent>, messages=...)calls, including at least one that gets interrupted (killed process, crashed bridge, etc.) rather than completing cleanly.run_session()/start_session()call fails with:What we found digging in
hai_agents_local/transport.py,CommandExchange.ensure_channel) POSTs to{base}/api/v1/trajectories/— a normal, documented-looking path.307redirect to/api/v1/agents/surferh/trajectories(confirmed directly withhttpx,follow_redirects=False).surferhis not a name that appears anywhere in the currenthai-agents/hai_agents_localsource — this looks like an internal routing detail pointing at legacy infrastructure (possibly related to the deprecatedsurfer-h-cli).https://agp.hcompany.ai/share/openapi.json), so there's no documented way to list or manage what it's holding.{"message":"Concurrent trajectory limit reached (3/3)","detail":[{"type":"too_many_requests","message":"Concurrent trajectory limit reached (3/3)"}]}Retry-Afterheader.client.sessions.list_sessions()shows zero active/non-terminal sessions, andclient.sessions.cancel_session(id=...)/hai sessions cancel <id>both report success on the sessions that are visible — none of it frees the legacy trajectory slot. We confirmed this is genuinely a separate pool, not a display lag: even withlist_sessions()showing nothing active, the 429 persists.Impact
Any crashed or force-killed local desktop session can silently exhaust the entire local desktop concurrency budget for the account, with no visibility into it (doesn't show in
list_sessions()) and no way to clear it (cancel_session()doesn't reach it). This happened to us repeatedly over the course of a single day of normal development/testing.Ask
sessions, socancel_session()/list_sessions()actually reach it).Happy to share full session IDs / timestamps / account details privately if useful — didn't want to post them in a public issue.
Environment:
hai-agents1.0.6 (the current/latest version per PyPI at time of writing), macOS, EU region (agp.eu.hcompany.ai), Python 3.14.