> ## 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.

# 非同期オペレーション

> AdCP の非同期オペレーションガイド: 同期・非同期・対話的（入力待ち）タスク種別のポーリング、SSE ストリーミング、タイムアウト戦略を含む処理方法。

AdCP のオペレーションは秒から日までかかることがあります。サーバーは、オペレーションにどれだけ時間がかかるか、何がブロックしているかに基づいて応答方法を決定します。

## 30 秒ルール

任意の AdCP タスクは次のいずれかのステータスを返せます。サーバーは、関与する作業について知っていることに基づいて選択します:

| 想定所要時間             | ステータス                  | 呼び出し元がすること                                                                               |
| ------------------ | ---------------------- | ---------------------------------------------------------------------------------------- |
| 30 秒未満             | `completed` / `failed` | 結果はインライン — 完了                                                                            |
| 30 秒超、サーバーが能動的に処理中 | `working`              | 帯域外の進捗シグナル。接続は開いたままで、準備できたら結果が届く。呼び出し元は待つだけ                                              |
| 外部依存でブロック          | `submitted`            | 真に非同期 — `task_id` でポーリング。設定されていれば `push_notification_config` で通知も配信できる。結果は数時間〜数日かかることがある |
| 人の入力でブロック          | `input-required`       | 呼び出し元は要求された入力を提供して継続する                                                                   |

**`working` は非同期ではありません。** これはサーバーが処理を続けながら帯域外（MCP ステータス通知または SSE 経由）で送る進捗シグナルです。呼び出し元は接続を保持し、準備できたら結果を受け取ります — ポーリングも Webhook もありません。「少し時間がかかっていますが、対応中です」と考えてください。

**`submitted` は非同期です。** オペレーションはサーバーの制御外の何か — パブリッシャー承認、人間のレビュー、サードパーティ処理 — でブロックされています。呼び出し元は常に `task_id` で AdCP タスクステータス面をポーリングできます。設定された Webhook は、バックグラウンドワークフロー向けの追加の通知チャネルであり、ポーリングの置き換えではありません。

:::tip `submitted` オペレーションの Webhook
**Webhook** はバックグラウンドの `submitted` オペレーションに推奨されます — 任意のトランスポート（MCP、A2A、REST）で動作し、単一セッションより長く続くオペレーションを扱えます。MCP/REST では、Webhook チャネルはタスクリクエストの `push_notification_config` で要求します。A2A では、snake\_case のスキルパラメーターではなく `configuration.pushNotificationConfig` のトランスポート設定のままです。リクエストが Webhook チャネルを含み、サーバーが `submitted` を返してタスクを受け入れた場合、サーバーは少なくとも終端の完了または失敗の通知をそのチャネルに配信しなければなりません（MUST）。中間の進捗通知は、別のオペレーション固有の契約が要求しない限り任意です。サーバーが要求された Webhook チャネルを尊重できない場合、配信を黙って格下げするのではなく、構造化エラーでリクエストを拒否しなければなりません（MUST）。[Push Notifications](/docs/building/by-layer/L3/webhooks) を参照してください。

**ポーリング**は AdCP タスクポーリング面を介して `submitted` タスクに常に有効です。3.x では、その面はレガシーの `tasks/get` で、セラーがエイリアスを宣伝する場合は任意で `get_task_status` を使用します。どちらの名前も同じペイロード形状を受け入れます。マルチアカウントの呼び出し元は、セラーがタスクの可視性を認証済みアカウント + プリンシパルのペアにスコープできるよう `account` を含めるべきです（SHOULD）。下記の[ポーリングパターン](#submitted-オペレーションのポーリング)を参照してください。

**トランスポートネイティブなタスクは AdCP ライフサイクルではありません。** MCP Tasks や A2A タスク更新は AdCP レスポンスを運んだりストリーミングしたりできますが、耐久性のあるタスク状態は AdCP ペイロード（`task_id`、`status`、Webhook ペイロード、AdCP ポーリング/リコンシリエーション）です。[MCP ガイド](/docs/building/by-layer/L0/mcp-guide#mcp-tasks-as-a-transport-wrapper)を参照してください。
:::

## オペレーションの例

### 同期（即時）

| Operation                            | Description                         |
| ------------------------------------ | ----------------------------------- |
| `get_adcp_capabilities`              | Agent capability discovery          |
| `list_creative_formats`              | Format catalog                      |
| `build_creative` (library retrieval) | Resolving an existing `creative_id` |

### 人の入力が必要な場合がある

| Operation                     | Description                                          |
| ----------------------------- | ---------------------------------------------------- |
| `get_products`                | When brief is vague or needs clarification           |
| `create_media_buy`            | When approval is required                            |
| `build_creative` (generation) | When creative direction or asset selection is needed |

### 非同期（`submitted`）になる場合がある

| Operation                           | Description                                                                |
| ----------------------------------- | -------------------------------------------------------------------------- |
| `create_media_buy`                  | Publisher approval workflows                                               |
| `update_media_buy`                  | Manual seller review for budget, targeting, or creative changes            |
| `get_products` (`brief` / `refine`) | Bespoke curation that depends on upstream inventory queries or HITL review |
| `get_signals` (`brief`)             | Semantic signal discovery that depends on slow provider queries or review  |
| `sync_creatives`                    | Asset review and transcoding pipelines                                     |
| `build_creative` (with review)      | Human creative review before finalizing                                    |
| `sync_catalogs`                     | Large feeds or feeds requiring content policy review                       |
| `activate_signal`                   | Platform deployment pipelines                                              |

これらのオペレーションは外部システムと連携するか人の承認を必要とします。ホールセールのフィード読み取りは例外です。`get_products buying_mode: "wholesale"` と `get_signals discovery_mode: "wholesale"` は同期的な修復/リコンシリエーション読み取りのままで、`submitted` ではなく `incomplete[]` で部分完了を報告します。

## タイムアウト設定

ステータスに応じて妥当なタイムアウトを設定します:

```javascript theme={null}
const TIMEOUTS = {
  sync: 30_000,         // 30 seconds — most operations complete here
  working: 300_000,      // 5 minutes — server is actively processing
  interactive: 300_000,  // 5 minutes for human input
  submitted: 86_400_000  // 24 hours for external dependencies
};

function getTimeout(status) {
  if (status === 'submitted') return TIMEOUTS.submitted;
  if (status === 'working') return TIMEOUTS.working;
  if (status === 'input-required') return TIMEOUTS.interactive;
  return TIMEOUTS.sync;
}
```

`working` はポーリング間隔ではなく接続タイムアウト（どれだけ開いたまま保持するか）を使います。サーバーは進捗を帯域外で送り、同じ接続で結果を配信します。`submitted` はポーリングまたは Webhook 配信のウィンドウを使います。Webhook を主要な通知パスとして設定していても、30 秒のポーリング間隔は妥当なデフォルトです。

## Human-in-the-Loop ワークフロー

### 設計原則

1. **デフォルトは任意** - 承認は実装ごとに設定
2. **明確なメッセージ** - 何を承認するかを明示
3. **適切なタイムアウト** - 人の入力で無期限にブロックしません
4. **監査証跡** - 誰が何をいつ承認したか記録

非同期オペレーションにおける Human-in-the-Loop パターンは [Embedded Human Judgment](/docs/governance/embedded-human-judgment) フレームワークを体現しています — 人間の判断は後付けではなく、システム設計に組み込まれます。

### 承認パターン

```javascript theme={null}
async function handleApprovalWorkflow(response) {
  if (response.status === 'input-required' && needsApproval(response)) {
    // Show approval UI with context
    const approval = await showApprovalUI({
      title: "Campaign Approval Required",
      message: response.message,
      details: response,  // Task fields are at top level
      approver: getCurrentUser()
    });

    // Send approval decision
    const decision = {
      approved: approval.approved,
      notes: approval.notes,
      approver_id: approval.approver_id,
      timestamp: new Date().toISOString()
    };

    return sendFollowUp(response.context_id, decision);
  }
}
```

### よくある承認トリガー

* **予算閾値**: \$100K 超のキャンペーン
* **新規広告主**: 初回の購入者
* **センシティブコンテンツ**: 特定業界や話題
* **手動インベントリ**: パブリッシャー承認が必要なプレミアム枠

## 進捗トラッキング

### 進捗更新

長時間処理では進捗情報が提供されることがあります:

```json theme={null}
{
  "status": "working",
  "message": "Processing creative assets...",
  "task_id": "task-456",
  "progress": 45,
  "step": "transcoding_video",
  "steps_completed": ["upload", "validation"],
  "steps_remaining": ["transcoding_video", "thumbnail_generation", "cdn_distribution"]
}
```

### 進捗表示

```javascript theme={null}
function displayProgress(response) {
  if (response.progress !== undefined) {
    updateProgressBar(response.progress);
  }

  if (response.step) {
    updateStatusText(`Step: ${response.step}`);
  }

  if (response.steps_completed) {
    updateStepsList(response.steps_completed, response.steps_remaining);
  }

  // Always show the message
  updateMessage(response.message);
}
```

## プロトコル非依存パターン

これらのパターンは MCP/A2A どちらでも機能します。

### 確認フローを含む商品探索

```javascript theme={null}
async function discoverProducts(brief) {
  let response = await adcp.send({
    task: 'get_products',
    brief: brief
  });

  // 確認ループを処理
  while (response.status === 'input-required') {
    const moreInfo = await promptUser(response.message);
    response = await adcp.send({
      context_id: response.context_id,
      additional_info: moreInfo
    });
  }

  if (response.status === 'completed') {
    return response.products;  // Task fields are at top level
  } else if (response.status === 'failed') {
    throw new Error(response.message);
  }
}
```

### 承認フローを含むキャンペーン作成

```javascript theme={null}
async function createCampaign(packages, budget) {
  let response = await adcp.send({
    task: 'create_media_buy',
    packages: packages,
    total_budget: budget
  });

  // Handle approval if needed
  if (response.status === 'input-required') {
    const approved = await getApproval(response.message);
    if (!approved) {
      throw new Error('Campaign creation not approved');
    }

    response = await adcp.send({
      context_id: response.context_id,
      approved: true
    });
  }

  // 'working' はサーバーが能動的に処理中 — 結果が届く
  // 'submitted' は外部依存でブロック — task_id でポーリング。
  // 設定されていれば Webhook も完了を配信し得る。
  if (response.status === 'submitted') {
    response = await pollForResult(response.task_id);
  }

  if (response.status === 'completed') {
    return response.media_buy_id;  // Task fields are at top level
  } else {
    throw new Error(response.message);
  }
}
```

### `submitted` オペレーションのポーリング

ポーリングは `submitted` オペレーションに常に有効です。設定されていれば Webhook も完了を配信し得ますが、呼び出し元はタスクポーリング面を通じてリコンサイルできます。`working` はポーリングしないでください — サーバーは開いた接続で結果を配信します。

```javascript theme={null}
async function pollForResult(taskId, options = {}) {
  const { maxWait = 86_400_000, pollInterval = 30_000 } = options;
  const startTime = Date.now();

  while (true) {
    if (Date.now() - startTime > maxWait) {
      throw new Error('Operation timed out');
    }

    await sleep(pollInterval);

    const response = await adcp.call('get_task_status', {
      task_id: taskId,
      include_result: true
    });

    if (['completed', 'failed', 'canceled'].includes(response.status)) {
      return response;
    }
  }
}
```

## 非同期前提の設計

### 状態を永続化します

非同期処理でメモリ状態に依存しない:

```javascript theme={null}
class AsyncOperationTracker {
  constructor(db) {
    this.db = db;
  }

  async startOperation(taskId, operationType, request) {
    await this.db.operations.insert({
      task_id: taskId,
      type: operationType,
      status: 'submitted',
      request: request,
      created_at: new Date(),
      updated_at: new Date()
    });
  }

  async updateStatus(taskId, status, result = null) {
    await this.db.operations.update(
      { task_id: taskId },
      {
        status: status,
        result: result,
        updated_at: new Date()
      }
    );
  }

  async getPendingOperations() {
    return this.db.operations.find({
      status: { $in: ['submitted', 'working', 'input-required'] }
    });
  }
}
```

### 再起動に耐える

オーケストレーター再起動後に追跡を再開:

```javascript theme={null}
async function onStartup() {
  const tracker = new AsyncOperationTracker(db);
  const pending = await tracker.getPendingOperations();

  for (const operation of pending) {
    // Check current status on server
    const response = await adcp.call('get_task_status', {
      task_id: operation.task_id,
      include_result: true
    });

    // Update local state
    await tracker.updateStatus(operation.task_id, response.status, response);

    // Resume polling if still pending
    if (['submitted', 'working'].includes(response.status)) {
      startPolling(operation.task_id);
    }
  }
}
```

## ベストプラクティス

1. **非同期前提で設計** - どの操作も時間がかかる前提
2. **状態を永続化** - メモリだけに依存しません
3. **再起動を考慮** - 起動時に追跡を再開
4. **タイムアウトを実装** - 無限に待たない
5. **進捗を表示** - ユーザーに状況を伝える
6. **キャンセル対応** - 長時間処理をキャンセル可能に
7. **監査証跡** - ステータス遷移をログ

## 次のステップ

* **Webhooks**: ポーリングの代わりにプッシュ通知を使う場合は [Webhooks](/docs/building/by-layer/L3/webhooks)
* **Task Lifecycle**: ステータス処理の詳細は [Task Lifecycle](/docs/building/by-layer/L3/task-lifecycle)
* **Orchestrator Design**: 本番パターンは [Orchestrator Design](/docs/building/operating/orchestrator-design)
