Skip to content

feat(sdk): add local desktop with long polling#104

Open
leone-hai wants to merge 1 commit into
mainfrom
leone/06-16-feat_sdk_add_local_desktop_with_long_polling
Open

feat(sdk): add local desktop with long polling#104
leone-hai wants to merge 1 commit into
mainfrom
leone/06-16-feat_sdk_add_local_desktop_with_long_polling

Conversation

@leone-hai

@leone-hai leone-hai commented Jun 16, 2026

Copy link
Copy Markdown

Summary

Adds a new [desktop] optional extra that lets users run a local sidecar on their machine and have H agents control it remotely via mouse, keyboard, and screenshots.

New public API:

  • LocalDesktopClient / LocalDesktopClientConfig — polls AgP for incoming desktop commands and executes them locally using pyautogui
  • session_id_from_environment_id(environment_id, api_key) — derives the deterministic routing UUID that ties the sidecar to an agent session

Usage:

# On the machine to control:
config = LocalDesktopClientConfig(environment_id="my-mac", api_key="...")
await LocalDesktopClient(config).run()

# To start an agent session pointing at that machine:
session_id = session_id_from_environment_id("my-mac", api_key)
client.run_session(
    agent="h/holo-desktop",
    environments=[{"kind": "desktop", "id": "h/local-desktop",
                   "backend": "local", "session_id": session_id}],
    messages="Take a screenshot",
)

Install: pip install "hai-agents[desktop]"


Note

High Risk
Remote agents can drive real input, filesystem, and shell on the host when the sidecar runs with a valid API key—high blast radius despite being an intentional feature.

Overview
Adds a [desktop] optional extra (pyautogui, pynput) and a new local_desktop module so a machine can run an async sidecar that long-polls AgP for commands and executes them locally.

Public API: LocalDesktopClient / LocalDesktopClientConfig, plus session_id_from_environment_id (UUID5 from environment_id + api_key) exported from hai_agents for routing agent sessions to the same machine without manual ID coordination.

The client ensures/creates an interactive trajectory, polls GET .../commands, dispatches named driver methods (mouse, keyboard, screenshots, file I/O, run_command with detach on POSIX/Windows), serializes results (e.g. PNG as base64), and posts back with retries and reconnect backoff. Tests stub pyautogui in conftest for headless CI; lockfile picks up desktop transitive deps and minor dev bumps (ruff, rich).

Reviewed by Cursor Bugbot for commit 080e475. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown
Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@leone-hai leone-hai force-pushed the leone/06-16-feat_sdk_add_local_desktop_with_long_polling branch from 7cd9325 to 314cbd2 Compare June 16, 2026 08:35
@leone-hai leone-hai marked this pull request as ready for review June 16, 2026 08:37
@leone-hai leone-hai force-pushed the leone/06-16-feat_sdk_add_local_desktop_with_long_polling branch from 314cbd2 to 842ce02 Compare June 16, 2026 08:41
Comment thread src/hai_agents/__init__.py Outdated
Comment thread src/hai_agents/local_desktop.py
@leone-hai leone-hai force-pushed the leone/06-16-feat_sdk_add_local_desktop_with_long_polling branch from 842ce02 to 74756b1 Compare June 16, 2026 09:28
Comment thread src/hai_agents/local_desktop.py
@leone-hai leone-hai force-pushed the leone/06-16-feat_sdk_add_local_desktop_with_long_polling branch from 74756b1 to 080e475 Compare June 16, 2026 09:38

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 080e475. Configure here.

await asyncio.sleep(0.5 * (attempt + 1))
logger.error(
"Failed to deliver result for command %s after %s attempts", command_id, cfg.post_result_retries + 1
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Failed results may rerun commands

Medium Severity

_post_result logs and returns after exhausting retries without surfacing failure to _process_commands or the poll loop. The sidecar then keeps polling, so the server may redeliver the same command and local actions like clicks or run_command can run more than once.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 080e475. Configure here.

if pyautogui is None:
raise ImportError(
"Local desktop control requires pyautogui and pynput. Install with: pip install 'hai-agents[desktop]'"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pynput required but unused

Low Severity

The desktop extra and _LocalDesktopDriver docs/error text require pynput, but the module only imports and uses pyautogui. Installations pull native pynput dependencies without any runtime use in this codepath.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 080e475. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant