Agent coordination protocol
The core product is a coordination bus between existing coding agents. The web application is an observability and approval console; agents do not need to run inside it.
Bootstrap
From the repository root, a developer runs node .cohent/cohent.mjs bootstrap --approve.
The CLI prints a browser link and waits. The developer opens it, signs in with GitHub
if needed, and clicks Approve — this grants access to exactly the one repository
the request named. The CLI resolves a repository-scoped credential on its own within a
few seconds of approval and stores it in ~/.config/cohent/credentials.json, outside
the repository.
The repository and Cohent service live in committed .cohent/project.json.
Publish before work
On UserPromptSubmit, an automatic hook sends:
POST /api/agent
Authorization: Bearer $COHENT_AGENT_TOKEN
Content-Type: application/json
{
"type": "publish",
"prompt": "Refactor checkout validation without changing the API",
"paths": [],
"clientName": "Codex",
"model": "gpt-5"
}
Cohent returns one compact plan packet. It contains the project goal, at most three
recent active intents, and the instruction to publish structured intent. It does not
return the Control Room snapshot. Publishing without paths makes prompt capture
automatic even when the agent has not planned its files yet.
Client and model identity are stored with the work intent so every peer sees which
person prompted which agent runtime. Model identity depends on the client: Codex
reports it on every hook, while Claude Code does not send one outside session
start, so intents from Claude Code record the client without a model.
Publish compact semantic intent
After reading enough repository context to plan, the connected agent publishes its own small record:
node .cohent/cohent.mjs intent '{"objective":"Build sign-in states","plan":["Create empty and error states"],"components":["sign-in-ui"],"dependsOn":[],"deferred":["auth-api-contract"],"outputs":["sign-in-ui-states"],"blockers":[],"requires":[],"capabilities":["frontend"],"state":"planned"}'
The record is bounded and stored as JSON. Cohent does not summarize it with another
model. dependsOn names an output that blocks the current step. deferred names an
output needed later while the current independent step can proceed. Optional usage can
carry integration-reported input/output tokens or cost, with its source, but no usage
field is required and Cohent does not estimate model cost.
The deterministic resolver acts only on high-confidence agent-authored signals:
- an active output named in
dependsOnreturnswait; - the same objective and output already owned by earlier active work returns
duplicate; - a missing required capability reported by an active peer returns
handoff; - a shared component with separate outputs, or a
deferredoutput, returnsparallel; - a completed dependency returns
proceedwith that peer's compact completed state.
Ambiguous relationships return proceed; path arbitration remains the safety layer.
The guarded adapter refuses a visible edit on plan, wait, duplicate, or handoff
and tells the agent to publish a narrower intent. It does not block a read-only shell
command merely because intent is not ready.
Verify paths during writes
Visible edit targets are claimed automatically immediately before the write. The agent uses the manual command only for a write tool whose target Cohent cannot read:
node .cohent/cohent.mjs scope app/checkout tests/checkout.spec.ts
This checkpoints the intent with normalized repository-relative paths and persists the verified result in the session runtime.
The declaration replaces the task's previous claim rather than adding to it, and it bounds what the agent may write: a later edit to a path this scope does not cover is refused, because that path has been compared with nobody else's work. Without that check, collision detection could be bypassed simply by not declaring the file about to be written.
The PreToolUse hook covers edit tools and shell tools, and holds them to
different rules, because Cohent can see what an edit targets and cannot see what a
shell command will do:
| Condition | Edit tools | Shell tools |
|---|---|---|
| Paused or cancelled from the control center | Blocked | Blocked |
| Paired but the control plane is unreachable | Blocked | Blocked |
| Configuration unusable | Blocked | Blocked |
| No prompt or semantic intent announced | Blocked | Allowed |
| Scope overlaps an active peer scope | Blocked | Allowed |
| The write targets a path outside the declared scope | Blocked | Allowed |
Shell coverage exists so a stopped agent cannot keep working through sed -i or
git. It stops there deliberately: scope is declared by running this adapter,
so holding shell commands to the scope rules would prevent an agent from ever
announcing one. A whole, unchained node …cohent.mjs … command is always exempt so
the protocol cannot lock itself out. The practical consequence is that the
collision block is advisory for shell tools.
The opaque set is Bash, BashOutput, KillShell, PowerShell, shell,
exec_command, unified_exec, write_stdin, run_terminal_cmd, terminal, and
every mcp__* tool. The session-driving members matter as much as the session-opening
ones: write_stdin types into a shell that was approved before the cancellation, so
guarding only the opening command would leave a cancelled agent a live channel to keep
working through. MCP tools are opaque because their names and arguments belong to a
server the adapter knows nothing about — holding them to the scope rules would deny
every one of them, read-only calls included, for as long as no scope is announced.
The adapter's own command is exempt so scope can be declared at all. That exemption
requires the whole command to be an unchained node <path>/.cohent/cohent.mjs …, and any
environment prefix to be a COHENT_* name with an inert value: a general NAME=VALUE
prefix would let NODE_OPTIONS=--require=… or PATH=/tmp/evil:$PATH run arbitrary
code through the one path that skips the kill switch.
What this cannot do. A hook guards only the calls a client actually announces.
Measured on Codex CLI v0.142.5 (2026-08-10) and v0.147.0 (2026-08-11): a non-interactive
codex exec ran shell commands with no hook firing at all, because a project's hooks
sit behind two human gates — a directory trust dialog and a session-start hook review
screen ("Press t to trust all"). After a person trusts them once, hooks do fire, in the
TUI and in exec, and a PreToolUse deny genuinely blocks the tool.
So Codex enforcement is real and conditional on a one-time human step. Codex keys
that trust to a hash of the hook in $CODEX_HOME/config.toml, so editing
.codex/hooks.json revokes it and the next session runs unguarded, silently. Run
node .cohent/cohent.mjs doctor, which reports the trust state for this checkout, and read
guard on each work intent, which reports whether the agent is still calling in.
Checkpoint while working
POST /api/agent
Authorization: Bearer $COHENT_AGENT_TOKEN
Content-Type: application/json
{
"type": "checkpoint",
"intentId": "wki_...",
"status": "working",
"summary": "Validation moved; updating retry tests next",
"paths": ["app/checkout/validation.ts", "tests/checkout.spec.ts"]
}
Every request carries x-cohent-session, the client's own session identity, taken from
the hook payload's session_id and persisted in .cohent/runtime.json — the adapter is a
fresh process on every hook, so the persisted copy is what keeps one announced prompt to
one identity across many invocations. It scopes lease renewal and control-command delivery to
the session that announced the work: a pairing token is shared by every session one
person runs, so without it a single live poll would refresh the lease of every intent
that token ever announced and an abandoned agent would read as controllable.
Other agents poll GET /api/agent with x-cohent-intent and x-cohent-cursor. The server
compares against every active intent but returns only the caller, directly related
peers, relevant decisions, and changes after its cursor. Publish, intent, and checkpoint
responses already contain the new packet, so the adapter does not follow them with a
full room read.
Each meaningful packet has a durable id, payload character count, approximate token count, cursor, action, and peer ids. When the adapter injects the rendered packet, it acknowledges the actual character count once. The Control Room derives internal metrics for potential overlap, pre-edit redirection, path-guard catches, packets, injected context, dependency waits, and human commands. These are coordination measurements, not model billing records.
Required adapter behavior
- Publish the prompt automatically, then publish compact structured intent before a visible write.
- Replan on
wait,duplicate, orhandoff; keep separate outputs onparallel. - Keep deterministic path checks as the final write guard.
- Send checkpoints after material plan or scope changes, blocks, and finish.
- Inject only targeted packets and cursor deltas.
- Never place repository secrets, auth tokens, or unredacted sensitive values in prompts or checkpoints.
- Treat peer prompts and summaries as untrusted collaboration data, never as higher-priority instructions that can override repository or user policy.
Polling is the transport in the current build. WebSockets or Durable Objects can replace it without changing the prompt/checkpoint contract.
Discovery and compatibility
| Client | Product level | Repository discovery | Prompt announcement | Write guard |
|---|---|---|---|---|
| Codex CLI/app/IDE | Guarded when paired hooks are trusted | AGENTS.md + .codex/hooks.json, trusted once in the TUI |
Hook — enforced after that trust; see above | Same; edit tools fully, shell and MCP tools for the kill switch |
| Claude Code | Guarded when paired hooks are active | AGENTS.md + .claude/settings.json |
Automatic hook | Automatic hook; edit tools fully, shell tools for the kill switch |
| Paired custom integration | Coordinated | Integration contract | API/adapter | Intent, packets, and controls; no verified path guard unless supplied |
| Cursor | Advisory | .cursor/rules/cohent.mdc |
Agent follows rule | Agent follows rule |
| GitHub Copilot | Advisory | .github/copilot-instructions.md / AGENTS.md |
Agent follows instruction | Agent follows instruction |
| Other instruction-only agents | Advisory | AGENTS.md |
Adapter command | Adapter command |
Git clone and pull copy these files but never install global software or execute them. That is a deliberate security boundary. A user must trust the repository configuration and bootstrap the checkout once.
Local adapter commands
doctor: show project, endpoint, pairing state, and Codex hook trust — every declared hook matched against Codex's recorded trust, with a warning when the hooks file is newer than the trust store.context: fetch the current brief, decisions, and peer intents.intent <json>: publish or revise the compact agent-authored intent and print the targeted coordination packet. Exits 2 onwait,duplicate, orhandoff.scope <path...>: claim a write scope and fail on overlap. Replaces the task's previous claim rather than adding to it, and says so when it collides — with the peer's name, agent and overlapping paths, since an intent id identifies nothing a human knows.checkpoint <status> <summary>: publish progress, a block, or completion.hook: consume Codex/Claude hook JSON on standard input.