@miadi/hermes-conductor
v0.1.1
Published
Lightweight multi-agent coordinator for Hermes-agent: board state, role nodes (Conductor / Builder / Reviewer / Scout), A2A contract-driven task routing, and multiplexer-friendly terminal event streams.
Readme
@miadi/hermes-conductor
ep114 — Lightweight multi-agent coordinator for Hermes-agent.
@miadi/hermes-conductor sits on top of @miadi/hooks-core to provide a board-level state machine, typed role nodes (Conductor, Builder, Reviewer, Scout), A2A contract-driven task routing, and multiplexer-friendly terminal event streams.
Goals
- Hermes-agent is the thinker, conductor is the orchestrator. Hermes-agent remains the core reasoning agent; task routing, multi-agent coordination, and the board lifecycle live here.
- A2A contract-first. All task and board payloads are strongly typed (
TaskContract,ConductorState,TerminalEventContract). No ad-hoc JSON is exposed to agents in the terminal. - Multiplexed terminal UX. tmux and Herdr share one backend contract while retaining provider-scoped pane identities.
- hooks-core integration. Terminal events are recorded into
@miadi/hooks-coresessiondata JSONL files so any Miadi tool can consume them.
v0.1 readiness boundary
This release is a coordination kernel, ready to embed and evolve: an in-process board, atomic file-backed snapshots, durable human steering state, typed event correlation, and command-runner-injected tmux/Herdr launch adapters.
It is not yet a hosted orchestration service. It does not launch reasoning agents by itself, provide a database/checkpointer, implement LangGraph, or expose full multiplexer event-subscription APIs. Those remain V2 integration work rather than hidden claims of v0.1.
Installation
# From npm:
npm install @miadi/hermes-conductor
# Inside the Miadi pnpm workspace during local development:
pnpm add @miadi/hermes-conductor@workspace:*Quick start
import { Board, makeAgent, makeTask, conductorCycle, builderCycle, reviewerCycle, staticPlanner } from "@miadi/hermes-conductor";
// 1. Create the board
const board = new Board({
planId: "plan-1",
goal: "Build @miadi/hermes-conductor",
endingCondition: "package published and all tests green",
createdAt: new Date().toISOString()
});
// 2. Register agents
const builder = makeAgent("builder", ["typescript", "pnpm"]);
board.registerAgent(builder);
// 3. Subscribe to terminal events (for your multiplexer panes)
board.on("terminal", (event) => {
process.stdout.write(`[${event.channel}] ${event.message}\n`);
});
// 4. Start the board
board.start();
// 5. Conductor cycle: plan tasks and assign builders
await conductorCycle(board, {
agentId: "conductor-1",
plan: staticPlanner([
{ title: "Scaffold package", description: "Create package.json, tsconfig, src/", priority: "high" },
{ title: "Write core types", description: "types.ts with TaskContract etc.", priority: "normal" }
])
});
// 6. Builder cycle: execute assigned task
const result = await builderCycle(board, {
agentId: builder.agentId,
execute: async (task) => {
// ... do real work, return completion artifacts
return [{ kind: "diff", path: "packages/hermes-conductor/src/types.ts", summary: "+200 lines" }];
}
});
// 7. Reviewer cycle: approve or reject
await reviewerCycle(board, {
agentId: "reviewer-1",
review: async (_task, _artifacts) => true // auto-approve; add real logic here
});Architecture
ConductorState (board)
├── plan: PlanContract
├── tasks: TaskContract[]
├── assignments: agentId → taskId
├── agents: AgentProfile[]
├── fileLocks: filepath → FileLock
├── humanSteering: requestId → HumanSteeringRequest
├── boardMetrics: BoardMetrics
└── terminalEvents: TerminalEventContract[] ←── multiplexer streams
Role nodes (pure functions operating on the board):
conductorCycle() — plan tasks, assign builders
builderCycle() — execute task, lock files, report artifacts
reviewerCycle() — approve/reject, human-in-the-loop gate
scoutCycle() — scale builders up/down based on queue depthEvent channels
| Channel | Events |
|---------|--------|
| board | board_started, board_stopped, plan_updated, tasks_planned, board_metrics_changed |
| agent | builder_started_task, builder_completed_task, reviewer_approved, scout_scaled_builders, human_input_required, … |
| file | file_locked, file_unlocked, lock_conflict_detected, lock_expired |
Durable state and human steering
const store = new FileBoardStateStore("/var/lib/hermes-conductor/board.json");
const board = Board.open(plan, store); // restores an existing run when present
const request = board.requestHumanSteering(taskId, "Approve or revise?", "reviewer-1");
// The process may stop here. The request, assignments, locks, and events persist.
const restored = Board.open(plan, store);
restored.respondToHuman(request.requestId, { decision: "approve" });
restored.resumeHumanSteering(request.requestId);Every event carries a stable runId; task events also share the task's
correlationId. State files are written atomically with owner-only permissions.
Multiplexer backends
createMuxBackend("tmux" | "herdr") returns the same MuxBackend interface.
Call launch() with a MuxCommandRunner that executes the supplied executable
and argument array without a shell. Herdr uses structured JSON returned by
workspace create and tab create; IDs are never inferred from labels.
CLI
hermes-conductor help # show help
hermes-conductor status # print board snapshot
hermes-conductor events # list event log files in the session dir
hermes-conductor mux # emit a tmux setup script (pipe to bash)Multiplexer setup
# Generate and run a tmux layout with board/agent/file panes:
CONDUCTOR_SESSION_ID=my-board hermes-conductor mux | bashEnvironment variables
| Variable | Default | Description |
|----------|---------|-------------|
| CONDUCTOR_SESSION_ROOT | /src/_sessiondata | sessiondata root (hooks-core compatible) |
| CONDUCTOR_SESSION_ID | conductor-default | session id for the board |
| CONDUCTOR_LOG_DIR | $CONDUCTOR_SESSION_ROOT/$CONDUCTOR_SESSION_ID | per-pane event log directory |
Relation to other packages
| Package | Relation |
|---------|----------|
| @miadi/hooks-core | Event sink: terminal events are recorded as hooks-core JSONL |
| @miadi/a2a-contracts | Design vocabulary: board/task shapes are inspired by HAWK interfaces |
| @miadi/tide-contract | Future: board events will be routable via tide runtime |
| Hermes-agent | Consumer: Hermes-agent uses hermes-conductor as its coordination layer |
Issue references
jgwill/Miadi#464— ep114 hermes-agent-coordinatorjgwill/Miadi#461— a2a-contracts V2 + hooks-core surfacejgwill/Miadi#463— hooks-core 0.4.0 plan-review contracts
License
MIT
