🛠️ agents-go v0.3.0 — reshaped in Go, grown into a workbench
v0.2.0 declared independence; v0.3.0 spends it. 153 commits since v0.2.0; 598 files changed, +52k/−19k lines. Three arcs run through this release: the SDK's public surface is reshaped around Go rather than around its Python ancestry (the batched breaking changes below are the price, paid once), Anthropic becomes a first-class second backend behind the same canonical Responses format, and agents-server grows from a demo into a workbench — providers, deterministic workflows, plan mode, and a context panel that shows where your window actually went.
Breaking changes
Batched per spec §5.8 — public API compatibility begins at v1.0.0; old spelling beside new.
- A tool is a struct. The
Toolinterface andFunctionToolcollapse into one concreteToolstruct — there is nothing left for a hosted tool to implement, which is the point. Optional capabilities remain side interfaces discovered withToolAs. - An item is a struct. The
RunIteminterface collapses into one struct with aKind. - Stored history moves to
agents/session—Storage,Session,Projector, entries and their value types live there now (aliased inagents); the JSONL store is renamedfilesession(its old package name,memory, was its opposite). mcpis its own module — the go-sdk dependency leaves the core; the import path is unchanged.- Handoffs declare their graph statically —
Handoff.Targetreplaces runtime discovery. - The naming batch aligns the model surface (
Respond/StreamResponse,InputItem, …) and drops zero-consumer exports across the SDK. InstructionsandPromptProviderare func types;compaction.PreserveSystem *boolbecomesDropSystem bool; errors carry one classification and a failed run still carries itsRunResult.tasks.Storeshrinks: the notify state machine (ConsumeNotify,MarkNotifyDelivered,ListPendingNotify,PendingNotifyParents),Config.GuardandNotifyFormatterare gone — the Manager reports endings (OnFinished/OnResultDelivered) and delivery is the host's, with the durable-host obligations recorded in the spec.- agents-server: credentials move off agent configs into first-class provider rows; agents and routes reference a
provider_id.
Highlights
A second backend, one canonical format
models/anthropic— the Messages API as a fullModelimplementation, translated inside the adapter to the same canonical Responses format everything else speaks (spec §5.10). Extended thinking, tool use, prompt caching.models/modelkit— the dependency-free adapter toolkit, plusmodelkit/conformancetest: a golden matrix everyModelimplementation must pass, so a third backend starts from a test suite instead of a guess.- ChatGPT (Codex) subscriptions served through a stream-only
Modeldecorator — blocking calls ride a stream underneath.
agents-server becomes a workbench
- Providers — one masked credential surface; ChatGPT OAuth login lives per provider; prefix routes and fallbacks resolve through the same rows.
- Workflows — fixed step sequences (plan → exec → verify, each step its own agent and model) running on a hidden child session, advanced by compare-and-set from run teardown, with
on_success/on_failureedges, retry-from-the-failed-step, and dismissal. Started only by the agent (start_workflow) — the brief has to be written by something that read the conversation. - One wake-up debt — tasks and workflows share a
wakeupstable; the debt lands in the same transaction as each terminal state, so a crash can never leave finished work whose conversation is never told. - Plan mode — a session-level restraint the person toggles with the message; gated tools stay visible and refuse while planning (backed by the new
Tool.ReadOnly), and the refusal outranks approval. - The Context panel — one window bar with the compaction threshold as a tick on it, the window's composition as honest same-ruler shares (prompt layers, tool schemas, the conversation), per-call growth, cache hits, and a Compact-now button.
- Sandboxes as facts — a session's sandbox binding is a stored, versioned fact bound by CAS at run start; persistent
exec_commandshells on terminal-capable backends; a real web terminal.
Background work grows up
- Task retry-in-place —
failed → workingis a store-level compare-and-set with the attempt ceiling enforced by the database; every attempt-scoped write names its attempt. - Replay, traced — the replay dialog reruns a call with schema/handoffs fidelity, SSE streaming, diffs and attempts.
Hardening, everywhere
- Severed model streams recover mid-turn; truncation classifies retryably.
- MCP shared connections survive one run's cancellation and redial themselves from the config that made them.
- Injected input is transactional against the turn that holds it; overflow recovery accounts for every item, on-chain or off.
- The session lifecycle, run-state resume window (spec §5.18), and the task state machine each took a dedicated audit-and-fix pass — the Fixes list below is that work.
Changelog
Fixes
- 6819ef8 fix(agents): commit injections only against a home that holds them; seed resume backlog eagerly
- cd0d846 fix(agents): injection transaction, usage snapshots, RunState wire completeness, abandonment cancellation
- cc841c3 fix(agents,models): recover severed streams — hold back pre-commit events, classify truncation retryably
- 55cd2d2 fix(agents-server): render a failed run's partial text as prose, not a chip
- 664f925 fix(compaction): summary prompt asks for plain prose over a transcript
- eab897f fix(handoff): validate the whole input schema, not root-level required
- e6ca8ed fix(mcp): a request on the shared connection never carries a caller's cancellation
- b80785a fix(mcp): pass multi-block tool results through as native content parts
- 94ea481 fix(mcp,toolchain): boundary fixes across mcp, tracing, tasks and the dev tools
- d5c0623 fix(models,session): record a committed fallback break, and align InMemoryRepo.List
- d578024 fix(models/openai): guard the server-side compaction rewrite with a last-seq check
- c00a32b fix(otel): stamp workflow attributes on every root span of a trace
- d0dac77 fix(run)!: injected input gets a real stream event name
- fde6166 fix(run): ResumeRun adopts a copy of the pause state's usage
- 2c10823 fix(run): a chain-built rewrite no longer deletes what the chain never saw
- b521036 fix(run): a projector that withholds an item puts it off the chain too
- e07d86e fix(run): a windowed read and a handoff filter also put items off the chain
- 5855cbd fix(run): overflow recovery keeps injected input the run had not stored yet
- 6ae8e6a fix(run): overflow recovery writes the turn on the side of the pass it survives
- cd6a515 fix(run): the forced pass on overflow reports off-chain items too
- 9514a6b fix(runstate): resume starts like a fresh run, and the decode window promises only what it can keep
- 3bcd000 fix(sandbox): a malformed exec_command call costs the call, not the run
- 5596023 fix(sandbox): advertise session_id in exec_command schema only when Sessions is on
- 1b24e47 fix(sandbox): give file tools exec's path view instead of a workdir chroot
- 5d2e26a fix(sandbox): make Policy compilation concurrency-safe
- 7a7fefa fix(sandbox): only null, 0 and false read as "none"; other scalars are refused as text
- 3ada563 fix(server): a cancelled run says so, and the bridge stops reaching past the store
- b37f50e fix(server): a tool result's display reaches the live card
- f531ae3 fix(server): restore per-method BeforeAppendModel doc comments required by revive
- 9cbe2b6 fix(server): test sinks count atomically; detach does not join the delivery goroutine
- 99a9b88 fix(session): equalDisplay compares Title and Summary too
- a0bb14e fix(tasks): a run cannot outlive the task that started it
- abd007d fix(tasks): a stop says what it did, and a fast finish is not a cancellation
- af221c1 fix(tasks): a stop that hears "already finished" looks again
- 657dcdb fix(tasks): stamp the spawning run's id on spawned tasks
- f543f80 fix(tasks): tell the model the result, or owe it — never neither
- 390a498 fix(tasks): the debt of a failed retry follows who was told
- 74b3242 fix(tasks,agents-server,web): a stop waits out a late outcome, and claims a lost one
- 89f4d75 fix(tools)!: AsTool's default input goes through schema validation
- fe0f612 fix(tools): name a nil Agent.Tools entry instead of panicking
- e461092 fix(verifydocs): widen the package list and stop prose from hiding references
- 72b67cc fix(web): a voided attempt's summary does not outlive it
- e3760ac fix: repair two regressions found reviewing the interface collapses
- 4792bce fix: review pass over the naming batch and the session split
Changes
- 79b1fce build: ignore the working plan for this review pass
- deda114 feat(agents): plan mode denies instead of hiding, backed by Tool.ReadOnly
- 1f661a3 feat(agents): stream-only Model decorator; serve ChatGPT Codex blocking calls through it
- 8036115 feat(display): Title and Summary are first-class, from the tool to the card
- b4137eb feat(handoff)!: Target declares the handoff graph statically
- e318576 feat(mcp,agents-server): a dead connection redials itself from the config that made it
- 8324815 feat(runstate): Extra carries host state across a pause, and the server stops destroying decodable states
- 483ab70 feat(sandbox): a docker project lives in a /workspace subtree, adopted only by fingerprint
- 9cbc84a feat(server)!: providers, workflows and the wake-up debt
- 388a6eb feat(server): a run binds its sandbox at start; instances retire by content generation
- 64f4969 feat(server): a session's sandbox binding is a stored, versioned fact
- da7ef60 feat(server): persistent exec_command shells on terminal-capable sandboxes; raise tool output caps
- 2f263c8 feat(server): retry a failed task from the API, and fix what that exposed
- 01ae18a feat(server): sandbox writes are strict, revisioned, and sever stale terminals
- f173be6 feat(server): session context report, prompt profile and manual compaction
- 8fca3ab feat(server): the REST surface for providers, workflows and the context report
- 92a28d0 feat(session): UserText reads the user-authored text of an input slice
- fed679f feat(stream): ItemsPersistedEvent announces the persist boundary
- a9815b7 feat(stream): the tool_called wrapper of a handoff says what it wraps
- 494eb33 feat(tasks): a failed task can be resumed instead of started over
- e5c2a69 feat(tasks): attempt-scoped writes name their attempt, and one suite holds three stores to it
- 28b25b3 feat(tasks): hand a wake-up launch its lineage
- 0faa732 feat(tools): NewToolNonStrict, and strict-schema errors that name a real exit
- 2b807e6 feat(tracing): record call_id on function spans
- a2cfdf1 feat(web): Context panel, and the decoupling it exposed
- a22e7f2 feat(web): a chat top bar names the session and hosts the panel entries
- f74b360 feat(web): retry a failed task, and stop the card lying while it runs
- 20c5778 feat(web): the composer picks a project; a bound session shows it read-only
- fa52dad feat(web): workflows, providers, plan mode and the context panel redesign
- 1cf9108 perf(server): materialize the append point, and hold it to the fold
- 8563a62 refactor!: drop zero-consumer public surface across the SDK
- f453d15 refactor(agents)!: Instructions and PromptProvider become func types
- 3b413ff refactor(agents)!: one channel per concern in the tool and agent-tool API; split run_step.go
- 5355fcd refactor(agents): fold hand-rolled helpers into the stdlib
- 442bd09 refactor(agents,models,agentstest)!: fold duplicated plumbing; fix log-level and Sensitive semantics
- 61c6104 refactor(agents-server): drop the Runner task facade the SDK tools replaced
- 91421a0 refactor(agents-server): one run pipeline, handler-owned routes, leaner hub plumbing
- 65ba8fe refactor(api)!: finish the naming batch
- 91d0464 refactor(compaction)!: PreserveSystem *bool becomes DropSystem bool
- 40b989d refactor(errors)!: one classification, and failed runs carry a RunResult
- c41e0e0 refactor(filesession)!: rename the memory package, whose contents were its opposite
- 38264b1 refactor(guardrail): dedup result construction into newGuardrailResult
- 0ed0972 refactor(items)!: collapse the RunItem interface into one struct with a Kind
- 7dc7906 refactor(mcp)!: fold RequireApprovalFunc into RequireApproval; reject conflicting naming options
- 03d7a9b refactor(mcp)!: split into its own module so the go-sdk stays out of the core
- b2cf726 refactor(mcp): blank the OnInvoke ToolContext param the meta-resolver removal orphaned
- f4290db refactor(middleware): remove Logging, the demo middleware with no consumer
- f7bb3f0 refactor(models): keep retry in one layer, disabling the client default
- 7f4fd3c refactor(run): collapse the loop's state, and let a cancelled boundary carry progress
- 1b5450d refactor(run): decompose loop's NextStep switch into named step handlers
- 9af6fe5 refactor(run): extract emitItems/appendInjected helpers, dedup injected-input handling
- b8ae25f refactor(sandbox): share ShellQuote across backends, plus small cleanups
- 9164820 refactor(sandbox/docker): one exec core per mode, and the split that hid a bug
- 4f35402 refactor(server): decompose buildAgentFromConfig
- f032f4e refactor(server): drop the dead ctx parameters staticcheck caught
- bfd14b4 refactor(server): share the REST error envelope, and modernize errors.As
- c4b3693 refactor(server,models): split the stream bridge, share the usage and event vocabularies
- adf4dcf refactor(session)!: extract stored history into agents/session
- 5f7202c refactor(session)!: three shapes on the session surface
- c38d031 refactor(session): remove the pop/undo storage primitive with no callers
- aa902ff refactor(session): share what was copied, prune what rotted
- 442ec3c refactor(sessions)!: task store on the reporting contract
- 3c4694b refactor(tasks)!: the Manager reports endings; delivery becomes the host's
- 736a845 refactor(tasks): collapse the four host hooks to func types
- eb58787 refactor(tools)!: collapse the Tool interface into the FunctionTool struct
- 49cee4a refactor(tools)!: rename FunctionTool to Tool
- c1af8e4 refactor(web): the default session name says session, not chat
- 2b3dc63 style(server): gofmt tab-align swagger annotations in sandbox.go
- c21743d style(web): tighten the chrome, and drop the Tasks item that led nowhere
- 9ba0b07 style: comments keep the invariant and drop the campaign
Full Changelog: v0.2.1...v0.3.0