diff --git a/schema/VERSION b/schema/VERSION index 994766d..5c3b96a 100644 --- a/schema/VERSION +++ b/schema/VERSION @@ -1 +1 @@ -refs/tags/schema-v1.16.0 +refs/tags/schema-v1.19.0 diff --git a/schema/schema.json b/schema/schema.json index a117ee8..0f44cbf 100644 --- a/schema/schema.json +++ b/schema/schema.json @@ -109,7 +109,7 @@ "anyOf": [ { "title": "CancelRequestNotification", - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or\nchanged at any point.\n\nCancels an ongoing request.\n\nThis is a notification sent by the side that sent a request to cancel that request.\n\nUpon receiving this notification, the receiver:\n\n1. MUST cancel the corresponding request activity and all nested activities\n2. MAY send any pending notifications.\n3. MUST send one of these responses for the original request:\n - Valid response with appropriate data (partial results or cancellation marker)\n - Error response with code `-32800` (Cancelled)\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/cancellation)", + "description": "Cancels an ongoing request.\n\nThis is a notification sent by the side that sent a request to cancel that request.\n\nUpon receiving this notification, the receiver:\n\n1. MAY cancel the corresponding request activity and all nested activities\n2. MAY send any pending notifications.\n3. MUST send one of these responses for the original request:\n - Valid response with appropriate data (partial results or cancellation marker)\n - Error response with code `-32800` (Cancelled)\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/cancellation)", "allOf": [ { "$ref": "#/$defs/CancelRequestNotification" @@ -332,6 +332,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -370,7 +371,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "limit": { "description": "Maximum number of lines to read.", @@ -379,7 +381,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -387,6 +390,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -430,6 +434,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -482,7 +487,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "content": { "description": "Replace the content collection.", @@ -509,10 +515,12 @@ "x-deserialize-skip-invalid-items": true }, "rawInput": { - "description": "Update the raw input." + "description": "Update the raw input.", + "x-deserialize-default-on-error": true }, "rawOutput": { - "description": "Update the raw output." + "description": "Update the raw output.", + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -520,6 +528,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -792,7 +801,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "priority": { "description": "Relative importance of this content when clients choose what to surface.", @@ -800,7 +810,8 @@ "number", "null" ], - "format": "double" + "format": "double", + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -808,6 +819,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -853,6 +865,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -889,7 +902,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -897,6 +911,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -935,6 +950,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -964,14 +980,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "mimeType": { "description": "MIME type describing the encoded media payload.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "name": { "description": "Human-readable name shown for this protocol object.", @@ -983,14 +1001,16 @@ "integer", "null" ], - "format": "int64" + "format": "int64", + "x-deserialize-default-on-error": true }, "title": { "description": "Optional display title for end-user UI.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "uri": { "description": "URI associated with this resource or media payload.", @@ -1002,6 +1022,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1042,7 +1063,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "text": { "description": "Text payload carried by this content block.", @@ -1058,6 +1080,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1079,7 +1102,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "uri": { "description": "URI associated with this resource or media payload.", @@ -1091,6 +1115,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1129,6 +1154,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1154,6 +1180,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1166,7 +1193,7 @@ "type": "object", "properties": { "path": { - "description": "The file path being modified.", + "description": "The absolute file path being modified.", "type": "string" }, "oldText": { @@ -1174,7 +1201,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "newText": { "description": "The new content after modification.", @@ -1186,6 +1214,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1216,6 +1245,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1228,7 +1258,7 @@ "type": "object", "properties": { "path": { - "description": "The file path being accessed or modified.", + "description": "The absolute file path being accessed or modified.", "type": "string" }, "line": { @@ -1238,7 +1268,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -1246,6 +1277,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1283,6 +1315,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1342,21 +1375,26 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "env": { "description": "Environment variables for the command.", "type": "array", "items": { "$ref": "#/$defs/EnvVariable" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "cwd": { - "description": "Working directory for the command (absolute path).", + "description": "Working directory for the command. Must be an absolute path.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "outputByteLimit": { "description": "Maximum number of output bytes to retain.\n\nWhen the limit is exceeded, the Client truncates from the beginning of the output\nto stay within the limit.\n\nThe Client MUST ensure truncation happens at a character boundary to maintain valid\nstring output, even if this means the retained output is slightly less than the\nspecified limit.", @@ -1365,7 +1403,8 @@ "null" ], "format": "uint64", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -1373,6 +1412,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1401,6 +1441,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1435,6 +1476,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1471,6 +1513,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1507,6 +1550,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1543,6 +1587,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1567,10 +1612,11 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, - "oneOf": [ + "anyOf": [ { "description": "Form-based elicitation where the client renders a form from the provided schema.", "type": "object", @@ -1606,6 +1652,69 @@ "$ref": "#/$defs/ElicitationUrlMode" } ] + }, + { + "title": "other", + "description": "Custom or future elicitation mode.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nClients that do not understand this mode should preserve the raw payload\nwhen storing, replaying, proxying, or forwarding elicitation requests.\nThey MUST NOT render it as a known elicitation mode.", + "type": "object", + "properties": { + "mode": { + "description": "Custom or future elicitation mode.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + } + }, + "required": [ + "mode" + ], + "anyOf": [ + { + "title": "Session", + "description": "Tied to a session, optionally to a specific tool call within that session.", + "allOf": [ + { + "$ref": "#/$defs/ElicitationSessionScope" + } + ] + }, + { + "title": "Request", + "description": "Tied to a specific JSON-RPC request outside of a session\n(e.g., during auth/configuration phases before any session is started).", + "allOf": [ + { + "$ref": "#/$defs/ElicitationRequestScope" + } + ] + } + ], + "unevaluatedProperties": true, + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "const": "form" + } + }, + "required": [ + "mode" + ] + }, + { + "type": "object", + "properties": { + "mode": { + "type": "string", + "const": "url" + } + }, + "required": [ + "mode" + ] + } + ] + } } ], "discriminator": { @@ -1638,7 +1747,8 @@ { "type": "null" } - ] + ], + "x-deserialize-default-on-error": true } }, "required": [ @@ -1668,6 +1778,7 @@ "properties": { "type": { "description": "Type discriminator. Always `\"object\"`.", + "x-deserialize-default-on-error": true, "default": "object", "allOf": [ { @@ -1680,7 +1791,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "properties": { "description": "Property definitions (must be primitive types).", @@ -1705,7 +1817,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -1713,6 +1826,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -1729,7 +1843,7 @@ }, "ElicitationPropertySchema": { "description": "Property schema for elicitation form fields.\n\nEach variant corresponds to a JSON Schema `\"type\"` value.\nSingle-select enums use the `String` variant with `enum` or `oneOf` set.\nMulti-select enums use the `Array` variant.", - "oneOf": [ + "anyOf": [ { "description": "String property (or single-select enum when `enum`/`oneOf` is set).", "type": "object", @@ -1819,6 +1933,85 @@ "$ref": "#/$defs/MultiSelectPropertySchema" } ] + }, + { + "title": "other", + "description": "Custom or future elicitation property schema.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nClients that do not understand this property schema type should preserve\nthe raw schema when storing, replaying, proxying, or forwarding\nelicitation requests. They MUST NOT render it as a known input control.", + "type": "object", + "properties": { + "type": { + "description": "Custom or future elicitation property schema type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + } + }, + "required": [ + "type" + ], + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "string" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "number" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "integer" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "boolean" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "array" + } + }, + "required": [ + "type" + ] + } + ] + }, + "additionalProperties": true } ], "discriminator": { @@ -1851,7 +2044,7 @@ ] }, "EnumOption": { - "description": "A titled enum option with a const value and human-readable title.", + "description": "A titled enum option with a const value, human-readable title, and optional description.", "type": "object", "properties": { "const": { @@ -1862,12 +2055,21 @@ "description": "Human-readable title for this option.", "type": "string" }, + "description": { + "description": "Human-readable description.", + "type": [ + "string", + "null" + ], + "x-deserialize-default-on-error": true + }, "_meta": { "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/extensibility)", "type": [ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -1885,14 +2087,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "description": { "description": "Human-readable description.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "minLength": { "description": "Minimum string length.", @@ -1935,7 +2139,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "enum": { "description": "Enum values for untitled single-select enums.", @@ -1963,6 +2168,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -1976,14 +2182,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "description": { "description": "Human-readable description.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "minimum": { "description": "Minimum value (inclusive).", @@ -2007,7 +2215,8 @@ "number", "null" ], - "format": "double" + "format": "double", + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -2015,6 +2224,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2028,14 +2238,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "description": { "description": "Human-readable description.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "minimum": { "description": "Minimum value (inclusive).", @@ -2059,7 +2271,8 @@ "integer", "null" ], - "format": "int64" + "format": "int64", + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -2067,6 +2280,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2080,21 +2294,24 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "description": { "description": "Human-readable description.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "default": { "description": "Default value.", "type": [ "boolean", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -2102,6 +2319,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2110,16 +2328,56 @@ "description": "Items for a multi-select (array) property schema.", "anyOf": [ { - "title": "Untitled", - "description": "Untitled multi-select items with plain string values.", + "description": "Multi-select string items with plain string values.", + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "string" + } + }, + "required": [ + "type" + ], "allOf": [ { - "$ref": "#/$defs/UntitledMultiSelectItems" + "$ref": "#/$defs/StringMultiSelectItems" } ] }, { - "title": "Titled", + "title": "other", + "description": "Custom or future typed multi-select items.", + "type": "object", + "properties": { + "type": { + "description": "Custom or future multi-select item type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + } + }, + "required": [ + "type" + ], + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "type": { + "type": "string", + "const": "string" + } + }, + "required": [ + "type" + ] + } + ] + }, + "additionalProperties": true + }, + { + "title": "titled", "description": "Titled multi-select items with human-readable labels.", "allOf": [ { @@ -2127,20 +2385,15 @@ } ] } - ] + ], + "discriminator": { + "propertyName": "type" + } }, - "UntitledMultiSelectItems": { - "description": "Items definition for untitled multi-select enum properties.", + "StringMultiSelectItems": { + "description": "String item schema for multi-select enum properties.", "type": "object", "properties": { - "type": { - "description": "Item type discriminator. Must be `\"string\"`.", - "allOf": [ - { - "$ref": "#/$defs/ElicitationStringType" - } - ] - }, "enum": { "description": "Allowed enum values.", "type": "array", @@ -2154,24 +2407,14 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "type", "enum" ] }, - "ElicitationStringType": { - "description": "Items definition for untitled multi-select enum properties.", - "oneOf": [ - { - "description": "String schema type.", - "type": "string", - "const": "string" - } - ] - }, "TitledMultiSelectItems": { "description": "Items definition for titled multi-select enum properties.", "type": "object", @@ -2189,6 +2432,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -2205,14 +2449,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "description": { "description": "Human-readable description.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "minItems": { "description": "Minimum number of items to select.", @@ -2248,7 +2494,9 @@ ], "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -2256,6 +2504,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -2351,7 +2600,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `mcp/connect`.", "type": "object", "properties": { - "acpId": { + "serverId": { "description": "The ACP MCP server ID that was provided by the component declaring the MCP server.", "allOf": [ { @@ -2365,11 +2614,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "acpId" + "serverId" ], "x-side": "client", "x-method": "mcp/connect" @@ -2408,6 +2658,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -2440,6 +2691,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -2712,6 +2964,7 @@ }, "agentCapabilities": { "description": "Capabilities supported by the agent.", + "x-deserialize-default-on-error": true, "default": { "loadSession": false, "promptCapabilities": { @@ -2761,6 +3014,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -2784,10 +3038,12 @@ "loadSession": { "description": "Whether the agent supports `session/load`.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "promptCapabilities": { "description": "Prompt capabilities supported by the agent.", + "x-deserialize-default-on-error": true, "default": { "image": false, "audio": false, @@ -2801,6 +3057,7 @@ }, "mcpCapabilities": { "description": "MCP capabilities supported by the agent.", + "x-deserialize-default-on-error": true, "default": { "http": false, "sse": false, @@ -2814,6 +3071,7 @@ }, "sessionCapabilities": { "description": "Session lifecycle and prompt capabilities advertised by the agent.", + "x-deserialize-default-on-error": true, "default": {}, "allOf": [ { @@ -2823,6 +3081,7 @@ }, "auth": { "description": "Authentication-related capabilities supported by the agent.", + "x-deserialize-default-on-error": true, "default": {}, "allOf": [ { @@ -2831,7 +3090,7 @@ ] }, "providers": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nBy supplying `{}` it means that the agent supports provider configuration methods.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports provider configuration methods.", "anyOf": [ { "$ref": "#/$defs/ProvidersCapabilities" @@ -2843,7 +3102,7 @@ "x-deserialize-default-on-error": true }, "nes": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the agent.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the agent.\n\nOptional. Omitted or `null` both mean the agent does not advertise support\nfor NES methods.", "anyOf": [ { "$ref": "#/$defs/NesCapabilities" @@ -2872,6 +3131,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2883,17 +3143,20 @@ "image": { "description": "Agent supports [`ContentBlock::Image`].", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "audio": { "description": "Agent supports [`ContentBlock::Audio`].", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "embeddedContext": { "description": "Agent supports embedded context in `session/prompt` requests.\n\nWhen enabled, the Client is allowed to include [`ContentBlock::Resource`]\nin prompt requests for pieces of context that are referenced in the message.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -2901,6 +3164,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2912,17 +3176,20 @@ "http": { "description": "Agent supports [`McpServer::Http`].", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "sse": { "description": "Agent supports [`McpServer::Sse`].", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "acp": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAgent supports [`McpServer::Acp`].", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -2930,6 +3197,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -2939,7 +3207,7 @@ "type": "object", "properties": { "list": { - "description": "Whether the agent supports `session/list`.", + "description": "Whether the agent supports `session/list`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports listing sessions.", "anyOf": [ { "$ref": "#/$defs/SessionListCapabilities" @@ -2963,7 +3231,7 @@ "x-deserialize-default-on-error": true }, "additionalDirectories": { - "description": "Whether the agent supports `additionalDirectories` on supported session lifecycle requests.\n\nAgents that also support `session/list` may return\n`SessionInfo.additionalDirectories` to report the complete ordered\nadditional-root list associated with a listed session.", + "description": "Whether the agent supports `additionalDirectories` on supported session lifecycle requests.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports `additionalDirectories` on\nsupported session lifecycle requests.\n\nAgents that also support `session/list` may return\n`SessionInfo.additionalDirectories` to report the complete ordered\nadditional-root list associated with a listed session.", "anyOf": [ { "$ref": "#/$defs/SessionAdditionalDirectoriesCapabilities" @@ -2975,7 +3243,7 @@ "x-deserialize-default-on-error": true }, "fork": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/fork`.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/fork`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports forking sessions.", "anyOf": [ { "$ref": "#/$defs/SessionForkCapabilities" @@ -2987,7 +3255,7 @@ "x-deserialize-default-on-error": true }, "resume": { - "description": "Whether the agent supports `session/resume`.", + "description": "Whether the agent supports `session/resume`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports resuming sessions.", "anyOf": [ { "$ref": "#/$defs/SessionResumeCapabilities" @@ -2999,7 +3267,7 @@ "x-deserialize-default-on-error": true }, "close": { - "description": "Whether the agent supports `session/close`.", + "description": "Whether the agent supports `session/close`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports closing sessions.", "anyOf": [ { "$ref": "#/$defs/SessionCloseCapabilities" @@ -3016,12 +3284,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionListCapabilities": { - "description": "Capabilities for the `session/list` method.\n\nBy supplying `{}` it means that the agent supports listing of sessions.", + "description": "Capabilities for the `session/list` method.\n\nSupplying `{}` means the agent supports listing sessions.", "type": "object", "properties": { "_meta": { @@ -3030,6 +3299,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3044,12 +3314,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionAdditionalDirectoriesCapabilities": { - "description": "Capabilities for additional session directories support.\n\nBy supplying `{}` it means that the agent supports the `additionalDirectories`\nfield on supported session lifecycle requests. Agents that also support\n`session/list` may return `SessionInfo.additionalDirectories` to report the\ncomplete ordered additional-root list associated with a listed session.", + "description": "Capabilities for additional session directories support.\n\nSupplying `{}` means the agent supports the `additionalDirectories` field on\nsupported session lifecycle requests. Agents that also support\n`session/list` may return `SessionInfo.additionalDirectories` to report the\ncomplete ordered additional-root list associated with a listed session.", "type": "object", "properties": { "_meta": { @@ -3058,12 +3329,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionForkCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for the `session/fork` method.\n\nBy supplying `{}` it means that the agent supports forking of sessions.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for the `session/fork` method.\n\nSupplying `{}` means the agent supports forking sessions.", "type": "object", "properties": { "_meta": { @@ -3072,12 +3344,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionResumeCapabilities": { - "description": "Capabilities for the `session/resume` method.\n\nBy supplying `{}` it means that the agent supports resuming of sessions.", + "description": "Capabilities for the `session/resume` method.\n\nSupplying `{}` means the agent supports resuming sessions.", "type": "object", "properties": { "_meta": { @@ -3086,12 +3359,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionCloseCapabilities": { - "description": "Capabilities for the `session/close` method.\n\nBy supplying `{}` it means that the agent supports closing of sessions.", + "description": "Capabilities for the `session/close` method.\n\nSupplying `{}` means the agent supports closing sessions.", "type": "object", "properties": { "_meta": { @@ -3100,6 +3374,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3109,7 +3384,7 @@ "type": "object", "properties": { "logout": { - "description": "Whether the agent supports the logout method.\n\nBy supplying `{}` it means that the agent supports the logout method.", + "description": "Whether the agent supports the logout method.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports the logout method.", "anyOf": [ { "$ref": "#/$defs/LogoutCapabilities" @@ -3126,12 +3401,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "LogoutCapabilities": { - "description": "Logout capabilities supported by the agent.\n\nBy supplying `{}` it means that the agent supports the logout method.", + "description": "Logout capabilities supported by the agent.\n\nSupplying `{}` means the agent supports the logout method.", "type": "object", "properties": { "_meta": { @@ -3140,12 +3416,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "ProvidersCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nBy supplying `{}` it means that the agent supports provider configuration methods.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nSupplying `{}` means the agent supports provider configuration methods.", "type": "object", "properties": { "_meta": { @@ -3154,6 +3431,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3192,6 +3470,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3218,6 +3497,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3292,6 +3572,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3306,6 +3587,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3328,6 +3610,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3360,6 +3643,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3374,6 +3658,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3388,6 +3673,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3474,6 +3760,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3489,7 +3776,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -3497,6 +3785,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3511,6 +3800,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3526,7 +3816,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -3534,6 +3825,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3549,7 +3841,8 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -3557,6 +3850,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3571,6 +3865,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3585,6 +3880,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -3676,16 +3972,19 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "secret": { "description": "Whether this value is a secret (e.g. API key, token).\nClients should use a password-style input for secret vars.\n\nDefaults to `true`.", "type": "boolean", + "x-deserialize-default-on-error": true, "default": true }, "optional": { "description": "Whether this variable is optional.\n\nDefaults to `false`.", "type": "boolean", + "x-deserialize-default-on-error": true, "default": false }, "_meta": { @@ -3694,6 +3993,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3722,21 +4022,25 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "vars": { "description": "The environment variables the client should set.", "type": "array", "items": { "$ref": "#/$defs/AuthEnvVar" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "link": { "description": "Optional link to a page where the user can obtain their credentials.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -3744,6 +4048,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3774,18 +4079,22 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "args": { "description": "Additional arguments to pass when running the agent binary for terminal auth.", "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "env": { "description": "Additional environment variables to set when running the agent binary for terminal auth.", "type": "object", + "x-deserialize-default-on-error": true, "additionalProperties": { "type": "string" } @@ -3796,6 +4105,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3825,7 +4135,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -3833,6 +4144,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3842,7 +4154,7 @@ ] }, "Implementation": { - "description": "Metadata about the implementation of the client or agent.\nDescribes the name and version of an MCP implementation, with an optional\ntitle for UI representation.", + "description": "Metadata about the implementation of the client or agent.\nDescribes the name and version of an ACP implementation, with an optional\ntitle for UI representation.", "type": "object", "properties": { "name": { @@ -3854,7 +4166,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "version": { "description": "Version of the implementation. Can be displayed to the user or used\nfor debugging or metrics purposes. (e.g. \"1.0.0\").", @@ -3866,6 +4179,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3884,6 +4198,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3899,9 +4214,7 @@ "type": "array", "items": { "$ref": "#/$defs/ProviderInfo" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "_meta": { "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/extensibility)", @@ -3909,6 +4222,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -3922,9 +4236,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nInformation about a configurable LLM provider.", "type": "object", "properties": { - "id": { + "providerId": { "description": "Provider identifier, for example \"main\" or \"openai\".", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "supported": { "description": "Supported protocol types for this provider.", @@ -3936,7 +4254,7 @@ "x-deserialize-skip-invalid-items": true }, "required": { - "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this id.", + "description": "Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this provider ID.", "type": "boolean" }, "current": { @@ -3956,15 +4274,20 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id", + "providerId", "supported", "required" ] }, + "ProviderId": { + "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 configurable LLM provider.", + "type": "string" + }, "LlmProtocol": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWell-known API protocol identifiers for LLM providers.\n\nAgents and clients MUST handle unknown protocol identifiers gracefully.\n\nProtocol names beginning with `_` are free for custom use, like other ACP extension methods.\nProtocol names that do not begin with `_` are reserved for the ACP spec.", "anyOf": [ @@ -4022,6 +4345,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4040,6 +4364,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4056,6 +4381,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4072,6 +4398,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4120,6 +4447,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4156,6 +4484,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4189,7 +4518,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -4197,6 +4527,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4226,7 +4557,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "category": { "description": "Optional semantic category for this option (UX only).", @@ -4246,6 +4578,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4273,7 +4606,7 @@ ] }, { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nBoolean on/off toggle.", + "description": "Boolean on/off toggle.", "type": "object", "properties": { "type": { @@ -4375,7 +4708,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -4383,6 +4717,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4412,7 +4747,9 @@ "type": "array", "items": { "$ref": "#/$defs/SessionConfigSelectOption" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -4420,6 +4757,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4460,7 +4798,7 @@ ] }, "SessionConfigBoolean": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA boolean on/off toggle session configuration option payload.", + "description": "A boolean on/off toggle session configuration option payload.", "type": "object", "properties": { "currentValue": { @@ -4506,6 +4844,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4530,7 +4869,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -4538,6 +4878,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4568,7 +4909,9 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "title": { "description": "Human-readable title for the session", @@ -4592,6 +4935,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4610,6 +4954,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4658,6 +5003,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4701,6 +5047,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4717,6 +5064,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4733,6 +5081,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4758,6 +5107,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4797,6 +5147,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4865,7 +5216,8 @@ "null" ], "format": "uint64", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "cachedReadTokens": { "description": "Total cache read tokens.", @@ -4874,7 +5226,8 @@ "null" ], "format": "uint64", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "cachedWriteTokens": { "description": "Total cache write tokens.", @@ -4883,7 +5236,8 @@ "null" ], "format": "uint64", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -4891,6 +5245,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4918,6 +5273,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -4936,9 +5292,7 @@ "type": "array", "items": { "$ref": "#/$defs/NesSuggestion" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "_meta": { "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/extensibility)", @@ -4946,6 +5300,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5035,6 +5390,10 @@ "propertyName": "kind" } }, + "NesSuggestionId": { + "description": "Unique identifier for a next edit suggestion.", + "type": "string" + }, "NesTextEdit": { "description": "A text edit within a suggestion.", "type": "object", @@ -5057,6 +5416,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5091,6 +5451,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5121,6 +5482,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5135,7 +5497,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The URI of the file to edit.", @@ -5166,6 +5532,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5181,7 +5548,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file to navigate to.", @@ -5201,6 +5572,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5216,7 +5588,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI containing the symbol.", @@ -5240,6 +5616,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5256,7 +5633,11 @@ "properties": { "id": { "description": "Unique identifier for accept/reject tracking.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "uri": { "description": "The file URI to search within.", @@ -5283,6 +5664,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5303,6 +5685,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5334,7 +5717,8 @@ "type": "string" }, "data": { - "description": "Optional primitive or structured value that contains additional information about the error.\nThis may include debugging information or context-specific details." + "description": "Optional primitive or structured value that contains additional information about the error.\nThis may include debugging information or context-specific details.", + "x-deserialize-default-on-error": true } }, "required": [ @@ -5382,7 +5766,7 @@ }, { "title": "Request cancelled", - "description": "**Request cancelled**: **UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nExecution of the method was aborted either due to a cancellation request from the caller or\nbecause of resource constraints or shutdown.", + "description": "**Request cancelled**: Execution of the method was aborted either due to a cancellation request from the caller or\nbecause of resource constraints or shutdown.", "type": "integer", "format": "int32", "const": -32800 @@ -5401,13 +5785,6 @@ "format": "int32", "const": -32002 }, - { - "title": "URL elicitation required", - "description": "**URL elicitation required**: **UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nThe agent requires user input via a URL-based elicitation before it can proceed.", - "type": "integer", - "format": "int32", - "const": -32042 - }, { "title": "Other", "description": "Other undefined error code.", @@ -5505,6 +5882,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5782,7 +6160,8 @@ { "type": "null" } - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -5790,6 +6169,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5815,6 +6195,7 @@ }, "kind": { "description": "The category of tool being invoked.\nHelps clients choose appropriate icons and UI treatment.", + "x-deserialize-default-on-error": true, "allOf": [ { "$ref": "#/$defs/ToolKind" @@ -5823,6 +6204,7 @@ }, "status": { "description": "Current execution status of the tool call.", + "x-deserialize-default-on-error": true, "allOf": [ { "$ref": "#/$defs/ToolCallStatus" @@ -5848,10 +6230,12 @@ "x-deserialize-skip-invalid-items": true }, "rawInput": { - "description": "Raw input parameters sent to the tool." + "description": "Raw input parameters sent to the tool.", + "x-deserialize-default-on-error": true }, "rawOutput": { - "description": "Raw output returned by the tool." + "description": "Raw output returned by the tool.", + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -5859,6 +6243,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5897,6 +6282,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -5965,6 +6351,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6042,7 +6429,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented as structured entries.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6065,11 +6452,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id", + "planId", "entries" ] }, @@ -6077,7 +6465,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented by a file URI.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6095,11 +6483,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id", + "planId", "uri" ] }, @@ -6107,7 +6496,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nA plan represented as raw markdown content.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to update.", "allOf": [ { @@ -6125,11 +6514,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id", + "planId", "content" ] }, @@ -6151,6 +6541,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6162,7 +6553,7 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRemoval notice for a plan identified by ID.", "type": "object", "properties": { - "id": { + "planId": { "description": "The plan ID to remove.", "allOf": [ { @@ -6176,11 +6567,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id" + "planId" ] }, "AvailableCommand": { @@ -6213,6 +6605,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6249,6 +6642,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6275,6 +6669,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6300,6 +6695,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6326,6 +6722,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6342,14 +6739,16 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "updatedAt": { "description": "ISO 8601 timestamp of last activity. Set to null to clear.", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -6357,6 +6756,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -6380,6 +6780,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6422,6 +6823,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6448,6 +6850,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6479,6 +6882,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true }, "_meta": { @@ -6487,6 +6891,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6739,6 +7144,7 @@ }, "clientCapabilities": { "description": "Capabilities supported by the client.", + "x-deserialize-default-on-error": true, "default": { "fs": { "readTextFile": false, @@ -6773,6 +7179,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -6788,6 +7195,7 @@ "properties": { "fs": { "description": "File system capabilities supported by the client.\nDetermines which file operations the agent can request.", + "x-deserialize-default-on-error": true, "default": { "readTextFile": false, "writeTextFile": false @@ -6801,10 +7209,11 @@ "terminal": { "description": "Whether the Client support all `terminal/*` methods.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "session": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSession-related capabilities supported by the client.", + "description": "Session-related capabilities supported by the client.\n\nOptional. Omitted or `null` both mean the client does not advertise any\nsession-related extensions.", "anyOf": [ { "$ref": "#/$defs/ClientSessionCapabilities" @@ -6816,7 +7225,7 @@ "x-deserialize-default-on-error": true }, "plan": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the client supports `plan_update` and `plan_removed` session updates.\n\nOptional. Omitted means the client does not advertise support.\nSupplying `{}` means the client can receive both update types.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the client supports `plan_update` and `plan_removed` session updates.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client can receive both update types.", "anyOf": [ { "$ref": "#/$defs/PlanCapabilities" @@ -6829,6 +7238,7 @@ }, "auth": { "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAuthentication capabilities supported by the client.\nDetermines which authentication method types the agent may include\nin its `InitializeResponse`.", + "x-deserialize-default-on-error": true, "default": { "terminal": false }, @@ -6839,7 +7249,7 @@ ] }, "elicitation": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nElicitation capabilities supported by the client.\nDetermines which elicitation modes the agent may use.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nElicitation capabilities supported by the client.\nDetermines which elicitation modes the agent may use.\n\nOptional. Omitted or `null` both mean the client does not advertise\nelicitation support.", "anyOf": [ { "$ref": "#/$defs/ElicitationCapabilities" @@ -6851,7 +7261,7 @@ "x-deserialize-default-on-error": true }, "nes": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client.\n\nOptional. Omitted or `null` both mean the client does not advertise any\nNES suggestion-kind extensions.", "anyOf": [ { "$ref": "#/$defs/ClientNesCapabilities" @@ -6877,6 +7287,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -6888,12 +7299,14 @@ "readTextFile": { "description": "Whether the Client supports `fs/read_text_file` requests.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "writeTextFile": { "description": "Whether the Client supports `fs/write_text_file` requests.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -6901,16 +7314,17 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "ClientSessionCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSession-related capabilities supported by the client.", + "description": "Session-related capabilities supported by the client.", "type": "object", "properties": { "configOptions": { - "description": "Config option capabilities supported by the client.\n\nOmitted or `null` means the client does not advertise support for any\nconfig option extensions.", + "description": "Config option capabilities supported by the client.\n\nOmitted or `null` both mean the client does not advertise support for any\nconfig option extensions.", "anyOf": [ { "$ref": "#/$defs/SessionConfigOptionsCapabilities" @@ -6927,16 +7341,17 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "SessionConfigOptionsCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSession configuration option capabilities supported by the client.", + "description": "Session configuration option capabilities supported by the client.", "type": "object", "properties": { "boolean": { - "description": "Whether the client supports boolean session configuration options.\n\nOmitted or `null` means the client does not advertise support.\nSupplying `{}` means agents may include `type: \"boolean\"` entries in\n`configOptions`, and the client may send `session/set_config_option`\nrequests with `type: \"boolean\"` and a boolean `value`.", + "description": "Whether the client supports boolean session configuration options.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means agents may include `type: \"boolean\"` entries in\n`configOptions`, and the client may send `session/set_config_option`\nrequests with `type: \"boolean\"` and a boolean `value`.", "anyOf": [ { "$ref": "#/$defs/BooleanConfigOptionCapabilities" @@ -6953,12 +7368,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "BooleanConfigOptionCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nCapabilities for boolean session configuration options.\n\nSupplying `{}` means the client supports boolean session configuration options.", + "description": "Capabilities for boolean session configuration options.\n\nSupplying `{}` means the client supports boolean session configuration options.", "type": "object", "properties": { "_meta": { @@ -6967,6 +7383,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -6981,6 +7398,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -6992,7 +7410,8 @@ "terminal": { "description": "Whether the client supports `terminal` authentication methods.\n\nWhen `true`, the agent may include `terminal` entries in its authentication methods.", "type": "boolean", - "default": false + "default": false, + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -7000,6 +7419,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7009,7 +7429,7 @@ "type": "object", "properties": { "form": { - "description": "Whether the client supports form-based elicitation.", + "description": "Whether the client supports form-based elicitation.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client supports form-based elicitation.", "anyOf": [ { "$ref": "#/$defs/ElicitationFormCapabilities" @@ -7021,7 +7441,7 @@ "x-deserialize-default-on-error": true }, "url": { - "description": "Whether the client supports URL-based elicitation.", + "description": "Whether the client supports URL-based elicitation.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client supports URL-based elicitation.", "anyOf": [ { "$ref": "#/$defs/ElicitationUrlCapabilities" @@ -7038,12 +7458,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "ElicitationFormCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nForm-based elicitation capabilities.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nForm-based elicitation capabilities.\n\nSupplying `{}` means the client supports form-based elicitation.", "type": "object", "properties": { "_meta": { @@ -7052,12 +7473,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } }, "ElicitationUrlCapabilities": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nURL-based elicitation capabilities.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nURL-based elicitation capabilities.\n\nSupplying `{}` means the client supports URL-based elicitation.", "type": "object", "properties": { "_meta": { @@ -7066,6 +7488,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7116,6 +7539,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7130,6 +7554,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7144,6 +7569,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7158,6 +7584,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -7180,6 +7607,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7199,6 +7627,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7206,12 +7635,16 @@ "x-method": "providers/list" }, "SetProviderRequest": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider id.", + "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/set`.\n\nReplaces the full configuration for one provider ID.", "type": "object", "properties": { - "id": { - "description": "Provider id to configure.", - "type": "string" + "providerId": { + "description": "Provider ID to configure.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "apiType": { "description": "Protocol type for this provider.", @@ -7238,11 +7671,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id", + "providerId", "apiType", "baseUrl" ], @@ -7253,9 +7687,13 @@ "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nRequest parameters for `providers/disable`.", "type": "object", "properties": { - "id": { - "description": "Provider id to disable.", - "type": "string" + "providerId": { + "description": "Provider ID to disable.", + "allOf": [ + { + "$ref": "#/$defs/ProviderId" + } + ] }, "_meta": { "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/extensibility)", @@ -7263,11 +7701,12 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ - "id" + "providerId" ], "x-side": "agent", "x-method": "providers/disable" @@ -7282,6 +7721,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7301,14 +7741,18 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "mcpServers": { "description": "List of MCP (Model Context Protocol) servers the agent should connect to.", "type": "array", "items": { "$ref": "#/$defs/McpServer" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -7316,6 +7760,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7412,6 +7857,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7445,6 +7891,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7479,6 +7926,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7496,7 +7944,7 @@ "description": "Human-readable name identifying this MCP server.", "type": "string" }, - "id": { + "serverId": { "description": "Unique identifier for this MCP server, generated by the component providing it.\n\nProviders MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible\non the same ACP connection.", "allOf": [ { @@ -7510,12 +7958,13 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, "required": [ "name", - "id" + "serverId" ] }, "McpServerStdio": { @@ -7527,7 +7976,7 @@ "type": "string" }, "command": { - "description": "Path to the MCP server executable.", + "description": "Absolute path to the MCP server executable.", "type": "string" }, "args": { @@ -7550,6 +7999,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7569,10 +8019,12 @@ "type": "array", "items": { "$ref": "#/$defs/McpServer" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "cwd": { - "description": "The working directory for this session.", + "description": "The working directory for this session. Must be an absolute path.", "type": "string" }, "additionalDirectories": { @@ -7580,7 +8032,9 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "sessionId": { "description": "The ID of the session to load.", @@ -7596,6 +8050,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7631,6 +8086,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7655,6 +8111,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7677,7 +8134,7 @@ ] }, "cwd": { - "description": "The working directory for this session.", + "description": "The working directory for this session. Must be an absolute path.", "type": "string" }, "additionalDirectories": { @@ -7685,14 +8142,18 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "mcpServers": { "description": "List of MCP servers to connect to for this session.", "type": "array", "items": { "$ref": "#/$defs/McpServer" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -7700,6 +8161,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7723,7 +8185,7 @@ ] }, "cwd": { - "description": "The working directory for this session.", + "description": "The working directory for this session. Must be an absolute path.", "type": "string" }, "additionalDirectories": { @@ -7731,14 +8193,18 @@ "type": "array", "items": { "type": "string" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "mcpServers": { "description": "List of MCP servers to connect to for this session.", "type": "array", "items": { "$ref": "#/$defs/McpServer" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -7746,6 +8212,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7774,6 +8241,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7809,6 +8277,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7845,6 +8314,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7918,6 +8388,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7937,7 +8408,8 @@ "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "workspaceFolders": { "description": "The workspace folders.", @@ -7947,9 +8419,7 @@ ], "items": { "$ref": "#/$defs/WorkspaceFolder" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "repository": { "description": "Repository metadata, if the workspace is a git repository.", @@ -7969,6 +8439,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -7993,6 +8464,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8023,6 +8495,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8070,8 +8543,7 @@ { "type": "null" } - ], - "x-deserialize-default-on-error": true + ] }, "triggerKind": { "description": "What triggered this suggestion request.", @@ -8090,8 +8562,7 @@ { "type": "null" } - ], - "x-deserialize-default-on-error": true + ] }, "_meta": { "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/extensibility)", @@ -8099,6 +8570,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8144,9 +8616,7 @@ ], "items": { "$ref": "#/$defs/NesRecentFile" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "relatedSnippets": { "description": "Related code snippets.", @@ -8156,9 +8626,7 @@ ], "items": { "$ref": "#/$defs/NesRelatedSnippet" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "editHistory": { "description": "Recent edit history.", @@ -8168,9 +8636,7 @@ ], "items": { "$ref": "#/$defs/NesEditHistoryEntry" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "userActions": { "description": "Recent user actions (typing, navigation, etc.).", @@ -8180,9 +8646,7 @@ ], "items": { "$ref": "#/$defs/NesUserAction" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "openFiles": { "description": "Currently open files in the editor.", @@ -8192,9 +8656,7 @@ ], "items": { "$ref": "#/$defs/NesOpenFile" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "diagnostics": { "description": "Current diagnostics (errors, warnings).", @@ -8204,9 +8666,7 @@ ], "items": { "$ref": "#/$defs/NesDiagnostic" - }, - "x-deserialize-default-on-error": true, - "x-deserialize-skip-invalid-items": true + } }, "_meta": { "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/extensibility)", @@ -8214,6 +8674,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -8240,6 +8701,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8270,6 +8732,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8304,6 +8767,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8331,6 +8795,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8371,6 +8836,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8421,6 +8887,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8463,6 +8930,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8516,6 +8984,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8644,20 +9113,20 @@ ] }, { - "title": "ExtMethodResponse", - "description": "Successful result returned by an extension method outside the core ACP method set.", + "title": "MessageMcpResponse", + "description": "Successful result returned by an MCP-over-ACP `mcp/message` request.", "allOf": [ { - "$ref": "#/$defs/ExtResponse" + "$ref": "#/$defs/MessageMcpResponse" } ] }, { - "title": "MessageMcpResponse", - "description": "Successful result returned by an MCP-over-ACP `mcp/message` request.", + "title": "ExtMethodResponse", + "description": "Successful result returned by an extension method outside the core ACP method set.", "allOf": [ { - "$ref": "#/$defs/MessageMcpResponse" + "$ref": "#/$defs/ExtResponse" } ] } @@ -8709,6 +9178,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8729,6 +9199,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8756,6 +9227,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8822,6 +9294,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8847,6 +9320,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8877,7 +9351,8 @@ { "type": "null" } - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -8885,6 +9360,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8906,14 +9382,16 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "signal": { "description": "The signal that terminated the process (may be null if exited normally).", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -8921,6 +9399,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } } @@ -8935,6 +9414,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8952,14 +9432,16 @@ "null" ], "format": "uint32", - "minimum": 0 + "minimum": 0, + "x-deserialize-default-on-error": true }, "signal": { "description": "The signal that terminated the process (may be null if exited normally).", "type": [ "string", "null" - ] + ], + "x-deserialize-default-on-error": true }, "_meta": { "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/extensibility)", @@ -8967,6 +9449,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8983,6 +9466,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -8999,10 +9483,11 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, - "oneOf": [ + "anyOf": [ { "description": "The user accepted and provided content.", "type": "object", @@ -9046,6 +9531,61 @@ "required": [ "action" ] + }, + { + "title": "other", + "description": "Custom or future elicitation action.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.\n\nAgents that do not understand this action should preserve the raw\npayload when storing, replaying, proxying, or forwarding elicitation\nresponses. They MUST NOT treat it as a known elicitation action.", + "type": "object", + "properties": { + "action": { + "description": "Custom or future elicitation action.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants.", + "type": "string" + } + }, + "required": [ + "action" + ], + "not": { + "anyOf": [ + { + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "accept" + } + }, + "required": [ + "action" + ] + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "decline" + } + }, + "required": [ + "action" + ] + }, + { + "type": "object", + "properties": { + "action": { + "type": "string", + "const": "cancel" + } + }, + "required": [ + "action" + ] + } + ] + }, + "additionalProperties": true } ], "discriminator": { @@ -9123,6 +9663,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9142,6 +9683,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9283,6 +9825,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9327,6 +9870,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9366,7 +9910,9 @@ "type": "array", "items": { "$ref": "#/$defs/TextDocumentContentChangeEvent" - } + }, + "x-deserialize-default-on-error": true, + "x-deserialize-skip-invalid-items": true }, "_meta": { "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/extensibility)", @@ -9374,6 +9920,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9411,6 +9958,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9440,6 +9988,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9472,6 +10021,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9525,6 +10075,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9552,7 +10103,11 @@ }, "id": { "description": "The ID of the accepted suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "_meta": { "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/extensibility)", @@ -9560,6 +10115,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9584,7 +10140,11 @@ }, "id": { "description": "The ID of the rejected suggestion.", - "type": "string" + "allOf": [ + { + "$ref": "#/$defs/NesSuggestionId" + } + ] }, "reason": { "description": "The reason for rejection.", @@ -9604,6 +10164,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, @@ -9640,7 +10201,7 @@ ] }, "CancelRequestNotification": { - "description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNotification to cancel an ongoing request.\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/cancellation)", + "description": "Notification to cancel an ongoing request.\n\nSee protocol docs: [Cancellation](https://agentclientprotocol.com/protocol/cancellation)", "type": "object", "properties": { "requestId": { @@ -9657,6 +10218,7 @@ "object", "null" ], + "x-deserialize-default-on-error": true, "additionalProperties": true } }, diff --git a/scripts/gen_all.py b/scripts/gen_all.py index de1cb52..53d4fe9 100644 --- a/scripts/gen_all.py +++ b/scripts/gen_all.py @@ -140,8 +140,8 @@ def download_schema(repo: str, ref: str) -> None: print(exc, file=sys.stderr) sys.exit(1) - SCHEMA_JSON.write_text(json.dumps(schema_data, indent=2), encoding="utf-8") - META_JSON.write_text(json.dumps(meta_data, indent=2), encoding="utf-8") + SCHEMA_JSON.write_text(json.dumps(schema_data, indent=2) + "\n", encoding="utf-8") + META_JSON.write_text(json.dumps(meta_data, indent=2) + "\n", encoding="utf-8") VERSION_FILE.write_text(ref + "\n", encoding="utf-8") print(f"Fetched schema and meta from {repo}@{ref}") diff --git a/scripts/gen_schema.py b/scripts/gen_schema.py index 571ad34..936f6a4 100644 --- a/scripts/gen_schema.py +++ b/scripts/gen_schema.py @@ -85,9 +85,11 @@ "CreateElicitationRequest2": "CreateFormRequestElicitationRequest", "CreateElicitationRequest3": "CreateUrlSessionElicitationRequest", "CreateElicitationRequest4": "CreateUrlRequestElicitationRequest", + "CreateElicitationRequest5": "CreateOtherElicitationRequest", "CreateElicitationResponse1": "AcceptElicitationResponse", "CreateElicitationResponse2": "DeclineElicitationResponse", "CreateElicitationResponse3": "CancelElicitationResponse", + "CreateElicitationResponse4": "OtherElicitationResponse", "ElicitationFormMode1": "ElicitationFormSessionMode", "ElicitationFormMode2": "ElicitationFormRequestMode", "ElicitationPropertySchema1": "ElicitationStringPropertySchema", @@ -95,6 +97,9 @@ "ElicitationPropertySchema3": "ElicitationIntegerPropertySchema", "ElicitationPropertySchema4": "ElicitationBooleanPropertySchema", "ElicitationPropertySchema5": "ElicitationMultiSelectPropertySchema", + "ElicitationPropertySchema6": "ElicitationOtherPropertySchema", + "MultiSelectItems1": "StringMultiSelectItems", + "MultiSelectItems2": "OtherMultiSelectItems", "ElicitationUrlMode1": "ElicitationUrlSessionMode", "ElicitationUrlMode2": "ElicitationUrlRequestMode", "NesSuggestion1": "NesEditSuggestionVariant", @@ -103,6 +108,21 @@ "NesSuggestion4": "NesSearchAndReplaceSuggestionVariant", } +# Extensible ("custom or future") unions: known const-tagged variants plus a +# catch-all member tagged `"title": "other"`. _normalize_catchall_unions strips the +# discriminator and the catch-all's `not` clause so datamodel-codegen produces a plain +# union; the exclusion is restored at runtime by a field_validator injected into the +# catch-all class, so a malformed known variant fails instead of silently parsing as +# custom (mirrors the TypeScript SDK's excludeKnownTags). Maps union def name -> +# catch-all class name; the set is asserted against the schema in +# _validate_schema_alignment. +EXTENSIBLE_UNIONS: dict[str, str] = { + "CreateElicitationRequest": "CreateOtherElicitationRequest", + "CreateElicitationResponse": "OtherElicitationResponse", + "ElicitationPropertySchema": "ElicitationOtherPropertySchema", + "MultiSelectItems": "OtherMultiSelectItems", +} + ENUM_LITERAL_MAP: dict[str, tuple[str, ...]] = { "PermissionOptionKind": ( "allow_once", @@ -257,10 +277,45 @@ def _load_schema() -> dict[str, Any]: def _preprocess_schema_for_codegen(schema: dict[str, Any]) -> dict[str, Any]: + schema = _normalize_catchall_unions(schema) defs = schema.get("$defs", {}) return _distribute_composed_object_schemas(schema, defs) +def _normalize_catchall_unions(node: Any) -> Any: + # ACP "custom or future" unions include a member tagged `"title": "other"` whose + # discriminator (type/mode/action) is a free-form string. datamodel-codegen cannot + # put that in a discriminated union, so it emits `#-special-#` placeholder literals. + # Drop the discriminator (the union is then validated structurally) and collapse the + # catch-all to a permissive object so unknown variants round-trip their raw payload. + if isinstance(node, list): + return [_normalize_catchall_unions(item) for item in node] + if not isinstance(node, dict): + return node + + transformed = {key: _normalize_catchall_unions(value) for key, value in node.items()} + for combinator in COMBINATOR_KEYS: + members = transformed.get(combinator) + if not isinstance(members, list): + continue + if not any(isinstance(member, dict) and member.get("title") == "other" for member in members): + continue + transformed.pop("discriminator", None) + transformed[combinator] = [ + _collapse_catchall_member(member) if isinstance(member, dict) and member.get("title") == "other" else member + for member in members + ] + return transformed + + +def _collapse_catchall_member(member: dict[str, Any]) -> dict[str, Any]: + collapsed: dict[str, Any] = {"type": "object", "additionalProperties": True} + for key in ("title", "description", "properties", "required"): + if key in member: + collapsed[key] = member[key] + return collapsed + + def _distribute_composed_object_schemas(node: Any, defs: dict[str, Any]) -> Any: if isinstance(node, list): return [_distribute_composed_object_schemas(item, defs) for item in node] @@ -429,6 +484,7 @@ def postprocess_generated_schema(output_path: Path) -> list[str]: _ProcessingStep("attach description comments", _add_description_comments), _ProcessingStep("ensure custom BaseModel", _ensure_custom_base_model), _ProcessingStep("inject field validators", _inject_field_validators), + _ProcessingStep("inject deserialize defaults", _inject_deserialize_defaults), _ProcessingStep("inject schema aliases", _inject_schema_aliases), ) @@ -522,9 +578,29 @@ def _validate_schema_alignment() -> list[str]: warnings.append( f"Enum mismatch for '{enum_name}': schema.json -> {schema_values}, generated aliases -> {expected_values}" ) + + detected_unions = _detect_extensible_unions() + if detected_unions != set(EXTENSIBLE_UNIONS): + warnings.append( + f"Extensible union drift: schema defines {sorted(detected_unions)}, " + f"EXTENSIBLE_UNIONS lists {sorted(EXTENSIBLE_UNIONS)}. Update EXTENSIBLE_UNIONS, the " + "RENAME_MAP catch-all names, and the alias template together." + ) return warnings +def _detect_extensible_unions() -> set[str]: + defs = _load_schema().get("$defs", {}) + detected: set[str] = set() + for name, definition in defs.items(): + if not isinstance(definition, dict) or "discriminator" not in definition: + continue + members = definition.get("anyOf") or definition.get("oneOf") or [] + if any(isinstance(member, dict) and member.get("title") == "other" for member in members): + detected.add(name) + return detected + + def _load_schema_enum_literals() -> dict[str, tuple[str, ...]]: schema_data = json.loads(SCHEMA_JSON.read_text(encoding="utf-8")) defs = schema_data.get("$defs", {}) @@ -600,9 +676,58 @@ def _ensure_pydantic_import(content: str, name: str) -> str: return content +def _extensible_union_excluded_tags(union_def: dict[str, Any], discriminator: str) -> tuple[str, ...]: + members = union_def.get("anyOf") or union_def.get("oneOf") or [] + other = next((member for member in members if isinstance(member, dict) and member.get("title") == "other"), None) + if other is None: + return () + tags: list[str] = [] + for excluded in other.get("not", {}).get("anyOf", []): + const = excluded.get("properties", {}).get(discriminator, {}).get("const") + if isinstance(const, str) and const not in tags: + tags.append(const) + return tuple(tags) + + +def _catchall_exclusion_injections() -> list[FieldValidatorInjection]: + defs = _load_schema().get("$defs", {}) + injections: list[FieldValidatorInjection] = [] + for union_name, catchall_class in EXTENSIBLE_UNIONS.items(): + union_def = defs.get(union_name) + if not isinstance(union_def, dict): + continue + discriminator = union_def.get("discriminator", {}).get("propertyName") + if not discriminator: + continue + tags = _extensible_union_excluded_tags(union_def, discriminator) + if not tags: + continue + field = _schema_field_name(discriminator) + injections.append( + FieldValidatorInjection( + class_name=catchall_class, + field_name=field, + method_name=f"_reject_known_{field}", + argument_name="value", + return_type="Any", + comment_lines=( + "Restore the schema's `not` clause dropped for codegen: reject the known", + "variants' discriminator values so a malformed known variant fails instead", + "of silently parsing as this catch-all.", + ), + body_lines=( + f"if value in {tags!r}:", + f' raise ValueError("{field} value is reserved by a known variant")', + "return value", + ), + ) + ) + return injections + + def _inject_field_validators(content: str) -> str: - """Inject field_validator methods into classes listed in CLASS_VALIDATOR_INJECTIONS.""" - for injection in CLASS_VALIDATOR_INJECTIONS: + """Inject field_validator methods for CLASS_VALIDATOR_INJECTIONS and catch-all exclusions.""" + for injection in (*CLASS_VALIDATOR_INJECTIONS, *_catchall_exclusion_injections()): content = _ensure_pydantic_import(content, "field_validator") class_pattern = re.compile( @@ -627,6 +752,118 @@ def _append_validator( return content +def _inject_deserialize_defaults(content: str) -> str: + defs = _load_schema().get("$defs", {}) + + # `_meta` carries x-deserialize-default-on-error on almost every model; handle it once + # on the shared BaseModel with check_fields=False so every subclass inherits the salvage. + meta_validator = ( + '@field_validator("field_meta", mode="wrap", check_fields=False)\n' + "@classmethod\n" + "def _salvage_meta_on_error(cls, value: Any, handler: Any) -> Any:\n" + " return salvage_on_error(value, handler, lambda: None)\n" + ) + content, count = _append_class_method(content, r"class BaseModel\(_BaseModel\):", meta_validator) + if count == 0: + print("Warning: custom BaseModel not found for _meta salvage injection", file=sys.stderr) + + for class_name, definition in defs.items(): + if not isinstance(definition, dict): + continue + salvage_groups, skip_fields = _deserialize_field_specs(definition) + methods: list[str] = [] + for index, (fallback, fields) in enumerate(sorted(salvage_groups.items())): + arguments = ", ".join(f'"{field}"' for field in sorted(fields)) + methods.append( + f'@field_validator({arguments}, mode="wrap")\n' + "@classmethod\n" + f"def _salvage_on_error_{index}(cls, value: Any, handler: Any) -> Any:\n" + f" return salvage_on_error(value, handler, {fallback})\n" + ) + for index, field in enumerate(sorted(skip_fields)): + methods.append( + f'@field_validator("{field}", mode="wrap")\n' + "@classmethod\n" + f"def _skip_invalid_items_{index}(cls, value: Any, handler: Any) -> Any:\n" + " return skip_invalid_items(value, handler)\n" + ) + # A plain object $def renders as `class Name(BaseModel)` (or `_Name` after a + # collision rename). A union $def has no class of its own; its common properties + # distribute to the member variant classes, so target those instead. + targets = [rf"class _?{re.escape(class_name)}\(BaseModel\):"] + members = _union_member_classes(class_name) + if members: + targets = [rf"class {re.escape(member)}\(\w+\):" for member in members] + for method in methods: + for target in targets: + content, count = _append_class_method(content, target, method) + if count == 0: + print(f"Warning: no class matched {target!r} for deserialize injection", file=sys.stderr) + + content = _ensure_pydantic_import(content, "field_validator") + return _ensure_deserialize_import(content) + + +def _union_member_classes(union_name: str) -> list[str]: + return [new for old, new in RENAME_MAP.items() if re.fullmatch(rf"{re.escape(union_name)}\d+", old)] + + +def _deserialize_field_specs(definition: dict[str, Any]) -> tuple[dict[str, list[str]], list[str]]: + """Return ({fallback_expr: [field, ...]}, [skip_field, ...]) for a $def. `_meta` is handled + on the shared BaseModel and excluded here.""" + required = set(definition.get("required", [])) + salvage: dict[str, list[str]] = {} + skip: list[str] = [] + for prop_name, prop in definition.get("properties", {}).items(): + if not isinstance(prop, dict) or prop_name == "_meta": + continue + field = _schema_field_name(prop_name) + if prop.get("x-deserialize-skip-invalid-items"): + skip.append(field) + elif prop.get("x-deserialize-default-on-error"): + salvage.setdefault(_fallback_expression(prop, prop_name in required), []).append(field) + return salvage, skip + + +def _fallback_expression(prop: dict[str, Any], is_required: bool) -> str: + if "default" in prop: + return f"lambda: {prop['default']!r}" + if _is_array_schema(prop) and (is_required or not _schema_allows_null(prop, {})): + return "lambda: []" + return "lambda: None" + + +def _is_array_schema(prop: dict[str, Any]) -> bool: + prop_type = prop.get("type") + if prop_type == "array" or (isinstance(prop_type, list) and "array" in prop_type): + return True + return "items" in prop + + +def _append_class_method(content: str, header_pattern: str, method_text: str) -> tuple[str, int]: + pattern = re.compile(rf"({header_pattern})(.*?)(?=\nclass |\Z)", re.DOTALL) + + def _append(match: re.Match[str]) -> str: + indented = "\n" + textwrap.indent(method_text, " ") + return match.group(1) + match.group(2) + indented + "\n" + + return pattern.subn(_append, content, count=1) + + +def _ensure_deserialize_import(content: str) -> str: + # Absolute import (not relative): gen_signature.py loads schema.py as a standalone + # module with no package context, where `from ._deserialize` cannot resolve. + statement = "from acp._deserialize import salvage_on_error, skip_invalid_items" + if statement in content: + return content + lines = content.splitlines() + for idx, line in enumerate(lines): + if line.startswith("from pydantic import "): + lines.insert(idx + 1, statement) + return "\n".join(lines) + "\n" + return content + + def _inject_schema_aliases(content: str) -> str: if "CreateElicitationRequest = Union[" in content: return content @@ -649,11 +886,13 @@ def _inject_schema_aliases(content: str) -> str: CreateElicitationRequest = Union[ CreateFormElicitationRequest, CreateUrlElicitationRequest, + CreateOtherElicitationRequest, ] CreateElicitationResponse = Union[ AcceptElicitationResponse, DeclineElicitationResponse, CancelElicitationResponse, + OtherElicitationResponse, ] """) pattern = re.compile( diff --git a/src/acp/__init__.py b/src/acp/__init__.py index d343529..e6cd484 100644 --- a/src/acp/__init__.py +++ b/src/acp/__init__.py @@ -49,6 +49,7 @@ CreateFormElicitationRequest, CreateFormRequestElicitationRequest, CreateFormSessionElicitationRequest, + CreateOtherElicitationRequest, CreateTerminalRequest, CreateTerminalResponse, CreateUrlElicitationRequest, @@ -64,6 +65,7 @@ ElicitationMode, ElicitationMultiSelectPropertySchema, ElicitationNumberPropertySchema, + ElicitationOtherPropertySchema, ElicitationSchema, ElicitationStringPropertySchema, ElicitationUrlCapabilities, @@ -77,6 +79,7 @@ LoadSessionResponse, NewSessionRequest, NewSessionResponse, + OtherElicitationResponse, PromptRequest, PromptResponse, ReadTextFileRequest, @@ -156,6 +159,7 @@ "ElicitationIntegerPropertySchema", "ElicitationBooleanPropertySchema", "ElicitationMultiSelectPropertySchema", + "ElicitationOtherPropertySchema", "CreateElicitationRequest", "CreateElicitationResponse", "CreateFormElicitationRequest", @@ -164,9 +168,11 @@ "CreateUrlElicitationRequest", "CreateUrlSessionElicitationRequest", "CreateUrlRequestElicitationRequest", + "CreateOtherElicitationRequest", "AcceptElicitationResponse", "DeclineElicitationResponse", "CancelElicitationResponse", + "OtherElicitationResponse", "CompleteElicitationNotification", # terminal types "CreateTerminalRequest", diff --git a/src/acp/_deserialize.py b/src/acp/_deserialize.py new file mode 100644 index 0000000..1cc2ea9 --- /dev/null +++ b/src/acp/_deserialize.py @@ -0,0 +1,43 @@ +"""Runtime helpers that restore the lenient-deserialization semantics the ACP schema +declares via ``x-deserialize-default-on-error`` and ``x-deserialize-skip-invalid-items`` +but that generated Pydantic models cannot express directly. + +Referenced by ``field_validator`` methods that ``scripts/gen_schema.py`` injects into +``schema.py``. Mirrors the TypeScript SDK's ``src/schema-deserialize.ts``. +""" + +from __future__ import annotations + +from collections.abc import Callable +from typing import Any + +from pydantic import ValidationError + + +def salvage_on_error(value: Any, handler: Callable[[Any], Any], fallback: Callable[[], Any]) -> Any: + """Return ``fallback()`` when ``value`` fails validation, otherwise the validated value. + + Restores ``x-deserialize-default-on-error``: a malformed non-critical field is replaced + with its default rather than failing the whole payload. + """ + try: + return handler(value) + except ValidationError: + return fallback() + + +def skip_invalid_items(value: Any, handler: Callable[[Any], Any]) -> Any: + """Drop array items that fail validation instead of failing the whole array. + + Restores ``x-deserialize-skip-invalid-items``. Each item is validated through the field's + own list handler, so item coercion and salvaging still apply to the survivors. + """ + if not isinstance(value, list): + return handler(value) + salvaged: list[Any] = [] + for item in value: + try: + salvaged.append(handler([item])[0]) + except ValidationError: + continue + return salvaged diff --git a/src/acp/client/router.py b/src/acp/client/router.py index c8b26bb..204a67b 100644 --- a/src/acp/client/router.py +++ b/src/acp/client/router.py @@ -11,11 +11,9 @@ from ..schema import ( CompleteElicitationNotification, CreateElicitationRequest, - CreateFormElicitationRequest, CreateFormRequestElicitationRequest, CreateFormSessionElicitationRequest, CreateTerminalRequest, - CreateUrlElicitationRequest, CreateUrlRequestElicitationRequest, CreateUrlSessionElicitationRequest, ElicitationFormRequestMode, @@ -37,12 +35,12 @@ _CREATE_ELICITATION_REQUEST_ADAPTER = TypeAdapter(CreateElicitationRequest) -def _validate_create_elicitation_request(params: Any) -> CreateFormElicitationRequest | CreateUrlElicitationRequest: +def _validate_create_elicitation_request(params: Any) -> CreateElicitationRequest: return _CREATE_ELICITATION_REQUEST_ADAPTER.validate_python(params) def _mode_from_create_elicitation_request( - request: CreateFormElicitationRequest | CreateUrlElicitationRequest, + request: CreateElicitationRequest, ) -> ElicitationFormSessionMode | ElicitationFormRequestMode | ElicitationUrlSessionMode | ElicitationUrlRequestMode: if isinstance(request, CreateFormSessionElicitationRequest): return ElicitationFormSessionMode( @@ -69,7 +67,7 @@ def _mode_from_create_elicitation_request( elicitation_id=request.elicitation_id, url=request.url, ) - raise TypeError(f"Unsupported elicitation request: {type(request).__name__}") + raise RequestError.invalid_params({"details": f"Unsupported elicitation mode: {request.mode!r}"}) def _make_create_elicitation_handler(client: Client) -> Any: diff --git a/src/acp/meta.py b/src/acp/meta.py index a7bd914..9c883c5 100644 --- a/src/acp/meta.py +++ b/src/acp/meta.py @@ -1,5 +1,5 @@ # Generated from schema/meta.json. Do not edit by hand. -# Schema ref: refs/tags/schema-v1.16.0 +# Schema ref: refs/tags/schema-v1.19.0 AGENT_METHODS = { "initialize": "initialize", "authenticate": "authenticate", diff --git a/src/acp/schema.py b/src/acp/schema.py index 6b74f46..e2dd1f1 100644 --- a/src/acp/schema.py +++ b/src/acp/schema.py @@ -1,12 +1,13 @@ # Generated from schema/schema.json. Do not edit by hand. -# Schema ref: refs/tags/schema-v1.16.0 +# Schema ref: refs/tags/schema-v1.19.0 from __future__ import annotations from enum import Enum from typing import Annotated, Any, Dict, List, Literal, Optional, Union -from pydantic import AnyUrl, BaseModel as _BaseModel, Field, RootModel, ConfigDict, field_validator +from pydantic import AnyUrl, BaseModel as _BaseModel, ConfigDict, Field, RootModel, field_validator +from acp._deserialize import salvage_on_error, skip_invalid_items PermissionOptionKind = Literal["allow_once", "allow_always", "reject_once", "reject_always"] PlanEntryPriority = Literal["high", "medium", "low"] @@ -25,6 +26,11 @@ def __getattr__(self, item: str) -> Any: return getattr(self, snake_cased) raise AttributeError(f"'{type(self).__name__}' object has no attribute '{item}'") + @field_validator("field_meta", mode="wrap", check_fields=False) + @classmethod + def _salvage_meta_on_error(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class Jsonrpc(Enum): field_2_0 = "2.0" @@ -55,6 +61,11 @@ class ReadTextFileRequest(BaseModel): ), ] = None + @field_validator("limit", "line", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class TextResourceContents(BaseModel): # MIME type describing the encoded media payload. @@ -82,6 +93,11 @@ class TextResourceContents(BaseModel): ), ] = None + @field_validator("mime_type", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class BlobResourceContents(BaseModel): # Base64-encoded bytes for a binary resource payload. @@ -109,10 +125,15 @@ class BlobResourceContents(BaseModel): ), ] = None + @field_validator("mime_type", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class Diff(BaseModel): - # The file path being modified. - path: Annotated[str, Field(description="The file path being modified.")] + # The absolute file path being modified. + path: Annotated[str, Field(description="The absolute file path being modified.")] # The original content (None for new files). old_text: Annotated[ Optional[str], @@ -133,6 +154,11 @@ class Diff(BaseModel): ), ] = None + @field_validator("old_text", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class Terminal(BaseModel): # Identifier of the terminal instance to embed in the content stream. @@ -158,8 +184,8 @@ class Terminal(BaseModel): class ToolCallLocation(BaseModel): - # The file path being accessed or modified. - path: Annotated[str, Field(description="The file path being accessed or modified.")] + # The absolute file path being accessed or modified. + path: Annotated[str, Field(description="The absolute file path being accessed or modified.")] # Optional line number within the file. line: Annotated[Optional[int], Field(description="Optional line number within the file.", ge=0)] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -175,6 +201,11 @@ class ToolCallLocation(BaseModel): ), ] = None + @field_validator("line", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class EnvVariable(BaseModel): # The name of the environment variable. @@ -277,6 +308,50 @@ class KillTerminalRequest(BaseModel): ] = None +class CreateOtherElicitationRequest(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + # A human-readable message describing what input is needed. + message: Annotated[ + str, + Field(description="A human-readable message describing what input is needed."), + ] + # The _meta property is reserved by ACP to allow clients and agents to attach additional + # metadata to their interactions. Implementations MUST NOT make assumptions about values at + # these keys. + # + # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + field_meta: Annotated[ + Optional[Dict[str, Any]], + Field( + alias="_meta", + 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/extensibility)", + ), + ] = None + # Custom or future elicitation mode. + # + # Values beginning with `_` are reserved for implementation-specific + # extensions. Unknown values that do not begin with `_` are reserved for + # future ACP variants. + mode: Annotated[ + str, + Field( + description="Custom or future elicitation mode.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants." + ), + ] + + @field_validator("mode", mode="before") + @classmethod + def _reject_known_mode(cls, value: Any) -> Any: + # Restore the schema's `not` clause dropped for codegen: reject the known + # variants' discriminator values so a malformed known variant fails instead + # of silently parsing as this catch-all. + if value in ("form", "url"): + raise ValueError("mode value is reserved by a known variant") + return value + + class ElicitationSessionScope(BaseModel): # The session this elicitation is tied to. session_id: Annotated[ @@ -289,6 +364,11 @@ class ElicitationSessionScope(BaseModel): Field(alias="toolCallId", description="Optional tool call within the session."), ] = None + @field_validator("tool_call_id", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ElicitationRequestScope(BaseModel): # The request this elicitation is tied to. @@ -298,11 +378,40 @@ class ElicitationRequestScope(BaseModel): ] +class ElicitationOtherPropertySchema(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + # Custom or future elicitation property schema type. + # + # Values beginning with `_` are reserved for implementation-specific + # extensions. Unknown values that do not begin with `_` are reserved for + # future ACP variants. + type: Annotated[ + str, + Field( + description="Custom or future elicitation property schema type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants." + ), + ] + + @field_validator("type", mode="before") + @classmethod + def _reject_known_type(cls, value: Any) -> Any: + # Restore the schema's `not` clause dropped for codegen: reject the known + # variants' discriminator values so a malformed known variant fails instead + # of silently parsing as this catch-all. + if value in ("string", "number", "integer", "boolean", "array"): + raise ValueError("type value is reserved by a known variant") + return value + + class EnumOption(BaseModel): # The constant value for this option. const: Annotated[str, Field(description="The constant value for this option.")] # Human-readable title for this option. title: Annotated[str, Field(description="Human-readable title for this option.")] + # Human-readable description. + description: Annotated[Optional[str], Field(description="Human-readable description.")] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at # these keys. @@ -316,6 +425,11 @@ class EnumOption(BaseModel): ), ] = None + @field_validator("description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class StringPropertySchema(BaseModel): # Optional title for the property. @@ -364,6 +478,11 @@ class StringPropertySchema(BaseModel): ), ] = None + @field_validator("default", "description", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NumberPropertySchema(BaseModel): # Optional title for the property. @@ -389,6 +508,11 @@ class NumberPropertySchema(BaseModel): ), ] = None + @field_validator("default", "description", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class IntegerPropertySchema(BaseModel): # Optional title for the property. @@ -414,6 +538,11 @@ class IntegerPropertySchema(BaseModel): ), ] = None + @field_validator("default", "description", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class BooleanPropertySchema(BaseModel): # Optional title for the property. @@ -435,6 +564,55 @@ class BooleanPropertySchema(BaseModel): ), ] = None + @field_validator("default", "description", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + +class OtherMultiSelectItems(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + # Custom or future multi-select item type. + # + # Values beginning with `_` are reserved for implementation-specific + # extensions. Unknown values that do not begin with `_` are reserved for + # future ACP variants. + type: Annotated[ + str, + Field( + description="Custom or future multi-select item type.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants." + ), + ] + + @field_validator("type", mode="before") + @classmethod + def _reject_known_type(cls, value: Any) -> Any: + # Restore the schema's `not` clause dropped for codegen: reject the known + # variants' discriminator values so a malformed known variant fails instead + # of silently parsing as this catch-all. + if value in ("string",): + raise ValueError("type value is reserved by a known variant") + return value + + +class _StringMultiSelectItems(BaseModel): + # Allowed enum values. + enum: Annotated[List[str], Field(description="Allowed enum values.")] + # The _meta property is reserved by ACP to allow clients and agents to attach additional + # metadata to their interactions. Implementations MUST NOT make assumptions about values at + # these keys. + # + # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + field_meta: Annotated[ + Optional[Dict[str, Any]], + Field( + alias="_meta", + 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/extensibility)", + ), + ] = None + class TitledMultiSelectItems(BaseModel): # Titled enum options. @@ -542,6 +720,11 @@ class PromptCapabilities(BaseModel): ), ] = None + @field_validator("audio", "embedded_context", "image", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + class McpCapabilities(BaseModel): # Agent supports [`McpServer::Http`]. @@ -572,6 +755,11 @@ class McpCapabilities(BaseModel): ), ] = None + @field_validator("acp", "http", "sse", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + class SessionListCapabilities(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -776,6 +964,11 @@ class NesRecentFilesCapabilities(BaseModel): ), ] = None + @field_validator("max_count", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesRelatedSnippetsCapabilities(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -815,6 +1008,11 @@ class NesEditHistoryCapabilities(BaseModel): ), ] = None + @field_validator("max_count", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesUserActionsCapabilities(BaseModel): # Maximum number of user actions the agent can use. @@ -839,6 +1037,11 @@ class NesUserActionsCapabilities(BaseModel): ), ] = None + @field_validator("max_count", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesOpenFilesCapabilities(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -911,6 +1114,21 @@ class AuthEnvVar(BaseModel): ), ] = None + @field_validator("optional", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + + @field_validator("label", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("secret", mode="wrap") + @classmethod + def _salvage_on_error_2(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: True) + class AuthMethodEnvVar(BaseModel): # Unique identifier for this authentication method. @@ -945,6 +1163,16 @@ class AuthMethodEnvVar(BaseModel): ), ] = None + @field_validator("description", "link", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("vars", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class AuthMethodTerminal(BaseModel): # Unique identifier for this authentication method. @@ -979,6 +1207,16 @@ class AuthMethodTerminal(BaseModel): ), ] = None + @field_validator("description", "env", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("args", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class AuthMethodAgent(BaseModel): # Unique identifier for this authentication method. @@ -1003,6 +1241,11 @@ class AuthMethodAgent(BaseModel): ), ] = None + @field_validator("description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class Implementation(BaseModel): # Intended for programmatic or logical use, but can be used as a display @@ -1044,6 +1287,11 @@ class Implementation(BaseModel): ), ] = None + @field_validator("title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AuthenticateResponse(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1063,7 +1311,7 @@ class AuthenticateResponse(BaseModel): class ProviderCurrentConfig(BaseModel): # Protocol currently used by this provider. api_type: Annotated[ - str, + Union[str, Dict[str, Any]], Field(alias="apiType", description="Protocol currently used by this provider."), ] # Base URL currently used by this provider. @@ -1156,6 +1404,11 @@ class SessionMode(BaseModel): ), ] = None + @field_validator("description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class SessionConfigSelectOption(BaseModel): # Unique identifier for this option value. @@ -1177,6 +1430,11 @@ class SessionConfigSelectOption(BaseModel): ), ] = None + @field_validator("description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class SessionConfigBoolean(BaseModel): # The current value of the boolean option. @@ -1226,6 +1484,16 @@ class SessionInfo(BaseModel): ), ] = None + @field_validator("title", "updated_at", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("additional_directories", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class DeleteSessionResponse(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1328,6 +1596,11 @@ class Usage(BaseModel): ), ] = None + @field_validator("cached_read_tokens", "cached_write_tokens", "thought_tokens", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class StartNesResponse(BaseModel): # The session ID for the newly started NES session. @@ -1466,7 +1739,7 @@ class CloseNesResponse(BaseModel): class PlanFile(BaseModel): # The plan ID to update. - id: Annotated[str, Field(description="The plan ID to update.")] + plan_id: Annotated[str, Field(alias="planId", description="The plan ID to update.")] # The URI of the file containing the plan. uri: Annotated[str, Field(description="The URI of the file containing the plan.")] # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1485,7 +1758,7 @@ class PlanFile(BaseModel): class PlanMarkdown(BaseModel): # The plan ID to update. - id: Annotated[str, Field(description="The plan ID to update.")] + plan_id: Annotated[str, Field(alias="planId", description="The plan ID to update.")] # Markdown content for the plan. content: Annotated[str, Field(description="Markdown content for the plan.")] # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1504,7 +1777,7 @@ class PlanMarkdown(BaseModel): class PlanRemoved(BaseModel): # The plan ID to remove. - id: Annotated[str, Field(description="The plan ID to remove.")] + plan_id: Annotated[str, Field(alias="planId", description="The plan ID to remove.")] # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at # these keys. @@ -1583,6 +1856,11 @@ class _SessionInfoUpdate(BaseModel): ), ] = None + @field_validator("title", "updated_at", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class Cost(BaseModel): # Total cumulative cost for session. @@ -1623,6 +1901,11 @@ class _UsageUpdate(BaseModel): ), ] = None + @field_validator("cost", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class CompleteElicitationNotification(BaseModel): # The ID of the elicitation that completed. @@ -1680,6 +1963,11 @@ class MessageMcpNotification(BaseModel): ), ] = None + @field_validator("params", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class FileSystemCapabilities(BaseModel): # Whether the Client supports `fs/read_text_file` requests. @@ -1711,6 +1999,11 @@ class FileSystemCapabilities(BaseModel): ), ] = None + @field_validator("read_text_file", "write_text_file", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + class BooleanConfigOptionCapabilities(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1765,6 +2058,11 @@ class AuthCapabilities(BaseModel): ), ] = None + @field_validator("terminal", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + class ElicitationFormCapabilities(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -1881,10 +2179,13 @@ class ListProvidersRequest(BaseModel): class SetProviderRequest(BaseModel): - # Provider id to configure. - id: Annotated[str, Field(description="Provider id to configure.")] + # Provider ID to configure. + provider_id: Annotated[str, Field(alias="providerId", description="Provider ID to configure.")] # Protocol type for this provider. - api_type: Annotated[str, Field(alias="apiType", description="Protocol type for this provider.")] + api_type: Annotated[ + Union[str, Dict[str, Any]], + Field(alias="apiType", description="Protocol type for this provider."), + ] # Base URL for requests sent through this provider. base_url: Annotated[ str, @@ -1916,8 +2217,8 @@ class SetProviderRequest(BaseModel): class DisableProviderRequest(BaseModel): - # Provider id to disable. - id: Annotated[str, Field(description="Provider id to disable.")] + # Provider ID to disable. + provider_id: Annotated[str, Field(alias="providerId", description="Provider ID to disable.")] # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at # these keys. @@ -2021,10 +2322,11 @@ class McpServerAcp(BaseModel): # # Providers MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible # on the same ACP connection. - id: Annotated[ + server_id: Annotated[ str, Field( - description="Unique identifier for this MCP server, generated by the component providing it.\n\nProviders MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible\non the same ACP connection." + alias="serverId", + description="Unique identifier for this MCP server, generated by the component providing it.\n\nProviders MUST NOT reuse an ID for multiple ACP-transport MCP servers that are visible\non the same ACP connection.", ), ] # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -2044,8 +2346,8 @@ class McpServerAcp(BaseModel): class McpServerStdio(BaseModel): # Human-readable name identifying this MCP server. name: Annotated[str, Field(description="Human-readable name identifying this MCP server.")] - # Path to the MCP server executable. - command: Annotated[str, Field(description="Path to the MCP server executable.")] + # Absolute path to the MCP server executable. + command: Annotated[str, Field(description="Absolute path to the MCP server executable.")] # Command-line arguments to pass to the MCP server. args: Annotated[ List[str], @@ -2486,6 +2788,11 @@ class TerminalExitStatus(BaseModel): ), ] = None + @field_validator("exit_code", "signal", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ReleaseTerminalResponse(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -2530,6 +2837,11 @@ class WaitForTerminalExitResponse(BaseModel): ), ] = None + @field_validator("exit_code", "signal", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class KillTerminalResponse(BaseModel): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -2578,6 +2890,45 @@ class CancelElicitationResponse(BaseModel): action: Literal["cancel"] +class OtherElicitationResponse(BaseModel): + model_config = ConfigDict( + extra="allow", + ) + # The _meta property is reserved by ACP to allow clients and agents to attach additional + # metadata to their interactions. Implementations MUST NOT make assumptions about values at + # these keys. + # + # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + field_meta: Annotated[ + Optional[Dict[str, Any]], + Field( + alias="_meta", + 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/extensibility)", + ), + ] = None + # Custom or future elicitation action. + # + # Values beginning with `_` are reserved for implementation-specific + # extensions. Unknown values that do not begin with `_` are reserved for + # future ACP variants. + action: Annotated[ + str, + Field( + description="Custom or future elicitation action.\n\nValues beginning with `_` are reserved for implementation-specific\nextensions. Unknown values that do not begin with `_` are reserved for\nfuture ACP variants." + ), + ] + + @field_validator("action", mode="before") + @classmethod + def _reject_known_action(cls, value: Any) -> Any: + # Restore the schema's `not` clause dropped for codegen: reject the known + # variants' discriminator values so a malformed known variant fails instead + # of silently parsing as this catch-all. + if value in ("accept", "decline", "cancel"): + raise ValueError("action value is reserved by a known variant") + return value + + class ElicitationContentValue(RootModel[Union[str, int, float, bool, List[str]]]): # Allowed wire representations for [`ElicitationContentValue`]. root: Annotated[ @@ -2836,6 +3187,16 @@ class Annotations(BaseModel): ), ] = None + @field_validator("last_modified", "priority", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("audience", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class TextContent(BaseModel): # Optional annotations that help clients decide how to display or route this content. @@ -2858,6 +3219,11 @@ class TextContent(BaseModel): ), ] = None + @field_validator("annotations", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ImageContent(BaseModel): # Optional annotations that help clients decide how to display or route this content. @@ -2893,6 +3259,11 @@ class ImageContent(BaseModel): ), ] = None + @field_validator("annotations", "uri", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AudioContent(BaseModel): # Optional annotations that help clients decide how to display or route this content. @@ -2923,6 +3294,11 @@ class AudioContent(BaseModel): ), ] = None + @field_validator("annotations", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ResourceLink(BaseModel): # Optional annotations that help clients decide how to display or route this content. @@ -2967,6 +3343,11 @@ class ResourceLink(BaseModel): ), ] = None + @field_validator("annotations", "description", "mime_type", "size", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class EmbeddedResource(BaseModel): # Optional annotations that help clients decide how to display or route this content. @@ -2992,6 +3373,11 @@ class EmbeddedResource(BaseModel): ), ] = None + @field_validator("annotations", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class PermissionOption(BaseModel): # Unique identifier for this permission option. @@ -3032,10 +3418,10 @@ class CreateTerminalRequest(BaseModel): Optional[List[EnvVariable]], Field(description="Environment variables for the command."), ] = None - # Working directory for the command (absolute path). + # Working directory for the command. Must be an absolute path. cwd: Annotated[ Optional[str], - Field(description="Working directory for the command (absolute path)."), + Field(description="Working directory for the command. Must be an absolute path."), ] = None # Maximum number of output bytes to retain. # @@ -3066,6 +3452,21 @@ class CreateTerminalRequest(BaseModel): ), ] = None + @field_validator("cwd", "output_byte_limit", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("args", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("env", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class CreateUrlSessionElicitationRequest(ElicitationSessionScope): # A human-readable message describing what input is needed. @@ -3145,11 +3546,32 @@ class ElicitationBooleanPropertySchema(BooleanPropertySchema): type: Literal["boolean"] -class UntitledMultiSelectItems(BaseModel): - # Item type discriminator. Must be `"string"`. - type: Annotated[str, Field(description='Item type discriminator. Must be `"string"`.')] - # Allowed enum values. - enum: Annotated[List[str], Field(description="Allowed enum values.")] +class StringMultiSelectItems(_StringMultiSelectItems): + type: Literal["string"] + + +class MultiSelectPropertySchema(BaseModel): + # Optional title for the property. + title: Annotated[Optional[str], Field(description="Optional title for the property.")] = None + # Human-readable description. + description: Annotated[Optional[str], Field(description="Human-readable description.")] = None + # Minimum number of items to select. + min_items: Annotated[ + Optional[int], + Field(alias="minItems", description="Minimum number of items to select.", ge=0), + ] = None + # Maximum number of items to select. + max_items: Annotated[ + Optional[int], + Field(alias="maxItems", description="Maximum number of items to select.", ge=0), + ] = None + # The items definition describing allowed values. + items: Annotated[ + Union[StringMultiSelectItems, OtherMultiSelectItems, TitledMultiSelectItems], + Field(description="The items definition describing allowed values."), + ] + # Default selected values. + default: Annotated[Optional[List[str]], Field(description="Default selected values.")] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at # these keys. @@ -3163,13 +3585,23 @@ class UntitledMultiSelectItems(BaseModel): ), ] = None + @field_validator("description", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("default", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ConnectMcpRequest(BaseModel): # The ACP MCP server ID that was provided by the component declaring the MCP server. - acp_id: Annotated[ + server_id: Annotated[ str, Field( - alias="acpId", + alias="serverId", description="The ACP MCP server ID that was provided by the component declaring the MCP server.", ), ] @@ -3223,9 +3655,14 @@ class MessageMcpRequest(BaseModel): class SessionCapabilities(BaseModel): # Whether the agent supports `session/list`. + # + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports listing sessions. list: Annotated[ Optional[SessionListCapabilities], - Field(description="Whether the agent supports `session/list`."), + Field( + description="Whether the agent supports `session/list`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports listing sessions." + ), ] = None # Whether the agent supports `session/delete`. # @@ -3239,6 +3676,10 @@ class SessionCapabilities(BaseModel): ] = None # Whether the agent supports `additionalDirectories` on supported session lifecycle requests. # + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports `additionalDirectories` on + # supported session lifecycle requests. + # # Agents that also support `session/list` may return # `SessionInfo.additionalDirectories` to report the complete ordered # additional-root list associated with a listed session. @@ -3246,7 +3687,7 @@ class SessionCapabilities(BaseModel): Optional[SessionAdditionalDirectoriesCapabilities], Field( alias="additionalDirectories", - description="Whether the agent supports `additionalDirectories` on supported session lifecycle requests.\n\nAgents that also support `session/list` may return\n`SessionInfo.additionalDirectories` to report the complete ordered\nadditional-root list associated with a listed session.", + description="Whether the agent supports `additionalDirectories` on supported session lifecycle requests.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports `additionalDirectories` on\nsupported session lifecycle requests.\n\nAgents that also support `session/list` may return\n`SessionInfo.additionalDirectories` to report the complete ordered\nadditional-root list associated with a listed session.", ), ] = None # **UNSTABLE** @@ -3254,21 +3695,34 @@ class SessionCapabilities(BaseModel): # This capability is not part of the spec yet, and may be removed or changed at any point. # # Whether the agent supports `session/fork`. + # + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports forking sessions. fork: Annotated[ Optional[SessionForkCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/fork`." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the agent supports `session/fork`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports forking sessions." ), ] = None # Whether the agent supports `session/resume`. + # + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports resuming sessions. resume: Annotated[ Optional[SessionResumeCapabilities], - Field(description="Whether the agent supports `session/resume`."), + Field( + description="Whether the agent supports `session/resume`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports resuming sessions." + ), ] = None # Whether the agent supports `session/close`. + # + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports closing sessions. close: Annotated[ Optional[SessionCloseCapabilities], - Field(description="Whether the agent supports `session/close`."), + Field( + description="Whether the agent supports `session/close`.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports closing sessions." + ), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at @@ -3283,15 +3737,21 @@ class SessionCapabilities(BaseModel): ), ] = None + @field_validator("additional_directories", "close", "delete", "fork", "list", "resume", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AgentAuthCapabilities(BaseModel): # Whether the agent supports the logout method. # - # By supplying `{}` it means that the agent supports the logout method. + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports the logout method. logout: Annotated[ Optional[LogoutCapabilities], Field( - description="Whether the agent supports the logout method.\n\nBy supplying `{}` it means that the agent supports the logout method." + description="Whether the agent supports the logout method.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports the logout method." ), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -3307,6 +3767,11 @@ class AgentAuthCapabilities(BaseModel): ), ] = None + @field_validator("logout", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesDocumentDidChangeCapabilities(BaseModel): # The sync kind the agent wants: `"full"` or `"incremental"`. @@ -3387,6 +3852,13 @@ class NesContextCapabilities(BaseModel): ), ] = None + @field_validator( + "diagnostics", "edit_history", "open_files", "recent_files", "related_snippets", "user_actions", mode="wrap" + ) + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class EnvVarAuthMethod(AuthMethodEnvVar): type: Literal["env_var"] @@ -3398,15 +3870,24 @@ class TerminalAuthMethod(AuthMethodTerminal): class ProviderInfo(BaseModel): # Provider identifier, for example "main" or "openai". - id: Annotated[str, Field(description='Provider identifier, for example "main" or "openai".')] + provider_id: Annotated[ + str, + Field( + alias="providerId", + description='Provider identifier, for example "main" or "openai".', + ), + ] # Supported protocol types for this provider. - supported: Annotated[List[str], Field(description="Supported protocol types for this provider.")] + supported: Annotated[ + List[Union[str, Dict[str, Any]]], + Field(description="Supported protocol types for this provider."), + ] # Whether this provider is mandatory and cannot be disabled via `providers/disable`. - # If true, clients must not call `providers/disable` for this id. + # If true, clients must not call `providers/disable` for this provider ID. required: Annotated[ bool, Field( - description="Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this id." + description="Whether this provider is mandatory and cannot be disabled via `providers/disable`.\nIf true, clients must not call `providers/disable` for this provider ID." ), ] # Current effective non-secret routing config. @@ -3428,6 +3909,11 @@ class ProviderInfo(BaseModel): ), ] = None + @field_validator("supported", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class SessionModeState(BaseModel): # The current mode the Agent is in. @@ -3456,6 +3942,11 @@ class SessionModeState(BaseModel): ), ] = None + @field_validator("available_modes", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class SessionConfigOptionBoolean(SessionConfigBoolean): # Unique identifier for the configuration option. @@ -3469,7 +3960,7 @@ class SessionConfigOptionBoolean(SessionConfigBoolean): ] = None # Optional semantic category for this option (UX only). category: Annotated[ - Optional[str], + Optional[Union[str, Dict[str, Any]]], Field(description="Optional semantic category for this option (UX only)."), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -3486,6 +3977,11 @@ class SessionConfigOptionBoolean(SessionConfigBoolean): ] = None type: Literal["boolean"] + @field_validator("category", "description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class SessionConfigSelectGroup(BaseModel): # Unique identifier for this group. @@ -3510,6 +4006,11 @@ class SessionConfigSelectGroup(BaseModel): ), ] = None + @field_validator("options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ListSessionsResponse(BaseModel): # Array of session information objects @@ -3536,6 +4037,16 @@ class ListSessionsResponse(BaseModel): ), ] = None + @field_validator("next_cursor", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("sessions", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class PromptResponse(BaseModel): # Indicates why the agent stopped processing the turn. @@ -3570,6 +4081,11 @@ class PromptResponse(BaseModel): ), ] = None + @field_validator("usage", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesJumpSuggestionVariant(NesJumpSuggestion): kind: Literal["jump"] @@ -3628,6 +4144,11 @@ class Error(BaseModel): ), ] = None + @field_validator("data", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AgentPlanRemovedUpdate(PlanRemoved): session_update: Annotated[Literal["plan_removed"], Field(alias="sessionUpdate")] @@ -3699,6 +4220,11 @@ class Plan(BaseModel): ), ] = None + @field_validator("entries", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class PlanUpdateFile(PlanFile): type: Literal["file"] @@ -3710,7 +4236,7 @@ class PlanUpdateMarkdown(PlanMarkdown): class PlanItems(BaseModel): # The plan ID to update. - id: Annotated[str, Field(description="The plan ID to update.")] + plan_id: Annotated[str, Field(alias="planId", description="The plan ID to update.")] # The list of tasks to be accomplished. # # When updating an item-based plan, the agent must send a complete list of all entries @@ -3734,6 +4260,11 @@ class PlanItems(BaseModel): ), ] = None + @field_validator("entries", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class AvailableCommandInput(RootModel[UnstructuredCommandInput]): # The input specification for a command. @@ -3746,14 +4277,14 @@ class AvailableCommandInput(RootModel[UnstructuredCommandInput]): class SessionConfigOptionsCapabilities(BaseModel): # Whether the client supports boolean session configuration options. # - # Omitted or `null` means the client does not advertise support. + # Optional. Omitted or `null` both mean the client does not advertise support. # Supplying `{}` means agents may include `type: "boolean"` entries in # `configOptions`, and the client may send `session/set_config_option` # requests with `type: "boolean"` and a boolean `value`. boolean: Annotated[ Optional[BooleanConfigOptionCapabilities], Field( - description='Whether the client supports boolean session configuration options.\n\nOmitted or `null` means the client does not advertise support.\nSupplying `{}` means agents may include `type: "boolean"` entries in\n`configOptions`, and the client may send `session/set_config_option`\nrequests with `type: "boolean"` and a boolean `value`.' + description='Whether the client supports boolean session configuration options.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means agents may include `type: "boolean"` entries in\n`configOptions`, and the client may send `session/set_config_option`\nrequests with `type: "boolean"` and a boolean `value`.' ), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -3769,17 +4300,32 @@ class SessionConfigOptionsCapabilities(BaseModel): ), ] = None + @field_validator("boolean", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ElicitationCapabilities(BaseModel): # Whether the client supports form-based elicitation. + # + # Optional. Omitted or `null` both mean the client does not advertise support. + # Supplying `{}` means the client supports form-based elicitation. form: Annotated[ Optional[ElicitationFormCapabilities], - Field(description="Whether the client supports form-based elicitation."), + Field( + description="Whether the client supports form-based elicitation.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client supports form-based elicitation." + ), ] = None # Whether the client supports URL-based elicitation. + # + # Optional. Omitted or `null` both mean the client does not advertise support. + # Supplying `{}` means the client supports URL-based elicitation. url: Annotated[ Optional[ElicitationUrlCapabilities], - Field(description="Whether the client supports URL-based elicitation."), + Field( + description="Whether the client supports URL-based elicitation.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client supports URL-based elicitation." + ), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at @@ -3794,6 +4340,11 @@ class ElicitationCapabilities(BaseModel): ), ] = None + @field_validator("form", "url", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ClientNesCapabilities(BaseModel): # Whether the client supports the `jump` suggestion kind. @@ -3827,6 +4378,11 @@ class ClientNesCapabilities(BaseModel): ), ] = None + @field_validator("jump", "rename", "search_and_replace", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class HttpMcpServer(McpServerHttp): type: Literal["http"] @@ -3849,8 +4405,11 @@ class LoadSessionRequest(BaseModel): description="List of MCP servers to connect to for this session.", ), ] - # The working directory for this session. - cwd: Annotated[str, Field(description="The working directory for this session.")] + # The working directory for this session. Must be an absolute path. + cwd: Annotated[ + str, + Field(description="The working directory for this session. Must be an absolute path."), + ] # Additional workspace roots to activate for this session. Each path must be absolute. # # When omitted or empty, no additional roots are activated. When non-empty, @@ -3879,12 +4438,25 @@ class LoadSessionRequest(BaseModel): ), ] = None + @field_validator("additional_directories", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("mcp_servers", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ForkSessionRequest(BaseModel): # The ID of the session to fork. session_id: Annotated[str, Field(alias="sessionId", description="The ID of the session to fork.")] - # The working directory for this session. - cwd: Annotated[str, Field(description="The working directory for this session.")] + # The working directory for this session. Must be an absolute path. + cwd: Annotated[ + str, + Field(description="The working directory for this session. Must be an absolute path."), + ] # Additional workspace roots to activate for this session. Each path must be absolute. # # When omitted or empty, no additional roots are activated. When non-empty, @@ -3918,12 +4490,25 @@ class ForkSessionRequest(BaseModel): ), ] = None + @field_validator("additional_directories", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("mcp_servers", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ResumeSessionRequest(BaseModel): # The ID of the session to resume. session_id: Annotated[str, Field(alias="sessionId", description="The ID of the session to resume.")] - # The working directory for this session. - cwd: Annotated[str, Field(description="The working directory for this session.")] + # The working directory for this session. Must be an absolute path. + cwd: Annotated[ + str, + Field(description="The working directory for this session. Must be an absolute path."), + ] # Additional workspace roots to activate for this session. Each path must be absolute. # # When omitted or empty, no additional roots are activated. When non-empty, @@ -3958,6 +4543,16 @@ class ResumeSessionRequest(BaseModel): ), ] = None + @field_validator("additional_directories", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("mcp_servers", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class StartNesRequest(BaseModel): # The root URI of the workspace. @@ -3988,6 +4583,11 @@ class StartNesRequest(BaseModel): ), ] = None + @field_validator("repository", "workspace_uri", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesRelatedSnippet(BaseModel): # The URI of the file containing the snippets. @@ -4040,6 +4640,11 @@ class NesOpenFile(BaseModel): ), ] = None + @field_validator("last_focused_ms", "visible_range", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NesDiagnostic(BaseModel): # The URI of the file containing the diagnostic. @@ -4101,6 +4706,11 @@ class TerminalOutputResponse(BaseModel): ), ] = None + @field_validator("exit_status", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AcceptElicitationResponse(ElicitationAcceptAction): # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -4200,6 +4810,11 @@ class RejectNesNotification(BaseModel): ), ] = None + @field_validator("reason", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class TextContentBlock(TextContent): type: Literal["text"] @@ -4243,40 +4858,8 @@ class Content(BaseModel): ] = None -class MultiSelectPropertySchema(BaseModel): - # Optional title for the property. - title: Annotated[Optional[str], Field(description="Optional title for the property.")] = None - # Human-readable description. - description: Annotated[Optional[str], Field(description="Human-readable description.")] = None - # Minimum number of items to select. - min_items: Annotated[ - Optional[int], - Field(alias="minItems", description="Minimum number of items to select.", ge=0), - ] = None - # Maximum number of items to select. - max_items: Annotated[ - Optional[int], - Field(alias="maxItems", description="Maximum number of items to select.", ge=0), - ] = None - # The items definition describing allowed values. - items: Annotated[ - Union[UntitledMultiSelectItems, TitledMultiSelectItems], - Field(description="The items definition describing allowed values."), - ] - # Default selected values. - default: Annotated[Optional[List[str]], Field(description="Default selected values.")] = None - # The _meta property is reserved by ACP to allow clients and agents to attach additional - # metadata to their interactions. Implementations MUST NOT make assumptions about values at - # these keys. - # - # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - field_meta: Annotated[ - Optional[Dict[str, Any]], - Field( - alias="_meta", - 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/extensibility)", - ), - ] = None +class ElicitationMultiSelectPropertySchema(MultiSelectPropertySchema): + type: Literal["array"] class AgentErrorMessage(BaseModel): @@ -4343,6 +4926,11 @@ class NesDocumentEventCapabilities(BaseModel): ), ] = None + @field_validator("did_change", "did_close", "did_focus", "did_open", "did_save", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class ListProvidersResponse(BaseModel): # Configurable providers with current routing info suitable for UI display. @@ -4421,6 +5009,11 @@ class NesEditSuggestion(BaseModel): ), ] = None + @field_validator("cursor_position", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class AgentPlanUpdate(Plan): session_update: Annotated[Literal["plan"], Field(alias="sessionUpdate")] @@ -4458,6 +5051,11 @@ class ContentChunk(BaseModel): ), ] = None + @field_validator("message_id", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class PlanUpdateItems(PlanItems): type: Literal["items"] @@ -4509,6 +5107,11 @@ class AvailableCommand(BaseModel): ), ] = None + @field_validator("input", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class _AvailableCommandsUpdate(BaseModel): # Commands the agent can execute @@ -4529,17 +5132,22 @@ class _AvailableCommandsUpdate(BaseModel): ), ] = None + @field_validator("available_commands", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ClientSessionCapabilities(BaseModel): # Config option capabilities supported by the client. # - # Omitted or `null` means the client does not advertise support for any + # Omitted or `null` both mean the client does not advertise support for any # config option extensions. config_options: Annotated[ Optional[SessionConfigOptionsCapabilities], Field( alias="configOptions", - description="Config option capabilities supported by the client.\n\nOmitted or `null` means the client does not advertise support for any\nconfig option extensions.", + description="Config option capabilities supported by the client.\n\nOmitted or `null` both mean the client does not advertise support for any\nconfig option extensions.", ), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -4555,6 +5163,11 @@ class ClientSessionCapabilities(BaseModel): ), ] = None + @field_validator("config_options", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NewSessionRequest(BaseModel): # The working directory for this session. Must be an absolute path. @@ -4595,6 +5208,16 @@ class NewSessionRequest(BaseModel): ), ] = None + @field_validator("additional_directories", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("mcp_servers", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class PromptRequest(BaseModel): # The ID of the session to send this user message to @@ -4745,13 +5368,103 @@ class DidChangeDocumentNotification(BaseModel): ), ] = None + @field_validator("content_changes", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ContentToolCallContent(Content): type: Literal["content"] -class ElicitationMultiSelectPropertySchema(MultiSelectPropertySchema): - type: Literal["array"] +class ElicitationSchema(BaseModel): + # Type discriminator. Always `"object"`. + type: Annotated[Optional[str], Field(description='Type discriminator. Always `"object"`.')] = "object" + # Optional title for the schema. + title: Annotated[Optional[str], Field(description="Optional title for the schema.")] = None + # Property definitions (must be primitive types). + properties: Annotated[ + Optional[ + Dict[ + str, + Union[ + ElicitationStringPropertySchema, + ElicitationNumberPropertySchema, + ElicitationIntegerPropertySchema, + ElicitationBooleanPropertySchema, + ElicitationMultiSelectPropertySchema, + ElicitationOtherPropertySchema, + ], + ] + ], + Field(description="Property definitions (must be primitive types)."), + ] = {} + # List of required property names. + required: Annotated[Optional[List[str]], Field(description="List of required property names.")] = None + # Optional description of what this schema represents. + description: Annotated[ + Optional[str], + Field(description="Optional description of what this schema represents."), + ] = None + # The _meta property is reserved by ACP to allow clients and agents to attach additional + # metadata to their interactions. Implementations MUST NOT make assumptions about values at + # these keys. + # + # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + field_meta: Annotated[ + Optional[Dict[str, Any]], + Field( + alias="_meta", + 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/extensibility)", + ), + ] = None + + @field_validator("type", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: "object") + + @field_validator("description", "title", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + +class ElicitationFormSessionMode(ElicitationSessionScope): + # A JSON Schema describing the form fields to present to the user. + requested_schema: Annotated[ + ElicitationSchema, + Field( + alias="requestedSchema", + description="A JSON Schema describing the form fields to present to the user.", + ), + ] + + +class ElicitationFormRequestMode(ElicitationRequestScope): + # A JSON Schema describing the form fields to present to the user. + requested_schema: Annotated[ + ElicitationSchema, + Field( + alias="requestedSchema", + description="A JSON Schema describing the form fields to present to the user.", + ), + ] + + +class ElicitationFormMode(RootModel[Union[ElicitationFormSessionMode, ElicitationFormRequestMode]]): + # **UNSTABLE** + # + # This capability is not part of the spec yet, and may be removed or changed at any point. + # + # Form-based elicitation mode where the client renders a form from the provided schema. + root: Annotated[ + Union[ElicitationFormSessionMode, ElicitationFormRequestMode], + Field( + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nForm-based elicitation mode where the client renders a form from the provided schema." + ), + ] class NesEventCapabilities(BaseModel): @@ -4773,6 +5486,11 @@ class NesEventCapabilities(BaseModel): ), ] = None + @field_validator("document", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class SessionConfigOptionSelect(SessionConfigSelect): # Unique identifier for the configuration option. @@ -4786,7 +5504,7 @@ class SessionConfigOptionSelect(SessionConfigSelect): ] = None # Optional semantic category for this option (UX only). category: Annotated[ - Optional[str], + Optional[Union[str, Dict[str, Any]]], Field(description="Optional semantic category for this option (UX only)."), ] = None # The _meta property is reserved by ACP to allow clients and agents to attach additional @@ -4803,6 +5521,11 @@ class SessionConfigOptionSelect(SessionConfigSelect): ] = None type: Literal["select"] + @field_validator("category", "description", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class LoadSessionResponse(BaseModel): # Initial mode state if supported by the Agent @@ -4835,6 +5558,16 @@ class LoadSessionResponse(BaseModel): ), ] = None + @field_validator("modes", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ForkSessionResponse(BaseModel): # Unique identifier for the newly created forked session. @@ -4875,6 +5608,16 @@ class ForkSessionResponse(BaseModel): ), ] = None + @field_validator("modes", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ResumeSessionResponse(BaseModel): # Initial mode state if supported by the Agent @@ -4907,6 +5650,16 @@ class ResumeSessionResponse(BaseModel): ), ] = None + @field_validator("modes", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class SetSessionConfigOptionResponse(BaseModel): # The full set of configuration options and their current values. @@ -4930,6 +5683,11 @@ class SetSessionConfigOptionResponse(BaseModel): ), ] = None + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class NesEditSuggestionVariant(NesEditSuggestion): kind: Literal["edit"] @@ -5013,6 +5771,21 @@ class ToolCall(BaseModel): ), ] = None + @field_validator("kind", "raw_input", "raw_output", "status", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("content", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("locations", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class _ConfigOptionUpdate(BaseModel): # The full set of configuration options and their current values. @@ -5036,6 +5809,11 @@ class _ConfigOptionUpdate(BaseModel): ), ] = None + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class ClientCapabilities(BaseModel): # File system capabilities supported by the client. @@ -5051,15 +5829,14 @@ class ClientCapabilities(BaseModel): Optional[bool], Field(description="Whether the Client support all `terminal/*` methods."), ] = False - # **UNSTABLE** - # - # This capability is not part of the spec yet, and may be removed or changed at any point. - # # Session-related capabilities supported by the client. + # + # Optional. Omitted or `null` both mean the client does not advertise any + # session-related extensions. session: Annotated[ Optional[ClientSessionCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nSession-related capabilities supported by the client." + description="Session-related capabilities supported by the client.\n\nOptional. Omitted or `null` both mean the client does not advertise any\nsession-related extensions." ), ] = None # **UNSTABLE** @@ -5068,12 +5845,12 @@ class ClientCapabilities(BaseModel): # # Whether the client supports `plan_update` and `plan_removed` session updates. # - # Optional. Omitted means the client does not advertise support. + # Optional. Omitted or `null` both mean the client does not advertise support. # Supplying `{}` means the client can receive both update types. plan: Annotated[ Optional[PlanCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the client supports `plan_update` and `plan_removed` session updates.\n\nOptional. Omitted means the client does not advertise support.\nSupplying `{}` means the client can receive both update types." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nWhether the client supports `plan_update` and `plan_removed` session updates.\n\nOptional. Omitted or `null` both mean the client does not advertise support.\nSupplying `{}` means the client can receive both update types." ), ] = None # **UNSTABLE** @@ -5095,10 +5872,13 @@ class ClientCapabilities(BaseModel): # # Elicitation capabilities supported by the client. # Determines which elicitation modes the agent may use. + # + # Optional. Omitted or `null` both mean the client does not advertise + # elicitation support. elicitation: Annotated[ Optional[ElicitationCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nElicitation capabilities supported by the client.\nDetermines which elicitation modes the agent may use." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nElicitation capabilities supported by the client.\nDetermines which elicitation modes the agent may use.\n\nOptional. Omitted or `null` both mean the client does not advertise\nelicitation support." ), ] = None # **UNSTABLE** @@ -5106,10 +5886,13 @@ class ClientCapabilities(BaseModel): # This capability is not part of the spec yet, and may be removed or changed at any point. # # NES (Next Edit Suggestions) capabilities supported by the client. + # + # Optional. Omitted or `null` both mean the client does not advertise any + # NES suggestion-kind extensions. nes: Annotated[ Optional[ClientNesCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the client.\n\nOptional. Omitted or `null` both mean the client does not advertise any\nNES suggestion-kind extensions." ), ] = None # **UNSTABLE** @@ -5137,6 +5920,31 @@ class ClientCapabilities(BaseModel): ), ] = None + @field_validator("terminal", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + + @field_validator("elicitation", "nes", "plan", "session", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("fs", mode="wrap") + @classmethod + def _salvage_on_error_2(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: {"readTextFile": False, "writeTextFile": False}) + + @field_validator("auth", mode="wrap") + @classmethod + def _salvage_on_error_3(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: {"terminal": False}) + + @field_validator("position_encodings", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class SuggestNesRequest(BaseModel): # The session ID for this request. @@ -5196,6 +6004,7 @@ class ClientResponseMessage(BaseModel): AcceptElicitationResponse, DeclineElicitationResponse, CancelElicitationResponse, + OtherElicitationResponse, ], Any, ], @@ -5273,35 +6082,28 @@ class ToolCallUpdate(BaseModel): ), ] = None + @field_validator("kind", "raw_input", "raw_output", "status", "title", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) -class ElicitationSchema(BaseModel): - # Type discriminator. Always `"object"`. - type: Annotated[Optional[str], Field(description='Type discriminator. Always `"object"`.')] = "object" - # Optional title for the schema. - title: Annotated[Optional[str], Field(description="Optional title for the schema.")] = None - # Property definitions (must be primitive types). - properties: Annotated[ - Optional[ - Dict[ - str, - Union[ - ElicitationStringPropertySchema, - ElicitationNumberPropertySchema, - ElicitationIntegerPropertySchema, - ElicitationBooleanPropertySchema, - ElicitationMultiSelectPropertySchema, - ], - ] - ], - Field(description="Property definitions (must be primitive types)."), - ] = {} - # List of required property names. - required: Annotated[Optional[List[str]], Field(description="List of required property names.")] = None - # Optional description of what this schema represents. - description: Annotated[ - Optional[str], - Field(description="Optional description of what this schema represents."), - ] = None + @field_validator("content", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + @field_validator("locations", mode="wrap") + @classmethod + def _skip_invalid_items_1(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + +class CreateFormSessionElicitationRequest(ElicitationSessionScope): + # A human-readable message describing what input is needed. + message: Annotated[ + str, + Field(description="A human-readable message describing what input is needed."), + ] # The _meta property is reserved by ACP to allow clients and agents to attach additional # metadata to their interactions. Implementations MUST NOT make assumptions about values at # these keys. @@ -5314,9 +6116,7 @@ class ElicitationSchema(BaseModel): 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/extensibility)", ), ] = None - - -class ElicitationFormSessionMode(ElicitationSessionScope): + mode: Literal["form"] # A JSON Schema describing the form fields to present to the user. requested_schema: Annotated[ ElicitationSchema, @@ -5327,7 +6127,25 @@ class ElicitationFormSessionMode(ElicitationSessionScope): ] -class ElicitationFormRequestMode(ElicitationRequestScope): +class CreateFormRequestElicitationRequest(ElicitationRequestScope): + # A human-readable message describing what input is needed. + message: Annotated[ + str, + Field(description="A human-readable message describing what input is needed."), + ] + # The _meta property is reserved by ACP to allow clients and agents to attach additional + # metadata to their interactions. Implementations MUST NOT make assumptions about values at + # these keys. + # + # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) + field_meta: Annotated[ + Optional[Dict[str, Any]], + Field( + alias="_meta", + 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/extensibility)", + ), + ] = None + mode: Literal["form"] # A JSON Schema describing the form fields to present to the user. requested_schema: Annotated[ ElicitationSchema, @@ -5338,18 +6156,31 @@ class ElicitationFormRequestMode(ElicitationRequestScope): ] -class ElicitationFormMode(RootModel[Union[ElicitationFormSessionMode, ElicitationFormRequestMode]]): - # **UNSTABLE** - # - # This capability is not part of the spec yet, and may be removed or changed at any point. - # - # Form-based elicitation mode where the client renders a form from the provided schema. - root: Annotated[ - Union[ElicitationFormSessionMode, ElicitationFormRequestMode], - Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nForm-based elicitation mode where the client renders a form from the provided schema." - ), - ] +ElicitationMode = Union[ + ElicitationFormSessionMode, + ElicitationFormRequestMode, + ElicitationUrlSessionMode, + ElicitationUrlRequestMode, +] +CreateFormElicitationRequest = Union[ + CreateFormSessionElicitationRequest, + CreateFormRequestElicitationRequest, +] +CreateUrlElicitationRequest = Union[ + CreateUrlSessionElicitationRequest, + CreateUrlRequestElicitationRequest, +] +CreateElicitationRequest = Union[ + CreateFormElicitationRequest, + CreateUrlElicitationRequest, + CreateOtherElicitationRequest, +] +CreateElicitationResponse = Union[ + AcceptElicitationResponse, + DeclineElicitationResponse, + CancelElicitationResponse, + OtherElicitationResponse, +] class NesCapabilities(BaseModel): @@ -5376,6 +6207,11 @@ class NesCapabilities(BaseModel): ), ] = None + @field_validator("context", "events", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + class NewSessionResponse(BaseModel): # Unique identifier for the created session. @@ -5418,6 +6254,16 @@ class NewSessionResponse(BaseModel): ), ] = None + @field_validator("modes", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("config_options", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class SuggestNesResponse(BaseModel): # The list of suggestions. @@ -5514,6 +6360,24 @@ def _coerce_protocol_version(cls, value: Any) -> int: except (TypeError, ValueError): return 1 + @field_validator("client_info", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("client_capabilities", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error( + value, + handler, + lambda: { + "fs": {"readTextFile": False, "writeTextFile": False}, + "terminal": False, + "auth": {"terminal": False}, + }, + ) + class RequestPermissionRequest(BaseModel): # The session ID for this request. @@ -5545,89 +6409,6 @@ class RequestPermissionRequest(BaseModel): ] = None -class CreateFormSessionElicitationRequest(ElicitationSessionScope): - # A human-readable message describing what input is needed. - message: Annotated[ - str, - Field(description="A human-readable message describing what input is needed."), - ] - # The _meta property is reserved by ACP to allow clients and agents to attach additional - # metadata to their interactions. Implementations MUST NOT make assumptions about values at - # these keys. - # - # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - field_meta: Annotated[ - Optional[Dict[str, Any]], - Field( - alias="_meta", - 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/extensibility)", - ), - ] = None - mode: Literal["form"] - # A JSON Schema describing the form fields to present to the user. - requested_schema: Annotated[ - ElicitationSchema, - Field( - alias="requestedSchema", - description="A JSON Schema describing the form fields to present to the user.", - ), - ] - - -class CreateFormRequestElicitationRequest(ElicitationRequestScope): - # A human-readable message describing what input is needed. - message: Annotated[ - str, - Field(description="A human-readable message describing what input is needed."), - ] - # The _meta property is reserved by ACP to allow clients and agents to attach additional - # metadata to their interactions. Implementations MUST NOT make assumptions about values at - # these keys. - # - # See protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility) - field_meta: Annotated[ - Optional[Dict[str, Any]], - Field( - alias="_meta", - 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/extensibility)", - ), - ] = None - mode: Literal["form"] - # A JSON Schema describing the form fields to present to the user. - requested_schema: Annotated[ - ElicitationSchema, - Field( - alias="requestedSchema", - description="A JSON Schema describing the form fields to present to the user.", - ), - ] - - -ElicitationMode = Union[ - ElicitationFormSessionMode, - ElicitationFormRequestMode, - ElicitationUrlSessionMode, - ElicitationUrlRequestMode, -] -CreateFormElicitationRequest = Union[ - CreateFormSessionElicitationRequest, - CreateFormRequestElicitationRequest, -] -CreateUrlElicitationRequest = Union[ - CreateUrlSessionElicitationRequest, - CreateUrlRequestElicitationRequest, -] -CreateElicitationRequest = Union[ - CreateFormElicitationRequest, - CreateUrlElicitationRequest, -] -CreateElicitationResponse = Union[ - AcceptElicitationResponse, - DeclineElicitationResponse, - CancelElicitationResponse, -] - - class AgentCapabilities(BaseModel): # Whether the agent supports `session/load`. load_session: Annotated[ @@ -5672,11 +6453,12 @@ class AgentCapabilities(BaseModel): # # Provider configuration capabilities supported by the agent. # - # By supplying `{}` it means that the agent supports provider configuration methods. + # Optional. Omitted or `null` both mean the agent does not advertise support. + # Supplying `{}` means the agent supports provider configuration methods. providers: Annotated[ Optional[ProvidersCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nBy supplying `{}` it means that the agent supports provider configuration methods." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nProvider configuration capabilities supported by the agent.\n\nOptional. Omitted or `null` both mean the agent does not advertise support.\nSupplying `{}` means the agent supports provider configuration methods." ), ] = None # **UNSTABLE** @@ -5684,10 +6466,13 @@ class AgentCapabilities(BaseModel): # This capability is not part of the spec yet, and may be removed or changed at any point. # # NES (Next Edit Suggestions) capabilities supported by the agent. + # + # Optional. Omitted or `null` both mean the agent does not advertise support + # for NES methods. nes: Annotated[ Optional[NesCapabilities], Field( - description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the agent." + description="**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nNES (Next Edit Suggestions) capabilities supported by the agent.\n\nOptional. Omitted or `null` both mean the agent does not advertise support\nfor NES methods." ), ] = None # **UNSTABLE** @@ -5715,6 +6500,31 @@ class AgentCapabilities(BaseModel): ), ] = None + @field_validator("load_session", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: False) + + @field_validator("nes", "position_encoding", "providers", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("mcp_capabilities", mode="wrap") + @classmethod + def _salvage_on_error_2(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: {"http": False, "sse": False, "acp": False}) + + @field_validator("prompt_capabilities", mode="wrap") + @classmethod + def _salvage_on_error_3(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: {"image": False, "audio": False, "embeddedContext": False}) + + @field_validator("auth", "session_capabilities", mode="wrap") + @classmethod + def _salvage_on_error_4(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: {}) + class SessionNotification(BaseModel): # The ID of the session this update pertains to. @@ -5825,6 +6635,7 @@ class AgentRequest(BaseModel): CreateFormRequestElicitationRequest, CreateUrlSessionElicitationRequest, CreateUrlRequestElicitationRequest, + CreateOtherElicitationRequest, ], Any, ] @@ -5886,6 +6697,31 @@ class InitializeResponse(BaseModel): ), ] = None + @field_validator("agent_info", mode="wrap") + @classmethod + def _salvage_on_error_0(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + @field_validator("agent_capabilities", mode="wrap") + @classmethod + def _salvage_on_error_1(cls, value: Any, handler: Any) -> Any: + return salvage_on_error( + value, + handler, + lambda: { + "loadSession": False, + "promptCapabilities": {"image": False, "audio": False, "embeddedContext": False}, + "mcpCapabilities": {"http": False, "sse": False, "acp": False}, + "sessionCapabilities": {}, + "auth": {}, + }, + ) + + @field_validator("auth_methods", mode="wrap") + @classmethod + def _skip_invalid_items_0(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + class AgentNotification(BaseModel): # The notification method name. diff --git a/tests/test_deserialize.py b/tests/test_deserialize.py new file mode 100644 index 0000000..42188ff --- /dev/null +++ b/tests/test_deserialize.py @@ -0,0 +1,62 @@ +"""Regression tests for lenient deserialization restored by ``acp._deserialize`` and the +validators ``scripts/gen_schema.py`` injects: ``x-deserialize-default-on-error`` (salvage a +malformed field to its default) and ``x-deserialize-skip-invalid-items`` (drop bad array +items). Mirrors the TypeScript SDK's ``src/schema-deserialize.test.ts``. +""" + +from typing import Any + +from pydantic import BaseModel, field_validator + +from acp._deserialize import salvage_on_error, skip_invalid_items +from acp.schema import ReadTextFileRequest, ToolCallUpdate, WriteTextFileRequest + + +class _Salvage(BaseModel): + n: int | None = None + + @field_validator("n", mode="wrap") + @classmethod + def _v(cls, value: Any, handler: Any) -> Any: + return salvage_on_error(value, handler, lambda: None) + + +def test_salvage_on_error_replaces_invalid_value() -> None: + assert _Salvage.model_validate({"n": "not-an-int"}).n is None + assert _Salvage.model_validate({"n": 7}).n == 7 + + +class _Skip(BaseModel): + xs: list[int] = [] + + @field_validator("xs", mode="wrap") + @classmethod + def _v(cls, value: Any, handler: Any) -> Any: + return skip_invalid_items(value, handler) + + +def test_skip_invalid_items_drops_bad_entries() -> None: + assert _Skip.model_validate({"xs": [1, "bad", 3]}).xs == [1, 3] + + +def test_meta_is_salvaged_to_none() -> None: + request = WriteTextFileRequest.model_validate({ + "sessionId": "s", + "path": "/p", + "content": "x", + "_meta": "not-a-dict", + }) + assert request.field_meta is None + + +def test_default_on_error_field_is_salvaged() -> None: + salvaged = ReadTextFileRequest.model_validate({"sessionId": "s", "path": "/p", "line": "nan"}) + assert salvaged.line is None + kept = ReadTextFileRequest.model_validate({"sessionId": "s", "path": "/p", "line": 5}) + assert kept.line == 5 + + +def test_skip_invalid_items_on_generated_model() -> None: + good = {"type": "content", "content": {"type": "text", "text": "ok"}} + update = ToolCallUpdate.model_validate({"toolCallId": "t", "content": [good, {"bogus": 1}]}) + assert len(update.content or []) == 1 diff --git a/tests/test_elicitation_catchall.py b/tests/test_elicitation_catchall.py new file mode 100644 index 0000000..331185f --- /dev/null +++ b/tests/test_elicitation_catchall.py @@ -0,0 +1,83 @@ +"""Regression tests for the schema-v1.18 "custom or future" catch-all union idiom. + +These guard the codegen support in ``scripts/gen_schema.py``: unknown discriminator +values must resolve to the catch-all variant while preserving the raw payload, and +known values must still resolve to their specific typed variant. +""" + +import pytest +from pydantic import TypeAdapter, ValidationError + +from acp.client.router import _mode_from_create_elicitation_request +from acp.exceptions import RequestError +from acp.schema import ( + AcceptElicitationResponse, + CreateElicitationRequest, + CreateElicitationResponse, + CreateOtherElicitationRequest, + ElicitationOtherPropertySchema, + ElicitationStringPropertySchema, + OtherElicitationResponse, + OtherMultiSelectItems, + StringMultiSelectItems, + TitledMultiSelectItems, +) + +_RESPONSE = TypeAdapter(CreateElicitationResponse) +_REQUEST = TypeAdapter(CreateElicitationRequest) + + +def test_known_elicitation_response_resolves_to_specific_variant() -> None: + assert isinstance(_RESPONSE.validate_python({"action": "accept"}), AcceptElicitationResponse) + + +def test_custom_elicitation_response_falls_back_to_catchall() -> None: + parsed = _RESPONSE.validate_python({"action": "x-snooze", "until": "later"}) + assert isinstance(parsed, OtherElicitationResponse) + assert parsed.action == "x-snooze" + assert parsed.model_dump(by_alias=True)["until"] == "later" + + +def test_malformed_known_variant_is_rejected_not_catchall() -> None: + # A "form" request missing the required requestedSchema must fail validation rather + # than silently degrade to the catch-all (restores the schema's dropped `not` clause). + with pytest.raises(ValidationError): + _REQUEST.validate_python({"mode": "form", "message": "hi", "sessionId": "s1"}) + + +def test_custom_elicitation_request_preserves_mode_and_payload() -> None: + parsed = _REQUEST.validate_python({ + "mode": "x-voice", + "message": "speak now", + "sessionId": "sess-1", + "codec": "opus", + }) + assert isinstance(parsed, CreateOtherElicitationRequest) + assert parsed.mode == "x-voice" + assert parsed.model_dump(by_alias=True)["codec"] == "opus" + + +def test_unknown_elicitation_mode_dispatches_to_clean_request_error() -> None: + # A custom mode parses (above); the client router must then reject it with a clean + # RequestError (invalid params) rather than a bare TypeError that surfaces as an + # opaque -32603 internal error. + request = CreateOtherElicitationRequest(message="hi", mode="x-voice") + with pytest.raises(RequestError) as exc_info: + _mode_from_create_elicitation_request(request) + assert isinstance(exc_info.value, RequestError) + assert exc_info.value.code == -32602 + + +def test_elicitation_property_schema_catchall() -> None: + adapter = TypeAdapter(ElicitationStringPropertySchema | ElicitationOtherPropertySchema) + assert isinstance(adapter.validate_python({"type": "string"}), ElicitationStringPropertySchema) + custom = adapter.validate_python({"type": "x-slider", "min": 0, "max": 9}) + assert isinstance(custom, ElicitationOtherPropertySchema) + assert custom.model_dump(by_alias=True)["max"] == 9 + + +def test_multi_select_items_variants() -> None: + adapter = TypeAdapter(StringMultiSelectItems | OtherMultiSelectItems | TitledMultiSelectItems) + assert isinstance(adapter.validate_python({"type": "string", "enum": ["a", "b"]}), StringMultiSelectItems) + assert isinstance(adapter.validate_python({"anyOf": [{"const": "a", "title": "A"}]}), TitledMultiSelectItems) + assert isinstance(adapter.validate_python({"type": "x-chips", "note": "hi"}), OtherMultiSelectItems) diff --git a/tests/test_gen_all.py b/tests/test_gen_all.py index 2cc95b5..da9f73d 100644 --- a/tests/test_gen_all.py +++ b/tests/test_gen_all.py @@ -1,5 +1,12 @@ from scripts.gen_all import resolve_ref, schema_source_paths -from scripts.gen_schema import _preprocess_schema_for_codegen, _restore_required_nullable_fields +from scripts.gen_schema import ( + _deserialize_field_specs, + _extensible_union_excluded_tags, + _fallback_expression, + _normalize_catchall_unions, + _preprocess_schema_for_codegen, + _restore_required_nullable_fields, +) def test_resolve_ref_accepts_schema_release_tags() -> None: @@ -87,6 +94,84 @@ def test_codegen_preprocess_distributes_common_object_properties() -> None: assert request["oneOf"][1]["allOf"] == [{"$ref": "#/$defs/ScopeB"}] +def test_codegen_preprocess_normalizes_catchall_unions() -> None: + schema = { + "anyOf": [ + { + "type": "object", + "properties": {"type": {"type": "string", "const": "known"}}, + "required": ["type"], + }, + { + "title": "other", + "description": "Custom or future.", + "type": "object", + "properties": {"type": {"type": "string"}}, + "required": ["type"], + "not": {"anyOf": [{"const": "known"}]}, + "unevaluatedProperties": True, + }, + ], + "discriminator": {"propertyName": "type"}, + } + + normalized = _normalize_catchall_unions(schema) + + assert "discriminator" not in normalized + known, other = normalized["anyOf"] + assert known["properties"]["type"]["const"] == "known" + assert other["additionalProperties"] is True + assert other["properties"] == {"type": {"type": "string"}} + assert other["required"] == ["type"] + assert "not" not in other + assert "unevaluatedProperties" not in other + + +def test_extensible_union_excluded_tags_reads_not_clause() -> None: + union_def = { + "discriminator": {"propertyName": "action"}, + "anyOf": [ + {"properties": {"action": {"const": "accept"}}, "required": ["action"]}, + { + "title": "other", + "properties": {"action": {"type": "string"}}, + "not": { + "anyOf": [ + {"properties": {"action": {"const": "accept"}}}, + {"properties": {"action": {"const": "decline"}}}, + ] + }, + }, + ], + } + + assert _extensible_union_excluded_tags(union_def, "action") == ("accept", "decline") + + +def test_deserialize_field_specs_groups_by_fallback_and_excludes_meta() -> None: + definition = { + "required": ["items"], + "properties": { + "_meta": {"x-deserialize-default-on-error": True}, + "note": {"type": "string", "x-deserialize-default-on-error": True}, + "flag": {"type": "boolean", "default": False, "x-deserialize-default-on-error": True}, + "items": {"type": "array", "x-deserialize-skip-invalid-items": True}, + }, + } + + salvage, skip = _deserialize_field_specs(definition) + + assert salvage == {"lambda: None": ["note"], "lambda: False": ["flag"]} + assert skip == ["items"] + + +def test_fallback_expression_matches_schema_default_rules() -> None: + assert _fallback_expression({"default": False}, is_required=False) == "lambda: False" + assert _fallback_expression({"type": "array"}, is_required=True) == "lambda: []" + assert _fallback_expression({"type": ["array", "null"]}, is_required=False) == "lambda: None" + assert _fallback_expression({"type": "string"}, is_required=False) == "lambda: None" + + def test_codegen_postprocess_preserves_required_nullable_fields() -> None: schema = { "$defs": {