{
  "markdown": "<p align=\"center\">\n  <picture>\n    <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://github.com/hcompai/hai-agents-python/blob/main/assets/banner-dark.gif?raw=true\" />\n    <img src=\"https://github.com/hcompai/hai-agents-python/blob/main/assets/banner-light.gif?raw=true\" alt=\"Computer-Use Agents\" width=\"700\" />\n  </picture>\n</p>\n\n<p align=\"center\">\n  <a href=\"https://pypi.org/project/hai-agents/\"><img src=\"https://img.shields.io/pypi/v/hai-agents.svg\" alt=\"PyPI\" /></a>\n  <a href=\"https://pypi.org/project/hai-agents/\"><img src=\"https://img.shields.io/pypi/pyversions/hai-agents.svg\" alt=\"Python versions\" /></a>\n  <a href=\"LICENSE\"><img src=\"https://img.shields.io/badge/License-MIT-yellow.svg\" alt=\"License: MIT\" /></a>\n</p>\n\n<p align=\"center\">\n  Python SDK for <a href=\"https://hcompany.ai\">H Company</a>'s <a href=\"https://hub.hcompany.ai/computer-use-agents\">Computer-Use Agents</a>.\n</p>\n\n<p align=\"center\">\n  <b><a href=\"https://hub.hcompany.ai/computer-use-agents\">Documentation</a></b>\n  &nbsp;·&nbsp;\n  <a href=\"https://platform.hcompany.ai/settings/api-keys\">Get an API key</a>\n  &nbsp;·&nbsp;\n  <a href=\"https://pypi.org/project/hai-agents/\">PyPI</a>\n  &nbsp;·&nbsp;\n  <a href=\"https://github.com/hcompai/hai-agents-ts\">TypeScript SDK</a>\n  &nbsp;·&nbsp;\n  <a href=\"https://hcompany.ai\">H Company</a>\n</p>\n\n## Installation\n\n```bash\npip install hai-agents\n```\n\nAdd the optional command-line tools with the `cli` extra:\n\n```bash\npip install \"hai-agents[cli]\"\n```\n\nPython 3.10 or newer is required. Get an API key at [platform.hcompany.ai/settings/api-keys](https://platform.hcompany.ai/settings/api-keys) and export it:\n\n```bash\nexport HAI_API_KEY=hk-...\n```\n\n## Quickstart\n\nLaunch the built-in `h/web-surfer-pro` agent, which ships with its own browser, and describe the task in plain language. `run_session` polls until the agent finishes and returns the final answer.\n\n```python\nfrom hai_agents import Client\n\nclient = Client()\n\nresult = client.run_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"What are the top 3 stories on Hacker News right now?\",\n)\n\nprint(result.status)\nprint(result.answer)\n```\n\n`Client()` reads `HAI_API_KEY` from the environment.\n\n`result` is a `SessionRunResult`: `id`, `status`, `answer`, the accumulated `events`, and `final_changes`.\n\n## How a session works\n\nA session is one run of an agent against a task. It moves through a small set of states: `pending`, `running`, and then a settled state such as `completed`, `idle`, `failed`, `timed_out`, or `interrupted`.\n\nYou drive a session two ways. `run_session` creates it and blocks until it settles, which suits one-shot tasks. `start_session` creates it and returns a handle right away, so you can read and steer the agent while it works.\n\n```python\nsession = client.start_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"Find the top story on Hacker News\",\n)\n\nprint(session.id)\nresult = session.wait_for_completion()\nprint(result.status, result.answer)\n```\n\n## Watch and steer a running session\n\nA handle bound to the session `id` exposes the full lifecycle. Read the agent's progress at three levels of detail:\n\n```python\nsession.status()\nsession.changes(from_index=0)\nsession.get()\n```\n\n`status()` is a cheap snapshot with the state, step count, and token usage. `changes(from_index=0)` long-polls for new events and the final answer. `get()` returns the full Session resource.\n\nWhile the session is not in a terminal state, you can intervene:\n\n```python\nsession.send_message({\"type\": \"user_message\", \"message\": \"Only consider the last 24 hours\"})\nsession.pause()\nsession.resume()\nsession.force_answer()\nsession.cancel()\n```\n\n`send_message` redirects the agent on its next step and wakes an `idle` session. `pause` halts with state preserved until `resume`. `force_answer` makes the agent stop exploring and answer from what it has. `cancel` ends the session as `interrupted`.\n\n## Multi-turn sessions\n\nBy default a session ends as soon as the agent answers. Set `idle_timeout_s` to keep it open: after each answer the session goes `idle` and waits that long for your next message, carrying its full context and browser state across turns.\n\n```python\nsession = client.start_session(\n    agent=\"h/web-surfer-pro\",\n    idle_timeout_s=600,\n    messages=\"Find the top story on Hacker News\",\n)\nfirst = session.wait_for_completion()\n\nsession.send_message({\"type\": \"user_message\", \"message\": \"Now summarize its comments\"})\nsecond = session.wait_for_completion()\n```\n\n## Structured output\n\nPass a pydantic model as `answer_schema` and the agent's final answer comes back as a validated instance. The model's JSON schema is sent as the agent's answer format; the raw wire value stays at `result.final_changes.answer`.\n\n```python\nfrom pydantic import BaseModel\nfrom hai_agents import Client\n\nclass Job(BaseModel):\n    title: str\n    company: str\n\nclass Jobs(BaseModel):\n    jobs: list[Job]\n\nclient = Client()\nresult = client.run_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"Find 3 open ML engineering roles in Paris.\",\n    answer_schema=Jobs,\n)\n\nfor job in result.answer.jobs:\n    print(job.title, \"@\", job.company)\n```\n\nA completed answer that does not match the schema raises `AnswerValidationError`, with the raw payload on `.raw`. Sessions that end without completing return their raw answer untouched.\n\n## Custom tools\n\nExpose your own Python functions to the agent. Pass them to `run_session` and the polling loop runs each one when the agent calls it, then posts the result back so the session continues. Any function with typed parameters and a docstring works; the input schema is derived from the signature.\n\n```python\nfrom hai_agents import Client\n\ndef get_weather(city: str) -> str:\n    \"\"\"Get the current weather for a city.\"\"\"\n    return f\"Sunny in {city}\"\n\nclient = Client()\n\nresult = client.run_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"What should I wear in Paris today?\",\n    tools=[get_weather],\n)\n```\n\nUse the `@tool` decorator to override the name or description:\n\n```python\nfrom hai_agents import tool\n\n@tool(name=\"lookup_order\", description=\"Look up an order by its id.\")\ndef lookup(order_id: str) -> dict:\n    return {\"id\": order_id, \"status\": \"shipped\"}\n```\n\nA tool that raises is reported to the agent as a tool error rather than crashing the run. With `AsyncClient`, tools may be `async def`.\n\n### Prebuilt: one-time passwords (2FA)\n\n`hai_agents_tools` ships ready-made tools. `otp_tool` lets the agent ask for a one-time password, verification code, or confirmation link when a login or signup step needs one. Without a handler it prompts on stdin; `imap_otp_handler` reads the code straight from a mailbox over IMAP (for Gmail, use an app password).\n\n```python\nimport os\n\nfrom hai_agents import Client\nfrom hai_agents_tools import imap_otp_handler, otp_tool\n\nhandler = imap_otp_handler(\n    host=\"imap.gmail.com\",\n    username=\"agent-inbox@gmail.com\",\n    password=os.environ[\"GMAIL_APP_PASSWORD\"],\n)\n\nclient = Client()\nresult = client.run_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"Log in to example.com and check for new notifications\",\n    tools=[otp_tool(handler)],\n)\n```\n\nLike every custom tool, the handler runs entirely in your process: the IMAP credentials never leave your machine, and the agent only receives the single extracted code or link -- never mailbox contents.\n\n## Browser profiles and vaults\n\nStart a session on a browser that already knows the user. A [browser profile](https://hub.hcompany.ai/computer-use-agents/browser-profiles) restores saved cookies and storage from an earlier session, and a [vault](https://hub.hcompany.ai/computer-use-agents/vaults) lets the agent sign in to sites with secrets that never enter its context. Bind both through per-run overrides:\n\n```python\nresult = client.run_session(\n    agent=\"h/web-surfer-pro\",\n    messages=\"Open my dashboard and report any new alerts\",\n    overrides={\n        \"agent.environments[kind=web].browser_profile_id\": \"<profile-id>\",\n        \"agent.environments[kind=web].vault_id\": \"<vault-id>\",\n    },\n)\n```\n\n## Async\n\n`AsyncClient` mirrors `Client` for asyncio. Every session method is a coroutine.\n\n```python\nimport asyncio\nfrom hai_agents import AsyncClient\n\nasync def main():\n    client = AsyncClient()\n    result = await client.run_session(\n        agent=\"h/web-surfer-pro\",\n        messages=\"What are the top 3 stories on Hacker News right now?\",\n    )\n    print(result.answer)\n\nasyncio.run(main())\n```\n\n## Inspect and share sessions\n\nList past sessions and create a public replay link:\n\n```python\npage = client.sessions.list_sessions(size=10)\nfor summary in page.items:\n    print(summary.id, summary.status)\n\nlink = client.sessions.share_session(\"<session-id>\")\nprint(link.share_url)\n```\n\n## Regions and configuration\n\nThe client targets the EU region by default; pass `environment` to use the US region instead:\n\n```python\nfrom hai_agents import Client, HaiAgentsEnvironment\n\nclient = Client(environment=HaiAgentsEnvironment.US)\n```\n\n`Client` also accepts a custom `base_url`, and an `api_key` when you do not want to use the environment variable:\n\n```python\nclient = Client(base_url=\"https://agp.hcompany.ai\", api_key=\"hk-...\")\n```\n\n## Errors\n\n```python\nfrom hai_agents import AnswerValidationError, UnprocessableEntityError\nfrom hai_agents.core import ApiError\n```\n\n`ApiError` is the base for HTTP failures and carries `.status_code` and `.body`. `UnprocessableEntityError` is the 422 raised when a request fails validation. `AnswerValidationError` is raised when a completed answer does not match `answer_schema`, with the unparsed value on `.raw`.\n\n## Webhooks\n\nVerify the signature on an incoming webhook before trusting it:\n\n```python\nfrom hai_agents import verify_webhook, WebhookVerificationError\n\nevent = verify_webhook(request_body, signature, timestamp, secret)\nprint(event.type, event.data)\n```\n\n## Command line\n\nThe `cli` extra installs the `hai` command for driving agents from your terminal:\n\n```bash\nhai login\nhai run \"What's the top story on Hacker News?\"\nhai sessions list\nhai sessions watch <session-id>\nhai mcp install\n```\n\n`hai login` signs in through the browser and stores a key in `~/.config/hai/.env`. `hai mcp install` adds the hai-agents MCP server to Cursor, VS Code, Claude Code, and other MCP clients. Credentials resolve from `--api-key`, then `HAI_API_KEY`, then a local `.env`, then `~/.config/hai/.env`. Run `hai --help` for the full command set.\n\n## Documentation\n\nGuides, core concepts, and the full API reference live at **[hub.hcompany.ai/computer-use-agents](https://hub.hcompany.ai/computer-use-agents)**.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 10628,
  "sha": "9929daffc8566339a193ae96aef6f850873616ac2ac2c5807c6616cd76f186fc",
  "repo_slug": "hcompai/hai-agents-python",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_hcompai_hai_agents_713a4f97/readme"
}