Proxy CLI
proxy is the command-line client for a running Proxy instance. Every command
is an HTTP call against the Grand Central server that Proxy hosts on
localhost:${PROXY_PORT:-51711} — management commands use the /cli routes,
headless party runs use the same /client API the Proxy app uses.
Proxy must be running. The CLI holds no state and runs no inference itself.
When nothing is listening it prints
Error: Cannot connect to Proxy on port <PORT>. Is Proxy running? and exits 1.
This page is the canonical reference. Rendered version: https://docs.theproxycompany.com/proxy/cli/
Global Flags
| Flag | Default | Description |
|---|---|---|
-p, --port <PORT> |
$PROXY_PORT, else 51711 |
Proxy HTTP port |
-f, --format <FORMAT> |
json |
json for machine parsing, human for readable summaries |
--host <HOST> |
127.0.0.1 |
Target Proxy host |
Port precedence: explicit -p/--port beats $PROXY_PORT, which beats 51711.
Output Contract
Proxy wraps every /cli response in an { "ok": bool, "data": ..., "error": ... }
envelope. The CLI unwraps it:
--format json(default): success prints thedatapayload as one line of compact JSON on stdout. Server errors print the full envelope to stderr.--format human: success prints[OK] <title>followed by the values; arrays of objects render as aligned tables, cells truncate at 80 characters.
Requests time out after 60 seconds (image generate allows 300 seconds;
headless run streams are not bounded by this).
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success (also --help and --version) |
| 1 | Usage error, cannot connect, transport failure — and every headless-run failure |
| 2 | Server-reported error (ok: false envelope or HTTP error status) from any non-headless command |
| 124 | Headless run hit --timeout before the party reached quiescence |
Headless Party Runs
proxy party run seats agents, posts a prompt, streams the live feed, and
prints the final transcript when the session goes quiescent. It is the primary
way for scripts and agents to use Proxy's multi-agent runtime without the app
UI.
# Seat roster agents by name (or ID) and run to quiescence
proxy party run "summarize the open PRs in this repo" --agents claude,codex
# Seat a saved team
proxy party run "triage today's crash reports" --team support-team
# Inline flow DSL (roles are roster agent names)
proxy party run "draft the Q3 plan" --flow "author -> [reviewer*3] ~> synthesizer"
# Bounded run: exit 124 if not quiescent within 300 seconds
proxy party run "audit the release checklist" --agents claude --timeout 300 --json
Exactly one prompt source is required: the positional PROMPT or the
--prompt TEXT flag alias. Exactly one seating source is required: --agents,
--team, or --flow (they are mutually exclusive).
| Flag | Description |
|---|---|
--agents <NAMES> |
Comma-separated roster agent names or IDs to seat |
--team <TEAM> |
Team ID or name to seat instead of --agents |
--flow <DSL> |
Inline flow DSL whose roles are roster agent names |
--format-id <FORMAT_ID> |
Party format: forum, swarm, pair_programming, or a definition ID. Default: forum for multi-agent, swarm for one. Conflicts with --flow (the flow defines the format) |
--prompt <TEXT> |
Flag alias of the positional PROMPT |
--name <TITLE> |
Session title |
--thread <THREAD_ID> |
Invoking thread recorded on the session as invoking_thread. Defaults from the environment when omitted |
--timeout <SECS> |
Exit with code 124 if the party is not quiescent in time |
--json |
Print the final transcript as JSON |
The invoking thread ties a headless run back to the agent session that
launched it: the --thread flag wins, then $CLAUDE_SESSION_ID, then
$PROXY_INVOKING_THREAD. Blank values fall through, and when nothing
resolves the run carries no invoking thread. The resolved value is exported
back into the session's participants as $PROXY_INVOKING_THREAD (see
Party Workspaces), so a party launched from inside a
party inherits the chain.
Stream and Transcript Contract
A run dispatches POST /client/v1/party/run (which returns party_id and
conversation_id), then subscribes to the session's server-sent event stream
until quiescence:
- Live feed goes to stderr as it happens: each posted message renders as
[speaker] text. System notes render as[system] noteand agent failures as[agent-id] error: message. Agent errors do not end the run. - The final transcript goes to stdout once the session's dispatch queue is
idle (quiescence). Default output is one
[speaker] textline per message. --jsonprints a single JSON object instead:
{
"party_id": "run-…",
"conversation_id": "…",
"messages": [
{ "speaker": "claude", "text": "…", "ts": "2026-07-16T00:00:01Z" }
]
}
Only items of type message with non-empty content appear in the transcript
(tool calls and other items are dropped). speaker is the participant display
name, falling back to the role; ts is the message creation time. The
transcript is paged from the server 500 messages at a time, so long sessions
arrive complete.
Reliability behavior, verified against the implementation:
- Queue-idle probe. The event broadcast has no replay: a session that
finished before the CLI subscribed already fired its idle event. On stream
connect the CLI probes
GET /v1/threads/<id>/queue-idleonce and skips straight to the transcript if the queue is already idle. - Keepalives and dead-peer detection. The server sends a keepalive every 15 seconds. Each stream read is bounded at 30 seconds; three consecutive silent reads (about 90 seconds without a single byte) declares the peer dead and the run fails with exit 1.
- Timeouts. The
--timeoutdeadline is checked between reads. A silent stream wakes the reader every 30 seconds, so exit can lag the deadline by up to 30 seconds. On expiry the CLI printsError: timed out waiting for party quiescenceto stderr and exits 124. No transcript is printed; the partial feed is already on stderr. - Errors exit 1. For
party run,party fire, and roster runs, dispatch failures, stream errors, and server-reported errors all exit 1 (the usual exit-2 server-error path is deliberately demoted for headless runs, so scripts can treat nonzero-but-not-124 uniformly).
Flow DSL
--flow compiles a format on the fly. For inline flows, every role name must
resolve to a roster agent (by name or ID); *N seats that many copies of the
agent.
| Operator | Meaning |
|---|---|
-> |
Sequence: dispatch order, implies round-robin dispatch |
[ ] |
Parallel group |
~> |
Quiescence: the left roles settle, then the right roles are dispatched for review |
=> |
Broadcast gate: the left role's posts are gated by the right role |
*N |
Role multiplicity (reviewer*3); bare * means unbounded |
, |
Separates independent clauses |
@ (human gate) parses but is not yet supported for compilation.
# Author drafts, three reviewers respond in parallel, a synthesizer
# reviews once the reviewers settle
proxy party run "draft the Q3 plan" --flow "author -> [reviewer*3] ~> synthesizer"
# Round-robin pair
proxy party run "debate this schema change" --flow "claude -> codex"
# Forum shape: participants settle into a moderator, and every
# participant post is gated by the moderator
proxy party run "research MLX kernel tricks" \
--flow "[scout*3] ~> editor, scout => editor"
Firing Standing Parties
proxy party fire <STANDING_PARTY_ID> [--timeout SECS] [--json]
party fire kicks off a standing party definition and streams it to
quiescence with the same stderr feed, stdout transcript, --json shape, and
exit codes as party run.
Roster Fallthrough
Any unknown top-level word is treated as a roster agent name and run as a party of one:
proxy claude "what changed in the last release?"
proxy codex -p "fix the failing test" --timeout 600 --json
proxy scout --prompt="scan today's inbox"
The prompt is the first bare positional argument, or -p/--prompt.
--timeout and --json behave exactly as in party run. The agent name is
matched case-insensitively against roster names (or exactly against agent
IDs); an unknown name fails with the list of available agents and exit 1.
Standing Parties
Standing parties are durable, schedulable party definitions. Each one lives in
a folder at $PROXY_DATA_ROOT/parties/<id>/:
| File | Purpose |
|---|---|
party.yaml |
Manifest: flow, team, roles, outputs, optional schedule.cron |
goal.md |
Kickoff prompt |
roles/*.md |
Role prompts |
playbook.md |
Optional playbook |
loadouts/*.md |
Loadout files (shared loadouts live in $PROXY_DATA_ROOT/loadouts/*.md) |
schemas/*.json |
Output schemas |
Definitions with an enabled cron schedule fire automatically; any definition
can be fired manually with proxy party fire <id>.
Saving a Live Session
proxy party save <SESSION_ID>
party save promotes a live party session into a standing party: it
dispatches POST /client/v1/party/<id>/save, which serializes the session
into a standing-party folder and runs the standing-folder import, so the
definition materializes through the same path as a hand-authored folder. On
success it prints the resulting party_id and folder_path.
The session's title becomes the party name, its seating becomes the team block, and its flow is derived from the session when the manifest has none. Saving an already-standing party re-serializes it: the existing manifest keeps its flow, schedule, roles, outputs, and goal file, while the name and team block refresh from the live session.
Party Workspaces
Every party session gets a shared workspace directory at
$PROXY_DATA_ROOT/party-workspaces/<party_id>/, created at session creation
with a best-effort .venv bootstrap (uv venv when uv is on PATH, else
python3 -m venv; bootstrap failures never block the session). Harness seats
without an explicit working directory run inside the workspace, so
participants in the same session share files by default.
Every harness/transport participant process receives the session's identity as environment variables:
| Variable | Value |
|---|---|
PROXY_PARTY_ID |
Stable party ID (shared across re-runs of a standing party) |
PROXY_CONVERSATION_ID |
This session's conversation ID |
PROXY_PARTY_WORKSPACE |
Absolute path to the shared workspace |
PROXY_INVOKING_THREAD |
The session's invoking thread, when one was recorded |
The run_python Party Tool
Alongside the workspace, every party participant gets a run_python tool
backed by one persistent, sandboxed Python interpreter per session:
variables and functions defined in one call persist across calls — and
across participants — until the session ends, and other sessions never see
them. Results carry captured stdout plus the final expression's value;
exceptions come back as the Python traceback without ending the turn, so
agents can fix the code and call again. Each call has a 60-second execution
budget. It is plain Python for computation, parsing, and data shaping: there
is no filesystem, network, or module import access, so no pip packages or C
extensions.
Known issue: on a directly-addressed turn the participant carries a pinned
share obligation, and tool choice is forced to share_to_party until it is
resolved — so run_python (or any other tool) cannot run before the reply
is shared on those turns. A fix is pending a design decision.
Command Reference
Provider
Inference providers and their API keys (stored in Keychain).
proxy provider list
proxy provider key set anthropic sk-ant-...
proxy provider key status anthropic
proxy provider key test anthropic
proxy provider key remove anthropic
Agent
The agent roster, registry sync, and default assignments.
proxy agent list
proxy agent get <AGENT_ID>
proxy agent create '{"name":"Scout","accent_color":"#4CBCF2"}'
proxy agent update <AGENT_ID> '{"name":"Scout","system_prompt":"..."}'
proxy agent delete <AGENT_ID>
# Sync from / reset to the remote registry
proxy agent sync
proxy agent reset
# Default assignments (for example the party moderator)
proxy agent defaults list --scope party
proxy agent defaults set '{"scope":"party","role":"moderator","agent_id":"agent-123","position":0}'
proxy agent defaults remove '{"scope":"party","role":"moderator","agent_id":"agent-123"}'
Backend and tuning keys in create/update payloads (provider_id,
model_id, harness_id, temperature, system_prompt,
working_directory, and the other loadout fields) are routed to the agent's
active loadout automatically. The removed evolution_tier, level, and xp
keys are rejected.
Party
Sessions, participants, messages, formats, external threads, and search. The
headless run and fire subcommands are documented above.
# Sessions
proxy party session list --limit 20
proxy party session create --title "Release War Room" --team <TEAM_ID> --message "kick off"
proxy party session get <SESSION_ID>
proxy party session update <SESSION_ID> '{"title":"Refactor Chat","pinned":true}'
proxy party session delete <SESSION_ID>
# Participants
proxy party session participants <SESSION_ID>
proxy party session add-participant <SESSION_ID> '{"agent_id":"agent-123","role":"participant"}'
proxy party session remove-participant <SESSION_ID> <PARTICIPANT_ID>
# Config overrides: whole session, or one participant
proxy party session set-config <SESSION_ID> '{"working_directory":"/path/to/repo"}'
proxy party session set-config <SESSION_ID> <PARTICIPANT_ID> '{"temperature":0.2}'
# Messages
proxy party session messages <SESSION_ID> --limit 50
proxy party session send <SESSION_ID> --content "review the latest changes"
# Synced external Codex / Claude Code / Hermes threads
proxy party external list --provider claude-code --limit 10
proxy party session add-external <SESSION_ID> --thread <THREAD_ID> --role participant
# Search and formats
proxy party search --query "deployment" --limit 50
proxy party format list
proxy party format roles <FORMAT_ID>
Harness
External CLI agent runtimes (claude-code, codex, opencode).
proxy harness list
proxy harness status claude-code
proxy harness discover claude-code
proxy harness configure claude-code
proxy harness unconfigure claude-code
proxy harness probe claude-code
proxy harness connect claude-code
proxy harness disconnect claude-code
proxy harness set-path claude-code /usr/local/bin/claude
# Run the harness binary with arguments after --
proxy harness run claude-code -- --version
# Flattened config, one key at a time, and the settings schema
proxy harness config claude-code
proxy harness config-set claude-code permissions.default_mode "acceptEdits"
proxy harness schema claude-code
# Tunnel configuration
proxy harness tunnel get
proxy harness tunnel set '{"username":"proxy","tunnel_token":"...","local_port":51711,"enabled":true}'
proxy harness tunnel clear
Image
Image generation with stored provider keys. The request timeout is 300 seconds.
proxy image generate "a solarpunk greenhouse at dusk" \
--provider openai --model gpt-image-2 \
--size 1536x1024 --quality high --background opaque \
-n 2 --output ~/Pictures/
Defaults: --provider openai, --model gpt-image-2, --size 1024x1024,
one image. Output defaults to <data dir>/images/. Human format prints one
generated file path per line; JSON prints the full response with paths.
Integration
MCP integrations: transports, registry, and secrets.
proxy integration list
proxy integration list --connected
proxy integration get <INTEGRATION_ID>
proxy integration add '{"name":"GitHub","slug":"github","transport":"http","transport_config":{"url":"https://example"}}'
proxy integration remove <INTEGRATION_ID>
# Connect over stdio or HTTP
proxy integration connect stdio <INTEGRATION_ID> '{"command":"mcp-server","args":["--stdio"],"env":{"TOKEN":"..."}}'
proxy integration connect http <INTEGRATION_ID> '{"url":"http://localhost:3000/mcp","headers":{"Authorization":"Bearer ..."}}'
proxy integration disconnect <INTEGRATION_ID>
proxy integration tools <INTEGRATION_ID>
# Registry
proxy integration registry search --query github --limit 10
proxy integration registry refresh
proxy integration registry install <NAME> --curated
# Import from external MCP config files
proxy integration import
# Secrets (Keychain)
proxy integration key set github api_key ghp_...
proxy integration key delete github
Loadout
Per-agent equipment: model tuning, skills, and integrations.
proxy loadout list
proxy loadout create '{"agent_id":"agent-123","name":"Deep Work","provider_id":"anthropic","model_id":"claude-sonnet-4-6"}'
proxy loadout activate <LOADOUT_ID>
proxy loadout tuning <LOADOUT_ID> '{"temperature":0.7,"working_directory":"/path/to/repo"}'
proxy loadout delete <LOADOUT_ID>
proxy loadout skill list <LOADOUT_ID>
proxy loadout skill equip <LOADOUT_ID> <SKILL_ID>
proxy loadout skill unequip <LOADOUT_ID> <SKILL_ID>
proxy loadout integration list <LOADOUT_ID>
proxy loadout integration equip <LOADOUT_ID> <INTEGRATION_ID>
proxy loadout integration unequip <LOADOUT_ID> <INTEGRATION_ID>
create requires agent_id; any keys beyond agent_id and name are
applied as a tuning update after creation.
Model
Models, families, compute providers, and compute targets.
proxy model list
proxy model list --provider openrouter
proxy model list --family llama3
proxy model list --embedding
proxy model get <MODEL_ID>
proxy model resolve llama-3.3-70b
proxy model family list
proxy model family list --party-only
proxy model family set-variant <FAMILY_ID> <VARIANT_ID>
proxy model family set-slot <FAMILY_ID> 2 # omit the slot to clear it
proxy model provider list --category local
proxy model provider get <PROVIDER_ID>
proxy model provider access-modes <PROVIDER_ID>
proxy model provider set-access-mode <PROVIDER_ID> <MODE_ID>
proxy model target list
proxy model target create '{"name":"Local LM Studio","access_mode_id":"lm-studio-http"}'
proxy model target update <TARGET_ID> '{"name":"Studio (M3)"}'
proxy model target delete <TARGET_ID>
Team
Saved teams and role assignments.
proxy team list
proxy team get <TEAM_ID>
proxy team create '{"name":"Research","format_id":"swarm","mode":"parallel"}'
proxy team update <TEAM_ID> '{"mode":"sequential"}'
proxy team delete <TEAM_ID>
proxy team assign add <TEAM_ID> '{"role":"participant","agent_id":"agent-123","position":0}'
proxy team assign update-config <ASSIGNMENT_ID> '{"working_directory":"/path/to/repo"}'
proxy team assign remove <ASSIGNMENT_ID>
proxy team roles <FORMAT_ID>
Skill
Skill sources (local folders or SSH remotes) and bundled defaults.
proxy skill source list
proxy skill source add my-skills "My Skills" /path/to/skills
proxy skill source add-remote terminal-skills "Terminal Skills" proxy-terminal-1 "~/.hermes/skills"
proxy skill source sync terminal-skills
proxy skill source remove my-skills
proxy skill defaults
Config
Proxy settings and the Grand Central config document.
proxy config list
proxy config get autoUpdate
proxy config set autoUpdate true
proxy config gc get
proxy config gc update '{"snapshots":{"enabled":true}}'
config set values are parsed as JSON when valid (true, 5173, "dark")
and sent as plain strings otherwise.
System
Health, snapshots, the Orchard runtime, UI navigation, and app updates.
proxy system health
proxy system snapshot create
proxy system snapshot list
proxy system snapshot restore <SNAPSHOT_PATH>
proxy system snapshot merge <SNAPSHOT_PATH> --include-party
proxy system orchard status
proxy system orchard start
proxy system orchard stop
# Navigate the Proxy UI
proxy system navigate widget home # home, timeline, roster, actions, command, lifeMap
proxy system navigate widget roster --tab agents
proxy system navigate widget lifeMap --node-id <NODE_ID>
proxy system navigate party --session-id <SESSION_ID>
proxy system navigate attic
proxy system update check
proxy system update status
proxy system update install
snapshot merge merges a snapshot into local data without deleting local
records; --include-party also merges the party/chat tables. --tab only
applies to the roster widget; the --node-* flags only apply to lifeMap.
Screenshot
Capture Proxy's current window as a PNG.
proxy screenshot
proxy screenshot --output /tmp/proxy.png
Human format prints the file path; JSON prints the response object including
path. Without --output the capture goes to a temporary file.