A2A の上の AdCP 慣例
このページのルールは AdCP 固有のセマンティクスを A2A に重ねます。非 AdCP の A2A エージェントはそれらを強制せず、準拠する出力を生成することを期待されるべきではありません。- 単一アーティファクト不変条件。 AdCP タスクはすべての出力パーツを含む 1 つのアーティファクトを生成する。クライアントは
artifacts[0]から読む。セラーが複数の distinct な成果物を必要とする場合、複数のアーティファクトではなく別々のタスクとしてモデル化すべき。 - Last-DataPart 権威。 1 つのアーティファクトに複数の DataPart が現れるとき(ストリーミング中に典型的)、最後のものが権威的。以前の DataPart は置き換えられた進捗スナップショット。
- First-DataPart は interim 用。
status.message.partsに複数の DataPart が現れるとき、最初のものが使われる — interim 更新は累積ではなく単一イベントのスナップショット。 - ラッパー拒否。
.dataが{ response: {...} }(responseという単一キー)の DataPart は、有効なペイロードではなくフレームワークラッパーのバグとして扱われる。
ワイヤー形式の互換性
このアルゴリズムは A2A 1.0 と v0.3 の両レスポンスを扱います。抽出は 1 つのワイヤー形式を仮定してはなりません — 同じ AdCP クライアントが v0.3 互換性期間中に両方と話す可能性があります。 State 値。status.state フィールドは、1.0 では ProtoJSON 形式("TASK_STATE_COMPLETED"、"TASK_STATE_WORKING" …)、v0.3 では小文字形式("completed"、"working" …)で到着します。クライアントは比較前に正規化します。
Part 形状。 1.0 の DataPart は非 null の data フィールドを持ち kind を持ちません。v0.3 の DataPart は kind: "data" と data フィールドを持ちます。両方が「data フィールドが非 null オブジェクト」を満たします。同じことが TextParts(text フィールド存在)と FileParts(1.0 の url/raw、または v0.3 の kind: "file")にも当てはまります。A2A 1.0 §4.1.6 に従い、Part は厳格な oneof です — text、raw、url、data の正確に 1 つが設定されます。複数のコンテンツフィールドを持つ Part を受け取るクライアントは、それを不正な形式として扱うべきです(SHOULD)。
ストリーミングエンベロープ。 A2A 1.0 は、ストリーミングレスポンスとプッシュ通知ペイロードを、task、message、statusUpdate、artifactUpdate の正確に 1 つのキーを持つ StreamResponse oneof でラップします(A2A 1.0 §3.2.3、§4.3.3)。非ストリーミングレスポンス(例: tasks/get、または HTTP 上の v0.3)は素のオブジェクトを配信します。抽出は下のアルゴリズムを適用する前に単一キーエンベロープをアンラップします。
ステータスベースの抽出
抽出場所はタスクのステータスに依存します。この表の State 名は正規化された小文字形式で示されます — 生のワイヤー値ではなく正規化された state に対してマッチしてください。
Final 状態は、
.artifacts が欠如または空のとき status.message.parts[] にフォールバックします — これは、別のアーティファクトではなくステータスメッセージに最終ペイロードを置くサーバーをカバーします。
Canceled タスクはめったにデータを運びません — DataPart が存在しないとき抽出は null を返し、それが期待されるケースです。Rejected タスクは、リクエストがなぜ拒否されたか(tier/policy/validation)を記述する adcp_error DataPart を運ぶことが期待されます。
抽出アルゴリズム
クライアントは、これらのステップを使って A2A レスポンスから AdCP データを抽出しなければなりません(MUST):-
ストリームエンベロープをアンラップ。 入力が
task、message、statusUpdate、artifactUpdateという正確に 1 つのトップレベルキーを持つオブジェクトで、そのキーの値が非 null・非配列オブジェクトなら、入力をその値で置き換える(A2A 1.0StreamResponseoneof)。素のTask/TaskStatusUpdateEventオブジェクト — 非ストリーミングレスポンスまたは v0.3 — は変更なく通過。artifactUpdateはタスクステータスを運ばない。アンラップされるとstatus.stateは欠如しステップ 1 は null を返す。 正確に一度 アンラップする。クライアントは再帰してはならない(MUST NOT)。アンラップされた内部オブジェクト自体が単一キーエンベロープ形状({ task: { task: {...} } }または任意の組み合わせ)を持つ場合、不正な形式として扱い null を返す — これはネストされたエンベロープの密輸試行。内部値のトップレベルキーがtask/message/statusUpdate/artifactUpdateのいずれかを含むエンベロープは拒否されなければならない(MUST)。 素の{ message }エンベロープ(帯域外エージェントメッセージ)はタスク指向の抽出器によって無視されなければならない(MUST) — アンラップされたオブジェクトがstatus.stateを持たないときステップ 1 は null を返す。Webhook/SSE ハンドラーは、認識されない{ message }エンベロープに200 OK承認を返してはならない(MUST NOT)。エンドポイントをプローブする攻撃者への存在オラクルとして動作するのを避けるため、400 Bad Requestを返すかトランスポート層で黙って破棄する。 -
status.stateを読む。 欠如なら null を返す。比較前に小文字形式に正規化(TASK_STATE_COMPLETED→completed)。正規化後、state は 正確な ASCII 文字列等価 で既知の final/interim トークンの 1 つに一致しなければならない(MUST)。クライアントは、繰り返しのセパレーターを折り畳んだり、空白をトリムしたり、ASCII 小文字を超えた Unicode case-folding を適用したりしてはならない(MUST NOT)。他の任意の値 — クライアントが認識しない新しいTASK_STATE_*入力を含む — は「unknown」で、抽出は null を返す(ステップ 4)。 -
Final 状態(
completed、failed、canceled、rejected): a.artifacts[0].parts[]で DataPart(dataフィールドが非 null オブジェクトの Part —kindの存在にかかわらず)を探す。 b. 最後の DataPart を権威的として使う(Last-DataPart Authority を参照)。 c. ラッパーを拒否: DataPart の.dataがオブジェクトを含む単一キーresponseを持つ場合、これはフレームワークラッパーのバグ。throw またはエラーをログ。 d..dataを返す。 e. フォールバック: アーティファクトがない、またはアーティファクトに DataPart がない場合、ステップ 3 を使ってstatus.message.parts[]を確認。 -
Interim 状態(
working、submitted、input-required、auth-required): a.status.message.parts[]で DataPart を探す。 b. 最初の DataPart を使う。 c..dataを返す、または DataPart が見つからなければ null。 - Unknown 状態: null を返す。前方互換のクライアントは認識されないステータス値で throw すべきではない(SHOULD NOT)。
TASK_STATE_ プレフィックスを除去、小文字化、アンダースコアをハイフンに置換。これは A2A 1.0("TASK_STATE_INPUT_REQUIRED")と v0.3("input-required")の両方を同じ値にマップします。
DataPart 検出はフィールド存在を使います — 1.0 Part { "data": {...} } と v0.3 Part { "kind": "data", "data": {...} } は両方とも「非 null オブジェクト data フィールド」テストを満たします。
Last-DataPart Authority
Final 状態については、artifacts[0].parts[] の 最後の DataPart が権威的です。ストリーミング中、中間の DataPart は最終結果に置き換えられる古い進捗データを含みうる:
{"progress": 25} ではなく {"products": [...], "total": 12} です。
Interim 状態については、interim 更新が累積ではなく単一イベントのスナップショットなので、最初の DataPart が使われます。
ラッパー拒否
クライアントは、.data がフレームワーク固有のオブジェクトでラップされた DataPart を拒否しなければなりません(MUST):
.data が値がオブジェクトの response という正確に 1 つのキーを持つ場合、それはラッパーです。これはサーバー側のバグです — クライアントは黙ってアンラップするのではなく throw またはエラーをログすべきです。
ラッパー検出は Final 状態のみ(アーティファクト)に適用されます。Interim ステータスメッセージは軽量な進捗スナップショットです — status.message.parts にラッパー検出は不要です。
例外: 他のキーと並んで response を持つ .data オブジェクトはラッパーでは ありません:
エラー抽出との関係
このアルゴリズムは、エラーペイロード(adcp_error)を含む A2A レスポンスから 任意の AdCP データを抽出します。エラー固有の抽出(Transport Error Mapping)は、抽出されたデータで adcp_error キーを確認する特殊化です。
transport-errors 仕様は、すべてのアーティファクトを adcp_error についてスキャンする独自の extractAdcpErrorFromA2A 関数を提供します。その関数はエラー検出(すべてのパーツをエラーキーについてスキャン)に最適化されています。この関数は汎用の抽出器(最初のアーティファクトからの最後の DataPart)です。単一の adcp_error DataPart を持つ failed タスクについては、両方が等価な結果を生成します。
典型的なクライアントフロー:
セキュリティ考慮事項
セラー制御データ
.artifacts[].parts[].data と status.message.parts[].data のすべてのデータはセラー制御です。Transport Error Mapping のプロンプトインジェクション、データ境界、サイズ制限の要件が適用されます。
プロトタイプ汚染
クライアントは、キーをフィルターせずに抽出された DataPart ペイロードをObject.assign やスプレッド経由でアプリケーション状態にマージしてはなりません(MUST NOT)。マージ前に期待されるタスクレスポンススキーマに対して検証してください。
FilePart URI 検証
A2A レスポンスは FilePart を含みうる。1.0 ではこれらはurl フィールド(参照によるファイル)または raw フィールド(base64 バイト)を運ぶ Part。v0.3 では uri フィールドを伴う kind: "file" を運ぶ。クライアントは、URL が https スキームを使い、userinfo コンポーネントを含まず、期待されるドメイン許可リストに一致することを検証しなければならない(MUST)。javascript:、data:、file:、http: URI を拒否。raw パーツについては、受け入れる前に最大デコードサイズを強制する。
Auth チャレンジ URL 検証
auth-required を扱うとき、セラーは status.message.parts に auth チャレンジ — 通常 auth_scheme、challenge_url、scopes のようなフィールドを持つ DataPart — を送る。クライアントが開くまたはフェッチするセラー制御の URL は OAuth フィッシングと SSRF ベクター。任意のユーザー向けまたはプログラム的な auth フローを開始する前に、クライアントは challenge_url を検証しなければならない(MUST):
- スキームは
httpsでなければならない(MUST)。http:、javascript:、data:、file:を拒否。 - URL は userinfo コンポーネント(
user:pass@host形式)を含んではならない(MUST NOT)。 - ホストは、このエージェントカードの認証されたセラーの登録された auth オリジンに一致しなければならない(MUST)。クライアントは、タスクペイロードから導出されるのではなく、Agent Card の
supportedInterfaces[].urlオリジンまたは宣言されたauthOrigin拡張フィールドからシードされたエージェントごとの許可リストを維持すべき(SHOULD)。 - 任意の
redirect_uri、return_url、または類似のクエリパラメーターは、ナビゲーション前にクライアントによって落とされるか上書きされなければならない(MUST)。セラー供給のリダイレクトを決して転送しない。 scopesは付与ではなくリクエストとして扱われなければならない(MUST)。scopes をユーザーに示し、各チャレンジで新鮮な同意を得る。
セラー制御文字列の衛生
すべてのadcp_error.message、adcp_error.details.*、ステータス TextPart コンテンツはセラー制御です。これらを UI にレンダリングするクライアントは、ターゲットコンテキスト(HTML、Slack、CLI)用にエスケープしなければならない(MUST)。それらをログするクライアントは、ログ注入を防ぐため CRLF を除去しなければならない(MUST)。これは adcp_error を運ぶすべての状態(failed、rejected、システム開始の canceled)と自由テキストの status.message に適用されます。
サイズ制限
クライアントはスキーマ検証前に最大 DataPart サイズ(例: 1MB)を強制すべきです(SHOULD)。エラーペイロード(4096 バイトに上限)とは異なり、成功ペイロードはより大きくなりうるが依然として境界が必要です。仲介者注入
last-DataPart 慣例は、アーティファクトが単一の信頼された送信者から無傷で受け取られることを仮定します。マルチホップシナリオ(buyer → orchestrator → seller)では、仲介者が追加のパーツを注入できます。仲介者を通じて動作するクライアントは、アーティファクトのパーツ数が期待に一致することを検証すべきです(SHOULD)。クライアントライブラリ要件
この仕様を実装するクライアントライブラリは次をしなければなりません(MUST):- A2A 1.0 ストリームエンベロープをアンラップ。
task、message、statusUpdate、artifactUpdateのキーを持つ単一キーオブジェクトはStreamResponseラッパー — アルゴリズムの残りを適用する前に内部オブジェクトにアンラップ。素のオブジェクトは変更なく通過。 - A2A 1.0 と v0.3 の両ワイヤー形状を受け入れる。 比較前に
status.stateを正規化(TASK_STATE_プレフィックス除去、小文字化、アンダースコアをハイフンに)。kindではなくフィールド存在(dataが非 null オブジェクト)で DataPart を検出。 - 正規化された state で分岐。 Final 状態(
completed、failed、canceled、rejected)はアーティファクトを使い、interim 状態(working、submitted、input-required、auth-required)はstatus.message.partsを使う。 - Final 状態には最後の DataPart を使う。 null、非オブジェクト、配列
.dataの DataPart をスキップ。 - Interim 状態には最初の DataPart を使う。
- ラッパーを検出し拒否。 単一キー
{response: {...}}ペイロードはバグ。 - 優雅にフォールバック。 Final 状態でアーティファクトが空なら、
status.message.partsを確認。 - Unknown 状態を扱う。 null を返し、throw しない。
テストベクター
機械可読なテストベクターは/static/test-vectors/a2a-response-extraction.json で利用可能です。各ベクターは次を含みます:
status: A2A タスクステータスpath: 抽出パス(artifact、status_message、またはnone)response: A2A Task または TaskStatusUpdateEventexpected_data: 抽出されるべき AdCP データ(またはnull)expected_error_type: 存在する場合、抽出は throw すべき(例:wrapper_detected)
関連項目
- A2A Response Format — セラーの正準レスポンス構造
- Transport Error Mapping — MCP と A2A からのエラー抽出
- MCP Response Extraction — MCP の同等仕様
- A2A Guide — A2A トランスポート統合