Skip to content

Commit accb008

Browse files
authored
feat(v2)!: bump ACP draft schema to 2.0.0-alpha.5 (#147)
Regenerate draft bindings, add notices, require prompt response message IDs, and preserve typed elicitation values and explicit null patches verified against Rust and Node. Include shared codegen support so this change applies directly to main. Validation: make check, make gen-check, make test (262 passed, 1 skipped), and make docs-test. BREAKING CHANGE: v2 prompt handlers must return PromptResponse(message_id=...). Elicitation content rejects unsupported nested objects.
1 parent 65e0112 commit accb008

11 files changed

Lines changed: 309 additions & 49 deletions

File tree

‎docs/experimental-v2.md‎

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
> **Experimental.** Protocol v2 is a draft. Import it from `acp.experimental` and
44
> expect its API and generated models to change with the upstream schema.
55
6+
The bindings use `schema-v2.0.0-alpha.5`.
7+
68
The v2 runtime is separate from the stable v1 API. Its methods accept and return
79
generated request and response models directly. Install update handlers on the
810
client before opening a session because updates are independent connection
@@ -29,18 +31,38 @@ initialized = await connection.initialize(
2931
session = await connection.new_session(
3032
v2.schema.NewSessionRequest(cwd="/workspace")
3133
)
32-
await connection.prompt(
34+
accepted = await connection.prompt(
3335
v2.schema.PromptRequest(
3436
session_id=session.session_id,
3537
prompt=[v2.schema.TextContentBlock(text="Hello")],
3638
)
3739
)
3840
```
3941

40-
`session/prompt` returns when the agent accepts the prompt. It does not define a
41-
boundary for session updates: they may arrive before, during, or after that
42-
request, and they do not carry a prompt identifier. Applications decide how to
43-
buffer or present them.
42+
`session/prompt` returns after the agent inserts the user message into the ACP
43+
conversation, without waiting for processing to finish. The response requires a
44+
non-null `message_id`. Agents return `v2.schema.PromptResponse(message_id=...)`
45+
and echo the user message in a `UserMessageUpdate` or `UserMessageChunk` carrying
46+
the same ID. That update may arrive before or after the response; use
47+
`accepted.message_id` to match it. Other session updates are independent traffic
48+
and do not carry a prompt identifier.
49+
50+
Agents can send `v2.schema.SessionNotice(severity="warning", title="Context is nearly full")`
51+
in an `UpdateSessionNotification`. V2 notices require no client capability and
52+
are live advisory events, outside retained session history. Clients may ignore
53+
them. Titles must be non-empty, and severity also accepts custom or future strings.
54+
55+
For patch fields in session updates, omit a field to leave its current
56+
value unchanged, or explicitly pass `None` to clear it. For example,
57+
`v2.schema.SessionToolCallUpdate(tool_call_id="tool-1", name=None)` clears the
58+
tool name, while omitting `name` leaves it unchanged. This also applies to
59+
terminal updates and patch metadata. When applying received patches, use
60+
`update.model_dump(by_alias=True, exclude_unset=True)` to retain that distinction.
61+
62+
Setting `replay_from=v2.schema.ReplayFromStartVariant()` on a `ResumeSessionRequest`
63+
requests all retained conversation history; agents need not retain every message.
64+
Accepted elicitation content validates scalar values and string lists; nested
65+
objects are not valid form values.
4466

4567
Agents that serve both versions use `AgentProtocolRouter`:
4668

‎schema/v2/VERSION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
refs/tags/schema-v2.0.0-alpha.3
1+
refs/tags/schema-v2.0.0-alpha.5

‎schema/v2/schema.json‎

Lines changed: 118 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -874,7 +874,7 @@
874874
]
875875
},
876876
"name": {
877-
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProgrammatic name of the tool being invoked.\n\nThis field is optional and has patch semantics. Omission means no\nchange, `null` clears the name, and a string replaces it. For a tool\ncall ID the client has not seen before, omission or `null` means that no\ntool name is available.",
877+
"description": "Programmatic name of the tool being invoked.\n\nThis field is optional and has patch semantics. Omission means no\nchange, `null` clears the name, and a string replaces it. For a tool\ncall ID the client has not seen before, omission or `null` means that no\ntool name is available.",
878878
"type": [
879879
"string",
880880
"null"
@@ -5812,9 +5812,17 @@
58125812
"x-method": "session/set_config_option"
58135813
},
58145814
"PromptResponse": {
5815-
"description": "Response acknowledging that a user prompt was accepted.\n\nThis response does not indicate that the agent has finished processing.\nProcessing and completion are reported through `state_update` session updates.\n\nSee protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/v2/draft/prompt-lifecycle#2-prompt-accepted)",
5815+
"description": "Response acknowledging that a user prompt was inserted into the ACP conversation.\n\nThis response does not indicate that the prompt was merely received or queued, nor that the\nagent has finished processing it.\nProcessing and completion are reported through `state_update` session updates.\n\nSee protocol docs: [Prompt Accepted](https://agentclientprotocol.com/protocol/v2/draft/prompt-lifecycle#2-prompt-accepted)",
58165816
"type": "object",
58175817
"properties": {
5818+
"messageId": {
5819+
"description": "Identifies the user message inserted into the ACP conversation.\n\nRequired and non-null. Omission and explicit `null` are both invalid.\n\nThe corresponding user-message session update carries this same identifier and may arrive\nbefore or after this response. Agents must echo the message during the live session, but are\nnot required to retain it. If retained and replayed, the message keeps this identifier.",
5820+
"allOf": [
5821+
{
5822+
"$ref": "#/$defs/MessageId"
5823+
}
5824+
]
5825+
},
58185826
"_meta": {
58195827
"description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/v2/draft/extensibility)",
58205828
"type": [
@@ -5825,9 +5833,16 @@
58255833
"additionalProperties": true
58265834
}
58275835
},
5836+
"required": [
5837+
"messageId"
5838+
],
58285839
"x-side": "agent",
58295840
"x-method": "session/prompt"
58305841
},
5842+
"MessageId": {
5843+
"description": "Unique identifier for a message within a session.",
5844+
"type": "string"
5845+
},
58315846
"StartNesResponse": {
58325847
"description": "Response to `nes/start`.",
58335848
"type": "object",
@@ -6853,6 +6868,24 @@
68536868
}
68546869
]
68556870
},
6871+
{
6872+
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAdvisory information for the user that is not part of session history.\n\nNo Client capability is required. Clients that do not understand or\npresent notices may ignore them.",
6873+
"type": "object",
6874+
"properties": {
6875+
"sessionUpdate": {
6876+
"type": "string",
6877+
"const": "notice"
6878+
}
6879+
},
6880+
"required": [
6881+
"sessionUpdate"
6882+
],
6883+
"allOf": [
6884+
{
6885+
"$ref": "#/$defs/Notice"
6886+
}
6887+
]
6888+
},
68566889
{
68576890
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA context compaction has been created or updated.",
68586891
"type": "object",
@@ -7108,6 +7141,18 @@
71087141
"sessionUpdate"
71097142
]
71107143
},
7144+
{
7145+
"type": "object",
7146+
"properties": {
7147+
"sessionUpdate": {
7148+
"type": "string",
7149+
"const": "notice"
7150+
}
7151+
},
7152+
"required": [
7153+
"sessionUpdate"
7154+
]
7155+
},
71117156
{
71127157
"type": "object",
71137158
"properties": {
@@ -7138,10 +7183,6 @@
71387183
}
71397184
]
71407185
},
7141-
"MessageId": {
7142-
"description": "Unique identifier for a message within a session.",
7143-
"type": "string"
7144-
},
71457186
"ContentChunk": {
71467187
"description": "A streamed item of message content.",
71477188
"type": "object",
@@ -8416,6 +8457,71 @@
84168457
"size"
84178458
]
84188459
},
8460+
"NoticeSeverity": {
8461+
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSeverity hint for a session notice.",
8462+
"anyOf": [
8463+
{
8464+
"description": "Informational notice.",
8465+
"type": "string",
8466+
"const": "info"
8467+
},
8468+
{
8469+
"description": "Warning notice.",
8470+
"type": "string",
8471+
"const": "warning"
8472+
},
8473+
{
8474+
"description": "Error notice.",
8475+
"type": "string",
8476+
"const": "error"
8477+
},
8478+
{
8479+
"title": "other",
8480+
"description": "Custom or future notice severity.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Other unknown values are reserved for future ACP severities.",
8481+
"type": "string"
8482+
}
8483+
]
8484+
},
8485+
"Notice": {
8486+
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nFire-and-forget advisory information for the user.\n\nNotices are live events rather than session history. Agents must not rely on\na notice being received, displayed, or seen by the user.\nNo Client capability is required, and unsupported Clients may ignore notices.\n\nSee RFD: [Session Notices](https://agentclientprotocol.com/rfds/session-notices)",
8487+
"type": "object",
8488+
"properties": {
8489+
"severity": {
8490+
"description": "Presentation severity hint.",
8491+
"allOf": [
8492+
{
8493+
"$ref": "#/$defs/NoticeSeverity"
8494+
}
8495+
]
8496+
},
8497+
"title": {
8498+
"description": "Required non-empty plain-text title that can stand alone.",
8499+
"type": "string",
8500+
"minLength": 1
8501+
},
8502+
"description": {
8503+
"description": "Optional plain-text detail or guidance.\n\nOmitted and `null` are equivalent and mean no description was supplied.",
8504+
"type": [
8505+
"string",
8506+
"null"
8507+
],
8508+
"x-deserialize-default-on-error": true
8509+
},
8510+
"_meta": {
8511+
"description": "Metadata scoped to this notice.\n\nOmitted and `null` are equivalent and mean no metadata was supplied.",
8512+
"type": [
8513+
"object",
8514+
"null"
8515+
],
8516+
"x-deserialize-default-on-error": true,
8517+
"additionalProperties": true
8518+
}
8519+
},
8520+
"required": [
8521+
"severity",
8522+
"title"
8523+
]
8524+
},
84198525
"CompactionId": {
84208526
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nUnique identifier for a context compaction within a session.",
84218527
"type": "string"
@@ -8727,7 +8833,7 @@
87278833
},
87288834
{
87298835
"title": "ResumeSessionRequest",
8730-
"description": "Resumes an existing session.\n\nThe agent should resume the session context, allowing the conversation\nto continue. If `replayFrom` is set, the agent should replay\nconversation history before responding.",
8836+
"description": "Resumes an existing session.\n\nThe agent should resume the session context, allowing the conversation\nto continue. If `replayFrom` is set, the agent should replay\nretained conversation history before responding.",
87318837
"allOf": [
87328838
{
87338839
"$ref": "#/$defs/ResumeSessionRequest"
@@ -8754,7 +8860,7 @@
87548860
},
87558861
{
87568862
"title": "PromptRequest",
8757-
"description": "Processes a user prompt within a session.\n\nThis request accepts the prompt:\n- Receives user messages with optional context (files, images, etc.)\n- Returns once the prompt is accepted\n\nAfter acceptance, the Agent reports the accepted user message,\nprocessing state, output, tool calls, and completion through\n`session/update` notifications.\n\nSee protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/v2/draft/prompt-lifecycle)",
8863+
"description": "Processes a user prompt within a session.\n\nAcceptance means insertion into the ACP conversation:\n- Receives user messages with optional context (files, images, etc.)\n- Returns the inserted user message's ID without waiting for processing to finish\n\nThe Agent reports the user message with the same ID through `session/update`;\nthis notification may arrive before or after the response. Processing state,\noutput, tool calls, and completion are also reported through session updates.\n\nSee protocol docs: [Prompt Lifecycle](https://agentclientprotocol.com/protocol/v2/draft/prompt-lifecycle)",
87588864
"allOf": [
87598865
{
87608866
"$ref": "#/$defs/PromptRequest"
@@ -9695,7 +9801,7 @@
96959801
"x-method": "session/fork"
96969802
},
96979803
"ResumeSessionRequest": {
9698-
"description": "Request parameters for resuming an existing session.\n\nResumes an existing session and optionally replays prior conversation\nhistory according to `replayFrom`.",
9804+
"description": "Request parameters for resuming an existing session.\n\nResumes an existing session and optionally replays retained conversation\nhistory according to `replayFrom`.",
96999805
"type": "object",
97009806
"properties": {
97019807
"sessionId": {
@@ -9733,7 +9839,7 @@
97339839
"x-deserialize-skip-invalid-items": true
97349840
},
97359841
"replayFrom": {
9736-
"description": "Inclusive cursor describing where conversation replay should begin.\n\nOptional. Omitted or `null` both mean the Agent should resume without\nreplaying previous conversation history. Replay cursors are inclusive:\nreplay includes the position identified by the cursor. Supplying\n`{ \"type\": \"start\" }` means the Agent should replay the whole\nconversation before responding.",
9842+
"description": "Inclusive cursor describing where conversation replay should begin.\n\nOptional. Omitted or `null` both mean the Agent should resume without\nreplaying previous conversation history. Replay cursors are inclusive:\nreplay includes the position identified by the cursor. Supplying\n`{ \"type\": \"start\" }` means the Agent should replay all retained\nconversation history before responding.",
97379843
"anyOf": [
97389844
{
97399845
"$ref": "#/$defs/ReplayFrom"
@@ -9765,7 +9871,7 @@
97659871
"description": "Inclusive cursor describing where replayed session history should begin.\n\nReplay includes the position identified by the cursor.",
97669872
"anyOf": [
97679873
{
9768-
"description": "Replay the whole conversation from its first replayable entry.",
9874+
"description": "Replay all retained conversation history from its first replayable entry.",
97699875
"type": "object",
97709876
"properties": {
97719877
"type": {
@@ -9825,7 +9931,7 @@
98259931
]
98269932
},
98279933
"ReplayFromStart": {
9828-
"description": "Inclusive replay cursor requesting replay from the start of the conversation.",
9934+
"description": "Inclusive replay cursor requesting replay from the start of retained conversation history.",
98299935
"type": "object",
98309936
"properties": {
98319937
"_meta": {

‎scripts/gen_schema_v2.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@
127127
"ConfigOptionUpdate",
128128
"SessionInfoUpdate",
129129
"UsageUpdate",
130+
"SessionNotice",
130131
"SessionCompactionUpdate",
131132
"SessionCompactionSummaryChunk",
132133
),
@@ -161,7 +162,7 @@
161162
inline_model_ref("NesSuggestion", ("anyOf", 4), ("object", None)): "OtherNesSuggestion",
162163
inline_model_ref("ElicitationPropertySchema", ("anyOf", 5), ("object", None)): ("ElicitationOtherPropertySchema"),
163164
inline_model_ref("MultiSelectItems", ("anyOf", 1), ("object", None)): "OtherMultiSelectItems",
164-
inline_model_ref("SessionUpdate", ("anyOf", 19), ("object", None)): "OtherSessionUpdate",
165+
inline_model_ref("SessionUpdate", ("anyOf", 20), ("object", None)): "OtherSessionUpdate",
165166
inline_model_ref("SessionUpdate", ("anyOf", 6), ("allOf", 0), ("allOf", None)): ("RunningSessionStateUpdateBase"),
166167
inline_model_ref("SessionUpdate", ("anyOf", 6), ("allOf", 1), ("allOf", None)): ("IdleSessionStateUpdateBase"),
167168
inline_model_ref("SessionUpdate", ("anyOf", 6), ("allOf", 2), ("allOf", None)): (
@@ -229,4 +230,6 @@
229230
schema_out=ROOT / "src" / "acp" / "experimental" / "v2" / "schema.py",
230231
base_class="acp.experimental.v2._schema_base.BaseModel",
231232
model_name_map=MODEL_NAME_MAP,
233+
# The generator loses additionalProperties types on nullable objects.
234+
field_type_overrides={"ElicitationAcceptAction.content": "acp._schema_base.ElicitationContent"},
232235
)

‎src/acp/experimental/v2/_methods.py‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ def notification(method: str, handler: str, params_type: Any) -> NotificationSpe
107107
"prompt",
108108
schema.PromptRequest,
109109
schema.PromptResponse,
110-
empty_response=True,
111110
),
112111
request(AGENT_METHODS["mcp_message"], "mcp_message", schema.MessageMcpRequest, Any),
113112
request(AGENT_METHODS["session_list"], "list_sessions", schema.ListSessionsRequest, schema.ListSessionsResponse),

‎src/acp/experimental/v2/_schema_base.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class BaseModel(_BaseModel):
3232
"compaction_summary_chunk",
3333
"compaction_update",
3434
"config_option_update",
35+
"notice",
3536
"plan_removed",
3637
"plan_update",
3738
"session_info_update",

‎src/acp/experimental/v2/agent.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626

2727
def _dump(model: BaseModel) -> dict[str, Any]:
28-
return model.model_dump(mode="json", by_alias=True, exclude_none=True, exclude_unset=True)
28+
return model.model_dump(mode="json", by_alias=True, exclude_unset=True)
2929

3030

3131
class _AgentRouter:

‎src/acp/experimental/v2/meta.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Generated from schema/v2/meta.json. Do not edit by hand.
2-
# Schema ref: refs/tags/schema-v2.0.0-alpha.3
2+
# Schema ref: refs/tags/schema-v2.0.0-alpha.5
33
AGENT_METHODS = {
44
"initialize": "initialize",
55
"auth_login": "auth/login",

0 commit comments

Comments
 (0)