Skip to main content
ACP evolves by adding new variants and values a client built yesterday has never heard of. 0.80.0 reworks several Java types specifically so that “unrecognized” doesn’t mean “crashes.”

Open value types, not enums

StopReason, ToolCallStatus, ToolKind, PermissionOptionKind, PlanEntryStatus, PlanEntryPriority, and Role are now records wrapping a String, not Java enums. This means: A value the running code has never heard of (because the peer speaks a newer protocol version) still round-trips correctly: it deserializes to the same record type, just holding a string .known() doesn’t list. Treating these as enums was never source-compatible with a hypothetical future protocol version; 0.80.0 makes that explicit in the type itself.

Unknown* variants

Several unions gained an explicit fallback member for a shape the running code doesn’t recognize: UnknownSessionUpdate, UnknownContentBlock, UnknownToolCallContent, UnknownPermissionOutcome, UnknownSessionConfigOption, UnknownElicitationPropertySchema, UnknownMultiSelectItems, UnknownAuthMethod (reading as AuthMethodAgent, in that specific case). Deserializing unrecognized data into a typed fallback, rather than failing the whole message, is what makes it safe to run this SDK’s client against a newer agent (or vice versa) without an immediate crash on day one of some future protocol version. No union in this SDK is sealed. Any instanceof chain over SessionUpdate, ContentBlock, or similar must end with the Unknown* branch and a default, never assume the listed cases are exhaustive:

_meta everywhere

Nearly every request, response, and notification carries an optional _meta: Map<String, Object> field for implementation-specific extension data. Round-trip it even when you don’t understand it: if you’re building a proxy, forwarding _meta verbatim is what keeps it useful for the two specific endpoints that defined it.

Error codes

0.80.0 aligns every error code with the ACP v1 schema exactly; the previous ad hoc choices are gone. The spec’s own error-code page is thin (“Documentation coming soon”); the schema’s ErrorCode is the source of truth, and this table is this SDK’s copy of it: AcpError.getCode() returns the numeric code; getMessage() is the peer’s message text (plus any detail from its error data), no longer [code=N] message; toString() still includes [code=N] for logs. Use error.toException() on the receiving side and JSONRPCError.from(e) when answering, rather than hand-building either direction.

Concepts

Session updates and the ordering guarantee these open types flow through

Migration Guide

Every error-code and constructor-signature change from 0.18.0, in one table