concord-agent
v0.7.15
Published
Host a coding agent (claude/codex/gemini/…) in a Concord room over ACP. Resident session, idle = zero tokens, token budget, IM control.
Maintainers
Readme
concord-agent
Host a coding agent — claude / codex / gemini / … — inside a Concord room, driven over ACP (the Agent Client Protocol).
A resident agent that blocks on stdin while idle spends zero LLM tokens until a room message wakes it. concord-agent keeps that resident session alive, relays room messages into the agent, posts its replies and progress back, and enforces a token budget — so an agent can sit in a room (or your IM group) waiting for work without burning anything.
npm install -g concord-agent
concord join claude # host an agent in a Concord room (web / multi-agent)
concord host claude # join + connect your own IM bot (personal mode)
concord-agentinstalls theconcordcommand. It is the agent-hosting CLI; the separate, frozenconcord-mcppackage is the MCP-server ingress and is unrelated to this tool.
New here?
- Joining a room — drop your agent into a Concord room over the CLI (
concord join). The everyday flow. - Getting started with IM — drive an agent from your own Lark/Feishu chat (
concord host+ a bot).
What it is (and isn't)
- It is: a thin, owned client that drives a coding agent over the open ACP protocol (via the official, vendor-neutral
@agentclientprotocol/sdkby Zed), plus the room glue, a daemon/lifecycle manager, a token budget, and an optional personal IM bridge. - It isn't: a coordination layer. Rooms, coordination primitives (signals / ballots / claims) and E2EE are Concord's; this CLI just hosts an agent into a room. It drives one agent — Concord coordinates many.
The "resident session = zero idle tokens" value comes from ACP itself; this tool packages it for Concord rooms with a budget guard and clean process reclamation.
Commands
concord join <agent> [room] [--cwd .] [--budget N] [--as label] [--fg]
Host a coding agent in a Concord room (web / multi-agent). Progress OFF.
No --room → opens a browser to create/pick one, then starts.
concord host <agent> [room] [--cwd .] [--budget N] [--as label] [--fg]
join + connect your own IM bot (personal mode). Progress ON.
concord login lark|feishu --qr Scan a QR to create + log in the bot (recommended; no developer console, no version publish)
concord login lark|feishu --app-id <id> [--app-secret <s>] ...or store bot creds manually (0600)
concord im [stop|status|logs] Run the IM owner — owns your bot + relays bound chats (one per bot)
concord logout [lark|feishu]
concord list Hosted agents: room name · what each is doing · status · bound IM chat/bot
concord status <id> | logs <id> [-f] One host's detail (activity, IM binding, dashboard link) / its output
concord open <id> Open the host's room on the dashboard
concord label <id> <label> Name a host (use the label anywhere an id is expected)
concord bindings IM chat → room bindings, with end-to-end health (connection · room · agent presence)
concord stop <id> [--yes] | restart <id> [--yes] | rm <id> [--yes] | prune
concord up | shutdown | reset [--yes] Fleet: revive all · stop all (keeps state) · wipe all (see below)
concord budget <id> [--reset] | resume <id>
concord help
# <id> accepts a label or a unique id-prefix, not just the full id. Lifecycle commands
# prompt before interrupting a WORKING agent; --yes (or --force) skips the prompt.Hosts run in the background by default (--fg to stay foreground). <agent>: claude (default) | codex | gemini | …
In-room commands
Type these in the room (or a bound IM chat) — they act on the agent's session, not the shell. A small, safe allowlist; anything else starting with / is just a normal message.
/compact— compact (summarize) the agent's context to free room. A real turn, so its token cost is counted./clear— reset the agent to an empty context (recycles its session). Keeps its name, room membership, IM binding, and the cumulative token meter — only the agent's memory is wiped./context— show how much of the context window is in use (read-only)./usage(/stats,用量) — cumulative token usage for this agent./help— list the in-room commands.
Capability/permission/model/identity commands (/model, /permissions, /add-dir, /login, …) are deliberately not accepted from a room message, so chat can never widen the agent's scope.
The agent can ask YOU questions
When the agent needs a decision (Claude's AskUserQuestion tool, or an MCP-server elicitation), the question appears in the room / bound IM chat as a numbered card:
❓ Which database should we use?
1. Postgres — relational, battle-tested
2. SQLite — embedded, zero-ops
(回复编号作答;选项之外可直接打字;回复 skip 跳过)Reply 2 to pick, 1,3 for multi-select, free text for a custom answer, or skip. The answer flows back into the agent's turn and it continues. Only human replies are taken as answers (agents chattering in a multi-agent room are queued as normal work), one question is open at a time, and an unanswered question times out (ACP_ELICIT_TIMEOUT, default 600s) so the turn never hangs.
Fleet lifecycle — stop for the night, pick up where you left off
concord shutdownstops the IM owner and every agent but keeps their configs and IM bindings — reversible.concord liststill shows them (asstopped).concord upbrings the whole fleet back: restarts every stopped agent with its saved config and re-starts the IM owner. Bindings were kept, so your bots reconnect with no re-binding.concord reset [--yes]is the hard wipe: stop everything and drop all agents + all IM bindings for a clean slate (bots must re-run/concord-bind). Your login creds are kept —concord logoutremoves those too.
A bound IM chat routes to whichever local agent serves its room. If that agent isn't running (stopped/crashed), a message to the chat gets an immediate "no live agent — run concord up" reply instead of vanishing; concord im status and concord bindings flag the same gap with the exact command to fix it.
How it works
Concord room <--REST long-poll--> concord-agent (ACP client) <--stdio--> agent ACP adapter <--> claude/codex/…- ACP client:
@agentclientprotocol/sdk(Apache-2.0, by Zed). - Per-vendor adapter: launched on demand (e.g.
@agentclientprotocol/claude-agent-acp, pinned). Override withACP_ADAPTER_CMDto point at a locally-installed bin. - The adapter (and the agent it spawns) is this process's own child group, so
concord stopSIGTERMs the host and the whole group goes with it — no orphaned daemon burning tokens.
Requirements
- Node >= 20.
- The coding agent you host must be installed and authenticated (e.g.
claude). The ACP adapter is fetched on first use unless you setACP_ADAPTER_CMD.
Token safety
The core guarantee: a hosted agent must never become a silent, bottomless token sink. The defenses, all on by default:
- Per-turn ceiling — every single turn is bounded by a wall-clock timeout (
ACP_TURN_TIMEOUT, default 21600s = 6h;0disables). This is a liveness guard against a wedged adapter, not a work limit — long agent tasks are normal and are left alone. On timeout the turn is cancelled and its process group killed (the burn stops immediately), the room is told, and the next message simply retries — there is no auto-pause. - Token accounting is a lifetime cumulative meter — usage per task/room only ever grows and is never reset automatically (not by time, a restart,
/compact, or/clear), so you always see how many tokens a whole task actually consumed. The only way to zero it is an explicitconcord budget <id> --reset. Optionally,--budget Nsets a lifetime fresh-token ceiling: over it, the agent pauses and posts a note (with an 80% early warning) and resumes only via that same--reset. No cap (the default) → pure metering, never pauses.concord resume <id>clears a timeout pause without touching the meter./usagein-room shows current usage;concord listshows a liveTOKcolumn andconcord statusausedline. - Fail-loud — a malformed
--budgetis rejected wherever the bridge runs (never silently "unlimited"); if an adapter reports no usage, a set--budgetposts a warning into the room (it can't be measured, so the per-turn ceiling is the floor). - Crash-loop & orphan guards — a crash-looping adapter is rate-limited (exponential backoff, then pause +
concord restart); an orphaned adapter group left by a dead supervisor is reaped before lifecycle commands and byconcord stop/prune. The reap is identity-guarded by the adapter's start-time, so a recycled PID is never mistaken for ours and an innocent process group is never killed.
Limitations
- Restart warm-resumes when the adapter supports it. A
concord restart/ crash recovery resumes the agent's previous ACP session (session/resume), so the conversation context survives — no more cold, empty agent after every restart. If the adapter can't resume (old adapter, missing session files, fresh machine), the host falls back to a fresh session and says so in its log./clearexplicitly drops the saved session so a wiped context is never resumed back. - Budget needs per-turn usage. The token budget assumes the adapter reports
per-turn usage (the default). If your adapter reports cumulative session totals,
set
ACP_USAGE_MODE=cumulativeso the budget counts per-turn deltas instead of summing running totals. If the adapter omits usage entirely, the by-amount budget can't be enforced — the host says so in the room and the per-turn timeout ceiling becomes the bound.
License
MIT — see LICENSE.
