Supported agents¶
lazy-harness wraps more than one coding agent. A profile names the agent it
runs, and every layer below — hooks, deploy, launch, metering, skills — resolves
that agent per profile, not once per machine. Two profiles on the same
laptop can run different agents, share a root directory, and both stay correct.
[profiles.personal]
config_dir = "~/.claude-personal" # agent omitted: inherits [agent].type
roots = ["~/code/personal"]
[profiles.cx]
agent = "codex"
config_dir = "~/.codex-cx"
roots = ["~/code/experiments"]
The registry holds four adapters: claude-code, codex, copilot and null
(a test sentinel that honours nothing). An unregistered name is refused loudly
at load time rather than silently falling back.
What "supported" means here¶
Support is not a boolean, and this page does not pretend it is. An adapter encodes what a run or a log measured, never what a vendor's help page claims. That gives three honest states, and the tables below use all three:
| State | Meaning |
|---|---|
| ✅ | Measured against a real binary and encoded. |
| ❌ | Measured and absent — the agent has no such thing. |
| — | Not measured. No probe has run, so nothing is asserted. |
The distinction is load-bearing. CopilotAdapter.bypass_argv() returns None
for all three bypass levels not because Copilot lacks them but because nobody
has measured them, and putting an unmeasured flag in front of a binary is how
you hand an agent a permission grant you did not intend.
Capability matrix¶
Identification and launch:
| Claude Code | Codex | Copilot | |
|---|---|---|---|
config.toml value |
claude-code |
codex |
copilot |
| Config-dir env var | CLAUDE_CONFIG_DIR |
CODEX_HOME |
COPILOT_HOME |
| Binary | claude |
codex |
copilot |
| Default config dir | ~/.claude-<profile> |
~/.codex-<profile> |
~/.copilot-<profile> |
~/.claude-style global link |
❌ | ❌ | ❌ |
What the harness writes into the config dir:
| Claude Code | Codex | Copilot | |
|---|---|---|---|
| System doc | CLAUDE.md |
AGENTS.md |
copilot-instructions.md |
| Config documents | settings.json, .claude.json |
hooks.json, config.toml |
hooks/lazy-harness.json |
| MCP servers placed | ✅ .claude.json |
✅ merged into config.toml |
❌ no MCP document |
| Skill root | <config_dir>/skills |
~/.agents/skills (host catalog) |
— |
What the harness can read back out:
| Claude Code | Codex | Copilot | |
|---|---|---|---|
| Sessions directory | projects/ |
sessions/ |
session-state/ |
| Transcript reader (metering) | ✅ | ✅ | — |
| Session/project identity | ✅ | ✅ | — |
| Credentials file the harness may check | .credentials.json |
❌ | ❌ (measured: auth survives a throwaway COPILOT_HOME) |
Headless lh exec |
✅ | ❌ | ❌ |
Permission bypass — lh run --bypass=<level>:
| Level | Claude Code | Codex | Copilot |
|---|---|---|---|
enable |
--allow-dangerously-skip-permissions |
❌ | — |
activate |
--dangerously-skip-permissions |
--approve-for-me |
— |
no-sandbox |
❌ | --dangerously-bypass-approvals-and-sandbox |
— |
An unsupported level is an error naming the agent and the level; nothing is
forwarded. It is never answered with a neighbouring level, because the levels
are ordered by how much they give away. Full reasoning: lh run
--bypass
and ADR-049.
A hook the agent cannot serve is left out, and named¶
Each built-in hook declares the Signals it needs and the Operations it
reasons about. Each adapter declares what its agent actually delivers. lh
deploy resolves the two before it writes a profile's config, omits every hook
the profile's agent cannot serve, and prints each omission:
· stop-verify-guard omitted in 'cx': agent 'codex' does not deliver goal_status
A built-in declared for other agents only is omitted the same way, with
declared for agents <list> as the reason.
This is not a deploy failure — the exit code is unchanged and other profiles are
untouched. lh doctor reads the same resolver, so what doctor reports is what
deploy omits. The mechanism, and why a silently-installed inert hook is worse
than an absent one, is in the agent contract.
Picking an agent for a profile¶
lh profile add cx --config-dir ~/.codex-cx --roots ~/code/experiments
# then set agent = "codex" under [profiles.cx] in config.toml
lh deploy --profile cx
lh doctor
lh run --profile cx
lh doctor is the single place that answers "is this profile's agent wired
correctly?" — it reports shared roots and their defaults, hook signal and
operation gaps, transcript readability, MCP servers an adapter cannot place,
and Codex hook trust. lh doctor --json emits the same verdicts by key for a
CI gate.
Per-agent notes¶
- Codex — hook trust, two edit paths, rollout streams,
AGENTS.md. - Copilot — eleven measured events, deny-only verdicts, and the large unmeasured surface.
- Claude Code is the reference implementation; it is what every other page on this site shows by default.
Design record¶
The contract is ADR-041, frozen on 2026-09-15 only after a second, non-identity adapter had run against it. The design and its blast-radius document were accepted on 2026-09-18 on an end-to-end acceptance run against the second agent — 17 assertions, none failed.
Adoption is measured, not assumed: a kill criterion opens on 2026-11-11 and
compares launches on a non-Claude profile against the trailing Claude baseline.
Below the threshold the adapters are removed rather than kept. lh doctor
prints horizon opens 2026-11-11 until then. See the roadmap,
Theme 5.