Skip to main content

Agent Parley — separate work, shared context

Separate worktrees. Shared context. One screen.

Run several coding agents at once and know who owns what.
Every claim, handoff and refusal is recorded, attributed and visible.

Release Zero runtime dependencies Python 3.12+ claude and codex MIT license

See it · Install · Run · Enforce · Filter · Providers · Docs · Limits


See it

Launch a lane, see who owns what, watch every lane at once, narrow to one provider, and watch a hook refuse a branch switch inside an assigned lane.

agent-parley top showing three lanes with issues, mail, leases, denials and served calls

One screen for every lane: session state, branch drift, issues owned, handoffs pending, unread mail, held reservations, delivered context, what enforcement denied, and what that lane's own client recorded for its session. Read-only, no model call, q quits.

A reservation that declared a time to live is counted with ! once that deadline passes, so a lane that died holding a path reads differently from one still working on it; nothing is revoked, and releasing it stays its owner's decision. TOKENS is the session total that lane's own native client already wrote to disk. No vendor is asked, no key is read and no price is applied, so it is a relative signal between refreshes rather than billed spend, and the cell is blank when nothing could be read.

An operator editing the base checkout is otherwise invisible to a lane until the merge conflicts. Every top and status frame reads git status of the base checkout once per project and matches the dirty paths against each lane's active reservations with the same rule a competing reservation is judged by. A match is printed under the lane's row, and the lane receives one advisory notice naming the path, repeated only when the set of paths changes. Nothing pauses, reverts or locks; reservations stay advisory. --no-operator-edits skips the reading for a repository whose base checkout is always dirty.

Every frame on this page is real command output from a demo project. Only the state and project paths are shortened.

Install

Linux, macOS or WSL2 with the repository in the Linux file system, Git, and uv. No clone. The wheel needs no third-party runtime packages.

uv tool install agent-parley
# to track the default branch instead:
# uv tool install git+https://github.com/suneel944/agent-parley

Then add the plugin to whichever CLI you drive. One marketplace serves both.

claude plugin marketplace add suneel944/agent-parley
claude plugin install agent-parley@agent-parley
codex plugin marketplace add suneel944/agent-parley
codex plugin add agent-parley@agent-parley

The plugin carries the shared coordinate skill, so an agent can read coordination state, claim an issue and hand work off in its own words. It is deliberately skill-only: the launcher supplies MCP configuration and lifecycle hooks per session, and it is also what creates the worktrees and runs the coordination service. The plugin alone gives an agent the skill and nothing to coordinate through.

For a pinned, checksummed install, take a wheel from Releases instead.

Shell completion is generated from the command tree itself, so it offers the commands and flags the installed version actually has. Install it where your shell looks, then reload the shell:

agent-parley completion bash > ~/.local/share/bash-completion/completions/agent-parley
agent-parley completion zsh  > "${fpath[1]}/_agent-parley"
agent-parley completion fish > ~/.config/fish/completions/agent-parley.fish

Rerun the command for your shell after an upgrade to regenerate the script. Participant names, providers, credential profiles, project roots and issue numbers complete from local coordination state; that lookup reads published files without taking the operation lock, so a busy store never stalls the shell.

Run it

From a committed, clean checkout, one terminal per agent:

# Terminal 1
agent-parley run claude

# Terminal 2
agent-parley run codex

That is the whole setup. The first run registers the repository, creates that participant's worktree and branch, starts the coordination service, and hands you the native CLI. Prompt it exactly as you always do.

A new name creates its own lane, so a second account of the same provider, or another provider, is one more terminal:

agent-parley credentials add account-2 --config-home ~/.claude-account-2
agent-parley run claude-2 --provider claude --credentials account-2

Then watch the work:

agent-parley status   # one table per project: ownership, activity, outcomes
agent-parley top      # every lane live, including what enforcement denied
agent-parley metrics  # the same numbers as Prometheus text, or --json

status prints one row per participant. Narrow it by appending a participant name, which reports that lane in full instead of as a row, or by filters that combine:

agent-parley status claude-1          # one lane, the whole reading
agent-parley status --drifted         # lanes off their assigned branch
agent-parley status --pending         # unread mail, offers or stale leases
agent-parley status --outcome blocked --provider codex

--drifted and --pending exit non-zero when a lane matches, so a shell gate fails on drift without parsing the table. Columns shrink to the terminal, and a redirected stream receives every column instead.

Steer one lane without taking over its terminal:

agent-parley say claude-2 "Rebase onto main before you open the pull request."

The message lands in that lane's inbox beside peer traffic, so the agent reads it at its next checkpoint. It comes from operator, a command-line identity: no participant can be named operator, and no MCP tool sends as it, so an agent cannot write in its name. Repeating the same message delivers nothing further, and --ack asks the lane to acknowledge it.

A steer can also wait for the moment it is useful:

agent-parley say claude-2 "Pick up 18 next." --when-released 17
agent-parley say claude-2 "Wrap up for today." --at 18:00
agent-parley mail pending   # recorded, not delivered yet

The item waits in coordination state and is delivered by the supervision poll that already watches every project: no scheduler process, and no read-only view ever delivers mail. A stopped service delivers nothing and loses nothing. --after, --unless-reported and a bounded --every ... --until ... repeat are described in docs/operations.md.

Steer a lane's life without typing into its terminal either:

agent-parley participant pause claude-2     # refuse its calls, keep its work
agent-parley participant resume claude-2    # let it act again
agent-parley participant stop claude-2      # end its session cleanly
agent-parley participant restart claude-2   # start it again from clean state

A paused lane keeps its session, its claims and its reservations. Only acting is refused: every coordination call and every tool use comes back denied naming the operator, and top shows paused. stop tells the lane once, then signals the recorded session process exactly as a normal exit does, and it never signals a process whose recorded identity no longer matches. restart refuses while a session is alive, refuses a dirty worktree naming the paths, replays the recorded init command and launches the same provider and account as before. None of the four releases a claim: ownership still moves only through an explicit release or an accepted handoff, and all four land in the event log.

When a lane's work is ready, integrate it from the base checkout:

agent-parley participant merge claude-2

It always records a merge commit, refuses on a running session, a dirty tree or a drifted lane, and leaves a conflict in place for you to resolve. It never resets, cleans, stashes or force-switches.

To see what that would bring in, and everything that would refuse it right now, ask for a preview first:

agent-parley participant merge claude-2 --preview

The preview only reads. It changes nothing, and it never takes the lane's session lock, so it is safe while that agent is still working. It attempts no merge, so it cannot predict conflicts.

With several lanes finished, integrate them as a set instead of deciding the order by hand:

agent-parley participant merge --all              # every lane reported ready
agent-parley participant merge --group rewrite    # one group of the plan
agent-parley participant merge --all --preview    # the ordered plan only

Both order the lanes from the dependency edges already recorded, so a lane whose issue waits on another is merged after the lane holding that issue. A cycle is refused and named, never quietly ordered. Every candidate is preflighted with the same conditions --preview reports, and each merge then runs through the single-lane path, so nothing is integrated on easier terms than it would be alone.

A group is admitted whole or not at all: one refused member leaves the group unmerged. Execution is ordered rather than atomic, so a merge or a gate failure part way through stops the run, leaves the earlier merge commits in place and reports what was integrated, what refused and what was not attempted. Nothing is reset or reverted.

With eight lanes, ending the day should not be eight commands. say, participant stop, participant pause, participant resume, participant merge, participant pr and issue assign take a lane selector in place of the positional name:

agent-parley say "Wrap up for today." --all
agent-parley participant stop --provider claude
agent-parley participant merge --outcome ready --yes
agent-parley participant resume --idle
agent-parley participant pr --drifted

--all selects every lane and the other filters narrow it, so they combine. Each filter reads the same lane facts status reports: the provider driving a lane, the lane's own latest reported outcome, whether its checkout sits on the branch it was assigned, and whether supervision reads it as stalled. A positional name and a selector together are refused.

Every bulk run prints the lanes it matched and what will happen to each, then asks once for the whole set; --yes skips that one question. A selector that matches nothing does nothing and says so. Independent operations continue past a lane that refuses, printing its refusal beside it, and the closing tally names what was done and what refused. Integration is different: it keeps the ordered stop-on-failure contract above, so the first refusal leaves every later lane unattempted. The exit status is non-zero when any lane refused or failed.

A bulk merge only ever considers lanes that report ready, and its plan names the prerequisites that lie outside the selected set with the ledger's account of each, because narrowing a selection never lifts a recorded dependency. issue assign carries one offer, so a selector stands in for its lane only while it matches a single lane; a wider match is refused and names what it matched.

Groups whose every member is reported ready are marked by plan show and status, and counted in the top header, so you learn a set is integrable without asking each lane. A reported state is a lane's own account, never review or independent verification.

A repository can also require its own command to pass before any merge. The command is recorded in coordination state, not in the repository:

agent-parley verify set 'make check'   # require it from now on
agent-parley verify show               # report what is required
agent-parley verify set ''             # remove the requirement

With one configured, participant merge runs it in the base checkout first and streams the command's output, refusing the merge on a non-zero exit and reporting the exit status. It runs as an argument list, never through a shell, and no flag skips it. It reports the base checkout as it stands before the merge, which is not a claim about the merged result. In a --all or --group run it also runs after each member, so a set whose halves pass alone but fail together is caught; a failure there leaves the merge commit present and visibly unverified.

A repository can also require your own recorded decision before a lane's work leaves its worktree:

agent-parley approval set merge pr   # refuse both until a decision exists
agent-parley approval show           # report what is required
agent-parley approval set            # require no approval again
agent-parley approve claude-2        # record that you approved its report
agent-parley reject claude-2 'Needs a test for the retry path'

With the requirement set, participant merge and participant pr refuse until approve records a decision on that lane's current ready report, and the refusal names both the report and the command that grants it. The decision is bound to that report, the lane's exact commit, its branch and base, and the verification and pull-request settings in force, so new commits, a further report, a retargeted base or a changed gate each need a new decision; the binding is rechecked immediately before the merge or the push. A decision that cannot be read refuses integration rather than allowing it. A rejection delivers your reason to the lane as operator mail and the lane keeps working: only these two commands are gated. status shows awaiting approval, approved or rejected beside a ready report, top counts the lanes awaiting one, and history --kind approval lists the decisions with the rest of the chain.

approve and reject run from the base checkout and refuse to run inside an assigned worktree, so no lane records the approval of its own work through these commands. That is this tool's command-line boundary and not an operating-system one: a program running as you can write coordination state directly. The decision also records that a human decided, not that the code is correct; the verification command, the attribution scan and GitHub's own checks all still run.

A new lane starts as a bare worktree, so every agent would otherwise spend its first turns installing dependencies or copying an untracked file. Record that setup once instead:

agent-parley init set 'uv sync --locked'   # run it in every new lane
agent-parley init show                     # report what runs
agent-parley init set ''                   # remove it

The command runs in the new worktree after it is created and before the native CLI starts, as an argument list, never through a shell, and no flag skips it. It runs only when a lane is created, never on a resume, and participant add runs it as well. AGENT_PARLEY_BASE names the base checkout while it runs, so the command can copy a file Git does not track. A non-zero exit refuses the launch and reports the exit status with the tail of the output; the worktree is left in place so you can see what happened.

Or send it for review instead:

agent-parley participant pr claude-2

That pushes the lane's branch and opens one pull request whose body is the lane's own recorded report, under the headings your pull-request template asks for, referencing the issue the lane claimed. It opens assigned to you and labelled from that issue, so it arrives owned and classified rather than needing repair. It uses your own gh sign-in, refuses when there is no report, no claimed issue, no change type on that issue or nothing to push, and reports an already-open pull request rather than opening a second one.

What it enforces

Ownership changes only through explicit claims and accepted handoffs. No timeout and no process exit moves an issue. agent-parley status reports who owns what, which handoff is waiting on an offer ID, and any lane that left its assigned branch. An owner can record that one issue waits on another with agent-parley issue block 42 --on 17; the listing then names who holds the blocking issue, and every lane sees the change at its next checkpoint. A recorded dependency informs, it does not gate.

agent-parley status listing issue owners, a pending handoff, and a lane on the wrong branch

agent-parley issue list showing an issue that waits on another, the participant holding it, and a pending handoff with its offer ID

Native hooks decide before the tool runs. They block branch changes inside an assigned lane, catch drift after any bypass, and deliver short updates only when coordination state actually changes. Each notice is capped at 1,536 UTF-8 bytes; an unchanged checkpoint adds no context at all. If a rename removed the assigned branch, the hook names the exact repair. Branch drift blocks completion once; a Stop retry can end the session while status continues to show the drift.

Two hook denials with their reasons, and the bounded briefing a session start receives

A deadline reports; it never transfers. issue claim 42 --within 2h, issue offer ... --within 30m and say ... --ack --within 15m record a deadline, and agent-parley deadlines set gives a project defaults to inherit. Past its deadline a claim reads overdue with the seconds over, top marks the issue #42!, and a lane that reports blocked on work it still holds spends one attempt of the recorded budget. Ownership never moves on a timer: an overdue claim is still owned, and only an explicit release or an accepted handoff transfers it.

A budget informs; it does not gate. participant budget NAME --tokens 2000000 --calls 5000 --hours 8 records advisory limits on a lane; the same flags on provider budget NAME and budget set give every lane of a provider or a project defaults to inherit, participant over provider over project, field by field, and any limit may stay unset. Consumption comes from what is already read: tokens as the TOKENS column counts them, calls from the served-call records, hours from the recorded session. No vendor is asked and no price is applied, so a token budget is a count and not spend. Past a limit top and status mark the lane over budget with the share consumed, the lane receives one bounded notice naming the crossed limit, and --over-budget selects such lanes, so participant pause --over-budget is one command. Nothing is stopped, revoked or refused: the operator decides.

An out-of-date install says so before it costs a turn. The launcher, the plugin and the store each state a protocol number, and a mismatch is refused at the boundary with both numbers and the one command that fixes it — when a lane starts, when a hook runs, and when a served call arrives. agent-parley doctor prints all three and exits non-zero on a mismatch, so a script can gate on it. It reads only, and prints no credential.

One screen says what needs you now. agent-parley problems lists, oldest first, every condition an operator should act on: a lane stalled or inactive past its supervision threshold, a claim past its deadline, a handoff offer with no answer, a message awaiting acknowledgement past --ack-after, a lane whose branch drifted or whose worktree is dirty with no recent activity, a lane over its advisory budget, a store schema behind the code, and a service that is down. Each row names the lane, the condition, how long it has held and the exact command that clears it. An empty list exits zero with one line saying so; any row exits 1, so a shell or a cron can notice. --json prints the same rows, and P in top shows them in place. The view reads the same snapshot status prints and moves nothing.

The work order is a file you can review. Write the issues, the dependencies between them and the groups that may run in parallel as TOML, then agent-parley plan apply work-order.toml. Applying records the same advisory dependencies issue block records and nothing else — no claim, no assignment, no gate. plan diff previews the edges first, plan show prints the plan as a tree with each issue's current owner, and every apply is versioned by the file's digest, so an edge added by hand afterwards is reported as exactly that.

A stalled lane says so. top and status mark a lane idle when its process is alive, no coordination call has been served for it within the configured interval, and it holds unread or unacknowledged mail at least that old — and they name the oldest waiting item and how long it has waited. The marker only reports: nothing is revoked, no claim is released and no ownership moves.

Your history stays yours. A lane branch is parley/PROJECT_KEY/lane-N: it carries no participant, provider or account name, and agent-parley branch set PREFIX changes the prefix per project. No lane signs its work either. A commit, merge, tag or pull request that credits an assistant, names a vendor or model in an authorship position, or carries a generator signature is denied before it lands, and every commit an integration would carry is scanned again at participant merge and participant pr. There is no flag that skips either check, on any repository or for any provider. Which assistant did the work stays in coordination state, where top and status read it.

Ten scoped MCP tools carry the coordination. Conflicting reservations grant nothing and name the blocking owner with that owner's declared reason. A reservation can name something that is not a file — port:5432, db:local, suite:integration, device:android-1 — because a worktree isolates none of those; a named resource conflicts on an exact match, and agent-parley resources set declares which ones exist. A granted reservation also carries a forecast: the files that changed together with a reserved path in at least three of the base checkout's last 500 commits and that a peer holds right now, each with the peer and the count, so the lane can renegotiate or sequence before it edits. issue claim reports the same forecast from the paths the issue's earlier pull requests touched when a forge is configured. The forecast is advisory and never withholds a grant or a claim.

The forge is selectable per project. github speaks through gh and is the default; beads speaks through the bd CLI and is detected when the repository carries a .beads/ ledger; null keeps issue numbers bare and calls nothing. agent-parley forge set NAME overrides detection. Every forge exchange is best effort and never decides ownership, and participant pr refuses in one sentence under a forge that opens no pull requests. Sends need an idempotency key, so a retry returns the original message instead of a duplicate. Every other write takes one too — reservations, releases, acknowledgements, issue transitions and reports — so a retried call returns the first result and changes nothing further, and the same key with different arguments is refused rather than applied. Fetching an inbox never marks a message read. A send can answer another message, which puts both in one thread, and a participant can read a thread in order or search its own mail:

agent-parley mail thread t12
agent-parley mail search "reservation conflict"

A granted reservation, a denied one naming the blocking owner, a deduplicated send, and an inbox page

Enforcement is recorded, not discarded. Every hook decision carries an enumerated reason and lands in that participant's event log; every served call is recorded inside the transaction that carried its effect. That is why top can show what was denied, to whom, and how often.

Idle time is a number, not an impression. top gains an IDLE column: how long each lane went without coordination activity inside the window, with the project total and the worst lane in the header. status prints the same figure and, under it, every pending item with the seconds it has already waited — a message before its first read, an ack_required message before acknowledgement, a handoff offer before an answer, a ready report before integration. Every figure comes from records the runtime already keeps, so it reports observed coordination inactivity and never claims to know what the native client was doing inside a turn.

No lane waits for a human to give it something to do. When a lane holds no claim, its next checkpoint carries the unclaimed work no dependency blocks — the issues other lanes wait on first — and names the peers holding more than one claim. When a lane holds several claims and a peer has been idle past the stall interval, that lane is told which peer could take one. Every offer is checked first against what the host can read without asking a vendor: the session process, the lane's own client records for a recent rate-limit or usage-window refusal, the worktree, and the acknowledgements it owes. A lane that fails a check is reported unfit with the failed check named, and no offer names it. top shows each lane's FIT result and whether an offer is pending. Nothing is claimed for anyone: issue offer stays the only transfer path, and the recipient still accepts or declines.

Ownership history is queryable. agent-parley history issue 42 lists every claim, handoff, reservation, message and report that touched it, with how long each participant held it; history participant NAME does the same for one lane, and history claim ID follows one claim through to the pull request that ended it. Every claim carries its own identifier, minted again on each claim and each accepted handoff, and every record made while it is held carries that identifier. It reads only: no lock, no rewrite, and a record older than the correlation reports unknown rather than being given an invented one.

That history is bounded, and it can leave the state directory. A lane keeps two event files and discards records older than fourteen days, so top reports recent enforcement rather than the whole project. --since narrows any count to a window, and events export writes the retained records as JSON Lines you can keep for as long as you need:

agent-parley top --since 6h
agent-parley events export --since 7d --output enforcement.jsonl

The whole state directory can leave the machine as one archive. agent-parley state export --output PATH snapshots the store through the SQLite backup interface while the project locks are held briefly and packs it with the manifests, ledgers, activity files, retained event and report logs and attachments; the manifest names the schema, the export time and a SHA-256 digest per member. Registration tokens, credential profiles and native MCP configurations are never archived. state show PATH lists what an archive holds, and state import PATH restores it into an empty state directory, validating every member first and refusing traversal, links and a newer schema; --merge adds projects beside existing ones and refuses a collision. Imported participants register again on their next run, and the import lists every lane path that does not exist here so the operator can recreate the worktree.

agent-parley state export --output parley.tar.gz --project ~/src/app
agent-parley state show parley.tar.gz
agent-parley state import parley.tar.gz --merge

One lane can be followed as a stream. agent-parley watch NAME prints one line per coordination event as it happens: claim issue 42, denied git (branch_switch), mail from codex-2 (thread t12), report ready, call send_message ok, session ended. It starts from the most recent twenty events, follows the store and the event log from there, and stops on q or on interrupt; --since 1h widens the backlog and --kind narrows it. When standard output is a pipe the lines are plain, and --json prints one event object per line, so watch NAME --json | jq works. It never shows the agent's conversation: the stream is coordination only, and the native client's transcript stays in that client.

agent-parley watch codex-2
agent-parley watch claude-1 --since 1h --kind claim --kind denied
agent-parley watch claude-1 --json | jq .description

Scrape the numbers

top is a screen. metrics prints the same counters and gauges as text a monitoring stack reads: the Prometheus text exposition format by default, labelled by project, participant and provider, and --json for scripts. --provider and --since narrow it exactly as they narrow the table:

agent-parley metrics
agent-parley metrics --json
agent-parley metrics --output /var/lib/node_exporter/parley.prom --every 30

--output writes the frame by atomic rename, so the textfile collector of node_exporter never reads a partial one, and --every rewrites it on that interval until you interrupt it. There is no HTTP endpoint and no new port: the file is the interface. The command reads the records top reads, holds no lock and writes no coordination state, and its tokens counter is what the native client counted rather than billed spend, exactly as the TOKENS column is.

Watch one provider

With a dozen lanes open, the whole table is rarely what you want. --provider narrows the view to the participants driven by one provider, and the header counts only the rows it shows:

agent-parley top --provider codex
agent-parley top --provider claude --provider codex

Shape the table

top fits the terminal it is given. Every column is as wide as the widest value in the frame, and a terminal too narrow for the whole set drops the lowest-priority columns in a fixed order and names them under the header instead of clipping every cell. Rows past the fold are paged, never dropped: the footer reads rows 1-8 of 31. --once prints at the width of the terminal and at the full width of the table when the output is a pipe, so a captured file keeps every column intact.

agent-parley top --sort IDLE --reverse
agent-parley top --project payments --participant codex
agent-parley top --columns PARTICIPANT,STATE,ISSUES,IDLE

The same choices are reachable from the live view with single keys:

Key Does
j, k, down, up Select the next or previous lane, paging the table.
enter Show every recorded field of the selected lane, unclipped.
s Order by the next column.
r Reverse the order.
f Narrow to participants, comma separated; empty clears.
o Narrow to projects, comma separated; empty clears.
c Choose the columns shown; empty shows all.
? Show the key map and the column legend.
q Leave. The view never writes state.

A lane that drifted from its branch, holds a stale lease, had a call rejected, owns an overdue issue or lost its session process is drawn in colour where the terminal offers it and in bold where it does not. Each of those also prints its own marker in the table, so a monochrome pipe reads exactly the same.

Providers and accounts

A provider states which native CLI drives a participant and how that CLI reaches a model. Every provider names one of five adapters, and the adapter decides how that CLI is handed its MCP server, its coordination prompt and its hooks. Two of the five take a published plugin, which is why two plugin installations cover every model-endpoint preset:

Provider Native CLI it drives Plugin that carries coordinate
claude claude Claude Code
codex codex Codex
copilot copilot none; the launcher writes that lane's files
gemini gemini none; the launcher writes a private settings overlay
opencode opencode none; the launcher writes a private config directory and plugin
amp amp none; the launcher writes a private settings file, and refuses to launch until Amp raises thread start and idle events
deepseek, kimi, grok claude or codex, vendor endpoint that adapter's plugin
your own, via agent-parley provider add the adapter you name that adapter's plugin

agent-parley provider list prints each definition with unavailable_hooks, the lifecycle events that CLI cannot deliver: PermissionRequest for gemini, SessionEnd for opencode, every event except PreToolUse and PostToolUse for amp, none for the others. A launch refuses an adapter that cannot deliver SessionStart, PreToolUse or Stop instead of running without those guards; today that refuses amp in one sentence naming the missing events.

claude, codex, gemini, opencode and amp use their native accounts. The deepseek, kimi and grok presets carry no endpoint, so their base URL and key must be exported in the launching shell; the launcher refuses to start when a required variable is unset rather than falling back to another account. Coordination state records variable names and config directories, never credential values.

agent-parley run gemini starts Gemini CLI with a lane-private MCP and hook overlay while preserving native system settings and authentication. Existing explicit provider definitions keep their adapter until you update them: a local gemini definition that rides claude or codex through a vendor endpoint shadows the preset and keeps working unchanged, and provider remove gemini reveals the native preset again.

agent-parley run opencode starts OpenCode with a lane-private copy of its configuration directory, selected with OPENCODE_CONFIG_DIR, that adds the MCP server under mcp and one plugin under plugin/ that runs the checkpoint hook command for each supported plugin event. The launch path is verified against a stub executable and the exact hook command; a live OpenCode session has not been exercised, see Operations.

agent-parley run amp builds a lane-private copy of Amp's settings.json, selected with AMP_SETTINGS_FILE and --settings-file, that adds the MCP server under amp.mcpServers and one amp.hooks entry per tool event that runs the checkpoint hook command. Amp raises no thread start or idle event, so SessionStart and Stop cannot run and the launch is refused rather than started unguarded; the overlay, hook translation and threads continue ID resume are verified against a stub executable only, see Operations.

Credential profiles point a provider's config-home variable at a separate directory, so one provider can run under several logins. Up to 32 participants per project.

Other agent CLIs

Six adapters cover the native configuration contracts. claude and codex take MCP servers, the coordination prompt and lifecycle hooks as command-line arguments. copilot reads them from files instead, so Agent Parley writes mcp-config.json and settings.json into that lane's own Copilot configuration directory; a copilot lane therefore requires a credential profile, and the launcher refuses without one rather than writing hooks into the configuration directory your own sessions use. gemini, opencode and amp each receive a lane-private copy of their native configuration that lives in Agent Parley's state directory, never in the repository, and is rebuilt on every launch and removed by participant retire. amp is refused at launch until Amp can raise the required guards. Operations records what each CLI supports, where its MCP and hook configuration lives, and which behaviour is verified against a stub rather than a live session.

Command reference

Use agent-parley COMMAND --help for arguments. --home DIR selects private state globally; repository commands accept --repo PATH unless noted below. Issue mutations, reports and lane mail resolve identity from the current lane. say and issue assign act as operator from any checkout of the repository.

Command Purpose
up Start the local coordination server.
down Stop the server while retaining state and worktrees.
completion SHELL Print a bash, zsh or fish completion script generated from the installed command tree.
status Show server health and one table per project; NAME reports one lane in full, and --project, --provider, --outcome, --drifted, --pending, --idle, --since, --over-budget and --issue narrow the rows.
setup PATH Register a repository from committed HEAD.
run NAME Launch a lane; supports --provider, --credentials, --repo, and --task.
top Watch lanes; --once prints a snapshot, --interval sets refresh seconds, --provider, --project, --participant and --since filter it, --sort, --reverse and --columns shape it.
metrics Export the live counters and gauges as Prometheus text or --json; --output writes a file atomically and --every rewrites it.
report Record --state, --summary, and required --remaining or --evidence; --idempotency-key makes a retry safe.
say NAME TEXT Send as operator; --ack requests acknowledgement and --key controls deduplication.
say NAME TEXT --after 30m Record the message for later; --at 18:00, --when-released N and --unless-reported set the trigger, and --every 1h --until 18:00 records a bounded repeat.
issue list Show claims, dependencies and handoff offers.
issue claim NUMBER Claim an available issue from this lane.
issue release NUMBER Release ownership without closing the GitHub issue.
issue offer NUMBER --to NAME --summary TEXT Pause work and offer ownership explicitly; --when-released N records it until that issue is released.
issue accept NUMBER --offer-id ID Accept the current offer addressed to this lane.
issue decline NUMBER --offer-id ID Decline the current offer addressed to this lane.
issue cancel NUMBER Cancel this lane's pending handoff offer.
issue assign NUMBER NAME Offer an issue to a lane as operator; --reason travels with the offer and --unassign withdraws one no lane accepted.
issue block NUMBER --on NUMBER Record an advisory issue dependency.
issue unblock NUMBER --on NUMBER Remove a recorded dependency.
plan apply PATH Record a TOML work order as advisory dependencies; plan diff PATH previews it.
plan show Print the applied plan as a tree with owners; --json prints it for scripts.
doctor Report launcher, plugin and store versions and their fit; non-zero exit on a mismatch.
problems List every lane, claim and store condition that needs an operator, oldest first, with the command that clears each; --ack-after sets the acknowledgement age, --json prints it for scripts, exit 1 when any row exists.
issue ... --idempotency-key KEY Retry any transition safely; the repeat returns the first result.
participant list List the project's lanes and their identities.
participant add NAME Create a lane with an optional provider and credential profile.
participant restore NAME Restore the assigned branch while preserving work.
participant retire NAME Retire an idle lane while preserving recoverable work.
participant pause NAME Refuse a lane's calls and tool use; keep its session and claims.
participant resume NAME Let a paused lane act again.
participant stop NAME End a lane's session from the base checkout; keep its claims.
participant restart NAME Start a stopped lane again from a clean worktree.
participant merge NAME Run the configured gate and merge; --preview only inspects.
participant pr NAME Push the lane branch and open or locate its pull request.
participant budget NAME Show or set the lane's advisory --tokens, --calls and --hours limits; 0 removes one. Crossing a limit marks the lane and stops nothing.
... --all --provider N --outcome S --drifted --idle --over-budget Select several lanes for one say, issue assign, participant stop/pause/resume/pr/merge; one plan and one confirmation, --yes to skip it.
approve NAME Record your approval of a lane's current ready report.
reject NAME REASON Record a rejection and deliver the reason to the lane.
approval show Show which steps require a recorded approval first.
approval set [STEP ...] Require an approval before merge, pr, both, or none.
provider list List built-in presets and local overrides.
provider add NAME Define a provider; warn when shadowing a built-in preset.
provider remove NAME Delete a local definition, restoring a shadowed preset.
provider budget NAME Show or set the advisory limits every lane on that provider inherits.
credentials list List native account profiles.
credentials add NAME Define a config home and environment requirements.
credentials remove NAME Delete a profile definition, preserving native files and logins.
branch show Show the prefix new lane branches are created under.
branch set PREFIX Set that prefix; existing lanes keep their branch.
forge show Show the issue tracker this project coordinates over.
forge set NAME Select github, beads or null; only github opens pull requests.
resources show Show the named resources lanes may reserve.
resources set NAMES Declare them; an empty string accepts any well-formed name.
deadlines show Show this project's deadline and attempt defaults.
deadlines set Set --claim, --offer, --ack windows and --attempts.
budget show Show the advisory token, call and hour limits every lane of this project inherits.
budget set Set --tokens, --calls and --hours project defaults; a budget informs and does not gate.
verify show Show the project's configured pre-merge command.
verify set COMMAND Set that command; an empty string removes it.
init show Show the command every new lane runs before it starts.
init set COMMAND Set that command; an empty string removes it.
mail thread ID Read this lane's messages in a thread; --after-id pages forward.
mail search QUERY Search this lane's mail with an optional --limit.
mail pending List operator messages and offers recorded but not delivered.
mail cancel ID Remove one recorded operator item before it is delivered.
history issue N List every record that touched an issue, with each holding.
history participant NAME List everything one lane filed.
history claim ID Follow one claim to the pull request that ended it.
events export Export JSON Lines; filter by --participant and --since, or write --output FILE.
state export --output PATH Write the whole state directory, or one --project ROOT, as one tar archive with a hashed manifest and no credentials.
state show PATH List an archive's projects, participants, issue counts and export time without importing it.
state import PATH Restore an archive into an empty state directory; --merge adds projects beside existing ones and refuses a collision.
watch NAME Follow one lane's coordination events as a stream; --since widens the backlog, --kind narrows it, --json prints JSON Lines. The agent's conversation is never shown.

Every read-only command above also accepts --json and prints exactly one JSON document, so a script, a shell prompt or another agent reads coordination state without parsing a table: status, top, issue list, participant list, mail thread, mail search, mail pending, verify show, init show, provider list and credentials list. top --json prints one frame and exits. The document carries the identifiers the table abbreviates — offer, message and thread IDs — with every time in RFC 3339, and no credential value. Field names are documented in docs/operations.md and carry the same stability promise as the flags. events export and watch --json stay JSON Lines, because each is a stream rather than a snapshot.

Removing a definition leaves participant references intact. Redefine that name before relaunching a lane that uses it. A removed provider override immediately reveals its built-in preset, if any.

The live dashboard drops columns by priority on narrow terminals and marks hidden participants when space runs out. running; no hooks means the session needs relaunching to obtain checkpoint reporting. top --once prints full detail.

MCP tools

Authentication supplies the lane identity; tool arguments cannot select another participant or project. Reservations are advisory, not filesystem locks.

Tool Purpose
send_message Send to peers using an idempotency key; optionally join a thread or require acknowledgement.
fetch_inbox Page inbox metadata and optional bodies; filter with unread or unacknowledged.
mark_message_read Explicitly mark a received message read; takes an optional idempotency_key.
acknowledge_message Explicitly acknowledge a reviewed message; takes an optional idempotency_key.
file_reservation_paths Reserve advisory path patterns and report conflicts; takes an optional idempotency_key.
release_file_reservations Release reservations owned by this lane; takes an optional idempotency_key.
list_participants Discover addressable identities, tasks and last coordination times.
read_thread Page messages this lane sent or received in one thread.
search_messages Search only messages this lane sent or received.
read_attachment Page an attachment a message, report or offer named; only its writer and its addressees may read it.

Inbox rows include read_ts and ack_ts. Fetching changes neither. Both filters can be combined; unacknowledged selects messages that requested an acknowledgement and have not received it. The result budget is 8,192 UTF-8 bytes.

A message body above 4,096 UTF-8 bytes, a report --evidence above 4,096 or a handoff summary above 2,048 is neither refused nor truncated: the whole body is kept as an attachment under the private state directory and the record carries the first bounded slice ending with [attachment message-12: 20480 bytes]. The peer's checkpoint notice stays within its 1,536-byte budget and ends with that reference. Nothing is delivered whole automatically; the reader calls read_attachment for 2,048 characters at a time, or prints it with agent-parley mail show ID --full and agent-parley report show ID --full. One attachment is capped at 65,536 bytes, a lane holds at most 1 MiB of them, and an attachment is removed when its record is pruned or its offer is declined, cancelled or released.

How it fits together

flowchart TD
    Repo[Your repository] --> Launcher[Agent Parley launcher]
    Launcher --> Claude[Participant · own worktree]
    Launcher --> Codex[Participant · own worktree]
    Claude <-->|Ten scoped MCP tools| Server[Local coordination service]
    Codex <-->|Ten scoped MCP tools| Server
    Server --> DB[(SQLite WAL · mail and reservations)]
    Claude --> Claims[Atomic issue claims and handoffs]
    Codex --> Claims
    DB --> Hooks[Native checkpoints · bounded updates]
    Claims --> Hooks
    Hooks -.-> Claude
    Hooks -.-> Codex

The coordination engine is built in-house with Python's standard library. It has no runtime dependencies and makes no model calls. Your existing logins and permission settings still apply.

What it does not do

Worktrees and reservations are coordination boundaries, not OS sandboxes. Agent Parley integrates a lane only when you run participant merge, and it never approves a command. The runtime can wake an idle lane to review pending mail, with global and per-lane opt-outs and at most three attempts per backlog. Reported ready is ready for review, not verified completion. Token usage still depends on the native agents: CONTEXT reports the bytes coordination itself injects and TOKENS repeats what a lane's own client counted, and neither is billed spend or a claim about a token-saving percentage.

Contributing

Run make check before opening a PR. It checks formatting, lint, typing, documentation rules, package builds, and behavior tests.

Contributing · Architecture · Operations · Security · Code of Conduct · MIT license

Metadata

Release files for agent-parley 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for agent-parley 0.8.0
File Size Uploaded
agent_parley-0.8.0.tar.gz 750.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for agent-parley 0.8.0
File Interpreter ABI Platform
agent_parley-0.8.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.0 MB

Release files / agent_parley-0.8.0.tar.gz

Download URL agent_parley-0.8.0.tar.gz
Size 750.0 kB
Tags Source
SHA-256 checksum
How to use checksums
fb267ad5117199e8aa36791f695ccf9a352fc4c7cdc80f2745c5762cb04d8e99
BLAKE2b-256 checksum
How to use checksums
49c68d70cf9ab52ba8b4920e8793506e7e939bf946c8fd9d81075b889f25cffd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release files / agent_parley-0.8.0-py3-none-any.whl

Download URL agent_parley-0.8.0-py3-none-any.whl
Size 261.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b2fe25132f7dc248ea532bc9dc96b17af051cf2e788c23be96446dbe65beeb56
BLAKE2b-256 checksum
How to use checksums
732657c017d8522e149e2e096617ec2ea53f7bfda1ad07168b2f59c666d7cc63
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 15, 2026.

Transparency log

Release history Release notifications | RSS feed

0.18.2

2 release files

0.18.1

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page