> ## Documentation Index
> Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt
> Use this file to discover all available pages before exploring further.

# A2A レスポンス抽出

> A2A Task オブジェクトから AdCP レスポンスデータを抽出する方法: ステータスベースの分岐、last-DataPart 権威、ラッパー拒否、クライアント実装要件。

このページは、A2A Task オブジェクトと TaskStatusUpdateEvents から AdCP レスポンスデータを抽出する規範的アルゴリズムを定義します。セラーが生成しなければならない正準レスポンス構造については [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) を参照。エラー固有の抽出については [Transport Error Mapping](/docs/building/operating/transport-errors) を参照。

## 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 に対してマッチしてください。

| Status           | Type         | Data Location                                              | DataPart Selection                                  |
| ---------------- | ------------ | ---------------------------------------------------------- | --------------------------------------------------- |
| `completed`      | Final        | `.artifacts[0].parts[]`（フォールバック: `status.message.parts[]`） | 最後の DataPart                                        |
| `failed`         | Final        | `.artifacts[0].parts[]`（フォールバック: `status.message.parts[]`） | 最後の DataPart                                        |
| `canceled`       | Final        | `.artifacts[0].parts[]`                                    | 最後の DataPart（通常なし）                                  |
| `rejected`       | Final（1.0）   | `.artifacts[0].parts[]`                                    | 最後の DataPart（ポリシー/検証拒否の `adcp_error` を運ぶ）           |
| `working`        | Interim      | `status.message.parts[]`                                   | 最初の DataPart                                        |
| `submitted`      | Interim      | `status.message.parts[]`                                   | 最初の DataPart                                        |
| `input-required` | Interim      | `status.message.parts[]`                                   | 最初の DataPart                                        |
| `auth-required`  | Interim（1.0） | `status.message.parts[]`                                   | 最初の DataPart（auth チャレンジデータ — scheme、URL、scopes を運ぶ） |

Final 状態は、`.artifacts` が欠如または空のとき `status.message.parts[]` にフォールバックします — これは、別のアーティファクトではなくステータスメッセージに最終ペイロードを置くサーバーをカバーします。

Canceled タスクはめったにデータを運びません — DataPart が存在しないとき抽出は null を返し、それが期待されるケースです。Rejected タスクは、リクエストがなぜ拒否されたか（tier/policy/validation）を記述する `adcp_error` DataPart を運ぶことが期待されます。

## 抽出アルゴリズム

クライアントは、これらのステップを使って A2A レスポンスから AdCP データを抽出しなければなりません（MUST）:

0. **ストリームエンベロープをアンラップ。** 入力が `task`、`message`、`statusUpdate`、`artifactUpdate` という正確に 1 つのトップレベルキーを持つオブジェクトで、そのキーの値が非 null・非配列オブジェクトなら、入力をその値で置き換える（A2A 1.0 `StreamResponse` oneof）。素の `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` を返すかトランスポート層で黙って破棄する。
1. **`status.state` を読む。** 欠如なら null を返す。比較前に小文字形式に正規化（`TASK_STATE_COMPLETED` → `completed`）。正規化後、state は **正確な ASCII 文字列等価** で既知の final/interim トークンの 1 つに一致しなければならない（MUST）。クライアントは、繰り返しのセパレーターを折り畳んだり、空白をトリムしたり、ASCII 小文字を超えた Unicode case-folding を適用したりしてはならない（MUST NOT）。他の任意の値 — クライアントが認識しない新しい `TASK_STATE_*` 入力を含む — は「unknown」で、抽出は null を返す（ステップ 4）。
2. **Final 状態**（`completed`、`failed`、`canceled`、`rejected`）:
   a. `artifacts[0].parts[]` で DataPart（`data` フィールドが非 null オブジェクトの Part — `kind` の存在にかかわらず）を探す。
   b. **最後の** DataPart を権威的として使う（[Last-DataPart Authority](#last-datapart-authority) を参照）。
   c. **ラッパーを拒否**: DataPart の `.data` がオブジェクトを含む単一キー `response` を持つ場合、これはフレームワークラッパーのバグ。throw またはエラーをログ。
   d. `.data` を返す。
   e. **フォールバック**: アーティファクトがない、またはアーティファクトに DataPart がない場合、ステップ 3 を使って `status.message.parts[]` を確認。
3. **Interim 状態**（`working`、`submitted`、`input-required`、`auth-required`）:
   a. `status.message.parts[]` で DataPart を探す。
   b. **最初の** DataPart を使う。
   c. `.data` を返す、または DataPart が見つからなければ null。
4. **Unknown 状態**: null を返す。前方互換のクライアントは認識されないステータス値で throw すべきではない（SHOULD NOT）。

State 正規化: `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` フィールド」テストを満たします。

<CodeGroup>
  ```javascript A2A Client theme={null}
  function normalizeState(state) {
    if (typeof state !== 'string') return null;
    return state.replace(/^TASK_STATE_/, '').toLowerCase().replace(/_/g, '-');
  }

  function isDataPart(p) {
    return p != null
      && p.data != null
      && typeof p.data === 'object'
      && !Array.isArray(p.data);
  }

  // A2A 1.0 StreamResponse oneof: { task } | { message } | { statusUpdate } | { artifactUpdate }
  function unwrapStreamEnvelope(input) {
    if (input == null || typeof input !== 'object' || Array.isArray(input)) return input;
    const keys = Object.keys(input);
    if (keys.length !== 1) return input;
    const envelopeKeys = ['task', 'message', 'statusUpdate', 'artifactUpdate'];
    if (envelopeKeys.includes(keys[0]) && typeof input[keys[0]] === 'object' && input[keys[0]] !== null) {
      return input[keys[0]];
    }
    return input;
  }

  function extractAdcpResponseFromA2A(input) {
    const task = unwrapStreamEnvelope(input);
    const state = normalizeState(task?.status?.state);
    if (!state) return null;

    const FINAL = ['completed', 'failed', 'canceled', 'rejected'];
    const INTERIM = ['working', 'submitted', 'input-required', 'auth-required'];

    if (FINAL.includes(state)) {
      // Final: last DataPart from artifacts[0]
      const artifact = task.artifacts?.[0];
      if (artifact?.parts) {
        const dataParts = artifact.parts.filter(isDataPart);
        if (dataParts.length > 0) {
          const last = dataParts[dataParts.length - 1];
          // Reject framework wrappers
          const keys = Object.keys(last.data);
          if (keys.length === 1 && keys[0] === 'response' && typeof last.data.response === 'object') {
            throw new Error(
              'Invalid response format: DataPart contains wrapper object {response: {...}}. ' +
              'This is a server-side bug.'
            );
          }
          return last.data;
        }
      }
      // Fallback to status.message.parts
      return extractFromMessage(task);
    }

    if (INTERIM.includes(state)) {
      return extractFromMessage(task);
    }

    return null; // Unknown state
  }

  function extractFromMessage(task) {
    const parts = task.status?.message?.parts;
    if (!Array.isArray(parts)) return null;
    const dataPart = parts.find(isDataPart);
    return dataPart?.data ?? null;
  }
  ```
</CodeGroup>

## Last-DataPart Authority

Final 状態については、`artifacts[0].parts[]` の **最後の** DataPart が権威的です。ストリーミング中、中間の DataPart は最終結果に置き換えられる古い進捗データを含みうる:

```json theme={null}
{
  "status": {"state": "TASK_STATE_COMPLETED"},
  "artifacts": [{
    "parts": [
      {"text": "Found products"},
      {"data": {"progress": 25}},
      {"data": {"products": [...], "total": 12}}
    ]
  }]
}
```

抽出されるデータは `{"progress": 25}` ではなく `{"products": [...], "total": 12}` です。

Interim 状態については、interim 更新が累積ではなく単一イベントのスナップショットなので、**最初の** DataPart が使われます。

## ラッパー拒否

クライアントは、`.data` がフレームワーク固有のオブジェクトでラップされた DataPart を拒否しなければなりません（MUST）:

```json theme={null}
// REJECTED: wrapper detected
{"data": {"response": {"products": [...]}}}

// ACCEPTED: direct payload
{"data": {"products": [...]}}
```

検出ルール: `.data` が値がオブジェクトの `response` という正確に 1 つのキーを持つ場合、それはラッパーです。これはサーバー側のバグです — クライアントは黙ってアンラップするのではなく throw またはエラーをログすべきです。

ラッパー検出は **Final 状態のみ**（アーティファクト）に適用されます。Interim ステータスメッセージは軽量な進捗スナップショットです — `status.message.parts` にラッパー検出は不要です。

**例外**: 他のキーと並んで `response` を持つ `.data` オブジェクトはラッパーでは **ありません**:

```json theme={null}
// NOT a wrapper — response is one of several keys
{"data": {"response": {...}, "status": "completed", "errors": []}}
```

## エラー抽出との関係

このアルゴリズムは、エラーペイロード（`adcp_error`）を含む A2A レスポンスから *任意の* AdCP データを抽出します。エラー固有の抽出（[Transport Error Mapping](/docs/building/operating/transport-errors)）は、抽出されたデータで `adcp_error` キーを確認する特殊化です。

transport-errors 仕様は、すべてのアーティファクトを `adcp_error` についてスキャンする独自の `extractAdcpErrorFromA2A` 関数を提供します。その関数はエラー検出（すべてのパーツをエラーキーについてスキャン）に最適化されています。この関数は汎用の抽出器（最初のアーティファクトからの最後の DataPart）です。単一の `adcp_error` DataPart を持つ failed タスクについては、両方が等価な結果を生成します。

典型的なクライアントフロー:

```javascript theme={null}
function handleA2aResponse(task) {
  const data = extractAdcpResponseFromA2A(task);

  // Check if the extracted data is an error
  if (data?.adcp_error) {
    return handleError(data.adcp_error);
  }

  return handleSuccess(data);
}
```

## セキュリティ考慮事項

### セラー制御データ

`.artifacts[].parts[].data` と `status.message.parts[].data` のすべてのデータはセラー制御です。[Transport Error Mapping](/docs/building/operating/transport-errors#security-considerations) のプロンプトインジェクション、データ境界、サイズ制限の要件が適用されます。

### プロトタイプ汚染

クライアントは、キーをフィルターせずに抽出された 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 をユーザーに示し、各チャレンジで新鮮な同意を得る。

クライアントがチャレンジ URL をサーバー側でフェッチする場合、レスポンスサイズとタイムアウトの境界が適用される（例: 256 KB レスポンス上限、10 秒タイムアウト、リダイレクト制限 3）。

### セラー制御文字列の衛生

すべての `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）:

1. **A2A 1.0 ストリームエンベロープをアンラップ。** `task`、`message`、`statusUpdate`、`artifactUpdate` のキーを持つ単一キーオブジェクトは `StreamResponse` ラッパー — アルゴリズムの残りを適用する前に内部オブジェクトにアンラップ。素のオブジェクトは変更なく通過。
2. **A2A 1.0 と v0.3 の両ワイヤー形状を受け入れる。** 比較前に `status.state` を正規化（`TASK_STATE_` プレフィックス除去、小文字化、アンダースコアをハイフンに）。`kind` ではなくフィールド存在（`data` が非 null オブジェクト）で DataPart を検出。
3. **正規化された state で分岐。** Final 状態（`completed`、`failed`、`canceled`、`rejected`）はアーティファクトを使い、interim 状態（`working`、`submitted`、`input-required`、`auth-required`）は `status.message.parts` を使う。
4. **Final 状態には最後の DataPart を使う。** null、非オブジェクト、配列 `.data` の DataPart をスキップ。
5. **Interim 状態には最初の DataPart を使う。**
6. **ラッパーを検出し拒否。** 単一キー `{response: {...}}` ペイロードはバグ。
7. **優雅にフォールバック。** Final 状態でアーティファクトが空なら、`status.message.parts` を確認。
8. **Unknown 状態を扱う。** null を返し、throw しない。

## テストベクター

機械可読なテストベクターは [`/static/test-vectors/a2a-response-extraction.json`](https://adcontextprotocol.org/test-vectors/a2a-response-extraction.json) で利用可能です。各ベクターは次を含みます:

* `status`: A2A タスクステータス
* `path`: 抽出パス（`artifact`、`status_message`、または `none`）
* `response`: A2A Task または TaskStatusUpdateEvent
* `expected_data`: 抽出されるべき AdCP データ（または `null`）
* `expected_error_type`: 存在する場合、抽出は throw すべき（例: `wrapper_detected`）

クライアントライブラリはこれらのベクターに対して抽出ロジックを検証すべきです（SHOULD）。

## 関連項目

* [A2A Response Format](/docs/building/by-layer/L0/a2a-response-format) — セラーの正準レスポンス構造
* [Transport Error Mapping](/docs/building/operating/transport-errors) — MCP と A2A からのエラー抽出
* [MCP Response Extraction](/docs/building/by-layer/L0/mcp-response-extraction) — MCP の同等仕様
* [A2A Guide](/docs/building/by-layer/L0/a2a-guide) — A2A トランスポート統合
