Metadata-Version: 2.4
Name: agent-conclave
Version: 0.1.0
Summary: One interface through which a coding agent spawns agents on any harness and exchanges messages with them
Keywords: claude-code,codex,opencode,agents,orchestration,cli
Author: anfreire
Author-email: anfreire <100360644+anfreire@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Software Development
Classifier: Topic :: Utilities
Requires-Python: >=3.12
Project-URL: Homepage, https://github.com/anfreire/conclave
Project-URL: Repository, https://github.com/anfreire/conclave
Project-URL: Issues, https://github.com/anfreire/conclave/issues
Description-Content-Type: text/markdown

# conclave

One interface through which a coding agent, on any harness, spawns agents on
any harness and exchanges messages with them. Any harness can be the parent
and any harness the child; the verbs, the knobs and the output are the same
whichever sits on either end.

## Install

```
uv tool install agent-conclave
conclave install
```

The first line puts `conclave` on your PATH. The second puts in place what
each harness on your machine needs — the skill, OpenCode's plugin, one Claude
Code setting — and says where:

```
INSTALLED harness=claude skill=/home/you/.claude/skills/conclave crossSessionInbound=accept
INSTALLED harness=codex skill=/home/you/.agents/skills/conclave
INSTALLED harness=opencode skill=/home/you/.agents/skills/conclave plugin=/home/you/.config/opencode/plugins/conclave.js
```

Nothing else: no daemon, no server, no config file. Needs Linux or macOS,
[uv](https://docs.astral.sh/uv/) (which brings Python), and any of `claude`,
`codex` and `opencode` on PATH.

## Use

Tell your agent:

```
Use conclave: spawn a Codex reviewer for the diff of HEAD, then a Claude Code
worker to fix what it finds. Supervise both and report back.
```

The agent does the rest with the verbs below: it briefs children, waits for
their results or has them pushed into its session, answers their questions
and reports. A Codex parent runs with its sandbox off
(`--sandbox danger-full-access`): the sandbox keeps its shell from writing
conclave's state and its children from reaching the network.

## Verbs

```
conclave spawn --harness <harness> [--cwd DIR] [--model M] [--effort E] [--name N] [message] [-- flags...]
conclave send <id|address> [message]
conclave wait <id> [--timeout SECONDS]
conclave list [--all]
conclave cancel <id>
conclave doctor
conclave install
```

A message is the positional argument or, when absent, stdin — a heredoc for
anything longer than a line.

**spawn** starts a child: a new session on the harness, given the message as
its first turn. It returns once the harness has named the session, which is
also proof the harness started — or, when the turn is already over by then,
with its result, as `wait` would print it:

```
SPAWNED id=reviewer-8f3a harness=claude session=01a84a47-…
```

**send** gives a child its next message; its session is resumed for one more
turn. A child's turns run one at a time, in the order they were sent, so a
message to a busy child waits its turn:

```
SENT id=reviewer-8f3a turn=2
```

Sent to an address instead — `claude:<session-id>`, `codex:<thread-id>` or
`opencode:<session-id>@<server-url>` — the message goes into that live
session's inbox and arrives the way its harness takes a message from outside:
Claude Code reads it between tool calls, Codex once its running turn ends,
OpenCode at its next step; when the session is idle, it starts a turn.
`CONCLAVE_PARENT` is such an address, or the parent's own id when the parent
is itself a child, so `conclave send "$CONCLAVE_PARENT" …` reaches the parent
either way:

```
SENT id=claude:01a84a47-…
```

**wait** blocks until the child's next result and prints it — oldest first,
each result once, however many times you ask:

```
DONE id=reviewer-8f3a turn=2
<the child's final message>
```

`FAILED` (exit 1) carries the error, whole: the harness's own, or what kept
it from starting. `CANCELLED` carries nothing. With `--timeout`,
`TIMEOUT id=… after=30s busy` or `… idle` says whether a turn is still
running; the same command again picks up where it left off. Nothing is lost
when a wait is cut short: results stay until delivered.

**list** prints one line per child of this session — `--all` for everyone's,
each line then ending in `parent=`:

```
id=reviewer-8f3a harness=claude status=busy turn=2 unread=0 session=01a84a47-… cwd=/work/repo
```

**cancel** stops whatever is in flight for a child and prints how many turns
it stopped. That line is those turns' result: `wait` does not print them
again. The session stays resumable: the next `send` starts a new turn.

```
CANCELLED id=reviewer-8f3a turns=1
```

**doctor** pushes a probe to this session the way results are pushed, and
says where it went:

```
PROBED address=claude:01a84a47-…
```

The probe arrives as a message from outside — proof that pushed results will.
In a child it prints `CHILD id=…` and probes nothing: a child's results arrive
as its next turn.

**install** is the second line of the install above; what it puts in place,
and where, is under "Where things are". The links point into the installed
package, so upgrading `conclave` upgrades them; run it again if a harness
arrives later.

## Results

A result reaches its parent once, by the first of these that applies:

- **`wait`** — whoever is waiting on the child gets it.
- **A push** — with nobody waiting, the result is delivered as a message into
  the parent's session: `[conclave] DONE id=reviewer-8f3a turn=2` and the
  text below it, exactly what `wait` would have printed.
- **The state directory** — when the push cannot reach the parent, the result
  stays for `wait`, and `list` counts it as unread.

A push lands in the parent's own inbox: Claude Code's session socket, Codex's
`codex queue`, OpenCode's server. Codex needs nothing for it. OpenCode needs
its plugin, which `install` put in place: it tells the session's shell which
session it is and where its server listens — how conclave learns who it is
working for. Claude Code, when the session bypasses permission prompts, holds
messages from outside unless `crossSessionInbound` is `accept`: `install`
seeded it, a project's or an organization's settings can tighten it, and
`conclave doctor` tells.

## Knobs

`--model` and `--effort` go to the harness verbatim, on every turn; the
harness validates them and its errors come back as `FAILED`. `--name`
gives the child a short name; the id is `<name>-<hex>`. `--cwd` is the
child's working directory. Anything after `--` rides the harness's launch
line, verbatim, every turn.

## Children

Children never prompt: each harness runs in its skip-permissions mode, with
no sandbox. A child asks by ending its turn with the question; the parent
answers with `send`. A child knows who it is and who spawned it through
`CONCLAVE_SELF` (its id) and `CONCLAVE_PARENT` (the parent's address). A
Claude Code child accepts messages sent to its address, mid-turn: it is
launched with that setting on its own launch line.

A brief that works is self-contained — the child shares none of your
context — states one job, and says how to finish: end the turn with the
report you need. The final message is the result `wait` prints.

## Where things are

```
~/.local/state/conclave/children/<id>/
    meta.json          what was spawned; never changes
    waiter             locked while someone waits, so results wait for them
    turns/<n>/
        message        what the child was told, verbatim
        stream         the harness's own output, one JSON line per event
        stderr         the harness's stderr, and the runner's
        runner, pid    process ids of the turn's owner — the caller, then
                       the runner — and of the harness
        session        the session id, as soon as the harness names it
        exit           the harness's exit code, when the runner saw it
        cancel         present once a cancel was asked for
        result         how the turn ended: status and text; written once
        delivered      present once the result was handed on
```

`XDG_STATE_HOME` moves the root. A session sees its own children: `list` is
scoped by the parent's address, which comes from the session variables of
the harness running the shell — `CLAUDE_CODE_SESSION_ID`, `CODEX_THREAD_ID`,
or the plugin's `OPENCODE_SESSION_ID` and `OPENCODE_SERVER_URL`; the nearest
harness when they nest — or from `CONCLAVE_SELF` when the parent is itself a
child.

What `install` puts in place — three symlinks into the installed package,
and one key:

```
~/.agents/skills/conclave                 the skill, where Codex and OpenCode look
~/.claude/skills/conclave                 the skill, where Claude Code looks
~/.config/opencode/plugins/conclave.js    the plugin, where OpenCode loads plugins
~/.claude/settings.json                   crossSessionInbound: accept, where nothing set it
```

The key is the setting Claude Code documents for unattended workers, and it
applies to every Claude Code session you run; why a session needs it is
under "Results". `install` seeds it only where nothing sets it, never
overrides a value you chose, and writes nothing else into any settings file.
`CLAUDE_CONFIG_DIR` and `OPENCODE_CONFIG_DIR` move the harness directories
here as they do for the harness. To remove conclave, delete the three links,
drop the key, and run `uv tool uninstall agent-conclave`.
