Any other host
The four documented hosts are examples, not a fixed list. magus owns the guard rules and the verdict, not integration code per host, so the host-specific part is a template or a few lines of config you control, and adding a host is your edit rather than a new magus release.
That is a standing decision rather than a gap waiting to be filled. A codec per host would cost us upkeep as the products change, and it would cost you more than it costs us: wiring you did not write is wiring you cannot repair on the afternoon your host changes its event shape, and this guard fails OPEN, so a hook that quietly stopped judging looks exactly like a session with nothing to deny. Read the template once and it is yours.
Any host that can run a command and read its output fits.
| what | where |
|---|---|
| skills | whichever directory your host discovers SKILL.md in |
| always-on rules | AGENTS.md, if your host reads one |
| guard wiring | whatever pre-tool hook or plugin your host offers |
| rehydration | whatever session-start or post-compaction event fires |
| MCP | MCP |
Skills
Not every host reads the Agent Skills format. If yours discovers SKILL.md
directories, install into the one it names:
magus agent install --tar | tar -xf - -C <the directory your host reads>
If it reads only an instruction file, run magus agent install for the
AGENTS.md block it prints and paste that instead. Skills covers
both.
MCP
Any client that takes the documented connection details and a bearer token can connect; see MCP. This wiring belongs to the host owner. An agent uses the CLI fallback when MCP is unavailable; it does not manually start Magus solely to obtain tools.
Guard hook
The wiring is the same shape everywhere: get the command or path out of the host
event, hand it to magus session hook, and render the verdict into the host's reply.
printf '%s' "$command" | magus session hook -o 'template=<your host reply>'
If your host writes its payload as JSON with tool_input.command or
tool_input.file_path, pipe the payload in unchanged: magus reads the envelope
itself, infers a write from a file path, and picks up session_id and
hook_event_name for attribution. Otherwise select the field yourself - jq -r '.<path>' is what the shipped templates use - and pass --path when the input
is a file. A lease id is not a field you select out of the event: the guard
inherits it from the worker's environment, which the orchestrator that spawned
the worker has to export - see
wiring a lease into a worker.
The fastest start is to copy magus-command.sh and
set its override variables: HOST_EVENT_PATH, HOST_RESPONSE, and the two
unavailable-response variables. Name your host on the command itself,
sh magus-command.sh --agent-name <your host>: the template reads the host from that
argument and nowhere else, and refuses a call without it
(MGS3024) rather than answering in
another host's dialect. That gets you the missing-binary and broken-binary handling
without writing it again.
Three decisions are yours to make:
- Which channel carries a deny. Most hosts have one that reaches the model.
- Which channel carries an advise, and when it arrives. It is often not the
gating event: a host that sends a message only with a denial has nothing to
attach an advisory to, and its post-tool event is the channel instead, which
lands after the call rather than before it.
HOST_ADVISE_BRANCHreshapes the arm for whatever your host takes. Reserve__MAGUS_NO_ADVISEfor a host that does worse than ignore a reply it does not know: one that treats an unsupported key as an error and then fails OPEN is disarmed by an advisory rather than merely deaf to it. Suppressed, an advise renders nothing at all. None of the four documented hosts needs it today. - What happens when magus cannot be found or cannot judge. Failing open keeps the session usable and is what every shipped template does. Say so visibly rather than exiting quietly, because an unguarded session you know about beats one you do not.
If you contribute the result back, add a magus-guard-coverage: line declaring
what your glue carries per surface and decision. A parity gate reads those lines
and fails the build when a host was never asked about a decision the contract
grew.
Lease capture
Separate from the guard, and the same shape: pipe the host's pre-tool event to
magus session hook when the tool being called is the one that hands work to a
sub-agent.
printf '%s' "$event" | magus session hook --agent-name <your host> >/dev/null 2>&1; exit 0
magus recognizes a lease by the FIELD it carries, never by a tool name it
would have to enumerate per host: a tool_input with a prompt is a spawn. It
reads the prompt as the handed context, takes the callee's label from
subagent_type, then description, then tool_name, and takes the parent's
session from session_id. If your host names those fields differently, reshape
the payload before piping it - jq is enough.
The ordering is deliberate: a payload carrying command or file_path is judged
as a command or a write exactly as before, and only one carrying neither is read
as a spawn. Adding this wiring cannot change a verdict you already had.
Nothing judges a lease. A prompt is prose, not a command line, so the verdict is always a pass and a prompt that mentions a denied command is recorded rather than blocked. Discard the output and exit 0.
The event
One agent_spawn event per lease, in the same activity trail and the same
listing as every other kind - no new endpoint, and the console's activity filters
already select it.
| field | what it carries |
|---|---|
time |
when the handoff was observed |
host, session |
the PARENT: the host you named, and its own session id |
actor |
agent |
action |
the child's label, or agent.spawn when the payload named none |
lease |
the job the context declared, when it declared one (see below) |
request_ref |
the handed context, fetched with GetPayload |
request_bytes |
how much context was handed over |
outcome |
ok means the handoff was OBSERVED, never that the child succeeded |
The context is a blob, not a field. A lease prompt runs to kilobytes and can carry anything the orchestrator pasted into it, so the event line holds a reference and the body is fetched deliberately, one row at a time. It is redacted through the same resolver as every other trail write, but it is still durable prose on disk: treat the activity trail as readable by anyone who can read the workspace cache.
Correlating a spawn to a lease
Correlation is COOPERATIVE. No host event names a magus lease and magus will not guess one from prose, so an orchestrator that wants the join writes ONE marker:
lease: <id>
It must be the FIRST non-blank line of the handed context, and its trimmed text
must be exactly that. The id is a bare token of letters, digits and the
separators - _ . / :, at most 128 characters, with nothing after it on
the line. Leading blank lines are skipped; the head of the context is capped at
4096 bytes, so a marker cannot hide behind a pathological first line.
Leading the prompt is the contract, not a convention. A delegation prompt
routinely quotes a job listing, a file, or another agent's transcript, and a
lease: line lifted from any of them would stamp the event with a lease this
handoff has nothing to do with. Position is what separates a marker you wrote
from one you pasted.
Anything else leaves lease empty: no marker, an id with prose after it, an id
carrying other punctuation, a marker below the first line. That is a missing
join, not an error, and it is the designed outcome for an orchestrator that
never opted in.
Notifications
Wire any event that means a human is needed to magus session notify; see Attention hooks.
Recording where the work stands
Wire whatever event fires when a session ends to
magus-checkpoint.sh, passing your
host's name as --agent-name <your host>. magus session lists what it records.
The requirements are close to nothing, which is deliberate. magus reads the
revision, branch and dirtiness from the tree itself, so a host that emits no
payload at all still produces a usable checkpoint. If your host does carry a
session id and a transcript path, pipe its event in and magus takes them from
the envelope, or pass --session and --transcript. Both are pointers magus
records and never opens.
magus needs no release to learn about your host. --agent-name is an opaque
label you choose, exactly as on the guard hook.
Handing a session its state back
If your host has an event for "a session started" or "the history was
compacted", wire it to
magus-rehydrate.sh and whatever it
prints reaches the model as context. It runs magus session --brief, which
reads this checkout off the disk: branch and revision, commits not yet on the
base ref, the dirty tree classified, the live leases, the last recorded run's
failures, the guard wiring, and where the rules live. Set REHYDRATE_RULES to
your host's own instruction file.
Nothing about it is host-shaped except which event you hang it on. If your host
has no such event, run magus session --brief by hand and paste it, or read it
yourself: it is the same answer either way.
Coverage and limits
Whatever your host's hook surface can carry. One binary produces the rules, so they are identical everywhere, and what differs is only how much of a verdict survives the trip to the model. Parity across hosts records that for the documented four.
Verify
printf '%s' 'git stash' | magus session hook -o name
magus doctor
The first proves magus judges at all from your shell. guard wiring in
doctor loads the harness descriptors available to this workspace and reports
each one as verified, uncovered, or invalid. A host without a descriptor
is portable by design, but its copied adapter is not automatically verifiable.