Skip to main content
While the agent works you can watch it, step in to redirect or stop it, and keep the session open for a back-and-forth. Everything here is addressed to the session’s id and works the same whether the agent runs alone or delegates to subagents. In the Python and TypeScript SDKs, starting a session returns a lightweight handle bound to that id; the snippets below read and steer through it. Every operation is also a direct client call or raw HTTP request, as each reference page shows. Every session is also visible in a UI: see Agent View for the live and replay flows.

Watch a run

There are three ways to read a session, from cheapest to most complete:
  • Liveness, with status. A small snapshot: current state, step count, and token usage. Poll it on an interval for a health check or to detect a terminal state.
  • The answer and live updates, with changes. Long-poll from an event index: the call blocks until something new happens, then returns the new events and the final answer when it lands. Use this while a run is active.
  • Full history, with events. The complete, paginated record of everything the agent observed and did. Page through it to replay or audit a run after the fact.
from hai_agents import Client

client = Client()  # reads HAI_API_KEY from the environment

# start_session(...) returns a handle you can read and steer;
# run_session(...) instead blocks until the agent finishes.
session = client.start_session(
    agent="h/web-surfer-flash",
    messages=[{"type": "user_message", "message": "Find the top story on Hacker News"}],
)

session.status()               # cheap liveness snapshot
session.changes(from_index=0)  # new events + final answer, long-polled
session.get()                  # the full Session resource

result = session.wait_for_completion()  # block until terminal, then read the answer
print(result.status, result.answer)

Stream events as they arrive

changes is a single long-poll: one request that returns the events available past an index. To consume a whole run as a live feed, the SDK handle exposes stream(), an iterator that runs the long-poll loop for you and yields each event in order until the session settles. It resumes from_index and drops the 204 no-change responses automatically, so you only see events. By default it stops as soon as the session settles (a terminal state, or idle awaiting your next message); pass until="terminal" to keep the feed open across the idle turns of an interactive session. stream() is a read-only view and does not answer tool calls: for runs that use custom tools, use wait_for_completion / run_session, which run the tools for you.
for event in session.stream():
    print(event.type)

# With the async client, iterate the same handle with `async for`.

Steer a running agent

As long as the session is not in a terminal state, you can intervene:
  • Send a message to add context or redirect the agent mid-run. The message is picked up on the next step; a message to an idle session also wakes it. See Send a message.
  • Pause and resume to halt the agent with its state preserved (for review or cost control), then continue. Sending a message auto-resumes a paused session. See Pause and Resume.
  • Force an answer to tell the agent to stop exploring and commit to a final answer from what it has so far. See Force an answer.
  • Cancel to stop the session for good; it ends in interrupted. See Cancel.
A blocking run-and-wait call never surfaces the session mid-run: start the session and keep its handle when you need to read or intervene while it works. Sending a steering message is the most common intervention:
hai sessions send "$SESSION_ID" "Only consider results from the last 24 hours"
The other interventions follow the same shape:
session.pause()         # halt, state preserved
session.resume()        # continue where it left off
session.force_answer()  # stop exploring and commit to a final answer
session.cancel()        # stop for good; ends in `interrupted`

Hold an interactive conversation

By default a session ends as soon as the agent answers. Set idle_timeout_s when you create it to keep it open: after each answer the session enters idle and waits that long for your next message before terminating. One session becomes a multi-turn conversation that keeps its full context and environment state across turns.
# Open an interactive session that stays alive for 10 minutes between turns.
SESSION_ID=$(curl -sX POST https://agp.eu.hcompany.ai/api/v2/sessions \
  -H "Authorization: Bearer $HAI_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "agent": "h/web-surfer-flash",
    "idle_timeout_s": 600,
    "messages": [{"type": "user_message", "message": "Find the top story on Hacker News"}]
  }' | jq -r .id)

# After it answers and goes idle, ask a follow-up in the same context.
curl -X POST "https://agp.eu.hcompany.ai/api/v2/sessions/$SESSION_ID/messages" \
  -H "Authorization: Bearer $HAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"type": "user_message", "message": "Now open its comments and summarize the discussion"}'
Watch the session’s status flip to idle between turns; it terminates once a turn goes unanswered for idle_timeout_s.