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:
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.