feat(spec): add schema generation pipeline & docs integration (stacked)#1175
Conversation
Summary of ChangesHello @muscariello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly overhauls the schema generation and documentation build process. It establishes Highlights
Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a significant improvement to the project's tooling by creating a pipeline to generate OpenAPI and JSON Schema from the canonical protobuf definitions. This change is well-supported by updates to the documentation, which now clearly define a2a.proto as the normative source of truth and include a helpful migration guide for legacy names and anchors. The new build scripts are robust and well-structured. My review includes a few minor suggestions for improving the scripts and clarifying the purpose of some of the new files.
| { | ||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | ||
| "title": "A2A Protocol Schemas", | ||
| "description": "Non-normative JSON Schema bundle extracted from generated OpenAPI document.", | ||
| "version": "v1", | ||
| "schemas": {"A2AServiceCancelTaskBody":{"type":"object","title":"--8<-- [start:CancelTaskRequest]"},"protobufAny":{"type":"object","properties":{"@type":{"type":"string"}},"additionalProperties":{}},"protobufNullValue":{"type":"string","enum":["NULL_VALUE"],"default":"NULL_VALUE","description":"`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\nThe JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value."},"rpcStatus":{"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"details":{"type":"array","items":{"type":"object","$ref":"#/definitions/protobufAny"}}}},"v1APIKeySecurityScheme":{"type":"object","properties":{"description":{"type":"string","description":"Description of this security scheme."},"location":{"type":"string","title":"Location of the API key, valid values are \"query\", \"header\", or \"cookie\""},"name":{"type":"string","description":"Name of the header, query or cookie parameter to be used."}},"title":"--8<-- [start:APIKeySecurityScheme]"},"v1AgentCapabilities":{"type":"object","properties":{"streaming":{"type":"boolean","title":"If the agent will support streaming responses"},"pushNotifications":{"type":"boolean","title":"If the agent can send push notifications to the clients webhook"},"extensions":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1AgentExtension"},"description":"Extensions supported by this agent."},"stateTransitionHistory":{"type":"boolean","description":"If the agent provides a history of state transitions for a task."}},"title":"--8<-- [start:AgentCapabilities]\nDefines the A2A feature set supported by the agent"},"v1AgentCard":{"type":"object","properties":{"protocolVersion":{"type":"string","description":"The version of the A2A protocol this agent supports."},"name":{"type":"string","title":"A human readable name for the agent.\nExample: \"Recipe Agent\""},"description":{"type":"string","title":"A description of the agent's domain of action/solution space.\nExample: \"Agent that helps users with recipes and cooking.\""},"url":{"type":"string","description":"A URL to the address the agent is hosted at. This represents the\npreferred endpoint as declared by the agent."},"preferredTransport":{"type":"string","description":"The transport of the preferred endpoint. If empty, defaults to JSONRPC."},"additionalInterfaces":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1AgentInterface"},"description":"Announcement of additional supported transports. Client can use any of\nthe supported transports."},"provider":{"$ref":"#/definitions/v1AgentProvider","description":"The service provider of the agent."},"version":{"type":"string","title":"The version of the agent.\nExample: \"1.0.0\""},"documentationUrl":{"type":"string","description":"A url to provide additional documentation about the agent."},"capabilities":{"$ref":"#/definitions/v1AgentCapabilities","description":"A2A Capability set supported by the agent."},"securitySchemes":{"type":"object","additionalProperties":{"$ref":"#/definitions/v1SecurityScheme"},"description":"The security scheme details used for authenticating with this agent."},"security":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Security"},"title":"protolint:disable REPEATED_FIELD_NAMES_PLURALIZED\nSecurity requirements for contacting the agent.\nThis list can be seen as an OR of ANDs. Each object in the list describes\none possible set of security requirements that must be present on a\nrequest. This allows specifying, for example, \"callers must either use\nOAuth OR an API Key AND mTLS.\"\nExample:\nsecurity {\n schemes { key: \"oauth\" value { list: [\"read\"] } }\n}\nsecurity {\n schemes { key: \"api-key\" }\n schemes { key: \"mtls\" }\n}"},"defaultInputModes":{"type":"array","items":{"type":"string"},"description":"protolint:enable REPEATED_FIELD_NAMES_PLURALIZED\nThe set of interaction modes that the agent supports across all skills.\nThis can be overridden per skill. Defined as mime types."},"defaultOutputModes":{"type":"array","items":{"type":"string"},"description":"The mime types supported as outputs from this agent."},"skills":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1AgentSkill"},"description":"Skills represent a unit of ability an agent can perform. This may\nsomewhat abstract but represents a more focused set of actions that the\nagent is highly likely to succeed at."},"supportsAuthenticatedExtendedCard":{"type":"boolean","description":"Whether the agent supports providing an extended agent card when\nthe user is authenticated, i.e. is the card from .well-known\ndifferent than the card from GetAgentCard."},"signatures":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1AgentCardSignature"},"description":"JSON Web Signatures computed for this AgentCard."},"iconUrl":{"type":"string","description":"An optional URL to an icon for the agent."}},"title":"--8<-- [start:AgentCard]\nAgentCard conveys key information:\n- Overall details (version, name, description, uses)\n- Skills; a set of actions/solutions the agent can perform\n- Default modalities/content types supported by the agent.\n- Authentication requirements\nNext ID: 19"},"v1AgentCardSignature":{"type":"object","properties":{"protected":{"type":"string","description":"The protected JWS header for the signature. This is always a\nbase64url-encoded JSON object. Required."},"signature":{"type":"string","description":"The computed signature, base64url-encoded. Required."},"header":{"type":"object","description":"The unprotected JWS header values."}},"description":"--8<-- [start:AgentCardSignature]\nAgentCardSignature represents a JWS signature of an AgentCard.\nThis follows the JSON format of an RFC 7515 JSON Web Signature (JWS).","required":["protected","signature"]},"v1AgentExtension":{"type":"object","properties":{"uri":{"type":"string","title":"The URI of the extension.\nExample: \"https://developers.google.com/identity/protocols/oauth2\""},"description":{"type":"string","title":"A description of how this agent uses this extension.\nExample: \"Google OAuth 2.0 authentication\""},"required":{"type":"boolean","title":"Whether the client must follow specific requirements of the extension.\nExample: false"},"params":{"type":"object","description":"Optional configuration for the extension."}},"description":"--8<-- [start:AgentExtension]\nA declaration of an extension supported by an Agent."},"v1AgentInterface":{"type":"object","properties":{"url":{"type":"string","description":"The url this interface is found at."},"transport":{"type":"string","description":"The transport supported this url. This is an open form string, to be\neasily extended for many transport protocols. The core ones officially\nsupported are JSONRPC, GRPC and HTTP+JSON."}},"description":"--8<-- [start:AgentInterface]\nDefines additional transport information for the agent."},"v1AgentProvider":{"type":"object","properties":{"url":{"type":"string","title":"The providers reference url\nExample: \"https://ai.google.dev\""},"organization":{"type":"string","title":"The providers organization name\nExample: \"Google\""}},"description":"--8<-- [start:AgentProvider]\nRepresents information about the service provider of an agent."},"v1AgentSkill":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier of the skill within this agent."},"name":{"type":"string","description":"A human readable name for the skill."},"description":{"type":"string","description":"A human (or llm) readable description of the skill\ndetails and behaviors."},"tags":{"type":"array","items":{"type":"string"},"title":"A set of tags for the skill to enhance categorization/utilization.\nExample: [\"cooking\", \"customer support\", \"billing\"]"},"examples":{"type":"array","items":{"type":"string"},"title":"A set of example queries that this skill is designed to address.\nThese examples should help the caller to understand how to craft requests\nto the agent to achieve specific goals.\nExample: [\"I need a recipe for bread\"]"},"inputModes":{"type":"array","items":{"type":"string"},"description":"Possible input modalities supported."},"outputModes":{"type":"array","items":{"type":"string"},"title":"Possible output modalities produced"},"security":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Security"},"description":"protolint:disable REPEATED_FIELD_NAMES_PLURALIZED\nSecurity schemes necessary for the agent to leverage this skill.\nAs in the overall AgentCard.security, this list represents a logical OR of\nsecurity requirement objects. Each object is a set of security schemes\nthat must be used together (a logical AND).\n\nprotolint:enable REPEATED_FIELD_NAMES_PLURALIZED"}},"description":"--8<-- [start:AgentSkill]\nAgentSkill represents a unit of action/solution that the agent can perform.\nOne can think of this as a type of highly reliable solution that an agent\ncan be tasked to provide. Agents have the autonomy to choose how and when\nto use specific skills, but clients should have confidence that if the\nskill is defined that unit of action can be reliably performed."},"v1Artifact":{"type":"object","properties":{"artifactId":{"type":"string","description":"Unique identifier (e.g. UUID) for the artifact. It must be at least unique\nwithin a task."},"name":{"type":"string","description":"A human readable name for the artifact."},"description":{"type":"string","description":"A human readable description of the artifact, optional."},"parts":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Part"},"description":"The content of the artifact."},"metadata":{"type":"object","description":"Optional metadata included with the artifact."},"extensions":{"type":"array","items":{"type":"string"},"description":"The URIs of extensions that are present or contributed to this Artifact."}},"description":"--8<-- [start:Artifact]\nArtifacts are the container for task completed results. These are similar\nto Messages but are intended to be the product of a task, as opposed to\npoint-to-point communication."},"v1AuthenticationInfo":{"type":"object","properties":{"schemes":{"type":"array","items":{"type":"string"},"title":"Supported authentication schemes - e.g. Basic, Bearer, etc"},"credentials":{"type":"string","title":"Optional credentials"}},"description":"--8<-- [start:PushNotificationAuthenticationInfo]\nDefines authentication details, used for push notifications."},"v1AuthorizationCodeOAuthFlow":{"type":"object","properties":{"authorizationUrl":{"type":"string","title":"The authorization URL to be used for this flow. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS"},"tokenUrl":{"type":"string","description":"The token URL to be used for this flow. This MUST be in the form of a URL.\nThe OAuth2 standard requires the use of TLS."},"refreshUrl":{"type":"string","description":"The URL to be used for obtaining refresh tokens. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS."},"scopes":{"type":"object","additionalProperties":{"type":"string"},"description":"The available scopes for the OAuth2 security scheme. A map between the\nscope name and a short description for it. The map MAY be empty."}},"title":"--8<-- [start:AuthorizationCodeOAuthFlow]"},"v1ClientCredentialsOAuthFlow":{"type":"object","properties":{"tokenUrl":{"type":"string","description":"The token URL to be used for this flow. This MUST be in the form of a URL.\nThe OAuth2 standard requires the use of TLS."},"refreshUrl":{"type":"string","description":"The URL to be used for obtaining refresh tokens. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS."},"scopes":{"type":"object","additionalProperties":{"type":"string"},"description":"The available scopes for the OAuth2 security scheme. A map between the\nscope name and a short description for it. The map MAY be empty."}},"title":"--8<-- [start:ClientCredentialsOAuthFlow]"},"v1DataPart":{"type":"object","properties":{"data":{"type":"object"}},"description":"--8<-- [start:DataPart]\nDataPart represents a structured blob. This is most commonly a JSON payload."},"v1FilePart":{"type":"object","properties":{"fileWithUri":{"type":"string"},"fileWithBytes":{"type":"string","format":"byte"},"mimeType":{"type":"string"},"name":{"type":"string"}},"description":"--8<-- [start:FilePart]\nFilePart represents the different ways files can be provided. If files are\nsmall, directly feeding the bytes is supported via file_with_bytes. If the\nfile is large, the agent should read the content as appropriate directly\nfrom the file_with_uri source."},"v1HTTPAuthSecurityScheme":{"type":"object","properties":{"description":{"type":"string","description":"Description of this security scheme."},"scheme":{"type":"string","description":"The name of the HTTP Authentication scheme to be used in the\nAuthorization header as defined in RFC7235. The values used SHOULD be\nregistered in the IANA Authentication Scheme registry.\nThe value is case-insensitive, as defined in RFC7235."},"bearerFormat":{"type":"string","description":"A hint to the client to identify how the bearer token is formatted.\nBearer tokens are usually generated by an authorization server, so\nthis information is primarily for documentation purposes."}},"title":"--8<-- [start:HTTPAuthSecurityScheme]"},"v1ImplicitOAuthFlow":{"type":"object","properties":{"authorizationUrl":{"type":"string","title":"The authorization URL to be used for this flow. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS"},"refreshUrl":{"type":"string","description":"The URL to be used for obtaining refresh tokens. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS."},"scopes":{"type":"object","additionalProperties":{"type":"string"},"description":"The available scopes for the OAuth2 security scheme. A map between the\nscope name and a short description for it. The map MAY be empty."}},"title":"--8<-- [start:ImplicitOAuthFlow]"},"v1ListTaskPushNotificationConfigResponse":{"type":"object","properties":{"configs":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1TaskPushNotificationConfig"},"description":"The list of push notification configurations."},"nextPageToken":{"type":"string","description":"A token, which can be sent as `page_token` to retrieve the next page.\nIf this field is omitted, there are no subsequent pages."}},"title":"--8<-- [start:ListTaskPushNotificationConfigResponse]"},"v1ListTasksResponse":{"type":"object","properties":{"tasks":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Task"},"description":"Array of tasks matching the specified criteria."},"nextPageToken":{"type":"string","description":"Token for retrieving the next page of results.\nEmpty string if no more results."},"totalSize":{"type":"integer","format":"int32","description":"Total number of tasks available (before pagination)."}},"title":"--8<-- [start:ListTasksResponse]"},"v1Message":{"type":"object","properties":{"messageId":{"type":"string","description":"The unique identifier (e.g. UUID)of the message. This is required and\ncreated by the message creator."},"contextId":{"type":"string","description":"The context id of the message. This is optional and if set, the message\nwill be associated with the given context."},"taskId":{"type":"string","description":"The task id of the message. This is optional and if set, the message\nwill be associated with the given task."},"role":{"$ref":"#/definitions/v1Role","description":"A role for the message."},"parts":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Part"},"description":"protolint:disable REPEATED_FIELD_NAMES_PLURALIZED\nParts is the container of the message content."},"metadata":{"type":"object","description":"protolint:enable REPEATED_FIELD_NAMES_PLURALIZED\nAny optional metadata to provide along with the message."},"extensions":{"type":"array","items":{"type":"string"},"description":"The URIs of extensions that are present or contributed to this Message."},"referenceTaskIds":{"type":"array","items":{"type":"string"},"description":"A list of task IDs that this message references for additional context."}},"description":"--8<-- [start:Message]\nMessage is one unit of communication between client and server. It is\nassociated with a context and optionally a task. Since the server is\nresponsible for the context definition, it must always provide a context_id\nin its messages. The client can optionally provide the context_id if it\nknows the context to associate the message to. Similarly for task_id,\nexcept the server decides if a task is created and whether to include the\ntask_id."},"v1MutualTlsSecurityScheme":{"type":"object","properties":{"description":{"type":"string","description":"Description of this security scheme."}},"title":"--8<-- [start:MutualTLSSecurityScheme]"},"v1OAuth2SecurityScheme":{"type":"object","properties":{"description":{"type":"string","description":"Description of this security scheme."},"flows":{"$ref":"#/definitions/v1OAuthFlows","title":"An object containing configuration information for the flow types supported"},"oauth2MetadataUrl":{"type":"string","description":"URL to the oauth2 authorization server metadata\n[RFC8414](https://datatracker.ietf.org/doc/html/rfc8414). TLS is required."}},"title":"--8<-- [start:OAuth2SecurityScheme]"},"v1OAuthFlows":{"type":"object","properties":{"authorizationCode":{"$ref":"#/definitions/v1AuthorizationCodeOAuthFlow"},"clientCredentials":{"$ref":"#/definitions/v1ClientCredentialsOAuthFlow"},"implicit":{"$ref":"#/definitions/v1ImplicitOAuthFlow"},"password":{"$ref":"#/definitions/v1PasswordOAuthFlow"}},"title":"--8<-- [start:OAuthFlows]"},"v1OpenIdConnectSecurityScheme":{"type":"object","properties":{"description":{"type":"string","description":"Description of this security scheme."},"openIdConnectUrl":{"type":"string","description":"Well-known URL to discover the [[OpenID-Connect-Discovery]] provider\nmetadata."}},"title":"--8<-- [start:OpenIdConnectSecurityScheme]"},"v1Part":{"type":"object","properties":{"text":{"type":"string"},"file":{"$ref":"#/definitions/v1FilePart"},"data":{"$ref":"#/definitions/v1DataPart"},"metadata":{"type":"object","description":"Optional metadata associated with this part."}},"description":"--8<-- [start:Part]\nPart represents a container for a section of communication content.\nParts can be purely textual, some sort of file (image, video, etc) or\na structured data blob (i.e. JSON)."},"v1PasswordOAuthFlow":{"type":"object","properties":{"tokenUrl":{"type":"string","description":"The token URL to be used for this flow. This MUST be in the form of a URL.\nThe OAuth2 standard requires the use of TLS."},"refreshUrl":{"type":"string","description":"The URL to be used for obtaining refresh tokens. This MUST be in the\nform of a URL. The OAuth2 standard requires the use of TLS."},"scopes":{"type":"object","additionalProperties":{"type":"string"},"description":"The available scopes for the OAuth2 security scheme. A map between the\nscope name and a short description for it. The map MAY be empty."}},"title":"--8<-- [start:PasswordOAuthFlow]"},"v1PushNotificationConfig":{"type":"object","properties":{"id":{"type":"string","description":"A unique identifier (e.g. UUID) for this push notification."},"url":{"type":"string","title":"Url to send the notification too"},"token":{"type":"string","title":"Token unique for this task/session"},"authentication":{"$ref":"#/definitions/v1AuthenticationInfo","title":"Information about the authentication to sent with the notification"}},"description":"--8<-- [start:PushNotificationConfig]\nConfiguration for setting up push notifications for task updates."},"v1Role":{"type":"string","enum":["ROLE_UNSPECIFIED","ROLE_USER","ROLE_AGENT"],"default":"ROLE_UNSPECIFIED","description":" - ROLE_USER: USER role refers to communication from the client to the server.\n - ROLE_AGENT: AGENT role refers to communication from the server to the client."},"v1Security":{"type":"object","properties":{"schemes":{"type":"object","additionalProperties":{"$ref":"#/definitions/v1StringList"}}}},"v1SecurityScheme":{"type":"object","properties":{"apiKeySecurityScheme":{"$ref":"#/definitions/v1APIKeySecurityScheme"},"httpAuthSecurityScheme":{"$ref":"#/definitions/v1HTTPAuthSecurityScheme"},"oauth2SecurityScheme":{"$ref":"#/definitions/v1OAuth2SecurityScheme"},"openIdConnectSecurityScheme":{"$ref":"#/definitions/v1OpenIdConnectSecurityScheme"},"mtlsSecurityScheme":{"$ref":"#/definitions/v1MutualTlsSecurityScheme"}},"title":"--8<-- [start:SecurityScheme]"},"v1SendMessageConfiguration":{"type":"object","properties":{"acceptedOutputModes":{"type":"array","items":{"type":"string"},"description":"The output modes that the agent is expected to respond with."},"pushNotification":{"$ref":"#/definitions/v1PushNotificationConfig","title":"A configuration of a webhook that can be used to receive updates"},"historyLength":{"type":"integer","format":"int32","description":"The maximum number of messages to include in the history. if 0, the\nhistory will be unlimited."},"blocking":{"type":"boolean","description":"If true, the message will be blocking until the task is completed. If\nfalse, the message will be non-blocking and the task will be returned\nimmediately. It is the caller's responsibility to check for any task\nupdates."}},"description":"--8<-- [start:MessageSendConfiguration]\nConfiguration of a send message request."},"v1SendMessageRequest":{"type":"object","properties":{"message":{"$ref":"#/definitions/v1Message","description":"The message to send to the agent."},"configuration":{"$ref":"#/definitions/v1SendMessageConfiguration","description":"Configuration for the send request."},"metadata":{"type":"object","description":"Optional metadata for the request."}},"title":"/////////// Request Messages ///////////\n--8<-- [start:SendMessageRequest]","required":["message"]},"v1SendMessageResponse":{"type":"object","properties":{"task":{"$ref":"#/definitions/v1Task"},"message":{"$ref":"#/definitions/v1Message"}},"title":"////// Response Messages ///////////\n--8<-- [start:SendMessageResponse]"},"v1StreamResponse":{"type":"object","properties":{"task":{"$ref":"#/definitions/v1Task"},"message":{"$ref":"#/definitions/v1Message"},"statusUpdate":{"$ref":"#/definitions/v1TaskStatusUpdateEvent"},"artifactUpdate":{"$ref":"#/definitions/v1TaskArtifactUpdateEvent"}},"title":"--8<-- [start:StreamResponse]\nThe stream response for a message. The stream should be one of the following\nsequences:\nIf the response is a message, the stream should contain one, and only one,\nmessage and then close\nIf the response is a task lifecycle, the first response should be a Task\nobject followed by zero or more TaskStatusUpdateEvents and\nTaskArtifactUpdateEvents. The stream should complete when the Task\nif in an interrupted or terminal state. A stream that ends before these\nconditions are met are"},"v1StringList":{"type":"object","properties":{"list":{"type":"array","items":{"type":"string"}}},"title":"protolint:disable REPEATED_FIELD_NAMES_PLURALIZED"},"v1Task":{"type":"object","properties":{"id":{"type":"string","description":"Unique identifier (e.g. UUID) for the task, generated by the server for a\nnew task."},"contextId":{"type":"string","description":"Unique identifier (e.g. UUID) for the contextual collection of interactions\n(tasks and messages). Created by the A2A server."},"status":{"$ref":"#/definitions/v1TaskStatus","description":"The current status of a Task, including state and a message."},"artifacts":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Artifact"},"description":"A set of output artifacts for a Task."},"history":{"type":"array","items":{"type":"object","$ref":"#/definitions/v1Message"},"description":"protolint:disable REPEATED_FIELD_NAMES_PLURALIZED\nThe history of interactions from a task."},"metadata":{"type":"object","description":"protolint:enable REPEATED_FIELD_NAMES_PLURALIZED\nA key/value object to store custom metadata about a task."}},"description":"--8<-- [start:Task]\nTask is the core unit of action for A2A. It has a current status\nand when results are created for the task they are stored in the\nartifact. If there are multiple turns for a task, these are stored in\nhistory."},"v1TaskArtifactUpdateEvent":{"type":"object","properties":{"taskId":{"type":"string","title":"The id of the task for this artifact"},"contextId":{"type":"string","title":"The id of the context that this task belongs too"},"artifact":{"$ref":"#/definitions/v1Artifact","title":"The artifact itself"},"append":{"type":"boolean","title":"Whether this should be appended to a prior one produced"},"lastChunk":{"type":"boolean","title":"Whether this represents the last part of an artifact"},"metadata":{"type":"object","description":"Optional metadata associated with the artifact update."}},"description":"--8<-- [start:TaskArtifactUpdateEvent]\nTaskArtifactUpdateEvent represents a task delta where an artifact has\nbeen generated."},"v1TaskPushNotificationConfig":{"type":"object","properties":{"name":{"type":"string","title":"The resource name of the config.\nFormat: tasks/{task_id}/pushNotificationConfigs/{config_id}"},"pushNotificationConfig":{"$ref":"#/definitions/v1PushNotificationConfig","description":"The push notification configuration details."}},"title":"--8<-- [start:TaskPushNotificationConfig]"},"v1TaskState":{"type":"string","enum":["TASK_STATE_UNSPECIFIED","TASK_STATE_SUBMITTED","TASK_STATE_WORKING","TASK_STATE_COMPLETED","TASK_STATE_FAILED","TASK_STATE_CANCELLED","TASK_STATE_INPUT_REQUIRED","TASK_STATE_REJECTED","TASK_STATE_AUTH_REQUIRED"],"default":"TASK_STATE_UNSPECIFIED","description":"--8<-- [start:TaskState]\nThe set of states a Task can be in.\n\n - TASK_STATE_SUBMITTED: Represents the status that acknowledges a task is created\n - TASK_STATE_WORKING: Represents the status that a task is actively being processed\n - TASK_STATE_COMPLETED: Represents the status a task is finished. This is a terminal state\n - TASK_STATE_FAILED: Represents the status a task is done but failed. This is a terminal state\n - TASK_STATE_CANCELLED: Represents the status a task was cancelled before it finished.\nThis is a terminal state.\n - TASK_STATE_INPUT_REQUIRED: Represents the status that the task requires information to complete.\nThis is an interrupted state.\n - TASK_STATE_REJECTED: Represents the status that the agent has decided to not perform the task.\nThis may be done during initial task creation or later once an agent\nhas determined it can't or won't proceed. This is a terminal state.\n - TASK_STATE_AUTH_REQUIRED: Represents the state that some authentication is needed from the upstream\nclient. Authentication is expected to come out-of-band thus this is not\nan interrupted or terminal state."},"v1TaskStatus":{"type":"object","properties":{"state":{"$ref":"#/definitions/v1TaskState","title":"The current state of this task"},"message":{"$ref":"#/definitions/v1Message","description":"A message associated with the status."},"timestamp":{"type":"string","format":"date-time","title":"Timestamp when the status was recorded.\nExample: \"2023-10-27T10:00:00Z\""}},"title":"--8<-- [start:TaskStatus]\nA container for the status of a task"},"v1TaskStatusUpdateEvent":{"type":"object","properties":{"taskId":{"type":"string","title":"The id of the task that is changed"},"contextId":{"type":"string","title":"The id of the context that the task belongs to"},"status":{"$ref":"#/definitions/v1TaskStatus","description":"The new status of the task."},"final":{"type":"boolean","description":"Whether this is the last status update expected for this task."},"metadata":{"type":"object","description":"Optional metadata to associate with the task update."}},"description":"--8<-- [start:TaskStatusUpdateEvent]\nTaskStatusUpdateEvent is a delta even on a task indicating that a task\nhas changed."}} | ||
| } |
There was a problem hiding this comment.
This file a2a-openapi-schemas.json is added in the PR but doesn't seem to be used by any of the new scripts or mentioned in the documentation. It also uses a different JSON Schema draft version (2020-12) and structure (a schemas key) compared to docs/spec-json/a2a.json. Was this file added intentionally? If it's an experimental artifact, it might be better to remove it to avoid confusion for other developers.
| regen_needed() { | ||
| if [ ! -f "$SCHEMA_JSON" ]; then return 0; fi | ||
| local newest_source | ||
| newest_source=$(stat -f %m "$PROTO_SRC" "$TS_SRC" | sort -nr | head -n1) | ||
| local schema_time | ||
| schema_time=$(stat -f %m "$SCHEMA_JSON") | ||
| [ "$newest_source" -gt "$schema_time" ] | ||
| } |
There was a problem hiding this comment.
This function regen_needed seems to be defined but is never called in the script. The script proceeds to regenerate the schema unconditionally. If this function is intended for future use to make regeneration conditional, it's worth noting that stat -f %m is specific to BSD/macOS and will not work on Linux systems, which typically use stat -c %Y. For now, it's dead code. Consider removing it if it's not going to be used, or fixing the portability issue and using it to avoid unnecessary regeneration on every build.
c5ac78a to
5c478d5
Compare
2b0e46b to
2e4d7b8
Compare
|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust pipeline for generating JSON schema from the canonical Protobuf definitions, which is a fantastic improvement for ensuring a single source of truth and reducing specification drift. The new scripts are well-structured, and the documentation updates clearly explain the new process. I've provided a few suggestions to improve the portability and efficiency of the new build scripts. Overall, this is an excellent change that significantly enhances the project's maintainability.
| echo "[build_docs] Checking schema freshness..." >&2 | ||
| echo "[build_docs] Regenerating a2a.json from proto (OpenAPI -> definitions)" >&2 | ||
| if [ -x "$ROOT_DIR/scripts/generate_openapi.sh" ]; then | ||
| OPENAPI_OUTPUT="$OPENAPI_FILE_V2" bash "$ROOT_DIR/scripts/generate_openapi.sh" || echo "[build_docs] Warning: OpenAPI generation failed" >&2 | ||
| if [ -s "$OPENAPI_FILE_V2" ]; then | ||
| if [ -x "$EXTRACT_SCRIPT" ]; then | ||
| bash "$EXTRACT_SCRIPT" "$OPENAPI_FILE_V2" "$SCHEMA_JSON" || echo "[build_docs] Warning: schema extraction failed" >&2 | ||
| # Copy into docs/ tree so MkDocs can publish the ephemeral artifact. | ||
| if [ -f "$SCHEMA_JSON" ]; then | ||
| mkdir -p "$SCHEMA_JSON_SITE_DIR" | ||
| cp "$SCHEMA_JSON" "$SCHEMA_JSON_SITE_FILE" | ||
| echo "[build_docs] Published schema to $SCHEMA_JSON_SITE_FILE" >&2 | ||
| else | ||
| echo "[build_docs] Schema file missing after extraction: $SCHEMA_JSON" >&2 | ||
| fi | ||
| else | ||
| echo "[build_docs] Extraction script not executable: $EXTRACT_SCRIPT" >&2 | ||
| fi | ||
| else | ||
| echo "[build_docs] OpenAPI swagger not produced (expected at $OPENAPI_FILE_V2)" >&2 | ||
| fi | ||
| else | ||
| echo "[build_docs] generate_openapi.sh missing or not executable; skipping proto-derived schema generation." >&2 | ||
| fi |
There was a problem hiding this comment.
This block unconditionally regenerates the schema on every build, which is inefficient and doesn't use the regen_needed function.
Additionally, the logic to copy the generated schema into the docs/spec-json directory is nested inside the regeneration block. This will cause an issue: if the schema is up-to-date and regeneration is skipped, the file won't be copied, and mkdocs will fail because it can't find the file.
The regeneration logic should be conditional, and the copy operation should be moved outside to ensure the schema file is always available for the documentation build.
echo "[build_docs] Checking schema freshness..." >&2
if regen_needed; then
echo "[build_docs] Regenerating a2a.json from proto (OpenAPI -> definitions)" >&2
if [ -x "$ROOT_DIR/scripts/generate_openapi.sh" ]; then
OPENAPI_OUTPUT="$OPENAPI_FILE_V2" bash "$ROOT_DIR/scripts/generate_openapi.sh" || echo "[build_docs] Warning: OpenAPI generation failed" >&2
if [ -s "$OPENAPI_FILE_V2" ]; then
if [ -x "$EXTRACT_SCRIPT" ]; then
bash "$EXTRACT_SCRIPT" "$OPENAPI_FILE_V2" "$SCHEMA_JSON" || echo "[build_docs] Warning: schema extraction failed" >&2
else
echo "[build_docs] Extraction script not executable: $EXTRACT_SCRIPT" >&2
fi
else
echo "[build_docs] OpenAPI swagger not produced (expected at $OPENAPI_FILE_V2)" >&2
fi
else
echo "[build_docs] generate_openapi.sh missing or not executable; skipping proto-derived schema generation." >&2
fi
else
echo "[build_docs] Schema is up to date. Skipping regeneration." >&2
fi
# Copy into docs/ tree so MkDocs can publish the ephemeral artifact.
if [ -f "$SCHEMA_JSON" ]; then
mkdir -p "$SCHEMA_JSON_SITE_DIR"
cp "$SCHEMA_JSON" "$SCHEMA_JSON_SITE_FILE"
echo "[build_docs] Published schema to $SCHEMA_JSON_SITE_FILE" >&2
else
echo "[build_docs] Schema file missing: $SCHEMA_JSON. A full build might be needed." >&2
fi|
/gemini review |
There was a problem hiding this comment.
Code Review
This pull request introduces a significant improvement by establishing a clear pipeline for generating schema artifacts from a single normative source (a2a.proto). The new scripts are well-structured and robust, and the documentation updates are excellent, clearly explaining the new process and principles for developers and users of the protocol. This will greatly enhance the maintainability and consistency of the specification. My review includes a few minor suggestions to further improve the new scripts' robustness and fix a small formatting issue in the documentation.
|
@muscariello These changes look good to merge into the transports branch, other than some of the changes to the mkdocs.yml file that I'm not clear why we are removing things. I have some questions about our versioning plans but we can discuss that before we merge the transports branch into main. Do you want me to merge into transports and we can discuss the mkdocs changes there? |
|
@darrelmiller I can also address your comments here before merging to transport. |
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
- Use regen_needed() function to conditionally regenerate schema only when sources are newer - Move schema copy outside regeneration block to ensure docs/spec-json/a2a.json always exists - Fix regen_needed() portability to work on both macOS (BSD stat) and Linux (GNU stat) - Improve logging with clear up-to-date vs regeneration messages Addresses PR feedback about unconditional regeneration and nested copy logic. Signed-off-by: Luca Muscariello <lumuscar@cisco.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Fixed duplicate function declarations that caused bash syntax error (exit code 2) - Kept cleaner implementation using uname detection for cross-platform compatibility - Script now runs successfully with proper conditional schema regeneration Signed-off-by: Luca Muscariello <lumuscar@cisco.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Comment out spec-json.md redirect (target may not exist if schema generation fails) - Disable Python SDK navigation entries (sdk/python/api/index.html doesn't exist) - Remove broken sdk/python/ redirect mapping - Add explanatory comments for disabled features - Eliminates MkDocs build warnings about missing redirect targets Signed-off-by: Luca Muscariello <muscariello@ieee.org>
e744b5c to
31efa92
Compare
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Add lowercase 'aip' term to fix spelling check failures. The uppercase 'AIP' was already present but lowercase variant was missing. Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Add lowercase 'aip' term to fix spelling check failures. The uppercase 'AIP' was already present but lowercase variant was missing. Signed-off-by: Luca Muscariello <muscariello@ieee.org>
Stacked tooling & documentation improvements atop transports without rebase.
Related to #1160
Highlights: