Releases: 1ay1/agentty
Releases · 1ay1/agentty
Release list
v0.2.7
v0.2.6
v0.2.5
v0.2.4
v0.2.3
v0.2.2
v0.2.1
agentty v0.2.0
Added
agentty airgap <host> --acp [flags…]— one-command Zed-over-airgap setup. Running agentty inside Zed on an internet-less remote used to mean hand-assembling assh -N -R 1080tunnel plus a Zedenvblock. The new--acpform prints a ready-to-paste Zedagent_serversconfig (and the path to yoursettings.json) whosecommandissshitself — its args open the reverse SOCKS5 tunnel and exec the remoteagentty acpin a single invocation, with the ACP JSON-RPC riding ssh's stdio. One ssh process is the tunnel, the agent, and the transport; Zed owns its lifecycle, so there's nothing to babysit. Everything after--acp(e.g.-m,--profile,--workspace,--sandbox) is forwarded verbatim to the remote agent. Pair with--setupto copy credentials over first.agentty acpnow supportssession/load— resume past conversations in Zed. The ACP agent advertisesloadSession: trueand persists every session to agentty's on-disk thread store (threads_dir()/<id>.json, the same format the TUI writes) after each turn, so sessions survive a subprocess restart. Onsession/loadit restores theThread(preferring an in-memory copy when the session is still live in this subprocess, else reading from disk), replays the entire conversation — user messages asuser_message_chunk, assistant text asagent_message_chunk, and each tool call as atool_callcard with its final input/output/status — assession/updatenotifications, then resolves the request, exactly per the ACP spec. Session ids are realThreadIds, so ACP sessions also appear in the standalone TUI's thread picker (and TUI threads are loadable from Zed). Fixes the "Loading or resuming sessions is not supported by this agent" error.agentty acpACP refinements: model + permission-profile flags, file follow-along, faster cold start.-m / --modelis now an ephemeral per-subprocess override in ACP mode (it no longer clobbers the TUI's saved model), so a Zedagent_serversentry can pin a fast model (e.g.claude-haiku-4-5) without touching your interactive default. A new-p / --profile {ask|minimal|write}flag tunes which tools trigger Zed's permission prompt:ask(default — prompt write/exec/net, auto-run reads),minimal(prompt everything including reads),write(never prompt reads). Tool calls now carry ACPlocations(file path + optional line) for read/edit/write/list_dir/git_diff/diagnostics, enabling Zed's "follow-along" file highlighting. ACP mode now prewarms the TLS/DNS connection to Anthropic before serving (matching the TUI), eliminating the ~150–300 ms handshake on the first prompt, and the wire tool list is built once instead of per-completion.agentty acp— run agentty as an ACP agent inside Zed (or any Agent Client Protocol client). A new headless subcommand speaks newline-delimited JSON-RPC 2.0 over stdio and implements the full ACP v1 agent surface:initialize(capability negotiation),authenticate,session/new,session/prompt(drives a complete agent turn), andsession/cancel. While a turn runs it streamssession/updatenotifications —agent_message_chunkfor model text,tool_call/tool_call_updatefor every tool (with ACPkind,rawInput, status transitions, anddiffcontent blocks for edit/write so Zed renders changes inline) — and calls back withsession/request_permissionbefore any side-effecting tool runs (Exec/WriteFs/Net), letting Zed show its native approval UI. The headless loop reuses the exact same provider, tool registry, wire-message shaping, workspace sandbox, and permission policy as the TUI (no maya/UI dependency), so behaviour is identical to interactive agentty. Configure in Zed'ssettings.jsonunderagent_serverswith{ "command": "agentty", "args": ["acp"] }; auth comes from your existingagentty login. See README → “Use agentty inside Zed (ACP)”.
Fixed
- Per-error-class retry caps (Zed-aligned) so a flaky mid-stream wire stops spamming the retry banner. Previously every transient shared one global
kMaxRetries(6), so a connection that kept cutting out mid-body stuttered through six loudtransient — retrying (attempt N/6)…banners before giving up. Mirroring Zed's agent loop (crates/agent/src/thread.rs::retry_strategy_for), the cap is now per error shape viaprovider::max_retries_for: rate-limit / overload (429 / 529) keep the full budget (the server is shedding load and usually hands aRetry-After), a clean connect blip with no content keeps the full budget (a fresh connection almost always recovers), but a mid-stream failure — the stall watchdog fired, or the stream had already delivered a delta this turn and then died — gets only 2 attempts, because a wire that keeps dropping after reaching us is a real outage, not a reconnect artifact. The attempt counter in the banner now shows the real per-class cap (N/2mid-stream,N/6otherwise). Budget-decay and the first-delta/heartbeat budget reset still apply on top, so a stream that recovers and runs for a while resets to a fresh ladder. x-stainless-retry-countnow reflects the real attempt number. It was hard-coded to0, so every retry looked like a fresh first attempt to Anthropic's edge — which reads this header for routing and to avoid penalising retried traffic, and could land a retry back on the same overloaded pop. The per-turntransient_retriescount is now plumbed throughprovider::Request→anthropic::Request→ the header, exactly as the official SDK / Zed increment it.- Frequent
transient — retrying…banner caused by stale pooled connections. The dominant trigger for the orange retry banner was a reused h2 connection that Anthropic's edge (or an intermediate proxy) had silently half-closed: the pool's acquire-time liveness checks (nghttp2protocol state + a non-blockingMSG_PEEK) passed because the FIN/GOAWAYwas still in flight, so a corpse got handed out. The new stream submitted on it was immediatelyRST_STREAM'd /GOAWAY'd — and because the old retry gate (any_bytes, set on headers) considered a headers-only:statusblock "committed," the HTTP layer couldn't re-dial. The error bubbled to the reducer, which restarted the whole turn loudly with backoff. Two transport-layer fixes: (1) the stream-commit point is now real SSE DATA (on_chunkwith body bytes), not headers — a stream that got only:statusbefore the reset is replay-safe and re-dials transparently; (2) a reused pooled connection that dies before delivering any data gets up to 2 free fresh re-dials that don't count against the transport attempt budget, so a pool-staleness artifact never surfaces as a user-visible error. Genuine fresh-dial failures and any reset after real content still converge to a terminal error exactly as before. This is what the official Anthropic SDK / Claude Code get for free from undici's managed pool (honorGOAWAY, retry transport resets on a fresh connection); agentty now matches it. - Transient backoff that never recovered + frequent "stream stalled" after long sessions.
transient_retriesonly reset to 0 on the first content delta, so a stream that connected, sent heartbeats, then went silent before any byte (common during brown-outs and long opus turns) climbed the retry ladder every attempt until it hitkMaxRetriesand latched terminal — the session was dead until restart. Two fixes: (1) a heartbeat (SSEping/thinking_delta) now resets the retry budget too, since it proves the wire is alive even pre-content; (2) the budget decays over wall-clock time — if the previous failure was longer ago thankRetryDecayWindow(90 s) the connection was healthy in between, so the new failure starts a fresh ladder instead of inheriting an unrelated earlier blip. Net effect: fast-failing connections (refused/reset within 90 s) still converge to terminal at attempt 6, but slow stalls minutes apart recover indefinitely.Escstill breaks the loop at any point.