Skip to main content
Use the Python client to launch HoloDesktop CLI from a script, demo harness, test runner, or local tool instead of calling holo run. The Python client starts or attaches to the same local hai-agent-runtime process used by the CLI, MCP, and ACP surfaces.

Basic script

Run this from the holo-desktop checkout after uv sync:
import asyncio

from holo_desktop.agent_client import AgentApiClient, SpawnConfig, ensure_running
from holo_desktop.agent_client.requests import build_session_request


async def main() -> None:
    daemon = await ensure_running(SpawnConfig(port=18795))
    try:
        async with AgentApiClient(daemon.base_url, daemon.token) as client:
            request = build_session_request(
                task="Open TextEdit and write a short note saying HoloDesktop CLI is embedded",
                max_steps=10,
                max_time_s=120,
            )
            session_id = await client.create_session(request)
            stream = client.stream(session_id)

            async for event in stream.events():
                print(event.type)

            print(stream.answer)
    finally:
        await daemon.aclose()


asyncio.run(main())
Run it with:
uv run python your_script.py

Use hosted or local mode

Hosted mode uses the same login state as the CLI. Sign in once:
uv run holo login
Local mode passes model settings at runtime spawn:
daemon = await ensure_running(
    SpawnConfig(
        port=18795,
        model="Hcompany/Holo-3.1-35B-A3B",
        base_url="http://localhost:8000/v1",
    )
)
The model and base URL are process-level settings. Start a separate runtime process when you need to switch model backend.

Keep logs for a run

To choose where runtime event logs are written:
from pathlib import Path

daemon = await ensure_running(
    SpawnConfig(
        port=18795,
        runs_dir=Path("/tmp/holo-runs"),
    )
)
The CLI equivalent is:
uv run holo run --runs-dir /tmp/holo-runs "Open TextEdit and write test"

Send another message

The client exposes pause, resume, cancel, and mid-run messages:
await client.pause(session_id)
await client.resume(session_id)
await client.send_message(session_id, "Continue, but do not send anything.")
await client.cancel(session_id)
Use these methods for interactive tools. For one-shot scripts, create a session, stream to end-of-turn, and close the daemon.

Handle cleanup

Always close the daemon:
finally:
    await daemon.aclose()
If your script spawned the runtime, closing the daemon stops it. If it attached to an already-running runtime on the same port, closing the daemon only releases the client-side handle.
If your program is interrupted while a session is active, cancel the session or close the daemon before exiting. Otherwise the runtime may keep working until its timeout or safety budget.

What to avoid

  • Do not pass secrets or hidden context only in your app state. HoloDesktop CLI sees the task string and the configured ~/.holo context, not your surrounding Python variables.
  • Do not switch model or base_url per session. Those are runtime process settings.
  • Do not leave a session running after your program exits. Cancel or close when interrupted.

What’s next

Use Debug a failed run to inspect runtime logs, or see the receipt demo in examples/holo-demos for a larger tested harness.