Add a Go ACP cookbook mirroring the Rust patterns (#22) - #41
Merged
Merged
Conversation
Port the reference Rust agent-client-protocol-cookbook recipes that map onto acp-go as a documentation-only cookbook package. Each recipe is one runnable example that go test executes against a deterministic in-process or subprocess agent, so the snippets cannot drift: - one-shot v1 and draft-v2 prompts, in-process and through both runners - building a v1 agent with streamed updates, tool calls, and permissions - ordered application dispatch on one FIFO - draft-v2 session resume/replay/close coordination - a proxyrouter proxy component that adds an MCP server to sessions - attaching MCP servers via the stable facade, mcp, and v2/mcp The package documentation links every recipe to the package it covers, records the Go ordering contract each relies on and where it differs from Rust, and explains the recipes that do not port. README, ROADMAP, the parity record, and CHANGELOG mark #22 delivered. No new dependencies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qKao9sNYaSpHw7wsaxcwR
- proxy: flush client notifications before returning a client's answer to a successor request, so session/cancel reaches the agent before the cancelled permission outcome; reject non-object session setup params instead of panicking; report non-EOF connection errors from Serve; document that envelope _meta is neither set nor forwarded, as in the reference conductor - mcp: close the connection on a version mismatch, like InitializeWithInfo, and say the helper replaces it - agent: close out the pending tool call with a failed update when the prompt is cancelled; stop dereferencing optional schema fields - ordered dispatch: stop the agent after a failed resume too, so the example cannot hang - runner fixtures: launch os.Executable() because the runners start the agent in its workspace, where a relative os.Args[0] does not resolve recipes_test.go pins these paths. The cancel-ordering test fails 20 of 20 runs with the proxy flush removed and passes with it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014qKao9sNYaSpHw7wsaxcwR
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #22.
Problem
examples/covers the basics, but nothing ties the patterns in the Rustagent-client-protocol-cookbookto acp-go's packages (acp,v2,runner,clienthost,proxyrouter,mcp,v2/mcp).What this adds
This PR adds
github.com/BrokkAi/acp-go/cookbook, a documentation-only package.doc.goholds the guide. Each recipe is one whole-fileExamplein its ownexample_*_test.go, andgo test ./...runs it against a deterministic agent. Each file has exactly one example plus its helpers, so pkg.go.dev renders the whole file.one_shot_promptExample_oneShotPrompt,Example_runneracp,runner,clienthostv2_one_shot_promptExample_v2OneShotPrompt,Example_v2Runnerv2(SessionTracker,SessionHandle,CancellablePermissions),v2/runnerbuilding_an_agentExample_buildingAnAgentagent: streamed updates, tool calls, permission requests, cancellationordered_application_dispatchExample_orderedApplicationDispatchacp.Notificationsordering,v2.ResumeSessionFromStartv2_session_coordinationExample_v2SessionCoordinationExample_proxyComponentproxyrouter: a v1 proxy that adds an MCP server tosession/new/session/loadExample_mcpServers,Example_v2McpServersmcp(nativeacptransport),v2/mcpThe runner recipes launch a real subprocess: the test binary re-executes itself as a fixture agent, the pattern
runner's tests already use. No credentials are needed.recipes_test.gopins the edge cases the examples' happy paths don't reach.The package docs also record:
on_receiving_resultcounterpart, so a later live update can be queued ahead of a response marker. The recipe states that the marker's queue position therefore cannot separate replayed updates from live ones.global_mcp_server,per_session_mcp_serverandfiltering_toolsneed an MCP server SDK and the MCP-over-ACP methods (Agent-side MCP-over-ACP method dispatch (mcp/connect, mcp/message, mcp/disconnect) #39).running_proxies_with_conductordepends on the conductor, which is won't port (Port the ACP proxy-chain conductor CLI (stdlib-only) #24).README, ROADMAP,
docs/rust-ecosystem-parity.mdand CHANGELOG now mark #22 delivered. No new dependencies.Notes for review
Proxy envelope. The proxy recipe hand-models the
_proxy/successorenvelope ({method, params}) from Rust SDK 2.2.0'sproxy_protocol.rs, because the pinned schema has no_proxy/*methods. Like the reference conductor, it neither sets nor forwards the envelope's own_meta; the params'_metapasses through untouched. The envelope lives in example code, not library API, which keeps Port the ACP proxy-chain conductor CLI (stdlib-only) #24's decision intact.testConductoris test scaffolding only.Proxy ordering. Callbacks must not write to their own connection, so the proxy queues notifications on a goroutine. Before forwarding any response or request, in either direction, it flushes that direction's queue:
session/updateof a turn before the turn's response;session/cancelbefore the client's cancelled permission outcome.One limit is documented:
acp.Connectionruns inbound requests concurrently, so a notification that follows a request (e.g.session/cancelright aftersession/prompt) can overtake it on the way to the successor.MCP recipe workaround.
mcp.NewSessiongates onunstable.InitializeResponse, but the stable facade doesn't decode the unstable MCP flags. The recipe therefore captures the rawinitializeresult and decodes it twice. That replacesInitializeWithInfoon the connection, so the recipe checks the negotiated version itself and closes the connection on a mismatch. A small SDK helper could remove this workaround; that would be a follow-up, not part of this PR.Validation
go test -race ./...,go vet ./...,gofmt -l .,python3 scripts/licenses.py,python3 -m unittest discover -s scripts -p '*_test.py': all clean._metapoint is documented as above. The cancel-ordering test fails 20/20 with the proxy's flush removed and passes 50/50 with it. The relative-path runner bug was reproduced with a compiled test binary (fork/exec ./cookbook.test: no such file or directory) before the fix and passes after it.cookbookpackage passed 500 consecutive-raceruns.🤖 Generated with Claude Code
https://claude.ai/code/session_014qKao9sNYaSpHw7wsaxcwR