Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ With the default `launcher="resident"`, `h5i` automatically starts the agent ses
<img src="./assets/h5i-python-short.gif" width="95%">
</p>

If you use [herdr](https://herdr.dev) (an agent multiplexer for the terminal), pass `launcher="herdr"` instead: each agent seat comes up as a herdr pane beside your work, labeled `h5i-orch-<run>-<agent>`, with herdr's own per-agent status (working / blocked / done) in its sidebar.

## 3. Examples

[examples/tutorial](./examples/tutorial/) provides basic multi-agent orchestration patterns:
Expand Down
7 changes: 5 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
Every example is a complete, resumable score: kill it at any point and run it
again — journaled steps replay and the run continues where it stopped. They
assume agent runtimes are available (`launcher="resident"` brings tmux
sessions up itself; drop it if you park resident sessions yourself).
sessions up itself; drop it if you park resident sessions yourself, or use
`launcher="herdr"` to bring seats up as [herdr](https://herdr.dev) panes
instead — no tmux needed).

You don't need to `tmux attach` by hand to watch the agents: a resident-launcher
score auto-opens a viewer on each agent session as it comes up — a window
Expand All @@ -24,7 +26,8 @@ You need five things:
or point `$H5I` at it, or pass `h5i_bin=...` to `Conductor`.
3. **Agent runtime CLIs** — every score hires `runtime="claude"` (Claude Code);
most also hire `runtime="codex"`. Both CLIs must be installed and logged in.
`launcher="resident"` additionally needs `tmux` (it spawns the sessions).
`launcher="resident"` additionally needs `tmux` (it spawns the sessions);
`launcher="herdr"` needs the `herdr` binary and a running herdr session.
4. **A repo to work on** — each score opens `Conductor(".", …)`, so run it from
the repository the agents should modify, with a **clean worktree** (the
arena and ensemble scores call `preflight(clean_worktree=True)` and fail
Expand Down
2 changes: 2 additions & 0 deletions src/h5i/orchestra/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ async def main():

from . import patterns, policy
from ._conductor import PROTOCOL_VERSION, Agent, Conductor, Scope
from ._herdr import HerdrLauncher
from ._errors import (
AskParseError,
BridgeClosedError,
Expand Down Expand Up @@ -60,6 +61,7 @@ async def main():
"Conductor",
"Agent",
"Scope",
"HerdrLauncher",
"PROTOCOL_VERSION",
# data
"Artifact",
Expand Down
17 changes: 14 additions & 3 deletions src/h5i/orchestra/_conductor.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@ class Conductor:

- ``launcher``: ``"attach"`` (default — resident sessions pick turns out
of their inboxes), ``"resident"`` (the bridge brings tmux sessions up
itself), or ``"client"`` (every turn is delivered to ``on_turn`` in
*this* process — how tests script agents, and how a score can spawn
its own runtimes). Passing ``on_turn`` implies ``"client"``.
itself), ``"herdr"`` (this process brings each seat up as a pane in a
running `herdr <https://herdr.dev>`_ session — visible in herdr's
sidebar with per-agent status, no tmux or viewer terminals needed), or
``"client"`` (every turn is delivered to ``on_turn`` in *this*
process — how tests script agents, and how a score can spawn its own
runtimes). Passing ``on_turn`` implies ``"client"``.
- ``isolation``: the run's default sandbox tier for hired agents' envs
(``"workspace"``, ``"process"``, ``"supervised"``, ``"container"``, …).
Every :meth:`hire` inherits it unless it passes its own. An explicit
Expand Down Expand Up @@ -125,6 +128,14 @@ def __init__(
raise TypeError('launcher="client" requires on_turn=...')
if on_turn is not None and launcher not in (None, "client"):
raise TypeError(f'on_turn=... implies launcher="client", not {launcher!r}')
if launcher == "herdr":
# The herdr launcher is client-mode with a built-in turn handler:
# the engine dispatches `launcher.on_turn` here, and we make sure
# the seat's pane is up (see `_herdr.HerdrLauncher`).
from ._herdr import HerdrLauncher

on_turn = HerdrLauncher(h5i_bin=h5i_bin)
launcher = "client"
self._repo = str(Path(repo).resolve())
self._run = run
self._title = title
Expand Down
Loading
Loading