# custom harnesses

Any agent CLI beyond the built-ins can be registered by dropping a harness.json next to the state file (~/Library/Application Support/hrdx/ on macOS, $XDG_CONFIG_HOME/hrdx/ on Linux). Registered harnesses appear everywhere the built-ins do: in the pickers, in agent cycling, in the sidebar agents list, and in settings.

[
  {
    "kind": "aider",
    "binary": "aider",
    "args": ["--no-auto-commits"],
    "resume": ["--restore-chat-history"],
    "busy": "Waiting for the model"
  },
  { "kind": "goose" }
]
fieldpurpose
kindidentifier used in pickers and pane names (required)
binaryexecutable to launch (default: same as kind)
argsextra arguments passed on every launch
resumearguments that resume the latest session on restore
resume_firstput the resume args before args (for subcommands)
busysubstring visible only while working, drives the spinner and finish sound; empty: braille spinner detection

## busy detection

Agents that report their state through the Program Status Protocol (OSC 7501) are tracked from those reports instead of the screen: working is busy, blocked shows the attention dot, and idle, done, or error are idle. Reports outrank title markers and the screen scrape. Agents that never report fall back to the busy substring or spinner detection above.

agentbusy detection
codexOSC 7501 reports
zot, piOSC 7501 reports; panes start with ZOT_PROGRAM_STATUS=1 and PI_PROGRAM_STATUS=1 unless already set (set 0 before launching hrdx to opt out)
claudeterminal title: ◐/◑ while working, ✳ when idle; OSC 7501 reports win when they arrive

## remote and container panes

Every pane is a real PTY, so a harness can just as well be an ssh, docker, or kubectl client running an agent at the target:

[
  {
    "kind": "remote-codex",
    "binary": "ssh",
    "args": ["-tt", "user@host", "cd /path/to/project && exec codex"]
  },
  {
    "kind": "docker-codex",
    "binary": "docker",
    "args": ["exec", "-it", "-w", "/workspace", "container-name", "codex"]
  }
]

Wrapper scripts are useful when the host, container, namespace, or working directory is dynamic. Set the harness binary to the wrapper path and put fixed parameters in args.