DEV Community

Cover image for agentproto 0.6 + 0.7 — pair two machines through a broker that can't read your traffic
Agentik
Agentik

Posted on

agentproto 0.6 + 0.7 — pair two machines through a broker that can't read your traffic

0.6.0 released 2026-07-16, 0.7.0 on 2026-07-17 · npm i -g @agentproto/cli · source · Apache-2.0

Your agent daemon runs on the machine with your credentials, your repo, and your
shell. Sooner or later you want to reach it from somewhere else — a laptop, a
phone, a CI box. That means a relay in the middle. And a relay in the middle is
a thing you have to trust.

0.6 is mostly about not having to.

Two breaking changes in here, so read the migration note at the bottom before
you upgrade.

Pairing: the broker is deliberately stupid

agentproto pair offer mints a one-time token and prints an agentproto://pair?…
URL. On the other machine:

# On the daemon machine
agentproto pair offer
# → agentproto://pair?t=<token>&rv=<url>&fp=<fingerprint>

# On the client machine
agentproto pair accept "agentproto://pair?..."
Enter fullscreen mode Exit fullscreen mode

That triggers a pair/v1 ECDH handshake over the rendezvous broker, after which
every byte is wrapped in an AEAD channel. The interesting part is what the broker
is allowed to be:

The rendezvous is a dumb WebSocket splicer. It matches two sockets on the same
one-time token, pipes bytes verbatim both ways, and parses nothing.

It learns the token, the IPs, message sizes, and timing. It cannot read content
and it cannot forge it. That's not a promise about our operational discipline —
it's a property of the protocol. The distinction matters, because "trust us with
your plaintext, we're careful" is exactly the claim nobody should accept, ours
included.

After first pairing, the daemon auto-reconnects using day-scoped epoch routing
tokens
derived from a shared pairRoot secret — HKDF(pairRoot, "rv-route"‖epoch).
It parks on both the current and previous epoch, so a client whose clock straddles
midnight still finds it. (A small thing that would otherwise be a mystifying
once-a-day bug.)

Revoke with agentproto pair rm <fingerprint> — the daemon stops parking on
those tokens immediately.

Don't want our broker? Self-host it. A Dockerfile ships with the release —
non-root, RENDEZVOUS_* env config, /healthz probe. Offers default to the
hosted rendezvous when nothing else is configured, and pair offer says so
explicitly
rather than defaulting silently.

The same treatment for serve --connect

Pairing isn't the only place you hand bytes to a middleman. agentproto serve
--connect
pushes your daemon out through a tunnel, and that tunnel host has
historically seen plaintext. 0.6 adds an opt-in E2E handshake — tunnel-e2e/v1
so it doesn't have to:

{ "tunnel": { "e2e": true } }
Enter fullscreen mode Exit fullscreen mode

It's token-authenticated rather than PKI-based: the same shared secret that
authenticates the tunnel also keys the channel, which is why there's no
certificate story to manage. It's opt-in on purpose, and the daemon refuses to
silently fall back to plaintext against a host that doesn't advertise e2e — if you
asked for encryption and can't have it, you get an error, not a quiet downgrade.
That's the whole reason to have the flag: a security property you can't verify
isn't one.

Breaking: Claude Code spawns now demand explicit credentials

@agentproto/adapter-claude-code@1.0.0 and @agentproto/driver-agent-cli@1.0.0
are major bumps, and this is why.

Spawning a Claude Code session used to inherit whatever the daemon's launching
shell happened to export. Which means a stray ANTHROPIC_API_KEY in your
environment could silently bill your org's API credits when you thought you were
on your Max plan — and you'd find out on the invoice.

Now the runtime resolves one of two modes at spawn time and applies it
mechanically:

mode sets scrubs
subscription CLAUDE_CODE_OAUTH_TOKEN (via claude setup-token) — bills your Max/Pro plan ANTHROPIC_AUTH_TOKEN, and the Bedrock/Vertex redirect toggles
api-key ANTHROPIC_API_KEY from the provider store the subscription credential

Scrubbing CLAUDE_CODE_USE_BEDROCK / CLAUDE_CODE_USE_VERTEX in subscription
mode is the quiet hero: an ambient env var could otherwise reroute a native spawn
to Bedrock without a word.

Configure once:

{
  "defaults": {
    "adapters": {
      "claude-code": {
        "auth": { "mode": "subscription", "token": "<bearer-token>" }
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Or override per-spawn via agent_start.auth. The same resolver now backs
adapter-codex, adapter-claude-sdk, and adapter-hermesevery adapter
uses catalog-sourced billing credentials instead of ambient env inference.

Keys you set with agentproto providers set <slug> <key> get injected into the
child process at spawn time, never into your shell. The resolver looks up the
adapter's declared provider in the catalog, maps it to an env var name
(providerEnvVar("anthropic")ANTHROPIC_API_KEY), and sets it there and only
there.

Worktrees grew a lifecycle — and a gc that learned restraint the hard way

If you run agents in parallel you have too many worktrees. I certainly do.

agentproto worktree new              # provisions under worktrees.root + a provenance marker
agentproto worktree ls --status      # classify everything
agentproto worktree gc               # dry-run plan
agentproto worktree gc --apply       # execute it
Enter fullscreen mode Exit fullscreen mode

ls --status classifies each worktree on three independent axes: tree
(clean/dirty), integration (eleven states — fresh, merged, open,
partial, pushed-no-pr, unpushed, local-only, gone-unexplained,
diverged, detached, unknown), and liveness (idle, sessions,
daemon-unreachable). The integration axis is squash-merge-proof — it checks SHA
containment against origin/main and caches immutable forge verdicts (a merged PR
stays merged), so it stays honest offline without needing a registry. It never
guesses: a cache miss while offline is unknown, full stop.

The gc ate a live worktree on July 15th

I'd rather tell you this than have you find it.

The first cut of gc had a rule that sounded safe: dirty worktrees get
salvaged (snapshotted to ~/.agentproto/worktree-salvage/) rather than
reclaimed, so nothing uncommitted is ever simply deleted. On 2026-07-15 that rule
destroyed a live worktree out from under a running agent. The mechanism: a branch
with zero commits of its own classified as fresh, fresh was treated as
license to salvage-then-remove, and the agent's uncommitted work went with it.
The snapshot existed — but the worktree the agent was actively writing to did not.

The bug wasn't the salvage step. It was letting an axis prove less than the code
assumed it proved.
fresh cannot distinguish "never had commits" from "real
commits, fast-forwarded into main." Both are consistent with fresh, and only one
of them is safe to destroy.

The rule in 0.6 is narrower, and the narrowness is the point:

  • Only merged — always forge-confirmed — ever licenses destroying dirty, uncommitted work. fresh never does, clean or not.
  • A merged ∧ dirty worktree written to recently holds anyway, inside a recent-write window. Someone is probably still in there.
  • Reclaim (clean trees only) needs tree=clean ∧ integration ∈ {merged, fresh} ∧ liveness ∈ {idle, daemon-unreachable}. fresh is fine here precisely because a clean tree has nothing to lose.
  • gc re-classifies every worktree from scratch immediately before touching it. A plan you printed ten minutes ago cannot cause a deletion now.

The hard guarantee against losing work was never the liveness probe — it's git's
own refusal to remove a dirty worktree without --force. The probe is an
optimization on top. We'd inverted that, and it cost a worktree.

worktree rm / archive replace the old blanket --force with explicit
--discard-untracked / --discard-modified. "Force" was one flag meaning two
very different kinds of destruction; now you have to name which one you meant.

Your agent stopped guessing about permission

When an agent pauses mid-turn to ask permission — Claude Code's
requestPermission before a shell command — the daemon now parks it in a durable
inbox instead of timing out or auto-approving.

agentproto permissions ls              # pending holds across all sessions
agentproto permissions approve <id>
agentproto permissions deny <id>
Enter fullscreen mode Exit fullscreen mode

Same three operations exist as MCP tools, so a supervising agent can answer
programmatically. SessionDescriptor gains blockedOn: "subagent" | "command" | null,
so a UI can finally say why a session looks stuck instead of showing a spinner.
(The case for gating writes rather than blanket-approving: --approve-all is how your agent ships the dangerous one.)

Also in the box

  • xAI / Grok is a first-class preset — grok-4.5, grok-4.3, grok-4.20, grok-build-0.1 registered in the catalog:
  agentproto providers set xai $XAI_API_KEY
  agentproto run --adapter claude-sdk --mode xai --model grok-4.5 "explain this codebase"
Enter fullscreen mode Exit fullscreen mode
  • Route-aware model identity — the catalog now separates a model's product identity from its serving route: openai/gpt-4o vs openai/gpt-4o@openrouter. Bare legacy ids still work. Custom routes register via registerCustomRoute — and only the env var name lives in config, never the value.
  • Fault-tolerant fan-outMapStep/PipelineStep take onError: "collect", so every item completes and you get a Promise.allSettled-shaped result instead of losing the batch to the first throw.
  • agentproto policy — completion policies are finally a CLI verb: attach / status / wait / ls / cancel. The engine existed for a while; it had no command, which meant in practice nobody attached one.
  • agentproto acp — any ACP-speaking agent, not just our adapter family, can be registered in config.json and driven directly.
  • SSE with replayGET /sessions/:id/events/stream replays stored events then hands off to live delivery. Reconnect mid-session and you miss nothing.
  • agentproto.json — declare setup/teardown hooks, supervised services, and a localhost reverse proxy per worktree.
  • agentproto run --output-schema — validate a run's output against a JSON Schema and fail the command if it doesn't conform. For CI that eats agent output.
  • agentproto sessions story <id> — a readable narrative of what a session actually did, collapsing noisy low-level events into steps.
  • agentproto run --model / --effort — pick the model and reasoning effort per run, with a friendly error on unknown flags instead of a stack trace.
  • daemon.authToken + --auth-token — a persistent gateway bearer token, so the daemon isn't re-authenticating from scratch every restart.
  • terminalPresets in config.json — name a terminal setup once, reuse it.
  • @agentproto/adapter-pi — an AIP-45 proprietary-arm adapter for earendil-works/pi. The adapter family keeps growing without the core knowing.
  • claude-opus-4-8, claude-sonnet-5, claude-fable-5 registered in the pricing catalog — and runnable is now decoupled from priced, so a model missing pricing data still runs (it reports no-pricing, it doesn't guess).
  • Apache-2.0. Every package relicensed from MIT — 92 packages, zero MIT left. No API changes.

The VS Code extension stopped lying to you

The bigger story across these two releases isn't in the CLI at all. The
VS Code extension
went 0.0.10.1.0 (in 0.6) → 0.1.1 (in 0.7), which is the arc from
"technically exists" to "the thing I actually watch my agents in."

0.1.0 gave it a spine: structured conversation rendering instead of a wall of
raw events, a sessions tree with filters/search/grouping, workspace autodetect on
spawn, session restart over MCP, and agentproto.openTerminal — a real
Pseudoterminal mirror for PTY and agent-cli sessions, so you can watch a session
in an actual terminal rather than a simulation of one.

0.1.1 is where it stopped misrepresenting state, which is the part I care
about:

  • Stop was painted as a crash. You'd stop a session deliberately and the UI would show you the same thing it shows when an agent dies. Tree icons and the status bar now run on an activity axis rather than a lifecycle one — a stopped session reads as stopped.
  • A sub-agent reaped after finishing read as "stopped" instead of "complete." It had done its job. The UI said otherwise.
  • The sidebar auto-refreshes on a clock, unread sessions carry a read-receipt dot, and a spawn shows up as an optimistic row instead of appearing several seconds late.

Both of those first two are the same bug in different clothes, and it's the bug
this whole project exists to fight: the interface asserting something about your
agents that isn't true.
A panel that calls a clean stop a crash trains you to
ignore it, and an ignored panel is worse than no panel — you've paid for the pixels
and gained nothing.

The composer also grew up in this window: a stop button, prompt history on ↑/↓,
drag-and-drop, @file mentions, attachment chips, and paste-an-image-into-the-
transcript. (Fair warning, since I'd rather you hear it here: several of those
landed without changesets, so they're in the shipped extension but missing from its
changelog. The tooling that's supposed to catch that didn't.)

0.7.0, a day later: the polish pass

Beyond the extension, 0.7 is exactly what a release should look like when the
previous one was big: fixes to the things 0.6 got slightly wrong.

  • Adapters no longer report themselves uninstalled while rebuilding. A last-known-good fallback covers the window where a package is mid-build — the old behaviour made a perfectly healthy adapter vanish from the catalog for a few seconds, which is a wonderful way to lose an afternoon.
  • models.allowed takes structured entries, fixing gateway model-mode binding in the VS Code picker.
  • Sessions record worktreePath / worktreeId at spawn time — so the session and the worktree it lives in are joinable without inference.
  • policy ls --session <id> (and the matching policy_list / GET /policies filter) — the policy list stops being unreadable once you have more than a handful.
  • --prompt strings get wrapped into a ContentBlock before session.send().
  • All CLI timeout/interval flags validate and echo their resolved duration, so --until 15m tells you what it parsed rather than silently meaning something else.
  • node-pty's spawn-helper exec bit is fixed, with PTY spawn errors that actually say what failed.
  • Test gateways stopped writing fake session rows into the real ~/.agentproto/. Our own test suite was polluting real user state. Worth naming, because it's the same class of bug as the gc one: something that only looks isolated.

Plus docs backfill for 0.6's policy / worktree verbs, config keys, and
sessions story — which had shipped without their pages.

Migrating from 0.5 to 0.7

adapter-claude-code and driver-agent-cli went to 1.0.0. If you relied on
Claude Code spawns inheriting credentials from the daemon's shell, they no longer
do. Declare the mode:

{
  "defaults": {
    "adapters": {
      "claude-code": { "auth": { "mode": "subscription", "token": "<bearer-token>" } }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Use mode: "api-key" to bill an API key from the provider store instead. This is
a change you want: the old behaviour is how you accidentally bill org credits
for a session you thought was on your subscription.

npm i -g @agentproto/cli
Enter fullscreen mode Exit fullscreen mode

Full notes: the July 2026 release.

Go deeper

What shipped in 0.6 / 0.7 The thinking behind it
Permission inbox, explicit credential modes --approve-all is how your agent ships the dangerous one (hands-on)
agentproto policy, completion policies Kill the loop: why while true is not reliability
Route-aware model identity, xAI preset Cheaper per token is not cheaper per outcome
agentproto.json, lifecycle hooks Your Claude skill is invisible to Codex (hands-on)
Session story, SSE replay, the VS Code extension Your agent says the tests passed. It didn't run them.
Why any of this matters at 5 agents You can parallelize the typing. You can't parallelize the trust.

Previously: 0.5.0 — credentials, sandboxes, and cost accounting that refuses to lie · 0.4.0 — the daemon grows up into a supervision surface


agentproto is an open-source (Apache-2.0) orchestration layer that runs on top
of your existing stack — it doesn't replace your coding agent, it supervises it.
File an issue.

Building agentproto in the open — follow @theagentproto and @agentik_ai on X.

Top comments (0)